udp: add source-specific multicast support

PR-URL: https://github.com/libuv/libuv/pull/2202
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
Vladimir Karnushin 2016-07-31 21:03:16 +03:00 committed by Santiago Gimeno
parent 2ee2d4622a
commit 91d95f8ceb
No known key found for this signature in database
GPG Key ID: F28C3C8DA33C03BE
4 changed files with 316 additions and 0 deletions

View File

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

View File

@ -364,6 +364,17 @@ typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle,
typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum);
#if defined(MCAST_JOIN_SOURCE_GROUP) && defined(MCAST_LEAVE_SOURCE_GROUP)
# ifndef IPV6_SSM_SUPPORT
# define IPV6_SSM_SUPPORT
# endif
# ifndef IPV6_ADD_SOURCE_MEMBERSHIP
# define IPV6_ADD_SOURCE_MEMBERSHIP MCAST_JOIN_SOURCE_GROUP
# endif
# ifndef IPV6_DROP_SOURCE_MEMBERSHIP
# define IPV6_DROP_SOURCE_MEMBERSHIP MCAST_LEAVE_SOURCE_GROUP
# endif
#endif
typedef enum {
UV_LEAVE_GROUP = 0,
@ -646,6 +657,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,

View File

@ -659,6 +659,100 @@ 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 -EINVAL;
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
optname,
&mreq,
sizeof(mreq))) {
return -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) {
#ifdef IPV6_SSM_SUPPORT
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;
}
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 = IP_ADD_SOURCE_MEMBERSHIP;
else if (membership == UV_LEAVE_GROUP)
optname = IP_DROP_SOURCE_MEMBERSHIP;
else
return -EINVAL;
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IPV6,
optname,
&mreq,
sizeof(mreq))) {
return -errno;
}
return 0;
#else
return -EPROTONOSUPPORT;
#endif /* IPV6_SSM_SUPPORT */
}
int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
int domain;
int err;
@ -748,6 +842,46 @@ 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;
union sockaddr {
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
};
union sockaddr mcast_addr;
union sockaddr src_addr;
err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.sa_in);
if (err) {
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.sa_in6);
if (err)
return err;
err = uv_ip6_addr(source_addr, 0, &src_addr.sa_in6);
if (err)
return err;
return uv__udp_set_source_membership6(handle,
&mcast_addr.sa_in6,
interface_addr,
&src_addr.sa_in6,
membership);
}
err = uv_ip4_addr(source_addr, 0, &src_addr.sa_in);
if (err)
return err;
return uv__udp_set_source_membership4(handle,
&mcast_addr.sa_in,
interface_addr,
&src_addr.sa_in,
membership);
}
static int uv__setsockopt(uv_udp_t* handle,
int option4,
int option6,

View File

@ -702,6 +702,114 @@ 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 -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) {
#ifdef IPV6_SSM_SUPPORT
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;
}
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 = IP_ADD_SOURCE_MEMBERSHIP;
else if (membership == UV_LEAVE_GROUP)
optname = IP_DROP_SOURCE_MEMBERSHIP;
else
return -EINVAL;
if (setsockopt(handle->socket,
IPPROTO_IPV6,
optname,
(char*) &mreq,
sizeof(mreq)) == SOCKET_ERROR) {
return uv_translate_sys_error(WSAGetLastError());
}
return 0;
#else
return UV_EPROTONOSUPPORT;
#endif /* IPV6_SSM_SUPPORT */
}
int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
@ -718,6 +826,45 @@ 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;
union sockaddr {
struct sockaddr_in sa_in;
struct sockaddr_in6 sa_in6;
};
union sockaddr mcast_addr;
union sockaddr src_addr;
err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.sa_in);
if (err) {
err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.sa_in6);
if (err)
return err;
err = uv_ip6_addr(source_addr, 0, &src_addr.sa_in6);
if (err)
return err;
return uv__udp_set_source_membership6(handle,
&mcast_addr.sa_in6,
interface_addr,
&src_addr.sa_in6,
membership);
}
err = uv_ip4_addr(source_addr, 0, &src_addr.sa_in);
if (err)
return err;
return uv__udp_set_source_membership4(handle,
&mcast_addr.sa_in,
interface_addr,
&src_addr.sa_in,
membership);
}
int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) {
struct sockaddr_storage addr_st;
struct sockaddr_in* addr4;