当一个网络接口接收到网络数据包后,网桥会将该数据包复制并发送给连接到它的其他网络接口
这种机制使得Linux网桥在容器间通信、虚拟网络构建等方面有着广泛的应用,例如Docker就是使用Linux网桥来实现容器间的通信
本文将深入探讨Linux网桥的源码实现,以揭示其内部工作原理和关键数据结构
通过对源码的分析,我们将更好地理解Linux网桥如何在Linux内核中高效、可靠地运作
一、Linux网桥的调用流程 Linux网桥的调用流程始于网络数据包的接收
在Linux内核中,网络数据包的接收和处理是由软中断函数`net_rx_action`来完成的
这个函数位于`src/net/core/dev.c`文件中,负责处理接收到的网络数据包
static voidnet_rx_action(struct softirq_action h) { // ... #if defined(CONFIG_BRIDGE) ||defined(CONFIG_BRIDGE_MODULE) if(skb->dev->br_port!= NULL && br_handle_frame_hook!= NULL) { handle_bridge(skb, pt_prev); dev_put(rx_dev); continue; } #endif // ... } 在上述代码中,如果系统定义了网桥或网桥模块(`CONFIG_BRIDGE`或`CONFIG_BRIDGE_MODULE`),并且接收到的数据包是从网桥端口(`skb->dev->br_port`)接收到的,那么`handle_bridge`函数将被调用,来处理该数据包
`handle_bridge`函数是一个内联函数,其定义如下: static __inline__ int handle_bridge(structsk_buff skb, struct packet_type pt_prev) { int ret =NET_RX_DROP; // ... if(pt_prev) { // 处理pt_prev相关逻辑 } br_handle_frame_hook(skb); return ret; } 在`handle_bridge`函数中,首先会处理一些与`pt_prev`相关的逻辑,然后调用`br_handle_frame_hook`钩子函数来处理数据包
`br_handle_frame_hook`是网桥处理函数的核心,它将在后续的步骤中被详细解析
二、钩子函数的注册与初始化 `br_handle_frame_hook`是一个钩子函数,用于处理网桥相关的数据包
这个钩子函数在网桥的初始化过程中被注册
网桥的初始化函数位于`net/bridge/br.c`文件中,名为`br_init`
static int__initbr_init(void){ printk(KERN_INFO NET4: Ethernet Bridge 008 for NET4.0n); br_handle_frame_hook = br_handle_frame; // ... return 0; } 在`br_init`函数中,`br_handle_frame_hook`被设置为指向`br_handle_frame`函数
`br_handle_frame`是网桥处理数据包的核心函数,它负责根据数据包的MAC地址和其他信息,决定如何转发或处理该数据包
三、网桥处理函数`br_handle_frame` `br_handle_frame`函数位于`br_input.c`文件中,是网桥处理数据包的核心逻辑所在
void br_handle_frame(structsk_buff skb) { structnet_bridge br; unsignedchar dest; structnet_bridge_port p; // 获取目的MAC地址 dest = skb->mac.ethernet->h_dest; // 获取接收数据包的网桥端口 p = skb->dev->br_port; if(p == NULL) { // 端口不是网桥组端口中的一部分 gotoerr_nolock; } // 获取本端口所属的网桥组 br = p->br; // 加锁,避免在转发过程中修改CAM表 read_lock(&br->lock); // 检查网桥端口和网桥设备状态 if(skb->dev->br_port == NULL|| !(br->dev.flags &IFF_UP) || p->state == BR_STATE_DISABLED) { goto err; } // 检查源MAC地址,如果是多播或广播地址,则丢弃该数据包 if(skb->mac.ethernet->h_source【0】 & 1) { goto err; } // 如果网桥处于学习或转发状态,则学习该数据包的源地址,并将其添加到CAM表中 if(p->state == BR_STATE_LEARNING || p->state ==BR_STATE_FORWARDING) { br_fdb_insert(br, p, skb->mac