对于数据包转发函数,主要是分为两大类:数据转发到指定端口、数据扩散到所有端口。
下面就从这两方面进行分析:
一 数据转发到指定端口
对于数据转发到指定端口的功能,也可以分为两个方面:对入口流量进行的数据转发、
对出口流量进行的数据转发
入口流量很好理解:就是桥组中的一个网桥端口接收了数据包,经过网卡驱动后经由函数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中。