Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

Vinllen Chen


To be a better coder

Linux bridge学习

  Linux bridge是Linux内核中虚拟网桥的一个实现,它与OpenvSwitch都实现交换机功能,但比后者简单一些,只实现了最简单的二层功能,而没有ovs下诸如QoS,OpenFlow,netconf等等复杂的功能。本文主要参考《深入理解Linux网络技术内幕》,更像一个读书笔记性质的小结。
  虚拟网桥是定义在真实设备之上的一个抽象设备,当该真实设备发生状态变化,则Linux bridge会受到影响。netdevice通知链向内核注册br_device_event回调函数,任何真实设备的变动会触发该回调函数。
  下图是在有无网桥情况下收发包的示意图。在设备上传输数据是通过dev_queue_xmit执行的,它会调用设备驱动函数hard_start_xmit进行发包,中间有一些查找发包的过程。dev_queue_xmit可以通过eth2接口进行发送,也可以调用br0进行发送,后者绑定了两个接口eth0和eth1。同理收包。下面将会具体进行介绍。
linux_bridge1
  下图是主要的结构图。其中:

  • net_bridge_fdb_entry为转发数据库的记录项,每个mac learning后的地址都会有个这样的记录。
  • net_device表示网络设备,比如bridge, router等等
  • net_bridge表示网桥
  • net_bridge_port表示网桥端口 linux_bridge2
      左侧为网桥结构,port_list指示网桥的端口list,每个listnet_bridge_port结构体,其dev指向该端口所绑定的设备,br指向端口所属的网桥。网桥结构内含一个哈希表,存储学到的转发表项net_bridge_fdb_entry,其dst指向网桥端口,age_list表示老化时间。

网桥和网桥端口的创建与删除

  网桥设备的建立和删除分别通过br_add_bridgebr_del_bridge,网桥端口的添加和删除是br_add_ifbr_del_if。注意以上四个函数运行加Netlink routing锁。br_add_bridge新建网桥主要调用new_bridge_dev完成创建:

  1. 分配一个net_device数据结构并初始化。
  2. 初始化私有数据结构。
  3. 初始化网桥优先权为默认值,32768(0x8000)。
  4. 初始化指定网桥ID,根路径开销以及根端口。
  5. 初始化老化时间,默认5min。
  6. br_stp_timer_init函数初始化每个网桥的定时器。

  注意,网桥端口和NIC是一一对应的,以下显示了br_add_if主要流程,图中dev_set_promiscuity为设置混杂模式,该模式下可以捕获所有LAN网络数据,即使不是发给本机,而且当网桥转发帧时,也需要进入混杂模式。
linux_bridge3

转发数据库

  该数据库嵌入在net_bridge数据结构中,并被定义成一个hash表,对于网桥的任何端口上所学到的每个mac地址,会为其在该数据库中插入一个net_bridge_fdb_entry数据结构实例。
  转发数据库中得记录是通过mac地址来标识的。查询这个表包括用br_mac_hash选出正确的hash表bucket,以及浏览bucket中得net_bridge_fdb_entry实例列表,找出第一个和指定mac地址相匹配的数据项。
  由于br_fdb_get查询转发数据库可能把结果缓存起来,所以每个数据项都会指定一个引用计数。当查询成功时,br_fdb_get会将其增1。当调用函数不再需要引用查询结果时,就应当用br_fdb_put将其减1。当计数为0时,br_fdb_put将会释放net_bridge_fdb_entry
  在创建一个网桥端口时,br_add_if通过br_fdb_insert函数把被绑定的设备的mac地址加入到转发数据库。对于可以添加到转发数据库的数据项的数目目前没有限制,目前,这可能会让系统遭受DOS攻击。

收包主要过程

  1. 包从入口进入到真实设备到bridge,触发netif_receive_skb。若内核不支持桥接,handle_bridge将被设为NULL,并且netif_receive_skb会把入口帧交给上层协议去处理。若内核支持桥接,当bridge收到包,将会调用handle_bridge进行入口帧处理。
  2. handle_bridge调用br_handle_frame_hook处理该帧,当初始化桥接模块时,该函数指针指向br_handle_frame
  3. br_handle_frame判断端口状态及STP状态,更新转发数据库,进行mac learning。注意,该函数跨越了NETFILTER中第一个钩子函数NF_BR_PRE_ROUTING。如下图所示:

    linux_bridge4

  4. br_handle_frame调用br_handle_frame_finish判断报文类型:单播,广播,组播,并查转发表,以及决定是否交给本地协议栈,若是,调用br_pass_frame_up。如下图所示:

    linux_bridge5

  5. br_pass_frame_up越过另外一个NETFILTER钩子函数:NF_BR_LOCAL_IN,然后调用br_pass_frame_up_finish
  6. br_pass_frame_up_finish中,skb->dev的值会被端口所值的网络设备替换,并且再次调用netif_receive_skb。此时,handle_bridge看到的设备已经不是bridge,因此会把该帧交给正确的协议处理函数。

参考

《深入理解Linux网络技术内幕》

说明

转载请注明出处:http://vinllen.com/linux-bridgeshe-ji-yu-shi-xian/


About the author

vinllen chen

Beijing, China

格物致知


Discussions

comments powered by Disqus