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

Closed
wants to merge 1 commit into
from
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;
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 {
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 {
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,
@indutny

indutny Jan 26, 2014

Contributor

Whitespace at the end of the line

+ const char* multicast_addr,
+ const char* interface_addr,
@indutny

indutny Jan 26, 2014

Contributor

Ditto

+ 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;
@indutny

indutny Jan 26, 2014

Contributor

Just return -errno

+ /*return uv__set_sys_error(handle->loop, UV_EINVAL);*/
@indutny

indutny Jan 26, 2014

Contributor

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;
@indutny

indutny Jan 26, 2014

Contributor

Ditto

+ }
+ }
+
+ if (uv_inet_pton(AF_INET6, multicast_addr, &multicast_addr_n) != 0) {
@indutny

indutny Jan 26, 2014

Contributor

And no need in braces if there will be one line

@indutny

indutny Jan 26, 2014

Contributor

Also in all other places in diff.

+ /*uv__set_artificial_error(handle->loop, UV_EINVAL);*/
+ return -1;
@indutny

indutny Jan 26, 2014

Contributor

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;
@indutny

indutny Jan 26, 2014

Contributor

Ditto

@indutny

indutny Jan 26, 2014

Contributor

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;
@indutny

indutny Jan 26, 2014

Contributor

Dito

+ }
+
+ return 0;
+}
+
static int uv__setsockopt_maybe_char(uv_udp_t* handle, int option, int val) {
#if defined(__sun)
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)) {
@indutny

indutny Jan 26, 2014

Contributor

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

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;
@indutny

indutny Jan 26, 2014

Contributor

Why?

}
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;
+
@indutny

indutny Jan 26, 2014

Contributor

Ditto

+ if (!(handle->flags & UV_HANDLE_IPV6)) {
+ return -1;
@indutny

indutny Jan 26, 2014

Contributor

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;
@indutny

indutny Jan 26, 2014

Contributor

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;
@indutny

indutny Jan 26, 2014

Contributor

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) {
BOOL optval = (BOOL) value;
int err;
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);
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)
Oops, something went wrong.