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

posted @ 2016-09-22 16:19  jack.chen  Views(8468)  Comments(0Edit  收藏  举报

以马内利