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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM