
如果你曾经在一台没有桌面环境的Linux服务器上手动连过Wi-Fi多半会撞上wpa_supplicant这个名字。它在Wi-Fi连接链里不是一个可有可无的辅助工具而是真正负责802.11认证、密钥握手和连接状态维护的后台守护进程。无论你是折腾树莓派、给嵌入式设备做开机自动联网还是把淘汰笔记本改造成无线接入点旁边的常驻客户端只要绕不开WPA/WPA2/WPA3加密的无线网络就必须跟它打交道。这篇文章不讲大而全的man手册翻译而是从实际使用场景出发把wpa_supplicant和配套的wpa_cli、配置文件、systemd服务、故障排查串成一套可以直接照搬的工作流。适合正在为无线网卡连不上公司Wi-Fi或者板子开机后没自动联网头疼的开发者也适合刚接触Linux无线网络、想搞清楚里面究竟发生了什么的新手。1. wpa_supplicant在无线连接栈里的位置不只是一个命令1.1 从网卡亮灯到获取IP中间发生了什么很多人第一次接触wpa_supplicant是在网上搜Linux命令行连Wi-Fi时看到的一段指令sudo wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf照抄之后发现Wi-Fi连上了但没拿到IP接着又得手动跑dhclient wlan0。于是有人疑惑wpa_supplicant到底管到哪一步答案很干脆它只管无线链路的建立不负责IP地址分配。在Linux里从无线网卡到真正能上网至少经过三件事驱动把网卡从硬件层面拉起来并且通过nl80211这个内核接口暴露给用户态。这一步通常用ip link set wlan0 up完成。wpa_supplicant对这个网卡执行扫描、选择网络、关联AP、完成WPA/WPA2/WPA3四次握手。这一步做完你的无线链路才处于连接已被信任的状态。DHCP客户端dhclient、udhcpc或dhcpcd在已关联的接口上请求IP地址。打个比方无线网卡是门锁驱动是锁芯wpa_supplicant是保安。保安负责核对你的身份、确认你能进门但进门之后坐哪个工位、领什么工牌是前台DHCP服务器说了算。三者缺一不可。1.2 为什么不能直接ip link set wlan0 up就完事有人会问不加密的开放网络是不是不需要wpa_supplicant理论上如果驱动和iw工具够新可以用iw wlan0 connect 开放SSID这种命令直接关联但实际使用中几乎没人这么干。原因有几个你连的AP不一定是开放网络绝大多数家庭路由器和企业网络都启用了WPA2或WPA3。iw直接连接只适合快速临时测试不处理重连、密匙协商、漫游和网络优先级这些工程化问题。内核里的cfg80211只提供机制不提供策略。而wpa_supplicant正是那个把选哪个网络、用什么方式认证、挂了怎么办这些策略跑起来的东西。换句话说wpa_supplicant不是一个简单的命令而是一个完整实现了IEEE 802.11i/RSNRobust Security Network机制的守护进程。它接管了无线网卡的认证状态机并且在状态异常时主动处理。这也是它值得花点时间搞明白的原因。2. 配置文件实战不同网络场景的wpa_supplicant.conf写法2.1 配置文件的骨架与全局选项/etc/wpa_supplicant/wpa_supplicant.conf是默认配置路径但不同发行版可能略有差异。一个最简可用的配置文件长这样ctrl_interface/run/wpa_supplicant update_config1 countryCN network{ ssidMyHomeWiFi pskmy-secret-password key_mgmtWPA-PSK }逐项说下ctrl_interface指定wpa_cli用来和守护进程通信的控制接口目录。这个字段必须存在否则wpa_cli没法用。update_config1允许wpa_cli save_config把运行时的网络配置写回配置文件。如果你不想让工具改配置可以不加。countryCN设置国家码直接影响无线网卡是否允许使用5GHz的DFS信道。很多人扫不到5G频段就是缺了这个字段。network{}定义一个要连接的网络。可以写多个后面会说优先级。配置文件里的引号是可选的前提是内容没有特殊字符。但如果SSID或密码里带空格、#、这类符号最好老老实实用双引号包起来否则解析时容易出问题。2.2 高频场景家庭PSK、开放网络、企业认证、WPA3不同安全级别的网络key_mgmt字段写法完全不同。我整理了几个高频可直接套用的配置。家庭路由器最常见的WPA2-PSKnetwork{ ssidHomeWiFi psk12345678 key_mgmtWPA-PSK pairwiseCCMP groupCCMP }pairwise和group是指定数据加密算法。现代路由器基本都用CCMP也就是AES一般不用写死但如果你遇到过握手成功但传不了数据的怪问题可以显式加上这两行。无密码的开放网络network{ ssidFreeAirportWiFi key_mgmtNONE }这里如果写成key_mgmtNONE但网络实际上要网页认证Captive Portalwpa_supplicant依然只能帮你关联上AP浏览器跳出的认证页面是另一个层面的事。企业WPA/WPA2-Enterprise典型PEAPMSCHAPv2network{ ssidCorpWiFi key_mgmtWPA-EAP eapPEAP identityyournamecompany.com passwordyourpassword phase2authMSCHAPV2 }企业网络认证失败的坑很多最常见的是缺少CA证书校验。上面的写法是匿名方式生产环境建议加上ca_cert/path/to/ca.pem。如果不加登录密码是以明文形式跑在受信任的加密隧道里安全系数会打折扣。WPA3-PersonalSAEnetwork{ ssidWPA3Test key_mgmtSAE sae_passwordpassword2024 ieee80211w2 }ieee80211w2表示PMFProtected Management Frames必须启用这是WPA3认证里比较常见的要求。但如果AP配置为混合WPA2/WPA3模式key_mgmtSAE可能会导致兼容性问题这时需要保留WPA-PSK备选。2.3 用wpa_passphrase生成psk隐藏SSID和特殊字符的坑明文psk写在配置文件里有个隐患如果配置文件权限没设好Wi-Fi密码就等于直接暴露了。更稳妥的做法是用wpa_passphrase生成256位PMK的预计算哈希wpa_passphrase MyHomeWiFi 12345678 /etc/wpa_supplicant/wpa_supplicant.conf输出类似network{ ssidMyHomeWiFi #psk12345678 psk4e1a1a5c5b6d8c2e... }wpa_supplicant支持用PSK哈希代替明文这样即使别人看到配置文件也拿不到原始密码。不过要注意如果密码特别简单哈希也能被字典攻击跑出来所以最终安全还是要靠文件权限chmod 600。隐藏SSID的网络需要在network块里加scan_ssid1否则扫描阶段因为不广播而找不到网络network{ ssidHiddenNet psksecret scan_ssid1 }这里有个容易忽略的细节如果你开启了scan_ssid1在某些驱动上主动探测隐藏SSID会降低扫描效率所以没必要对每个网络都加这一行。3. 命令行控制台wpa_cli连接Wi-Fi的交互操作3.1 前台启动与后台启动各什么时候用排障时我会用前台模式sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -D nl80211这样日志直接打到终端上三秒钟之内能看到扫描、关联、握手的全过程。确认没问题后再用-B切到后台sudo wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -D nl80211-D nl80211是指定驱动后端。现代Linux无线网卡基本都走nl80211老掉牙的驱动可能用wext一般不需要手动指定但如果遇到nl80211: Driver does not support authentication/association这类报错可以试试-D wext。3.2 wpa_cli最常用命令与完整连接流程wpa_cli是一个和wpa_supplicant进程通信的命令行客户端。进入交互模式比较简单sudo wpa_cli -i wlan0也可以一条一条执行比如扫描sudo wpa_cli -i wlan0 scan sudo wpa_cli -i wlan0 scan_resultsscan_results输出的内容非常直观bssid / frequency / signal level / flags / ssid。看到自己要连的网络后可以用一组命令把网络加上并连接# 添加一个新网络返回网络ID通常从0开始 sudo wpa_cli -i wlan0 add_network # 设置SSID和PSK sudo wpa_cli -i wlan0 set_network 0 ssid MyHomeWiFi sudo wpa_cli -i wlan0 set_network 0 psk 12345678 # 启用并选择该网络 sudo wpa_cli -i wlan0 enable_network 0 sudo wpa_cli -i wlan0 select_network 0 # 保存配置到文件 sudo wpa_cli -i wlan0 save_config这里有一个特别容易踩的坑set_network 0 ssid指令里值必须带引号而且如果你是在shell里执行需要写成MyHomeWiFi。因为wpa_cli会把字符串中的双引号解析为边界不加的话SSID会少一截。如果SSID里有空格、中文或特殊符号这一步写错基本连不上。wpa_cli的常用指令用一个表格总结指令作用status查看当前连接状态、SSID、IP等scan触发一轮无线扫描scan_results显示扫描结果list_networks显示配置文件中的网络列表add_network新建一个网络并返回编号set_network id field value设置网络参数ssid/psk/key_mgmt等enable_network id启用某个网络disable_network id禁用某个网络select_network id强制连接某个网络其他网络将被忽略save_config把运行时的网络配置写回文件disconnect/reconnect断开/重新连接当前无线链路quit退出交互模式3.3 读懂status输出定位连接到了哪一步运行wpa_cli -i wlan0 status会得到类似这样的输出Selected interface wlan0 BSSIDxx:xx:xx:xx:xx:xx freq2412 ssidMyHomeWiFi id0 key_mgmtWPA2-PSK pairwise_cipherCCMP group_cipherCCMP wpa_stateCOMPLETED ip_address192.168.1.100 p2p_device_address...重点看wpa_state字段DISCONNECTED还没关联到AP或主动断开。SCANNING正在扫描可用网络。AUTHENTICATING正在和AP做802.11认证。ASSOCIATING正在关联想到一个AP门下面。ASSOCIATED已经关联上AP但还没完成WPA握手。COMPLETED四次握手完成无线链路已经安全建立。如果状态卡在AUTHENTICATING大概率是密码协商问题如果一直SCANNING半天不动作多半是扫描列表里没有匹配的SSID或者AP射频被关了。记住这个状态流转后面排障会顺很多。4. 用systemd把Wi-Fi连接做成稳定服务4.1 自己写wpa_supplicant.service而不是依赖发行版默认很多发行版自带的wpa_supplicant.service是多接口通用服务配置方式比较绕。我更推荐在嵌入式设备或服务器上给每个无线接口单独建一个服务单元。例如创建/etc/systemd/system/wpa_supplicantwlan0.service[Unit] DescriptionWPA supplicant for wlan0 Requiressys-subsystem-net-devices-wlan0.device Aftersys-subsystem-net-devices-wlan0.device Beforenetwork.target [Service] Typesimple ExecStart/sbin/wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant-wlan0.conf Restarton-failure RestartSec5 [Install] WantedBymulti-user.target这里有两个值得注意的细节Requiressys-subsystem-net-devices-wlan0.device是一个udev生成的设备单元。写成依赖后如果无线网卡不存在服务就不会启动避免出现开机时网卡还没枚举好wpa_supplicant先启动结果报错的经典问题。Restarton-failure配合RestartSec5意思是如果wpa_supplicant因为异常原因退出5秒后自动拉起。无线网卡驱动偶发挂掉时这个配置能省去手动重启服务的时间。启动方式sudo systemctl daemon-reload sudo systemctl enable wpa_supplicantwlan0 --now4.2 与NetworkManager、dhcpcd的分工和冲突如果你的系统同时装了NetworkManager开机后它也会尝试管理wlan0结果就是两个进程同时去控制同一个无线网卡日志里全是奇怪的错误。解决方式有几种最简单粗暴禁用NetworkManager的无线管理或干脆禁用服务。在Raspberry Pi OS上我会直接sudo systemctl disable NetworkManager然后让wpa_supplicant接管无线。如果不想停用NetworkManager可以用nmcli device set wlan0 managed no告诉它别管这个接口。但重启后需要再执行最好写进rc.local或systemd里。在纯命令行环境我习惯搭配dhcpcd来获取IP。装好dhcpcd后只要无线链路完成它会自动在wlan0上跑DHCP不需要额外配置。但要注意一点wpa_supplicant只是负责链路层它不会自动通知DHCP客户端我现在连上了。所以得靠dhcpcd监听接口事件或者配合wpa_cli -a脚本触发DHCP请求否则极可能出现无线已连接但没IP的尴尬。4.3 开机自启后网络没起来的排查systemd配置没问题但开机依然没网络我碰过的情况基本是这三类配置文件路径不对ExecStart里写的是/etc/wpa_supplicant/wpa_supplicant-wlan0.conf但实际配置文件在别的路径服务启动直接失败。服务起来了但没连上systemctl status显示running可wpa_cli status还停在SCANNING。这种基本是扫描阶段就卡住可能是rfkill把无线网卡禁用了。ctrl_interface目录不存在或权限不对wpa_cli连不上进程。对应方法是确保/run/wpa_supplicant目录存在且属于根用户或者把配置里的ctrl_interfaceDIR/run/wpa_supplicant GROUPnetdev加上GROUP允许特定用户组访问。排查套路很固定先看systemctl status然后看journalctl -u wpa_supplicantwlan0 -f最后再看wpa_cli status。大多数问题都能在日志里找到答案。5. 排查无线连接问题的完整套路5.1 扫描不到AP时的根因定位无线网卡扫不到任何SSID通常先怀疑三个地方驱动、射频开关、国家码。先确认网卡是否被识别iw dev如果没有输出说明驱动没加载或硬件被禁用。再执行rfkill list如果wlan0显示Soft blocked: yes或Hard blocked: yes用rfkill unblock wifi解除软锁定。硬锁定是笔记本上的物理开关只能手动拨。如果网卡正常但只扫到2.4GHz扫不到5GHz请检查配置文件里的country字段。很多网卡出厂默认在world regulatory domain会限制5GHz频段。设置好国家码后要重启wpa_supplicant再扫描否则不生效。还有一个容易忽略的点某些USB无线网卡在虚拟机里直通时驱动有兼容性问题iw scan结果为空但dmesg里能看到驱动报错。这种要先解决驱动不要继续在wpa_supplicant层面浪费时间。5.2 认证失败和四次握手超时的根因如果扫描能看见AP但状态一直在AUTHENTICATING或ASSOCIATING反复横跳最常见的原因排序是密码错误。PSK输错一个字符就会导致四次握手失败。推荐用wpa_passphrase生成哈希避免特殊字符被配置文件解析吃掉。AP启用了MAC地址过滤。检查AP管理页面是否把当前网卡的MAC加入了白名单。混合WPA2/WPA3模式下key_mgmt不匹配。如果路由器的安全模式是WPA2-PSK/WPA3-SAE混合需要在配置里用key_mgmtWPA-PSK SAE而不是只写SAE。驱动对PMF支持不完整。可以把ieee80211w1改成ieee80211w0试试虽然降低了安全性但很多老驱动兼容性能好一大截。日志里如果出现4-Way Handshake failed基本可以断定是PSK不一致或者AP端的协商策略有问题而不是射频信号问题。信号弱通常表现为反复重连而不是连一次就握手失败。5.3 用调试日志把隐藏信息挖出来很多东西藏着看不到是因为你没开调试日志。用这种方式启动sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf -D nl80211 -dd-d增加调试等级-dd更详细。日志会爆量但里面包含了AP协商的细节。几个常见的关键词日志内容含义CTRL-EVENT-SCAN-RESULTS扫描完成结果已返回Trying to associate with xx:xx:xx:xx:xx:xx准备关联某个BSSIDWPA: 4-Way Handshake completed四次握手成功链路已加密Authentication with xx:xx:xx:xx:xx:xx timed out认证超时AP没有响应WPA: 4-Way Handshake failed - pre-shared key mismatchPSK不匹配密码有误CTRL-EVENT-DISCONNECTED bssid... reason3被AP断开reason3常见于AP主动踢人或链路老化对reason code有疑问时直接去查802.11规范里的reason code列表。比如reason2是前一个认证失效reason6是类2帧接收失败reason15是4次握手超时。手边没有文档时先在搜索引擎里搜wifi reason code 15即可。区块好表格我会放一个常见reason映射表。Reason Code含义常见场景2之前的认证不再有效AP重启或客户端漫游3发送站即将离开AP手动断开或AP主动踢人6收到非认证帧兼容性问题或干扰15四次握手超时PSK错误、加密套件不一致17AP被太多客户端占用性能问题6. 自动化与应急处置用脚本接管Wi-Fi连接6.1 用wpa_cli -a做事件驱动脚本wpa_cli -a /path/to/script是经常被忽略的利器。当wpa_supplicant状态发生变化时脚本会被调用并传入参数。最简单的脚本#!/bin/sh IFNAME$1 ACTION$2 case $ACTION in CONNECTED) logger Wi-Fi connected on $IFNAME # 这里可以触发dhclient或更新路由 ;; DISCONNECTED) logger Wi-Fi disconnected on $IFNAME ;; esac给脚本加执行权限后运行sudo wpa_cli -i wlan0 -a /usr/local/bin/wifi-event.sh -B这样当无线从断开变为连接时脚本会立刻拿到事件。官方的事件除了CONNECTED和DISCONNECTED还有SCAN_STARTED、SCAN_RESULTS、TERMINATING等。实际工作中我最常用它来做连接成功后自动执行DHCP并更新DNS这类操作。也可以把它做成systemd服务让它开机常驻。比如[Unit] Descriptionwpa_cli event handler Afterwpa_supplicantwlan0.service [Service] Typesimple ExecStart/usr/bin/wpa_cli -i wlan0 -a /usr/local/bin/wifi-event.sh -B Restartalways RestartSec3 [Install] WantedBymulti-user.target注意-B参数表示让wpa_cli自身后台运行但如果你放到systemd服务里可以不加-B让systemd直接管理前台进程。6.2 多网络自动切换与优先级出差或在多个环境切换的场景可以在一个配置文件里定义多个network块并用priority字段决定连接顺序。数值越大优先级越高。network{ ssidHomeWiFi psk12345678 priority10 } network{ ssidCompanyWiFi key_mgmtWPA-EAP eapPEAP identityusercompany passwordpass priority20 }wpa_supplicant会在开机或断线时自动按优先级选择可用网络。这个优先级只在同时扫描到多个已知SSID时起作用如果高优先级网络不在范围内会自动连次级网络。隐藏SSID的scan_ssid1配合priority也常见。不过要注意隐藏网络每次都要主动探测耗电和扫描时长都有代价能不用就别用。6.3 断线重连的保活技巧无线网络受干扰、AP重启、物理遮挡影响偶尔断线很难避免。wpa_supplicant本身有重连机制但某些驱动在断线后进入假死状态表现为wpa_cli status一直显示DISCONNECTED就是不重新扫描。我常用的保活手段是写一个简单的cron任务每两分钟检查一次#!/bin/bash if ! wpa_cli -i wlan0 status | grep -q wpa_stateCOMPLETED; then wpa_cli -i wlan0 reconnect sleep 5 if ! wpa_cli -i wlan0 status | grep -q wpa_stateCOMPLETED; then systemctl restart wpa_supplicantwlan0 fi fi第一次先尝试reconnect如果5秒内还没状态恢复就重启整个wpa_supplicant服务。重启虽然暴力但对驱动级卡死是最有效的。另外部分USB无线网卡在长时间运行后会出现丢包率飙升的问题。此时可以尝试ip link set wlan0 down ip link set wlan0 up配合systemctl restart wpa_supplicantwlan0一起用。这个操作会重置网卡内部状态比反复切换工作模式要干净得多。6.4 实测经验先小步验证再上自动化我个人的习惯是不管在服务器还是嵌入式板子上第一遍都先手工跑前台wpa_supplicant盯着日志确认能稳定连上然后再写systemd和事件脚本。直接一步到位上自动化的代价是出问题时日志被systemd吞掉了排查成本反而高很多。还有一个小经验不要把wpa_supplicant和hostapd同时开在同一块物理网卡上除非你非常清楚自己在做什么并行机制。无线网卡同一时刻要么作为station要么作为APnl80211会拒绝这种二象性配置报错信息往往是Device or resource busy。真需要同时做STA和AP要用支持虚拟接口的网卡通过iw dev wlan0 interface add wlan1 type __ap创建第二块虚拟接口然后再一个跑hostapd、一个跑wpa_supplicant。我在实际使用中最大的体会是别把wpa_supplicant当成一个运行完就退出的命令。它是一个有状态、有事件、需要长期驻留的守护进程。理解了它的运行模型再配合wpa_cli和systemdLinux下面的Wi-Fi连接管理就变得非常可控。哪怕你最后用回NetworkManager再遇到复杂的无线问题时也知道底层到底发生了什么。