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 <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-03-27 17:15:44 +08:00 committed by Xiang Xiao
parent 408320f2ba
commit 806d783fd6
3 changed files with 118 additions and 54 deletions

View File

@ -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 *udp_active(FAR struct net_driver_s *dev,
FAR struct udp_conn_s *conn,
FAR struct udp_hdr_s *udp); FAR struct udp_hdr_s *udp);
/**************************************************************************** /****************************************************************************

View File

@ -188,15 +188,16 @@ static FAR struct udp_conn_s *udp_find_conn(uint8_t domain,
#ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv4
static inline FAR struct udp_conn_s * 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 #ifdef CONFIG_NET_BROADCAST
static const in_addr_t bcast = INADDR_BROADCAST; static const in_addr_t bcast = INADDR_BROADCAST;
#endif #endif
FAR struct ipv4_hdr_s *ip = IPv4BUF; 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) while (conn)
{ {
/* If the local UDP port is non-zero, the connection is considered /* 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 #ifdef CONFIG_NET_IPv6
static inline FAR struct udp_conn_s * 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 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) while (conn != NULL)
{ {
/* If the local UDP port is non-zero, the connection is considered /* 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 *udp_active(FAR struct net_driver_s *dev,
FAR struct udp_conn_s *conn,
FAR struct udp_hdr_s *udp) FAR struct udp_hdr_s *udp)
{ {
#ifdef CONFIG_NET_IPv6 #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)) if (IFF_IS_IPv6(dev->d_flags))
#endif #endif
{ {
return udp_ipv6_active(dev, udp); return udp_ipv6_active(dev, conn, udp);
} }
#endif /* CONFIG_NET_IPv6 */ #endif /* CONFIG_NET_IPv6 */
@ -763,7 +766,7 @@ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev,
else else
#endif #endif
{ {
return udp_ipv4_active(dev, udp); return udp_ipv4_active(dev, conn, udp);
} }
#endif /* CONFIG_NET_IPv4 */ #endif /* CONFIG_NET_IPv4 */
} }

View File

@ -63,6 +63,78 @@
* Private Functions * 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 * 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_hdr_s *udp;
FAR struct udp_conn_s *conn; 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; unsigned int udpiplen;
#ifdef CONFIG_NET_UDP_CHECKSUMS #ifdef CONFIG_NET_UDP_CHECKSUMS
uint16_t chksum; 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". /* Demultiplex this UDP packet between the UDP "connections".
* *
* REVISIT: The logic here expects either a single receive socket or * REVISIT: If the callback logic that receives a packet responds with
* none at all. However, multiple sockets should be capable of * an outgoing packet, then it may be ignored. recvfrom() will not do
* receiving a UDP datagram (multicast reception). This could be * that, however.
* 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.
*/ */
conn = udp_active(dev, udp); conn = udp_active(dev, NULL, udp);
if (conn) 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); while ((nextconn = udp_active(dev, conn, udp)) != NULL)
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 /* Yes... There are multiple listeners on the same port.
* that the driver may retry again later. We still need to * We need to clone the packet and deliver it to each listener.
* set d_len to zero so that the driver is aware that there
* is nothing to be sent.
*/ */
nwarn("WARNING: Packet not processed\n"); iob = netdev_iob_clone(dev, true);
dev->d_len = 0; if (iob == NULL)
ret = -EAGAIN; {
} 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) netdev_iob_replace(dev, iob);
{ udp = IPBUF(iplen);
udp_send(dev, conn); conn = nextconn;
} }
#endif
/* We can deliver the packet directly to the last listener. */
ret = udp_input_conn(dev, conn, udpiplen);
} }
else else
{ {