LInux下桥接模式详解三
上篇文章介绍了Linux内核桥接模式涉及到的几个结构,本节就重点放在数据包的处理上!
本节所有代码参考LInux 3.10.1内核!
前面已经提到一个数据包从网卡流到Linux内核中的L2层,最终被交付到__netif_receive_skb_core函数中,看下该函数中引用rx_hander的片段
1 rx_handler = rcu_dereference(skb->dev->rx_handler); 2 if (rx_handler) { 3 if (pt_prev) { 4 ret = deliver_skb(skb, pt_prev, orig_dev); 5 pt_prev = NULL; 6 } 7 switch (rx_handler(&skb)) { 8 case RX_HANDLER_CONSUMED: 9 ret = NET_RX_SUCCESS; 10 goto unlock; 11 case RX_HANDLER_ANOTHER: 12 goto another_round; 13 case RX_HANDLER_EXACT: 14 deliver_exact = true; 15 } 16 case RX_HANDLER_PASS: 17 break; 18 default: 19 BUG(); 20 }
可以看到这里首先从设备结构net_device中获取其rx_handler指针,该指针在网卡的混杂模式下指向一个处理函数叫做br_handle_frame,即网桥的处理流程
1 rx_handler_result_t br_handle_frame(struct sk_buff **pskb) 2 { 3 struct net_bridge_port *p; 4 struct sk_buff *skb = *pskb; 5 const unsigned char *dest = eth_hdr(skb)->h_dest;//获取skb的目的MAC 6 br_should_route_hook_t *rhook; 7 8 if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) 9 return RX_HANDLER_PASS; 10 11 if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) 12 goto drop; 13 14 skb = skb_share_check(skb, GFP_ATOMIC); 15 /*只有skb是共享的且clone的时候分配内存出错skb才会是null*/ 16 if (!skb) 17 return RX_HANDLER_CONSUMED; 18 19 p = br_port_get_rcu(skb->dev); 20 21 if (unlikely(is_link_local_ether_addr(dest))) { 22 /* 23 * See IEEE 802.1D Table 7-10 Reserved addresses 24 * 25 * Assignment Value 26 * Bridge Group Address 01-80-C2-00-00-00 27 * (MAC Control) 802.3 01-80-C2-00-00-01 28 * (Link Aggregation) 802.3 01-80-C2-00-00-02 29 * 802.1X PAE address 01-80-C2-00-00-03 30 * 31 * 802.1AB LLDP 01-80-C2-00-00-0E 32 * 33 * Others reserved for future standardization 34 */ 35 /*目的MAC 地址 ,判断是否是特殊的目的MAC地址*/ 36 switch (dest[5]) { 37 case 0x00: /* Bridge Group Address */ 38 /* If STP is turned off, 39 then must forward to keep loop detection */ 40 if (p->br->stp_enabled == BR_NO_STP) 41 goto forward; 42 break; 43 44 case 0x01: /* IEEE MAC (Pause) */ 45 goto drop; 46 47 default: 48 /* Allow selective forwarding for most other protocols */ 49 if (p->br->group_fwd_mask & (1u << dest[5])) 50 goto forward; 51 } 52 53 /* Deliver packet to local host only */ 54 if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, 55 NULL, br_handle_local_finish)) { 56 return RX_HANDLER_CONSUMED; /* consumed by filter */ 57 } else { 58 *pskb = skb; 59 return RX_HANDLER_PASS; /* continue processing */ 60 } 61 } 62 //开始转发 63 forward: 64 switch (p->state) { 65 case BR_STATE_FORWARDING: 66 rhook = rcu_dereference(br_should_route_hook); 67 if (rhook) { 68 if ((*rhook)(skb)) { 69 *pskb = skb; 70 return RX_HANDLER_PASS; 71 } 72 dest = eth_hdr(skb)->h_dest; 73 } 74 /* fall through */ 75 case BR_STATE_LEARNING: 76 if (ether_addr_equal(p->br->dev->dev_addr, dest))//如果数据包进入的端口的MAC和数据包的目的MAC相同 77 skb->pkt_type = PACKET_HOST;//表明这是host的数据,需要直接上缴给协议栈 78 79 NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, 80 br_handle_frame_finish); 81 break; 82 default: 83 drop: 84 kfree_skb(skb); 85 } 86 return RX_HANDLER_CONSUMED; 87 }
这里函数的含义还比较明确,我们先看下所有数据包的类型定义
1 #define PACKET_HOST 0 /* To us */ 2 #define PACKET_BROADCAST 1 /* To all */ 3 #define PACKET_MULTICAST 2 /* To group */ 4 #define PACKET_OTHERHOST 3 /* To someone else */ 5 #define PACKET_OUTGOING 4 /* Outgoing of any type */ 6 /* These ones are invisible by user level */ 7 #define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */ 8 #define PACKET_FASTROUTE 6 /* Fastrouted frame */
数据包的这个特性记录在skb->pkt_type字段中,只是占用三个bit位。
继续看函数体
在函数中,前半部分都是一些验证,这里首先验证数据包的类型,然后验证数据包中源mac地址的合法性,
接着检查skb是否是共享的,这一些都通过后会判断目的MAC地址是否是特殊的MAC地址,虽然这一可能性不大,但是还是要判断下。这里判断的内容不是本文重点,就不在详细描述。
然后就到了forward节:
这里根据端口的state做switch
在BR_STATE_FORWARDING状态下,调用了一个hook函数。这部分内容还不是很理解。
而在BR_STATE_LEARNING状态下,首先判断了目的MAC是否和数据流入端口的mac地址是否相同,相同就表明数据包是发往本机的,设置skb的包类型为PACKET_HOST,然后调用了
br_handle_frame_finish函数
1 int br_handle_frame_finish(struct sk_buff *skb) 2 { 3 const unsigned char *dest = eth_hdr(skb)->h_dest; 4 struct net_bridge_port *p = br_port_get_rcu(skb->dev); 5 struct net_bridge *br; 6 struct net_bridge_fdb_entry *dst; 7 struct net_bridge_mdb_entry *mdst; 8 struct sk_buff *skb2; 9 bool unicast = true; 10 u16 vid = 0; 11 //如果端口不可用,则直接丢弃数据包 12 if (!p || p->state == BR_STATE_DISABLED) 13 goto drop; 14 //对vlan标签做相关判断,查看skb是否符合 15 if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) 16 goto drop; 17 18 /* insert into forwarding database after filtering to avoid spoofing */ 19 br = p->br;//获取网桥结构 20 if (p->flags & BR_LEARNING)//如果网桥具备学习能力,更新转发表 21 br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); 22 //如果不是广播地址&&是多播地址&&多播发送成功 23 if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && 24 br_multicast_rcv(br, p, skb)) 25 goto drop; 26 //此时已经更新表完毕,端口若还是处于学习状态就drop 27 if (p->state == BR_STATE_LEARNING) 28 goto drop; 29 30 BR_INPUT_SKB_CB(skb)->brdev = br->dev; 31 32 /* The packet skb2 goes to the local host (NULL to skip). */ 33 skb2 = NULL; 34 //判断网卡若是处于混杂模式 35 if (br->dev->flags & IFF_PROMISC) 36 skb2 = skb; 37 38 dst = NULL; 39 //如果是广播地址 40 if (is_broadcast_ether_addr(dest)) { 41 skb2 = skb; 42 unicast = false;//设置单播标识为false 43 } else if (is_multicast_ether_addr(dest)) { 44 mdst = br_mdb_get(br, skb, vid); 45 if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && 46 br_multicast_querier_exists(br, eth_hdr(skb))) { 47 if ((mdst && mdst->mglist) || 48 br_multicast_is_router(br)) 49 skb2 = skb; 50 br_multicast_forward(mdst, skb, skb2); 51 skb = NULL; 52 if (!skb2) 53 goto out; 54 } else 55 skb2 = skb; 56 unicast = false; 57 br->dev->stats.multicast++; 58 //查找转发表并判断表项,如果表项存在且端口是本地端口 59 } else if ((dst = __br_fdb_get(br, dest, vid)) && 60 dst->is_local) { 61 skb2 = skb; 62 /* Do not forward the packet since it's local. */ 63 skb = NULL; 64 } 65 //fdb表中存在表项且是本地端口或者多播处理完成 skb2=skb skb=null unicast=true 66 //广播或者多播未处理完成 skb2=skb skb!=null unicast=false 67 //fdb表中未找到表项或者不是本地端口 skb!=null skb2=null unicast=true 68 if (skb) { 69 if (dst) { 70 //转发表中表项存在且不是本地端口,即需要转发到其他端口 71 dst->used = jiffies; 72 //实施转发 73 br_forward(dst->dst, skb, skb2); 74 } else 75 //处理广播或者多播或者未找到端口的单播 76 br_flood_forward(br, skb, skb2, unicast); 77 } 78 //目的端口是本地端口&&多播&&广播 79 if (skb2) 80 return br_pass_frame_up(skb2); 81 82 out: 83 return 0; 84 drop: 85 kfree_skb(skb); 86 goto out; 87 }
这里就要做比较详细的判断了,首先判断端口的状态,然后调用br_allowed_ingress函数验证vlan标签,这里就不深入去查看了。接着就调用br_fdb_update更新网桥的转发表,对组播数据包进行预处理。
接着就开始了数据包转发前的地址判断,先判断是否是广播地址,是就令skb2=skb即复制一份数据包,并设置unicast为false。
然后判断是否是组播地址,是就从组播数据库中获取对应的net_bridge_mdb_entry结构,该结构中记录了组播组中的端口,在经过几个验证之后就调用br_multicast_forward进行组播数据包的转发,之后置空skb
最后就剩下单播地址了,从地址转发表中获取net_bridge_fdb_entry结构并判断其is_local属性,如果is_lcoal为true则表示这个发往host的数据包,就设置复制一份skb,然后置空skb指针。
然后就开始其他端口的转发,这里在前一部分已经根据不同的情况设置了skb指针,所以如果skb指针不为空就表示这是单播或者广播或者多播未处理的情况,然后判断前面获取的单播转发表的表项是否为空,如果不为空就表示这个发往其他端口的单播数据包,那么就调用br_forward进行转发。如果为空就表示这有可能是其他的情况,那么就调用br_flood_forward进行处理,注意这里还有一个参数就是unicast,这是单播标识,函数中会用到。
br_flood_forward会把单播数据包发往所有支持BR_FLOOD特性的端口,上面也许注意到了不只是单播数据包可以走到这里,广播也可以走到这里,这也难怪,单播在未找到表项的情况下只能向所有其他的支持BR_FLOOD特性的端口转发,这和广播很相似,只不过是广播的话BR_FLOOD特性也不起作用,直接全部转发了。只是我不太明白的是未处理的组播数据包怎么办了??
接着就处理本地数据包的情况,即数据包目的地址是host的单播数据、广播、组播都需要给host上层交付,那么这里就调用br_pass_frame_up函数
1 static int br_pass_frame_up(struct sk_buff *skb) 2 { 3 struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; 4 struct net_bridge *br = netdev_priv(brdev); 5 struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); 6 7 u64_stats_update_begin(&brstats->syncp); 8 brstats->rx_packets++; 9 brstats->rx_bytes += skb->len; 10 u64_stats_update_end(&brstats->syncp); 11 12 /* Bridge is just like any other port. Make sure the 13 * packet is allowed except in promisc modue when someone 14 * may be running packet capture. 15 */ 16 if (!(brdev->flags & IFF_PROMISC) && 17 !br_allowed_egress(br, br_get_vlan_info(br), skb)) { 18 kfree_skb(skb); 19 return NET_RX_DROP; 20 } 21 22 skb = br_handle_vlan(br, br_get_vlan_info(br), skb); 23 if (!skb) 24 return NET_RX_DROP; 25 26 indev = skb->dev; 27 skb->dev = brdev; 28 29 return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, 30 netif_receive_skb); 31 }
到了该函数已经要准备把数据交付给网络层了,并且已经设置数据包的设备skb->dev修改为网桥代表的设备,表明这是从网桥发出的数据包。最后会再次调用netif_receive_skb重新接受数据包但是这时skb->dev是网桥,并且网桥设备的rx_handler指针肯定为空,那么就不会再次进入网桥的处理,而是直接交付上层了。
br_flood_forward