
手里有块开发板、一台装了 Linux 的旧笔记本或者一台只有网口没有无线模块的工控机临时要给几台设备组个无线局域网路由器又不在手边——这种场景我碰过太多次。把 Linux 主机上的无线网卡从 station客户端模式切成 APAccess Point模式让它自己变成热点是最省事、最不依赖外设的解法。事情听起来简单实际卡人的地方特别多网卡驱动到底支不支持 AP、NetworkManager 会不会在背后把接口抢回去、hostapd 起来了但客户端拿不到 IP、能连上却上不了外网、换个 5G 信道直接起不来……每一环都够你耗掉半天。这篇学习记录就是把我自己踩过的坑和最后跑通的那套配置整理出来涵盖网卡能力确认、hostapd 与 dnsmasq 的配合、NAT 转发以及一些只有在现场才会碰到的怪问题。不管你是刚接触 Linux 网络配置的新手还是想在嵌入式板子上做 SoftAP 的老手下面这套思路和配置基本都能直接抄。1. 动手之前先摸清网卡的底子1.1 用 iw list 确认 AP 模式能力这一步是分水岭。很多 USB 无线网卡在 Windows 下能被各种随身 WiFi 助手一键切成热点但换到 Linux 上驱动未必把 AP 能力暴露出来。判断方法很直接插上网卡确认接口名通常是 wlan0 或 wlp3s0然后执行iw list找到Supported interface modes这一段。iw dev # 输出里能看到 Interface wlan0 / type managed 之类的信息 iw list | sed -n /Supported interface modes/,/Band/p如果输出里出现* AP说明驱动层支持如果只有managed、monitor那这条路基本就堵死了要么换网卡要么改驱动。这里有个容易被忽略的点iw list读的是内核里 mac80211 上报的能力集而厂商私有驱动比如一些 USB 小模块如果没走 mac80211 框架iw根本不认这个接口你会看到nl80211 not found或者接口压根不出现。这种情况得先lsmod、dmesg | tail确认驱动加载没加载。接下来要看的是valid interface combinations这一段。它决定了你能不能一卡两用——同时当客户端又当热点。典型输出长这样valid interface combinations: * #{ managed } 1, #{ AP } 1, total 2, #channels 1#channels 1是关键它意味着所有虚拟接口必须待在同一个信道上。如果你一边连着家里的 5G 热点一边想在自己 AP 上跑 2.4G 信道 6那是做不到的驱动会直接拒绝。我在这上面栽过一次折腾了半天 hostapd 起不来最后才发现是信道冲突。1.2 驱动与固件的坑比想象中深网卡认出来了、iw list里也有 AP不代表一定跑得起来真正的拦路虎往往是固件和驱动版本。市面上常见的几类无线芯片在 Linux 下表现差别很大。Realtek 的 RTL8852BE 这类 PCIe 网卡走的是 rtw89 驱动主线内核从 5.16 之后支持逐渐完善。早期版本iw list里能看到 AP但 hostapd 一启动就报nl80211: Could not configure driver mode。解决办法通常是升级内核到较新版本或者从厂商仓库里拉对应分支编译。而 AIC8800D40 这种 USB 模块更典型——它在很多国产小主机、随身 WiFi 硬件上出现但主线内核压根没有驱动必须自己编译厂商提供的源码编译前还得先确认内核头文件版本匹配。驱动装好之后还有固件。dmesg里出现Direct firmware load for xxx.bin failed就说明固件没放对位置。大多数发行版把固件放在/lib/firmware驱动代码里写死的路径通常是/lib/firmware/rtlwifi/或/lib/firmware/aic8800/这种子目录路径错一个字母就加载失败。提示装驱动之前先把内核版本记下来uname -r然后对照厂商 README 里的支持列表。厂商驱动对内核版本极其敏感差一个小版本就可能编译不过。还有一类问题是国家码regulatory domain。无线网卡出厂时带的信道表来自 EEPROM但内核还叠加了一层监管限制。iw reg get看当前设置如果显示country 00: DFS-UNSET很多信道会被标记为不可用hostapd 会直接拒绝启动。用iw reg set CN或写进/etc/conf.d/wireless-regdom不同发行版路径不一样再配合 hostapd 配置里的country_codeCN基本就能解决。1.3 单网卡还是双网卡选型决定后面所有事架构上其实只有两种玩法选错方向后面全是白费功夫。双网卡方案是最稳的一张无线网卡专门做 AP一张有线网卡或者另一张无线网卡做上行通过 NAT 把客户端流量转出去。这种方案我强烈推荐因为不用考虑信道共享、接口并发这些恶心事调试起来也简单——AP 侧的配置和上行侧完全解耦。单网卡方案就是一张无线网卡既当热点又负责上行。它要么把上行也走无线网卡同时做 STA AP依赖前面说的 interface combinations 支持要么干脆不接外网只做本地局域网比如几个设备之间互相传文件、做调试网。后者其实很常见给嵌入式板子开一个热点手机连上去用浏览器访问板子上的管理页面压根不需要外网。我自己的习惯是只要手边有网口一律走双网卡方案只有在真的只有一个无线接口时才去啃单网卡的并发配置。这个判断看着简单但能省掉后面一半的调试时间。2. 方案选型为什么是三件套2.1 hostapd 而不是 NetworkManager 的共享模式现在很多桌面发行版Ubuntu、Fedora的图形界面里点两下就能开热点背后是 NetworkManager 调用 hostapd 加上自己的 DHCP 和 NAT 实现。这条路径能用但不适合作为学习记录原因有两个一是它把细节全藏起来了出问题只能看journalctl里的层层日志二是它在无桌面的服务器版、嵌入式系统里根本没这套东西。直接用 hostapd 则完全可控。它只负责一件事让网卡进入 AP 模式、处理 802.11 的认证和关联、管理客户端接入。至于给客户端分 IP、把流量转出去它一概不管——这正是它设计得好的地方职责单一出问题好定位。2.2 三段式分工认证、地址、转发跑通一个能上网的热点实际需要三个组件各司其职。组件职责关键点hostapd802.11 层SSID 广播、认证、关联、加密只管二层不管 IPdnsmasqDHCP 下发地址 DNS 转发要绑定到 AP 接口别和系统 DNS 打架内核转发 NAT把 AP 侧流量转成上行口的流量需要开 ip_forward 和 MASQUERADE这套组合的好处是每一层都能单独测hostapd 起来后能搜到 SSID 说明二层通了客户端拿到 IP 说明 DHCP 通了能 ping 通网关说明链路通了能上外网说明 NAT 通了。一旦出问题顺着这四步往下查基本三分钟能定位。2.3 开干之前必须做的环境清理这是最容易翻车的一步我把它单独拎出来。Linux 桌面发行版里NetworkManager 和 wpa_supplicant 默认都在跑它们会牢牢抓住无线接口。你前脚ip link set wlan0 down后脚 NetworkManager 就把它拉起来重新扫描hostapd 直接报错退出。清理顺序大致是这样# 1. 解除 rfkill 软阻塞很多笔记本有硬件开关或 Fn 组合键 rfkill list rfkill unblock wifi # 2. 让 NetworkManager 别管这个接口不要直接 stop 服务其他网卡还要用 nmcli dev set wlan0 managed no # 3. 停掉可能占用接口的 wpa_supplicant systemctl stop wpa_supplicant.service 2/dev/null # 4. 清掉接口上的残留配置 ip link set wlan0 down ip addr flush dev wlan0如果你用的是 systemd-networkd 而不是 NetworkManager思路一样在对应.network文件里把 wlan0 设为Unmanagedyes然后 reload。Debian 老版本用/etc/network/interfaces的话确保 wlan0 没有auto声明。注意nmcli dev set wlan0 managed no是临时的重启失效。要永久生效得写配置文件或者在启动脚本里每次都执行一遍。我习惯写进脚本反正最后都要做开机自启。还有一个隐蔽的坑是systemd-resolved。它默认监听 53 端口dnsmasq 一起来就报failed to create listening socket for port 53: Address already in use。两种处理方式一是在 dnsmasq 配置里加bind-interfaces只绑 wlan0二是把 resolved 的 stub listener 关掉。3. 实操把 wlan0 变成热点3.1 依赖安装与静态 IP 设置先装包。Debian/Ubuntu 系apt update apt install hostapd dnsmasq iw iproute2 iptables关于 nftables 还是 iptables新一点的发行版Debian 11、Ubuntu 22.04默认是 nftables 后端iptables 命令其实是 nft 的兼容层。两者都能用但别混着写规则不然iptables -L和nft list ruleset看到的完全对不上。我后面两套都会给。装完 hostapd 后Debian 会自动启用hostapd.service而它默认读的配置文件里没内容会失败退出。建议先禁用等配置写好再手动启动systemctl unmask hostapd systemctl disable hostapd然后是给 AP 接口配静态 IP。这里不需要改/etc/network/interfaces直接在启动脚本里用 ip 命令最快也最不容易被系统网络管理覆盖ip addr add 192.168.50.1/24 dev wlan0 ip link set wlan0 up选网段也有讲究。192.168.50.0/24 这种不常被家用路由器占用的段比较安全192.168.1.0/24 和 192.168.0.0/24 撞车概率极高尤其是你的上行网络也在同一段时会出现路由混乱——客户端明明拿到了 IP但发包发不出去。3.2 hostapd.conf 逐行拆解配置文件我建议放/etc/hostapd/hostapd.conf下面是 2.4G 的最小可用版本interfacewlan0 drivernl80211 ssidMyLinuxAP hw_modeg channel6 ieee80211n1 wmm_enabled1 macaddr_acl0 auth_algs1 ignore_broadcast_ssid0 country_codeCN ieee80211d1 wpa2 wpa_passphraseYourStrongPass wpa_key_mgmtWPA-PSK wpa_pairwiseTKIP rsn_pairwiseCCMP几个参数的选型逻辑值得说一下。hw_modeg表示 2.4Ghw_modea是 5G。信道别乱选2.4G 只有 1、6、11 三个互不重叠的信道选 3 或者 9 会造成邻频干扰实测吞吐掉得厉害。ieee80211n1打开 802.11n能显著提升速率但需要配合 HT 能力声明有些老网卡开了反而连不上可以先不加跑通了再加。wpa_pairwiseTKIP这行现在其实可以去掉因为wpa2只走 WPA2-PSK而rsn_pairwiseCCMP已经指定了 CCMP 加密。留着 TKIP 是为了兼容极老的设备但会导致强制走 WPA2-TKIP 混合模式反而降低速率。新设备多的场景直接删掉这行。5G 版本的配置需要额外几行hw_modea channel149 ieee80211ac1 ieee80211d1 vht_oper_chwidth1 vht_oper_centr_freq_seg0_idx155这里channel选 149 是有理由的——它是 5G 里的非 DFS 信道。像 52-64、100-144 这些属于 DFS 频段hostapd 启动前要做 CAC信道可用性检查要等 60 秒甚至更久日志里会卡在DFS start CAC。测试阶段直接用 149、153、157、161 这些高频段信道启动是秒开的。vht_oper_chwidth1是 80MHz 带宽中心频率索引要根据主信道算。比如主信道 14980MHz 覆盖 149-161中心是 155所以写 155。这里写错的后果是 hostapd 报Invalid VHT channel configuration直接退出。3.3 dnsmasq 的最小配置/etc/dnsmasq.d/ap.conf或者直接塞进/etc/dnsmasq.conf末尾interfacewlan0 bind-interfaces dhcp-range192.168.50.10,192.168.50.200,255.255.255.0,12h dhcp-option3,192.168.50.1 dhcp-option6,192.168.50.1 domain-needed bogus-priv log-dhcpinterfacewlan0加bind-interfaces的组合是让 dnsmasq 只在无线接口上监听 DHCP 和 DNS避免和系统上其他服务抢端口。dhcp-option3把默认网关指到 AP 自己dhcp-option6把 DNS 也指过来——因为 dnsmasq 本身能做 DNS 转发上行的 DNS 服务器它会从/etc/resolv.conf里读。坑在这里如果上行网络的 DNS 是通过 DHCP 拿的而且你的发行版用了 systemd-resolved/etc/resolv.conf里可能只有一个127.0.0.53的 stub 地址。dnsmasq 去问这个地址而 resolved 又只监听本机的 loopback问题是能通的但偶尔会有查询延迟。更稳的做法是给 dnsmasq 显式指定上游no-resolv server223.5.5.5 server119.29.29.29log-dhcp建议先在调试阶段加上能清楚看到哪个客户端在什么时候拿了哪个 IP出问题时省一半时间。3.4 打开转发与 NAT内核默认不允许转发得先打开sysctl -w net.ipv4.ip_forward1 # 永久生效写进 /etc/sysctl.d/99-ap.conf echo net.ipv4.ip_forward1 /etc/sysctl.d/99-ap.confiptables 版本WANeth0 APwlan0 iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o $WAN -j MASQUERADE iptables -A FORWARD -i $WAN -o $AP -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i $AP -o $WAN -j ACCEPT三条规则的分工第一条是源地址伪装把内网地址翻译成上行口的地址发出去第二条允许已建立连接的回包进来第三条允许内网主动发起的流量出去。这两条 FORWARD 缺一不可只写第三条的话客户端能发请求但收不到响应表现为能连上、能拿到 IP、就是打不开网页。nftables 版本nft add table ip nat nft add chain ip nat postrouting { type nat hook postrouting priority 100 ; } nft add rule ip nat postrouting oifname eth0 ip saddr 192.168.50.0/24 masquerade nft add table inet filter nft add chain inet filter forward { type filter hook forward priority 0 ; policy drop ; } nft add rule inet filter forward iifname wlan0 oifname eth0 accept nft add rule inet filter forward iifname eth0 oifname wlan0 ct state established,related accept注意 nft 里policy drop是个陷阱——如果系统里已经有其他 forward 规则加这条会覆盖默认策略导致别的转发失效。稳妥点先nft list ruleset看看现状。3.5 一键启动脚本与开机自启把上面所有步骤串起来写成/usr/local/bin/ap-up.sh#!/bin/bash set -e IFACEwlan0 WANeth0 AP_IP192.168.50.1 SUBNET192.168.50.0/24 rfkill unblock wifi nmcli dev set $IFACE managed no 2/dev/null || true ip link set $IFACE down ip addr flush dev $IFACE ip addr add $AP_IP/24 dev $IFACE ip link set $IFACE up sysctl -w net.ipv4.ip_forward1 /dev/null systemctl restart hostapd systemctl restart dnsmasq iptables -t nat -C POSTROUTING -s $SUBNET -o $WAN -j MASQUERADE 2/dev/null || \ iptables -t nat -A POSTROUTING -s $SUBNET -o $WAN -j MASQUERADE iptables -C FORWARD -i $WAN -o $IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT 2/dev/null || \ iptables -A FORWARD -i $WAN -o $IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -C FORWARD -i $IFACE -o $WAN -j ACCEPT 2/dev/null || \ iptables -A FORWARD -i $IFACE -o $WAN -j ACCEPT echo AP is up. SSID: MyLinuxAP, Gateway: $AP_IP用iptables -C先检查再添加是为了让脚本可重复执行。直接-A的话手动跑两次脚本就会插入重复规则虽然功能上不冲突但iptables -L看起来会很乱。停机的脚本反过来写一遍主要是 flush 地址、停服务、删 NAT 规则。写完后chmod x,再写个 systemd unit 挂在network.target之后启动就实现了开机自动变热点。4. 验证链路与常见问题排查4.1 从客户端侧倒着验一遍配置完别急着说好了按这个顺序验证# 第 1 步AP 起来了没 systemctl status hostapd --no-pager hostapd_cli status iw dev wlan0 info # 应该看到 type AP # 第 2 步dnsmasq 在听吗 ss -lunp | grep :67 # DHCP 是 UDP 67 ss -ltnp | grep :53 # 第 3 步客户端连上后在服务器侧看租约 cat /var/lib/misc/dnsmasq.leases # 第 4 步在客户端上测通链路 ping 192.168.50.1 # 网关通不通 ping 223.5.5.5 # 外网 IP 通不通排除 DNS 问题 nslookup example.com # DNS 通不通这个顺序的精妙之处在于它把问题切成了互不干扰的几段。ping 223.5.5.5通但nslookup不通那一定是 DNS 配置问题跟网络转发没关系反过来ping 网关都不通那 NAT 压根不用看问题在二层或者 DHCP。服务器侧抓包也很有用尤其当客户端连上但没反应时tcpdump -i wlan0 -n port 67 or port 68如果看到客户端的 DHCP Discover 但没看到 Offer那就是 dnsmasq 没绑对接口或者被防火墙拦了。如果连 Discover 都看不到说明二层关联有问题回去看 hostapd 日志。4.2 常见问题速查表现象大概率原因处理方式hostapd 报Could not configure driver mode接口被 NetworkManager 或 wpa_supplicant 占用nmcli dev set wlan0 managed no停 wpa_supplicant日志停在DFS start CAC选了 DFS 信道换 36/149 等非 DFS 信道nl80211: Driver does not support驱动不支持 AP 或固件缺失查iw list、dmesg客户端搜不到 SSID信道非法、国家码未设、网卡被 rfkill 阻塞iw reg get、country_codeCN、rfkill unblock能连上但拿不到 IPdnsmasq 未启动、绑错接口、53/67 端口冲突查bind-interfaces、systemd-resolved有 IP 却上不了外网ip_forward 没开、缺 FORWARD 规则、上行口名写错sysctl net.ipv4.ip_forward、iptables -t nat -L -n -v连接频繁掉线省电模式导致、信道干扰iw dev wlan0 set power_save off、换 1/6/11速率只有几十 KB2.4G 邻频干扰、加密走了 TKIP换信道、删掉wpa_pairwiseTKIP5G 配置报 VHT 参数错vht_oper_centr_freq_seg0_idx算错主信道 149 配 155主 36 配 424.3 提升稳定性与吞吐的几个调优跑通只是起点实际用起来还得调。首先是关闭省电模式无线网卡默认会进 PSM 省电状态作为 AP 时会导致响应延迟波动很大iw dev wlan0 set power_save off其次是带宽选择。2.4G 上千万别开 40MHzHT40因为整个 2.4G 频段才 83MHz 宽开 40M 意味着占用两个信道会和几乎所有邻居打架实际体验反而更差。5G 上 80MHzVHT80才是合理选择条件允许的话 160MHz 也可以试但需要网卡和驱动同时支持。第三是并发客户端数。hostapd 默认的max_num_sta是 2007实际上受限于网卡和 CPU。用树莓派这种单板机做 AP同时挂十几个客户端跑满带宽时CPU 会先到瓶颈因为整个数据转发路径要过一遍协议栈。这时候可以打开内核的net.core.netdev_max_backlog和调整txqueuelen但根本上还是换个性能好点的主机。还有个容易被忽略的点wlan0 和 eth0 的 MTU 不一致。如果上行是有线千兆MTU 1500无线侧也是 1500一般没事。但要是上行走了 PPPoE 之类的环境MTU 会被压到 1492客户端发大包时会出现能打开小网页、大文件下载卡死的诡异现象。用ip link show对比一下两边的 MTU必要时给 AP 侧调小。5. 几个折腾很久才搞明白的细节5.1 那些只有现场才会碰到的坑坑一ip addr flush忘了做。如果 wlan0 之前被 NetworkManager 或 dhcpcd 配过地址直接ip addr add会加上第二个地址而不是替换。结果是接口上同时挂着 192.168.1.x 和 192.168.50.1客户端拿到的网关可能是错的。养成先 down、再 flush、再加地址、最后 up的习惯顺序不能乱。坑二dnsmasq 和 systemd-resolved 抢 53 端口。症状很隐蔽——dnsmasq 服务显示 active但journalctl -u dnsmasq里有一行failed to create listening socket for port 53。它其实还在提供 DHCP只是 DNS 挂了表现就是能上网但域名解析不了。处理办法是给 dnsmasq 加bind-interfaces它就不会去抢 0.0.0.0:53只在 wlan0 上监听。坑三iptables 规则重启就没了。我一开始每次重启都要手动跑脚本后来才知道要么写iptables-save要么直接用 systemd unit 在启动时重建规则。这里不建议用iptables-persistent那种包因为服务器上其他服务也可能在改规则持久化会带来意外的规则冲突。用脚本重建虽然土但每一版规则都看得见。坑四hostapd 的配置文件权限。里面明文写着wpa_passphrase文件权限设成 644 的话任何用户都能读到。改成chmod 600并且确保 hostapd 服务以 root 运行。这条看似不起眼但在一台多用户共用的机器上很关键。5.2 嵌入式与 buildroot 环境的差异如果目标是一块嵌入式板子比如跑 Buildroot 的国产 SoC 平台思路一样但细节全变了。内核配置层面必须打开这几项CONFIG_CFG80211、CONFIG_MAC80211、CONFIG_NL80211_TESTMODE对应无线子系统和 hostapd 需要的 netlink 接口。USB 无线模块比如 AIC8800D40 这类还得额外把厂商驱动编进内核或作为外部模块编译同时把.bin固件文件打到 rootfs 的/lib/firmware对应目录下Buildroot 里用BR2_ROOTFS_OVERLAY挂一个 overlay 目录最方便。用户空间包要选BR2_PACKAGE_HOSTAPD、BR2_PACKAGE_HOSTAPD_DRIVER_NL80211、BR2_PACKAGE_DNSMASQ、BR2_PACKAGE_IPTABLES或者BR2_PACKAGE_NFTABLES、BR2_PACKAGE_IW。hostapd 在 Buildroot 里会依赖 libnl 和 openssl或者 mbedtls构建时如果报 openssl 找不到往往是BR2_PACKAGE_OPENSSL没勾上。还有个很实用的技巧用 mac80211_hwsim 做无硬件验证。它是内核自带的虚拟无线驱动能凭空造出几个假的无线接口支持 AP 模式。在板子上还没接真网卡、或者调试驱动之前先用它把整套 hostapd dnsmasq 流程跑通确认配置逻辑没错再换真硬件能省大量时间modprobe mac80211_hwsim radios2 iw dev # 会看到 wlan0 和 wlan1两个虚拟接口之间可以互相连接一个做 AP 一个做 STA用来验证配置是否正确非常方便。5.3 关于调试节奏的一点个人经验折腾这类配置我最大的体会是别想着一口气把全流程配完再测。正确节奏是层层递进先让 hostapd 起来用手机搜到 SSID 就算这一层过了再配 dnsmasq看到手机拿到 IP 算过了再开转发能 ping 通外网算过了最后加 NAT能打开网页算过了。每过一层存一次配置出问题时退回上一版比对着几十行配置猜要快得多。另外journalctl -u hostapd -f和dmesg -w这两个窗口建议一直开着。hostapd 的日志非常诚实几乎所有握手失败、信道切换、认证错误都会打出来只是很多人不去看。我遇到过最离谱的一次是客户端的加密方式和 AP 不匹配日志里就一行WPA: 4-Way Handshake failed看了日志两分钟解决不看的话能猜一天。现在这套配置我基本是模板化保存的换机型时只改interface、hw_mode、channel三个值就能跑。真正花时间的从来不是配置本身而是驱动和硬件的适配——那部分每个平台都不一样只能一单一单地趟。