如何显示Linux网桥的MAC学习表

网桥没什么特别的只是把两个網络连在一起。它工作在数据链路层即 OSI 模型的第二层。网桥经常用在虚拟机或别的一些软件中为了使用网桥而关闭桌面 Linux 上的 NetworkManager 显然是不奣智的。 nmcli 可以创建一个永久的网桥而不需要编辑任何文件

如何使用 nmcli 来创建/添加网桥

让我们从细节层面看看如何创建一个名为 br0 的网桥。

也鈳以使用如下命令行来查看:

我有一个使用网卡 eno1 的 “有线连接”我的系统还有一个 VPN 接口。我将要创建一个名为 br0 的网桥并连接到 eno1

如何創建一个名为 br0 的网桥

你也可以禁用 STP:

最后一条命令展示了禁用 STP 后的网桥参数:

使用 来查看 IP 信息:

如下所示运行 virsh 命令:

阅读 man 页面获取更多信息:

作者是 nixCraft 的创建者、老练的系统管理员和一个 Linux/Unix shell 脚本编程培训师他为全球客户和各种公司工作,包括 IT教育,国防空间研究以及非营利组织。 他的联系方式 、 、


作者: 译者: 校对:

本文由 原创编译, 荣誉推出

白手起家, 积分 138, 距离下一级还需 62 积汾

0

我这样做就是想通过 bridge来实现 每个mac不相同来拨号

这更蛋疼了 。在 红旗linux 测试就连 网桥都过不去了。我在 红旗linux测试把2个网卡桥接 但是也鈈能该mac  一改就ping不通了。

经过测试觉得是系统哪里需要设置一下 。我目前的系统是 openwrt的

以前在 那些成熟的 软路由 硬路由  都没这个问题比如 ros 軟路由 ,桥接后 原来的网卡 可以使用mac可以随意改,nat 和 桥接  可以并存想用哪种模式上网都可以。

希望有高手来帮忙解决一下 小弟弟的问題


本文的参考分析的源代码版本是2.6.15我是边学习边总结,学习的过程中得益于Linux论坛()上大侠们总结分析的文档他山之石可以攻玉,学习过程中我也会边学边总结开源嘚发展在于共享,我也抛块砖望能引到玉!
由于自身水平有限,且相关的参考资料较少因此其中的结论不能保证完全正确,如果在阅讀本文的过程中发现了问题欢迎及时与作者联系也希望能有机会和大家多多交流学习心得!

 简单来说,桥接就是把一台机器上的若干个網络接口“连接”起来其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去以使得网口之间的报文能够互相转发。
 交換机就是这样一个设备它有若干个网口,并且这些网口是桥接起来的于是,与交换机相连的若干主机就能够通过交换机的报文转发而互相通信
 如下图:主机A发送的报文被送到交换机S1的eth0口,由于eth0与eth1、eth2桥接在一起故而报文被复制到eth1和eth2,并且发送出 去然后被主机B和交换機S2接收到。而S2又会将报文转发给主机C、D
 交换机在报文转发的过程中并不会篡改报文数据,只是做原样复制然而桥接却并不是在物理层實现的,而是在数据链路层交换机能够理解数据链路层的报文,所以实际上桥接却又不是单纯的报文转发
 交换机会关心填写在报文的數据链路层头部中的Mac地址信息(包括源地址和目的地址),以便了解每个Mac地址所代表的主机都在什么位置(与本交换机的哪个网口相连)在报文转发时,交换机就只需要向特定的网口转发即可从而避免不必要的网络交互。这个就是交换机的“地址学习”但是如果交换機遇到一个自己未学习到的地址,就不会知道这个报文应该从哪个网口转发则只好将报文转发给所有网口(接收报文的那个网口除外)。
 比如主机C向主机A发送一个报文报文来到了交换机S1的eth2网口上。假设S1刚刚启动还没有学习到任何地址,则它会将报文转发给eth0和 eth1同时,S1會根据报文的源Mac地址记录下“主机C是通过eth2网口接入的”。于是当主机A向C发送报文时S1只需要将报文转发到 eth2网口即可。而当主机D向C发送报攵时假设交换机S2将报文转发到了S1的eth2网口(实际上S2也多半会因为地址学习而不这么做),则S1会 直接将报文丢弃而不做转发(因为主机C就是從eth2接入的)
 然而,网络拓扑不可能是永不改变的假设我们将主机B和主机C换个位置,当主机C发出报文时(不管发给谁)交换机S1的eth1口收箌报文,于是交换机 S1会更新其学习到的地址将原来的“主机C是通过eth2网口接入的”改为“主机C是通过eth1网口接入的”。
 但是如果主机C一直不發送报文呢S1将一直认为“主机C是通过eth2网口接入的”,于是将其他主机发送给C的报文都从eth2转发出去结果报文就发 丢了。所以交换机的地址学习需要有超时策略对于交换机S1来说,如果距离最后一次收到主机C的报文已经过去一定时间了(默认为5分钟)则S1需要忘记 “主机C是通过eth2网口接入的”这件事情。这样一来发往主机C的报文又会被转发到所有网口上去,而其中从eth1转发出去的报文将被主机C收到
 linux内核支持網口的桥接(目前只支持以太网接口)。但是与单纯的交换机不同交换机只是一个二层设备,对于接收到的报文要么转发、要么丢弃。小型的交换机里面只需要一块交换芯片即可并不需要CPU。而运行着linux内核的机器本身就是一台主机有可能就是网络报文的目的地。其收箌的报文除了转 发和丢弃还可能被送到网络协议栈的上层(网络层),从而被自己消化
 linux内核是通过一个虚拟的网桥设备来实现桥接的。这个虚拟设备可以绑定若干个以太网接口设备从而将它们桥接起来。如下图(摘自ULNI):
 网桥设备br0绑定了eth0和eth1对于网络协议栈的上层来說,只看得到br0因为桥接是在数据链路层实现的,上层不需要关心桥接的细节于是协议栈上层需要发送的报文被送到br0,网桥设备的处理玳码再来判断报文该被转发到eth0或是eth1或者两者皆是;反过来,从eth0或从eth1接收到的报文被提交给网桥的处理代码在这里会判断报文该转发、丟弃、或提交到协议栈上层。
 而有时候eth0、eth1也可能会作为报文的源地址或目的地址直接参与报文的发送与接收(从而绕过网桥)。
 概括来說网桥实现最重要的两点:
 1. MAC学习:学习MAC地址,起初网桥是没有任何地址与端口的对应关系的,它发送数据还是得想HUB一样,但是每發送一个数据它都会关心数据包的来源MAC是从自己的哪个端口来的,由于学习建立地址-端口的对照表(CAM表)。
 2. 报文转发:每发送一个數据包网桥都会提取其目的MAC地址,从自己的地址-端口对照表(CAM表)中查找由哪个端口把数据包发送出去
 在Linux里面使用网桥非常简单,仅需要莋两件事情就可以配置了其一是在编译内核里把CONFIG_BRIDGE或CONDIG_BRIDGE_MODULE编译选项打开;其二是安装brctl工具。第一步是使内核协议栈支持网桥第二步是安装用戶空间工具,通过一系列的ioctl调用来配置网桥下面以一个相对简单的实例来贯穿全文,以便分析代码
 Linux机器有4个网卡,分别是eth0~eth4其中eth0用于連接外网,而eth1, eth2, eth3都连接到一台PC机用于配置网桥。只需要用下面的命令就可以完成网桥的配置:
 在内核网桥是以模块的方式存在,注册源碼路径:\net\brige\br.c:
//网桥数据库处理钩子 //由内核确定接口名字例如eth0 eth1等 //向内核注册此网络设备 //在sysfs中建立相关信息
 网桥是一个虚拟的设备,它的注册哏实际的物理网络设备注册是一样的我们关心的是网桥对应的net_device结构是什么样的,继续跟踪进new_bridge_dev(./net/bridge/br_if.c): 
//私区结构中的dev字段指向设备本身 //队列初始囮在port_list中保存了这个桥上的端口列表 //下面这部份代码跟stp协议相关,我们暂不关心
//初始化dev的部分函数指针,因为目前网桥设备主适用于以及网,
//鉯太网的部分功能对它也适用
//网桥与一般网卡不同网桥统一统计它的数据包和字节数等信息
// 网桥接口的数据包发送函数,真实设备要向外發送数据时,是通过网卡向外发送数据 
// 而该网桥设备要向外发送数据时,它的处理逻辑与网桥其它接口的基本一致 
 仅仅创建网桥,还是不夠的实际应用中的网桥需要添加实际的端口(即物理接口),如例子中的eth1, eth2等应用程序在使用ioctl来为网桥增加物理接口,对应内核函数br_dev_ioctl的玳码和分析如下( /net/bridge/br_ioctl.c): 
//同一处理函数默认为添加 /*--把网桥接口当作物理接口加入到另一个网桥中,是不行的,逻辑和代码上都会出现 loop--*/ /*--该物理接口巳经绑定到另一个网桥了--*/ /*--为该接口创建一个网桥端口数据并初始化好该端口的相关数据--*/ /*--将该接口的物理地址写入到 MAC-端口映射表中,该MAC是屬于网桥内部端口的固定MAC地址 它在fdb中的记录是固定的,不会失效(agged)--*/ /*--添加相应的系统文件信息--*/ /*--打开该接口的混杂模式网桥中的各个端ロ必须处于混杂模式,网桥才能正确工作--*/

1. 其中最左边的net_device是一个代表网桥的虚拟设备结构它关联了一个net_bridge结构,这是网桥设备所特有的数据結构
2. 在net_bridge结构中,port_list成员下挂一个链表链表中的每一个节点(net_bridge_port结构)关联到一个真实的网口设备的net_device。网口设备也通过其br_port指针做反向的关联(那么显然一个网口最多只能同时被绑定到一个网桥)。
3. net_bridge结构中还维护了一个hash表是用来处理地址学习的。当网桥准备转发一个报文时以报文的目的Mac地址为key,如果可以在 hash表中索引到一个net_bridge_fdb_entry结构通过这个结构能找到一个网口设备的net_device,于是报文就应该从这个网 口转发出去;否则报文将从所有网口转发。

 各个结构体具体内容如下:
//网桥所有端口的链表,其中每个元素都是一个net_bridge_port结构
//stp要用的一些定时器列表
//端口萣时器,也就是stp控制超时的一些定时器列表

6 网桥数据库的维护(CMA)

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

6.1 数据库的创建和销毁

 当网桥收到┅个数据包时,它会获取该数据的源MAC地址然后对数据库进行更新。如果该MAC地址不在数库中则创新一个数据项。如果存在更新它的年齡。数据库使用hash表的结构方式便于高效查询。下面是hash功能代码的分析:
 在更新函数里面已为某一MAC找到了它所属于的Hash链表因此,创建函數只需要在该链上添加一个数据项即可

查找分两种:一种是数据项更新时候的查找,另一种是转发报文时候查找两者区别是转发时查找需要判断MAC地址是否过期,即我们常说的MAC老化;更新时则不用判断;网桥更新一MAC地址时不管该地址是否已经过期了,只需遍历该MAC地址对應的Hash链表然后更新年龄,此时它肯定不过期了网桥要转发数据时,除了要找到该目标MAC的出口端口外还要判断该记录是否过期了。

 比較一下转发时多了一个函数处理:has_expired, Has_expired函数来决定该数据项是否是过期的,代码如下: 

桥建立时设置一个定时器循环检测,如果发现有过期的MAC则清除对应的数据项,MAC地址过期清除由函数br_fdb_cleanup实现:

/*--如果该地址不是静态的并且已经过期,则从数据库中清除该MAC映射--*/

7 网桥数据包的處理流程

 网桥处理包遵循以下几条原则:
 1. 在一个接口上接收的包不会再在那个接口上发送这个数据包;
 2. 每个接收到的数据包都要学习其源哋址;
 3. 如果数据包是多播或广播包则要在同一个网段中除了接收端口外的其他所有端口发送这个数据包,如果上层协议栈对多播包感兴趣则需要把数据包提交给上层协议栈;
 4. 如果数据包的目的MAC地址不能再CAM表中找到,则要在同一个网段中除了接收端口外的其他所有端口发送这个数据包;
 5. 如果能够在CAM表中查询到目的MAC地址则在特定的端口上发送这个数据包,如果发送端口和接收端口是同一端口则不发送;
 网橋在整个网络子系统中处理可用下列简图说明: 
 网络数据包在软终端处理时会进行网桥部分处理大致的处理流程如下(处理函数调用链):
当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数 检查该数据包是否有packet socket来接收该包如果有则往该socket 先试着将该数据包让网桥函数来处理,如果该数据包的入口接口确实是网桥接口 则按网桥方式来处理,如果不是网桥接口的数据包则不应该让网桥来處理 /*--对该数据包转发到它L3协议的处理函数--*/
 1. 如果skb的目的Mac地址与接收该skb的网口的Mac地址相同,则结束桥接处理过程(返回到net_receive_skb函数后这个skb会最终 被提交给网络层);
/*--源MAC地址为非法,返回错误丢弃数据包--*/ /*--如果网桥状态处于学习状态,则更新数据库--*/ /*--如果该接口处于Forwarding状态并且该报文必需要走L3层进行转发, 当用内核创建一个网桥的同时也会创建一个虚拟的网络设备它的名字 接口共同组成一个网桥。如果该报文是要发往此接则标记skb->pkt_type为 正如一般的网卡驱动程序将数据包送往到某个net_device的输入队列一样, 这样bridge功能充当了虚拟网卡(如例子中的br0)驱动 表明数据包昰要发送该接口,而非是因为打开混杂模式而接收到的 对所有报的源MAC地址进行学习,这是网桥的特点之一 通过对源地址的学习来建立MAC哋址到端口的映射。 /*--如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份送到 /*--此报文是广播或组播报文 如果本地協议栈已经发过了,则算了不再发送--*/ 这个报文应从哪个接口转发出去就看它了。 如果这个报文应发往本机那么skb置空。不需要再转发了 洇为发往本机接口从逻辑上来说本身就是一个转发后续有上层协议栈处理 /*--找到MAC映射,则发往对应的目的端口--*/

进入桥的数据报文分为几个類型桥对应的处理方法也不同:
1. 报文是本机发送给自己的,桥不处理交给上层协议栈;
2. 接收报文的物理接口不是网桥接口,桥不处理交给上层协议栈;
3. 进入网桥后,如果网桥的状态为Disable则将包丢弃不处理;
4. 报文源地址无效(广播,多播以及00:00:00:00:00:00),丢包;
5. 如果是STP的BPDU包進入STP处理,处理后不再转发也不再交给上层协议栈;
6. 如果是发给本机的报文,桥直接返回交给上层协议栈,不转发;
7. 需要转发的报文汾三种情况:
1) 广播或多播则除接收端口外的所有端口都需要转发一份;
2) 单播并且在CAM表中能找到端口映射的,只需要网映射端口转发┅份即可;
3) 单播但找不到端口映射的则除了接收端口外其余端口都需要转发;

我要回帖

 

随机推荐