
 |
<noscript></noscript>
<noscript><tr
valign="top"><td width="8"><img alt="" height="1" width="8"
src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16"
height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small"
width="122"><p><span class="ast">未显示需要 JavaScript
的文档选项</span></p></td></tr></noscript>
|
<!-- START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!-- END RESERVED FOR FUTURE USE INCLUDE FILES-->
|
级别: 初级
祝顺民
(getmoon@163.com
)
XML error: Please enter a value for the author element's jobtitle attribute, or the company-name element, or both. |
2004 年 3 月 09 日
本
文分析了linux
2.4.x内核的网桥的实现方法,并且描述了如何使用2.4中的网桥。网桥,类似于中继器,连接局域网中两个或者多个网段。它与中继器的不同之处就在于它
能够解析它收发的数据,读取目标地址信息(MAC),并决定是否向所连接网络的其他网段转发数据包。为了能够决策向那个网段发送数据包,网桥学习接收到数
据包的源MAC地址,在本地建立一个以MAC和端口为记录项的信息数据库。
<!-- START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
<!-- END RESERVED FOR FUTURE USE INCLUDE FILES-->
一、Linux内核网桥的实现分析
Linux 内核分别在2.2 和 2.4内核中实现了网桥。但是2.2 内核和 2.4内核的实现有很大的区别,2.4中的实现几乎是全部重写了所有的实现代码。本文以2.4.0内核版本为例进行分析。
在分析具体的实现之前,先描述几个概念,有助于对网桥的功能及实现有更深的理解。
- 冲突域
一
个冲突域由所有能够看到同一个冲突或者被该冲突涉及到的设备组成。以太网使用C S M A / C D(Carrier Sense
Multiple Access with Collision
Detection,带有冲突监测的载波侦听多址访问)技术来保证同一时刻,只有一个节点能够在冲突域内传送数据。网桥或者交换机,构成了一个冲突域的边
界。缺省情况下,网桥中的每个端口实际上就是一个冲突域的结束点。
- 广播域
一
个广播域由所有能够看到一个广播数据包的设备组成。一个路由器,构成一个广播域的边界。网桥能够延伸到的最大范围就是一个广播域。缺省的情况下,一个网桥
或交换机的所有端口在同一个广播域中。VLAN技术可以把交换机或者网桥的不同端口分割成不同的广播域。一般情况下, 一个广播域代表一个逻辑网段。
- 网桥中的CAM表
网桥和交换机一样,为了能够实现对数据包的转发,网桥保存着许多(MAC,端口)项。所有的这些项组成一个表,叫做CAM表。每个项有超时机制,如果一定时间内未接收到以这个MAC为源MAC地址的数据包,这个项就会被删除。
图1:一个交换网络的逻辑图

在Linux内核网桥的实现中,一个逻辑网段用net_bridge结构体表示。一个逻辑网段需要保留的信息有:
- 本逻辑网段中所有的端口(port_list)
每个端口用net_bridge_port结构体来表示,从net_bridge_port结构体中可以看出,它主要有:
- 逻辑网段中的下一个端口(next)
- 本端口所属的逻辑网段(br)
- 本端口所指向的物理网卡(dev)
- 本端口在网桥中的编号(port_no)
- 用于生成树管理的信息
-
一个逻辑网段中可以具有很多个端口,所有的端口都挂在以port_list为链表头的链表上。
本网段中CAM表(hash[BR_HASH_SIZE])
CAM表中的每个项用net_bridge_fdb_entry结构体代表,每项中有:
- 用于CAM表连接的链表指针(next_hash,pprev_hash)
- 此项当前的引用计数(use_count)
- MAC地址(addr)
- 此项所对应的端口(dst)
- 处理MAC超时(ageing_timer)
- 是否是本机的MAC地址(is_local)
- 是否是静态MAC地址(is_static)
-
一个逻辑网段中的所有表项形成一个CAM表,他们之间的组织关系是一个HASH链表。HASH链的个数为BR_HASH_SIZE(256)。
本逻辑网段用于和外部通信的虚拟网络设备(dev)
Linux网桥可以在网桥上为每个逻辑网段配置一个IP,用于和外部通信。实际上这个IP不是配置在一个特定的物理网卡上面, 而是建立一个虚拟的网卡,虚拟网卡可以附在每个同一逻辑网段的物理网卡上,让这个网卡可以象所有的物理网卡一样工作。从而使网桥可以和外部通信。
- 本逻辑网段虚拟网卡的统计数据(statistics)
按照Linux网卡驱动的接口,一个网卡的统计信息是由每个网卡的私有数据处理的。一般的写法是用dev->priv来指向每个网卡的统计数据。网卡的get_stats方法就是用来读取统计数据。
- 用户一个网段的生成树(STP)信息
以上对几个结构体的描述和分析可以通过下图来表示:
图2:Linux网桥数据结构描述图

描述了网桥的数据结构后,就可以开始数据包处理流程的分析。
网桥处理包遵循着以下几条原则:
- 在一个接口上接收到的包不会再在那个接口上发送这个数据包。
- 每个接收到的数据包都要学习其源MAC地址。
- 如果数据包是多播包或广播包,则要在同一个网段中除了接收端口外的其他所有端口发送这个数据包,如果上层协议栈对多播包感兴趣,则需要把数据包提交给上层协议栈。
- 如果数据包的目的MAC地址不能在CAM表中找到,则要在同一个网段中除了接收端口外的其他所有端口发送这个数据包。
- 如果能够在CAM表中查询到目的MAC地址,则在特定的端口上发送这个数据包,如果发送端口和接收端口是同一端口,则不发送。
在网络软中断处理函数net_rx_action中,嵌入了handle_bridge用于把数据包skb送入网桥模块处理。
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port != NULL &&
br_handle_frame_hook != NULL) {
handle_bridge(skb, pt_prev);
dev_put(rx_dev);
continue;
}
#endif
|
br_handle_frame_hook是网桥处理
接收到数据包的中入口,网桥初始化(br_init)的时候,把br_handle_frame_hook赋值为br_handle_frame。skb
->dev->br_port用于判断接收到这个数据包的接口是否是网桥中的一个端口,如果是,skb->dev->
br_port不为NULL,那么数据包应该由网桥处理。反之,数据包由上层协议栈处理。网桥中虚拟网卡对应的数据包就是在这个判断点时不再进入网桥。
(实际上虚拟网卡并不会自己主动接收数据包,而是在网桥处理中把数据包向本地上层协议栈提交,并且修改了skb->dev,使得数据包不会多次进入
桥处理代码)。
前面提到,网桥处理接收包的入口是br_handle_frame(net/bridge/br_input.c)函数。
br_handle_frame
函数首先从skb中获得这个包属于的逻辑网段。然后调用__br_handle_frame进行转发处理。
br_handle_frame函数里有一个值得了解的地方,里面有一个加读锁。因为在转发中需要读CAM表,所以必须加读锁,避免在这个过程中另外的内
核控制路径(如多处理机上另外一个CPU上的系统调用)修改CAM表。
对输入包的转发决策都是在__br_handle_frame函数中。这个函数的处理可以分为以下几个部分:
- 如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份送到AF_PACKET协议处理体(网络软中断函数net_rx_action中ptype_all链的处理)。
if (br->dev.flags & IFF_PROMISC) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
|
- 如果源MAC地址是多播或者是广播地址,那么这个包格式是错误的,简单的丢弃。
if (skb->mac.ethernet->h_source[0] & 1)
goto freeandout;
|
- 如果是一个多播包,则需要向本机的上层协议栈传送这个数据包(如果在之前没有向上提交的话,即passedup为0。如果为1,则前面已经发送了,现在就不需要提交了,在后面中的处理都是一样的)。
if (!passedup &&
(dest[0] & 1) &&
(br->dev.flags & IFF_ALLMULTI || br->dev.mc_list != NULL)) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
passedup = 1;
br_pass_frame_up(br, skb2);
}
}
|
- 如果启动了生成树协议,一个生成树包需要由生成树协议处理模块单独处理。如果不支持,则这个包的目的MAC肯定在CAM中查询不到,所以是向所有的端口发送(除接收口)。这样才不会影响整个网络的生成树协议运行。
if (br->stp_enabled &&
!memcmp(dest, bridge_ula, 5) &&
!(dest[5] & 0xF0))
goto handle_special_frame;
|
- 如果接收端口不是处于LEARNING或者FORWARDING,那么就学习这个包的源MAC地址,或者更新CAM表中相应项的定时器。
if (p->state == BR_STATE_LEARNING ||
p->state == BR_STATE_FORWARDING)
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
|
- 如果是一个多播包或广播包,则调用br_flood函数向每个口发送(除接收口)这个数据包。如果之前没有提交上层协议,则需要克隆一个包提交上层协议。
if (dest[0] & 1) {
br_flood(br, skb, 1);
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
return;
}
|
- 用接收到数据包的目的MAC地址查询CAM表。
dst = br_fdb_get(br, dest);
|
- 查询CAM表后,如果能够找到表项,并且目的MAC是到本机的虚拟网卡的,那么就需要把这个包提交给上层协议。网桥就是通过这个地方的处理和外部通信,实现远程管理的目的。
if (dst != NULL && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
br_fdb_put(dst);
return;
}
|
- 如果查询CAM表有结果,并且目的MAC不是到本地的,那么就通过调用br_forward发送到特定的端口。
if (dst != NULL) {
br_forward(dst->dst, skb);
br_fdb_put(dst);
return;
}
|
- 如果在CAM表中查询不到数据包的目的MAC地址,那么就需要向别的每个端口发送这个数据包。调用br_flood来进行这个处理。
br_flood(br, skb, 0);
return;
|
在br_forward和br_flood函数中都必须判断源接口和目的接口是否是同一个,如果是同一端口,就不发送这个数据包。数据包的最后发送都是通过统一的发送接口dev_queue_xmit函数来完成的。
以下就是数据包的处理流程:
图3:数据包处理流程图

前
面多次提到网桥的虚拟网卡,实际上在网桥中,这个网卡存在着一个net_device结构(在net_bridge里),但是不存在着实际的物理设备,而
是附在网桥中每个物理网卡上面。这个虚拟网卡的支持函数在(br_device.c)。因为是虚拟的网卡,所以没有物理中断产生,每个需要发送到这个设备
的数据包都是靠判断数据包的目的MAC地址来决定是否需要提交到本地上层协议栈(在__br_handle_frame判断)。
如
果数据包需要向上层协议提交,都调用br_pass_frame_up函数来处理。在这个函数中,首先把skb->dev设置成br->
dev。然后再模拟在中断中处理数据包一样,进行相应的处理,
然后调用netif_rx放入接收队列。这里有一个要十分注意的地方,这个数据包的skb->dev已经变成br->dev。所以在网络接收
软中断处理函数net_rx_action中不会再次进入handle_bridge了。
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
br->statistics.rx_packets++;
br->statistics.rx_bytes += skb->len;
skb->dev = &br->dev;
skb->pkt_type = PACKET_HOST;
skb_pull(skb, skb->mac.raw - skb->data);
skb->protocol = eth_type_trans(skb, &br->dev);
netif_rx(skb);
}
|
二、配置内核 2.4 Linux 网桥
要配置网桥,首先需要网桥的配置工具bridge-utils。这个配置程序的源代码可以在
http://bridge.sourceforge.net/bridge-utils/
下载。编译成功之后,就可以生成网桥配置的主要工具brctl。
下面,我们将用brctl对以下网络拓扑配置网桥,使Linux能够对数据包进行交换。

上
图中,有五台主机。其中中间那台主机装有linux
,安装了网桥模块,而且有四块物理网卡,分别连接同一网段的其他主机。我们希望其成为一个网桥,为其他四台主机(IP分别为192.168.1.2
,192.168.1.3,192.168.1.4,192.168.1.5)
之间转发数据包。同时,为了方便管理,希望网桥能够有一个IP(192.168.1.1),那样管理员就可以在192.168.1.0/24网段内的主机
上telnet到网桥,对其进行配置,实现远程管理。
前一节中提到,网桥在同一个逻辑网段转发数据包。针对上面的拓扑,这个逻辑网段就是192.168.1.0/24网段。我们为这个逻辑网段一个名称,br_192。首先需要配置这样一个逻辑网段。
# brctl addbr br_192 (建立一个逻辑网段,名称为br_192)
|
实际上,我们可以把逻辑网段192.168.1.0/24看作使一个VLAN ,而br_192则是这个VLAN的名称。
建
立一个逻辑网段之后,我们还需要为这个网段分配特定的端口。在Linux中,一个端口实际上就是一个物理网卡。而每个物理网卡的名称则分别为eth0,
eth1,eth2,eth3。我们需要把每个网卡一一和br_192这个网段联系起来,作为br_192中的一个端口。
# brctl addif br_192 eth0 (让eth0成为br_192的一个端口)
# brctl addif br_192 eth1 (让eth1成为br_192的一个端口)
# brctl addif br_192 eth0 (让eth2成为br_192的一个端口)
# brctl addif br_192 eth3 (让eth3成为br_192的一个端口)
|
网桥的每个物理网卡作为一个端口,运行于混杂模式,而且是在链路层工作,所以就不需要IP了。
# ifconfig eth0 0.0.0.0
# ifconfig eth1 0.0.0.0
# ifconfig eth2 0.0.0.0
# ifconfig eth3 0.0.0.0
|
然后给br_192的虚拟网卡配置IP:192.168.1.1。那样就能远程管理网桥。
# ifconfig br_192 192.168.1.1
|
给br_192配置了IP之后,网桥就能够工作了。192.168.1.0/24网段内的主机都可以telnet到网桥上对其进行配置。
以上配置的是一个逻辑网段,实际上Linux网桥也能配置成多个逻辑网段(相当于交换机中划分多个VLAN)。具体的方法可以参考bridge-util中的HOWTO。
三、总结
本文分析了Linux网桥的实现,并且举例说明如何配置网桥。 通过学习网桥的实现,就能够了解网络中二层交换的原理。
网桥和交换机的功能非常相似,所以在分析网桥的时候,绝大多数情况下可以用交换机的处理方法来分析网桥的动作。
分享到:
相关推荐
- **bridge-utils**:包含了创建和管理Linux网桥所需的工具,如`brctl`命令等。 #### 三、安装与配置 以Linux AS4操作系统为例,介绍如何配置透明网桥: 1. **准备环境**:确保系统中已经安装了`sysfsutils-1.2.0...
假设校园网络中心子网 A(210.37.144.0/255.255.255.0)通过 Cisco 路由器(210.37.144.10)连接中国教育科研同 CERNET 子网 B(210.37.158.1/255.255.255.0),希望通过 Linux 系统实现 IP 路由器(eth0:210.37....
模拟实现网桥的转发功能,以从文件中读取帧模拟网桥从网络中收到一帧,即从两个文件中读入一系列帧,从第一个文件中读入一帧然后从第二个文件中再读入一帧,如此下去。对每一帧,显示网桥是否会转发。 要求:Windows...
根据给定的信息,本文将详细解释如何通过Java编程语言实现模拟网桥的功能,重点在于网桥的转发逻辑处理。此程序旨在模拟网桥在不同网段之间转发数据帧的行为,并根据网桥内部的转发表来决定是否进行转发操作。 ### ...
Docker 默认使用的是 Linux 自带的网桥实现,实际上,OpenvSwitch 项目作为一个成熟的虚拟交换机实现,具备更丰富的功能。个人认为,将来 Docker 必然会支持 OpenvSwitch 作为其默认网桥实现。有兴趣的同学欢迎通过...
在μClinux环境下实现虚拟局域网(VLAN)...总的来说,μClinux环境下的VLAN实现是一个涉及网络接口、虚拟网桥、VLAN配置工具和网络管理策略的综合过程。正确配置后,它可以提高网络效率,增强安全性,并便于网络管理。
23. 若要将鼠标从 VM 中释放出来,可按(A)Ctrl+Alt 键来实现。 知识点:虚拟机管理。Ctrl+Alt 键用于释放鼠标从 VM 中。 24. 如果用户想对某一命令详细的了解,可用(C)man 命令。 知识点:Linux 命令行界面。...
- **设备与网络**: 局域网的常见网络设备包括交换机、路由器、网桥和HUB。Linux默认的文件系统为EXT3。 ### 系统管理与配置 - **Samba服务**: Samba服务器的配置文件是`smb.conf`,用于配置文件共享和打印服务。 ...
- **配置NAT**:使用命令 `iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE` 设置NAT规则。 可以使用命令 `iptables -t nat -L POSTROUTING` 来检查NAT规则是否正确添加。 ##### 6. 构建网桥 如果需要...
### Linux安装和基本命令详解 #### 一、Linux安装流程 Linux系统的安装通常涉及以下步骤,这些步骤在教育环境...以上涵盖了Linux安装与基础命令的概览,对于初学者而言,熟练掌握这些命令是进入Linux世界的敲门砖。
23. 若要将鼠标从 VM 中释放出来,可按(A)键来实现:Ctrl + Alt 键用于释放鼠标。 24. 如果用户想对某一命令详细的了解,可用(C):man 命令用于显示命令的详细信息。 25. Samba 服务器的配置文件是(D):smb....
21. 局域网的网络设备包括交换机(A),路由器(B),网桥(C),双绞线(D),以及HUB(E)。 22. Linux默认的分区类型通常是EXT3(B)。 23. 在VMware等虚拟环境中,释放鼠标可以按Ctrl + Alt(A)键。 24. 要获取某一命令的...
这里忽略了帧的数据部分,因为实验重点在于模拟网桥如何构建和使用转发表。 网桥的转发表用于记录每个MAC地址对应的接收端口。当从一个端口(例如A或B)收到帧时,程序会将源MAC地址与接收端口对应存储。接着,程序...
### 最新嵌入式系统的以太网接口设计及Linux内核网络设备驱动 ...通过使用合适的硬件接口芯片(如DM9000A)和精心设计的Linux内核网络驱动程序,可以有效地扩展嵌入式处理器的网络功能,实现高效稳定的网络通信。
8. 列出所有文件:使用`ls -a`命令可以显示包括隐藏文件在内的目录下所有文件。 9. 转换为超级用户:使用`su`命令可以将普通用户转换为超级用户。 10. cp命令默认路径:除非特别指定,`cp`命令假定要拷贝的文件在...
总的来说,Docker网络涉及了从容器内部的网络命名空间到宿主机的桥梁连接,再到与外部网络的交互,这一过程需要理解Linux的Namespace、虚拟网桥、IP转发和iptables规则等核心概念。掌握这些知识将有助于更好地管理和...
8. 列出一种目录下所有文献需要使用命令行(C)ls –a。 9. 将普通顾客转换成超级顾客的命令是(D)su。 10. 除非特别指定,cp 假定要拷贝文献在下面哪个目录下(D)当前目录。 11. 在 vi 编辑器里,命令"dd"用来...