kernel 网桥代码分析

作者:林海枫

本文地址:http://blog.csdn.net/linyt/archive/2010/01/14/5191512.aspx

注:本文由作者所拥用,欢迎转载,但请全文转载并注明作者,请勿用于 任何商途。

本文分析的kernel版本为:2.6.24.4,网桥代码目录为:linux-2.6.24.4/net/bridge。

本文着重分析网桥的基本功能,关于STP的功能,我想从另写一篇文章进行分析。由于时间仓促,分析可能存在不足之外。

      网桥是kernel网络模块中相于独立的module,读者具有简单的kernel网络设备驱动开发和kerenl网络协议的基础知识即可。我在2007年就开始接触网桥了,当时有位同事为了测试网桥的功能,还特地查看了网桥的代码,还特意转告我一定要看看这部分的代码,他说比较简单,也很容易看个明白。我当时在做Linux系统的测试工作,还未正式进行开发工作,虽然把代码查看了一翻,但由于经验关系,看得一窍不通。两年过去了,在Linux的开发过程了,接触了kernel的机会也很多。去年3月份,阅读了kernel中网络子模块的部分代码。最近由于工作的需要,阅读了项目中网络驱动部分的代码,就这样,目光转向了Linux网桥代码。遂有写此文之愿。

 

第一部分: 网桥的报文处理功能分析


1.1  Linux网桥的配置实例

      在Linux里面使用网桥非常简单,仅需要做两件事情就可以配置了。其一是在编译内核里把CONFIG_BRIDGE或CONDIG_BRIDGE_MODULE编译选项打开;其二是安装brctl工具。第一步是使内核协议栈支持网桥,第二步是安装用户空间工具,通过一系列的ioctl调用来配置网桥。下面以一个相对简单的实例来贯穿全文,以便分析代码。

clip_image002

      Linux机器有4个网卡,分别是eth0~eth4,其中eth0用于连接外网,而eth1, eth2, eth3都连接到一台PC机,用于配置网桥。只需要用下面的命令就可以完成网桥的配置:

Brctl addbr br0 (建立一个网桥br0, 同时在Linux内核里面创建虚拟网卡br0)

Brctl addif br0 eth1

Brctl addif br0 eth2

Brctl addif br0 eth3 (分别为网桥br0添加接口eth1, eth2和eth3)

      其中br0作为一个网桥,同时也是虚拟的网络设备,它即可以用作网桥的管理端口,也可作为网桥所连接局域网的网关,具体情况视你的需求而定。要使用br0接口时,必需为它分配IP地址。为正常工作,PC1, PC2,PC3和br0的IP地址必须分配在同一个网段。

1.2  网桥的数据结构

      网桥的核心数据结构主要有:struct net_bridge和struct net_bridge_port这两个结构,当然还有通用的网络设备结构struct net_device。为了简单起见,我们以上述为例子,描述出此时它的静态结构。

clip_image004

      每个网桥由struct net_bridge结构来维护,它主要的成员有:port_list,dev和hash。Port_list是一个双向链表,它元素的结构为struct net_bridge_port,每个加入到网桥的设备都在里面占一个元素结点。Dev指针指向net_device变量,它存放网络设备br0的信息。Hash是MAC地址的hash表,MAC地址的hash值为数组结构的下标,每个数组元素为链表,每个元素就是唯一的struct net_bridge_fdb_entry结构,以MAC地址为标识符。

1.3  网桥数据包入口

      网桥是一种2层网络互连设备,而不是一种网络协议。它在协议结构上并没有占有一席之地,因此不能通过向协议栈注册协议的方式来申请网桥数据包的处理。相反,网桥接口(如上述的eth1)的数据包和一般接口(如eth0)在格式上完全是一样的,不同之处是网桥在2层上就对它进行了转了,而一般接口要在3层才能根据路由信息来决定是否要转发,如何转发。那么一个网络接口,在驱动处理完数据包后,怎么才知道该接口分配在一个网桥里面呢?其实很简单,当brctl工具通过ioctl系统调用时,kernel为该添加的设备生成一个bridge_port结构并放到port_list链中,同时将该bridge_port的值赋予设备net_device的br_port指针。因此,要识别接口是否属于某个网桥,只需判断net_device的br_port指针是否不为空即可。

     现假设PC1向PC2发送其个数据包,数据首先会由eth1网卡接收,此后网卡向CPU发送接收中断。当CPU执行当前指令后(如果开中断的话),马上跳到网卡的驱动程去。Eth1的网卡驱动首先生成一个skb结构,然后对以太网层进行分析,最后驱动将该skb结构放到当前CPU的输入队列中,唤醒软中断。如果没有其它中断的到来,那么软中断将调用netif_receive_skb函数。代码和分析如下所述:

[linux-2.6.24.4/net/core/dev.c]


1.4  handle_bridge处理函数

[linux-2.6.24.4/net/core/dev.c]

 

1.5  网桥处理逻辑

[linux-2.6.24.4/net/bridge/br_input.c]

1.6  br_handle_frame_finish函数

[linux-2.6.24.4/net/bridge/br_input.c]

 

1.7  通过br_pass_frame_up函数将报文发往本机接口。

[linux-2.6.24.4/net/bridge/br_input.c]

 

1.8  通过br_forward函数将报文从另一个端口转发出去

 

        Should_deliver函数来测试是否应将该包转发出去,它由出口端的状态和报文的入口端口信息来决定,它的定义如下:

[linux-2.6.24.4/net/bridge/br_forward.c]

 

 

      若报文的确需要转发,因为目标主机是在另一个子网段,而且没有其它网相连的网格端口可抵达该子网段(这里考虑到启用STP功能,如果搞不清楚可略过)。将调用__br_forward函数实施这一转发功能。

[linux-2.6.24.4/net/bridge/br_forward.c]

 

 

[linux-2.6.24.4/net/bridge/br_forward.c]

 

     Br_dev_queue_push_xmit在调用dev_queue_xmit函数前做些必要的检查工作。例如,报文的长度比出口端口的MTU还大,则丢掉该报文。

[linux-2.6.24.4/net/bridge/br_forward.c]

 

1.9  br_flood_forward 函数把报文转发到网桥所有出口端口

[linux-2.6.24.4/net/bridge/br_forward.c]

 

 

     __br_forward代码已在前面分析过,它从指定的出口端口转发该报文。

      而br_flood函数,把__br_forward函数作为回调函数,依次遍网桥的所有出口端,调用__br_forward函数将该报文转发出去。一个广播报文从某一端口进入,应该其余的端口都应该转发出去,但入口端口就不需要了。下面的代码看似把报文从所有端口都转发一份,其实不然,should_deliver会阻止这样的事情发生。

[linux-2.6.24.4/net/bridge/br_forward.c]

 

 

第二部分:网桥转发数据库的维护

      众所周知,网桥需要维护一个MAC地址-端口映射表,端口是指网桥自身提供的端口,而MAC地址是指与端口相连的另一端的MAC地址。当网桥收到一个报文时,先获取它的源MAC,更新数据库,然后读取该报文的目标MAC地址,查找该数据库,如果找到,根据找到条目的端口进行转发;否则会把数据包向除入口端口以外的所有端口转发。

2.1 数据库的创建和销毁

     数据库使用kmem_cache_create函数进行创建,使用kmem_cache_desctory进行销毁。

[linux-2.6.24.4/net/bridge/br_fdb.c]

 

 

[linux-2.6.24.4/net/bridge/br_fdb.c]


 

2.2 数据库更新

      当网桥收到一个数据包时,它会获取该数据的源MAC地址,然后对数据库进行更新。如果该MAC地址不在数库中,则创新一个数据项。如果存在,更新它的年龄。数据库使用hash表的结构方式,便于高效查询。下面是hash功能代码的分析:

[linux-2.6.24.4/net/bridge/br_fdb.c]


 

2.3 创建数据项

     在更新函数里面已为某一MAC找到了它所属于的Hash链表,因此,创建函数只需要在该链上添加一个数据项即可。

[linux-2.6.24.4/net/bridge/br_fdb.c]

2.4 查找数据项。

      网桥的数据项查找与一般的查找类似,但略有不同。前面提到,如果要更新一MAC地址,不管该地址是否已经过期了,只需遍历该MAC地址对应的Hash链表,然后更新年龄,此时它肯定不过期了。但网桥要转发数据时,除了要找到该目标MAC的出口端口外,还要判断该记录是否过期了。因此,数据项的查找有两种,一种用于更新,另一用于转发。

[linux-2.6.24.4/net/bridge/br_fdb.c]

 

 

 [linux-2.6.24.4/net/bridge/br_fdb.c]

 

      除了__br_fdb_get函数多调用了has_expired外,其余无一不同。Has_expired函数来决定该数据项是否是过期的,代码如下:

[linux-2.6.24.4/net/bridge/br_fdb.c]

 

第三部分: ioctl管理网桥

 

3.1 通过ioctl系统调用创建网桥

     仍然以前的配置作为例,我们分用户空间程序brctl是如何通过ioctl系统调用在kernel空间内创建上述的数据结构。创建网桥,我们不需要预知任何网络设备信息,因此我们通过ioctl来创建网桥时不应该与任何网络设备绑定到一起。网桥模块为此ioctl函数提供了一个恰如其分的名字 br_ioctl_deviceless_stub。Brctl工具使用的ioctl系统调用最终会调用此函数,它相关代码如下:

[linux-2.6.24.4/net/bridge/br.c]

brioctl_set(br_ioctl_deviceless_stub);

[linux-2.6.24.4/net/socket.c]

 

        用户空间程序使用网桥相关的命令来调用ioctl函数时,它经kernel依据命令所属的分类分派到sock_ioctl函数。在sock_ioctl函数里面,当ioctl命令为SIOCGIFBR,SIOCSIFBR, SIOCBRADDBR 和SIOCBRDELBR,它将ioctl的请求转发到br_ioctl_deviceless_stub函数。

Br_ioctl_deviceless_stub函数代码和分析如下:

[linux-2.6.24.4/net/bridge/br_ioctl.c]

 

      该函数调用br_add_bridge和br_del_brdge函数的实现新建和删除网桥的功能。由于这两个函数所完成的事情刚好相反,在此,我们只讨论br_add_bridge的代码:

[linux-2.6.24.4/net/bridge/br_if.c]

 

      现在创建网桥设备的任务落到new_bridge_dev的身上。New_bridge_dev函数的功能与一般的网卡驱动初化为代码非常类似的。因为这里段代就创建一个网桥设备,从这个层面来说,这段代码也算是驱动代码,结构和真实驱动非常类似。

[linux-2.6.24.4/net/bridge/br_if.c]

 

 [linux-2.6.24.4/net/bridge/br_device.c]

 

3.2 通过ioctl系统调用为网桥添加端口

        仅仅创建网桥,还是不够的。实际应用中的网桥需要添加实际的端口(即物理接口),如例子中的eth1, eth2等。应用程序在使用ioctl来为网桥增加物理接口,br_dev_ioctl的代码和分析如下:

[linux-2.6.24.4/net/bridge/br_ioctl.c]

 

    这段代码一目了然,通过add_del_if函数来控制网桥的物理接口,该函数的代码和分析如下:

[linux-2.6.24.4/net/bridge/br_ioctl.c]

 

     具体的代码在br_add_if和br_del_if中,出于讨论的方便,我们只分析br_add_if函数。

[linux-2.6.24.4/net/bridge/br_if.c]

 

第四部分: 总结

    网桥是2层的网格连接设备,它工作在协议栈的第二层。本文以简单的例子作为基础,分析网桥处理报文,更新MAC-端口映射表,和如何控制网桥和端口的功能。文中帖上了大量的关键代码,并以代码加上注释这种贴近程序员的方式来分析代码。对于缺少kernel网络编程经验的朋友,在某些代码处,写了在背景知识的分析和解释。

  • 4
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 27
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值