net:add IP_MULTICAST_IF & IPV6_MULTICAST_IF function implementation
refer to https://man7.org/linux/man-pages/man7/ip.7.html IP_MULTICAST_IF (since Linux 1.2) Set the local device for a multicast socket. The argument for setsockopt(2) is an ip_mreqn or (since Linux 3.5) ip_mreq structure similar to IP_ADD_MEMBERSHIP, or an in_addr structure. (The kernel determines which structure is being passed based on the size passed in optlen.) For getsockopt(2), the argument is an in_addr structure. refer to https://man7.org/linux/man-pages/man7/ipv6.7.html IPV6_MULTICAST_IF Set the device for outgoing multicast packets on the socket. This is allowed only for SOCK_DGRAM and SOCK_RAW socket. The argument is a pointer to an interface index (see netdevice(7)) in an integer. testcase1: TEST_IMPL(udp_multicast_interface) { /* TODO(gengjiawen): Fix test on QEMU. */ RETURN_SKIP("Test does not currently work in QEMU"); int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in addr; struct sockaddr_in baddr; close_cb_called = 0; sv_send_cb_called = 0; ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); ASSERT(r == 0); r = uv_udp_set_multicast_interface(&server, "0.0.0.0"); ASSERT(r == 0); /* server sends "PING" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*)&addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); ASSERT(client.send_queue_size == 0); ASSERT(server.send_queue_size == 0); MAKE_VALGRIND_HAPPY(); return 0; } testcase2: TEST_IMPL(udp_multicast_interface6) { /* TODO(gengjiawen): Fix test on QEMU. */ RETURN_SKIP("Test does not currently work in QEMU"); int r; uv_udp_send_t req; uv_buf_t buf; struct sockaddr_in6 addr; struct sockaddr_in6 baddr; if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); close_cb_called = 0; sv_send_cb_called = 0; ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); ASSERT(r == 0); ASSERT(0 == uv_ip6_addr("::", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); ASSERT(r == 0); r = uv_udp_set_multicast_interface(&server, "::1%lo0"); r = uv_udp_set_multicast_interface(&server, NULL); ASSERT(r == 0); /* server sends "PING" */ buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*)&addr, sv_send_cb); ASSERT(r == 0); ASSERT(close_cb_called == 0); ASSERT(sv_send_cb_called == 0); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(sv_send_cb_called == 1); ASSERT(close_cb_called == 1); MAKE_VALGRIND_HAPPY(); return 0; } Signed-off-by: wangchen <wangchen41@xiaomi.com>
This commit is contained in:
parent
50f0fd4df2
commit
99ee94728a
@ -208,10 +208,84 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
|
||||
}
|
||||
break;
|
||||
|
||||
/* The following IPv4 socket options are defined, but not implemented */
|
||||
|
||||
case IP_MULTICAST_IF: /* Set local device for a multicast
|
||||
* socket */
|
||||
#ifdef NET_UDP_HAVE_STACK
|
||||
{
|
||||
FAR struct udp_conn_s *conn;
|
||||
FAR struct net_driver_s *dev;
|
||||
struct ip_mreqn mreq;
|
||||
|
||||
conn = psock->s_conn;
|
||||
if (value == NULL || value_len == 0)
|
||||
{
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value_len >= sizeof(struct ip_mreqn))
|
||||
{
|
||||
memcpy(&mreq, value, sizeof(mreq));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&mreq, 0, sizeof(mreq));
|
||||
if (value_len >= sizeof(struct ip_mreq))
|
||||
{
|
||||
memcpy(&mreq, value, sizeof(struct ip_mreq));
|
||||
}
|
||||
else if (value_len >= sizeof(struct in_addr))
|
||||
{
|
||||
memcpy(&mreq.imr_multiaddr,
|
||||
value, sizeof(struct in_addr));
|
||||
}
|
||||
}
|
||||
|
||||
if (!mreq.imr_ifindex)
|
||||
{
|
||||
if (net_ipv4addr_cmp(mreq.imr_multiaddr.s_addr, INADDR_ANY))
|
||||
{
|
||||
conn->mreq.imr_interface.s_addr = 0;
|
||||
conn->mreq.imr_ifindex = 0;
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
|
||||
dev = netdev_findby_lipv4addr(mreq.imr_multiaddr.s_addr);
|
||||
if (dev)
|
||||
{
|
||||
mreq.imr_ifindex = dev->d_ifindex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dev = netdev_findbyindex(mreq.imr_ifindex);
|
||||
}
|
||||
|
||||
if (!dev)
|
||||
{
|
||||
ret = -EADDRNOTAVAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto &&
|
||||
mreq.imr_ifindex != conn->sconn.s_boundto)
|
||||
{
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
conn->mreq.imr_interface.s_addr = mreq.imr_multiaddr.s_addr;
|
||||
conn->mreq.imr_ifindex = mreq.imr_ifindex;
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The following IPv4 socket options are defined, but not implemented */
|
||||
|
||||
case IP_MULTICAST_LOOP: /* Set/read boolean that determines
|
||||
* whether sent multicast packets
|
||||
* should be looped back to local
|
||||
|
@ -32,9 +32,11 @@
|
||||
|
||||
#include <nuttx/net/net.h>
|
||||
|
||||
#include "netdev/netdev.h"
|
||||
#include "mld/mld.h"
|
||||
#include "inet/inet.h"
|
||||
#include "socket/socket.h"
|
||||
#include "udp/udp.h"
|
||||
|
||||
#if defined(CONFIG_NET_IPv6) && defined(CONFIG_NET_SOCKOPTS)
|
||||
|
||||
@ -103,10 +105,42 @@ int ipv6_setsockopt(FAR struct socket *psock, int option,
|
||||
}
|
||||
break;
|
||||
|
||||
/* The following IPv6 socket options are defined, but not implemented */
|
||||
|
||||
case IPV6_MULTICAST_IF: /* Interface to use for outgoing multicast
|
||||
* packets */
|
||||
#ifdef NET_UDP_HAVE_STACK
|
||||
{
|
||||
FAR struct net_driver_s *dev;
|
||||
FAR struct udp_conn_s *conn = psock->s_conn;
|
||||
int ifindex = *(FAR int *)value;
|
||||
|
||||
if (ifindex > 0)
|
||||
{
|
||||
dev = netdev_findbyindex(ifindex);
|
||||
if (dev == NULL)
|
||||
{
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto &&
|
||||
ifindex != conn->sconn.s_boundto)
|
||||
{
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
conn->mreq.imr_ifindex = ifindex;
|
||||
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The following IPv6 socket options are defined, but not implemented */
|
||||
|
||||
case IPV6_MULTICAST_LOOP: /* Multicast packets are delivered back to
|
||||
* the local application */
|
||||
#endif
|
||||
|
@ -147,6 +147,10 @@ struct udp_conn_s
|
||||
FAR struct devif_callback_s *sndcb;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_MLD)
|
||||
struct ip_mreqn mreq;
|
||||
#endif
|
||||
|
||||
/* The following is a list of poll structures of threads waiting for
|
||||
* socket events.
|
||||
*/
|
||||
|
@ -143,27 +143,6 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
|
||||
{
|
||||
in_addr_t raddr;
|
||||
|
||||
if (conn->u.ipv4.laddr != INADDR_ANY)
|
||||
{
|
||||
/* If the socket is bound to some non-zero, local address.
|
||||
* Normal lookup using the verified local address.
|
||||
*/
|
||||
|
||||
return netdev_findby_lipv4addr(conn->u.ipv4.laddr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto != 0)
|
||||
{
|
||||
/* If the socket is bound to a local network device.
|
||||
* Select the network device that has been bound.
|
||||
* If the index is invalid, return NULL.
|
||||
*/
|
||||
|
||||
return netdev_findbyindex(conn->sconn.s_boundto);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (remote)
|
||||
{
|
||||
FAR const struct sockaddr_in *inaddr =
|
||||
@ -175,6 +154,40 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
|
||||
net_ipv4addr_copy(raddr, conn->u.ipv4.raddr);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_IGMP) && defined(CONFIG_NET_BINDTODEVICE)
|
||||
if (IN_MULTICAST(NTOHL(raddr)))
|
||||
{
|
||||
if ((conn->sconn.s_boundto == 0) &&
|
||||
(conn->mreq.imr_ifindex != 0))
|
||||
{
|
||||
return netdev_findbyindex(conn->mreq.imr_ifindex);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (conn->u.ipv4.laddr != INADDR_ANY)
|
||||
{
|
||||
/* If the socket is bound to some non-zero, local address.
|
||||
* Normal lookup using the verified local address.
|
||||
*/
|
||||
|
||||
return netdev_findby_lipv4addr(conn->u.ipv4.laddr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto != 0)
|
||||
{
|
||||
/* If the socket is bound to a local network device.
|
||||
* Select the network device that has been bound.
|
||||
* If the index is invalid, return NULL.
|
||||
*/
|
||||
|
||||
return netdev_findbyindex(conn->sconn.s_boundto);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Normal lookup using the verified remote address */
|
||||
|
||||
return netdev_findby_ripv4addr(conn->u.ipv4.laddr, raddr);
|
||||
@ -186,43 +199,57 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
|
||||
else
|
||||
#endif
|
||||
{
|
||||
net_ipv6addr_t raddr;
|
||||
|
||||
if (!net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr))
|
||||
{
|
||||
/* If the socket is bound to some non-zero, local address.
|
||||
* Normal lookup using the verified local address.
|
||||
*/
|
||||
|
||||
return netdev_findby_lipv6addr(conn->u.ipv6.laddr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto != 0)
|
||||
{
|
||||
/* If the socket is bound to a local network device.
|
||||
* Select the network device that has been bound.
|
||||
* If the index is invalid, return NULL.
|
||||
*/
|
||||
|
||||
return netdev_findbyindex(conn->sconn.s_boundto);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct in6_addr raddr;
|
||||
if (remote)
|
||||
{
|
||||
FAR const struct sockaddr_in6 *inaddr =
|
||||
(FAR const struct sockaddr_in6 *)remote;
|
||||
net_ipv6addr_copy(raddr, inaddr->sin6_addr.s6_addr16);
|
||||
net_ipv6addr_copy(raddr.in6_u.u6_addr16,
|
||||
inaddr->sin6_addr.s6_addr16);
|
||||
}
|
||||
else
|
||||
{
|
||||
net_ipv6addr_copy(raddr, conn->u.ipv6.raddr);
|
||||
net_ipv6addr_copy(raddr.in6_u.u6_addr16, conn->u.ipv6.raddr);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NET_MLD) && defined(CONFIG_NET_BINDTODEVICE)
|
||||
if (IN6_IS_ADDR_MULTICAST(&raddr))
|
||||
{
|
||||
if ((conn->sconn.s_boundto == 0) &&
|
||||
(conn->mreq.imr_ifindex != 0))
|
||||
{
|
||||
return netdev_findbyindex(conn->mreq.imr_ifindex);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (!net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr))
|
||||
{
|
||||
/* If the socket is bound to some non-zero, local address.
|
||||
* Normal lookup using the verified local address.
|
||||
*/
|
||||
|
||||
return netdev_findby_lipv6addr(conn->u.ipv6.laddr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_BINDTODEVICE
|
||||
if (conn->sconn.s_boundto != 0)
|
||||
{
|
||||
/* If the socket is bound to a local network device.
|
||||
* Select the network device that has been bound.
|
||||
* If the index is invalid, return NULL.
|
||||
*/
|
||||
|
||||
return netdev_findbyindex(conn->sconn.s_boundto);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Normal lookup using the verified remote address */
|
||||
|
||||
return netdev_findby_ripv6addr(conn->u.ipv6.laddr, raddr);
|
||||
return netdev_findby_ripv6addr(conn->u.ipv6.laddr,
|
||||
raddr.in6_u.u6_addr16);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user