Как настроить сетевое пространство имен Linux, разрешающее широковещательную передачу UDP

Я пытаюсь использовать ip netns Семейство команд в Linux для создания сетевого пространства имен, в котором я могу запустить программу, использующую широковещательную передачу 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 хотя бы частично работает.

Как я могу настроить вещи так, чтобы широковещательное сообщение проходило? Код сервер / клиент извлекается из большей кодовой базы, и его нелегко изменить, поэтому единственное, что я могу изменить, - это моя сетевая конфигурация.

2 ответа

Решение

Эта конкретная проблема решается путем добавления маршрута по умолчанию в veth0:

$nsexec ip route add default via 192.168.99.1 dev veth0

Добавьте эту строку сразу после строки, которая приносит veth0 и сценарий успешно выполняется.

Ну, есть ряд причин, по которым это не работает.

  1. Вы создаете пару веток, а затем не можете добавить одну ее сторону в новое пространство имен сети.
  2. Одна из сторон ветхих не вверх.
  3. Указание широковещательного адреса как 255.255.255.255 как в вашем примере, вызвать поиск в таблице маршрутизации и посылку пакета по маршруту по умолчанию.
  4. Следовательно, вы не используете SO_BINDTODEVICE чтобы указать, какой интерфейс вы хотите отправить. Обратите внимание, что для этого требуются привилегии суперпользователя, что во многих случаях не идеально.

Кроме того, вы не настроили никаких маршрутизационных отношений между дочерним пространством имен и родительским пространством имен, поэтому он даже не будет работать с эхо-запросом напрямую.

В общем, использование общего широковещательного адреса для чего-либо, кроме предоставления основных сетевых услуг, не является хорошей практикой. Вы должны использовать широковещательный адрес подсети, на которую вы нацелены.

Я получил все, что вы упомянули, работая над подготовкой пространства имен сети.

# 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))
Другие вопросы по тегам