Broadcasts and Multicasts, Hints and Tips

This thread has been locked by a moderator; it no longer accepts new replies.

For important background information, read Extra-ordinary Networking before reading this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


Broadcasts and Multicasts, Hints and Tips

I regularly see folks struggle with broadcasts and multicasts on Apple platforms. This post is my attempt to clear up some of the confusion.

This post covers both IPv4 and IPv6. There is, however, a key difference. In IPv4, broadcasts and multicasts are distinct concepts. In contrast, IPv6 doesn’t support broadcast as such; rather, it treats broadcasts as a special case of multicasts. IPv6 does have an all nodes multicast address, but it’s rarely used.

Before reading this post, I suggest you familiarise yourself with IP addresses in general. A good place to start is The Fount of All Knowledge™.

Service Discovery

A lot of broadcast and multicast questions come from folks implementing their own service discovery protocol. I generally recommend against doing that, for the reasons outlined in the Service Discovery section of Don’t Try to Get the Device’s IP Address.

There are, however, some good reasons to implement a custom service discovery protocol. For example, you might be working with an accessory that only supports this custom protocol [1]. If you must implement your own service discovery protocol, read this post and also read the advice in Don’t Try to Get the Device’s IP Address.

IMPORTANT Sometimes I see folks implementing their own version of mDNS. This is almost always a mistake:

  • If you’re using third-party tooling that includes its own mDNS implementation, it’s likely that this tooling allows you to disable that implementation and instead rely on the Bonjour support that’s built-in to all Apple platforms.

  • If you’re doing some weird low-level thing with mDNS or DNS-SD, it’s likely that you can do that with the low-level DNS-SD API.

[1] And whose firmware you can’t change! I talk more about this in Working with a Wi-Fi Accessory.

API Choice

Broadcasts and multicasts typically use UDP [1]. TN3151 Choosing the right networking API describes two recommended UDP APIs:

Our general advice is to prefer Network framework over BSD Sockets, but UDP broadcasts and multicasts are an exception to that rule. Network framework has very limited UDP broadcast support. And while it’s support for UDP multicasts is less limited, it’s still not sufficient for all UDP applications. In cases where Network framework is not sufficient, BSD Sockets is your only option.

[1] It is possible to broadcast and multicast at the Ethernet level, but I almost never see questions about that.

UDP Broadcasts in Network Framework

Historically I’ve claimed that Network framework was useful for UDP broadcasts is very limited circumstances (for example, in the footnote on this post). I’ve since learnt that this isn’t the case. Or, more accurately, this support is so limited (r. 122924701) as to be useless in practice.

For the moment, if you want to work with UDP broadcasts, your only option is BSD Sockets.

UDP Multicasts in Network Framework

Network framework supports UDP multicast using the NWConnectionGroup class with the NWMulticastGroup group descriptor. This support has limits. The most significant limit is that it doesn’t support broadcasts; it’s for multicasts only.

Note This only relevant to IPv4. Remember that IPv6 doesn’t support broadcasts as a separate concept.

There are other limitations, but I don’t have a good feel for them. I’ll update this post as I encounter issues.

Local Network Privacy

Some Apple platforms support local network privacy. This impacts broadcasts and multicasts in two ways:

  • Broadcasts and multicasts require local network access, something that’s typically granted by the user.

  • Broadcasts and multicasts are limited by a managed entitlement (except on macOS).

TN3179 Understanding local network privacy has lots of additional info on this topic, including the list of platforms to which it applies.

Send, Receive, and Interfaces

When you broadcast or multicast, there’s a fundamental asymmetry between send and receive:

  • You can reasonable receive datagrams on all broadcast-capable interfaces.

  • But when you send a datagram, it has to target a specific interface.

The sending behaviour is the source of many weird problems. Consider the IPv4 case. If you send a directed broadcast, you can reasonably assume it’ll be routed to the correct interface based on the network prefix. But folks commonly send an all-hosts broadcast (255.255.255.255), and it’s not obvious what happens in that case.

Note If you’re unfamiliar with the terms directed broadcast and all-hosts broadcast, see IP address.

The exact rules for this are complex, vary by platform, and can change over time. For that reason, it’s best to write your broadcast code to be interface specific. That is:

  1. Identify the interfaces on which you want to work.

  2. Create a socket per interface.

  3. Bind that socket to that interface.

Note Use the IP_BOUND_IF (IPv4) or IPV6_BOUND_IF (IPv6) socket options rather than binding to the interface address, because the interface address can change over time.

Extra-ordinary Networking has links to other posts which discuss these concepts and the specific APIs in more detail.

Miscellaneous Gotchas

A common cause of mysterious broadcast and multicast problems is folks who hard code BSD interface names, like en0. Doing that might work for the vast majority of users but then fail in some obscure scenarios.

BSD interface names are not considered API and you must not hard code them. Extra-ordinary Networking has links to posts that describe how to enumerate the interface list and identify interfaces of a specific type.

Don’t assume that there’ll be only one interface of a given type. This might seem obviously true, but it’s not. For example, our platforms support peer-to-peer Wi-Fi, so each device has multiple Wi-Fi interfaces.

When sending a broadcast, don’t forget to enable the SO_BROADCAST socket option.

If you’re building a sandboxed app on the Mac, working with UDP requires both the com.apple.security.network.client and com.apple.security.network.server entitlements.

Some folks reach for broadcasts or multicasts because they’re sending the same content to multiple devices and they believe that it’ll be faster than unicasts. That’s not true in many cases, especially on Wi-Fi. For more on this, see the Broadcasts section of Wi-Fi Fundamentals.

Snippets

To send a UDP broadcast:

func broadcast(message: Data, to interfaceName: String) throws {
let fd = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)
defer { try! fd.close() }
try fd.setSocketOption(SOL_SOCKET, SO_BROADCAST, 1 as CInt)
let interfaceIndex = if_nametoindex(interfaceName)
guard interfaceIndex > 0 else { throw }
try fd.setSocketOption(IPPROTO_IP, IP_BOUND_IF, interfaceIndex)
try fd.send(data: message, to: ("255.255.255.255", 2222))
}

Note These snippet uses the helpers from Calling BSD Sockets from Swift.

To receive UDP broadcasts:

func receiveBroadcasts(from interfaceName: String) throws {
let fd = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)
defer { try! fd.close() }
let interfaceIndex = if_nametoindex(interfaceName)
guard interfaceIndex > 0 else { fatalError() }
try fd.setSocketOption(IPPROTO_IP, IP_BOUND_IF, interfaceIndex)
try fd.setSocketOption(SOL_SOCKET, SO_REUSEADDR, 1 as CInt)
try fd.setSocketOption(SOL_SOCKET, SO_REUSEPORT, 1 as CInt)
try fd.bind("0.0.0.0", 2222)
while true {
let (data, (sender, port)) = try fd.receiveFrom()
}
}

IMPORTANT This code runs synchronously, which is less than ideal. In a real app you’d run the receive asynchronously, for example, using a Dispatch read source. For an example of how to do that, see this post.

If you need similar snippets for multicast, lemme know. I’ve got them lurking on my hard disk somewhere (-:

Other Resources

Apple’s official documentation for BSD Sockets is in the man pages. See Reading UNIX Manual Pages. Of particular interest are:

If you’re not familiar with BSD Sockets, I strongly recommend that you consult third-party documentation for it. BSD Sockets is one of those APIs that looks simple but, in reality, is ridiculously complicated. That’s especially true if you’re trying to write code that works on BSD-based platforms, like all of Apple’s platforms, and non-BSD-based platforms, like Linux.

I specifically recommend UNIX Network Programming, by Stevens et al, but there are lots of good alternatives.

https://unixnetworkprogramming.com/

Boost
Broadcasts and Multicasts, Hints and Tips
 
 
Q