一、内核补丁
因为Linux内核会在注册特定设备时将会将dev->priv_flags置为IFF_DONT_BRIDGE ,所以现还不支持sta、p2p_client、adhoc等无线接口加入到桥接中去的,所以要支持WLAN接口桥接需要修改内核代码,如下内核补丁以3.10.63内核为例,其他版本类似,找到对应函数修改即可。
1.源文件 linux-3.10.63/net/wireless/util.c,
函数cfg80211_change_iface()
,注释掉以下部分
2.源文件 linux-3.10.63/net/ wireless/util.c,
函数cfg80211_change_iface()
,注释掉以下部分
3.源文件 linux-3.10.63/net/wireless/core.c,
函数 cfg80211_netdev_notifier_call()
,注释掉以下部分
4.源文件 linux-3.10.63/net/bridge/br_if.c,
函数 br_port_carrier_check()
,
注释掉判断语句里netif_oper_up(dev)
部分
5.源文件 linux-3.10.63/net/bridge/br_if.c,
函数br_add_if()
,注释掉判断语句里 netif_oper_up(dev)
部分
6.源文件 linux-3.10.63/net/bridge/br_notify.c,
函数br_device_event()
,
注释掉判断语句里netif_oper_up(dev)
部分
7.源文件 linux-3.10.63/net/bridge/br_stp_if.c,
函数 br_stp_enable_bridge()
,
注释掉判断语句里 netif_oper_up(p->dev)
部分
二、转发设置
工作原理:
内网主机向公网发送数据包时,由于目的主机跟源主机不在同一网段,所以数据包暂时发往内网默认网关处理,而本网段的主机对此数据包不做任何回应。由于源主机ip是私有的,禁止在公网使用,所以必须将数据包的源发送地址修改成公网上的可用ip,这就是网关收到数据包之后首先要做的工作–ip转换。然后网关再把数据包发往目的主机。目的主机收到数据包之后,只认为这是网关发送的请求,并不知道内网主机的存在,也没必要知道,目的主机处理完请求,把回应信息发还给网关。网关收到后,将目的主机发还的数据包的目的ip地址修改为发出请求的内网主机的ip地址,并将其发给内网主机。这就是网关的第二个工作–数据包的路由转发。内网的主机只要查看数据包的目的ip与发送请求的源主机ip地址相同,就会回应,这就完成了一次请求。
出于安全考虑,Linux系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将包发往本机另一网卡,该网卡根据路由表继续发送数据包。这通常就是路由器所要实现的功能。
设置
Linux系统缺省并没有打开IP转发功能,要确认IP转发功能的状态,可以查看/proc文件系统,使用下面命令: cat /proc/sys/net/ipv4/ip_forward
如果上述文件中的值为0,说明禁止进行IP转发;如果是1,则说明IP转发功能已经打开。
可以直接修改上述文件打开IP转发功能:echo 1 > /proc/sys/net/ipv4/ip_forward
上面的命令并没有保存对IP转发配置的更改,下次系统启动时仍会使用原来的值,要想永久修改IP转发,需要修改/etc/sysctl.conf
文件,修改下面一行的值: net.ipv4.ip_forward = 1
修改后可以重启系统来使修改生效,
也可以执行下面的命令来使修改生效: sysctl -p /etc/sysctl.conf
进行了上面的配置后,IP转发功能就永久使能了
三、建立桥接
网络桥接命令 brctl,BusyBox 包含 brctl 命令,在配置编译 BusyBox 时选上即可。
以sta(wlan0)和softap(wlan1)桥接为例,
#创建网桥 br0
brctl addbr br0
#把wlan0 wlan1添加到网桥 br0
brctl addif br0 wlan0
brctl addif br0 wlan1
#将3个接口up起来
ifconfig br0 up
ifconfig wlan0 up
ifconfig wlan1 up
#在wlan0起wpa_supplicant
wpa_supplicant -iwlan0 -Dnl80211 -c wpa_supplicant.conf -b br0
#为br0获取ip
dhclient br0
#在wlan1起softap hostapd.conf配置有 bridge=br0
hostapd -iwlan1 -c hostapd.conf
#查看网桥信息
brctl show
#删除网桥
brctl delbr br0
#更多内容使用 brctl--help 查看
这样建立完桥接后,br0 MAC为wlan0、1中MAC地址较小一个,原理可以查看 br_add_if时调用的 br_stp_recalculate_bridge_id()
函数,此时驱动需要对转发的DHCP包进行广播、ARP包修改target HWaddr、IP包修改MAC地址等等,此时连着softap的设备通过sta连着的路由分配IP,二者再同一网段,相当于wlan0与wlan1下的设备共享同一ap。
三、内核获取网络设备的网桥接口
#ifdef CONFIG_BR_EXT
void netdev_br_init(struct net_device *netdev)
{
//内核 netdev_priv
_adapter *adapter = (_adapter *)rtw_netdev_priv(netdev);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
rcu_read_lock();
#endif
/* if(check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE) == _TRUE) */
{
/* struct net_bridge *br = netdev->br_port->br; */ /* ->dev->dev_addr; */
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
if (netdev->br_port)
#else
//在添加桥接口时(br_add_if),调用函数(netdev_rx_handler_register)注册rx_handler,将网桥接口赋值给rx_handler_data变量
if (rcu_dereference(adapter->pnetdev->rx_handler_data))
#endif
{
struct net_device *br_netdev;
//内核函数dev_get_by_name
br_netdev = rtw_get_bridge_ndev_by_name(CONFIG_BR_SUPPORT_BRNAME);
if (br_netdev) {
memcpy(adapter->br_mac, br_netdev->dev_addr, ETH_ALEN);
dev_put(br_netdev);
RTW_INFO(FUNC_NDEV_FMT" bind bridge dev "NDEV_FMT"("MAC_FMT")\n"
, FUNC_NDEV_ARG(netdev), NDEV_ARG(br_netdev), MAC_ARG(br_netdev->dev_addr));
} else {
RTW_INFO(FUNC_NDEV_FMT" can't get bridge dev by name \"%s\"\n"
, FUNC_NDEV_ARG(netdev), CONFIG_BR_SUPPORT_BRNAME);
}
}
adapter->ethBrExtInfo.addPPPoETag = 1;
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
rcu_read_unlock();
#endif
}
#endif /* CONFIG_BR_EXT */
Linux内核中有两种获取网络设备的桥接口方法,其中一种是在控制路径上;另一种是在数据路径上。
https://blog.youkuaiyun.com/sinat_20184565/article/details/80390514