net/udp: fix the invaild udp destination address

If the udp socket not connected, it is possible to have
multi-different destination address in each iob entry,
update the remote address every time to avoid sent to the
incorrect destination.

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2021-12-17 14:35:51 +08:00 committed by Xiang Xiao
parent 26c7ec5087
commit b10dcf7c0d
6 changed files with 143 additions and 87 deletions

View File

@ -133,7 +133,7 @@ int psock_fstat(FAR struct socket *psock, FAR struct stat *buf)
* order to get the MTU and LL_HDRLEN:
*/
dev = udp_find_raddr_device(conn);
dev = udp_find_raddr_device(conn, NULL);
if (dev == NULL)
{
/* This should never happen except perhaps in some rare race

View File

@ -246,6 +246,22 @@ FAR struct udp_conn_s *udp_active(FAR struct net_driver_s *dev,
FAR struct udp_conn_s *udp_nextconn(FAR struct udp_conn_s *conn);
/****************************************************************************
* Name: udp_select_port
*
* Description:
* Select an unused port number.
*
* NOTE that in principle this function could fail if there is no available
* port number. There is no check for that case and it would actually
* in an infinite loop if that were the case. In this simple, small UDP
* implementation, it is reasonable to assume that that error cannot happen
* and that a port number will always be available.
*
****************************************************************************/
uint16_t udp_select_port(uint8_t domain, FAR union ip_binding_u *u);
/****************************************************************************
* Name: udp_bind
*
@ -610,7 +626,9 @@ FAR struct net_driver_s *udp_find_laddr_device(FAR struct udp_conn_s *conn);
*
****************************************************************************/
FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn);
FAR struct net_driver_s *
udp_find_raddr_device(FAR struct udp_conn_s *conn,
FAR struct sockaddr_storage *remote);
/****************************************************************************
* Name: udp_callback

View File

@ -172,76 +172,6 @@ static FAR struct udp_conn_s *udp_find_conn(uint8_t domain,
return NULL;
}
/****************************************************************************
* Name: udp_select_port
*
* Description:
* Select an unused port number.
*
* NOTE that in principle this function could fail if there is no available
* port number. There is no check for that case and it would actually
* in an infinite loop if that were the case. In this simple, small UDP
* implementation, it is reasonable to assume that that error cannot happen
* and that a port number will always be available.
*
* Input Parameters:
* None
*
* Returned Value:
* Next available port number
*
****************************************************************************/
static uint16_t udp_select_port(uint8_t domain, FAR union ip_binding_u *u)
{
static uint16_t g_last_udp_port;
uint16_t portno;
net_lock();
/* Generate port base dynamically */
if (g_last_udp_port == 0)
{
g_last_udp_port = clock_systime_ticks() % 32000;
if (g_last_udp_port < 4096)
{
g_last_udp_port += 4096;
}
}
/* Find an unused local port number. Loop until we find a valid
* listen port number that is not being used by any other connection.
*/
do
{
/* Guess that the next available port number will be the one after
* the last port number assigned.
*/
++g_last_udp_port;
/* Make sure that the port number is within range */
if (g_last_udp_port >= 32000)
{
g_last_udp_port = 4096;
}
}
while (udp_find_conn(domain, u, htons(g_last_udp_port)) != NULL);
/* Initialize and return the connection structure, bind it to the
* port number
*/
portno = g_last_udp_port;
net_unlock();
return portno;
}
/****************************************************************************
* Name: udp_ipv4_active
*
@ -527,6 +457,76 @@ static inline FAR struct udp_conn_s *
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: udp_select_port
*
* Description:
* Select an unused port number.
*
* NOTE that in principle this function could fail if there is no available
* port number. There is no check for that case and it would actually
* in an infinite loop if that were the case. In this simple, small UDP
* implementation, it is reasonable to assume that that error cannot happen
* and that a port number will always be available.
*
* Input Parameters:
* None
*
* Returned Value:
* Next available port number
*
****************************************************************************/
uint16_t udp_select_port(uint8_t domain, FAR union ip_binding_u *u)
{
static uint16_t g_last_udp_port;
uint16_t portno;
net_lock();
/* Generate port base dynamically */
if (g_last_udp_port == 0)
{
g_last_udp_port = clock_systime_ticks() % 32000;
if (g_last_udp_port < 4096)
{
g_last_udp_port += 4096;
}
}
/* Find an unused local port number. Loop until we find a valid
* listen port number that is not being used by any other connection.
*/
do
{
/* Guess that the next available port number will be the one after
* the last port number assigned.
*/
++g_last_udp_port;
/* Make sure that the port number is within range */
if (g_last_udp_port >= 32000)
{
g_last_udp_port = 4096;
}
}
while (udp_find_conn(domain, u, htons(g_last_udp_port)) != NULL);
/* Initialize and return the connection structure, bind it to the
* port number
*/
portno = g_last_udp_port;
net_unlock();
return portno;
}
/****************************************************************************
* Name: udp_initialize
*

View File

@ -184,7 +184,9 @@ FAR struct net_driver_s *udp_find_laddr_device(FAR struct udp_conn_s *conn)
*
****************************************************************************/
FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn)
FAR struct net_driver_s *
udp_find_raddr_device(FAR struct udp_conn_s *conn,
FAR struct sockaddr_storage *remote)
{
/* We need to select the device that is going to route the UDP packet
* based on the provided IP address.
@ -195,13 +197,25 @@ FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn)
if (conn->domain == PF_INET)
#endif
{
in_addr_t raddr;
if (remote)
{
FAR const struct sockaddr_in *inaddr =
(FAR const struct sockaddr_in *)remote;
net_ipv4addr_copy(raddr, inaddr->sin_addr.s_addr);
}
else
{
net_ipv4addr_copy(raddr, conn->u.ipv4.raddr);
}
/* Check if the remote, destination address is the broadcast
* or multicast address. If this is the case, select the device
* using the locally bound address (assuming that there is one).
*/
if (conn->u.ipv4.raddr == INADDR_BROADCAST ||
IN_MULTICAST(NTOHL(conn->u.ipv4.raddr)))
if (raddr == INADDR_BROADCAST || IN_MULTICAST(NTOHL(raddr)))
{
/* Make sure that the socket is bound to some non-zero, local
* address. Zero is used as an indication that the laddr is
@ -225,12 +239,12 @@ FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn)
* address.
*/
else if (conn->u.ipv4.raddr != INADDR_ANY)
else if (raddr != INADDR_ANY)
{
/* Normal lookup using the verified remote address */
return netdev_findby_ripv4addr(conn->u.ipv4.laddr,
conn->u.ipv4.raddr);
raddr);
}
else
{
@ -248,12 +262,25 @@ FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn)
else
#endif
{
net_ipv6addr_t raddr;
if (remote)
{
FAR const struct sockaddr_in6 *inaddr =
(FAR const struct sockaddr_in6 *)remote;
net_ipv6addr_copy(raddr, inaddr->sin6_addr.s6_addr16);
}
else
{
net_ipv6addr_copy(raddr, conn->u.ipv6.raddr);
}
/* Check if the remote, destination address is a multicast
* address. If this is the case, select the device
* using the locally bound address (assuming that there is one).
*/
if (net_is_addr_mcast(conn->u.ipv6.raddr))
if (net_is_addr_mcast(raddr))
{
/* Make sure that the socket is bound to some non-zero, local
* address. The IPv6 unspecified address is used as an
@ -278,12 +305,12 @@ FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn)
* address.
*/
else if (!net_ipv6addr_cmp(conn->u.ipv6.raddr, g_ipv6_unspecaddr))
else if (!net_ipv6addr_cmp(raddr, g_ipv6_unspecaddr))
{
/* Normal lookup using the verified remote address */
return netdev_findby_ripv6addr(conn->u.ipv6.laddr,
conn->u.ipv6.raddr);
raddr);
}
else
{

View File

@ -279,7 +279,6 @@ static int sendto_next_transfer(FAR struct socket *psock,
{
FAR struct udp_wrbuffer_s *wrb;
FAR struct net_driver_s *dev;
int ret;
/* Set the UDP "connection" to the destination address of the write buffer
* at the head of the queue.
@ -292,11 +291,15 @@ static int sendto_next_transfer(FAR struct socket *psock,
return -ENOENT;
}
ret = udp_connect(conn, (FAR const struct sockaddr *)&wrb->wb_dest);
if (ret < 0)
/* Has this address already been bound to a local port (lport)? */
if (!conn->lport)
{
nerr("ERROR: udp_connect failed: %d\n", ret);
return ret;
/* No.. Find an unused local port number and bind it to the
* connection structure.
*/
conn->lport = htons(udp_select_port(conn->domain, &conn->u));
}
/* Get the device that will handle the remote packet transfers. This
@ -308,7 +311,7 @@ static int sendto_next_transfer(FAR struct socket *psock,
* transmission could harm performance.
*/
dev = udp_find_raddr_device(conn);
dev = udp_find_raddr_device(conn, &wrb->wb_dest);
if (dev == NULL)
{
nerr("ERROR: udp_find_raddr_device failed\n");
@ -453,6 +456,14 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
wrb = (FAR struct udp_wrbuffer_s *)sq_peek(&conn->write_q);
DEBUGASSERT(wrb != NULL);
/* If the udp socket not connected, it is possible to have
* multi-different destination address in each iob entry,
* update the remote address every time to avoid sent to the
* incorrect destination.
*/
udp_connect(conn, (FAR const struct sockaddr *)&wrb->wb_dest);
/* Get the amount of data that we can send in the next packet.
* We will send either the remaining data in the buffer I/O
* buffer chain, or as much as will fit given the MSS and current

View File

@ -431,7 +431,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf,
* should never be NULL.
*/
state.st_dev = udp_find_raddr_device(conn);
state.st_dev = udp_find_raddr_device(conn, NULL);
if (state.st_dev == NULL)
{
nerr("ERROR: udp_find_raddr_device failed\n");