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 #964

Closed
wants to merge 1 commit into from
Closed
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
19 changes: 19 additions & 0 deletions docs/src/udp.rst
Expand Up @@ -181,6 +181,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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to add a .. versionadded:: 1.10.0 here


.. versionadded:: 1.20.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
16 changes: 16 additions & 0 deletions include/uv.h
Expand Up @@ -359,6 +359,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,
Expand Down Expand Up @@ -634,6 +645,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
134 changes: 134 additions & 0 deletions src/unix/udp.c
Expand Up @@ -578,6 +578,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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one level of indentation is missing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's consistent with other "switch..case" instructions in this file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though the style is not consistent in the project, I would add indentation here as it's already the most commonly used format in the source and I think consistency is good. Anyway, let's see what others think.


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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't mreq.gsr_interface already 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


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;
Expand Down Expand Up @@ -660,6 +754,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,
Expand Down
147 changes: 147 additions & 0 deletions src/win/udp.c
Expand Up @@ -700,6 +700,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here that mentions what #ifdef this corresponds to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return UV_EPROTONOSUPPORT;
#endif /* IPV6_SSM_SUPPORT */
}


int uv_udp_set_membership(uv_udp_t* handle,
const char* multicast_addr,
const char* interface_addr,
Expand All @@ -716,6 +824,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;
Expand Down