Permalink
Browse files

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>
  • Loading branch information...
1 parent 017e2d5 commit d14d741bca158fc62b9968a471100006cf877794 @snoj committed Feb 4, 2013
Showing with 314 additions and 6 deletions.
  1. +1 −0 include/uv.h
  2. +2 −1 src/unix/internal.h
  3. +3 −0 src/unix/linux/linux-core.c
  4. +80 −2 src/unix/udp.c
  5. +81 −3 src/win/udp.c
  6. +4 −0 src/win/util.c
  7. +2 −0 test/test-list.h
  8. +140 −0 test/test-udp-multicast-join6.c
  9. +1 −0 uv.gyp
View
@@ -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;
View
@@ -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 */
@@ -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) {
@@ -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 {
View
@@ -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) {
@@ -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,
@@ -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) {
@@ -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)
View
@@ -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);
@@ -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;
View
@@ -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);
View
@@ -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)
@@ -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)
Oops, something went wrong.

0 comments on commit d14d741

Please sign in to comment.