Linux 网桥代码分析 五 网桥端口设备接收数据包的处理分析

对于网桥端口设备,底层接收到数据以后,经过网卡驱动的接收函数处理以后,最终会调用函数netif_receive_skb,而netif_receive_skb在对数据包头进行相关处理以及对ptype_all链上注册的相关协议进行调用deliver_skb处理后(包括PF_PACKET类型的rawsocket处理),会调用handle_bridge进入网桥处理,而其会调用br_handle_frame_hook,即会调用br_handle_frame。

函数调用流程为netif_receive_skb->handle_bridge->br_handle_frame.

下面分析函数br_handle_frame,其作用是处理网桥入口数据主要函数。

1、判断mac地址是否有效(既不是组播mac地址也不是0mac地址)

2、判断skb包是否有效数据包

3、判断目的mac地址是否是01 80 c2 00 0x类型,若是,则继续判断是0x8808 协议,若是0x8808,个人理解则可能是mpcp相关

的数据包,而mpcp是epon相关的协议,而mpcp协议中相关的消息和epon mac的硬件息息相关,linux内核对这类数据包就

没有提供相关的公共函数了。对于其他类型的 数据包,则调用函数br_handle_local_finish进行后续处理,而br_handle_local_finish

也仅仅是调用br_fdb_update,更新fdb数据库

4、对于网桥端口是forward和learning状态的,则调用防火墙处理函数

处理NF_BR_PRE_ROUTING的ebtables相关的规则。

5、当通过NF_BR_PRE_ROUTING相关的ebtables规则后,则会调用函数

br_handle_frame_finish继续进行数据处理

struct sk_buff *br_handle_frame(struct net_bridge_port *p,struct sk_buff *skb)

{

const unsigned char*dest = eth_hdr(skb)->h_dest;

int (*rhook)(structsk_buff *skb);

if(!is_valid_ether_addr(eth_hdr(skb)->h_source))

goto drop;

skb =skb_share_check(skb, GFP_ATOMIC);

if (!skb)

return NULL;

if(unlikely(is_link_local(dest))) {

/* Pause framesshouldn’t be passed up by driver anyway */

if(skb->protocol == htons(ETH_P_PAUSE))

goto drop;

/* If STP isturned off, then forward */

if(p->br->stp_enabled == BR_NO_STP && dest[5] == 0)

goto forward;

if(NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,

NULL, br_handle_local_finish))

return NULL; /* frame consumed by filter */

else

return skb; /* continue processing */

}

forward:

switch (p->state) {

caseBR_STATE_FORWARDING:

rhook =rcu_dereference(br_should_route_hook);

if (rhook != NULL){

if (rhook(skb))

returnskb;

dest =eth_hdr(skb)->h_dest;

}

/* fall through */

caseBR_STATE_LEARNING:

if(!compare_ether_addr(p->br->dev->dev_addr, dest))

skb->pkt_type= PACKET_HOST;

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

br_handle_frame_finish);

break;

default:

drop:

kfree_skb(skb);

}

return NULL;

}

在这个函数的尾部,会调用br_handle_frame_finish,进行后续的处理,我们继续分析函数br_handle_frame_finish。该函数的作用是决定数据包的走向:转发、扩散或是丢弃

下面分析这个函数的主要流程:

1、首先判断接收到数据包的设备对应的网桥端口的状态是否为disable

2、调用br_fdb_update,更新fdb数据库,为数据包源mac地址与源网桥端口

添加fdb entry,关于br_fdb_update,请参考我前面的分析文档

3、如果源网桥端口的 状态为learning,则不处理该数据包

4、a)如果网桥设备处于混杂模式或者数据包的目的mac地址为组播

地址,则需要将该skb的一个拷贝,发送给上层协议栈(通过调用

br_pass_frame_up实现)

b)如果数据包的目的mac地址为本地mac,则只只需要将该数据包

发送给上次协议栈,而不需转发数据包。

5、调用__br_fdb_get查找符合条件的fdb entry,

a)若查找到了了符合条件的fdbentr

i)若该fdb entry为local类型的,说明该数据包是发往本地的,则将

skb赋值给skb2,然后skb指向NULL,不对该数据包进行转发。

ii)若该fdb entry不是local类型的,则调用br_forward,将数据包从指定端口转发

出去。

b)若没有查找到指定的端口,则调用br_flood_forward,将数据从其他所有

网桥端口发送出去

int br_handle_frame_finish(struct sk_buff *skb)

{

const unsigned char*dest = eth_hdr(skb)->h_dest;

struct net_bridge_port*p = rcu_dereference(skb->dev->br_port);

struct net_bridge *br;

structnet_bridge_fdb_entry *dst;

struct sk_buff *skb2;

if (!p || p->state== BR_STATE_DISABLED)

goto drop;

/* insert intoforwarding database after filtering to avoid spoofing */

br = p->br;

br_fdb_update(br, p,eth_hdr(skb)->h_source);

if (p->state ==BR_STATE_LEARNING)

goto drop;

/* The packet skb2goes to the local host (NULL to skip). */

skb2 = NULL;

if(br->dev->flags & IFF_PROMISC)

skb2 = skb;

dst = NULL;

if(is_multicast_ether_addr(dest)) {

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

skb2 = skb;

} else if ((dst =__br_fdb_get(br, dest)) && dst->is_local) {

skb2 = skb;

/* Do not forwardthe packet since it’s local. */

skb = NULL;

}

if (skb2 == skb)

skb2 =skb_clone(skb, GFP_ATOMIC);

if (skb2)

br_pass_frame_up(br,skb2);

if (skb) {

if (dst)

br_forward(dst->dst,skb);

else

br_flood_forward(br,skb);

}

out:

return 0;

drop:

kfree_skb(skb);

goto out;

}

在这个函数里,主要的处理函数有br_pass_frame_up、br_forward、br_flood_forward,而br_forward、br_flood_forward是转发相关的函数,我们下节进行分析。

我们现在分析下函数br_pass_frame_up

该函数的作用是将数据包传递给上层协议栈进行处理。该函数的处理流程如下:

1、将skb->dev设置为brdev

2、调用NF_HOOK处理input链上的防火墙规则,对于准许通过的

数据包,则调用netif_receive_skb进行后续处理,此时由于skb->dev

为brdev,所以这次netif_receive_skb调用则会跳过brdige_handle,上传给

上层协议栈进行后续处理。

static void br_pass_frame_up(struct net_bridge *br, structsk_buff *skb)

{

struct net_device*indev, *brdev = br->dev;

brdev->stats.rx_packets++;

brdev->stats.rx_bytes+= skb->len;

indev = skb->dev;

skb->dev = brdev;

NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_IN, skb, indev, NULL,

netif_receive_skb);

}

至此,入口数据处理函数分析完毕,主要是br_input.c中的函数。

学习会使你永远立于不败之地。

Linux 网桥代码分析 五 网桥端口设备接收数据包的处理分析

相关文章:

你感兴趣的文章:

标签云: