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:
wangchen 2023-08-21 10:49:02 +08:00 committed by Xiang Xiao
parent 50f0fd4df2
commit 99ee94728a
4 changed files with 190 additions and 51 deletions

View File

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

View File

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

View File

@ -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.
*/

View File

@ -143,6 +143,29 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
{
in_addr_t raddr;
if (remote)
{
FAR const struct sockaddr_in *inaddr =
(FAR const struct sockaddr_in *)remote;
net_ipv4addr_copy(raddr, inaddr->sin_addr.s_addr);
}
else
{
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.
@ -163,16 +186,6 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
return netdev_findbyindex(conn->sconn.s_boundto);
}
#endif
if (remote)
{
FAR const struct sockaddr_in *inaddr =
(FAR const struct sockaddr_in *)remote;
net_ipv4addr_copy(raddr, inaddr->sin_addr.s_addr);
}
else
{
net_ipv4addr_copy(raddr, conn->u.ipv4.raddr);
}
/* Normal lookup using the verified remote address */
@ -186,8 +199,31 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
else
#endif
{
net_ipv6addr_t raddr;
struct in6_addr raddr;
if (remote)
{
FAR const struct sockaddr_in6 *inaddr =
(FAR const struct sockaddr_in6 *)remote;
net_ipv6addr_copy(raddr.in6_u.u6_addr16,
inaddr->sin6_addr.s6_addr16);
}
else
{
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.
@ -208,21 +244,12 @@ udp_find_raddr_device(FAR struct udp_conn_s *conn,
return netdev_findbyindex(conn->sconn.s_boundto);
}
#endif
if (remote)
{
FAR const struct sockaddr_in6 *inaddr =
(FAR const struct sockaddr_in6 *)remote;
net_ipv6addr_copy(raddr, inaddr->sin6_addr.s6_addr16);
}
else
{
net_ipv6addr_copy(raddr, conn->u.ipv6.raddr);
}
/* 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
}