Board logo

标题: ALinux网桥的实现分析与使用(1)Linux内核网桥的实现分析 [打印本页]

作者: look_w    时间: 2018-5-7 19:38     标题: ALinux网桥的实现分析与使用(1)Linux内核网桥的实现分析

一、Linux内核网桥的实现分析Linux 内核分别在2.2 和 2.4内核中实现了网桥。但是2.2 内核和 2.4内核的实现有很大的区别,2.4中的实现几乎是全部重写了所有的实现代码。本文以2.4.0内核版本为例进行分析。
在分析具体的实现之前,先描述几个概念,有助于对网桥的功能及实现有更深的理解。
图1:一个交换网络的逻辑图在Linux内核网桥的实现中,一个逻辑网段用net_bridge结构体表示。一个逻辑网段需要保留的信息有:
以上对几个结构体的描述和分析可以通过下图来表示:
图2:Linux网桥数据结构描述图描述了网桥的数据结构后,就可以开始数据包处理流程的分析。
网桥处理包遵循着以下几条原则:
在网络软中断处理函数net_rx_action中,嵌入了handle_bridge用于把数据包skb送入网桥模块处理。
1
2
3
4
5
6
7
8
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
            if (skb->dev->br_port != NULL &&
                br_handle_frame_hook != NULL) {
                handle_bridge(skb, pt_prev);
                dev_put(rx_dev);
                continue;
            }
#endif




br_handle_frame_hook是网桥处理接收到数据包的中入口,网桥初始化(br_init)的时候,把br_handle_frame_hook赋值为br_handle_frame。skb->dev->br_port用于判断接收到这个数据包的接口是否是网桥中的一个端口,如果是,skb->dev->br_port不为NULL,那么数据包应该由网桥处理。反之,数据包由上层协议栈处理。网桥中虚拟网卡对应的数据包就是在这个判断点时不再进入网桥。(实际上虚拟网卡并不会自己主动接收数据包,而是在网桥处理中把数据包向本地上层协议栈提交,并且修改了skb->dev,使得数据包不会多次进入桥处理代码)。
前面提到,网桥处理接收包的入口是br_handle_frame(net/bridge/br_input.c)函数。
br_handle_frame函数首先从skb中获得这个包属于的逻辑网段。然后调用__br_handle_frame进行转发处理。 br_handle_frame函数里有一个值得了解的地方,里面有一个加读锁。因为在转发中需要读CAM表,所以必须加读锁,避免在这个过程中另外的内核控制路径(如多处理机上另外一个CPU上的系统调用)修改CAM表。
对输入包的转发决策都是在__br_handle_frame函数中。这个函数的处理可以分为以下几个部分:
在br_forward和br_flood函数中都必须判断源接口和目的接口是否是同一个,如果是同一端口,就不发送这个数据包。数据包的最后发送都是通过统一的发送接口dev_queue_xmit函数来完成的。
以下就是数据包的处理流程:
图3:数据包处理流程图前面多次提到网桥的虚拟网卡,实际上在网桥中,这个网卡存在着一个net_device结构(在net_bridge里),但是不存在着实际的物理设备,而是附在网桥中每个物理网卡上面。这个虚拟网卡的支持函数在(br_device.c)。因为是虚拟的网卡,所以没有物理中断产生,每个需要发送到这个设备的数据包都是靠判断数据包的目的MAC地址来决定是否需要提交到本地上层协议栈(在__br_handle_frame判断)。
如果数据包需要向上层协议提交,都调用br_pass_frame_up函数来处理。在这个函数中,首先把skb->dev设置成br->dev。然后再模拟在中断中处理数据包一样,进行相应的处理, 然后调用netif_rx放入接收队列。这里有一个要十分注意的地方,这个数据包的skb->dev已经变成br->dev。所以在网络接收软中断处理函数net_rx_action中不会再次进入handle_bridge了。
1
2
3
4
5
6
7
8
9
10
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
    br->statistics.rx_packets++;
    br->statistics.rx_bytes += skb->len;
    skb->dev = &br->dev;
    skb->pkt_type = PACKET_HOST;
    skb_pull(skb, skb->mac.raw - skb->data);
    skb->protocol = eth_type_trans(skb, &br->dev);
    netif_rx(skb);
}






欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0