34.8.桥接 ========= 有时将网络(如以太网段)划分为网段很有用,而无需创建 IP 子网并使用路由器将这些网段连接在一起。以这种方式将两个网络连接在一起的设备称为“网桥”。 网桥的工作原理是学习其每个网络接口上设备的 MAC 地址。仅当源和目标 MAC 地址位于不同的网络上时,它才会在网络之间转发流量。在许多方面,网桥就像一个端口很少的以太网交换机。具有多个网络接口的 FreeBSD 系统可以被配置为充当网桥。 桥接在以下情况下可能很有用: - **连接网络** 网桥的基本操作是连接两个或多个网段。使用基于主机的网桥而不是网络设备的原因有很多,例如布线约束或防火墙。网桥还可以将以 hostap 模式运行的无线接口连接到有线网络,并充当接入点。 - **过滤/流量整形防火墙** 当需要防火墙功能时,可以使用网桥,而无需路由或网络地址转换(NAT)。例如,通过 DSL 或 ISDN 连接到 ISP 的小公司。网络上有来自 ISP 的 13 个公共 IP 地址和 10 台计算机。在这种情况下,由于子网划分问题,使用基于路由器的防火墙很困难。可以配置基于网桥的防火墙,而不会出现任何 IP 寻址问题。 - **网络分路器** 网桥可以连接两个网段,以便在网桥接口上使用 `bpf(4) <https://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&format=html>`__ 和 `tcpdump(1) <https://www.freebsd.org/cgi/man.cgi?query=tcpdump&sektion=1&format=html>`__ 检查在网桥之间传递的所有以太网帧,或者通过发送所有帧的副本输出一个称为 span 端口的附加接口。 - **2 层 VPN** 通过将网络桥接到 EtherIP 隧道或基于 `tap(4) <https://www.freebsd.org/cgi/man.cgi?query=tap&sektion=4&format=html>`__ 的解决方案(如 OpenVPN),可以跨 IP 链路连接两个以太网。 - **2 层冗余** 网络可以通过多个链路连接在一起,并使用生成树协议(STP)来阻止冗余路径。 本节说明如何使用 `if_bridge(4) <https://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4&format=html>`__ 将 FreeBSD 系统配置为网桥。netgraph 桥接驱动程序也是可用的,在 `ng_bridge(4) <https://www.freebsd.org/cgi/man.cgi?query=ng_bridge&sektion=4&format=html>`__ 中描述。 **注意** 数据包过滤可以与任何挂接到 `pfil(9) <https://www.freebsd.org/cgi/man.cgi?query=pfil&sektion=9&format=html>`__ 框架中的防火墙软件包一起使用。该桥可以用作具有 `altq(4) <https://www.freebsd.org/cgi/man.cgi?query=altq&sektion=4&format=html>`__ 或 `dummynet(4) <https://www.freebsd.org/cgi/man.cgi?query=dummynet&sektion=4&format=html>`__ 的流量调整器。 34.8.1.启用网桥 --------------- 在 FreeBSD 中,\ `if_bridge(4) <https://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4&format=html>`__ 是一个内核模块,在创建桥接接口时由 `ifconfig(8) <https://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8&format=html>`__ 自动加载。也可以通过在定制的内核配置文件中加入 ``device if_bridge`` 来将桥接支持编译到定制的内核中。 网桥是使用接口克隆创建的。要创建网桥接口: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge create bridge0 # ifconfig bridge0 bridge0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0 创建网桥接口时,会自动分配一个随机生成的以太网地址。\ ``maxaddr`` 和 ``timeout`` 参数控制了网桥在其转发表中保留多少个 MAC 地址,以及每个条目在最后一次被看到后有多少秒会被删除。其他参数控制 STP 的运行方式。 接下来,指定要添加为网桥成员的网络接口。要使网桥转发数据包,所有成员接口和网桥都需要处于打开状态: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 addm fxp0 addm fxp1 up # ifconfig fxp0 up # ifconfig fxp1 up 网桥现在可以在 **fxp0** 和 **fxp1** 之间转发以太网帧。将以下行添加到 **/etc/rc.conf** 中,以便在启动时创建网桥: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session cloned_interfaces="bridge0" ifconfig_bridge0="addm fxp0 addm fxp1 up" ifconfig_fxp0="up" ifconfig_fxp1="up" 如果网桥主机需要 IP 地址,请在网桥接口上设置该地址,而不是在成员接口上设置该地址。该地址可以静态设置,也可以通过 DHCP 设置。此示例设置静态 IP 地址: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 inet 192.168.0.1/24 也可以将 IPv6 地址分配给网桥接口。要使更改永久化,请将寻址信息添加到 **/etc/rc.conf**\ 。 **注意** 启用数据包筛选后,桥接数据包将通过网桥接口上原始接口上的筛选器入站,并在相应的接口上通过出站。任何一个阶段都可以禁用。当数据包流的方向很重要时,最好在成员接口上设置防火墙,而不是网桥本身。 该网桥具有多个可配置的设置,用于传递非 IP 和 IP 数据包,以及使用 `ipfw(8) <https://www.freebsd.org/cgi/man.cgi?query=ipfw&sektion=8&format=html>`__ 的第 2 层防火墙。有关详细信息,请参阅 `if_bridge(4) <https://www.freebsd.org/cgi/man.cgi?query=if_bridge&sektion=4&format=html>`__ 34.8.2.启用生成树 ----------------- 要使以太网网络正常工作,两个设备之间只能存在一个活动路径。STP 协议检测环路并将冗余链路置于阻塞状态。如果其中一个活动链路发生故障,STP 将计算不同的树,并启用其中一个被阻止的路径以恢复与网络中所有点的连接。 快速生成树协议(RSTP 或 802.1w)提供与传统 STP 的向后兼容性。RSTP 提供更快的收敛速度,并与相邻交换机交换信息,从而快速过渡到转发模式,而无需创建环路。FreeBSD 支持 RSTP 和 STP 作为操作模式,RSTP 是默认模式。 STP 可以使用 `ifconfig(8) <https://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8&format=html>`__ 在成员接口上启用。对于以 **fxp0** 和 **fxp1** 作为当前接口的网桥,请使用以下命令启用 STP: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 stp fxp0 stp fxp1 bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether d6:cf:d5:a0:94:6d id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0 member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP> port 3 priority 128 path cost 200000 proto rstp role designated state forwarding member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP> port 4 priority 128 path cost 200000 proto rstp role designated state forwarding 该网桥的生成树 ID 为 ``00:01:02:4b:d4:50``\ ,优先级为 ``32768``\ 。由于根 ID 是相同的,它表明这是该树的根桥。 网络上的另一个网桥也启用了 STP: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 ether 96:3d:4b:f1:79:7a id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200 root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4 member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP> port 4 priority 128 path cost 200000 proto rstp role root state forwarding member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP> port 5 priority 128 path cost 200000 proto rstp role designated state forwarding 行 ``root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4`` 表示根桥为 ``00:01:02:4b:d4:50``\ ,从该桥到该桥的路径开销为\ ``400000``\ 。到根桥的路径是通过 ``port 4``\ ,也就是 **fxp0**\ 。 34.8.3.桥接接口参数 ------------------- 有几个 ifconfig 参数是桥接接口所特有的。本节总结了这些参数的一些常见用途。完整的可用参数列表在 `ifconfig(8) <https://www.freebsd.org/cgi/man.cgi?query=ifconfig&sektion=8&format=html>`__ 中介绍。 - **private** 一个专用接口不转发任何流量到任何其他也被指定为专用接口的端口。流量被无条件地阻止,因此没有以太网帧会被转发,包括 ARP 数据包。如果需要有选择地阻断流量,应使用防火墙来代替。 - **span** span 端口传输网桥接收到的每个以太网帧的副本。网桥上配置的 span 端口数不受限制,但如果将接口指定为跨度端口,则不能将其用作常规网桥端口。这对于在连接到网桥的跨端口之一的另一台主机上被动侦听桥接网络最有用。例如,要将所有帧的副本发送到名为 **fxp4** 的接口,请执行以下操作: .. code:: shell-session # ifconfig bridge0 span fxp4 - **sticky** 如果一个网桥成员接口被标记为粘性,动态学习的地址条目会被视为转发缓存中的静态条目。粘性条目不会从缓存中过期或被替换,即使该地址在不同的接口上被看到。这就提供了静态地址条目的好处,而不需要预先填充转发表。在网桥的一个特定网段上学习的客户不能漫游到另一个网段。 使用粘性地址的一个例子是将网桥与 VLAN 结合起来,以便在不浪费 IP 地址空间的情况下隔离客户网络。假设 ``CustomerA`` 在 ``vlan100`` 上,\ ``CustomerB`` 在 ``vlan101`` 上,网桥的地址为 ``192.168.0.1``\ : .. code:: shell-session # ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101 # ifconfig bridge0 inet 192.168.0.1/24 在这个例子中,两个客户都把 ``192.168.0.1`` 看作他们的默认网关。由于网桥缓存是粘性的,一个主机不能欺骗另一个客户的 MAC 地址,以拦截他们的通信。 VLAN 之间的任何通信都可以用防火墙来阻断,或者如本例中所见,用私有接口来阻断: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 private vlan100 private vlan101 客户之间是完全隔离的,并且可以分配完整的 /24 地址范围而不需要划分子网。 一个接口后面的唯一源 MAC 地址的数量可以被限制。一旦达到限制,具有未知源地址的数据包将被丢弃,直到现有的主机缓存条目过期或被删除。 下面的例子将 ``vlan100`` 上 ``CustomerA`` 的最大以太网设备数设置为 ``10``\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 ifmaxaddr vlan100 10 网桥接口还支持监视模式,在该模式下,数据包在 `bpf(4) <https://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&format=html>`__ 处理后被丢弃,并且不会进一步处理或转发。这可用于将两个或多个接口的输入多路复用到单个 `bpf(4) <https://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&format=html>`__ 流中。这对于重建通过两个单独的接口传输 RX/TX 信号的网络分路器的流量非常有用。例如,要将来自四个网络接口的输入作为一个流读取: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up # tcpdump -i bridge0 34.8.4.SNMP 监控 ---------------- 桥接接口和 STP 参数可以通过 `bsnmpd(1) <https://www.freebsd.org/cgi/man.cgi?query=bsnmpd&sektion=1&format=html>`__ 进行监控,它包含在 FreeBSD 基础系统中。导出的网桥 MIB 符合 IETF 标准,因此任何 SNMP 客户端或监控包都可用于检索数据。 要在网桥上启用监视,请在 **/etc/snmpd.config** 中取消注释此行,方法是删除开头的 ``#`` 号: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" 可能需要在此文件中修改其他配置设置,例如社区名称和访问列表。有关更多信息,请参见 `bsnmpd(1) <https://www.freebsd.org/cgi/man.cgi?query=bsnmpd&sektion=1&format=html>`__ 和 `snmp_bridge(3) <https://www.freebsd.org/cgi/man.cgi?query=snmp_bridge&sektion=3&format=html>`__\ 。这些编辑被保存后,将此行添加到 **/etc/rc.conf**\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session bsnmpd_enable="YES" 然后,启动 `bsnmpd(1) <https://www.freebsd.org/cgi/man.cgi?query=bsnmpd&sektion=1&format=html>`__\ : .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # service bsnmpd start 以下示例使用 Net-SNMP 软件(\ `net-mgmt/net-snmp <https://cgit.freebsd.org/ports/tree/net-mgmt/net-snmp/pkg-descr>`__\ )从客户端系统查询网桥。也可以使用 `net-mgmt/bsnmptools <https://cgit.freebsd.org/ports/tree/net-mgmt/bsnmptools/pkg-descr>`__ 端口。从运行 Net-SNMP 的 SNMP 客户端中,将以下行添加到 **$HOME/.snmp/snmp.conf** 中,以便导入网桥 MIB 定义: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session mibdirs +/usr/share/snmp/mibs mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB 要使用 IETF BRIDGE-MIB(RFC4188)监视单个网桥: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44 BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2 BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50 ... BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5) BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1) BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000 BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0 BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50 BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80 BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1 RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2) ``dot1dStpTopChanges.0`` 值为 2,表示 STP 网桥拓扑已更改两次。拓扑更改意味着网络中的一个或多个链路已更改或发生故障,并且已计算出新树。\ ``dot1dStpTimeSinceTopologyChange.0`` 值将显示发生这种情况的时间。 要监视多个网桥接口,可以使用专用 BEGEMOT-BRIDGE-MIB: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % snmpwalk -v 2c -c public bridge1.example.com enterprises.fokus.begemot.begemotBridge BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1 ... BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31 BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9 要改变通过 ``mib-2.dot1dBridge`` 子树监控的网桥接口: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % snmpset -v 2c -c private bridge1.example.com BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2