This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 99ee94728a net:add IP_MULTICAST_IF & IPV6_MULTICAST_IF function
implementation
99ee94728a is described below
commit 99ee94728a68c018768e4b9d6d5d6e3e37f47258
Author: wangchen <[email protected]>
AuthorDate: Mon Aug 21 10:49:02 2023 +0800
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 <[email protected]>
---
net/inet/ipv4_setsockopt.c | 78 +++++++++++++++++++++++++++++-
net/inet/ipv6_setsockopt.c | 38 ++++++++++++++-
net/udp/udp.h | 4 ++
net/udp/udp_finddev.c | 117 ++++++++++++++++++++++++++++-----------------
4 files changed, 188 insertions(+), 49 deletions(-)
diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c
index 5cfc853a09..7553cdf134 100644
--- a/net/inet/ipv4_setsockopt.c
+++ b/net/inet/ipv4_setsockopt.c
@@ -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
diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c
index 2df0b6c8a1..2fc08de040 100644
--- a/net/inet/ipv6_setsockopt.c
+++ b/net/inet/ipv6_setsockopt.c
@@ -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
diff --git a/net/udp/udp.h b/net/udp/udp.h
index 622897cd0b..354aefb8ea 100644
--- a/net/udp/udp.h
+++ b/net/udp/udp.h
@@ -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.
*/
diff --git a/net/udp/udp_finddev.c b/net/udp/udp_finddev.c
index 93c5d4b7ee..2240ac05ba 100644
--- a/net/udp/udp_finddev.c
+++ b/net/udp/udp_finddev.c
@@ -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))
+ struct in6_addr raddr;
+ if (remote)
{
- /* 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);
+ 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);
}
-
-#ifdef CONFIG_NET_BINDTODEVICE
- if (conn->sconn.s_boundto != 0)
+ else
{
- /* 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);
+ net_ipv6addr_copy(raddr.in6_u.u6_addr16, conn->u.ipv6.raddr);
}
-#endif
- if (remote)
+#if defined(CONFIG_NET_MLD) && defined(CONFIG_NET_BINDTODEVICE)
+ if (IN6_IS_ADDR_MULTICAST(&raddr))
{
- FAR const struct sockaddr_in6 *inaddr =
- (FAR const struct sockaddr_in6 *)remote;
- net_ipv6addr_copy(raddr, inaddr->sin6_addr.s6_addr16);
+ if ((conn->sconn.s_boundto == 0) &&
+ (conn->mreq.imr_ifindex != 0))
+ {
+ return netdev_findbyindex(conn->mreq.imr_ifindex);
+ }
}
else
+#endif
{
- net_ipv6addr_copy(raddr, conn->u.ipv6.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
}
/* 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
}