Como configurar um namespace de rede Linux que permita a transmissão UDP

2

Estou tentando usar a família de comandos ip netns no Linux para criar um namespace de rede no qual eu possa executar um programa que use a difusão UDP. Eu não preciso de acesso à Internet, ou qualquer interface no namespace raiz (mas se isso é necessário para fazer as coisas funcionarem, é definitivamente aceitável).

Aqui está um exemplo de servidor e cliente em Ruby (testado com o Ruby 1.9.3, mas espero que funcione em outras versões):

#! /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

Cria um servidor ou cliente. O servidor atende na interface 0.0.0.0 ( soc.bind('', ...) ). O cliente envia uma mensagem para o endereço de broadcast ( soc.send(..., ..., '<broadcast>', ...) ).

Quando executado dentro do namespace raiz, parece funcionar corretamente:

$ ./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

Aqui está um script em que eu tento criar um novo namespace de rede e executar os mesmos comandos:

#!

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

Quando executado, produz a seguinte saída:

$ 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>'

Agora, se eu alterar o endereço no qual o servidor está escutando e para o qual o cliente envia uma mensagem, para 192.168.99.1 , a mensagem será transmitida, por isso sei que meu veth0 , pelo menos, funciona parcialmente. / p>

Como posso configurar coisas para que a mensagem transmitida seja transmitida? O código servidor / cliente é extraído de uma base de código maior e não é facilmente alterado, portanto, a única coisa que posso alterar é a configuração da minha rede.

    
por Patrick 27.01.2015 / 14:41

2 respostas

1

Esse problema específico é solucionado adicionando uma rota padrão a veth0 :

$nsexec ip route add default via 192.168.99.1 dev veth0

Adicione essa linha imediatamente após a linha que traz veth0 para cima e o script é executado com êxito.

    
por 09.02.2015 / 17:15
1

Bem, há várias razões para que isso não funcione.

  1. Você cria um par veth e falha em adicionar um lado dele ao novo namespace de rede.
  2. Um dos lados de veths não está ativo.
  3. A especificação do endereço de difusão como 255.255.255.255 , como em seu exemplo, faz com que uma pesquisa na tabela de roteamento e o pacote sejam enviados na rota padrão.
  4. Conseqüentemente, você não usa SO_BINDTODEVICE para especificar para qual interface deseja realmente enviar. Observe que isso requer privilégios de root, que em muitos casos não é o ideal.

Além disso, você não configurou nenhum relacionamento de roteamento entre o namespace filho e o namespace pai para que ele nem sequer funcionasse fazendo ping no host diretamente.

Em geral, usar o endereço de broadcast genérico para qualquer coisa além da provisão de serviços de rede fundamental não é uma boa prática. Você deve usar o endereço de broadcast da sub-rede que está mirando.

Eu recebi tudo o que você mencionou trabalhando, fazendo o seguinte para o namespace da rede ser preparado.

# 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

Aqui está o script usado. Observe o SO_BINDTODEVICE call ..

#!/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")

E depois o resultado ..

# ip netns exec TEST python test.py server &
[1] 24961
# python test.py client
Got ('hello world\n', ('192.168.1.3', 41971))
    
por 27.01.2015 / 23:54