Каждое отправленное многоадресное сообщение UDP принимается дважды из-за двух сообщений присоединения IGMPv2. Как избежать?

У меня есть программа Python, которая использует сокеты для отправки и получения сообщений многоадресной рассылки UDP на IP-адрес многоадресной рассылки 224.0.1.1 и порт UDP 20001.

На стороне приема я создаю единственный принимающий сокет и вызываю socket.setsockopt один раз, используя параметр сокета IP_ADD_MEMBERSHIP для присоединения к группе многоадресной IP-рассылки.

Однако Wireshark сообщает, что один вызов setsockopt вызывает отправку двух отдельных сообщений о присоединении (отчет о членстве IGMPv2):

  • Одно сообщение о присоединении с использованием адреса источника Ethernet 01: 00: 52: 00: 01: 01, который является адресом многоадресной рассылки Ethernet, соответствующим группе многоадресной рассылки IP.

  • Одно сообщение о присоединении с использованием адреса источника Ethernet a8: 66: 7f: 3a: 2b: 1a, который представляет собой одноадресный адрес Ethernet, соответствующий физическому интерфейсу en0, по которому было отправлено сообщение о присоединении.

На стороне отправки я создаю единственный сокет отправки, call socket.connect, чтобы связать сокет с IP-адресом многоадресной рассылки 224.0.1.1 и портом UDP 20001.

Затем я вызываю socket.send один раз, чтобы отправить одно тестовое сообщение. Из-за двух отдельных сообщений о соединении отправленное тестовое сообщение появляется ДВАЖДЫ на проводе, один раз с адресом Ethernet назначения 01: 00: 52: 00: 01: 01 и один раз с адресом Ethernet назначения a8: 66: 7f: 3a: 2b: 1а.

На принимающей стороне оба сообщения принимаются отдельно. Таким образом, каждое отправленное сообщение принимается ДВАЖДЫ.

Возникает вопрос: как этого не допустить?

Минимальный пример, воспроизводящий поведение, выглядит следующим образом:

import socket
import struct
import time

mcast_ipv4_address = "224.0.1.1"
port = 20001
group = (mcast_ipv4_address, port)

txsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
txsock.connect(group) 

rxsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
req = struct.pack("=4sl", socket.inet_aton(mcast_ipv4_address), socket.INADDR_ANY)
rxsock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, req)
rxsock.bind(group)

time.sleep(1.0)

print("Sending single message...")
msg = b"test"
txsock.send(msg)
print("Sent {}".format(msg))

print("Receiving first message...")
msg = rxsock.recv(65535)
print("Received {}".format(msg))

print("Receiving second message...")
msg = rxsock.recv(65535)
print("Received {}".format(msg))

time.sleep(0.1)

Дополнительные детали:

1) Операционная система - macOS High Sierra 10.13.5.

2) Версия Python - 3.5.1

3) Первый сон необходим; без него проблема не возникает, потому что для отправки сообщений присоединения требуется некоторое время

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

5) Я попытался использовать реальный IP-адрес исходящего интерфейса вместо INADDR_ANY в структуре req, но это не имеет никакого значения.


person Bruno Rijsman    schedule 07.08.2018    source источник


Ответы (1)


Я нашел ответ на свой вопрос:

Если вы отключите опцию IP_MULTICAST_LOOP на отправляющем сокете, тогда:

1) Wireshark по-прежнему будет сообщать о двух сообщениях о присоединении IGMPv2, как и раньше.

2) Wireshark ВСЕ ЕЩЕ будет сообщать о двух многоадресных сообщениях UDP, как и раньше.

3) Однако принимающий сокет будет получать только одно многоадресное сообщение UDP (программа-пример блокирует "Получение второго сообщения ...")

Вот обновленный код для macOS:

txsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 
socket.IPPROTO_UDP)
txsock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)  # <<< FIX
txsock.connect(group)

К сожалению, поведение в Linux противоположное:

  • Если вы оставите для IP_MULTICAST_LOOP значение по умолчанию - enabled, как в исходной программе-примере, вы получите ровно одну копию отправленного пакета.

  • Если вы отключите IP_MULTICAST_LOOP, как в «фиксированном» примере программы, вы не получите никакой копии отправленного пакета (по крайней мере, не на AWS).

После дальнейшего исследования я обнаружил, что поведение зависит не от платформы, на которой выполняется код (macOS против Linux), а от маршрутизатора, к которому подключена платформа.

person Bruno Rijsman    schedule 07.08.2018