Linux下的虚拟Bridge实现
Linux下的Bridge也是一种虚拟设备,这多少和vlan有点相似,它依赖于一个或多个从设备。与VLAN不同的是,它不是虚拟出和从设备同一层次的镜像设备,而是虚拟出一个高一层次的设备,并把从设备虚拟化为端口port,且同时处理各个从设备的数据收发及转发,再加上netfilter框架的一些东西,使得它的实现相比vlan复杂得多。
1.Bridge的功能框图
它是Linux下虚拟出来bridge设备,Linux下可用brctl命令创建br设备,如下
brctl addbr brname
然后添加port,并进行相应配置,就可以使用了
brctl addif brname eth0
brctl addif brname eth1
ifconfig brname IP up
ifconfig eth0 0.0.0.0 up
ifconfig eth1 0.0.0.0 up
可见br设备是建立在从设备之上的(这些从设备可以是实际设备,也可以是vlan设备等),并且可以为br准备一个IP(br设备的MAC地址是它所有从设备中最小的MAC地址),这样该主机就可以通过这个br设备与网络中的其它主机通信了(详见发送功能框图)。
另外它的从设备被虚拟化为端口port,它们的IP及MAC都不再可用,且它们被设置为接收任何包,最终由bridge设备来决定数据包的去向:接收到本机、转发、丢弃(详见接收功能框图)。
发送功能 接收功能
简单的数据结构框图如下所示。
2.Bridge设备的创建
和Vlan一样,bridge也被当成一个module加载进内核,它的module_init()函数和vlan差不多,进行一些namespace的注册,特殊的是它还注册了一个netfilter_ops,在内核全局的HOOK函数表中增加了7个函数,其中5个的pf=Bridge,另两个的pf分别为INET、INET6,它们主要用于bridge中的netfilter操作(后面会细讲)。
最后,也是我们这最关心的是,它注册了一个ioctl函数br_ioctl_deviceless_stub(),该ioctl函数和vlan的一样,都会作为sock_ioctl()的特殊情况被调用。映射到应用层,它应该是对某个socket插口进行ioctl操作,详见brctl源码。该ioctl函数中最主要的就是br_add_bridge(net,buf),用于创建bridge设备,如下图所示:
该函数调用netdev_alloc(),申请net_device(),并分配私有空间net_bridge()结构,指明初始化函数为br_dev_setup(),最后register_netdev()把该设备组册进内核,可见bridge设备和一般的设备差不多。
主要看其中br_dev_setup(),首先初始化设备的type、flags为bridge,然后最关键的是设置其dev->netdev_ops = br_netdev_ops,即内核为bridge设备准备好了一套通用的驱动函数,这个直接关系到bridge的工作方法,后面再细讲。然后初始化私有空间net_bridge()结构,设置bridge的本地设备及从设备的list(当然这是还没有从设备加进来),然后设置了桥的group_address,即上一节所说的特殊的MAC地址,最后还初始化了timer相关的。
这时bridge还不完整,还需添加port从设备,由命令brctl addif brname portdev完成,但要注意,虽然还是brctl命令,但此时的操作对象是已经存在的bridge设备,映射到内核中就是br_netdev_ops->ioctl()中的br_add_if()(它是br设备的ioctl操作,和之前那个sock_ioctl的分支不是一个层次上的)。至于怎么从应用层直接操作底层的net_device设备的,可以参见brctl源码,以后再看吧,先看看这里的br_add_if(),如下图:
首先判断dev从设备必须不是loopback,不是bridge,不是其他bridge的port,且要是ethernet设备,才能继续;然后根据br、dev选择一个index号,并分配一个新的net_bridge_port结构,初始化之,并将它加入bridge的port_list中;最后br的一些物理参数,其MAC地址为所有从设备中MAC最小的(由上一节知,从设备被设置成全接收模式,其IP和MAC都没有了),且其MTU也为所有从设备中最小的。
上面设置br的相关参数,下面还要设置从设备,首先使dev->master=br_dev(实际上就是构成上一节数据结构中的索引关系);然后设置dev->prive_flags加上IFF_BRIDGE_PORT,这样它就不能再作为其他br的从设备了;最后也是最关键的,设置dev->rx_handler为br_handler_frame(),为数据接收作准备。
3.Bridge设备的发送流程
前面也讲过了,Linux下的bridge设备,对下层而言是一个桥设备,进行数据的转发(实际上对下也有接收能力,下一节讲)。而对上层而言,它就像普通的ethernet设备一样,有自己的IP和MAC地址,那么上层当然可以把它加入路由系统,并利用它发送数据啦,并且很容易想到,它的发射函数最终肯定是利用某个从设备的驱动去完成实际的发送的,这个和VLAN是相通的。具体看代码:
上层根据目的IP地址,路由选择了该br_dev设备发送,并且由ARP缓存中得到了对应的目的MAC,填写在了skb中,然后启动了发送流程dev_queue_xmit(skb)。因为此时的skb->dev为br_dev,无queue,直接去调用br设备的发送函数,该函数就是br_netdev_ops中定义的br_dev_xmit(skb,br_dev)。
该函数首先根据目的MAC地址,确定是广播还是单播,这里仅讨论单播时,根据DMAC在net_bridge的fdb_hash中找到相应的net_bridge_fdb_entry项,并索引到对应的端口net_bridge_port。最后利用该端口的从设备来发送数据,注意,这里是直接调用dev->ops->ndo_start_xmit(skb,dev)的,一放面这里的dev已经是从设备了,另一方面,这里没有像VLAN中那样重定位skb->dev,并重启发送流程dev_queue_xmit(),是因为一个从设备只能作为一个bridge的port,没有其它身份,不存在竞争问题。
4.Bridge设备的接收流程
和VLAN一样,实际接收由硬件设备完成,最终通过netif_receive_skb(skb)函数提交给上层,而在该函数中会处理vlan、bridge这类特殊设备。与LVAN的仅是把skb设备重定位以实现对上层透明的要求不同,Bridge接受过程复杂得多,因此专门注册了一个函数来处理,即前面提到的rx_handler(),它被注册在port设备的net_device结构中(这算是port设备失去自身IP、MAC的一个补偿吧J),如下图所示。只有作为bridge的从设备才会注册rx_handler(),并在这里执行,处理桥接,普通的设备不会执行到这里。
这里两种典型的返回值是RX_CONSUMED、RX_PASS,后者表示处理了一下回来,继续之前的流程,实际上就是对应的接收功能框图中的第一种情况;前者表示该skb已不再属于这个从设备了,而是被提交给了br设备,所以本次netif_receive_skb()就不用管啦,直接goto out,这里还要再分两种情况,一是转发的,这时br就真的充当了桥的角色,二是由br提交给上层的,这时br充当的是一个以太网设备,如前面所述。
要处理这么多情况,代码需设计得很巧妙,这里的rx_handler被设置为br_handle_frame(**pskb),看具体代码:
功能框架清楚了,代码流程就清楚了,就不细看了,有几个注意的地方:一是bridge的端口处于FORWARD或LEARNING状态没多大区别,只是FORWARD要多执行一个二层防火墙,所有用了一个中间没有break的switch结构;二是要时刻记着,bridge本身除了有转发端口外,自己也是一个设备,广播(多播)时也要发一份给自己,且是以br_dev的身份递交上层;三是所有的HOOK函数都会比较复杂,因为内核的netfilter框架建立在网络层,而bridge在链路层就转发了,相当于跳过了netfilter,所以在这些hook中都会去调用INET域的hook函数。
5.小结
通过对Bridge和vlan的学习,了解了网络栈底层的工作方式,发送这个主动过程相对简单,而接收过程则相对复杂,用到BH模型,NAPI等。
Vlan和bridge功能有所不同,但相似处很多,更重要的是:它们都对上层透明,所以不会牵扯到协议域的问题。
Linux下的虚拟Bridge实现相关推荐
- Linux下Apache虚拟主机配置
说明: Apache版本:httpd-2.2.31 Apache安装目录:/usr/local/apache 目的: 配置Apache虚拟主机 具体操作: 1.修改Apache主配置文件 cp /us ...
- linux下使用虚拟光驱,在Linux操作系统下使用虚拟光驱的方法
在Linux下使用虚拟光驱 其实根本不需要什么虚拟光驱软件,用mount命令就可以完成. 1. 把光盘制作成iso文件 cp /dev/cdrom XXXXX.iso XXXXX.iso为你所命名的镜 ...
- linux apache部署php,Linux下apache虚拟主机配置多版本php同时运行 | 系统运维
学习本教程须掌握: 1.Linux下指定版本编译安装LAMP 2.Linux下Apache虚拟主机配置 3.CentOS 7.x编译安装Nginx1.10.3+MySQL5.7.16+PHP5.2 5 ...
- Linux下使用虚拟光驱
在Linux下使用虚拟光驱 其实根本不需要什么虚拟光驱软件,用mount命令就可以完成. 1. 把光盘制作成iso文件 cp /dev/cdrom XXXXX.iso XXXXX.iso为你所命名的镜 ...
- Linux虚拟用户怎么建立,linux下ftp虚拟用户的创建方法
本文介绍下,在linux系统中,为ftp创建虚拟用户的方法,这里使用vsftpd作为服务器软件,有需要的朋友参考学习下. vsftpd虚拟用户创建示例 Linux的FTP服务支持3种用户: 1.匿名帐 ...
- Linux下的虚拟串口驱动(一)
欢迎转载,转载请注明出处. 前言 最近准备在Linux下,实现虚拟串口驱动:但因为毕业后,一直从事的是裸机驱动开发,所以Linux下的设备驱动,就慢慢忘记了:为了实现这一功能,在网上也查找了很多资料, ...
- linux下安装虚拟天文馆,如何在Ubuntu 20.04、18.04中安装Stellarium 0.20.0虚拟天文馆
如何在Ubuntu 20.04.18.04中安装Stellarium 0.20.0虚拟天文馆 几天前发布了Stellarium 0.20.0,它时一个免费开源的桌面虚拟天文馆软件.以下是在Ubuntu ...
- Linux下自定义虚拟串口驱动
前些天给新的板子修改BUG的时候,发现这块板子的串口是接在板载MCU上,我们的主SOC(海思HI3520D)上已经没有多余的串口.于是问题来了- 我们很多的上层应用都是通过串口和传感器通讯的,而且程序 ...
- linux下ftp虚拟账号创建2
环境:CentOS 5.0 操作系统 一.安装: 1.安装Vsftpd服务相关部件: [root@CentOS ~]# yum install vsftpd* 或 [root@CentOS ~]# ...
最新文章
- MVC,三层架构,工厂模型,七层
- [LOJ 6288]猫咪[CF 700E]Cool Slogans
- flex 正则表达式匹配规则
- 复制文本朗读_原创:昭明文选配乐朗读 卷第五十一 论一 东方曼倩 非有先生论 王子渊 四子讲德论 并序...
- lamda获取参数集合去空_lambda表达式对集合的遍历
- 在 Mac 上如何将 Better Zip 设置为自动更新?
- MySQL-快速入门(9)视图
- Ansys模态计算结果图片批量导出命令流
- python卡尔曼滤波室内定位_基于卡尔曼滤波的室内定位方法设计
- Cell-type–specific || 单细胞文章新范式
- 分位数回归模型学习笔记
- 计算机键盘图 指法,键盘指法练习图
- 中兴--维克多博士问题(背包问题升级版)
- Kotlin学习笔记12——数据类和密封类
- 三星识别文字_三星手机实现音频转文字、文字录入的方法,不知道的看这里!一遍就会了...
- Linux WWW 服务器
- JS 基础(11月1日)
- 数据可视化echarts介绍
- Idea 中解决git冲突
- 如何清除office的KMS的激活信息
热门文章
- SpringBoot项目集成Mybatis Plus(二)代码生成器
- java 蓝桥杯 Huffuman树
- 喵喵的华为DevCloud使用指南(2)-看板项目
- matplotlib调整子图尺寸,消除大图白边框
- php获取当前几点,学习猿地-php 怎么获取当前几点
- bootstrap----表格
- jackson 复杂对象集合 的几种简单转换
- TortoiseSVN的bin目录下面没有svn.exe
- The file “XXX.app” couldn’t be opened because you don’t have permission to view it.问题修复...
- [转载]什么是 Design Hackathon?