1. 介绍
关于网桥相关知识,可参考<网桥技术介绍>
我们需要了解的是网桥是一种工作在数据链路层的存储转发设备,用来连接局域网,基于MAC地址来进行端口转发。
比较常见的即透明网桥(Transparent Bridge)
1.1 功能
网桥的作用主要如下
- 源地址跟踪: 网桥通过转发表来记录网桥所能见到的连接站点地址, 为帧的转发提供了路径选择
- 帧的转发和过滤: 网桥可以对帧进行转发或者过滤
1.2 工作原理
如下图所示,br0是一个网桥,拥有两个端口eth0和eth1
对于协议栈上层的来说,只看得到br0,因为桥接是在数据链路层实现的,上层不需要关心桥接的细节
于是协议栈上层将需要发送的报文被送到br0,网桥设备来判断报文该被转发到eth0或是eth1,或者两者皆是;
反过来,从eth0或从eth1接收到的报文被提交给网桥来处理
在这里会判断报文该转发、丢弃、或提交到协议栈上层
而有时候eth0、eth1也可能会作为报文的源地址或目的地址,直接参与报文的发送与接收(从而绕过网桥)。
2. 实现
2.1 数据结构
在Linux中,网桥的主要数据结构有
- net_bridge: 表示网桥
- net_bridge_port: 则表示网桥中的某个端口, 一般都是某个物理接口
- net_bridge_fdb_entry: 保存了MAC地址->端口映射对(hash表), 以数组的方式存储在net_bridge结构中
数据结构之间的关系如下图所示
2.2 数据处理
在v2.6.36以前, 网桥对于数据包的处理过程是通过直接调用hook函数br_handle_frame_hook()来进行
在v2.6.36以后,不再是使用hook函数的方式,而是以注册事件的方式netdev_rx_handler_register()
对不同设备的rx处理进行注册,在netif_receive_skb函数处理过程中,会将该设备的所有事件函数一一调用
而网桥端口注册的处理函数正是br_handle_frame()
故实际上网桥数据的处理入口点就是br_handle_frame()函数
br_handle_frame主要流程如下
1. 首先进行一些的检查 2. 判断是否本地链路保留地址,满足调用br_handle_local_finish()简单更新转发表就结束处理 3. 而对于非特殊链路地址,根据端口的状态决定对数据的处理(如下),最后调用br_handle_frame_finish() - LEARNING: 只是简单更新转发表,就丢弃报文。 - FORWARDING: 进行ebtables hook处理
br_handle_frame_finish()会判断数据的目的地,对于发往本地的地址调用br_pass_frame_up();需要转发的数据则通过br_forward()来完全网桥转发
注意:br_pass_frame_up()会再次调用netif_receive_skb(),但是此时不会再进入网桥处理,而是直接进行协议栈进行处理(因为此时skb->dev已经不是网卡设备了,而是网桥设备)
3. 使用
网桥在用户空间的工具室是通过bridge-util软件包,它提供了brctl命令来完成对网桥的使用
一个电脑上有四个网卡,下面我们把这四个端口加入到同一个桥中
首先新建一个网桥br0
# brctl addbr br0
将四个物理网卡加入网桥br0作为端口
# brctl addif br0 eth0
# brctl addif br0 eth1
# brctl addif br0 eth2
# brctl addif br0 eth3
网桥的每个物理网卡作为一个端口,运行于混杂模式,而且是在链路层工作,所以就不需要IP了
# ifconfig eth0 0.0.0.0 # ifconfig eth1 0.0.0.0 # ifconfig eth2 0.0.0.0 # ifconfig eth3 0.0.0.0
然后给br0的虚拟网卡配置IP:192.168.1.1
# ifconfig br0 192.168.1.1
参考
<Linux网桥的实现分析与使用>