Linux

如何配置允許 UDP 廣播的 Linux 網路命名空間

  • February 9, 2015

我正在嘗試使用ip netnsLinux 中的命令系列來創建一個網路命名空間,我可以在其中執行一個使用 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腳本就會成功執行。

好吧,它不起作用的原因有很多。

  1. 您創建了一個 veth 對,然後未能將其一側添加到新的網路命名空間中。
  2. 其中一個 veths 方面沒有上升。
  3. 如您的範例中那樣指定廣播地址255.255.255.255會導致路由表查找並根據預設路由發送數據包。
  4. 因此,您不使用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))

引用自:https://serverfault.com/questions/662699