
멀티캐스트 프로그램을 테스트하고 개발하기 위해 가상 네트워크 장치를 만들려고 합니다. 내 컴퓨터에는 인터넷에 연결하기 위한 네트워크 카드와 이더넷 포트가 있습니다. 테스트를 위해 2대의 다른 컴퓨터가 연결된 브리지에 연결된 두 번째(가상) NIC가 필요합니다. 다시 말해서:
- 가상 네트워크 카드를 만듭니다.
- 가상 브리지/스위치를 만듭니다.
- 가상 네트워크 카드를 가상 브리지에 연결합니다.
- 두 개의 추가 가상 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
...한 가지 예외가 있습니다. 및의 MAC 주소는 br0
동일합니다. 이는 브리지 장치에 대한 나의 이해가 잘못되었으며 실제로 브리지 장치에 연결된 가상 네트워크 카드가 아니라 브리지도 네트워크 카드도 아니고 정상적인 사용이 불가능한 이상한 것임을 시사하기 때문에 걱정됩니다. 어쨌든, 이것이 나머지 테스트에 방해가 될 것이라고는 생각하지 않습니다. 가상 장치를 통한 라우팅도 작동하지 않습니다.
테스트의 경우 루프백 device() 를 통해서만 모든 장치에 ping을 보낼 수 있습니다 lo
. 라우팅 테이블은 패킷을 및 br0
로 올바르게 라우팅 하지만 다음을 반환합니다 .dum0
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
이 시점에서 나는 내가 무엇을 잘못하고 있는지 정말로 모릅니다. 방화벽을 통해 모든 것을 패치했습니다. 제가 생각하는 유일한 것은 가상 장치입니다. "그냥 가상 네트워크 카드를 만드는" 방법을 조사해 보았지만 결과는 매우 실망스러웠습니다. 매뉴얼 ip-link(8)
페이지에는 문자 그대로 수십 개의 가능한 장치가 나열되어 있지만 해당 장치의 기능이 서로 어떻게 다른지 또는 언제 사용할 수 있는지 전혀 알 수 없습니다. 이 문제는 너무 간단해 보이지만 이에 대한 정보를 찾는 것이 너무 어렵기 때문에 제가 얼마나 열심히 조사했는지는 강조할 수 없습니다(아직 모르는 경우).
가상 장치가 다른 곳이 아닌 모호한 소스에서 데이터를 삭제할 수 있다는 내용을 읽었습니다. 이 경우 ARP 요청을 삭제할 수 있으며 해당 MAC 주소를 찾을 수 없습니다(이 가상 구성이 필요한 경우). 또한 (Linux Taps)를 사용해 보았 ip tuntap
으나 작동하지 않았습니다. 그러나 올바르게 이해했다면 원시 IP 패킷(터널링용) 또는 이더넷 프레임(탭용)을 커널에서 요청하는 프로그램에 제공합니다. 그렇지 않으면 모두 데이터도 삭제됩니다.
그렇다면 어떤 종류의 장비가 필요합니까? 멀티캐스트 프로그램을 테스트하는 데에도 사용할 수 있나요? 장치의 주소에 바인딩하고, 해당 장치에서 멀티캐스트 트래픽을 보내고, 브리지를 통해 전송하고, 다른 장치의 주소에 바인딩된 멀티캐스터에서 수신할 수 있습니까? 이것은 매우 복잡하므로 도움을 주시고 읽을 수 있는 모든 분들께 감사드립니다. 감사해요!
답변1
@AB가 제안한 것처럼 해결책은 여러 네트워크 네임스페이스를 사용하는 것입니다. 호스트의 네트워크 스택을 프로세스(수신 -> 프로세스 -> 출력)로 생각할 수 있습니다. Linux는 출력을 입력으로 다시 루핑하는 것을 허용하지 않으므로 원래 구성의 라우팅이 정확하더라도 패킷이 삭제되었습니다. 네트워크 스택이 하나뿐이었고 출력 패킷이 동일한 네트워크 스택에서 다시 처리될 수 없었습니다. 네트워크 네임스페이스를 사용하면 여러 네트워크 스택을 생성할 수 있으며, 이 스택은 필요에 따라 ARP 요청, 핑 및 멀티캐스트 트래픽에 응답할 수 있습니다.
링크 유형을 사용하면 각 네트워크 장치가 링크의 한쪽 끝(더 정확하게는 이더넷 케이블의 한쪽 끝에 연결된 가상 이더넷 네트워크 장치)을 나타내 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
)와 두 개의 가상 이더넷 쌍( veth0
to veth
및 veth1
to 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 neighbour
ARP 캐시를 모니터링하기 위해 및 브리지를 사용하여 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
다른 터미널에서 핑을 보내기 전에 시작하십시오.
# 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)
훌륭한.