Skip to content

Commit

Permalink
unix, windows: extend uv_udp_set_membership for IPv6 multicast.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
snoj committed Feb 4, 2013
1 parent 017e2d5 commit d14d741
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 6 deletions.
1 change: 1 addition & 0 deletions include/uv.h
Expand Up @@ -1433,6 +1433,7 @@ struct uv_cpu_info_s {
struct uv_interface_address_s {
char* name;
int is_internal;
uint32_t if_index;
union {
struct sockaddr_in address4;
struct sockaddr_in6 address6;
Expand Down
3 changes: 2 additions & 1 deletion src/unix/internal.h
Expand Up @@ -102,7 +102,8 @@ enum {
UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */
UV_TCP_NODELAY = 0x100, /* Disable Nagle. */
UV_TCP_KEEPALIVE = 0x200, /* Turn on keep-alive. */
UV_TCP_SINGLE_ACCEPT = 0x400 /* Only accept() when idle. */
UV_TCP_SINGLE_ACCEPT = 0x400, /* Only accept() when idle. */
UV_HANDLE_IPV6 = 0x01000000 /* Indicate socket is IPv6. */
};

/* core */
Expand Down
3 changes: 3 additions & 0 deletions src/unix/linux/linux-core.c
Expand Up @@ -717,6 +717,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
#else
struct ifaddrs *addrs, *ent;
char ip[INET6_ADDRSTRLEN];

uv_interface_address_t* address;

if (getifaddrs(&addrs) != 0) {
Expand Down Expand Up @@ -764,6 +765,8 @@ uv_err_t 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 {
Expand Down
82 changes: 80 additions & 2 deletions src/unix/udp.c
Expand Up @@ -37,6 +37,10 @@ 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_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[],
int bufcnt, struct sockaddr* addr, socklen_t addrlen, uv_udp_send_cb send_cb);
static int uv__udp_set_membership6(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
uv_membership membership);


void uv__udp_close(uv_udp_t* handle) {
Expand Down Expand Up @@ -466,6 +470,7 @@ int uv__udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned flags) {


int uv__udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) {
handle->flags |= UV_HANDLE_IPV6;
return uv__bind(handle,
AF_INET6,
(struct sockaddr*)&addr,
Expand Down Expand Up @@ -519,11 +524,18 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
}


int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
const char* interface_addr, uv_membership membership) {
int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
uv_membership membership) {

int optname;
struct ip_mreq mreq;

if (handle->flags & UV_HANDLE_IPV6) {
return uv__udp_set_membership6(handle, multicast_addr, interface_addr, membership);
}

memset(&mreq, 0, sizeof mreq);

if (interface_addr) {
Expand Down Expand Up @@ -554,6 +566,72 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
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).code != UV_OK)
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;
}
}

if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n).code != UV_OK) {
uv__set_sys_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_sys_error(handle->loop, UV_EINVAL);
return -1;
}

if (setsockopt(handle->io_watcher.fd, IPPROTO_IPV6, optname, &mreq, sizeof mreq) == -1) {
uv__set_sys_error(handle->loop, errno);
return -1;
}

return 0;
}


static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) {
#if defined(__sun)
Expand Down
84 changes: 81 additions & 3 deletions src/win/udp.c
Expand Up @@ -587,14 +587,13 @@ 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) &&
uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) {
return -1;
}

if (handle->flags & UV_HANDLE_IPV6) {
uv__set_artificial_error(handle->loop, UV_ENOSYS);
return -1;
return uv__udp_set_membership6(handle, multicast_addr, interface_addr, membership);
}

memset(&mreq, 0, sizeof mreq);
Expand Down Expand Up @@ -632,6 +631,85 @@ int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
}


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;

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 != NULL) {
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;
}

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;
}
}
if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n).code != UV_OK) {
uv__set_sys_error(handle->loop, WSAGetLastError());
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;
}

if (setsockopt(handle->socket,
IPPROTO_IPV6,
optname,
(char*) &mreq,
sizeof mreq) == SOCKET_ERROR) {
uv__set_sys_error(handle->loop, WSAGetLastError());
return -1;
}

return 0;
}

int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
BOOL optval = (BOOL) value;

Expand Down
4 changes: 4 additions & 0 deletions src/win/util.c
Expand Up @@ -901,6 +901,10 @@ uv_err_t 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);
Expand Down
2 changes: 2 additions & 0 deletions test/test-list.h
Expand Up @@ -74,6 +74,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)
Expand Down Expand Up @@ -305,6 +306,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)
Expand Down

0 comments on commit d14d741

Please sign in to comment.