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:
parent
408320f2ba
commit
806d783fd6
|
@ -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);
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue