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:
parent
2ee2d4622a
commit
91d95f8ceb
@ -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
|
||||
|
||||
16
include/uv.h
16
include/uv.h
@ -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,
|
||||
|
||||
134
src/unix/udp.c
134
src/unix/udp.c
@ -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,
|
||||
|
||||
147
src/win/udp.c
147
src/win/udp.c
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user