Linux 网桥代码分析 六 网桥数据转发函数分析

对于数据包转发函数,主要是分为两大类:数据转发到指定端口、数据扩散到所有端口。

下面就从这两方面进行分析:

一 数据转发到指定端口

对于数据转发到指定端口的功能,也可以分为两个方面:对入口流量进行的数据转发、

对出口流量进行的数据转发

入口流量很好理解:就是桥组中的一个网桥端口接收了数据包,经过网卡驱动后经由函数netif_receive_skb、handle_bridge、br_handle_frame等函数,最后调用函数br_forward,实现数据转发。

那对于出口流量怎么理解呢?

我的理解是,对于一个路由器,我们可能会建立路由wan连接,那当外网数据要和lan側的pc进行数据传输时,外网数据会首先到达wan接口,wan接口通过路由规则和arp,会将数据从网桥发送出去,最后网桥就会调用br-> netdev_ops->ndo_start_xmit,即br_dev_xmit,br_dev_xmit中就会调用br_deliver、br_flood_deliver,进行出口流量的转发。

下面我们分析入口流量指定端口转发的函数br_forward

是__br_forward的封装函数,并增加了判断端口是否符合数据转发功能的判断

/* called with rcu_read_lock*/

void br_forward(const structnet_bridge_port *to, struct sk_buff *skb)

{

if (should_deliver(to, skb)) {

__br_forward(to, skb);

return;

}

kfree_skb(skb);

}

该函数的两个函数,都值得我们去分析,在这两个函数里,我们都可以做自己的代码功能扩展。

should_deliver的作用是判断是否将数据包从网桥端口p转发出去

符合转发的条件为

1、网桥端口的flag为BR_HAIRPIN_MODE并且网桥端口的状态为forward时,则符合

转发条件(此处BR_HAIRPIN_MODE是什么呢??)

2、数据包入口端口与出口端口不同,且网桥端口的状态为forward。

static inline intshould_deliver(const struct net_bridge_port *p,

const struct sk_buff*skb)

{

return (((p->flags & BR_HAIRPIN_MODE) || skb->dev !=p->dev) &&

p->state == BR_STATE_FORWARDING);

}

__br_forward主要会调用防火墙相关的函数做最后的判断,对于允许通过的函数,,则调用br_forward_finish进行转发最后的处理。

1、修改skb指向的net dev

2、调用NF_HOOK处理forward链上的规则,并对允许通过的

数据包调用函数br_forward_finish进行后续的处理

*/

static void __br_forward(conststruct net_bridge_port *to, struct sk_buff *skb)

{

struct net_device *indev;

/*gso相关设置,对gso不熟悉…*/

if (skb_warn_if_lro(skb)) {

kfree_skb(skb);

return;

}

indev = skb->dev;

skb->dev = to->dev;

/*如果有ip层校验和策略,则修改为NONE*/

skb_forward_csum(skb);

/*处理forward链的规则,对于允许通过的数据包,调用br_forward_finish

进行后续处理*/

NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,

br_forward_finish);

}

br_forward_finish主要实现以下功能

1、调用NF_HOOK,处理网桥防火墙中post链中的规则进行匹配处理

2、若该数据包没有被防火墙规则丢弃掉,则调用函数

br_dev_queue_push_xmit,传输数据包

int br_forward_finish(structsk_buff *skb)

{

return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,skb->dev,

br_dev_queue_push_xmit);

}

网桥设备的传输函数br_dev_queue_push_xmit主要实现调用dev_queue_xmit,最后实现调用网卡驱动的发包处理函数。br_dev_queue_push_xmit的功能如下:

1、若数据包的大于设备的mtu值,且不支持gso,则释放该skb

2、若数据包大小正常,则调用dev_queue_xmit,传输数据包

intbr_dev_queue_push_xmit(struct sk_buff *skb)

{

/* drop mtu oversized packets except gso */

if (packet_length(skb) > skb->dev->mtu &&!skb_is_gso(skb))

kfree_skb(skb);

else {

/* ip_refrag calls ip_fragment, doesn’t copy the MAC header. */

if (nf_bridge_maybe_copy_header(skb))

kfree_skb(skb);

else {

skb_push(skb, ETH_HLEN);

dev_queue_xmit(skb);

}

}

return 0;

}

下面我们继续分析出口流量指定端口转发的函数br_deliver

该函数是__br_deliver 的封装,只不过增加了判断端口是否符合转发数据包的函数调用

void br_deliver(const structnet_bridge_port *to, struct sk_buff *skb)

{

if (should_deliver(to, skb)) {

__br_deliver(to, skb);

return;

}

kfree_skb(skb);

}

_br_deliver 主要实现以下功能:

1、设置skb->dev执行要转发数据的设备dev

2、调用NF_HOOK,处理网桥防火墙中调用out链中的规则

3、若数据包没有被丢弃,则br_forward_finish进行转发处理

*/

static void __br_deliver(conststruct net_bridge_port *to, struct sk_buff *skb)

{

skb->dev = to->dev;

NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,

br_forward_finish);

}

而对于函数br_forward_finish,我们在前面已经分析过了。

二、数据转发扩散函数

对于扩散函数,同样分为两方面,入口流量的扩散,出口流量的扩散

首先分析入口流量的扩散函数br_flood_forward

该函数主要是调用函数br_flood,并注册br_flood的回调处理函数为__br_forward

/* called under bridge lock */

void br_flood_forward(structnet_bridge *br, struct sk_buff *skb)

{

br_flood(br, skb, __br_forward);

}

下面分析函数 br_flood

该函数的逻辑还是很简单的。

主要是循环遍历给定网桥下的所有端口,对于符合

数据转发条件的端口,则调用函数__packet_hook进行后续

处理。

/* called under bridge lock */

static void br_flood(structnet_bridge *br, struct sk_buff *skb,

void (*__packet_hook)(const struct net_bridge_port *p,

struct sk_buff*skb))

{

struct net_bridge_port *p;

struct net_bridge_port *prev;

prev = NULL;

list_for_each_entry_rcu(p, &br->port_list, list) {

if (should_deliver(p, skb)) {

if (prev != NULL) {

struct sk_buff *skb2;

if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {

br->dev->stats.tx_dropped++;

kfree_skb(skb);

return;

}

__packet_hook(prev, skb2);

}

prev = p;

}

}

if (prev != NULL) {

__packet_hook(prev, skb);

return;

}

kfree_skb(skb);

}

接着分析出口流量的扩散函数br_flood_deliver

主要是调用函数br_flood,并注册br_flood的回调处理函数为__br_deliver

void br_flood_deliver(structnet_bridge *br, struct sk_buff *skb)

{

br_flood(br, skb, __br_deliver);

}

而br_flood我们已经分析过了。

至此我们分析完了转发数据相关的函数,主要是集中在br_forward.c中。

而其实你还爱着他,你一点也不好。

Linux 网桥代码分析 六 网桥数据转发函数分析

相关文章:

你感兴趣的文章:

标签云: