Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

unix, win: extend uv_udp_set_membership for IPv6 multicast. #692

Closed
wants to merge 1 commit into from

7 participants

Josh Erickson Andrius Bentkus Ilya Shaisultanov Saúl Ibarra Corretgé Michał Prokopowicz Fedor Indutny Ben Noordhuis
Josh Erickson

Added uv_udp_set_membership6.
Modified uv_udp_set_membership to call uv_udp_set_membership6
when IPv6 is used.

Google groups discussion...not much there mainly easy access for myself. https://groups.google.com/forum/?fromgroups=#!topic/libuv/TxZmEnH2wP4

Josh Erickson

Ahhh shoot. Previous comments you guys put in are gone.

Anyway, made the changes that were recommended and refined the multicast binding. Now it doesn't send out on all interfaces if one is given. In order to do this though, I had to get the interface index which I did by extending uv_interface_address_s.

I'll start working on the Linux versions if there is nothing more.

Andrius Bentkus

If you come to the irc channel right now, I will aid you in this task.

src/win/udp.c
((12 lines not shown))
+ int interfaces_count;
+
+ if (!(handle->flags & UV_HANDLE_IPV6)) {
+ return uv__set_artificial_error(handle->loop, UV_EINVAL);
+ }
+
+ if (!(handle->flags & UV_HANDLE_BOUND) &&
+ uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) {
+ return -1;
+ }
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if(interface_addr) {

Style: space between keyword and paren.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/win/udp.c
((16 lines not shown))
+ }
+
+ if (!(handle->flags & UV_HANDLE_BOUND) &&
+ uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) {
+ return -1;
+ }
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if(interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {
+ uv__set_sys_error(handle->loop, WSAGetLastError());
+ return -1;

Fold into previous line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/win/udp.c
((21 lines not shown))
+ }
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if(interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {
+ uv__set_sys_error(handle->loop, WSAGetLastError());
+ return -1;
+ }
+
+ while(interfaces_count--) {
+ if(interfaces[interfaces_count].address.address4.sin_family == AF_INET6) {
+ if(memcmp(&interfaces[interfaces_count].address.address6.sin6_addr, &interface_addr_n, sizeof interface_addr_n) == 0) {

Long line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/win/udp.c
((14 lines not shown))
+ if (!(handle->flags & UV_HANDLE_IPV6)) {
+ return uv__set_artificial_error(handle->loop, UV_EINVAL);
+ }
+
+ if (!(handle->flags & UV_HANDLE_BOUND) &&
+ uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) {
+ return -1;
+ }
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if(interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {

Leaking interfaces?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/win/util.c
@@ -839,6 +839,9 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr,
win_address->FirstUnicastAddress == NULL)
continue;
+ /* Add four bytes for the interface index */
+ uv_address_buf_size += 4;

Use sizeof(fieldname) here.

By the way, is it necessary? It gets incremented with sizeof(uv_interface_address_t) further down below, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/win/util.c
@@ -901,6 +904,10 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr,
(int) max_name_size,
NULL,
FALSE);
+
+ uv_address->ifIndex = 0;
+ uv_address->ifIndex = (win_address->IfIndex) ? win_address->IfIndex : win_address->Ipv6IfIndex;

Superfluous assignment, superfluous parens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
include/uv.h
@@ -1433,6 +1450,7 @@ struct uv_cpu_info_s {
struct uv_interface_address_s {
char* name;
int is_internal;
+ ULONG ifIndex;

Use snake_case, not camelCase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Josh Erickson

Super special thanks to @txdv for fixing the bad code style!

src/unix/internal.h
@@ -244,3 +244,6 @@ static void uv__update_time(uv_loop_t* loop) {
}
#endif /* UV_UNIX_INTERNAL_H_ */
+
+/* */
+#define UV_HANDLE_IPV6 0x01000000

What's that doing there?

Josh Erickson
snoj added a note

A copy of UV_HANDLE_IPV6 from src/win/internal.h. It was otherwise not available on my linux build.

Okay, but look closely where you put it in src/unix/internal.h.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
@@ -524,6 +525,11 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
int optname;
struct ip_mreq mreq;
+
+ if (handle->flags & UV_HANDLE_IPV6) {
+ return uv_udp_set_membership6(handle, multicast_addr, interface_addr, membership);

If we're doing an implicit call then uv_udp_set_membership6 doesn't have to be public (and should be renamed to uv__udp_set_membership6 to indicate it's internal.)

EDIT: it should probably be static as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
@@ -554,6 +560,69 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
return 0;
}
+int uv_udp_set_membership6(uv_udp_t* handle, const char* multicast_addr,
+ const char* interface_addr, uv_membership membership) {

Arguments should line up, i.e.:

int uv_udp_set_membership6(uv_udp_t* handle,
                           const char* multicast_addr,
                           const char* interface_addr,
                           uv_membership membership
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
@@ -554,6 +560,69 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
return 0;
}
+int uv_udp_set_membership6(uv_udp_t* handle, const char* multicast_addr,
+ const char* interface_addr, uv_membership membership) {
+
+ int optname;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+

Style: No newline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
((8 lines not shown))
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+
+ uv_interface_address_t* interfaces;
+ int interfaces_count;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {
+ uv__set_sys_error(handle->loop, UV_EINVAL);
+ return -1;

Style: fold into a single-line statement (return uv__set_sys_error(handle->loop, UV_EINVAL);) and drop the braces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
((11 lines not shown))
+
+ uv_interface_address_t* interfaces;
+ int interfaces_count;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {
+ uv__set_sys_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
+ while (interfaces_count--) {

Micro-optimization and micro-style: use a for (i = 0; i < interfaces_count; i++) loop here.

Micro-optimization: CPU prefetchers are good at recognizing memory access patterns in ascending order but generally not when they're in descending order.

Micro-style: Prefer not mutating variables. It's a kind of future-proofing; if someone changes the code later on and assumes interfaces_count is factual, things will go badly wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
((13 lines not shown))
+ int interfaces_count;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count).code != UV_OK) {
+ uv__set_sys_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
+ while (interfaces_count--) {
+ if (interfaces[interfaces_count].address.address4.sin_family == AF_INET6) {
+ if (memcmp(&interfaces[interfaces_count].address.address6.sin6_addr, &interface_addr_n, sizeof interface_addr_n) == 0) {

Style: Long line. Wrap at 80 characters max. I believe I pointed that out last night too. :-/

Also, using memcmp() here is potentially wrong. The struct may have holes in it that contain garbage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
((4 lines not shown))
+int uv_udp_set_membership6(uv_udp_t* handle, const char* multicast_addr,
+ const char* interface_addr, uv_membership membership) {
+
+ int optname;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+
+ uv_interface_address_t* interfaces;
+ int interfaces_count;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr) {

Style: interface_addr != NULL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/unix/udp.c
((44 lines not shown))
+
+ mreq.ipv6mr_multiaddr = multicast_addr_n;
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ uv__set_sys_error(handle->loop, UV_EINVAL);
+ return -1;
+ }
+
+ if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, (void*) &mreq, sizeof mreq) == -1) {

Superfluous cast to void*.

Andrius Bentkus
txdv added a note
  • space
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
test/test-udp-multicast-join6.c
((26 lines not shown))
+#include <stdlib.h>
+#include <string.h>
+
+#define CHECK_HANDLE(handle) \
+ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client)
+
+static uv_udp_t server;
+static uv_udp_t client;
+
+static int cl_recv_cb_called;
+
+static int sv_send_cb_called;
+
+static int close_cb_called;
+
+static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) {

Style: Two newlines between declarations and functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Josh Erickson

I believe the new forced commit takes care of most of the comments. I couldn't figure out a way compare ip addresses without more variables and conversions, perhaps I'll need to resort to that that.

Ilya Shaisultanov

I've tried to use multicast with IPv6 address but I keep getting addMembership EINVAL. Does this PR address that?

Ilya Shaisultanov diversario referenced this pull request in diversario/node-disco
Closed

Consider using IPv6 #10

Josh Erickson

@diversario, When I started this, node/libuv did not support ipv6 multicast. I don't recall that EINVAL (see google group link at the top), but they may have changed the error codes since.

Since you're trying to use ipv6 multicasting, I think something like this would work for you. This code probably several version behind by now though. I or someone would need to review it.

Saúl Ibarra Corretgé
Owner

Sorry this was left hanging. Can yoou please rebase it against current master?

src/unix/linux/linux-core.c
@@ -717,6 +717,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
#else
struct ifaddrs *addrs, *ent;
char ip[INET6_ADDRSTRLEN];
+
Fedor Indutny Owner
indutny added a note

Unnecessary whitespace change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Josh Erickson

@saghul I'll attempt to rebase and run tests by sometime this coming weekend. But with it being christmas and with all the family get togethers, I can't guarantee anything.

Saúl Ibarra Corretgé
Owner
Josh Erickson

Definitely not happening this weekend. To many changes have happened since writing this.

Saúl Ibarra Corretgé
Owner
Michał Prokopowicz

Any progress on this? Can't wait for got this in node release.
It's critical for my project :)

Josh Erickson

Been working the last couple/few days with @mprokopowicz on this. Appears to be working, but when I rebased on the latest master, node broke. Tonight I'm hoping to rebase on the version node is using and going from there.

Josh Erickson snoj unix, windows: extend uv_udp_set_membership for IPv6 multicast.
Add uv__udp_set_membership6.
Modify uv_udp_set_membership to call uv__udp_set_membership6
  when IPv6 is used.
Add if_index to struct uv_interface_address_s for IPv6 multicasting.
Add UV_HANDLE_IPV6 to unix/interna.h

Signed-off-by: snoj <josh@snoj.us>
3c711be
Josh Erickson

Done? Code compiles and ipv6 multicast tests completes without errors.

!ping @saghul ?

Fedor Indutny indutny commented on the diff
src/unix/udp.c
@@ -35,6 +35,10 @@
static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_sendmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain);
+static int uv__udp_set_membership6(uv_udp_t* handle,
Fedor Indutny Owner
indutny added a note

Whitespace at the end of the line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
@@ -35,6 +35,10 @@
static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_sendmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain);
+static int uv__udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr,
+ const char* interface_addr,
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
@@ -585,6 +585,87 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
}
+int uv_udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr,
+ const char* interface_addr,
+ uv_membership membership) {
+ int optname;
+ int interfaces_count;
+ int i;
+ int err;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny
Owner

Got this on OSX:

../../src/unix/udp.c:549:15: error: use of undeclared identifier 'IPV6_ADD_MEMBERSHIP'
    optname = IPV6_ADD_MEMBERSHIP;
              ^
../../src/unix/udp.c:552:15: error: use of undeclared identifier 'IPV6_DROP_MEMBERSHIP'
    optname = IPV6_DROP_MEMBERSHIP;
              ^
Fedor Indutny
Owner

Perhaps it is called IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP on osx?

Fedor Indutny
Owner

I'd suggest adding UV__IPV6_JOIN_GROUP and UV__IPV6_LEAVE_GROUP in src/unix/internal.h and setting them to the appropriate value

Fedor Indutny indutny commented on the diff
test/test-udp-multicast-join6.c
((92 lines not shown))
+ ASSERT(!memcmp("PING", buf->base, nread));
+
+ /* we are done with the client handle, we can close it */
+ uv_close((uv_handle_t*) &client, close_cb);
+}
+
+
+TEST_IMPL(udp_multicast_join6) {
+ int r;
+ uv_udp_send_t req;
+ uv_buf_t buf;
+ struct sockaddr_in6 addr;
+
+ ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr));
+
+ r = uv_udp_init(uv_default_loop(), &server);
Fedor Indutny Owner
indutny added a note

Please do ASSERT(0 == ...) everywhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((10 lines not shown))
+ int interfaces_count;
+ int i;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr != NULL) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count) != 0)
+ return -1;
+ /*return uv__set_sys_error(handle->loop, UV_EINVAL);*/
Fedor Indutny Owner
indutny added a note

This is a bad idea :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((9 lines not shown))
+ int optname;
+ int interfaces_count;
+ int i;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr != NULL) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count) != 0)
+ return -1;
Fedor Indutny Owner
indutny added a note

Just return -errno

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((25 lines not shown))
+ /*return uv__set_sys_error(handle->loop, UV_EINVAL);*/
+
+ for (i = 0; i < interfaces_count; i++) {
+ if (interfaces[i].address.address6.sin6_family == AF_INET6) {
+ if (memcmp(&interfaces[i].address.address6.sin6_addr,
+ &interface_addr_n,
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((31 lines not shown))
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
+ }
+ }
+
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((29 lines not shown))
+ if (memcmp(&interfaces[i].address.address6.sin6_addr,
+ &interface_addr_n,
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
+ }
+ }
+
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
Fedor Indutny Owner
indutny added a note

And no need in braces if there will be one line

Fedor Indutny Owner
indutny added a note

Also in all other places in diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((45 lines not shown))
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
+ }
+
+ mreq.ipv6mr_multiaddr = multicast_addr_n;
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Fedor Indutny Owner
indutny added a note

Probably should be an assertion, or UV_ENOTSUP

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/unix/udp.c
((50 lines not shown))
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
+ }
+
+ if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, &mreq, sizeof mreq) != 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Dito

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
@@ -539,7 +539,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
struct ip_mreq mreq;
/* If the socket is unbound, bind to inaddr_any. */
- if (!(handle->flags & UV_HANDLE_BOUND)) {
+ if (!(handle->flags & UV_HANDLE_IPV6) && !(handle->flags & UV_HANDLE_BOUND)) {
Fedor Indutny Owner
indutny added a note

!(handle->flags & (UV_HANDLE_IPV6 | UV_HANDLE_BOUND))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
@@ -570,7 +570,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
optname = IP_DROP_MEMBERSHIP;
break;
default:
- return UV_EINVAL;
+ return UV__EINVAL;
Fedor Indutny Owner
indutny added a note

Why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
@@ -585,6 +585,87 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
}
+int uv_udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr,
+ const char* interface_addr,
+ uv_membership membership) {
+ int optname;
+ int interfaces_count;
+ int i;
+ int err;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
+ if (!(handle->flags & UV_HANDLE_IPV6)) {
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
((39 lines not shown))
+ return uv_translate_sys_error(WSAGetLastError());
+ }
+
+ for (i = 0; i < interfaces_count; i++) {
+ if (interfaces[i].address.address6.sin6_family == AF_INET6) {
+ if (memcmp(&interfaces[i].address.address6.sin6_addr,
+ &interface_addr_n,
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ return -1;
Fedor Indutny Owner
indutny added a note

Should return some meaningful error here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny indutny commented on the diff
src/win/udp.c
((56 lines not shown))
+ }
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
+ return uv_translate_sys_error(WSAGetLastError());
+ }
+
+ mreq.ipv6mr_multiaddr = multicast_addr_n;
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ return -1;
Fedor Indutny Owner
indutny added a note

And there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fedor Indutny
Owner

As another note, I do get EADDRNOTAVAIL on OSX and test is failing.

Fedor Indutny
Owner

Please fix all that things and I will finish reviewing

Fedor Indutny
Owner

And generally it looks quite neat ;)

Saúl Ibarra Corretgé
Owner

The UV_HANDLE_IPV6 flag is only used for UDP, maybe you could rename it to something like UV_UDP_IPV6?

Saúl Ibarra Corretgé
Owner

I'd name the extra field you added to uv_interface_address_s just "index". Also, AFAIS it's only set on Linux and Windows. Don't other platforms need it as well?

Saúl Ibarra Corretgé
Owner

Instead of branching out to a ipv6 version of the function and continuing on the same otherwise, I'd split uv_udp_set_membership into 2: uv__udp_set_membership4/6.

PS: sorry for the brevity, I'm on mobile.

Michał Prokopowicz

Hi,

i was mailing with snoj and unfortunately he won't have time to finish this commit. Is any one here who can handle it?

Saúl Ibarra Corretgé
Owner

I'll take over.

Michał Prokopowicz

@saghul Awsome news :) Can U provide any estimates? (No pressure, just curious... )

Saúl Ibarra Corretgé
Owner
Saúl Ibarra Corretgé
Owner

For those interested, I reworked the Unix part here: https://github.com/saghul/libuv/tree/set-membership-v6

I'll send a proper PR for review once I'm done with the Windows part.

Saúl Ibarra Corretgé
Owner

Superseded by #1202. Lets continue the discussion there.

Saúl Ibarra Corretgé saghul closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 25, 2014
  1. Josh Erickson

    unix, windows: extend uv_udp_set_membership for IPv6 multicast.

    snoj authored
    Add uv__udp_set_membership6.
    Modify uv_udp_set_membership to call uv__udp_set_membership6
      when IPv6 is used.
    Add if_index to struct uv_interface_address_s for IPv6 multicasting.
    Add UV_HANDLE_IPV6 to unix/interna.h
    
    Signed-off-by: snoj <josh@snoj.us>
This page is out of date. Refresh to see the latest.
18 include/uv.h
View
@@ -952,6 +952,23 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,
uv_membership membership);
/*
+* Set membership for a multicast IPV6 address
+*
+* Arguments:
+* handle UDP handle. Should have been initialized with
+* `uv_udp_init`.
+* multicast_addr multicast address to set membership for
+* interface_addr interface address
+* membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
+*
+* Returns:
+* 0 on success, or an error code < 0 on failure.
+*/
+UV_EXTERN int uv_udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr, const char* interface_addr,
+ uv_membership membership);
+
+/*
* Set IP multicast loop flag. Makes multicast packets loop back to
* local sockets.
*
@@ -1599,6 +1616,7 @@ struct uv_interface_address_s {
char* name;
char phys_addr[6];
int is_internal;
+ uint32_t if_index;
union {
struct sockaddr_in address4;
struct sockaddr_in6 address6;
3  src/unix/internal.h
View
@@ -125,7 +125,8 @@ enum {
UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */
UV_TCP_NODELAY = 0x400, /* Disable Nagle. */
UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */
- UV_TCP_SINGLE_ACCEPT = 0x1000 /* Only accept() when idle. */
+ UV_TCP_SINGLE_ACCEPT = 0x1000, /* Only accept() when idle. */
+ UV_HANDLE_IPV6 = 0x01000000 /* Indicate socket is IPv6. */
};
typedef enum {
2  src/unix/linux-core.c
View
@@ -751,6 +751,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses,
address->name = strdup(ent->ifa_name);
+ address->if_index = if_nametoindex(address->name);
+
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else {
76 src/unix/udp.c
View
@@ -35,6 +35,10 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static void uv__udp_sendmsg(uv_loop_t* loop, uv__io_t* w, unsigned int revents);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain);
+static int uv__udp_set_membership6(uv_udp_t* handle,
Fedor Indutny Owner
indutny added a note

Whitespace at the end of the line

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ const char* multicast_addr,
+ const char* interface_addr,
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ uv_membership membership);
void uv__udp_close(uv_udp_t* handle) {
@@ -455,6 +459,11 @@ int uv_udp_set_membership(uv_udp_t* handle,
uv_membership membership) {
struct ip_mreq mreq;
int optname;
+ struct sockaddr_in6 addr6;
+
+ if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) {
+ return uv__udp_set_membership6(handle, multicast_addr, interface_addr, membership);
+ }
memset(&mreq, 0, sizeof mreq);
@@ -488,6 +497,73 @@ int uv_udp_set_membership(uv_udp_t* handle,
return 0;
}
+static int uv__udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr,
+ const char* interface_addr,
+ uv_membership membership) {
+
+ int optname;
+ int interfaces_count;
+ int i;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
+ memset(&mreq, 0, sizeof mreq);
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr != NULL) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count) != 0)
+ return -1;
Fedor Indutny Owner
indutny added a note

Just return -errno

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ /*return uv__set_sys_error(handle->loop, UV_EINVAL);*/
Fedor Indutny Owner
indutny added a note

This is a bad idea :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ for (i = 0; i < interfaces_count; i++) {
+ if (interfaces[i].address.address6.sin6_family == AF_INET6) {
+ if (memcmp(&interfaces[i].address.address6.sin6_addr,
+ &interface_addr_n,
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+ }
+
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
Fedor Indutny Owner
indutny added a note

And no need in braces if there will be one line

Fedor Indutny Owner
indutny added a note

Also in all other places in diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ mreq.ipv6mr_multiaddr = multicast_addr_n;
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Fedor Indutny Owner
indutny added a note

Probably should be an assertion, or UV_ENOTSUP

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, &mreq, sizeof mreq) != 0) {
+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
Fedor Indutny Owner
indutny added a note

Dito

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ return 0;
+}
+
static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) {
#if defined(__sun)
87 src/win/udp.c
View
@@ -539,7 +539,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
struct ip_mreq mreq;
/* If the socket is unbound, bind to inaddr_any. */
- if (!(handle->flags & UV_HANDLE_BOUND)) {
+ if (!(handle->flags & UV_HANDLE_IPV6) && !(handle->flags & UV_HANDLE_BOUND)) {
Fedor Indutny Owner
indutny added a note

!(handle->flags & (UV_HANDLE_IPV6 | UV_HANDLE_BOUND))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
err = uv_udp_try_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
@@ -549,7 +549,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
}
if (handle->flags & UV_HANDLE_IPV6) {
- return UV_ENOSYS;
+ return uv_udp_set_membership6(handle, multicast_addr, interface_addr, membership);
}
memset(&mreq, 0, sizeof mreq);
@@ -570,7 +570,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
optname = IP_DROP_MEMBERSHIP;
break;
default:
- return UV_EINVAL;
+ return UV__EINVAL;
Fedor Indutny Owner
indutny added a note

Why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
}
if (setsockopt(handle->socket,
@@ -585,6 +585,87 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
}
+int uv_udp_set_membership6(uv_udp_t* handle,
+ const char* multicast_addr,
+ const char* interface_addr,
+ uv_membership membership) {
+ int optname;
+ int interfaces_count;
+ int i;
+ int err;
+ struct ipv6_mreq mreq;
+ struct in6_addr multicast_addr_n;
+ struct in6_addr interface_addr_n;
+ uv_interface_address_t* interfaces;
+
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if (!(handle->flags & UV_HANDLE_IPV6)) {
+ return -1;
Fedor Indutny Owner
indutny added a note

Ditto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ if (!(handle->flags & UV_HANDLE_BOUND)) {
+ err = uv_udp_try_bind(handle,
+ (const struct sockaddr*) &uv_addr_ip6_any_,
+ sizeof(uv_addr_ip6_any_),
+ 0);
+
+ if (err) {
+ return uv_translate_sys_error(err);
+ }
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ memset(&multicast_addr_n, 0, sizeof multicast_addr_n);
+ memset(&interface_addr_n, 0, sizeof interface_addr_n);
+
+ if (interface_addr != NULL) {
+ uv_inet_pton(AF_INET6, interface_addr, &interface_addr_n);
+ if (uv_interface_addresses(&interfaces, &interfaces_count) != 0) {
+ return uv_translate_sys_error(WSAGetLastError());
+ }
+
+ for (i = 0; i < interfaces_count; i++) {
+ if (interfaces[i].address.address6.sin6_family == AF_INET6) {
+ if (memcmp(&interfaces[i].address.address6.sin6_addr,
+ &interface_addr_n,
+ sizeof interface_addr_n) == 0) {
+ mreq.ipv6mr_interface = interfaces[i].if_index;
+ break;
+ }
+ }
+ }
+
+ if (mreq.ipv6mr_interface == 0) {
+ return -1;
Fedor Indutny Owner
indutny added a note

Should return some meaningful error here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+ }
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
+ return uv_translate_sys_error(WSAGetLastError());
+ }
+
+ mreq.ipv6mr_multiaddr = multicast_addr_n;
+
+ switch (membership) {
+ case UV_JOIN_GROUP:
+ optname = IPV6_ADD_MEMBERSHIP;
+ break;
+ case UV_LEAVE_GROUP:
+ optname = IPV6_DROP_MEMBERSHIP;
+ break;
+ default:
+ return -1;
Fedor Indutny Owner
indutny added a note

And there

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ if (setsockopt(handle->socket,
+ IPPROTO_IPV6,
+ optname,
+ (char*) &mreq,
+ sizeof mreq) == SOCKET_ERROR) {
+ return uv_translate_sys_error(WSAGetLastError());
+ }
+
+ return 0;
+}
+
int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
BOOL optval = (BOOL) value;
int err;
4 src/win/util.c
View
@@ -920,6 +920,10 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
(int) max_name_size,
NULL,
FALSE);
+
+ uv_address->if_index = 0;
+ uv_address->if_index = (win_address->IfIndex) ? win_address->IfIndex : win_address->Ipv6IfIndex;
+
if (name_size <= 0) {
free(win_address_buf);
free(uv_address_buf);
2  test/test-list.h
View
@@ -80,6 +80,7 @@ TEST_DECLARE (tcp_bind6_error_inval)
TEST_DECLARE (tcp_bind6_localhost_ok)
TEST_DECLARE (udp_send_and_recv)
TEST_DECLARE (udp_multicast_join)
+TEST_DECLARE (udp_multicast_join6)
TEST_DECLARE (udp_multicast_ttl)
TEST_DECLARE (udp_dgram_too_big)
TEST_DECLARE (udp_dual_stack)
@@ -339,6 +340,7 @@ TASK_LIST_START
TEST_ENTRY (udp_ipv6_only)
TEST_ENTRY (udp_options)
TEST_ENTRY (udp_multicast_join)
+ TEST_ENTRY (udp_multicast_join6)
TEST_ENTRY (udp_multicast_ttl)
TEST_ENTRY (udp_open)
148 test/test-udp-multicast-join6.c
View
@@ -0,0 +1,148 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "task.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CHECK_HANDLE(handle) \
+ ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client)
+
+static uv_udp_t server;
+static uv_udp_t client;
+
+static int cl_recv_cb_called;
+
+static int sv_send_cb_called;
+
+static int close_cb_called;
+
+static void alloc_cb(uv_handle_t* handle,
+ size_t suggested_size,
+ uv_buf_t* buf) {
+ static char slab[65536];
+ CHECK_HANDLE(handle);
+ ASSERT(suggested_size <= sizeof(slab));
+ buf->base = slab;
+ buf->len = sizeof(slab);
+}
+
+
+static void close_cb(uv_handle_t* handle) {
+ CHECK_HANDLE(handle);
+ close_cb_called++;
+}
+
+
+static void sv_send_cb(uv_udp_send_t* req, int status) {
+ ASSERT(req != NULL);
+ ASSERT(status == 0);
+ CHECK_HANDLE(req->handle);
+
+ sv_send_cb_called++;
+
+ uv_close((uv_handle_t*) req->handle, close_cb);
+}
+
+
+static void cl_recv_cb(uv_udp_t* handle,
+ ssize_t nread,
+ const uv_buf_t* buf,
+ const struct sockaddr* addr,
+ unsigned flags) {
+ CHECK_HANDLE(handle);
+ ASSERT(flags == 0);
+
+ cl_recv_cb_called++;
+
+ if (nread < 0) {
+ ASSERT(0 && "unexpected error");
+ }
+
+ if (nread == 0) {
+ /* Returning unused buffer */
+ /* Don't count towards cl_recv_cb_called */
+ ASSERT(addr == NULL);
+ return;
+ }
+
+ ASSERT(addr != NULL);
+ ASSERT(nread == 4);
+ ASSERT(!memcmp("PING", buf->base, nread));
+
+ /* we are done with the client handle, we can close it */
+ uv_close((uv_handle_t*) &client, close_cb);
+}
+
+
+TEST_IMPL(udp_multicast_join6) {
+ int r;
+ uv_udp_send_t req;
+ uv_buf_t buf;
+ struct sockaddr_in6 addr;
+
+ ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr));
+
+ r = uv_udp_init(uv_default_loop(), &server);
Fedor Indutny Owner
indutny added a note

Please do ASSERT(0 == ...) everywhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ ASSERT(r == 0);
+
+ r = uv_udp_init(uv_default_loop(), &client);
+ ASSERT(r == 0);
+
+ /* bind to the desired port */
+ r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0);
+ ASSERT(r == 0);
+
+ /* join the multicast channel */
+ r = uv_udp_set_membership(&client, "FF02::3", NULL, UV_JOIN_GROUP);
+ ASSERT(r == 0);
+
+ r = uv_udp_recv_start(&client, alloc_cb, cl_recv_cb);
+ ASSERT(r == 0);
+
+ buf = uv_buf_init("PING", 4);
+
+ /* server sends "PING" */
+ r = uv_udp_send(&req,
+ &server,
+ &buf,
+ 1,
+ (const struct sockaddr*) &addr,
+ sv_send_cb);
+ ASSERT(r == 0);
+
+ ASSERT(close_cb_called == 0);
+ ASSERT(cl_recv_cb_called == 0);
+ ASSERT(sv_send_cb_called == 0);
+
+ /* run the loop till all events are processed */
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ ASSERT(cl_recv_cb_called == 1);
+ ASSERT(sv_send_cb_called == 1);
+ ASSERT(close_cb_called == 2);
+
+ MAKE_VALGRIND_HAPPY();
+ return 0;
+}
1  uv.gyp
View
@@ -383,6 +383,7 @@
'test/test-udp-options.c',
'test/test-udp-send-and-recv.c',
'test/test-udp-multicast-join.c',
+ 'test/test-udp-multicast-join6.c',
'test/test-dlerror.c',
'test/test-udp-multicast-ttl.c',
'test/test-ip4-addr.c',
Something went wrong with that request. Please try again.