0

I cannot get UDP multicast over IPv6 to work. The platform on which I'm trying to do this is iOS (using Swift). I have a GCDAsyncUdpSocket and configure it like this:

self.socket!.setIPv4Enabled(false)
self.socket!.setIPv6Enabled(true)
do {
    try self.socket!.bindToPort(Announcement.ipv6BroadcastPort, interface: "en0")
}
catch let error {
    print("bind failed: \(error)")
    throw error
}
do {
    try self.socket!.joinMulticastGroup("ff32::5222", onInterface: "en0")
}
catch let e {
    print("join multicast failed: \(e)")
}

After that, I try to send some data to a multicast address:

socket.sendData(data, toHost:"ff32::5222", port: 21026, withTimeout: 5, tag: 0)

At that point, when stepping through the GCDAsyncUDPSocket code, I end up in a call to sendto in the method doSend (line 3919). This call returns -1 and the error is "no route to host". I'm running this in the iOS simulator. When I enumerate all available network interfaces, I get this:

Name: lo0
Address: Optional("::1ebc:680b:100:0")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: lo0
Address: Optional("::2bc:680b:100:0")
Family: NetUtils.Interface.Family.ipv4
Supports multicast: true

Name: lo0
Address: Optional("fe80::1ebc:680b:100:0")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: en0
Address: Optional("fe80::1ebc:680b:100:0")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: en0
Address: Optional("2001:984:3427:1:1ebc:680b:100::")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: en0
Address: Optional("2001:984:3427:1:1ebc:680b:100::")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: en0
Address: Optional("::2bc:680b:100:0")
Family: NetUtils.Interface.Family.ipv4
Supports multicast: true

Name: awdl0
Address: Optional("fe80::1ebc:680b:100:0")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: utun0
Address: Optional("fe80::1ebc:680b:100:0")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Name: utun0
Address: Optional("fde6:4b33:67e4:9d5e:1ebc:680b:100::")
Family: NetUtils.Interface.Family.ipv6
Supports multicast: true

Any help is much appreciated!

Stefan
  • 152
  • 6
  • I'm not sure about your whole problem, but I don't see a link-local address on your en0 interface. The (missing) link-local address should be the source address for a link-local multicast such as you are trying to send. Also, your multicast address seems to be poorly chosen. It is in the Unicast-based (Including SSM) Multicast Group IDs, and it is reserved for future SSM address use, but currently invalid. It is also a link-local scoped multicast address, so it cannot cross a layer-3 boundary. – Ron Maupin Jul 23 '15 at 02:34
  • Thanks Ron, I'll look into this, it'll take a few days. I'll also try to run my test on an actual iPhone and see if that has a different set of interfaces. Regarding the address: it is specified here: https://github.com/syncthing/specs/blob/master/DISCOVERYv2.md. I'll pass your comment on to them so that they may explain and/or use it to their advantage. – Stefan Jul 23 '15 at 19:20
  • That's interesting. It looks like they made up their own multicast address. See the IANA document (last updated 07/21/2015) about that IPv6 multicast range: http://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml#unicast-multicast-group-ids – Ron Maupin Jul 23 '15 at 21:00
  • A bit more information: after switching to a different way of extracting the network address for my debug output, I get fe80::1ebc:680b:100:0 instead of nil for the first en0 interface address. I updated my post to reflect this. So I do in fact have a link-local address, I just generated the debug output the wrong way. Does this shine a different light on things? – Stefan Jul 24 '15 at 20:48

1 Answers1

2

I have solved my issue, I succeeded in doing a send/receive over IPv6 UDP Multicast. The problem turned out to be a combination of issues.

  • For sending these messages, it is required to set the socket option IPV6_MULTICAST_IF, which GCDAsyncUdpSocket does not do. (Receiving doesn't seem to require it.)
  • When sending a message, you do have to bind before you can send (but don't bind to a specific port, because what you're binding is the source port, not the destination port).
  • When you try to set a Swift object as a delegate of an Objective-C object, make sure your Swift object inherits from NSObject, or be screwed! No warnings, no errors, the delegate simply doesn't get called.
  • Wireshark was my friend again :). I could see that the socket started listening because of the ICMPv6 "Multicast Listener Report v2" coming by, with contents: "Multicast Address Record Changed to exclude: ff12::5222". Note that "exclude" means that it does not get filtered away, in other words: that it is passed on. (Thanks Paul!)

Working proof of concept here: https://source.ind.ie/project/pulse-swift/blob/28bd0f48e3d4fa99f6095321a89036863c3f7a83/pulse-swift/discovery/Greeter.swift

Stefan
  • 152
  • 6
  • I'd been struggling with this problem on and off for about a year, and came across your post in the last couple of days. It's interesting that this is not an issue on Android or Windows – Dave Nottage Mar 09 '18 at 22:36