bridge 是linux上的虚拟交换机,具有交换机的功能。
网卡收到包后,走到__netif_receive_skb_core后,剥完vlan找到vlan子接口(如果有的话),如果skb->dev是bridge成员口,就会走到bridge成员口的接收处理函数。
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
......
/*
bridge、ovs的接口,都会走到。
如果一个dev被添加到一个bridge(做为bridge的一个接口),这个接口设备的rx_handler将被设置为,
br_handle_frame函数这是在br_add_if函数中设置的,而br_add_if (net/bridge/br_if.c)是在向
网桥设备上添加接口时设置的。进入br_handle_frame也就进入了bridge的逻辑代码。*/
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (rx_handler) {
if (pt_prev) {
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = NULL;
}
switch (rx_handler(&skb)) {
case RX_HANDLER_CONSUMED: // 报文已经被消费,结束处理
ret = NET_RX_SUCCESS;
goto out;
case RX_HANDLER_ANOTHER: // skb->dev 被修改,重新走一次
goto another_round;
case RX_HANDLER_EXACT: /* 精确传递到ptype->dev == skb->dev */
deliver_exact = true;
case RX_HANDLER_PASS:
break;
default:
BUG();
}
}
......
}
bridge 的接收处理函数为br_handler_frame,在为bridge添加接口操作时,如brctl addif命令行将一个接口加入到bridge中,就会为接口的 net_device挂载此处理函数。
int br_add_if(struct net_bridge *br, struct net_device *dev)
{
......
err = netdev_rx_handler_register(dev, br_handle_frame, p);
if (err)
goto err4;
......
}
int netdev_rx_handler_register(struct net_device *dev,
rx_handler_func_t *rx_handler,
void *rx_handler_data)
{
ASSERT_RTNL();
if (dev->rx_handler)
return -EBUSY;
/* Note: rx_handler_data must be set before rx_handler */
rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);
rcu_assign_pointer(dev->rx_handler, rx_handler);
return 0;
}
bridge接收处理函数br_handle_frame,非linklocal地址的情况下,主要做:
1、ebtables 的BROUTING 表处理,这是在bridge协议栈中将二层转发切换到主机协议栈的三层转发的hook点;
2、bridge NF_BR_PRE_ROUTING hook点处理,配置net.bridge.bridge-nf-call-iptables 系统配置的情况下还会 调用iptables规则处理;
3、进br_handle_frame_finish函数,根据src mac学习fdb表项,继续做转发处理或本机报文上送三层协议栈。
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
br_should_route_hook_t *rhook;
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto drop;
// 如果skb被其它流程共享,clone一份
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return RX_HANDLER_CONSUMED;
p = br_port_get_rcu(skb->dev);
if (unlikely(is_link_local_ether_addr(dest))) {
u16 fwd_mask = p->br->group_fwd_mask_required;
/*
* See IEEE 802.1D Table 7-10 Reserved addresses
*
* Assignment Value
* Bridge Group Address 01-80-C