Linux
如何配置允許 UDP 廣播的 Linux 網路命名空間
我正在嘗試使用
ip netns
Linux 中的命令系列來創建一個網路命名空間,我可以在其中執行一個使用 UDP 廣播的程序。我不需要訪問 Internet 或根名稱空間上的任何介面(但如果這是使事情正常執行所必需的,那絕對是可以接受的)。這是 Ruby 中的範例伺服器和客戶端(使用 Ruby 1.9.3 測試,但我希望它可以在其他版本中使用):
#! /usr/bin/env ruby require 'socket' PORT = 5000 case ARGV[0] when 'server' soc = UDPSocket.open begin soc.bind('', PORT) puts "SERVER #{Process.pid} listening on #{PORT}" msg = soc.recv(1) puts "SERVER got msg: #{msg}" ensure soc.close end when 'client' soc = UDPSocket.open begin soc.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true) puts "CLIENT sending message" soc.send('m', 0, '<broadcast>', PORT) ensure soc.close end else abort "usage: #{$0} {server | client}" end
它創建伺服器或客戶端。伺服器偵聽
0.0.0.0
介面 (soc.bind('', ...)
)。客戶端向廣播地址 ( ) 發送消息soc.send(..., ..., '<broadcast>', ...)
。在根命名空間中執行時,它似乎可以正常工作:
$ ./udp-broadcast.rb server & sleep 0.5 && sudo netstat --listen --udp -p | grep 5000 && ./udp-broadcast.rb client SERVER 22981 listening on 5000 udp 0 0 *:5000 *:* 22981/ruby CLIENT sending message SERVER got msg: m
這是一個腳本,我嘗試在其中創建一個新的網路命名空間並執行相同的命令:
#! set -e NS=udp-broadcast-test nsexec="ip netns exec $NS" ip netns add $NS trap "ip netns delete $NS" EXIT $nsexec ip link set lo up # Can loopback have a broadcast address? # $nsexec ip link set lo broadcast 255.255.255.255 # RTNETLINK answers: Invalid argument # $nsexec ip addr add broadcast 255.255.255.255 dev lo # RTNETLINK answers: Invalid argument $nsexec ip link add veth0 type veth peer name veth1 $nsexec ifconfig veth0 192.168.99.1/24 up $nsexec ip link $nsexec ip route $nsexec ifconfig timeout 2s $nsexec ./udp-broadcast.rb server & sleep 0.2 $nsexec netstat -n --udp --listen -p timeout 2s $nsexec ./udp-broadcast.rb client wait
執行時,它會產生以下輸出:
$ sudo ./netns.sh 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether e2:a1:c4:14:c4:5e brd ff:ff:ff:ff:ff:ff 3: veth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000 link/ether a6:2f:84:9f:08:36 brd ff:ff:ff:ff:ff:ff 192.168.99.0/24 dev veth0 proto kernel scope link src 192.168.99.1 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) veth0 Link encap:Ethernet HWaddr a6:2f:84:9f:08:36 inet addr:192.168.99.1 Bcast:192.168.99.255 Mask:255.255.255.0 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) SERVER 23320 listening on 5000 Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name udp 0 0 0.0.0.0:5000 0.0.0.0:* 23320/ruby CLIENT sending message ./udp-broadcast.rb:23:in `send': Network is unreachable - sendto(2) (Errno::ENETUNREACH) from ./udp-broadcast.rb:23:in `<main>'
現在,如果我更改伺服器正在偵聽的地址,並且客戶端將消息發送到
192.168.99.1
,那麼消息就會通過,所以我知道我veth0
至少部分有效。如何配置廣播消息通過?伺服器/客戶端程式碼是從更大的程式碼庫中提取的,不容易更改,所以我唯一可以更改的是我的網路配置。
通過向以下位置添加預設路由來解決此特定問題
veth0
:$nsexec ip route add default via 192.168.99.1 dev veth0
在出現的行之後立即添加該行,
veth0
腳本就會成功執行。
好吧,它不起作用的原因有很多。
- 您創建了一個 veth 對,然後未能將其一側添加到新的網路命名空間中。
- 其中一個 veths 方面沒有上升。
- 如您的範例中那樣指定廣播地址
255.255.255.255
會導致路由表查找並根據預設路由發送數據包。- 因此,您不使用
SO_BINDTODEVICE
指定您實際想要發送到的介面。請注意,這需要 root 權限,這在許多情況下並不理想。此外,您沒有在子命名空間和父命名空間之間設置任何路由關係,因此它甚至無法直接 ping 主機。
通常,將通用廣播地址用於提供基本網路服務之外的任何事情都不是一個好的做法。您應該使用目標子網的廣播地址。
我得到了你提到的所有工作,為網路命名空間做以下準備。
# ip netns add TEST # ip link add veth0 type veth peer name veth1 # ip link set dev veth1 netns TEST # ip link set dev veth0 up # ip netns exec TEST ip link set dev veth1 up # ip netns exec TEST ip addr add 10.10.10.10/32 dev veth1 # ip route add 10.10.10.10/32 dev veth0 # ip netns exec TEST ip route add 192.168.1.3/32 dev veth1 # ping -c1 10.10.10.10 PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data. 64 bytes from 10.10.10.10: icmp_seq=1 ttl=64 time=0.202 ms --- 10.10.10.10 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.202/0.202/0.202/0.000 ms
這是使用的腳本。注意
SO_BINDTODEVICE
電話..#!/usr/bin/python import socket as sock import sys, time, os if __name__ == "__main__": if sys.argv[1] == "server": s = sock.socket(sock.AF_INET, sock.SOCK_DGRAM) s.bind(('0.0.0.0', 50000)) data = s.recvfrom(50) print "Got {0}".format(data) elif sys.argv[1] == "client": s = sock.socket(sock.AF_INET, sock.SOCK_DGRAM) s.setsockopt(sock.SOL_SOCKET, sock.SO_BROADCAST, 1) s.setsockopt(sock.SOL_SOCKET, sock.SO_BINDTODEVICE, "veth0") s.connect(('255.255.255.255', 50000)) s.send("hello world\n")
然後結果..
# ip netns exec TEST python test.py server & [1] 24961 # python test.py client Got ('hello world\n', ('192.168.1.3', 41971))