diff --git a/net/socket/net_fstat.c b/net/socket/net_fstat.c index 24af5b32761..7c75b8545c0 100644 --- a/net/socket/net_fstat.c +++ b/net/socket/net_fstat.c @@ -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 diff --git a/net/udp/udp.h b/net/udp/udp.h index 868ea6ddd3e..20832609061 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -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 diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index acb43779a5a..57b46f1015e 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -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 * diff --git a/net/udp/udp_finddev.c b/net/udp/udp_finddev.c index e0efac17bb0..3cacdb2bd49 100644 --- a/net/udp/udp_finddev.c +++ b/net/udp/udp_finddev.c @@ -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 { diff --git a/net/udp/udp_sendto_buffered.c b/net/udp/udp_sendto_buffered.c index 9f8840bba0d..4ab651a4039 100644 --- a/net/udp/udp_sendto_buffered.c +++ b/net/udp/udp_sendto_buffered.c @@ -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 diff --git a/net/udp/udp_sendto_unbuffered.c b/net/udp/udp_sendto_unbuffered.c index f9d73855a33..12d5f81cede 100644 --- a/net/udp/udp_sendto_unbuffered.c +++ b/net/udp/udp_sendto_unbuffered.c @@ -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");