上篇文章介紹了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
