Multicast entitlement applied incorrectly?

I am using Godot 4.2.1 and C# (.NET 8.0.1) to create an app. In Godot the app is exported to an XCode project so it can then be built to run on IOS devices. Access WiFi is checked in the Godot presets I have the multicast entitlement from Apple. Communication over UDP using Unicast to send and receive packets works correctly on iOS, macOS and Android devices.

To set the multicast entitlement, the project name is selected in XCode, to open a big dialog box with multiple tabs. Click on the tab Signing and Capabilities. To add the multicast to the project "+ Capability" is clicked, search for "multi" which brings up multicast networking. Click on multicast networking to add it to the project.

Then in the same dialog box, click the tab build settings and under "Signing" code signing identity is changed to iOS developer for all Debug and release items.

The project is then built and run on the iOS device. The symptom is that no multicast packets are received.

The multicast receiving code below works on macOS and Android devices so there must be something going on with the iOS devices that I'm missing.

The UDP server is configured in C# code as a _listener:

 private const string MULTICAST_ADDRESS = "239.255.1.1";
 private const int BCON_PORT = 49707;

_listener = new UdpClient();
_listener.ExclusiveAddressUse = false;
_listener.JoinMulticastGroup(IPAddress.Parse(MULTICAST_ADDRESS));
_listener.Client.Bind(new IPEndPoint(IPAddress.Any, BCON_PORT));

Replies

The symptom is that no multicast packets are received.

There are lots of potential reasons for that, most of which are unrelated to the multicasts entitlement. Unfortunately it’s hard to offer concrete advice given that you’re using a third-party wrapper around (presumably) BSD Sockets.

Let me start by asking a simple question: What are you using multicasts for? I presume you’re building a game, and most game developers who hit this issue are implementing local network multiplayer. If that’s the case:

  • You can avoid this whole exercise by using GameKit.

  • If GameKit doesn’t work for you — perhaps you want to support non-Apple platforms — adopt Bonjour [1]. This is widely supported on non-Apple platforms (including Windows, Android, and Linux).

Both of these get you out of the business of dealing with multicasts directly. They’re also likely to be more efficient on the network than any custom solution you create.


If you’re must use multicasts directly, it’s possible that you can avoid this grief by adopting Network framework. Here’s a trivial example of how to set that up in Swift:

// Multicast addresses are those reserved for experimental use by RFC
// 4727.
//
// <https://tools.ietf.org/html/rfc4727>
let groupAddress = NWEndpoint.hostPort(host: "224.0.0.254", port: 12345)

let description = try NWMulticastGroup(for: [groupAddress])
let group = NWConnectionGroup(with: description, using: .udp)
group.setReceiveHandler(maximumMessageSize: 64 * 1024) { … }
group.stateUpdateHandler = { … }
group.start(queue: .main)

If you can’t use Network framework, you have to deal with BSD Sockets )-: In that case it’s best to target a specific interface explicitly. Without that, the system has to choose the interface to target, and my experience is that the system rarely does what you want [2].

Unfortunately that requires you to work with network interfaces directly, which is an exercise fraught with peril. I discuss this in much detail in Extra-ordinary Networking.

Here’s a snippet that shows how to set up a socket to send and receive multicasts on a specific interface:

// Multicast addresses are those reserved for experimental use by RFC
// 4727.
//
// <https://tools.ietf.org/html/rfc4727>
let groupAddress = (address: "224.0.0.254", port: 12345 as UInt16)

let socket = try FileDescriptor.socket(AF_INET, SOCK_DGRAM, 0)

// Join the group.

let ifAddr = interfaceAddress()
let groupAddrIP = in_addr(s_addr: inet_addr(groupAddress.address))
let ifAddrIP = in_addr(s_addr: inet_addr(ifAddr))
var req = ip_mreq(imr_multiaddr: groupAddrIP, imr_interface: ifAddrIP)
try socket.setSocketOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, MemoryLayout.size(ofValue: req))

// Bind the socket.

try socket.setSocketOption(SOL_SOCKET, SO_REUSEPORT, 1 as CInt)
try socket.bind("0.0.0.0", groupAddress.port)

// Enable non-blocking mode.

try socket.setNonBlocking()

This is using the QSocket wrapper discussed here.

The tricky part with this code is the interfaceAddress() function. For advice on that, see Don’t Try to Get the Device’s IP Address. Specifically, if you are doing service discovery then follow the advice in the Service Discovery section of that post.

Share and Enjoy

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

[1] An Apple term for three Internet standards:

[2] Worse yet, it might do the right thing in your environment but then fail in the field, or change behaviour over time.

This is for a game. The only purpose for a multicast is to find the devices on the local network that are running the game. Once the device is found the rest of the communicatio is done with unicast. Unicast communications with the game works fine once the IPaddres is provided

I would like to save the user of the game the hassle of finding the ip address of their device

Im using Godot to develop the multiplatform game. Godot allows the use of their own script language or C# (Net 8.)

The Godot script language appears to be a BSD socket as it required the interface to be specified. Finding the interface is less intuitive for regular users than finding an up address. So using Godot script has been abandoned

Godot also allows the use of C# for its scripts. Using C# I have been able to run the game on Mac, Windows and Android platforms, but unfortunately not n apple devices. There is no need to specify the interface when using C#

Since the application is being developed on a Godot game engine platform using objective-c is not an option

I don’t know if it would be possible to have an objective-c section of code merged with the .NET export from Godot

Another hack I’ve considered is creating a 10 line app in objective-c that be the multicast receiver and stuff the received IP addresses into a file that the primary app could read It might work but it would be an ugly solution

It would be optimal make the C# solution work. Since C# is a Microsoft product and I’m trying to make it work on an Apple platform, who can I contact to resolve this cross-platform issue?

The only purpose for a multicast is to find the devices on the local network that are running the game.

I recommend that you use Bonjour for that. It’s easier to implement, and results in better on-the-wire network behaviour.

Share and Enjoy

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

If I understand correctly, To use Bonjour in a C# application, I will need to use a wrapper like Zeroconfig (a GitHub project) and add a dynamic library dependency in my Godot project

Unfortunately, it appears that the zeroconfig wrapper only discovers services that broadcast a message with a service discovery format of DNS-SD

Is there is a different wrapper that would let me discover packets with a BCON…. Pattern? Writing a custom wrapper for Bonjour would be a steep learning curve

Is there a C# wrapper for Network framework or Gamekit?

it appears that the zeroconfig wrapper only discovers services that broadcast a message with a service discovery format of DNS-SD

Right. That’s one of the network protocols that Apple lumps under the marketing term Bonjour. See the footnote in my earlier post.

Share and Enjoy

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