Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udp_set_membership fails for IPv6 destination addresses #39208

Closed
wjholden opened this issue Jan 12, 2021 · 5 comments
Closed

udp_set_membership fails for IPv6 destination addresses #39208

wjholden opened this issue Jan 12, 2021 · 5 comments

Comments

@wjholden
Copy link
Contributor

function udp_set_membership(sock::UDPSocket, group_addr::String,

I am not able to open join an IPv6 multicast destination. Here is an example program that first listens to on an IPv4 multicast destination, then IPv6:

using Sockets

# The the following IPv4 and IPv6 multicast destinations,
for group in [ip"239.2.0.21", @ip_str "ff05::2021"]
	# Create a socket
	s = Sockets.UDPSocket()

	# Bind this socket to all interfaces on port 2021, allowing port reuse.
	# The "ipv6only" parameter does not change the outcome.
	bind(s, ip"0.0.0.0", 2021)

	# Join the multicast destination group
	join_multicast_group(s, group)

	# Print the first ten datagrams received as strings.
	for _ in 1:10
		println(String(recv(s)))
	end

	# Leave the group
	leave_multicast_group(s, group)

	# Close the socket
	close(s)
end

Here is a simple program that sends input to the above:

using Sockets

# For the same two multicast destinations
for group in [ip"239.2.0.21", @ip_str "ff05::2021"]
	# Open a socket. There is no need to bind anything.
	s = Sockets.UDPSocket()

	# Send ten packets.
	for i in 1:10	
		send(s, group, 2021, "Test $i")
	end

	# Close the socket
	close(s)
end

The error I get is:

ERROR: IOError: uv_udp_set_membership: invalid argument (EINVAL)
Stacktrace:
 [1] uv_error at .\libuv.jl:97 [inlined]
 [2] udp_set_membership(::UDPSocket, ::String, ::Nothing, ::Int64) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Sockets\src\Sockets.jl:740
 [3] join_multicast_group at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Sockets\src\Sockets.jl:753 [inlined]
 [4] join_multicast_group at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Sockets\src\Sockets.jl:760 [inlined]
 [5] join_multicast_group(::UDPSocket, ::IPv6) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Sockets\src\Sockets.jl:757
 [6] top-level scope at .\REPL[3]:10

I have been poking around in Sockets.jl and https://github.com/libuv/libuv/blob/v1.x/src/win/udp.c but have not come up with anything.

I am translating a Python program into Julia. This may or may not be relevant, but in Python I had to set the address family to scoket.IPPROTO_IPV6 for IPv6 multicast (see http://svn.python.org/projects/python/trunk/Demo/sockets/mcast.py). Here is a small Python IPv6 receiver for reference:

import socket
import struct

# Open a socket initialized with an explicit IPv6 address family.
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)

# Allow port reuse.
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the port on all interfaces to UDP port 2021.
s.bind(('', 2021))

# Join the multicast destination
group_bin = socket.inet_pton(socket.AF_INET6, "ff05::2021")
mreq = group_bin + struct.pack("@I", 0)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)

for _ in range(10):
	data, _ = s.recvfrom(1500)
	print(str(data))
@wjholden
Copy link
Contributor Author

I got it to work! The syntax for this is a little trickier than one might expect.

using Sockets

# Use the Sockets.IPv6 function to parse IPv6 addresses.
group = Sockets.IPv6("ff05::2021")
s = Sockets.UDPSocket()

# Binding fails if you try to use Sockets.IPv4("0.0.0.0"),
# with and without the ipv6only parameter.
bind(s, Sockets.IPv6("::"), 2021)

join_multicast_group(s, group)

for _ in 1:10
	println(String(recv(s)))
end

leave_multicast_group(s, group)

close(s)

To send data, I used the ncat program on my Windows computer.

"This actually works!" | ncat -v --send-only -u "ff05::2021" 2021

I found that IPv6 multicast group joins fail if you try to bind to all interfaces with Sockets.IPv4("0.0.0.0"). The bind function only worked for me with Sockets.IPv6("::").

@StefanKarpinski
Copy link
Member

That code looks pretty reasonable to me. Any suggestions for what to improve here?

wjholden added a commit to wjholden/julia that referenced this issue Jul 6, 2021
Added a section at the end showing examples of IPv4/IPv6 UDP multicast. See JuliaLang#39208.
@wjholden
Copy link
Contributor Author

wjholden commented Jul 6, 2021

I think a small section in the documentation on UDP multicast could be useful to others. I have added a short section and submitted a pull request. (I am completely new to contributing to someone else's open-source project, so if there is something I can do better about this please let me know).

DilumAluthge pushed a commit that referenced this issue Aug 3, 2021
Added a section at the end showing examples of IPv4/IPv6 UDP multicast. See #39208.

Co-authored-by: Jameson Nash <vtjnash@gmail.com>
@quildtide
Copy link
Contributor

I have to thank this post for helping me figure out what I was doing wrong. I was mistakenly trying to bind to the multicast address itself instead of 0.0.0.0 (for IPv4), and that returns false unless one calls join_multicast_group first, which doesn't work. (My networking knowledge is weak enough that I don't quite understand if that's not intended behavior)

This issue and the referenced documentation pull request are the only examples of working Julia multicasting on the indexed internet that I know of.

Thanks for the documentation addition; I think it'll help a lot of people once 1.7 hits the live documentation.

@wjholden
Copy link
Contributor Author

The documentation is now merged at https://docs.julialang.org/en/v1/manual/networking-and-streams/#Multicast. I will close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants