Linux 上的透明 LAN 服務
我需要在 Linux 上實現基於 VLAN 的透明 LAN 服務。這意味著我需要採用配置的 VLAN 並將其直接轉發到指定的埠(所有廣播/多播和單播數據包)。
簡單的解決方案是在 VLAN 介面和指定埠之間定義 1 對 1 橋接。該解決方案的缺點是我開始了解此隧道上的所有 MAC 地址。我在具有有限 mac 表的嵌入式設備上執行,並希望避免從我正在連接的網路中的設備污染 mac 表。
我試圖找到一些方法來使用 ebtables 來完成這項任務,但似乎 ebtables 的 -o 選項僅在 MAC 學習之後的 FORWARD 鏈上才有用。BROUTING 鍊是我需要的,但似乎我不能從這一點強制數據包在特定介面上出口。
所以,ebtables 似乎是一條死胡同。還有其他選擇嗎?在理想的世界中,我更願意擁有基於任何密鑰的 TLS 服務,不僅是 VLAN,VLAN 也可以。
謝謝,伊利亞。
更新:添加了一個仍然使用網橋的解決方案。無論如何,對於 VLAN 情況,有可能使用 Linux 網橋來實現其 VLAN 過濾功能,並完全禁用 MAC 學習。
tc
下面可能仍然對其匹配選擇器的通用方式有用(使用tc與 VLAN 作為選擇器以外的其他事物進行充分匹配可能比使用沒有程式碼來處理它的網橋更容易)。在每個埠上禁用 MAC 學習的橋接
可以禁用 MAC 地址學習。它是用
bridge link
命令完成的。然後可以設置網橋進行 VLAN 過濾(也使用bridge vlan
):它不需要任何 MAC 地址,所有轉發都將根據配置的 VLAN 設置完成。學習或學習
控制給定埠是否將從接收到的流量中學習 MAC 地址。如果學習是否關閉,網橋將最終淹沒任何沒有 FDB 條目的流量。預設情況下,此標誌處於打開狀態。
learning_sync 開啟或 learning_sync 關閉
控制給定埠是否將在設備埠上學習的 MAC 地址同步到橋接 FDB。
因此,例如,讓我們看一個系統,其介面 eth0 作為帶有標記幀的主幹,以及 eth1 eth2 eth3 resp。對於 vlan id 10、20 和 30,未標記。這將通過以下方式完成:
ip link add name br0 type bridge vlan_filtering 1 #remove implicit bridge's self port br0 from any interaction. # Might have to not be done if using an IP on the bridge # but more configuration might then be needed anyway. bridge vlan del vid 1 dev br0 self bridge link set dev br0 learning off learning_sync off self for $nic in eth0 eth1 eth2 eth3; do ip link set dev $nic master br0 bridge link set dev $nic learning off learning_sync off bridge vlan del vid 1 dev $nic done ip link set br0 up bridge vlan add 10 dev eth0 bridge vlan add 20 dev eth0 bridge vlan add 30 dev eth0 bridge vlan add vid 10 pvid 10 untagged dev eth1 bridge vlan add vid 20 pvid 20 untagged dev eth2 bridge vlan add vid 30 pvid 30 untagged dev eth3
要測試這種不同設置的行為方式,只需在最後替換設置腳本中的以下幾行(它們使用答案下一部分中描述的tc方法):
ip netns exec fakebridge tc qdisc add dev trunk0 ingress for vlan in 10 20 30; do ip netns exec fakebridge tc qdisc add dev vlan$vlan ingress ip netns exec fakebridge tc filter add dev vlan$vlan parent ffff: matchall action vlan push id $vlan action mirred egress redirect dev trunk0 ip netns exec fakebridge tc filter add dev trunk0 parent ffff: basic match "meta(vlan mask 0xfff eq $vlan)" action vlan pop action mirred egress redirect dev vlan$vlan done
用這些代替(它不再是假橋,但無論如何……):
ip -n fakebridge link add name br0 type bridge vlan_filtering 1 ip netns exec fakebridge bridge vlan del vid 1 dev br0 self #remove implicit bridge's self port br0 from any interaction ip -n fakebridge link set dev trunk0 master br0 ip netns exec fakebridge bridge vlan del vid 1 dev trunk0 ip netns exec fakebridge bridge link set dev trunk0 learning off learning_sync off for vlan in 10 20 30; do ip -n fakebridge link set dev vlan$vlan master br0 ip netns exec fakebridge bridge link set dev vlan$vlan learning off learning_sync off ip netns exec fakebridge bridge vlan add vid $vlan dev trunk0 ip netns exec fakebridge bridge vlan del vid 1 dev vlan$vlan ip netns exec fakebridge bridge vlan add vid $vlan pvid $vlan untagged dev vlan$vlan done ip -n fakebridge link set br0 up
也可以根本不使用網橋,並使用 VLAN ID 進行操作,使用…
tc(交通管制)
tc能夠使用tc vlan直接操作 VLAN :
描述
vlan 操作允許對數據包執行 802.1Q 封裝或解封裝,這反映在操作模式 POP、PUSH 和 MODIFY 上。POP 模式很簡單,因為不需要進一步的資訊就可以刪除最外層的 VLAN 封裝。PUSH 和 MODIFY 模式至少需要一個 VLANID,並允許選擇性地選擇要使用的 VLANPROTO。
連同其他tc功能:
- matchall(可以
u32 match u32 0 0
在舊核心上替換為)以無條件匹配數據包,- basic + ematch以匹配 vlan id 等元資訊(此 SF Q/A 幫助: tc u32 — 如何匹配最近核心中的 L2 協議?),
- mirred實際在介面之間移動數據包,無需橋接或路由。
和通常的管道(有qdisc ,使用action附加過濾器),可以在封裝或解封裝 802.1Q VLAN ID 時將數據包從一個介面移動到另一個介面。系統不會橋接或路由這些數據包。系統不必記住 MAC 地址或操作 IP,它對數據包和協議的感知將僅限於使用.
tc
請注意,這是一個概念證明。當然,真正的系統仍然必須使用 IP 進行通信,注意不要干擾這些設置。考慮到tc是一個複雜的工具,正確地為生產實現這一點可能會涉及到無法預料的額外困難。tc還可能有其他更好的方法可以更通用地處理事情(考慮tc flow使用 VLAN ID 作為映射到 classid 的鍵,可以更通用地使用,或者可以使用其他東西作為鍵旁邊VLAN,只要有封裝/解封的方法。)。
因此,例如,讓我們看一個系統,其介面 eth0 作為帶有標記幀的主幹,以及 eth1 eth2 eth3 resp。對於 vlan id 10、20 和 30,未標記。允許標記端與正確的未標記端通信,反之亦然:
tc qdisc add dev eth0 handle ffff: ingress tc qdisc add dev eth1 handle ffff: ingress tc qdisc add dev eth2 handle ffff: ingress tc qdisc add dev eth3 handle ffff: ingress tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 10)" action vlan pop action mirred egress redirect dev eth1 tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 20)" action vlan pop action mirred egress redirect dev eth2 tc filter add dev eth0 parent ffff: basic match "meta(vlan mask 0xfff eq 30)" action vlan pop action mirred egress redirect dev eth3 tc filter add dev eth1 parent ffff: matchall action vlan push id 10 action mirred egress redirect dev eth0 tc filter add dev eth2 parent ffff: matchall action vlan push id 20 action mirred egress redirect dev eth0 tc filter add dev eth3 parent ffff: matchall action vlan push id 30 action mirred egress redirect dev eth0
實際介面必須置於混雜模式以實際重定向流量似乎是合乎邏輯的,但無論如何測試時核心 5.0.x 和veth介面都不需要這樣做。
使用ip netns進行網路命名空間的模型
我做了一些實驗來實現一個帶有一個標記的中繼介面和一些使用網路命名空間的未標記的 vlan 介面的假網橋。每個“主機”都有自己的名稱空間,並使用網路元素連結到其他主機,這些主機本身與包括網橋的其他網路名稱空間一起實現。模擬嵌入式設備的實際系統將被稱為fakebridge,因為它看起來類似於 VLAN 感知網橋。
標記未標記_______ .|host10b| +-------+ . ======= + ------ + | | .... vlan10 .... | host10 | | |.......備份箱......|假| ====== |路由器|........| |....vlan20....|host20| | | (vlans 10+20+30) |網橋| ====== + ------ + | | .... vlan30 .... | host30 | +-------+ ------
所以 1+1+4 = 6 個主機,1 + 3 = 4 個網路,總共 10 個命名空間。
一旦下面的腳本執行(以 root 身份),就可以使用以下命令進行測試和觀察:
術語1:
ip netns exec fakebridge tcpdump -l -n -s0 -e -p -i trunk0
術語2:
ip netns exec host10 ping -c1 198.51.100.20
舉個例子:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on trunk0, link-type EN10MB (Ethernet), capture size 262144 bytes 00:27:56.036743 c2:e8:f4:79:28:96 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Request who-has 192.0.2.110 tell 192.0.2.10, length 28 00:27:56.036777 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Reply 192.0.2.110 is-at 16:51:fa:18:21:b0, length 28 00:27:56.036794 c2:e8:f4:79:28:96 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 102: vlan 10, p 0, ethertype IPv4, 192.0.2.10 > 198.51.100.20: ICMP echo request, id 13483, seq 1, length 64 00:27:56.036807 16:51:fa:18:21:b0 > ff:ff:ff:ff:ff:ff, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Request who-has 198.51.100.20 tell 198.51.100.120, length 28 00:27:56.036832 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Reply 198.51.100.20 is-at b6:1d:bc:33:87:98, length 28 00:27:56.036841 16:51:fa:18:21:b0 > b6:1d:bc:33:87:98, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 192.0.2.10 > 198.51.100.20: ICMP echo request, id 13483, seq 1, length 64 00:27:56.036860 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 198.51.100.20 > 192.0.2.10: ICMP echo reply, id 13483, seq 1, length 64 00:27:56.036867 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 102: vlan 10, p 0, ethertype IPv4, 198.51.100.20 > 192.0.2.10: ICMP echo reply, id 13483, seq 1, length 64 00:28:01.043203 16:51:fa:18:21:b0 > c2:e8:f4:79:28:96, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Request who-has 192.0.2.10 tell 192.0.2.110, length 28 00:28:01.043246 b6:1d:bc:33:87:98 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Request who-has 198.51.100.120 tell 198.51.100.20, length 28 00:28:01.043287 c2:e8:f4:79:28:96 > 16:51:fa:18:21:b0, ethertype 802.1Q (0x8100), length 46: vlan 10, p 0, ethertype ARP, Reply 192.0.2.10 is-at c2:e8:f4:79:28:96, length 28 00:28:01.043284 16:51:fa:18:21:b0 > b6:1d:bc:33:87:98, ethertype 802.1Q (0x8100), length 46: vlan 20, p 0, ethertype ARP, Reply 198.51.100.120 is-at 16:51:fa:18:21:b0, length 28
設置腳本以 root 身份執行。它使用 創建各種網路命名空間
ip netns
,填充所需的網路連結(bridges 和veth ),在**fakebridge上設置tc過濾器,最後配置各種主機的 IP,以便進行實驗。fakebridge沒有 IP 也沒有網橋。沒有可以填寫的 MAC 表:或者不會顯示與流量相關的任何內容,因為沒有 IP 沒有 ARP,沒有網橋就沒有 MAC 學習。ip neigh``bridge fdb
#!/bin/sh if ip netns id | grep -qv '^ *$' ; then printf 'ERROR: leave netns "%s" first\n' $(ip netns id) >&2 exit 1 fi hosts='router fakebridge host10 host10b host20 host30' nets='trunk vlan10 vlan20 vlan30' for ns in $hosts $nets; do ip netns del $ns 2>/dev/null || : ip netns add $ns ip netns exec $ns sysctl -q -w net.ipv6.conf.default.disable_ipv6=1 ip netns exec $ns sysctl -q -w net.ipv4.icmp_echo_ignore_broadcasts=0 done for ns in $hosts; do ip -n $ns link set lo up done bmac=1 for ns in $nets; do ip -n $ns link add bridge0 address 02:00:00:00:00:$(printf '%02d' $bmac) type bridge ip -n $ns link set bridge0 up bmac=$(($bmac+1)) done link_ns () { ip -n $1 link add name "$3" type veth peer netns $2 name "$4" ip -n $1 link set dev "$3" up ip -n $2 link set dev "$4" up if printf '%s\n' "$nets" | grep -q -w "$1"; then ip -n "$1" link set dev "$3" master bridge0 fi if printf '%s\n' "$nets" | grep -q -w "$2"; then ip -n "$2" link set dev "$4" master bridge0 fi } link_ns trunk fakebridge fakebridge trunk0 link_ns vlan10 fakebridge fakebridge vlan10 link_ns vlan20 fakebridge fakebridge vlan20 link_ns vlan30 fakebridge fakebridge vlan30 link_ns trunk router router trunk0 link_ns vlan10 host10 host10 eth0 link_ns vlan10 host10b host10b eth0 link_ns vlan20 host20 host20 eth0 link_ns vlan30 host30 host30 eth0 ip netns exec fakebridge tc qdisc add dev trunk0 ingress for vlan in 10 20 30; do ip netns exec fakebridge tc qdisc add dev vlan$vlan ingress ip netns exec fakebridge tc filter add dev vlan$vlan parent ffff: matchall action vlan push id $vlan action mirred egress redirect dev trunk0 ip netns exec fakebridge tc filter add dev trunk0 parent ffff: basic match "meta(vlan mask 0xfff eq $vlan)" action vlan pop action mirred egress redirect dev vlan$vlan done for vlan in 10 20 30; do ip -n router link add link trunk0 name trunk.$vlan type vlan id $vlan ip -n router link set dev trunk.$vlan up ip netns exec router sysctl -q -w net.ipv4.conf.trunk/$vlan.forwarding=1 done ip -n router address add 192.0.2.110/24 dev trunk.10 ip -n router address add 198.51.100.120/24 dev trunk.20 ip -n router address add 203.0.113.130/24 dev trunk.30 ip -n host10 address add 192.0.2.10/24 dev eth0 ip -n host10b address add 192.0.2.11/24 dev eth0 ip -n host20 address add 198.51.100.20/24 dev eth0 ip -n host30 address add 203.0.113.30/24 dev eth0 ip -n host10 route add default via 192.0.2.110 ip -n host10b route add default via 192.0.2.110 ip -n host20 route add default via 198.51.100.120 ip -n host30 route add default via 203.0.113.130