Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/uv.h
Expand Up @@ -951,6 +951,23 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr, const char* interface_addr, const char* multicast_addr, const char* interface_addr,
uv_membership membership); 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 * Set IP multicast loop flag. Makes multicast packets loop back to
* local sockets. * local sockets.
Expand Down Expand Up @@ -1599,6 +1616,7 @@ struct uv_interface_address_s {
char* name; char* name;
char phys_addr[6]; char phys_addr[6];
int is_internal; int is_internal;
uint32_t if_index;
union { union {
struct sockaddr_in address4; struct sockaddr_in address4;
struct sockaddr_in6 address6; struct sockaddr_in6 address6;
Expand Down
3 changes: 2 additions & 1 deletion src/unix/internal.h
Expand Up @@ -125,7 +125,8 @@ enum {
UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */ UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */
UV_TCP_NODELAY = 0x400, /* Disable Nagle. */ UV_TCP_NODELAY = 0x400, /* Disable Nagle. */
UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */ 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 { typedef enum {
Expand Down
2 changes: 2 additions & 0 deletions src/unix/linux-core.c
Expand Up @@ -751,6 +751,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses,


address->name = strdup(ent->ifa_name); address->name = strdup(ent->ifa_name);


address->if_index = if_nametoindex(address->name);

if (ent->ifa_addr->sa_family == AF_INET6) { if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else { } else {
Expand Down
76 changes: 76 additions & 0 deletions src/unix/udp.c
Expand Up @@ -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_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 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_maybe_deferred_bind(uv_udp_t* handle, int domain);
static int uv__udp_set_membership6(uv_udp_t* handle,
Copy link
Contributor

Choose a reason for hiding this comment

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

Whitespace at the end of the line

const char* multicast_addr,
const char* 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.

Ditto

uv_membership membership);




void uv__udp_close(uv_udp_t* handle) { void uv__udp_close(uv_udp_t* handle) {
Expand Down Expand Up @@ -455,6 +459,11 @@ int uv_udp_set_membership(uv_udp_t* handle,
uv_membership membership) { uv_membership membership) {
struct ip_mreq mreq; struct ip_mreq mreq;
int optname; 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); memset(&mreq, 0, sizeof mreq);


Expand Down Expand Up @@ -488,6 +497,73 @@ int uv_udp_set_membership(uv_udp_t* handle,
return 0; 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;
Copy link
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Just return -errno

/*return uv__set_sys_error(handle->loop, UV_EINVAL);*/
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a bad idea :)


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

Choose a reason for hiding this comment

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

Ditto

}
}

if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

And no need in braces if there will be one line

Copy link
Contributor

Choose a reason for hiding this comment

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

Also in all other places in diff.

/*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

}

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

Choose a reason for hiding this comment

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

Ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably should be an assertion, or UV_ENOTSUP

}

if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, &mreq, sizeof mreq) != 0) {
/*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Dito

}

return 0;
}



static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) { static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) {
#if defined(__sun) #if defined(__sun)
Expand Down
87 changes: 84 additions & 3 deletions src/win/udp.c
Expand Up @@ -539,7 +539,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
struct ip_mreq mreq; struct ip_mreq mreq;


/* If the socket is unbound, bind to inaddr_any. */ /* 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)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

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

err = uv_udp_try_bind(handle, err = uv_udp_try_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_, (const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_), sizeof(uv_addr_ip4_any_),
Expand All @@ -549,7 +549,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
} }


if (handle->flags & UV_HANDLE_IPV6) { 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); memset(&mreq, 0, sizeof mreq);
Expand All @@ -570,7 +570,7 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
optname = IP_DROP_MEMBERSHIP; optname = IP_DROP_MEMBERSHIP;
break; break;
default: default:
return UV_EINVAL; return UV__EINVAL;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why?

} }


if (setsockopt(handle->socket, if (setsockopt(handle->socket,
Expand All @@ -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;

Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

if (!(handle->flags & UV_HANDLE_IPV6)) {
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

}

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

Choose a reason for hiding this comment

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

Should return some meaningful error here

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

Choose a reason for hiding this comment

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

And there

}

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) { int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
BOOL optval = (BOOL) value; BOOL optval = (BOOL) value;
int err; int err;
Expand Down
4 changes: 4 additions & 0 deletions src/win/util.c
Expand Up @@ -920,6 +920,10 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
(int) max_name_size, (int) max_name_size,
NULL, NULL,
FALSE); FALSE);

uv_address->if_index = 0;
uv_address->if_index = (win_address->IfIndex) ? win_address->IfIndex : win_address->Ipv6IfIndex;

if (name_size <= 0) { if (name_size <= 0) {
free(win_address_buf); free(win_address_buf);
free(uv_address_buf); free(uv_address_buf);
Expand Down
2 changes: 2 additions & 0 deletions test/test-list.h
Expand Up @@ -80,6 +80,7 @@ TEST_DECLARE (tcp_bind6_error_inval)
TEST_DECLARE (tcp_bind6_localhost_ok) TEST_DECLARE (tcp_bind6_localhost_ok)
TEST_DECLARE (udp_send_and_recv) TEST_DECLARE (udp_send_and_recv)
TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join)
TEST_DECLARE (udp_multicast_join6)
TEST_DECLARE (udp_multicast_ttl) TEST_DECLARE (udp_multicast_ttl)
TEST_DECLARE (udp_dgram_too_big) TEST_DECLARE (udp_dgram_too_big)
TEST_DECLARE (udp_dual_stack) TEST_DECLARE (udp_dual_stack)
Expand Down Expand Up @@ -339,6 +340,7 @@ TASK_LIST_START
TEST_ENTRY (udp_ipv6_only) TEST_ENTRY (udp_ipv6_only)
TEST_ENTRY (udp_options) TEST_ENTRY (udp_options)
TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_join)
TEST_ENTRY (udp_multicast_join6)
TEST_ENTRY (udp_multicast_ttl) TEST_ENTRY (udp_multicast_ttl)


TEST_ENTRY (udp_open) TEST_ENTRY (udp_open)
Expand Down