首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

[linux][network]net bridge技术分析

前言: 对于作者这种没有在通信设备方面工作经验的人来说,理解网桥还是挺困难的。 二层之上的数据处理,协议分层,都是相对容易一些(尽管TCP协议复杂的一塌糊涂),毕竟在linux的协议栈代码中,逻辑层次都很清晰。 然后网桥却不同,它是一个二层逻辑。同时,它又不是一个具体的设备(具体的设备,有连接的物理的port口,插入网线就能通数据)。 在虚拟化场景下,虚拟机需要发送、接受数据,和外部交互,就需要有这样的设备。所以有必要深入了解一下网桥的具体的工作原理。 分析: 1,concept 网上的很多说法,网桥类似于交换机。交换机就是这样一个设备,它有若干个网口,并且这些网口是桥接起来的。于是,与交换机相连的若干主机就能够通过交换机的报文转发而互相通信。

如上图(选自网络):主机A发送的报文被送到交换机S1的eth0口,由于eth0与eth1、eth2桥接在一起,故而报文被复制到eth1和eth2,并且发送出去,然后被主机B和交换机S2接收到。而S2又会将报文转发给主机C、D。 当然,上述是物理的网桥的拓扑,我们说的网桥,是在本机中的一个虚拟设备。一种典型的应用场景就是:在本机中虚拟出来多块网卡,通过网桥,再通过物理网卡,和外界进行交互。 2,addbr brctl是一条常用的网桥操作命令。源代码在bridge-util中。作者大概翻了一下,比较简单,基本都是通过系统调用到内核中。所以下面的分析就不分析bridge-util了,直接分析kernel代码(主要逻辑在linux-4.4.61/net/bridge中实现)了。 在shell中敲命令创建网桥br0:brctl addbr br0 会继续进入到kernel中执行如下代码:

可见,bridge在linux中也一个是net device,它实现了自己的ops---br_link_ops。 在bridge的device中,有双链表指针port_list,bridge会把所有关联到bridge上的其他设备组织到port_list上。 3,addif 在shell中敲命令把eth0加入到br0中(想象一下,就是把一个网线插入到交换机的一个LAN口上):brctl addbr br0 eth0 作者截断了一部分检查逻辑的代码,把主要逻辑留下来:

首先,eth0在内核态中也是使用net device来管理的,这里的dev变量就是eth0的数据结构,它的rx被重载为br_handle_frame,也就是说,在eth0接收到数据的时候,会使用br_handle_frame来处理;其次,把eth0通过net_bridge_port数据结构组织到bridge的port list中;再通过br_fdb_insert加入到forward database中。 那么就是说,eth0收到数据,通过br_handle_frame交给bridge来处理。如果bridge发送数据给eth0,那么可以找到它;如果bridge想广播,那么就遍历port list。 4,br_dev_queue_push_xmit linux-4.4.61/net/bridge/br_forward.c中:

可见,bridge选择好了net device之后,就使用br_dev_queue_push_xmit (当然,更具体点就是通过dev_queue_xmit)发送数据了。 5,forward 为了便于分析网络过程,作者给vm配置了tap网卡,在虚拟机中发送数据包,然后抓到了这份backtrace。

backtrace从后向前看: a,首先看qemu使用tap发送包的进程。尽管这里的栈底不是syscall,但是qemu发送完数据包到这里是同步的。 b,使用__do_softirq处理网络包。 c,网络包的通用逻辑—net_rx_action->process_backlog->__netif_receive_skb->__netif_receive_skb_core。 d,调用到了3步注册的br_handle_frame。 e,继续deliver,再执行转发,最终使用br_dev_queue_push_xmi发送数据包。 6,br_handle_frame_finish 选择br_handle_frame_finish的关键逻辑:

a,如果是广播地址(当然,这里操作的地址都是MAC,并非IP地址),会使用flood的方式forward。当然,里面还需要检查vlan。具体逻辑下文展开。 b,如果是多播,则需要查询数据库(mdb是使用slab实现的),再进行多播。相比起代码,看协议更容易写。 c,查询forward db(也是使用slab实现的),在3步中br_fdb_insert已经把eth0加入到fdb中。 看到这里,大约也可以看到bridge的模型了:在二层网络上,广播,多播,转发,这几个是bridge的核心能力。至于mdb和fdb,都是辅助实现功能的方式。vlan可以算是一个扩展能力。 7,br_flood 广播的情况下,执行flood。逻辑比较简单,就是对bridge的port list进行遍历,逐个尝试发送数据包。

这里需要注意的是,在maybe_deliver中还是执行了vlan校验的,所以flood并不会跨vlan的。 8,fdb fdb的实现逻辑在linux-4.4.61/net/bridge/br_fdb.c。 使用slab,hash list等技术实现。需要注意的是在6步中用到的fdb查询函数---__br_fdb_get。

这里进行了vlan id的检查,也就是常说的逻辑:vlan id不匹配,则网桥(或者说交换机)会拒绝转发数据包。 9,eth handle frame 再来看另外的一份backtrace,物理网卡收到数据包,然后到网桥的过程:

老习惯,从下向上分析。首先网卡产生了irq,kernel响应irq。然后softirq继续处理(原因就是网卡的处理流程可能会比较长,而irq需要尽快返回,所以先响应了irq,在用softirq继续处理)。把数据交给网卡的driver(作者使用的是e1000网卡,所以bt中有e1000_receive_skb这样的函数),driver处理完,数据交给通用的网络数据包处理逻辑。 相比于上面的5步的trace,前面触发的逻辑不同,但是后面处理的逻辑是相同的。换言之,就是不管网络数据,还是tap这种虚拟网卡的数据,它们接到bridge上之后,收到数据后都会把数据交给bridge,bridge决定数据包下一步的流向。 这里再多提到一句,如果网卡没有接入到bridge中,那么就会把数据包提交到三层上,例如ip,ip层上判断传输层协议,如果是udp,就组装包,交给udp协议栈。 10,tap write 既然虚拟机使用tap类型网卡,就在这里继续分析一下qemu对tap类型网卡的写操作: 抓到这份bt,可以看到:

a,qemu通过系统调用写tap设备。 b,vfs的通用处理逻辑—SyS_writev→vfs_writev→do_readv_writev→do_iter_readv_writev。 c,调用tap设备的write逻辑。 d,触发softirq。

继续分析softirq:

好吧,可以看到,这里已经接上了5步的最后一帧了---do_softirq_own_stack。 11,tap read

这里简单了很多,就是bridge向tap转发了数据包之后,qemu通过read系统调用,经过vfs最后到达了tap设备的driver,最终拿到了数据。 后记: qemu使用tap和外面建立桥接的这个过程,困扰了作者很长时间。“二层网络”这个概念,也让作者很迷惑。 这次代码的整体分析,了解了基本原理。 这当中,在交换机的概念理解和细节上,还请教过几次朋友。在这里感谢@吉普。

下一篇
举报
领券