From 806d783fd6dab9cb4de79da2516e9ece71b4efdc Mon Sep 17 00:00:00 2001 From: Zhe Weng Date: Wed, 27 Mar 2024 17:15:44 +0800 Subject: [PATCH] net/udp: Deliver data into multiple UDP conn bound to same port Note: We'll only get multiple conn bound to same port when we support SO_REUSEADDR Signed-off-by: Zhe Weng --- net/udp/udp.h | 1 + net/udp/udp_conn.c | 19 +++--- net/udp/udp_input.c | 152 ++++++++++++++++++++++++++++++-------------- 3 files changed, 118 insertions(+), 54 deletions(-) diff --git a/net/udp/udp.h b/net/udp/udp.h index 8b8aa4bfe10..3f07fefc79a 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -237,6 +237,7 @@ void udp_free(FAR struct udp_conn_s *conn); ****************************************************************************/ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev, + FAR struct udp_conn_s *conn, FAR struct udp_hdr_s *udp); /**************************************************************************** diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index 49fa9c3f54a..3205c93e6a7 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -188,15 +188,16 @@ static FAR struct udp_conn_s *udp_find_conn(uint8_t domain, #ifdef CONFIG_NET_IPv4 static inline FAR struct udp_conn_s * - udp_ipv4_active(FAR struct net_driver_s *dev, FAR struct udp_hdr_s *udp) +udp_ipv4_active(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, + FAR struct udp_hdr_s *udp) { #ifdef CONFIG_NET_BROADCAST static const in_addr_t bcast = INADDR_BROADCAST; #endif FAR struct ipv4_hdr_s *ip = IPv4BUF; - FAR struct udp_conn_s *conn; - conn = (FAR struct udp_conn_s *)g_active_udp_connections.head; + conn = udp_nextconn(conn); + while (conn) { /* If the local UDP port is non-zero, the connection is considered @@ -330,12 +331,13 @@ static inline FAR struct udp_conn_s * #ifdef CONFIG_NET_IPv6 static inline FAR struct udp_conn_s * - udp_ipv6_active(FAR struct net_driver_s *dev, FAR struct udp_hdr_s *udp) +udp_ipv6_active(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, + FAR struct udp_hdr_s *udp) { FAR struct ipv6_hdr_s *ip = IPv6BUF; - FAR struct udp_conn_s *conn; - conn = (FAR struct udp_conn_s *)g_active_udp_connections.head; + conn = udp_nextconn(conn); + while (conn != NULL) { /* If the local UDP port is non-zero, the connection is considered @@ -747,6 +749,7 @@ void udp_free(FAR struct udp_conn_s *conn) ****************************************************************************/ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev, + FAR struct udp_conn_s *conn, FAR struct udp_hdr_s *udp) { #ifdef CONFIG_NET_IPv6 @@ -754,7 +757,7 @@ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev, if (IFF_IS_IPv6(dev->d_flags)) #endif { - return udp_ipv6_active(dev, udp); + return udp_ipv6_active(dev, conn, udp); } #endif /* CONFIG_NET_IPv6 */ @@ -763,7 +766,7 @@ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev, else #endif { - return udp_ipv4_active(dev, udp); + return udp_ipv4_active(dev, conn, udp); } #endif /* CONFIG_NET_IPv4 */ } diff --git a/net/udp/udp_input.c b/net/udp/udp_input.c index aff8e6e79c8..3b9c9910f4c 100644 --- a/net/udp/udp_input.c +++ b/net/udp/udp_input.c @@ -63,6 +63,78 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: udp_input_conn + * + * Description: + * Handle incoming UDP input for the case where there is an active + * connection. + * + * Input Parameters: + * dev - The device driver structure containing the received UDP pkt + * conn - The UDP connection structure associated with the packet + * udpiplen - Length of the IP and UDP headers + * + * Returned Value: + * OK - The packet has been processed + * -EAGAIN - Hold the packet and try again later. There is a listening + * socket but no receive in place to catch the packet yet. The + * device's d_len will be set to zero in this case as there is + * no outgoing data. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static int udp_input_conn(FAR struct net_driver_s *dev, + FAR struct udp_conn_s *conn, unsigned int udpiplen) +{ + uint16_t flags; + + /* Set-up for the application callback */ + + dev->d_appdata = IPBUF(udpiplen); + dev->d_sndlen = 0; + + /* Perform the application callback */ + + flags = udp_callback(dev, conn, UDP_NEWDATA); + + /* If the operation was successful and the UDP data was "consumed," + * then the UDP_NEWDATA flag will be cleared by logic in + * udp_callback(). The packet memory can then be freed by the + * network driver. OK will be returned to the network driver to + * indicate this case. + * + * "Consumed" here means that either the received data was (1) + * accepted by a socket waiting for data on the port or was (2) + * buffered in the UDP socket's read-ahead buffer. + */ + + if ((flags & UDP_NEWDATA) != 0) + { + /* No.. the packet was not processed now. Return -EAGAIN so + * that the driver may retry again later. We still need to + * set d_len to zero so that the driver is aware that there + * is nothing to be sent. + */ + + nwarn("WARNING: Packet not processed\n"); + dev->d_len = 0; + return -EAGAIN; + } + + /* If the application has data to send, setup the UDP/IP header */ + + if (dev->d_sndlen > 0) + { + udp_send(dev, conn); + } + + return OK; +} + /**************************************************************************** * Name: udp_input * @@ -90,6 +162,10 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned int iplen) { FAR struct udp_hdr_s *udp; FAR struct udp_conn_s *conn; +#ifdef CONFIG_NET_SOCKOPTS + FAR struct udp_conn_s *nextconn; + FAR struct iob_s *iob; +#endif unsigned int udpiplen; #ifdef CONFIG_NET_UDP_CHECKSUMS uint16_t chksum; @@ -157,64 +233,48 @@ static int udp_input(FAR struct net_driver_s *dev, unsigned int iplen) { /* Demultiplex this UDP packet between the UDP "connections". * - * REVISIT: The logic here expects either a single receive socket or - * none at all. However, multiple sockets should be capable of - * receiving a UDP datagram (multicast reception). This could be - * handled easily by something like: - * - * for (conn = NULL; conn = udp_active(dev, udp); ) - * - * If the callback logic that receives a packet responds with an - * outgoing packet, then it will over-write the received buffer, - * however. recvfrom() will not do that, however. We would have to - * make that the rule: Recipients of a UDP packet must treat the - * packet as read-only. + * REVISIT: If the callback logic that receives a packet responds with + * an outgoing packet, then it may be ignored. recvfrom() will not do + * that, however. */ - conn = udp_active(dev, udp); + conn = udp_active(dev, NULL, udp); if (conn) { - uint16_t flags; + /* We'll only get multiple conn when we support SO_REUSEADDR */ - /* Set-up for the application callback */ +#ifdef CONFIG_NET_SOCKOPTS + /* Do we have second connection that can hold this packet? */ - dev->d_appdata = IPBUF(udpiplen); - dev->d_sndlen = 0; - - /* Perform the application callback */ - - flags = udp_callback(dev, conn, UDP_NEWDATA); - - /* If the operation was successful and the UDP data was "consumed," - * then the UDP_NEWDATA flag will be cleared by logic in - * udp_callback(). The packet memory can then be freed by the - * network driver. OK will be returned to the network driver to - * indicate this case. - * - * "Consumed" here means that either the received data was (1) - * accepted by a socket waiting for data on the port or was (2) - * buffered in the UDP socket's read-ahead buffer. - */ - - if ((flags & UDP_NEWDATA) != 0) + while ((nextconn = udp_active(dev, conn, udp)) != NULL) { - /* No.. the packet was not processed now. Return -EAGAIN so - * that the driver may retry again later. We still need to - * set d_len to zero so that the driver is aware that there - * is nothing to be sent. + /* Yes... There are multiple listeners on the same port. + * We need to clone the packet and deliver it to each listener. */ - nwarn("WARNING: Packet not processed\n"); - dev->d_len = 0; - ret = -EAGAIN; - } + iob = netdev_iob_clone(dev, true); + if (iob == NULL) + { + nerr("ERROR: IOB clone failed.\n"); + break; /* We can still process one time without clone. */ + } - /* If the application has data to send, setup the UDP/IP header */ + ret = udp_input_conn(dev, conn, udpiplen); + if (ret < 0) + { + nwarn("WARNING: A conn failed to process the packet %d\n", + ret); /* We can still continue for next conn. */ + } - if (dev->d_sndlen > 0) - { - udp_send(dev, conn); + netdev_iob_replace(dev, iob); + udp = IPBUF(iplen); + conn = nextconn; } +#endif + + /* We can deliver the packet directly to the last listener. */ + + ret = udp_input_conn(dev, conn, udpiplen); } else {