マルチキャストプログラム用の仮想テスト環境を作成するには?

マルチキャストプログラム用の仮想テスト環境を作成するには?

マルチキャストプログラムをテストして開発するために仮想ネットワークデバイスを作成しようとしています。マイコンピュータには、インターネットに接続するためのネットワークカードとイーサネットポートがあります。テストには、2台の異なるコンピュータが接続されているブリッジに接続されている2番目の(仮想)NICが必要です。言い換えれば:

  1. 仮想ネットワークカードを作成します。
  2. 仮想ブリッジ/スイッチを作成します。
  3. 仮想ネットワークカードを仮想ブリッジに接続します。
  4. 2つの追加の仮想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

dum0...1つの例外があります。とのM​​ACアドレスはbr0同じです。これは、ブリッジデバイスの私の理解が間違っており、実際にブリッジデバイスに接続された仮想ネットワークカードではなく、ブリッジもネットワークカードでもなく、通常の使用が不可能な奇妙なものであることを示唆しているので心配です。とにかく、これがテストの残りの部分に邪魔になるとは思わない。仮想デバイスを介したルーティングも機能しません。

テストでは、ループバックdevice()を介してのみすべてのデバイスにpingを送信できますlo。ルーティングテーブルはパケットをbr0正しくルーティングしますが、次のように返します。dum0dum1Destination 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

この時点で、私は何が間違っているのか本当にわかりません。ファイアウォールを介してすべてをパッチしました。私が考える唯一のものは仮想デバイスです。 「ちょうど仮想ネットワークカードを作成する」方法を調べてみましたが、結果はとても残念でした。マニュアルip-link(8)ページには文字通り何十もの可能なデバイスがリストされていますが、そのデバイスの機能がどのように異なるか、いつ使用できるかはまったくわかりません。この問題は単純すぎるようですが、それについての情報を見つけるのは難しいので、私がどれほど懸命に調査したかを強調することはできません(まだわからない場合)。

仮想デバイスが他の場所ではなく曖昧なソースからデータを削除できることを読みました。この場合、ARP要求を削除でき、対応するMACアドレスが見つかりません(この仮想構成が必要な場合)。また、(Linux Taps)を試してみましたip tuntapが、うまくいきませんでした。ただし、正しく理解している場合は、ネイティブIPパケット(トンネリング用)またはイーサネットフレーム(タブ用)をカーネルから要求するプログラムに提供してください。それ以外の場合、すべてのデータも削除されます。

では、どのような機器が必要ですか?マルチキャストプログラムのテストにも使用できますか?デバイスのアドレスにバインドし、そのデバイスからマルチキャストトラフィックを送信し、ブリッジを介して送信し、他のデバイスのアドレスにバインドされたマルチキャスターで受信できますか?これは非常に複雑なので、助けてくれて読むことができるすべての人に感謝します。ありがとうございます!

答え1

@ABが提案したように、解決策は複数のネットワーク名前空間を使用することです。ホストのネットワークスタックをプロセス(受信 - >プロセス - >出力)と考えることができます。 Linux は出力を入力にループすることを許可しないので、元の設定のルーティングが正確であってもパケットは破棄されました。ネットワークスタックは1つだけで、出力パケットは同じネットワークスタックで再処理できませんでした。ネットワークネームスペースを使用すると、複数のネットワークスタックを作成できます。このスタックは、必要に応じて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)と2つの仮想イーサネットペア(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アドレスがありません。ブリッジを介してホストにルーティングするため、IP アドレスは不要です。必要に応じて、そのデバイスのIPアドレスを指定して、vethブリッジングなしで直接ルーティングできます。生成された名前空間は次のとおりです。

# 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を試してみましょう。ip neighbourARPキャッシュを監視し、ブリッジを使用して、すべてが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

アドレスsocat PIPEタイプは、UDP-RECVFROMポート9000でUDPデータグラムを受信するのを待ち、それを名前付きパイプに書き込んで名前付きパイプから再読み込みし、ポート9000でユニキャストUDPデータグラムに送信元IPアドレスに送り返します。アドレスSTDIOタイプ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)

素晴らしい。

関連情報