Linux

如何為多播節目創建虛擬測試環境?

  • June 25, 2020

我正在嘗試創建虛擬網路設備以測試和開發多播程序。我的電腦有一個帶有一個乙太網埠的網卡,它連接到 Internet。我想要一個連接到網橋的第二個(虛擬)網卡,該網橋有 2 台其他電腦連接到它以進行測試。換句話說:

  1. 創建一個虛擬網卡。
  2. 創建一個虛擬網橋/交換機。
  3. 將虛擬網卡連接到虛擬網橋。
  4. 創建兩個額外的虛擬 NIC 設備(用作遠端主機)並將它們連接到虛擬網橋。

據我了解,在 Linux 中創建虛擬網橋會隱式創建虛擬網卡並將其連接到它,該網卡可作為網路介面訪問。我在這裡回答了一個解釋這個問題的問題(儘管我可能錯了)。

我知道我可以用虛擬機測試多播程序,但這很麻煩,而且我的理解是,如果我將程序綁定到適當的虛擬網路設備和地址,我應該能夠使用適當的路由表本地執行程序。到目前為止,我什至無法正常工作,更不用說多播了。這就是我所擁有的:

ip link add br0 type bridge
ip link add dum0 type dummy
ip link add dum1 type dummy
ip link set dev dum0 master br0
ip link set dev dum1 master br0
ip addr add 10.0.0.1/24 brd + dev br0
ip addr add 10.0.0.2/24 brd + dev dum0
ip addr add 10.0.0.3/24 brd + dev dum1
ip link set br0 up
ip link set dum0 up
ip link set dum1 up
ip route del 10.0.0.0/24 dev dum0
ip route del 10.0.0.0/24 dev dum1
ip route del broadcast 10.0.0.0 dev dum0
ip route del broadcast 10.0.0.0 dev dum1
ip route del broadcast 10.0.0.255 dev dum0
ip route del broadcast 10.0.0.255 dev dum1
ip route del local 10.0.0.2
ip route del local 10.0.0.3

為方便起見,您可以使用以下命令撤消該操作:

ip link del dev dum1
ip link del dev dum0
ip link del dev br0

經檢查,所有配置都與物理硬體完全相同:

$ ip addr show br0
41: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
   link/ether 56:47:31:fd:10:c0 brd ff:ff:ff:ff:ff:ff
   inet 10.0.0.1/24 brd 10.0.0.255 scope global br0
      valid_lft forever preferred_lft forever
   inet6 fe80::5447:31ff:fefd:10c0/64 scope link
      valid_lft forever preferred_lft forever
$ ip addr show dum0
42: dum0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN group default qlen 1000
   link/ether 56:47:31:fd:10:c0 brd ff:ff:ff:ff:ff:ff
   inet 10.0.0.2/24 brd 10.0.0.255 scope global dum0
      valid_lft forever preferred_lft forever
   inet6 fe80::5447:31ff:fefd:10c0/64 scope link
      valid_lft forever preferred_lft forever
$ ip addr show dum1
43: dum1: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UNKNOWN group default qlen 1000
   link/ether d2:47:c8:19:4a:60 brd ff:ff:ff:ff:ff:ff
   inet 10.0.0.3/24 brd 10.0.0.255 scope global dum1
      valid_lft forever preferred_lft forever
   inet6 fe80::d047:c8ff:fe19:4a60/64 scope link
      valid_lft forever preferred_lft forever
$ ip route show table main
10.0.0.0/24 dev br0 proto kernel scope link src 10.0.0.1
$ ip route show table local
broadcast 10.0.0.0 dev br0 proto kernel scope link src 10.0.0.1
local 10.0.0.1 dev br0 proto kernel scope host src 10.0.0.1
broadcast 10.0.0.255 dev br0 proto kernel scope link src 10.0.0.1
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
$ ip route get to 10.0.0.1
local 10.0.0.1 dev lo src 10.0.0.1 uid 1000
   cache <local>
$ ip route get to 10.0.0.2
10.0.0.2 dev br0 src 10.0.0.1 uid 1000
   cache

…除了一個例外: 和 的 MAC 地址dum0br0相同的。這讓我很擔心,因為這表明我對橋接設備的理解是錯誤的,它實際上並不是連接到橋接設備的虛擬網卡,而是某種奇怪的既不是網橋也不是網卡,不能正常使用。無論如何,我認為這不會干擾其餘的測試。通過虛擬設備進行路由也不起作用。

至於測試,我只能通過環回設備(lo)ping任何設備。路由表正確地將數據包路由br0dum0and dum1,但它返回Destination Host Unreachable

$ ping -c 2 10.0.0.1 # br0 through lo OK
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.029 ms

--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 56ms
rtt min/avg/max/mdev = 0.029/0.041/0.053/0.012 ms
$ ping -c 2 10.0.0.2 # dum0 through br0 BAD
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
From 10.0.0.1 icmp_seq=1 Destination Host Unreachable
From 10.0.0.1 icmp_seq=2 Destination Host Unreachable

--- 10.0.0.2 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 62ms
pipe 2
$ ping -c 2 -I lo 10.0.0.2 # dum0 through lo OK
ping: Warning: source address might be selected on device other than lo.
PING 10.0.0.2 (10.0.0.2) from x.x.x.x lo: 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.047 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.033 ms

--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 35ms
rtt min/avg/max/mdev = 0.033/0.040/0.047/0.007 ms

在這一點上,我真的不知道我做錯了什麼。我通過防火牆修補了所有內容。我認為唯一的事情是虛擬設備。我嘗試研究如何“只創建一個虛擬 NIC”,這非常令人沮喪。手冊頁從ip-link(8)字面上列出了數十種可能的設備,完全不知道它們之間的不同之處或您何時使用它們。我不能強調我是多麼努力地研究這個,因為它看起來很簡單,但是很難找到關於它的資訊(如果你還不知道的話)。

我已經讀到虛擬設備可能會簡單地丟棄數據(來自一個晦澀的來源而不是其他任何地方),在這種情況下,它們可能會丟棄 ARP 請求並且我找不到它們的 MAC 地址(如果在此虛擬配置中甚至有必要的話)。我也嘗試使用ip tuntap(Linux taps),但這也不起作用,但如果我理解正確,它們會為從核心請求它們的程序提供原始 IP 數據包(用於隧道)或乙太網幀(用於 taps),否則也會刪除所有數據。

那麼,我需要什麼樣的設備?這甚至可以用來測試多播節目嗎?我是否能夠綁定到設備的地址,從該設備發送多播流量,通過網橋發送,並被綁定到另一個設備地址的多播程序接收?這涉及很多,所以我感謝任何幫助以及任何可以閱讀它的人。謝謝!

正如@AB 所建議的,解決方案是使用多個網路命名空間。我們可以把主機的網路棧想像成一個程序:Intake -> Processing -> Outtake。Linux 不允許將輸出循環回輸入,因此即使我原始配置中的路由是正確的,數據包也被丟棄了——只有一個網路堆棧,並且輸出數據包不能被同一個網路堆棧再次處理。使用網路命名空間允許創建多個網路堆棧,然後可以根據需要響應 ARP 請求、ping 和多播流量。

鏈路類型可veth用於創建乙太網對,這樣每個veth網路設備都代錶鍊路的一端(或者更準確地說,連接到乙太網電纜一端的虛擬乙太網網路設備)。一端保留在預設網路命名空間中並添加到虛擬網橋中,而另一端則添加到已創建的網路命名空間中。這允許命名空間之間的通信!這是程式碼:

ip link add br0 type bridge mcast_snooping 1 mcast_router 2
ip netns add net0
ip link add veth0 type veth peer name veth
ip link set veth netns net0
ip link set dev veth0 master br0
ip netns add net1
ip link add veth1 type veth peer name veth
ip link set veth netns net1
ip link set dev veth1 master br0
ip addr add 10.0.0.1/24 brd + dev br0
ip link set br0 up
ip link set veth0 up
ip link set veth1 up
ip netns exec net0 ip addr add 10.0.0.2/24 brd + dev veth
ip netns exec net1 ip addr add 10.0.0.3/24 brd + dev veth
ip -all netns exec ip link set lo up
ip -all netns exec ip link set veth up

您可以使用以下方法撤消該操作:

ip link del dev veth1
ip link del dev veth0
ip link del dev br0
ip netns del net1
ip netns del net0

這將創建一個虛擬網橋 ( br0) 和兩個虛擬乙太網對 ( veth0tovethveth1to veth),並將veth設備添加到單獨的網路命名空間(在任何名稱衝突之前)。在這裡我們可以看到結果:

$ ip addr show br0
25: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
   link/ether 1a:96:25:a0:43:c3 brd ff:ff:ff:ff:ff:ff
   inet 10.0.0.1/24 brd 10.0.0.255 scope global br0
      valid_lft forever preferred_lft forever
   inet6 fe80::3c91:4be6:d418:e045/64 scope link 
      valid_lft forever preferred_lft forever
$ ip addr show veth0
27: veth0@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP group default qlen 1000
   link/ether 1a:96:25:a0:43:c3 brd ff:ff:ff:ff:ff:ff link-netns net0
   inet6 fe80::3c91:4be6:d418:e045/64 scope link 
      valid_lft forever preferred_lft forever
$ ip addr show veth1
29: veth1@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP group default qlen 1000
   link/ether b6:41:52:5f:ef:eb brd ff:ff:ff:ff:ff:ff link-netns net1
   inet6 fe80::b4fa:8f8c:5976:59c9/64 scope link 
      valid_lft forever preferred_lft forever

請注意,預設命名空間中的虛擬乙太網設備沒有 IP 地址——它們不需要它們,因為我們通過網橋路由到達主機。如果需要,我們可以為他們提供相應veth設備的 IP 地址,以便直接路由到他們,而無需使用網橋。以下是創建的命名空間看到的內容:

# ip netns exec net0 ip addr show veth
26: veth@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
   link/ether 46:11:7c:77:fc:01 brd ff:ff:ff:ff:ff:ff link-netnsid 0
   inet 10.0.0.2/24 brd 10.0.0.255 scope global veth
      valid_lft forever preferred_lft forever
   inet6 fe80::4411:7cff:fe77:fc01/64 scope link 
      valid_lft forever preferred_lft forever
# ip netns exec net1 ip addr show veth
28: veth@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
   link/ether 12:bc:a0:99:8d:43 brd ff:ff:ff:ff:ff:ff link-netnsid 0
   inet 10.0.0.3/24 brd 10.0.0.255 scope global veth
      valid_lft forever preferred_lft forever
   inet6 fe80::10bc:a0ff:fe99:8d43/64 scope link 
      valid_lft forever preferred_lft forever

現在讓我們嘗試 ping。我們可以監控 ARP 記憶體ip neighbour和網橋,tcpdump讓我們確信事情正在按預期工作:

$ ip neigh
$ ping -c 2 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.124 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.059 ms

--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 58ms
rtt min/avg/max/mdev = 0.059/0.091/0.124/0.033 ms
$ ip neigh
10.0.0.2 dev br0 lladdr 46:11:7c:77:fc:01 REACHABLE

從另一個終端,在 ping 之前開始:

# tcpdump -i br0
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
00:54:49.536867 ARP, Request who-has 10.0.0.2 tell 10.0.0.1, length 28
00:54:49.536908 ARP, Reply 10.0.0.2 is-at 46:11:7c:77:fc:01 (oui Unknown), length 28
00:54:49.536911 IP 10.0.0.1 > 10.0.0.2: ICMP echo request, id 9342, seq 1, length 64
00:54:49.536937 IP 10.0.0.2 > 10.0.0.1: ICMP echo reply, id 9342, seq 1, length 64
00:54:50.594136 IP 10.0.0.1 > 10.0.0.2: ICMP echo request, id 9342, seq 2, length 64
00:54:50.594174 IP 10.0.0.2 > 10.0.0.1: ICMP echo reply, id 9342, seq 2, length 64

這可以使用ip netns exec命令在每個網路命名空間內重複,結果相同。socat最後,我們可以通過一個簡單的程序在一個命名空間中偵聽多播地址,並在另一個命名空間中發送多播流量來測試跨兩個命名空間的多播流量:

# ip netns exec net0 socat PIPE \
> UDP-RECVFROM:9000,bind=239.0.0.1,ip-add-membership=239.0.0.1:veth &
[1] 9474
# echo ECHO | ip netns exec net1 socat STDIO \
> UDP-DATAGRAM:239.0.0.1:9000,bind=10.0.0.3:9000
ECHO
[1]+  Done

和地址類型等待在埠 9000 上接收 UDP 數據報socat PIPEUDP-RECVFROM將其寫入未命名管道,從未命名管道讀回,然後在埠 9000 上將其作為單播 UDP 數據報發送回源 IP 地址。STDIO和address 類型從UDP-DATAGRAM讀取數據stdin,將其作為多播 UDP 數據報發送,接收單播 UDP 數據報,並將其內容寫入stdout

從另一個終端,在伺服器之前啟動:

# tcpdump -i br0
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br0, link-type EN10MB (Ethernet), capture size 262144 bytes
01:06:04.002116 ARP, Request who-has 10.0.0.3 tell 10.0.0.2, length 28
01:06:04.002129 ARP, Reply 10.0.0.3 is-at 12:bc:a0:99:8d:43 (oui Unknown), length 28
01:06:05.126134 IP 10.0.0.2 > igmp.mcast.net: igmp v3 report, 1 group record(s)
01:06:05.858118 IP 10.0.0.2 > igmp.mcast.net: igmp v3 report, 1 group record(s)
01:06:06.368349 IP 10.0.0.3.9000 > 239.0.0.1.9000: UDP, length 5
01:06:06.368499 IP 10.0.0.2.9000 > 10.0.0.3.9000: UDP, length 5
01:06:06.371106 IP 10.0.0.2 > igmp.mcast.net: igmp v3 report, 1 group record(s)
01:06:06.946105 IP 10.0.0.2 > igmp.mcast.net: igmp v3 report, 1 group record(s)

極好的。

引用自:https://unix.stackexchange.com/questions/594760