Широковещательные пакеты не распространяются через veth-туннель Linux
Я разрабатываю сетевое приложение, использующее широковещательную передачу в подсети. Я решил настроить контролируемую среду для тестирования и разработки на своей локальной машине с использованием виртуальных интерфейсов Ethernet (veth
). Настройка проста:
ip link add veth0 type veth peer name veth1
ifconfig veth0 192.168.241.1 netmask 255.255.255.0 up
ifconfig veth1 192.168.241.2 netmask 255.255.255.0 up
ip link
подтверждает, что интерфейсы включены и установлены флаги широковещания:
8: veth1@veth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 46:29:76:81:27:af brd ff:ff:ff:ff:ff:ff
9: veth0@veth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 3a:ad:f9:cb:28:a8 brd ff:ff:ff:ff:ff:ff
Тривиальный тест в Python REPL показывает, что интерфейсы работают. Это приемник:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.bind(('192.168.241.2', 48469))
>>> s.recvfrom(1000)
(b'abc', ('192.168.241.1', 45560))
А это отправитель:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.connect(('192.168.241.2', 48469))
>>> s.send(b'abc')
3
Хорошо работает для одноадресных пакетов. Однако широковещательные пакеты отбрасываются незаметно. Для воспроизведения проблемы можно использовать следующий REPL на стороне отправителя:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
>>> s.connect(('192.168.241.255', 48469))
>>> s.send(b'abc')
3
>>> s # For diagnostic purposes
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('192.168.241.1', 48065), raddr=('192.168.241.255', 48469)>
Сокет настроен успешно, но отправленные широковещательные пакеты никогда не попадают на другую сторону туннеля. Это не то поведение, которого я ожидал, особенно с учетом того, что флаги интерфейса указывают на то, что интерфейсы поддерживают широковещательную передачу. Я запускаю эти тесты на Linux Mint 18 со стандартным ядром. Что мне не хватает?
1 ответ
Туннель в порядке.
Проблема заключалась в том, что Linux отбрасывает весь входящий широковещательный трафик, если сокет не привязан к INADDR_ANY
. Если REPL на стороне получателя обновляется следующим образом, все работает:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.bind(('', 48469)) # Bind to INADDR_ANY to accept broadcast packets
>>> s.recvfrom(1000)
(b'abc', ('192.168.241.1', 45560))
В Windows поведение другое (независимо от туннеля): привязка сокета к определенному интерфейсу не отклоняет широковещательный трафик.
Man 7 IP говорит, что SO_BROADCAST
должен быть установлен для приема широковещательных дейтаграмм; однако поведение, которое я наблюдаю на принимающей стороне, похоже, не соответствует описанию:
Дейтаграммы на широковещательные адреса могут быть отправлены или получены, только если установлен флаг сокета SO_BROADCAST.
Широковещательные дейтаграммы всегда можно получить с INADDR_ANY
; никогда, если сокет привязан к определенному интерфейсу. Оба утверждения верны независимо отs.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
.