IPv6 Multicast problems

Godot Version

v4.6.1.stable.official.14d19694e

Question

Hi! I am currently developing LAN broadcast, IPv4 was simple and works perfectly, but when trying to support IPv6, it is weird. The code below works, but it would send the packet using weird interfaces, such as localhost on Windows and dummy0 on Android, despite Wi-Fi and other interfaces being available, which is not good, because the only device that then discovers that packet is the device which sent it. Is there a way to choose which interface sends the IPv6 packet, or something that would fix my situation? IPv4 broadcast is good enough for my project, but I am curious if its possible.

# SERVER

var peer := PacketPeerUDP.new()
peer.set_dest_address("ff12::6435", 40256)
peer.put_packet("Hi! Join me on port xxxx".to_utf8_buffer())
# CLIENT

var peer := PacketPeerUDP.new()
peer.bind(40256, "*")

# The following line is called on every interface
peer.join_multicast_group("ff12::6435", "wlan0")
peer.join_multicast_group("ff12::6435", "lo")

# When a packet is available
var packet: PackedByteArray = peer.get_packet()
var ip: String = peer.get_packet_ip()

Example Console output from Windows:

New packet from fe80:0:0:0:f474:f79c:4bb8:baf1 (PORT 52761)

Where fe80:0:0:0:f474:f79c:4bb8:baf1 is the Wi-Fi interface, which is weird, because just a second ago, without changing the code it would output ::1, but on Android it still outputs the ip from dummy0. (This is the first time I was able to recieve the signal from another device, as of writing this post)

Another question, do I have to be using diffrent PacketPeerUDP for IPv4 and IPv6 (for recieving)?

Also, a MULTICAST_LOCK is aquired on Android, which works, because IPv4 broadcast works fine (without it not):

# The reference to this Object is never lost
static var _android_fix: Object = null

func get_lock() -> bool:
	var android_runtime: JNISingleton = Engine.get_singleton("AndroidRuntime")
	if android_runtime == null:
		printerr("Android MulticastLock: AndroidRuntime not available")
		return false
	var activity: Object = android_runtime.call("getActivity")
	if activity == null:
		printerr("Android MulticastLock: No Android activity")
		return false
	var wifi_service: Object = activity.call("getSystemService", "wifi")
	if wifi_service == null:
		printerr("Android MulticastLock: No Wi-Fi service")
		return false
	_android_fix = wifi_service.call("createMulticastLock", "godot_multicast_lock")
	if _android_fix == null:
		return false
	return true

Anyone has an idea why?

To start off, I am not too familar with multicast or Android networking.

A shot in the dark, but have you tried changing peer.bind(40256, "*") to peer.bind(40256, "::")? It may make a difference, as the :: is the wildcard address for IPv6; and * is the wildcard for IPv4. I looked in the source, and it may work?

The Godot Documentation also mentions to have networking permissions set and enabled in order for this to work. I think without it, Android may still allow loopback networking (but I’m not sure if it can exit the device/app). This may explain some strange behaviour (see https://developer.android.com/privacy-and-security/local-network-permission).

From some Googling, it appers that the interface chosen depends on the OS’ implementation. And for UDP sockets it appers that the interface that has a valid route to your destination is chosen. Not that it’s a good idea (for your sanity), but you can try to override this by likely chaing the source code.

For your second question, I think each PacketPeerUDP has its own socket, therefore you would need multiple PackerPeerUDP to work across IPv4 and IPv6. Looking over the initaisation logic it either picks IPv4 or IPv6, but not both.

From my understanding, PeerPacketUDP derives from PeerPacket, for which the socket implementaiton is provided by NetSocket. NetSocket is a purely virtual class which is defined by each platform.

NetSocketAndroid is the one specific to Android, however it gains most of its functionality from NetSocketUnix aka one used by Unix-like systems (e.g. Linux). It binds to what it sees as the path, defined by the CharString NetSocket::Address::_path member.

Now, NetSocketUnix::_unix_bind is what is called when you try to bind an IPv6 address. This calls the bind(2) function which should be for all interfaces as long as INADDR_ANY is passed for the address (potentially a :: wildcard). See bind(2) - Linux manual page and https://stackoverflow.com/questions/12709771/how-to-bind-a-socket-to-multiple-interfaces .

Please double check my thought process/work by checking these files yourself. Please let me know if I’ve gotten something horribly wrong.

Links to source files mentioned

godot/core/io/packet_peer_udp.h at master · godotengine/godot · GitHub
godot/core/io/packet_peer_udp.cpp at master · godotengine/godot · GitHub
godot/core/io/packet_peer.h at master · godotengine/godot · GitHub
godot/core/io/packet_peer.cpp at master · godotengine/godot · GitHub
godot/core/io/net_socket.h at master · godotengine/godot · GitHub
godot/platform/android/net_socket_android.h at master · godotengine/godot · GitHub
godot/platform/android/net_socket_android.cpp at master · godotengine/godot · GitHub
godot/drivers/unix/net_socket_unix.h at master · godotengine/godot · GitHub
godot/drivers/unix/net_socket_unix.cpp at master · godotengine/godot · GitHub

Aside from that, I’d suggest trying to see if it is still an issue if you use different software. On Android you can download Termux and run a lot of Linux applications and e.g. use Python to create a multicast server/client and see what happens. Multicast networking can be a bit shoddy at times due to vendor-specific implementations.

For example, this StackOverflow has some code to create a multicast reciever: https://stackoverflow.com/questions/15197569/any-small-program-to-receive-multicast-packets-on-specified-udp-port

Hope this helps!

If you call peer.bind() on the serverwith the specific nic you want the server to send on, that will tell the OS what you want to do. Otherwise each OS will pick the interface on its own based on its onw heuristic. I think you can also just add %en0 (or whatever your interface is) at the end of the ipv6 address on the server to achieve the same thing.

Thanks a lot! Will look into it.