Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udp: add source-specific multicast support #2202

Merged
merged 7 commits into from
Aug 23, 2019
Merged
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS/390")
list(APPEND uv_defines _OPEN_MSGQ_EXT)
list(APPEND uv_defines _OPEN_SYS_FILE_EXT)
list(APPEND uv_defines _OPEN_SYS_IF_EXT)
list(APPEND uv_defines _OPEN_SYS_SOCK_EXT3)
list(APPEND uv_defines _OPEN_SYS_SOCK_IPV6)
list(APPEND uv_defines _UNIX03_SOURCE)
list(APPEND uv_defines _UNIX03_THREADS)
Expand Down
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ libuv_la_CFLAGS += -D_UNIX03_THREADS \
-D_XOPEN_SOURCE_EXTENDED \
-D_ALL_SOURCE \
-D_LARGE_TIME_API \
-D_OPEN_SYS_SOCK_EXT3 \
-D_OPEN_SYS_SOCK_IPV6 \
-D_OPEN_SYS_FILE_EXT \
-DUV_PLATFORM_SEM_T=int \
Expand Down
19 changes: 19 additions & 0 deletions docs/src/udp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,25 @@ API

:returns: 0 on success, or an error code < 0 on failure.

.. c:function:: int uv_udp_set_source_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, const char* source_addr, uv_membership membership)

Set membership for a source-specific multicast group.

:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.

:param multicast_addr: Multicast address to set membership for.

:param interface_addr: Interface address.

:param source_addr: Source address.

:param membership: Should be ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``.

:returns: 0 on success, or an error code < 0 on failure.

.. versionadded:: 1.32.0

.. c:function:: int uv_udp_set_multicast_loop(uv_udp_t* handle, int on)

Set IP multicast loop flag. Makes multicast packets loop back to
Expand Down
5 changes: 5 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
uv_membership membership);
UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
const char* source_addr,
uv_membership membership);
UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on);
UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl);
UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle,
Expand Down
137 changes: 137 additions & 0 deletions src/unix/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,98 @@ static int uv__udp_set_membership6(uv_udp_t* handle,
}


static int uv__udp_set_source_membership4(uv_udp_t* handle,
const struct sockaddr_in* multicast_addr,
const char* interface_addr,
const struct sockaddr_in* source_addr,
uv_membership membership) {
struct ip_mreq_source mreq;
int optname;
int err;

err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR);
if (err)
return err;

memset(&mreq, 0, sizeof(mreq));

if (interface_addr != NULL) {
err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
if (err)
return err;
} else {
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
}

mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;

if (membership == UV_JOIN_GROUP)
optname = IP_ADD_SOURCE_MEMBERSHIP;
else if (membership == UV_LEAVE_GROUP)
optname = IP_DROP_SOURCE_MEMBERSHIP;
else
return UV_EINVAL;

if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
optname,
&mreq,
sizeof(mreq))) {
return UV__ERR(errno);
}

return 0;
}


static int uv__udp_set_source_membership6(uv_udp_t* handle,
const struct sockaddr_in6* multicast_addr,
const char* interface_addr,
const struct sockaddr_in6* source_addr,
uv_membership membership) {
struct group_source_req mreq;
struct sockaddr_in6 addr6;
int optname;
int err;

err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR);
if (err)
return err;

memset(&mreq, 0, sizeof(mreq));

if (interface_addr != NULL) {
err = uv_ip6_addr(interface_addr, 0, &addr6);
if (err)
return err;
mreq.gsr_interface = addr6.sin6_scope_id;
} else {
mreq.gsr_interface = 0;
}

memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group));
memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source));

if (membership == UV_JOIN_GROUP)
optname = MCAST_JOIN_SOURCE_GROUP;
else if (membership == UV_LEAVE_GROUP)
optname = MCAST_LEAVE_SOURCE_GROUP;
else
return UV_EINVAL;

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

return 0;
}


int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
int domain;
int err;
Expand Down Expand Up @@ -748,6 +840,51 @@ int uv_udp_set_membership(uv_udp_t* handle,
}
}


int uv_udp_set_source_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
const char* source_addr,
uv_membership membership) {
int err;
struct sockaddr_storage mcast_addr;
struct sockaddr_in* mcast_addr4;
struct sockaddr_in6* mcast_addr6;
struct sockaddr_storage src_addr;
struct sockaddr_in* src_addr4;
struct sockaddr_in6* src_addr6;

mcast_addr4 = (struct sockaddr_in*)&mcast_addr;
mcast_addr6 = (struct sockaddr_in6*)&mcast_addr;
src_addr4 = (struct sockaddr_in*)&src_addr;
src_addr6 = (struct sockaddr_in6*)&src_addr;

err = uv_ip4_addr(multicast_addr, 0, mcast_addr4);
if (err) {
err = uv_ip6_addr(multicast_addr, 0, mcast_addr6);
if (err)
return err;
err = uv_ip6_addr(source_addr, 0, src_addr6);
if (err)
return err;
return uv__udp_set_source_membership6(handle,
mcast_addr6,
interface_addr,
src_addr6,
membership);
}

err = uv_ip4_addr(source_addr, 0, src_addr4);
if (err)
return err;
return uv__udp_set_source_membership4(handle,
mcast_addr4,
interface_addr,
src_addr4,
membership);
}


static int uv__setsockopt(uv_udp_t* handle,
int option4,
int option6,
Expand Down
150 changes: 150 additions & 0 deletions src/win/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,112 @@ int uv__udp_set_membership6(uv_udp_t* handle,
}


static int uv__udp_set_source_membership4(uv_udp_t* handle,
const struct sockaddr_in* multicast_addr,
const char* interface_addr,
const struct sockaddr_in* source_addr,
uv_membership membership) {
struct ip_mreq_source mreq;
int optname;
int err;

if (handle->flags & UV_HANDLE_IPV6)
return UV_EINVAL;

/* If the socket is unbound, bind to inaddr_any. */
err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip4_any_,
sizeof(uv_addr_ip4_any_),
UV_UDP_REUSEADDR);
if (err)
return uv_translate_sys_error(err);

memset(&mreq, 0, sizeof(mreq));

if (interface_addr != NULL) {
err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr);
if (err)
return err;
} else {
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
}

mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr;
mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr;

if (membership == UV_JOIN_GROUP)
optname = IP_ADD_SOURCE_MEMBERSHIP;
else if (membership == UV_LEAVE_GROUP)
optname = IP_DROP_SOURCE_MEMBERSHIP;
else
return UV_EINVAL;

if (setsockopt(handle->socket,
IPPROTO_IP,
optname,
(char*) &mreq,
sizeof(mreq)) == SOCKET_ERROR) {
return uv_translate_sys_error(WSAGetLastError());
}

return 0;
}


int uv__udp_set_source_membership6(uv_udp_t* handle,
const struct sockaddr_in6* multicast_addr,
const char* interface_addr,
const struct sockaddr_in6* source_addr,
uv_membership membership) {
struct group_source_req mreq;
struct sockaddr_in6 addr6;
int optname;
int err;

if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
return UV_EINVAL;

err = uv_udp_maybe_bind(handle,
(const struct sockaddr*) &uv_addr_ip6_any_,
sizeof(uv_addr_ip6_any_),
UV_UDP_REUSEADDR);

if (err)
return uv_translate_sys_error(err);

memset(&mreq, 0, sizeof(mreq));

if (interface_addr != NULL) {
err = uv_ip6_addr(interface_addr, 0, &addr6);
if (err)
return err;
mreq.gsr_interface = addr6.sin6_scope_id;
} else {
mreq.gsr_interface = 0;
}

memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group));
memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source));

if (membership == UV_JOIN_GROUP)
optname = MCAST_JOIN_SOURCE_GROUP;
else if (membership == UV_LEAVE_GROUP)
optname = MCAST_LEAVE_SOURCE_GROUP;
else
return UV_EINVAL;

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_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
Expand All @@ -718,6 +824,50 @@ int uv_udp_set_membership(uv_udp_t* handle,
}


int uv_udp_set_source_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
const char* source_addr,
uv_membership membership) {
int err;
struct sockaddr_storage mcast_addr;
struct sockaddr_in* mcast_addr4;
struct sockaddr_in6* mcast_addr6;
struct sockaddr_storage src_addr;
struct sockaddr_in* src_addr4;
struct sockaddr_in6* src_addr6;

mcast_addr4 = (struct sockaddr_in*)&mcast_addr;
mcast_addr6 = (struct sockaddr_in6*)&mcast_addr;
src_addr4 = (struct sockaddr_in*)&src_addr;
src_addr6 = (struct sockaddr_in6*)&src_addr;

err = uv_ip4_addr(multicast_addr, 0, mcast_addr4);
if (err) {
err = uv_ip6_addr(multicast_addr, 0, mcast_addr6);
if (err)
return err;
err = uv_ip6_addr(source_addr, 0, src_addr6);
if (err)
return err;
return uv__udp_set_source_membership6(handle,
mcast_addr6,
interface_addr,
src_addr6,
membership);
}

err = uv_ip4_addr(source_addr, 0, src_addr4);
if (err)
return err;
return uv__udp_set_source_membership4(handle,
mcast_addr4,
interface_addr,
src_addr4,
membership);
}


int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
struct sockaddr_storage addr_st;
struct sockaddr_in* addr4;
Expand Down