10

I am experimenting with IPv6 UDP multicast over a VPN. I have tried the following code:

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);

The script runs, and I see packets being sent/received with Wireshark, but neither end shows them in the console.

Wireshark UDP Capture

What am I doing wrong? What's a way to send and receive basic multicast with IPv6?

Brad
  • 159,648
  • 54
  • 349
  • 530
  • What exactly are you doing? The multicast group to which you are sending is a reserved multicast group for Link-Local Multicast Name Resolution ([RFC 4795](https://tools.ietf.org/html/rfc4795)). That multicast group will never be sent off the link on which it is originated. – Ron Maupin Jul 19 '16 at 03:22
  • @RonMaupin I only need to send multicast packets across the broadcast domain. The VPN takes care of putting everything on the same broadcast domain, and this is indeed working. I can see the multicast packets between machines over the VPN. I tried switching to `ff02::2`, and that still didn't work. What multicast address should I be using? – Brad Jul 19 '16 at 03:36
  • 1
    There are flags and scopes for IPv6 multicast. The multicast group `FF02::1` is the Link-Local All Nodes Address. You could start with that. Beyond that, you should research the various scopes and flags. For instance the scopes are: 0 Reserved, 1 Interface-Local scope, 2 Link-Local scope, 3 Realm-Local scope, 4 Admin-Local scope, 5 Site-Local scope, 6 Unassigned, 7 Unassigned, 8 Organization-Local scope, 9 Unassigned, A Unassigned, B Unassigned, C Unassigned, D Unassigned, E Global scope, F Reserved. – Ron Maupin Jul 19 '16 at 03:51
  • @RonMaupin Sorry, that was a typo. I used `ff02::1`, and that did not work for me. And, thanks for clarifying the scope. I've been getting started by looking at this: http://www.txv6tf.org/wp-content/uploads/2013/07/Martin-IPv6-Multicast-TM-v3.pdf I suppose if `ff02::1` is a valid multicast address, then I have a code problem of some sort. – Brad Jul 19 '16 at 03:54
  • @RonMaupin Another note... if I use `ff02::1`, I do not receive packets from my remote host over the VPN. If I use `ff02::1:3`, I do. Could this be due to a quirk/bug of the VPN implementation? – Brad Jul 19 '16 at 03:56
  • I think it may be swallowed by the device on which the VPN is configured. You could try and address in any of the link-local variable scope allocation ranges: [IANA Multicast Address Space Registry](https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml). – Ron Maupin Jul 19 '16 at 04:02
  • @RonMaupin Thanks Ron. I tried `ff12::1` and that doesn't work either. Neither do any of the other scopes, `ff13::1`, `ff14::1`, etc. I suppose the VPN software (Hamachi) is making an exception for `ff02:1:3`, but not allowing any other multicast? – Brad Jul 19 '16 at 04:13
  • You are changing the flags, and that is not a good idea, unless you really know what you are doing. Try something like `ff02::db8:0:1`. – Ron Maupin Jul 19 '16 at 04:17
  • Right, I was setting the temporary address flag, thinking that might have something to do with it, and then I tried the various scopes just to see if one would overcome the issue. `ff02::db8:0:1` does not work. A quick look in dmesg reveals a clue: `[639610.928709] [UFW BLOCK] IN=eth0 OUT= MAC= SRC=2620:009b:0000:0000:0000:0000:1908:468c DST=ff02:0000:0000:0000:0000:0db8:0000:0001 LEN=87 TC=0 HOPLIMIT=1 FLOWLBL=0 PROTO=UDP SPT=36912 DPT=36912 LEN=47` Firewall rules permit anything on the `ham0` interface (the VPN), but somehow this packet is getting stopped... – Brad Jul 19 '16 at 04:22
  • ... it looks like it's not bound to the correct interface. It's using `eth0` for some reason. – Brad Jul 19 '16 at 04:22
  • for the add_membership, the 2nd address should be a link-local (fe80..) address on the desired interface for it to get the right interface#, but I don't see where you are trying to recv? – lossleader Jul 21 '16 at 21:45
  • @lossleader Are you saying I should swap the parameter order I have for addMembership? And, what is the proper code for receiving? I had the bind in there, which I thought was enough. – Brad Jul 22 '16 at 00:27
  • @Brad, I am saying the 2nd address looks like the wrong type(global?) so the OS might not find the interface number from it and you should have (or create) a link local one to pass as addMembership's 2nd arg, but addMembership is just receive side. On the send side, there is no node wrapper around uv_udp_set_multicast_interface() so I think you have to fix that in an OS specific way. – lossleader Jul 22 '16 at 10:05
  • @lossleader The second address, `2620:9b::1944:e598`, is the address of the interface on the VPN. I'm using Hamachi. Historically their address have been `5.0.0.0/8` and then they moved to `25.0.0.0/8`. It has some link-local addresses as well. You're saying I should use these for adding membership? How about the bind? On sending, it was my understanding that the previous `bind` and/or `addMembership` is what took care of setting the interface. Is this not the case? – Brad Jul 22 '16 at 16:32
  • @Brad, w/v6 the 2nd position is an interface id, but node's wrapper/uv are hiding this difference by looking it up from an ip's sin6_scope_id which I think will be 0 and therefore your default interface (eth0) on any non-link-local ip.. so using any of its link local addresses will add receiving on that interface. The bind overrides source address selection (SAS) but interface selection is a higher level task that usually affects SAS, not the reverse. Multicast v6 interface selection is done explicitly with a different sock option node doesn't have a wrapper for, so your only option is via OS. – lossleader Jul 22 '16 at 18:04
  • @lossleader Ok, ignoring my code above, is there any way at all to broadcast data across an IPv6 network and receive it with Node.js? One machine sends to others, and the others receive it? IPv4 had normal broadcast, and I'll go back to that if I have to. But, I'd rather get IPv6 multicast working. (Multicast is a better option overall, and IPv6 of course doesn't have broadcast support anyway.) And, if this isn't possible with Node.js, could you post an answer citing code or some other reference? I'll happily award you the bounty if you can prove what I need isn't possible. – Brad Jul 22 '16 at 18:45
  • @lossleader Even better if you could file a bug report for Node.js. :-) I think I'd throw another 250 on for that, or more if you wanted. – Brad Jul 22 '16 at 18:46
  • "_ignoring my code above, is there any way at all to broadcast data across an IPv6 network_" IPv6 doesn't even have a broadcast concept. You must use multicast. To eliminate the code problem, try it on a small test LAN with just two PCs and a switch using ethernet. If it works for that, then there is some sort of problem with the VPN or devices which are the VPN endpoints. – Ron Maupin Jul 23 '16 at 01:00
  • @RonMaupin Yes, I'm aware, hence my comment stating that IPv6 doesn't have broadcast which is why I need multicast to work. I think I'll pick up an Raspberry Pi or something this weekend to experiment with. Somehow over the years of migrating everything to cloud services, using VMs for development, and eliminating apartment clutter, I'm down to one actual PC. :-) It's a new era for my utility bill. – Brad Jul 23 '16 at 01:05
  • @Brad Hopefully using explicitly scoped addresses isn't a problem in your setup, since they seem to be how libuv has smoothed the differences in v4/v6.. I'm still looking into the situation of the missing wrapper, but ironically it impacts v4 since it has no scoped addresses for as a work around. – lossleader Jul 23 '16 at 12:19

2 Answers2

8

Scope id -> interface number

In IPv6, there is a concept of a scope_id of an address that is supposed to indicate a context for the IP address, and generally just means what interface it is reachable on. While scopes have OS specific names, each just translates to an interface number, with 0 usually meaning the system's default.

In IPv6 multicast, this scope_id is provided directly to IP_ADD_MEMBERSHIP and IP_MULTICAST_IF instead of providing an ip associated with the interface as IPv4 does.

wrapper smoothing of v6's differences

node (via libuv) hides this difference for you in addMembership by looking up the scope_id from the "interface address" you provide.

Unfortunately, starting from just an IP and getting a scope doesn't make a lot of sense (the whole point of the scope is that the IP could have different uses in different scopes.) So libuv is only able to fill in a scope if you explicitly provide it at the end of the address, using the %[scope] format.

Using Addresses with Explicit Scopes

The way around this seems to be:

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

Where:

  • using :: (or no address) in bind is necessary since you are combining receive which will filter the multicast address on this with send which needs a normal address.

  • using %[iface#] forces the scope of this interface #.

  • The second argument of addMembership could really start with any address since we are forcing the scope and the rest is discarded.

  • Usually the send socket is separated and given a different port or an anonymous port as you are either limited in what you can configure or in danger of getting EADDRINUSE errors for having sockets that are too similar.

Community
  • 1
  • 1
lossleader
  • 13,182
  • 2
  • 31
  • 45
1

I followed the steps in this answer but still couldn't get IPv6 multicast to work. It turned out I was setting socket.setMulticastLoopback(false) to filter out messages coming from the node itself, which worked well for IPv4, but was blocking all messages for IPv6. Removing this fixed the issue and messages started appearing correctly, with no need to filter.

Adam Reis
  • 4,165
  • 1
  • 44
  • 35