Linuxで_ip netns
_ファミリーのコマンドを使用して、UDPブロードキャストを使用するプログラムを実行できるネットワーク名前空間を作成しようとしています。インターネットやルート名前空間のインターフェイスにアクセスする必要はありません(ただし、それが機能するために必要なものであれば、間違いなく受け入れられます)。
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
を起動する行の直後にその行を追加すると、スクリプトが正常に実行されます。
まあ、それが機能しない理由はいくつかあります。
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))