背景
这两天突然想在自己的树莓派4b(rpi4)上搞一点小开发,需要rpi4实现wifi热点,让其他设备连接rpi4,同时又要能够桥接到自己家里的局域网。网上搜了一圈发现都是“有线网+AP”或者是需要额外增加一块usb无线网卡的方案,都不能满足我既不想用有线也不想额外买USB无线网卡的最基本需求。查询了很久都没有合适的方案,期间曾经一度放弃,甚至觉得一张wifi网卡在linux上理论上是行不通的。
后来突然想到openwrt,里面查看一下家里安装openwrt的TPlink路由器,发现居然也只有一张网卡。顿时又来了信心,能不能在rpi4上通过安装openwrt实现呢?想了想还是不够完美,比较openwrt是偏向路由器的系统,路由器功能太强,其他功能使用起来比较麻烦,而我的需求并不需要那么强的路由器功能,只需要能够让其他设备通过wifi访问rpi4,同时rpi4能正常访问internet就行。还是继续研究研究吧,都是基于linux的系统,rpi的系统不可能不行的。
继续研究openwrt的实现,发现openwrt是通过虚拟网卡的方式在无线网卡的基础上虚拟一块网卡的方式实现,那么怎么虚拟网卡呢?openwrt上看不出来,通过ip link的新增虚拟网卡的方式也行不通。在百思不得其解的时候无意间搜索到create_ap这个开源项目。create_ap真是一个让人惊喜的东西,居然能满足我所有的需求,短短一千多行的shell,操作极为简单,太棒了!
从github上clone了create_ap 在本地的x86上试用,非常完美,没有问题。但上传rpi4上后不能用,出现brmfmac网卡驱动不支持,导致不能使用虚拟网卡的问题,心里顿时凉了半截。查看了提示的信息地址github.com/oblique/create_ap/issues/203,发现原来的brmfmac的网卡驱动在试用虚拟网卡是会导致内核内存泄漏,所以create_ap不支持。仔细看了bug分析之后有一段话引起了我的兴趣:
根据这段说明,发现原来功能是可以用的,只不过create_ap为了避免问题所以不建议试用。从另一个角度来看,还是可以通过自己编写脚本实现功能的,所有就参考create_ap自己写了一个在RPI上的实现原生无线网卡的wifi桥接功能的脚本。
软件准备
WIFI连接和管理工具:iw,wpa_supplicant
AP热点服务软件:hostapd
DHCP服务软件:dnsmasq
软件安装和配置
- 安装工具软件
sudo apt install iw wpa_supplicant hostapd dnsmasp
- 配置wpa_supplicant连接wlan0的ssid和密码
(wpa_supplicant其实不是必须的,只有在你需要连接WPA-PSK或者WPA-PSK2加密方式的wifi网络才需要,其他的通过iw就可以直接连接)在/etc/wpa_supplicant/wpa_supplicant.conf 文件中添加需要连接wifi的SSID和密码。
network={
ssid="MY-SSID" // SSID名字
psk="" //wifi连接密码
key_mgmt=WPA-PSK
}
- 配置hostapd
RPI下下载的hostapd默认没有配置文件,需要自行增加hostapd.conf
interface=ap0
driver=nl80211
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
ssid=RPI-AP
utf8_ssid=1
country_code=CN
hw_mode=g
channel=6
auth_algs=1
ieee80211n=1
wpa=2
wpa_passphrase=12345678
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
rsn_pairwise=CCMP
- 配置dnsmasq,配置文件dnsmasq.conf如下:
listen-address=192.168.12.1
bind-dynamic
dhcp-range=192.168.12.1,192.168.12.254,255.255.255.0,24h
dhcp-option-force=option:router,192.168.12.1
dhcp-option-force=option:dns-server,192.168.12.1
dhcp-option-force=option:mtu,1500
no-hosts
- 配置防火墙规则
由于做nat转换需要使用到iptables进行ip forward的,所以需要准备好防火墙规则文件,如下:
配置桥接脚本
服务软件安装完之后需要整合各个软件实现wifi桥接功能。具体思路为:在wlan0的无线网卡上虚拟一个ap0的网卡(网卡需要支持ap mode)。然后将hostapd服务在ap0端口上启动,实现在ap0接口上的wifi热点接入。之后再在ap0接口上启动hdsmasq,实现在ap0接口上的HDCP服务。配置iptables的Nat规则,实现ap0通过Nat访问Wlan0的外网。最后在wlan0上连接外网wifi。
- 检查物理网卡是否支持AP Mode
要实现wifi热点接入,需要建立在无线网卡的ap功能之上。前提是要求物理网卡支持ap mode模式,所以先用iw查看主机的网卡是否支持ap mode:
sudo iw phy phy0 info
查看Supported interface modes
项。如果有AP
则为支持ap mode,否则网卡硬件不支持ap mode 则理论上无法实现wifi热点,幸运的是RPI4的bcm网卡是支持ap mode的。
- 新增ap0虚拟网卡
如果物理网卡支持Ap mode模式,则可以通过iw命令新增虚拟的ap0网卡。
iw dev wlan0 interface add ap0 type __ap
#在设备wlan0的基础上增加 ap模式的网卡ap0
如果新增成功则通过会出现一个和wlan0并列的网络接口ap0:
- 停止原有在ap0上的wpa_supplican进程
由于iw增加 ap0 接口后,会在ap0接口上启动wpa_supplican进程连接wifi,但由于ap0接口用于热点接入,是不需要连接wifi的,所以需要将这个wpa_supplican进程kill掉。
sleep 5
# 由于新增ap网卡为异步操作,所以需要暂停几秒等待新增ap0之后,启动对应的wpa_supplincan进程。
array=(ps -ef | grep "wpa_supplican" | grep iap0 | sed 's/[ ][ ]*/\n/g'`)
if [ array[1] > 0 ]; then
echo ${array[1]}
kill -9 ${array[1]}
fi
- 配置ap0接口ip地址
ip addr add 192.168.12.1/24 dev ap0
- 启动dnsmasq服务
dnsmasq -C /home/pi/ap_init/dnsmasq.conf
-x /tmp/dnsmasq.pid -l dnsmasq.leases -p 5353
- 启动内核ip转发
echo "1" > /proc/sys/net/ipv4/ip_forward #配置内核参数允许内核ip转发
- 配置防火墙nat规则
iptables-restore < /home/pi/ap_init/iptables.conf #从之前的防火墙规则导入
- 在ap0上启动hostapd热点接入服务进程
/usr/sbin/hostapd -B -P /tmp/hostapd.pid \
/home/pi/ap_init/hostapd.conf >> /home/pi/ap_init/hostapd.log &
在其他设备上查看是否有一个SSID为RPI-AP的wifi接入点。
- 重启wlan0上的wpa_supplican进程
array=(ps -ef | grep "wpa_supplican" | grep wlan0 | sed 's/[ ][ ]*/\n/g'`)
if [ array[1] > 0 ] && [ "IFNULL${array[1]}" != "IFNULL" ]; then
echo "heool${array[1]}"
kill -9 ${array[1]}
fi
wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211,wext
if [ $? != 0 ]; then
echo "wlan0 connect failed!"
exit 1
fi
如果一切顺利wlan0已经能成功连上外网的wifi了,并且分配到相应的ip地址。
到此已经完成了相关功能软件的配置和启动,已经能够实现RPI的wifi桥接功能,其他设备通过SSID:RPI-AP就可以通过的网络桥接联网了。如果需要RPI开机就启动wifi桥接可以新增相应的RPI Service 并enable实现开机启动。
配置开机启动服务
- 编写相应的Service配置文件
在/lib/systemd/system/下新建rpi_ap.service的systemd配置文件。
- 重载服务配置
执行sudo systemctl daemon-reload
命令重载服务配置。
- 执行
sudo systemclt start rpi_ap
启动rpi_ap的systemd服务 - 执行
sudo systemclt enable rpi_ap
配置rpi_ap服务开机启动
wifi桥接配置脚本和rpi_ap服务配置文件
- ap_start: //wifi桥接启动脚本
#! /bin/bash
ap0=`ip addr | grep ap0:`
if [ "IFNULL$ap0" != "IFNULL" ]; then
iw dev ap0 del
if [ $? != 0 ]; then
echo "del exist apo iface failed!"
exit 1
fi
fi
sleep 1
iw dev wlan0 interface add ap0 type __ap
if [ $? != 0 ]; then
echo "add vitrul inerface failed!"
exit 1
fi
sleep 5 #由于新增ap网卡为异步操作,所以需要暂停几秒等待新增ap0之后,启动对应的wpa_supplincan进程。
array=(`ps -ef | grep "wpa_supplican" | grep iap0 | sed 's/[ ][ ]*/\n/g'`)
if [ array[1] > 0 ]; then
echo ${array[1]}
kill -9 ${array[1]}
fi
ip addr add 192.168.12.1/24 dev ap0
dnsmasq -C /home/pi/ap_init/dnsmasq.conf -x /tmp/dnsmasq.pid -l dnsmasq.leases -p 5353
if [ $? != 0 ]; then
echo "dns start failed!"
exit 1
fi
echo "1" > /proc/sys/net/ipv4/ip_forward #配置内核参数允许内核ip转发
if [ $? != 0 ]; then
echo "start ip_forward failed!"
exit 1
fi
iptables-restore < /home/pi/ap_init/iptables.conf
if [ $? != 0 ]; then
echo "iptables config failed!"
exit 1
fi
/usr/sbin/hostapd -B -P /tmp/hostapd.pid /home/pi/ap_init/hostapd.conf >> /home/pi/ap_init/hostapd.log &
if [ $? != 0 ]; then
echo "hostapd start failed!"
exit 1
fi
array=(`ps -ef | grep "wpa_supplican" | grep wlan0 | sed 's/[ ][ ]*/\n/g'`)
if [ array[1] > 0 ] && [ "IFNULL${array[1]}" != "IFNULL" ]; then
echo "heool${array[1]}"
kill -9 ${array[1]}
fi
wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211,wext
if [ $? != 0 ]; then
echo "wlan0 connect failed!"
exit 1
fi
- ap_stop: //wifi桥接停止脚本
#! /bin/bash
ap0=`ip addr | grep ap0:`
if [ "IFNULL$ap0" != "IFNULL" ]; then
iw dev ap0 del
if [ $? != 0 ]; then
echo "del exist apo iface failed!"
exit 1
fi
fi
- rpi_ap.service: //rpi_ap的systemd服务配置文件
[Unit]
Description=RPI AP share server by laoflch
After=network.target
[Service]
Type=oneshot
#PIDFile=/run/rpi_ap.pid
#Restart=on-failure
#RestartSec=2
RemainAfterExit=yes
ExecStart=/home/pi/ap_init/ap_start
ExecStop=/home/pi/ap_init/ap_stop
[Install]
WantedBy=multi-user.target