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 *conn,
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
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 */
}

View File

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