文章目錄
- 1. Linux Bridge簡述
- 2. 網橋創建
- 創建
- 配置持久化
- 在Debian/Ubuntu系統上:
- 在CentOS/RHEL系統上:
- 啟用和驗證
- 3. 關于linux網橋不轉發ip幀的問題
- 原因
- 解決
- 配置持久化
- 4. 查看網橋學習交換表
- 手動添加或刪除條目
- 添加條目
- 刪除條目
- 配置靜態條目
- 設置條目的老化時間
- 持久化配置
- 5. 關于linux網橋STP
- 6. 關于linux網橋不轉發LLDP幀
- 原因
- 解決
- 持久化配置
- 7. 網橋vlan
- 配置持久化
- 8. 其他
- 1. 網卡混雜模式配置
- 2. 網卡接收多播包配置
- 3. 創建VLAN接口
- 參考鏈接
作為一個工業自動化行業,常游走于各種 OT 網絡和 IT 網絡之間的碼農,linux 網橋是常使用的工具之一,每每涉及Linux 網橋, 這些記憶性的操作都要現查,零散且麻煩,所以簡單整理一下,主要方便自己查詢,同時分享給大家參考,希望對你有所幫助。
1. Linux Bridge簡述
有了虛擬網卡,我們很自然就會聯想到讓網卡接入到交換機里,來實現多個容器間的相互連接。而Linux Bridge就是 Linux 系統下的虛擬化交換機,雖然它是以“網橋”(Bridge)而不是“交換機”(Switch)為名,但在使用過程中,你會發現 Linux Bridge 看起來像交換機,功能使用起來像交換機、程序實現起來也像交換機,所以它實際就是一臺虛擬交換機。
Linux Bridge 是在 Linux Kernel 2.2 版本開始提供的二層轉發工具,由brctl命令創建和管理。Linux Bridge 創建以后,就能夠接入任何位于二層的網絡設備,無論是真實的物理設備(比如 eth0),還是虛擬的設備(比如 veth 或者 tap),都能與 Linux Bridge 配合工作。當有二層數據包(以太幀)從網卡進入 Linux Bridge,它就會根據數據包的類型和目標 MAC 地址,按照如下規則轉發處理:
-
如果數據包是廣播幀,轉發給所有接入網橋的設備。如果數據包是單播幀,且 MAC 地址在地址轉發表中不存在,則洪泛(Flooding)給所有接入網橋的設備,并把響應設備的接口與 MAC 地址學習(MAC Learning)到自己的 MAC 地址轉發表中。
-
如果數據包是單播幀,且 MAC 地址在地址轉發表中已存在,則直接轉發到地址表中指定的設備。
-
如果數據包是此前轉發過的,又重新發回到此 Bridge,說明冗余鏈路產生了環路。由于以太幀不像 IP 報文那樣有 TTL 來約束,所以一旦出現環路,如果沒有額外措施來處理的話,就會永不停歇地轉發下去。那么對于這種數據包,就需要交換機實現生成樹協議(Spanning Tree Protocol,STP)來交換拓撲信息,生成唯一拓撲鏈路以切斷環路。
剛剛提到的這些名詞,比如二層轉發、泛洪、STP、MAC 學習、地址轉發表,等等,都是物理交換機中已經非常成熟的概念了,它們在 Linux Bridge 中都有對應的實現,所以我才說,Linux Bridge 不僅用起來像交換機,實現起來也像交換機。
不過,它與普通的物理交換機也還是有一點差別的,普通交換機只會單純地做二層轉發,Linux Bridge 卻還支持把發給它自身的數據包,接入到主機的三層協議棧中。
對于通過brctl命令顯式接入網橋的設備,Linux Bridge 與物理交換機的轉發行為是完全一致的,它也不允許給接入的設備設置 IP 地址,因為網橋是根據 MAC 地址做二層轉發的,就算設置了三層的 IP 地址也沒有意義。然而,Linux Bridge 與普通交換機的區別是,除了顯式接入的設備外,它自己也無可分割地連接著一臺有著完整網絡協議棧的 Linux 主機,因為 Linux Bridge 本身肯定是在某臺 Linux 主機上創建的,我們可以看作是 Linux Bridge 有一個與自己名字相同的隱藏端口,隱式地連接了創建它的那臺 Linux 主機。
因此,Linux Bridge 允許給自己設置 IP 地址,這樣就比普通交換機多出了一種特殊的轉發情況:如果數據包的目的 MAC 地址為網橋本身,并且網橋設置了 IP 地址的話,那該數據包就會被認為是收到發往創建網橋那臺主機的數據包,這個數據包將不會轉發到任何設備,而是直接交給上層(三層)協議棧去處理。這時,網橋就取代了物理網卡 eth0 設備來對接協議棧,進行三層協議的處理。
涉及工具:
sudo apt-get install bridge-utils iproute2 # 對于Debian/Ubuntu系統
2. 網橋創建
創建
創建網橋br0
并將eth0
、eth1
添加到網橋:
# 創建網橋 br0
sudo brctl addbr br0# 添加物理接口 eth0 到網橋
sudo brctl addif br0 eth0
sudo brctl addif br0 eth1# set up
sudo ifconfig br0 up
sudo ifconfig br0 eth0
sudo ifconfig br0 eth1
或者使用ip
命令:
# 使用 ip 命令
sudo ip link add name br0 type bridge
sudo ip link set dev br0 up
sudo ip link set dev eth0 master br0
sudo ip link set dev eth1 master br0
可以使用以下命令驗證網橋和接口的配置:
# 查看網橋信息
sudo bridge link show
sudo bridge fdb show br0# 或者
sudo brctl show
配置持久化
為了在系統重啟后保持網橋配置,你需要將相應的配置添加到網絡配置文件中。
在Debian/Ubuntu系統上:
編輯/etc/network/interfaces
文件,添加如下內容:
auto br0
iface br0 inet staticaddress 192.168.1.100netmask 255.255.255.0bridge_ports eth0 eth1bridge_stp on
在CentOS/RHEL系統上:
創建或編輯/etc/sysconfig/network-scripts/ifcfg-br0
文件,添加如下內容:
DEVICE=br0
TYPE=Bridge
BOOTPROTO=static
IPADDR=192.168.1.100
NETMASK=255.255.255.0
ONBOOT=yes
STP=on# 添加橋接的接口
BRIDGE_PORTS="eth0 eth1"
并確保eth0
和eth1
的配置文件中有以下內容:
# /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
BRIDGE=br0# /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
BRIDGE=br0
啟用和驗證
重新啟動網絡服務以應用配置:
# 對于Debian/Ubuntu
sudo systemctl restart networking# 對于CentOS/RHEL
sudo systemctl restart network
通過檢查網橋狀態和接口狀態來驗證配置:
# 查看網橋信息
sudo bridge link show
sudo bridge fdb show br0# 或者
sudo brctl show
3. 關于linux網橋不轉發ip幀的問題
原因
Docker使用iptables時,默認將通過網橋的數據包發送到iptables進行處理,sysctl
屬性net.bridge.bridge-nf-call-iptables=1
)。
這使得橋接幀(以太網,第2層)受制于iptables filter(IP,第3層),導致網橋上的3層協議通信異常。
允許{ip、ip6、arp}表看到橋接通信可以使用位于/proc/sys/net/bridge/
中的適當proc條目禁用或啟用:
bridge-nf-call-arptables
bridge-nf-call-iptables
bridge-nf-call-ip6tables
對應內核橋模塊識別的3個“可調參數”的設置:
net.bridge.bridge-nf-call-arptables
net.bridge.bridge-nf-call-ip6tables
net.bridge.bridge-nf-call-iptables
它們控制是否將通過網橋的數據包發送到iptables進行處理。在使用網橋將虛擬機連接到網絡的情況下,通常這種處理是不希望的,因為它會導致guests流量被阻止,因為主機iptables規則只考慮主機本身,而不是guests。
然而,內核中的橋模塊將所有這三個值的默認值設置為“1”(“on”,即“do send the packets to iptables”),并且由于歷史原因,內核維護人員拒絕更改此默認值(參見http://patchwork.ozlabs.org/patch/29319/)。
在內核拒絕了上述對編譯默認值的更改之后,許多Linux發行版(包括Fedora,RHEL和CentOS)試圖通過在/etc/sysctl.conf中添加行來修改編譯到橋模塊中的默認設置來解決這個問題:
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
解決
要禁用br_netfilter's
代碼對iptable的調用,如下所示:
sudo sysctl -w net.bridge.bridge-nf-call-iptables=0
#或者
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
內核(>= 5.3),在每個橋單獨啟用,而不是每個命名空間啟用。
sudo ip link set dev br0 type bridge nf_call_iptables 1
參考鏈接https://wiki.libvirt.org/Net.bridge.bridge-nf-call_and_sysctl.conf.html
配置持久化
除使用最原始的腳本配置外,可以使用udev+systemd,在橋的創建(加載模塊)上重寫udev規則來實現。
1)在文件/etc/udev/rules.d/99-bridge.rules
中:
ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=net/bridge
2)在文件/etc/sysctl.d/bridge.conf
中:
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
3)重新引導或重新加載udev和sysctl。
4. 查看網橋學習交換表
使用bridge
命令查看網橋的學習交換表:
bridge fdb show br br0
這個命令會展示網橋br0
的當前學習交換表,包括各個MAC地址和它們對應的端口。
手動添加或刪除條目
添加條目
你可以手動添加一個MAC地址到網橋的學習交換表中。假設你要將MAC地址00:11:22:33:44:55
綁定到接口eth0
:
sudo bridge fdb add 00:11:22:33:44:55 dev eth0 master br0
刪除條目
同樣,你可以手動刪除一個MAC地址條目:
sudo bridge fdb del 00:11:22:33:44:55 dev eth0 master br0
配置靜態條目
你可以配置靜態條目,這樣這些MAC地址永遠不會從網橋的學習交換表中刪除:
sudo bridge fdb add 00:11:22:33:44:55 dev eth0
設置條目的老化時間
網橋會自動老化條目,并在一段時間不使用后刪除它們。可以設置這個老化時間(以秒為單位):
sudo ip link set dev br0 type bridge ageing_time 300
這會將老化時間設置為300秒(5分鐘)。
持久化配置
為了在系統重啟后保持這些配置,可以將相應的命令添加到啟動腳本中。例如,在Debian/Ubuntu系統中,可以將這些命令添加到/etc/network/interfaces
文件中:
auto br0
iface br0 inet staticaddress 192.168.1.100netmask 255.255.255.0bridge_ports eth0 eth1post-up bridge fdb add 00:11:22:33:44:55 dev eth0 master br0 static
在CentOS/RHEL系統中,可以將命令添加到/etc/sysconfig/network-scripts/ifcfg-br0
文件中:
DEVICE=br0
TYPE=Bridge
BOOTPROTO=static
IPADDR=192.168.1.100
NETMASK=255.255.255.0
ONBOOT=yes# Add these lines to the end of the file
BRIDGE_STP=no
BRIDGE_PORTS="eth0 eth1"
BRIDGE_AGEING_TIME=300# Add static FDB entries
POST_UP="bridge fdb add 00:11:22:33:44:55 dev eth0 master br0 static"
5. 關于linux網橋STP
STP的目的是防止網絡環路,這可能導致網絡中的流量風暴。Linux橋接從2.4和2.6內核系列開始就支持STP。要在網橋上啟用STP:
sudo ip link set br0 type bridge stp_state 1
#或者
sudo brctl stp br0 on
注意:Linux網橋不支持快速生成樹協議(RSTP)
網橋上顯示STP阻塞狀態:
sudo ip -j -p -d link show br0 | grep root_port
sudo bridge link show
#或
sudo brctl showstp br0
要更改STP call時間:
sudo ip link set br0 type bridge hello_time 300sudo ip -j -p -d link show br0 | grep \"hello_time\""hello_time": 300,
可以使用相同的基本方法來更改其他STP參數,如最大年齡、轉發延遲、老化時間等
6. 關于linux網橋不轉發LLDP幀
原因
IEEE為標準協議留出的范圍,使用這些地址的包將被網橋過濾,而不會被轉發。
IEEE 802.1D MAC Bridge Filtered MAC Group Addresses: 01-80-C2-00-00-00 to 01-80-C2-00-00-0F; MAC frames that have a destination MAC address within this range are not relayed by MAC bridges conforming to IEEE 802.1D.
IEEE協議中規定的預留的MAC地址表如下:
MAC address | Protocol |
---|---|
01-80-C2-00-00-00 | Spanning Tree (STP/RSPT/MSTP) 生成樹(STP/RSPT/MSTP) |
01-80-C2-00-00-01 | Ethernet Flow Control (pause frames) 以太網流量控制(暫停幀) |
01-80-C2-00-00-02 | Link Aggregation Control Protocol (LACP) 鏈路聚合控制協議(LACP) |
01-80-C2-00-00-03 | 802.1X Port-Based Network Access Control 802.1X基于端口的網絡訪問控制 |
01-80-C2-00-00-08 | Provider Bridge protocols (STP) 供應商橋接協議(STP) |
01-80-C2-00-00-0D | Provider Bridge protocols (MVRP) 提供商橋接協議(MVRP) |
01-80-C2-00-00-0E | 802.1AB Link Layer Discovery Protocol (LLDP) 802.1AB鏈路層發現協議(LLDP) |
解決
綜上網橋不轉發LLDP(鏈路層發現協議)幀,但從Linux內核2.6開始,允許通過在/sys/class/net/bridge-iface/bridge/group_fwd_mask
中設置特定的位掩碼來控制網橋應該轉發IEEE 802.1D中定義的范圍內的哪些鏈路本地幀。默認值0表示Linux網橋不轉發任何鏈路本地幀。
將此值設置為16384將允許網橋轉發LLDP幀(01-80-C2-00-00- 0E
):
# 關閉網橋的 LLDP 過濾
echo 16384 > /sys/class/net/br0/bridge/group_fwd_mask
需要注意的是,在默認的發行版本中,對于該MAC地址范圍中的前三個(-00,-01,-02)是不能通過以上方式控制的, 意味著在gns3的模擬環境中,我們仍然不能成功的測試STP,流控和LACP。要克服這個限制,必須要自己編譯linux kernel才行,也可以下載EVE編譯好的版本,點擊進入鏈接。這樣就可以隨意調整group_fwd_mask的值,支持不過濾01-80-C2-00-00-0x的所有地址了。
前文中把group_fwd_mask設為16384,對應的MAC是01-80-C2-00-00-0E,對應的協議是LLDP, 那么group_fwd_mask是如何計算的呢?
位掩碼是一個16位的數字,其中第一位(最低有效位)表示MAC地址01-80-C2-00 - 00-00,第16位(最高有效位)表示01-80-C2-00-00-0F。默認值(所有位均為0)不轉發任何鏈路本地幀。要啟用特定MAC地址的幀轉發,我們需要將相應的位設置為1。例如,為了允許轉發LLDP幀(01-80-C2-00-00- 0E),我們需要將第15位設置為1,并將其余位保留為0:
MAC | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BIt | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
這意味著我們使用二進制數0100 0000 0000 0000作為位掩碼,它轉換為十進制數16384,就像我們在前面的例子中使用的那樣。
如果我們想將LACP(01-80-C2-00-00-02)和802.1X(01-80-C2-00-00-03)添加到混合中,我們還將第3位和第4位設置為1。
MAC | 0F | 0E | 0D | 0C | 0B | 0A | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BIt | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
bitmasks是二進制數0100 0000 0000 1100, 轉換成10進制則為16396。這樣就可以允許LLDP,LACP,802.1x協議了。
按照這樣的方法,要解除linux網橋對規定地址范圍內所有地址的過濾,就把所有的bit都置為1 即可。
持久化配置
在/etc/rc.local
中添加:
#!/bin/bash# 設置網橋 br0 的組轉發掩碼
echo 65 > /sys/class/net/br0/bridge/group_fwd_mask
exit 0
確保腳本具有可執行權限:
sudo chmod +x /etc/rc.local
或者使用systemd 創建或編輯一個系統服務單元文件,例如/etc/systemd/system/set-group-fwd-mask.service
:
[Unit]
Description=Set group forward mask for bridge br0
After=network.target[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo 65 > /sys/class/net/br0/bridge/group_fwd_mask'
RemainAfterExit=yes[Install]
WantedBy=multi-user.target
啟用并啟動該服務:
sudo systemctl enable set-group-fwd-mask.service
sudo systemctl start set-group-fwd-mask.service
參考鏈接
https://interestingtraffic.nl/2017/11/21/an-oddly-specific-post-about-group_fwd_mask/
http://standards.ieee.org/develop/regauth/tut/macgrp.pdf
7. 網橋vlan
查看網橋vlan信息
bridge vlan show
要顯式禁用網橋的VLAN 過濾功能,可以使用以下命令:
sudo ip link set dev br0 type bridge vlan_filtering 0
這將確保網橋br0
不會處理VLAN標簽
要顯示VLAN流量狀態,啟用VLAN統計(在內核4.7中添加):
sudo ip link set br0 type bridge vlan_stats_enabled 1
前面的命令只在網橋上啟用全局VLAN統計信息,并且沒有細粒度地顯示每個VLAN的狀態。要在網橋中沒有端口號時啟用每個VLAN的統計信息,還需要啟用vlan_stats_per_port(在內核4.20中添加)
sudo ip link set br0 type bridge vlan_stats_per_port 1
顯示vlan統計信息
sudo bridge -s vlan show
配置持久化
為了在系統重啟后保持配置,你需要將相應的命令添加到網絡配置文件中。
在Debian/Ubuntu系統上編輯/etc/network/interfaces
文件,添加如下內容:
auto br0
iface br0 inet staticaddress 192.168.1.100netmask 255.255.255.0bridge_ports eth0 eth1post-up ip link set dev br0 type bridge vlan_filtering 0
8. 其他
1. 網卡混雜模式配置
#打開混雜模式
sudo ip link set eth0 promisc on#關閉混雜模式
sudo ip link set eth0 promisc off
或者使用ifconfig
#打開混雜模式
sudo ifconfig eth0 promisc#關閉混雜模式
sudo ifconfig eth0 -promisc
2. 網卡接收多播包配置
# 確認你要配置的網絡接口名稱
ip link show#加入多播組
sudo ip maddr add 239.255.255.250 dev eth0#使能網卡接收多播幀
sudo ifconfig eth0 allmulti
#或
sudo ip link set dev eth0 allmulticast on#驗證配置
ip maddr show dev eth0
3. 創建VLAN接口
啟用802.1Q VLAN標簽支持
sudo modprobe 8021q
為了使網卡能夠接收所有VLAN包,你需要為每個VLAN創建虛擬接口。假設你需要接收VLAN ID為10和20的包。
sudo ip link add link eth0 name eth0.10 type vlan id 10
sudo ip link add link eth0 name eth0.20 type vlan id 20
sudo ip link set dev eth0.10 up
sudo ip link set dev eth0.20 up
或者使用 vconfig
命令創建VLAN接口(舊版命令)
sudo vconfig add eth0 10
sudo vconfig add eth0 20
sudo ifconfig eth0.10 up
sudo ifconfig eth0.20 up
你可以使用以下命令來驗證VLAN接口是否已正確配置:
ip -d link show eth0.10
ip -d link show eth0.20
為了在系統重啟后保持這些配置,你需要將相應的命令添加到系統啟動腳本中。
在ubuntu上,編輯/etc/network/interfaces
文件,添加如下內容:
auto eth0
iface eth0 inet staticaddress 192.168.1.100netmask 255.255.255.0up ip link set eth0 promisc onauto eth0.10
iface eth0.10 inet manualvlan-raw-device eth0auto eth0.20
iface eth0.20 inet manualvlan-raw-device eth0
在CentOS/RHEL系統上:
創建或編輯/etc/sysconfig/network-scripts/ifcfg-eth0
文件,確保包含以下內容:
DEVICE=eth0
BOOTPROTO=none
ONBOOT=yes
PROMISC=yes
創建或編輯VLAN接口配置文件,例如/etc/sysconfig/network-scripts/ifcfg-eth0.10
和/etc/sysconfig/network-scripts/ifcfg-eth0.20
:
# /etc/sysconfig/network-scripts/ifcfg-eth0.10
DEVICE=eth0.10
BOOTPROTO=none
ONBOOT=yes
VLAN=yes# /etc/sysconfig/network-scripts/ifcfg-eth0.20
DEVICE=eth0.20
BOOTPROTO=none
ONBOOT=yes
VLAN=yes
重新啟動網絡服務以應用配置:
# 對于Debian/Ubuntu
sudo systemctl restart networking# 對于CentOS/RHEL
sudo systemctl restart network
參考鏈接
https://developers.redhat.com/articles/2022/04/06/introduction-linux-bridging-commands-and-features#vlan_filter
https://interestingtraffic.nl/2017/11/21/an-oddly-specific-post-about-group_fwd_mask/
http://standards.ieee.org/develop/regauth/tut/macgrp.pdf
https://wiki.libvirt.org/Net.bridge.bridge-nf-call_and_sysctl.conf.html