I'm trying to use the ip netns
family of commands in Linux to create a network namespace in which I can run a program that uses UDP broadcast. I do not need access to the Internet, or any interface on the root namespace (but if that's what's necessary to get things working, it's definitely acceptable).
Here's an example server and client in Ruby (tested with Ruby 1.9.3, but I expect it will work in other versions):
#! /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
It creates either a server or client. The server listens on the 0.0.0.0
interface (soc.bind('', ...)
). The client sends a message to the broadcast address (soc.send(..., ..., '<broadcast>', ...)
).
When run within the root namespace, it seems to work correctly:
$ ./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
Here is a script where I attempt to create a new network namespace and run the same commands:
#!
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
When run, it produces the following output:
$ 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>'
Now, if I change the address that the server is listening on, and that the client sends a message to, to 192.168.99.1
, then the message gets through, so I know my veth0
at least partially works.
How can I configure things such that the broadcast message gets through? The server/client code is extracted from a larger codebase and not easily changed, so the only thing I can change is my network configuration.
Best Answer
Well, theres a number of reasons it does not work.
255.255.255.255
as in your example cause a routing table lookup and the packet to be sent against the default route.SO_BINDTODEVICE
to specify which interface you actually want to send down to. Note that this requires root privileges, which in many cases is not ideal.Also, you did not setup any routing relationship between the child namespace and the parent namespace so it wouldnt even work pinging the host directly.
In general, using the generic broadcast address for anything aside from provision of fundamental network services is not a good practice. You should use the broadcast address of the subnet you are aiming for.
I got all of what you mentioned working doing the following for the network namespace to prepare.
Here is the script used. Notice the
SO_BINDTODEVICE
call..And then the result..