-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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: add source-specific multicast support #964
Conversation
.gitignore
Outdated
@@ -74,5 +74,7 @@ ipch | |||
*.xcodeproj | |||
*.xcworkspace | |||
|
|||
CMakeLists.txt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unneeded change, please drop it
Thanks for contributing @falanger! Mostly LGTM, left some comments. |
Hi, @saghul! Thanks for your time! I've fixed all the comments. |
Changes look good, though I'd like to add IPv6 support if possible. /cc @libuv/collaborators any other reviews? |
optname = IP_DROP_SOURCE_MEMBERSHIP; | ||
break; | ||
default: | ||
return -EINVAL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think one level of indentation is missing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's consistent with other "switch..case" instructions in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though the style is not consistent in the project, I would add indentation here as it's already the most commonly used format in the source and I think consistency is good. Anyway, let's see what others think.
@saghul sorry, I was wrong about IPv6 support (thanks, @santigimeno). |
@saghul @santigimeno any comments? |
src/unix/udp.c
Outdated
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr6); | ||
if (err) | ||
return err; | ||
err = uv_ip6_addr(multicast_addr, 0, &src_addr6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be source_addr
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/unix/udp.c
Outdated
if (err) | ||
return err; | ||
|
||
memset(&mreq, 0, sizeof mreq); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use consistent parentheses with sizeof
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
I was hoping to do some source-specific multicast in node.js (which uses libuv). This PR would introduce that capability but it is unfortunately stalled, is there any way to unblock it? It seemed like the last review identified some minor style issues but otherwise things were ok. |
} | ||
|
||
return 0; | ||
#else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment here that mentions what #ifdef
this corresponds to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
docs/src/udp.rst
Outdated
@@ -181,6 +181,25 @@ API | |||
|
|||
:returns: 0 on success, or an error code < 0 on failure. | |||
|
|||
.. c:function:: int uv_udp_set_source_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, const char* source_addr, uv_membership membership) | |||
|
|||
Set membership for a source-specific multicast group |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing period at the end of the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
docs/src/udp.rst
Outdated
|
||
:returns: 0 on success, or an error code < 0 on failure. | ||
|
||
.. versionadded:: 1.10.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version number needs to be updated. Currently, this would be 1.15.0 at the earliest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@falanger it's been quite a while. Sorry about that. Are you still interested in pursuing this PR? |
@cjihrig Hi! Yes, I am interested in. I'll try to fix issues on this week. |
test/test-udp-multicast-join6.c
Outdated
@@ -134,6 +134,17 @@ TEST_IMPL(udp_multicast_join6) { | |||
|
|||
ASSERT(r == 0); | |||
|
|||
r = uv_udp_set_source_membership(&client, "ff02::1", NULL, "2001:db8:a0b:12f0::1", UV_JOIN_GROUP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the rest of the test still be run if UV_ENODEV
or UV_EPROTONOSUPPORT
is returned? If so, maybe the assertion should just allow them so that the remainder of the test is still executed. If that can't be done, maybe a separate test file should be created.
test/test-udp-multicast-join.c
Outdated
@@ -120,6 +120,12 @@ TEST_IMPL(udp_multicast_join) { | |||
RETURN_SKIP("No multicast support."); | |||
ASSERT(r == 0); | |||
|
|||
/* join the source-specific multicast channel */ | |||
r = uv_udp_set_source_membership(&client, "239.255.0.1", NULL, "10.50.129.200", UV_JOIN_GROUP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here about finishing the rest of the test.
src/win/udp.c
Outdated
if (err) { | ||
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr6); | ||
if (err) | ||
return err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two space indentation please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I have fixed all indentation issues.
src/win/udp.c
Outdated
return err; | ||
err = uv_ip6_addr(source_addr, 0, &src_addr6); | ||
if (err) | ||
return err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here.
src/win/udp.c
Outdated
return uv_translate_sys_error(WSAGetLastError()); | ||
} | ||
|
||
return 0; | ||
#else | ||
return -EPROTONOSUPPORT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UV_EPROTONOSUPPORT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/unix/udp.c
Outdated
if (err) { | ||
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr6); | ||
if (err) | ||
return err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, two space indentation. I'll stop commenting on it :-)
src/unix/udp.c
Outdated
@@ -490,7 +501,7 @@ static int uv__udp_set_membership4(uv_udp_t* handle, | |||
int optname; | |||
int err; | |||
|
|||
memset(&mreq, 0, sizeof mreq); | |||
memset(&mreq, 0, sizeof(mreq)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you revert all of the unrelated style changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. BTW, why the style is inconsistent in file (and project) scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the style is inconsistent in file (and project) scope?
Because we haven't agreed on and enforced automated linting. So, some stuff gets by the human eye test.
src/unix/udp.c
Outdated
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; | ||
mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; | ||
|
||
switch (membership) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just write this as an if...else if...else
. It would be several lines shorter, and there are only a couple of options. I'd also move it to the top of the function so that -EINVAL
can be returned right away if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you, but in this case functions uv_udp_set_membership
and uv_udp_set_source_membership
will be written in different manner. If this is OK, I would replace switch .. case
with if .. else
construction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I'd go with if...else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/unix/udp.c
Outdated
|
||
return 0; | ||
#else | ||
return UV_EPROTONOSUPPORT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be -EPROTONOSUPPORT
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come to think of it, I think we return ENOSYS
in similar situations, but I could be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, ENOSYS
means that function not implemented, so I think that EPROTONOSUPPORT
or ENOPROTOOPT
is more suitable error in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to other @libuv/collaborators. Either way, this should be -EWHATEVER
in the Unix code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/unix/udp.c
Outdated
memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group)); | ||
memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source)); | ||
|
||
switch (membership) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as the previous switch
statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/win/udp.c
Outdated
@@ -28,6 +28,17 @@ | |||
#include "stream-inl.h" | |||
#include "req-inl.h" | |||
|
|||
#if defined(MCAST_JOIN_SOURCE_GROUP) && defined(MCAST_LEAVE_SOURCE_GROUP) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to consolidate with the same code in src/unix/udp.c
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, It's possible. Can I place this defines in uv.h
header file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to uv.h
src/win/udp.c
Outdated
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; | ||
mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; | ||
|
||
switch (membership) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about the switch
. I'll stop repeating it now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/win/udp.c
Outdated
memset(&mreq, 0, sizeof(mreq)); | ||
|
||
if (interface_addr) { | ||
err = uv_ip6_addr(interface_addr, 0, &addr6) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing semi-colon, causes a compile failure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/win/udp.c
Outdated
err = uv_ip6_addr(interface_addr, 0, &addr6) | ||
if (err) | ||
return err; | ||
mreq.ipv6mr_interface = addr6.sin6_scope_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fails to compile for me:
src\win\udp.c(798): error C2039: 'ipv6mr_interface': is not a member of 'group_source_req'
Do you mean mreq.gsr_interface
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's right. Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@LPardue please, send me a mention next time. I'll try to write tests as soon as I can - it's the last thing that needs to be done to merge this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@falanger will do, feel free to take inspiration from the SSM node tests I created in nodejs/node#15735
docs/src/udp.rst
Outdated
|
||
:returns: 0 on success, or an error code < 0 on failure. | ||
|
||
.. versionadded:: 1.15.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would now be 1.16.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/unix/udp.c
Outdated
mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; | ||
mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; | ||
|
||
switch (membership) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I'd go with if...else
src/unix/udp.c
Outdated
|
||
memset(&mreq, 0, sizeof(mreq)); | ||
|
||
if (interface_addr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing throughout this PR. Can you try to be more explicit in these if
s by checking for ==
and !=
to 0, NULL
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/unix/udp.c
Outdated
|
||
return 0; | ||
#else | ||
return UV_EPROTONOSUPPORT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to other @libuv/collaborators. Either way, this should be -EWHATEVER
in the Unix code.
Seems like this is required to unblock a PR in Node.js. It would be great if we could get this to land some time soon therefore :-) |
Ping @cjihrig @saghul @bnoordhuis |
Unnecessary ping. It looks like there are a few unresolved review comments. |
@cjihrig Hi! I've resolved all issues. |
docs/src/udp.rst
Outdated
|
||
:returns: 0 on success, or an error code < 0 on failure. | ||
|
||
.. versionadded:: 1.16.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it should be Version 1.20.0
now because the current version is already Version 1.19.1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there any resolution with regards to the tests?
src/unix/udp.c
Outdated
struct sockaddr_in mcast_addr4; | ||
struct sockaddr_in src_addr4; | ||
struct sockaddr_in6 mcast_addr6; | ||
struct sockaddr_in6 src_addr6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could use 2 sockaddr_storage
that you could later on cast to sockaddr_in
or sockaddr_in6
. See: uv_udp_set_multicast_interface
implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so because the uv_udp_set_source_membership
function should reproduce uv_udp_set_membership
functionality with additional parameter source_addr
. These functions have the same behavior and they should be written in the same manner IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see that uv_udp_set_membership
is not using sockaddr_storage
but it seems to me that it could and you could avoid having 2 extra sockaddr structs in the stack.
struct sockaddr_storage mcast_addr;
struct sockaddr_in* mcast_addr4;
struct sockaddr_in6* mcast_addr6;
struct sockaddr_storage src_addr;
struct sockaddr_in* src_addr4;
struct sockaddr_in6* src_addr6;
mcast_addr4 = (struct sockaddr_in*)&mcast_addr;
mcast_addr6 = (struct sockaddr_in6*)&mcast_addr;
src_addr4 = (struct sockaddr_in*)&src_addr;
src_addr6 = (struct sockaddr_in6*)&src_addr;
Though it seems too verbose. Maybe using an union
looks nicer. Something like this:
int uv_udp_set_source_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
const char* source_addr,
uv_membership membership) {
int err;
union address {
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
};
union address mcast_addr;
union address src_addr;
err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.sa_in);
if (err) {
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.sa_in6);
if (err)
return err;
err = uv_ip6_addr(source_addr, 0, &src_addr.sa_in6);
if (err)
return err;
return uv__udp_set_source_membership6(handle, &mcast_addr.sa_in6, interface_addr, &src_addr.sa_in6, membership);
}
err = uv_ip4_addr(source_addr, 0, &src_addr.sa_in);
if (err)
return err;
return uv__udp_set_source_membership4(handle, &mcast_addr.sa_in, interface_addr, &src_addr.sa_in, membership);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll take a look!
src/unix/udp.c
Outdated
struct sockaddr_in6 mcast_addr6; | ||
struct sockaddr_in6 src_addr6; | ||
|
||
int err = uv_ip4_addr(multicast_addr, 0, &mcast_addr4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: we separate variable declaration from assignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/win/udp.c
Outdated
struct sockaddr_in mcast_addr4; | ||
struct sockaddr_in src_addr4; | ||
struct sockaddr_in6 mcast_addr6; | ||
struct sockaddr_in6 src_addr6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments as on unix
.
src/unix/udp.c
Outdated
err = uv_ip4_addr(source_addr, 0, &src_addr4); | ||
if (err) | ||
return err; | ||
return uv__udp_set_source_membership4(handle, &mcast_addr4, interface_addr, &src_addr4, membership); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: long lines. Max should be 80 characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/win/udp.c
Outdated
err = uv_ip4_addr(source_addr, 0, &src_addr4); | ||
if (err) | ||
return err; | ||
return uv__udp_set_source_membership4(handle, &mcast_addr4, interface_addr, &src_addr4, membership); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: long lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Are you planning on adding tests? |
Sure, I plan to write tests for join/leave SSM group. Unfortunately, it not possible to write tests that receive/send a multicast packet (just like a "usual" multicast) because they require a "real" multicast network. |
Ping @falanger |
I see there was a push to the branch after my last comment but I am not sure what the status of this is @falanger |
Ping @falanger |
@santigimeno would you be willing to add some tests for this? It would really be great to unblock the Node.js PR by getting this ready and to land it. |
A pragmatic decision would be to just have a simple test that shows data flow between sender and receiver in a similar vein to the existing any source multicast. Full testing of source-specific multicast needs an environment more complex than what the framework provides (e.g. multiple network subnets and a router). It appears this ticket is stalled on that issue and there is lack of input from the project on what is an appropriate solution. |
What's the status of this lack of SSM support in node.js is turning into a major pain for a lot of projects. |
Superseded by #2202. |
Add RFC 4607 support for IPv4 and IPv6.