Linux 系统开启无线共享,使用同一个无线网卡,可以实现 “wifi -> wifi热点” 的共享。Linux 发行版为 Arch Linux,init 是 systemd。另外,使用这里的配置,能够把任意网络连接共享到 wifi 热点,比如“有线 -> wifi热点”、“USB网卡 -> wifi热点”等等。


0、查看基本信息

运行iw list查看输出信息

Supported interface modes:
    * AP
...
valid interface combinations:
    * #{ managed } <= 2048, #{ AP, mesh point } <= 8, #{ P2P-client, P2P-GO } <= 1,
        total <= 2048, #channels <= 1, STA/AP BI must match
  • * AP表明网卡支持开启热点。
  • #{AP,mesh point} <= 8,表示网卡可以在连接其他热点的同时开启热点,最多可有8对(具体含义我不是很清楚)。
  • #channels <= 1,表示网卡最多能够在一个频道上同时工作。(我这的网卡只有单天线,一些多天线网卡可以同时在多个频道工作)

得到了上面的网卡硬件信息,接下来运行iwconfig,查看网络接口信息

eth0 : no wireless extensions.

wlan0 : flags<...>,mtu ...
    inet ...
    Rx...
    Tx...

lo : no wireless extensions.

默认的无线接口是wlan0

1、新建虚拟无线网络接口(interface)

终端执行iw list,第一行输出无线网卡名称

Wiphy phy0

得到物理无线网卡名称phy0,然后新建此无线网卡上的接口为wlan0_apaddr指定此接口的 MAC 地址(可根据具体需要自行指定)。运行下面的命令

# iw phy phy0 interface add wlan0_ap type managed addr 12:34:56:ab:cd:ef

再解除NetworkManager对这个接口的管理,修改/etc/NetworkManager/NetworkManager.conf,在[Keyfile]下增加这一行unmanaged-devices=mac:mac:后面填写刚刚的 MAC 地址。

2、配置hostapd

hostapd 用以指定建立热点的具体设置,加载设备驱动等。新建或编辑/etc/hostapd/hostapd.conf(hostapd配置文件,具体目录根据情况决定)

ssid=Arch_Linux
utf8_ssid=1
wpa_passphrase=archlinux
interface=wlan0_ap

auth_algs=3
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP CCMP
rsn_pairwise=CCMP

driver=nl80211
country_code=UK
hw_mode=g
ieee80211n=1
wmm_enabled=1
channel=6
ht_capab=[HT40+][SHORT-GI-20][SHORT-GI-40]
  • ssid=Arch_Linux填写热点名称为"Arch_Linux"。uft8_ssid=1,utf8 编码热点名称,避免在有些设备上出现乱码。
  • wpa_passphrase密码
  • interface=wlan0_ap填写刚刚新建的那个虚拟接口"wlan0_ap"
  • driver填写使用的驱动,可以通过网卡型号 google 查找 linux 需要的驱动。
  • hw_mode=g热点使用 802.11g 模式。(即使是使用 n 模式也需要填写为g,因为 n 模式是在 g 模式的基础上进行展频)
  • ieee80211n=1wmm_enabled=1,保证能够使用 n 模式。
  • channel使用频道,ht_capab是展频相关设置,关于二者的具体关系可查看 hostapd 官方文档

注:若channel <= 1请看这里

在“0、查看基本信息”中,如果#channels大于1,则不需要这部分配置;小于等于1则需要,保证频道(channel 选项)一致,否则开启的热点和已经连上的 wifi 会发生频道冲突,二者随机掉线。
新建保存/etc/hostapd/pre-hostapd.sh如下,然后 root 运行chmod +x /etc/hostapd/pre-hostapd.sh添加执行权限。

#!/bin/bash
WIFI_STAT=`iw dev wlan0 link`
HOSTAPD_CONF="/etc/hostapd/hostapd.conf"
CHANNEL_CONN=`iw dev wlan0 info | grep channel | awk '{print $2}'`

# delete previous channel settings
sed -ie '/channel/d' $HOSTAPD_CONF
sed -ie '/ht_capab/d' $HOSTAPD_CONF

if [[ $WIFI_STAT == "Not connected." ]]; then
    echo "channel=6" >> $HOSTAPD_CONF;
    echo "ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]" >> $HOSTAPD_CONF

elif (( $CHANNEL_CONN >= 1 && $CHANNEL_CONN <= 7 )); then
    echo "channel=$CHANNEL_CONN" >> $HOSTAPD_CONF;
    echo "ht_capab=[HT40+][SHORT-GI-20][SHORT-GI-40]" >> $HOSTAPD_CONF;

elif (( $CHANNEL_CONN >= 5 && $CHANNEL_CONN <= 13 )); then
    echo "channel=$CHANNEL_CONN" >> $HOSTAPD_CONF;
    echo "ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]" >> $HOSTAPD_CONF;
fi;

3、配置内网DHCP服务

注意,这里使用的包名称是dhcpd,并不是dhcpcd!前者是服务端,后者是客户端!

  1. 新建或编辑 dhcpd 配置文件/etc/dhcpd.conf如下
option domain-name-servers 8.8.8.8, 8.8.4.4;
option routers 10.0.0.100;
option subnet-mask 255.255.255.0;
subnet 10.0.0.0 netmask 255.255.255.0 {
  range 10.0.0.150 10.0.0.250;
}
  • domain-name-servers指定dns服务器,根据需要自行指定。
  • routers将虚拟网卡wlan0_ap的内网地址指定为10.0.0.100(请务必使用保留地址以避免冲突)。
  • subnet-mask指定掩码,决定内网ip范围。
  • range指定实际可用的内网ip范围。
  1. 运行# ip addr add 10.0.0.100/24 dev wlan0_ap,为虚拟网卡绑定内网地址10.0.0.100(和上面指定的地址要一致)。

  2. dhcpd 默认在所有接口生效,这里只需要它在虚拟接口生效。新建文件/etc/systemd/system/dhcpd4@.service如下,保存后运行# systemctl daemon-reload生效

[Unit]
Description=IPv4 DHCP server on %I
Wants=network.target
After=network-pre.target
Before=network.target

[Service]
Type=forking
PIDFile=/run/dhcpd4.pid
ExecStartPre=/usr/bin/ifconfig %I up
ExecStart=/usr/bin/dhcpd -4 -q -pf /run/dhcpd4.pid %I
KillSignal=SIGINT

[Install]
WantedBy=multi-user.target

4、配置iptables NAT转发

iptables -t nat -A POSTROUTING -j MASQUERADE
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED
iptables -A FORWARD -i wlan0_ap -j ACCEPT
iptables-save > /etc/iptables/iptables.rules

root 执行以上命令保存 iptables 规则。注意,这里的规则实际上是“全能”的共享方式,不仅保证“wifi -> wifi热点”的共享,也能够“有线 -> wifi热点”、“USB网卡 -> wifi热点”等方式共享。

5、开启内核转发功能

运行# sysctl net.ipv4.ip_forward=1。在 Arch Linux 中需要将这个规则写入文件,防止重启失效。把net.ipv4.ip_forward=1写入/etc/sysctl.d/30-ipforward.conf文件。

6、保存虚拟接口信息

Arch Linux 使用 udev 来加载设备,因此每次重启后,之前新建的虚拟接口会失效。可以用一个 systemd 服务模块来解决问题。新建文件/etc/systemd/system/iw-interface-add@.service如下,保存后运行# systemctl daemon-reload生效

[Unit]
Description=Add interface %i
Wants=network.target
Before=network.target

[Service]
Type=oneshot
ExecStartPre=/usr/bin/iw phy phy0 interface add %i type managed addr 12:34:56:ab:cd:ef
ExecStart=/usr/bin/ip addr add 10.0.0.100/24 dev %i

[Install]
WantedBy=multi-user.target

再修改之前创建的 /etc/systemd/system/dhcpd4@.service如下,保存后运行# systemctl daemon-reload生效

[Unit]
Description=Add interface %i and start IPv4 DHCP server on it
Wants=network.target
After=iw-interface-add@%i.service network-pre.target
Before=network.target
Requires=iw-interface-add@%i.service

[Service]
Type=forking
PIDFile=/run/dhcpd4.pid
ExecStartPre=/usr/bin/ifconfig %i up
ExecStart=/usr/bin/dhcpd -4 -q -pf /run/dhcpd4.pid %i
KillSignal=SIGINT

[Install]
WantedBy=multi-user.target

7、整合服务

为了方便,可将所有启动命令整合进 hostapd 的 systemd 模块。编辑/usr/lib/systemd/system/hostapd.service如下,保存后运行# systemctl daemon-reload使修改生效。

[Unit]
Description=Hostapd IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator
After=network.target
Requires=dhcpd4@wlan0_ap.service iptables.service
BindsTo=dhcpd4@wlan0_ap.service iptables.service

[Service]
ExecStartPre=/etc/hostapd/pre-hostapd.sh
ExecStart=/usr/bin/hostapd /etc/hostapd/hostapd.conf
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
  • 其中ExecStartPre=/etc/hostapd/pre-hostapd#channel <= 1需要的配置,若大于1则需要删除这一行。

现在运行# systemctl start hostapd.service可立即开启 wifi 共享。