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: add source-specific multicast support #964

Closed
wants to merge 1 commit into from
Closed

udp: add source-specific multicast support #964

wants to merge 1 commit into from

Conversation

falanger
Copy link
Contributor

@falanger falanger commented Aug 1, 2016

Add RFC 4607 support for IPv4 and IPv6.

@falanger falanger changed the title Source-specific multicast support udp4: add source-specific multicast support Aug 1, 2016
.gitignore Outdated
@@ -74,5 +74,7 @@ ipch
*.xcodeproj
*.xcworkspace

CMakeLists.txt
Copy link
Member

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

@saghul
Copy link
Member

saghul commented Aug 2, 2016

Thanks for contributing @falanger! Mostly LGTM, left some comments.

@falanger
Copy link
Contributor Author

falanger commented Aug 7, 2016

Hi, @saghul! Thanks for your time! I've fixed all the comments.

@saghul
Copy link
Member

saghul commented Aug 8, 2016

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;
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

@falanger falanger changed the title udp4: add source-specific multicast support udp: add source-specific multicast support Sep 5, 2016
@falanger
Copy link
Contributor Author

falanger commented Sep 5, 2016

@saghul sorry, I was wrong about IPv6 support (thanks, @santigimeno).

@falanger
Copy link
Contributor Author

@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);
Copy link
Member

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?

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@cjihrig cjihrig added the stalled label Sep 5, 2017
@LPardue
Copy link

LPardue commented Sep 18, 2017

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@cjihrig
Copy link
Contributor

cjihrig commented Sep 18, 2017

@falanger it's been quite a while. Sorry about that. Are you still interested in pursuing this PR?

@falanger
Copy link
Contributor Author

@cjihrig Hi! Yes, I am interested in. I'll try to fix issues on this week.

@@ -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);
Copy link
Contributor

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.

@@ -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);
Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two space indentation please.

Copy link
Contributor Author

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;
Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UV_EPROTONOSUPPORT

Copy link
Contributor Author

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;
Copy link
Contributor

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));
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

@falanger falanger Oct 20, 2017

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.

Copy link
Contributor

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

Copy link
Contributor Author

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;
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

@falanger falanger Oct 20, 2017

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?

Copy link
Contributor Author

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link

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

Copy link
Contributor Author

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;
Copy link

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?

Copy link
Contributor Author

@falanger falanger Oct 20, 2017

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

Copy link
Contributor Author

@falanger falanger Oct 20, 2017

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.

Copy link

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
Copy link
Contributor

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

Copy link
Contributor Author

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) {
Copy link
Contributor

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) {
Copy link
Contributor

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 ifs by checking for == and != to 0, NULL, etc.

Copy link
Contributor Author

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;
Copy link
Contributor

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.

@BridgeAR
Copy link

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 :-)

@BridgeAR
Copy link

Ping @cjihrig @saghul @bnoordhuis

@cjihrig
Copy link
Contributor

cjihrig commented Jan 19, 2018

Unnecessary ping. It looks like there are a few unresolved review comments.

@BridgeAR
Copy link

@cjihrig you are right. Sorry, I did not check the comments again. I looked at to many PRs today.

@falanger can you please have a look at the comments?

@falanger
Copy link
Contributor Author

falanger commented Feb 6, 2018

@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
Copy link

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Member

@santigimeno santigimeno left a 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;
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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);
}

Copy link
Contributor Author

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);
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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);
Copy link
Member

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.

Copy link
Contributor Author

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: long lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@santigimeno
Copy link
Member

Are you planning on adding tests?

@falanger
Copy link
Contributor Author

falanger commented Feb 7, 2018

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.

@BridgeAR
Copy link

BridgeAR commented Mar 2, 2018

Ping @falanger

@BridgeAR
Copy link

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

@BridgeAR
Copy link

Ping @falanger

@BridgeAR
Copy link

@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.

@LPardue
Copy link

LPardue commented Jun 1, 2018

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.

@OlofBailey
Copy link

What's the status of this lack of SSM support in node.js is turning into a major pain for a lot of projects.

@santigimeno
Copy link
Member

Superseded by #2202.

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

Successfully merging this pull request may close these issues.

None yet

7 participants