Получение пакетов UDP для широковещательной передачи в Linux

У нас есть существующее программное обеспечение, которое периодически передает UDP-пакеты на определенный порт (7125) в локальной подсети (x.x.x.255). У нас есть программное обеспечение для мониторинга, работающее на HP-UX (11.11), которое не может получить эти пакеты без проблем. Однако после переноса программного обеспечения мониторинга на Linux (RHEL 6.1) мы обнаружили, что он не получает широковещательные пакеты. tcpdump показывает пакеты, поступающие на хост Linux, но ядро ​​не отправляет их на наше программное обеспечение.

Я использую пару скриптов python 2.x, которые имитируют API сокета, используемый программным обеспечением мониторинга для тестирования различных сценариев. Ядро Linux передает пакеты в программное обеспечение получателя, если отправитель использует одноадресную рассылку (10.1.0.5), но не транслируется (10.1.0.255). Я несколько раз искал в Интернете и не нашел никого с той же проблемой. Любые идеи?

receiver.py

from __future__ import print_function
import socket

localHost = ''
localPort = 7125
remoteHost = '10.1.0.5'
remotePort = 19100

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
print('Listening on {0}:{1} for traffic from {2}:{3}'.format(localHost, localPort, remoteHost, remotePort))
data = s.recv(1024)
print('Received: {0}'.format(data))
s.close()

sender.py

from __future__ import print_function
import socket
import time

localHost = ''
localPort = 19100
remoteHost = '10.1.0.255'
remotePort = 7125

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((localHost, localPort))
s.connect((remoteHost, remotePort))
data = 'sending this from {0}:{1} to {2}:{3}'.format(localHost, localPort, remoteHost, remotePort)
print(data)
print('2')
time.sleep(1)
print('1')
time.sleep(1)
s.send(data)
print('sent at {0}'.format(time.ctime()))
s.close()

Ответ 1

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

В дополнение к установке опции сокета SO_BROADCAST с обеих сторон (как вы уже делаете это правильно), вы также должны привязать ваш ресивер к широковещательному адресу (например, INADDR_BROADCAST, который равен 255.255.255.255, и по существу служит ту же роль, что и INADDR_ANY для одноадресной передачи).

По-видимому, в конфигурации HP-UX исходного плаката сокет UDP, связанный с одноадресным адресом (или INADDR_ANY, в частности), но с установленным параметром сокета SO_BROADCAST, все равно будет получать все датаграммы UDP, адресованные локальный широковещательный адрес, а также одноадресный трафик, направляемый на хост.

В Linux это не так. Связывание сокета UDP, даже если SO_BROADCAST -enabled, до INADDR_ANY недостаточно для приема как одноадресных, так и широковещательных дейтаграмм на связанном порту. Для широковещательного трафика можно использовать отдельный сокет INADDR_BROADCAST -bound SO_BROADCAST.