Многоадресная рассылка IPv6 с Node.js

Я экспериментирую с многоадресной рассылкой IPv6 UDP через VPN. Я пробовал следующий код:

const dgram = require('dgram');

let sock = dgram.createSocket('udp6', {
  reuseAddr: true
});

sock.on('message', (data, source) => {
  console.log('on message', arguments);
});

sock.bind('36912', '2620:9b::1944:e598', () => {
  sock.addMembership('ff02::1:3', '2620:9b::1944:e598');
});


setInterval(() => {
  let buf = Buffer.from((new Date()).toString());
  sock.send(buf, 0, buf.length, 36912, 'ff02::1:3');
}, 500);

Выполняется script, и я вижу, что пакеты отправляются/принимаются с Wireshark, но ни один из них не показывает их в консоли.

Wireshark UDP Capture

Что я делаю неправильно? Каков способ отправки и получения базовой многоадресной рассылки с IPv6?

Ответ 1

Идентификатор области → номер интерфейса

В IPv6 существует понятие scope_id адреса, который должен указывать контекст для IP-адреса, и, как правило, просто означает, к какому интерфейсу он доступен. В то время как в обласках есть конкретные имена ОС, каждый из них просто преобразуется в номер интерфейса, причем 0 обычно означает стандартную систему.

В многоадресной рассылке IPv6 этот scope_id предоставляется непосредственно IP_ADD_MEMBERSHIP и IP_MULTICAST_IF вместо предоставления ip, связанного с интерфейсом, как это делает IPv4.

сглаживание оболочки v6 различий

node (через libuv) скрывает эту разницу для вас в addMembership, просматривая область видимости с "адрес интерфейса", который вы предоставляете.

К сожалению, начиная с простого IP-адреса и получения области не имеет большого смысла (вся суть области заключается в том, что IP может иметь разные применения в разных областях). Таким образом, libuv может только заполнять область, если вы явно предоставляете ее в конце адреса, используя формат %[scope].

Использование адресов с явными областями

Кажется, что это вокруг:

 sock.bind('36912', '::', () => {
   sock.addMembership('ff02::1:3', '::%eth2');
 ...
 sock.send(buf, 0, buf.length, 36912, 'ff02::1:3%eth2');

Где:

  • с помощью :: (или без адреса) в bind необходимо, так как вы комбинируете прием, который будет фильтровать многоадресный адрес на этом с помощью отправки, для которой необходим нормальный адрес.

  • с помощью %[iface#] заставляет область действия этого интерфейса #.

  • Второй аргумент addMembership действительно может начинаться с любого адреса, поскольку мы вынуждаем область действия, а остальная часть отбрасывается.

  • Обычно сокет отправки разделяется и получает другой порт или анонимный порт, так как вы либо ограничены тем, что можете настроить, либо рискуете получить ошибки EADDRINUSE для слишком близких сокетов.

Ответ 2

Я выполнил шаги в этом ответе, но не смог заставить многоадресную рассылку IPv6 работать. Оказалось, что я устанавливал socket.setMulticastLoopback(false) для фильтрации сообщений, исходящих из самого node, который хорошо работал для IPv4, но блокировал все сообщения для IPv6. Удаление этого исправлено, проблема и сообщения начали появляться правильно, без необходимости фильтровать.