From e75b5e9d86db281e22a97c58605aea5192c6035a Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Tue, 21 Jan 2020 15:50:39 +0800 Subject: [PATCH] net/tcp and udp: Move tcp/udp recvfrom into tcp/udp folder Move tcp/udp recvfrom into tcp/udp folder and remove inet_recvfrom.c --- net/inet/Make.defs | 4 +- net/inet/inet.h | 35 - net/inet/inet_sockif.c | 120 +++ net/tcp/Make.defs | 2 +- net/tcp/tcp.h | 25 + .../inet_recvfrom.c => tcp/tcp_recvfrom.c} | 708 ++---------------- net/udp/Make.defs | 2 + net/udp/udp.h | 24 + net/udp/udp_recvfrom.c | 707 +++++++++++++++++ 9 files changed, 930 insertions(+), 697 deletions(-) rename net/{inet/inet_recvfrom.c => tcp/tcp_recvfrom.c} (52%) create mode 100644 net/udp/udp_recvfrom.c diff --git a/net/inet/Make.defs b/net/inet/Make.defs index c27b728aed..6503ff9464 100644 --- a/net/inet/Make.defs +++ b/net/inet/Make.defs @@ -38,10 +38,10 @@ SOCK_CSRCS += inet_txdrain.c ifeq ($(CONFIG_NET_IPv4),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c +SOCK_CSRCS += inet_sockif.c SOCK_CSRCS += inet_globals.c else ifeq ($(CONFIG_NET_IPv6),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c +SOCK_CSRCS += inet_sockif.c SOCK_CSRCS += inet_globals.c endif diff --git a/net/inet/inet.h b/net/inet/inet.h index eef1f36986..81f923c905 100644 --- a/net/inet/inet.h +++ b/net/inet/inet.h @@ -240,41 +240,6 @@ int ipv6_getpeername(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen); #endif -/**************************************************************************** - * Name: inet_recvfrom - * - * Description: - * Implements the socket recvfrom interface for the case of the AF_INET - * and AF_INET6 address families. inet_recvfrom() receives messages from - * a socket, and may be used to receive data on a socket whether or not it - * is connection-oriented. - * - * If 'from' is not NULL, and the underlying protocol provides the source - * address, this source address is filled in. The argument 'fromlen' is - * initialized to the size of the buffer associated with from, and - * modified on return to indicate the actual size of the address stored - * there. - * - * Input Parameters: - * psock A pointer to a NuttX-specific, internal socket structure - * buf Buffer to receive data - * len Length of buffer - * flags Receive flags - * from Address of source (may be NULL) - * fromlen The length of the address structure - * - * Returned Value: - * On success, returns the number of characters received. If no data is - * available to be received and the peer has performed an orderly shutdown, - * recv() will return 0. Otherwise, on errors, a negated errno value is - * returned (see recvfrom() for the list of appropriate error values). - * - ****************************************************************************/ - -ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - int flags, FAR struct sockaddr *from, - FAR socklen_t *fromlen); - /**************************************************************************** * Name: inet_close * diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 5ce865f3b2..8a66b89919 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -90,6 +90,9 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, static ssize_t inet_sendfile(FAR struct socket *psock, FAR struct file *infile, FAR off_t *offset, size_t count); #endif +static ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); /**************************************************************************** * Private Data @@ -1274,6 +1277,123 @@ static ssize_t inet_sendfile(FAR struct socket *psock, } #endif +/**************************************************************************** + * Name: inet_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * and AF_INET6 address families. inet_recvfrom() receives messages from + * a socket, and may be used to receive data on a socket whether or not it + * is connection-oriented. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Input Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +static ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + ssize_t ret; + + /* If a 'from' address has been provided, verify that it is large + * enough to hold this address family. + */ + + if (from) + { + socklen_t minlen; + + /* Get the minimum socket length */ + + switch (psock->s_domain) + { +#ifdef CONFIG_NET_IPv4 + case PF_INET: + { + minlen = sizeof(struct sockaddr_in); + } + break; +#endif + +#ifdef CONFIG_NET_IPv6 + case PF_INET6: + { + minlen = sizeof(struct sockaddr_in6); + } + break; +#endif + + default: + DEBUGPANIC(); + return -EINVAL; + } + + if (*fromlen < minlen) + { + return -EINVAL; + } + } + + /* Read from the network interface driver buffer. + * Or perform the TCP/IP or UDP recv() operation. + */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef NET_TCP_HAVE_STACK + ret = psock_tcp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#ifdef NET_UDP_HAVE_STACK + ret = psock_udp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + { + nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); + ret = -ENOSYS; + } + break; + } + + return ret; +} + #endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** diff --git a/net/tcp/Make.defs b/net/tcp/Make.defs index 8b165daf56..595f137518 100644 --- a/net/tcp/Make.defs +++ b/net/tcp/Make.defs @@ -40,7 +40,7 @@ ifneq ($(CONFIG_NET_TCP_NO_STACK),y) # Socket layer -SOCK_CSRCS += tcp_connect.c tcp_accept.c +SOCK_CSRCS += tcp_connect.c tcp_accept.c tcp_recvfrom.c ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y) SOCK_CSRCS += tcp_send_buffered.c diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 93e3cf6b71..e1ae7669e1 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -1267,6 +1267,31 @@ int tcp_backlogdelete(FAR struct tcp_conn_s *conn, int psock_tcp_accept(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen, FAR void **newconn); + +/**************************************************************************** + * Name: psock_tcp_recvfrom + * + * Description: + * Perform the recvfrom operation for a TCP/IP SOCK_STREAM + * + * Input Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * buf Buffer to receive data + * len Length of buffer + * from INET address of source (may be NULL) + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *from, + FAR socklen_t *fromlen); + /**************************************************************************** * Name: psock_tcp_send * diff --git a/net/inet/inet_recvfrom.c b/net/tcp/tcp_recvfrom.c similarity index 52% rename from net/inet/inet_recvfrom.c rename to net/tcp/tcp_recvfrom.c index 7d56b54ebd..e3aa345d41 100644 --- a/net/inet/inet_recvfrom.c +++ b/net/tcp/tcp_recvfrom.c @@ -1,7 +1,7 @@ /**************************************************************************** - * net/inet/inet_recvfrom.c + * net/tcp/tcp_recvfrom.c * - * Copyright (C) 2007-2009, 2011-2018 Gregory Nutt. All rights reserved. + * Copyright (C) 2020 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -38,37 +38,23 @@ ****************************************************************************/ #include +#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET - -#include -#include -#include #include #include #include #include -#include - -#include -#include #include #include #include #include #include -#include #include "netdev/netdev.h" #include "devif/devif.h" #include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" -#include "inet/inet.h" /**************************************************************************** * Pre-processor Definitions @@ -77,9 +63,6 @@ #define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) #define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define UDPIPv4BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) -#define UDPIPv6BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) - #define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) #define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) @@ -87,8 +70,7 @@ * Private Types ****************************************************************************/ -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -struct inet_recvfrom_s +struct tcp_recvfrom_s { FAR struct socket *ir_sock; /* The parent socket structure */ FAR struct devif_callback_s *ir_cb; /* Reference to callback instance */ @@ -100,19 +82,17 @@ struct inet_recvfrom_s ssize_t ir_recvlen; /* The received length */ int ir_result; /* Success:OK, failure:negated errno */ }; -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: inet_update_recvlen + * Name: tcp_update_recvlen * * Description: * Update information about space available for new data and update size - * of data in buffer, This logic accounts for the case where - * inet_udp_readahead() sets state.ir_recvlen == -1 . + * of data in buffer. * * Input Parameters: * pstate recvfrom state structure @@ -123,10 +103,8 @@ struct inet_recvfrom_s * ****************************************************************************/ -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) - -static inline void inet_update_recvlen(FAR struct inet_recvfrom_s *pstate, - size_t recvlen) +static inline void tcp_update_recvlen(FAR struct tcp_recvfrom_s *pstate, + size_t recvlen) { if (pstate->ir_recvlen < 0) { @@ -137,10 +115,9 @@ static inline void inet_update_recvlen(FAR struct inet_recvfrom_s *pstate, pstate->ir_buffer += recvlen; pstate->ir_buflen -= recvlen; } -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_recvfrom_newdata + * Name: tcp_recvfrom_newdata * * Description: * Copy the read data from the packet @@ -157,9 +134,8 @@ static inline void inet_update_recvlen(FAR struct inet_recvfrom_s *pstate, * ****************************************************************************/ -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -static size_t inet_recvfrom_newdata(FAR struct net_driver_s *dev, - FAR struct inet_recvfrom_s *pstate) +static size_t tcp_recvfrom_newdata(FAR struct net_driver_s *dev, + FAR struct tcp_recvfrom_s *pstate) { size_t recvlen; @@ -181,14 +157,13 @@ static size_t inet_recvfrom_newdata(FAR struct net_driver_s *dev, /* Update the accumulated size of the data read */ - inet_update_recvlen(pstate, recvlen); + tcp_update_recvlen(pstate, recvlen); return recvlen; } -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_tcp_newdata + * Name: tcp_newdata * * Description: * Copy the read data from the packet @@ -205,13 +180,12 @@ static size_t inet_recvfrom_newdata(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef NET_TCP_HAVE_STACK -static inline void inet_tcp_newdata(FAR struct net_driver_s *dev, - FAR struct inet_recvfrom_s *pstate) +static inline void tcp_newdata(FAR struct net_driver_s *dev, + FAR struct tcp_recvfrom_s *pstate) { /* Take as much data from the packet as we can */ - size_t recvlen = inet_recvfrom_newdata(dev, pstate); + size_t recvlen = tcp_recvfrom_newdata(dev, pstate); /* If there is more data left in the packet that we could not buffer, then * add it to the read-ahead buffers. @@ -253,42 +227,9 @@ static inline void inet_tcp_newdata(FAR struct net_driver_s *dev, dev->d_len = 0; } -#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_udp_newdata - * - * Description: - * Copy the read data from the packet - * - * Input Parameters: - * dev The sructure of the network driver that generated the event - * pstate recvfrom state structure - * - * Returned Value: - * None. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static inline void inet_udp_newdata(FAR struct net_driver_s *dev, - FAR struct inet_recvfrom_s *pstate) -{ - /* Take as much data from the packet as we can */ - - inet_recvfrom_newdata(dev, pstate); - - /* Indicate no data in the buffer */ - - dev->d_len = 0; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: inet_tcp_readahead and inet_udp_readahead + * Name: tcp_readahead * * Description: * Copy the read-ahead data from the packet @@ -304,8 +245,7 @@ static inline void inet_udp_newdata(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef NET_TCP_HAVE_STACK -static inline void inet_tcp_readahead(struct inet_recvfrom_s *pstate) +static inline void tcp_readahead(struct tcp_recvfrom_s *pstate) { FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->ir_sock->s_conn; FAR struct iob_s *iob; @@ -329,7 +269,7 @@ static inline void inet_tcp_readahead(struct inet_recvfrom_s *pstate) /* Update the accumulated size of the data read */ - inet_update_recvlen(pstate, recvlen); + tcp_update_recvlen(pstate, recvlen); /* If we took all of the data from the I/O buffer chain is empty, then * release it. If there is still data available in the I/O buffer @@ -365,98 +305,9 @@ static inline void inet_tcp_readahead(struct inet_recvfrom_s *pstate) } } } -#endif /* NET_TCP_HAVE_STACK */ - -#ifdef NET_UDP_HAVE_STACK - -static inline void inet_udp_readahead(struct inet_recvfrom_s *pstate) -{ - FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; - FAR struct iob_s *iob; - int recvlen; - - /* Check there is any UDP datagram already buffered in a read-ahead - * buffer. - */ - - pstate->ir_recvlen = -1; - - if ((iob = iob_peek_queue(&conn->readahead)) != NULL) - { - FAR struct iob_s *tmp; - uint8_t src_addr_size; - - DEBUGASSERT(iob->io_pktlen > 0); - - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), 0); - if (recvlen != sizeof(uint8_t)) - { - goto out; - } - - if (0 -#ifdef CONFIG_NET_IPv6 - || src_addr_size == sizeof(struct sockaddr_in6) -#endif -#ifdef CONFIG_NET_IPv4 - || src_addr_size == sizeof(struct sockaddr_in) -#endif - ) - { - if (pstate->ir_from) - { - socklen_t len = *pstate->ir_fromlen; - len = (socklen_t)src_addr_size > len ? len : (socklen_t)src_addr_size; - - recvlen = iob_copyout((FAR uint8_t *)pstate->ir_from, iob, - len, sizeof(uint8_t)); - if (recvlen != len) - { - goto out; - } - } - } - - if (pstate->ir_buflen > 0) - { - recvlen = iob_copyout(pstate->ir_buffer, iob, pstate->ir_buflen, - src_addr_size + sizeof(uint8_t)); - - ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); - - /* Update the accumulated size of the data read */ - - pstate->ir_recvlen = recvlen; - pstate->ir_buffer += recvlen; - pstate->ir_buflen -= recvlen; - } - else - { - pstate->ir_recvlen = 0; - } - -out: - /* Remove the I/O buffer chain from the head of the read-ahead - * buffer queue. - */ - - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); - - /* And free the I/O buffer chain */ - - iob_free_chain(iob, IOBUSER_NET_UDP_READAHEAD); - } -} -#endif /**************************************************************************** - * Name: inet_tcp_sender + * Name: tcp_sender * * Description: * Getting the sender's address from the UDP packet @@ -473,9 +324,8 @@ out: * ****************************************************************************/ -#ifdef NET_TCP_HAVE_STACK -static inline void inet_tcp_sender(FAR struct net_driver_s *dev, - FAR struct inet_recvfrom_s *pstate) +static inline void tcp_sender(FAR struct net_driver_s *dev, + FAR struct tcp_recvfrom_s *pstate) { /* Get the family from the packet type, IP address from the IP header, and * the port number from the TCP header. @@ -524,10 +374,9 @@ static inline void inet_tcp_sender(FAR struct net_driver_s *dev, } #endif /* CONFIG_NET_IPv4 */ } -#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_tcp_eventhandler + * Name: tcp_eventhandler * * Description: * This function is called with the network locked to perform the actual @@ -546,12 +395,11 @@ static inline void inet_tcp_sender(FAR struct net_driver_s *dev, * ****************************************************************************/ -#ifdef NET_TCP_HAVE_STACK -static uint16_t inet_tcp_eventhandler(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) +static uint16_t tcp_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) { - FAR struct inet_recvfrom_s *pstate = (struct inet_recvfrom_s *)pvpriv; + FAR struct tcp_recvfrom_s *pstate = (struct tcp_recvfrom_s *)pvpriv; #if 0 /* REVISIT: The assertion fires. Why? */ FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; @@ -581,11 +429,11 @@ static uint16_t inet_tcp_eventhandler(FAR struct net_driver_s *dev, * packet in the read-ahead buffer). */ - inet_tcp_newdata(dev, pstate); + tcp_newdata(dev, pstate); /* Save the sender's address in the caller's 'from' location */ - inet_tcp_sender(dev, pstate); + tcp_sender(dev, pstate); /* Indicate that the data has been consumed and that an ACK * should be sent. @@ -672,223 +520,9 @@ static uint16_t inet_tcp_eventhandler(FAR struct net_driver_s *dev, return flags; } -#endif /* NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_udp_sender - * - * Description: - * Getting the sender's address from the UDP packet - * - * Input Parameters: - * dev - The device driver data structure - * pstate - the recvfrom state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static inline void inet_udp_sender(FAR struct net_driver_s *dev, - FAR struct inet_recvfrom_s *pstate) -{ - /* Get the family from the packet type, IP address from the IP header, and - * the port number from the UDP header. - */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - if (IFF_IS_IPv6(dev->d_flags)) -#endif - { - FAR struct sockaddr_in6 *infrom = - (FAR struct sockaddr_in6 *)pstate->ir_from; - FAR socklen_t *fromlen = pstate->ir_fromlen; - - if (infrom) - { - FAR struct udp_hdr_s *udp = UDPIPv6BUF; - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - - infrom->sin6_family = AF_INET6; - infrom->sin6_port = udp->srcport; - *fromlen = sizeof(struct sockaddr_in6); - - net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); - } - } -#endif /* CONFIG_NET_IPv6 */ - -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - else -#endif - { - FAR struct sockaddr_in *infrom = - (FAR struct sockaddr_in *)pstate->ir_from; - - if (infrom) - { -#ifdef CONFIG_NET_IPv6 - FAR struct udp_conn_s *conn = - (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; - - /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special - * class of addresses, the IPv4-mapped IPv6 addresses. - */ - - if (conn->domain == PF_INET6) - { - FAR struct sockaddr_in6 *infrom6 = (FAR struct sockaddr_in6 *)infrom; - FAR socklen_t *fromlen = pstate->ir_fromlen; - FAR struct udp_hdr_s *udp = UDPIPv6BUF; - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - in_addr_t ipv4addr; - - /* Encode the IPv4 address as an IPv4-mapped IPv6 address */ - - infrom6->sin6_family = AF_INET6; - infrom6->sin6_port = udp->srcport; - *fromlen = sizeof(struct sockaddr_in6); - - ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr); - ip6_map_ipv4addr(ipv4addr, infrom6->sin6_addr.s6_addr16); - } - else -#endif - { - FAR struct udp_hdr_s *udp = UDPIPv4BUF; - FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - - infrom->sin_family = AF_INET; - infrom->sin_port = udp->srcport; - - net_ipv4addr_copy(infrom->sin_addr.s_addr, - net_ip4addr_conv32(ipv4->srcipaddr)); - } - } - } -#endif /* CONFIG_NET_IPv4 */ -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: inet_udp_terminate - * - * Description: - * Terminate the UDP transfer. - * - * Input Parameters: - * pstate - The recvfrom state structure - * result - The result of the operation - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static void inet_udp_terminate(FAR struct inet_recvfrom_s *pstate, int result) -{ - /* Don't allow any further UDP call backs. */ - - pstate->ir_cb->flags = 0; - pstate->ir_cb->priv = NULL; - pstate->ir_cb->event = NULL; - - /* Save the result of the transfer */ - - pstate->ir_result = result; - - /* Wake up the waiting thread, returning the number of bytes - * actually read. - */ - - nxsem_post(&pstate->ir_sem); -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: inet_udp_eventhandler - * - * Description: - * This function is called with the network locked to perform the actual - * UDP receive operation via by the lower, device interfacing layer. - * - * Input Parameters: - * dev The structure of the network driver that generated the event. - * pvconn The connection structure associated with the socket - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static uint16_t inet_udp_eventhandler(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ - FAR struct inet_recvfrom_s *pstate = (FAR struct inet_recvfrom_s *)pvpriv; - - ninfo("flags: %04x\n", flags); - - /* 'priv' might be null in some race conditions (?) */ - - if (pstate) - { - /* If the network device has gone down, then we will have terminate - * the wait now with an error. - */ - - if ((flags & NETDEV_DOWN) != 0) - { - /* Terminate the transfer with an error. */ - - nerr("ERROR: Network is down\n"); - inet_udp_terminate(pstate, -ENETUNREACH); - } - - /* If new data is available, then complete the read action. */ - - else if ((flags & UDP_NEWDATA) != 0) - { - /* Copy the data from the packet */ - - inet_udp_newdata(dev, pstate); - - /* We are finished. */ - - ninfo("UDP done\n"); - - /* Save the sender's address in the caller's 'from' location */ - - inet_udp_sender(dev, pstate); - - /* Don't allow any further UDP call backs. */ - - inet_udp_terminate(pstate, OK); - - /* Indicate that the data has been consumed */ - - flags &= ~UDP_NEWDATA; - } - } - - return flags; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: inet_recvfrom_initialize + * Name: tcp_recvfrom_initialize * * Description: * Initialize the state structure @@ -906,15 +540,14 @@ static uint16_t inet_udp_eventhandler(FAR struct net_driver_s *dev, * ****************************************************************************/ -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -static void inet_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, - size_t len, FAR struct sockaddr *infrom, - FAR socklen_t *fromlen, - FAR struct inet_recvfrom_s *pstate) +static void tcp_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *infrom, + FAR socklen_t *fromlen, + FAR struct tcp_recvfrom_s *pstate) { /* Initialize the state structure. */ - memset(pstate, 0, sizeof(struct inet_recvfrom_s)); + memset(pstate, 0, sizeof(struct tcp_recvfrom_s)); /* This semaphore is used for signaling and, hence, should not have * priority inheritance enabled. @@ -937,12 +570,10 @@ static void inet_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, * semaphore. */ -#define inet_recvfrom_uninitialize(s) nxsem_destroy(&(s)->ir_sem) - -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ +#define tcp_recvfrom_uninitialize(s) nxsem_destroy(&(s)->ir_sem) /**************************************************************************** - * Name: inet_recvfrom_result + * Name: tcp_recvfrom_result * * Description: * Evaluate the result of the recv operations @@ -958,8 +589,7 @@ static void inet_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, * ****************************************************************************/ -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -static ssize_t inet_recvfrom_result(int result, struct inet_recvfrom_s *pstate) +static ssize_t tcp_recvfrom_result(int result, struct tcp_recvfrom_s *pstate) { /* Check for a error/timeout detected by the event handler. Errors are * signaled by negative errno values for the rcv length @@ -985,131 +615,13 @@ static ssize_t inet_recvfrom_result(int result, struct inet_recvfrom_s *pstate) return pstate->ir_recvlen; } -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ /**************************************************************************** - * Name: inet_udp_recvfrom - * - * Description: - * Perform the recvfrom operation for a UDP SOCK_DGRAM - * - * Input Parameters: - * psock Pointer to the socket structure for the SOCK_DRAM socket - * buf Buffer to receive data - * len Length of buffer - * from INET address of source (may be NULL) - * - * Returned Value: - * On success, returns the number of characters received. On error, - * -errno is returned (see recvfrom for list of errnos). - * - * Assumptions: - * + * Public Functions ****************************************************************************/ -#ifdef NET_UDP_HAVE_STACK -static ssize_t inet_udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - FAR struct sockaddr *from, FAR socklen_t *fromlen) -{ - FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)psock->s_conn; - FAR struct net_driver_s *dev; - struct inet_recvfrom_s state; - int ret; - - /* Perform the UDP recvfrom() operation */ - - /* Initialize the state structure. This is done with the network locked - * because we don't want anything to happen until we are ready. - */ - - net_lock(); - inet_recvfrom_initialize(psock, buf, len, from, fromlen, &state); - - /* Copy the read-ahead data from the packet */ - - inet_udp_readahead(&state); - - /* The default return value is the number of bytes that we just copied - * into the user buffer. We will return this if the socket has become - * disconnected or if the user request was completely satisfied with - * data from the readahead buffers. - */ - - ret = state.ir_recvlen; - - /* Handle non-blocking UDP sockets */ - - if (_SS_ISNONBLOCK(psock->s_flags)) - { - /* Return the number of bytes read from the read-ahead buffer if - * something was received (already in 'ret'); EAGAIN if not. - */ - - if (ret < 0) - { - /* Nothing was received */ - - ret = -EAGAIN; - } - } - - /* It is okay to block if we need to. If there is space to receive anything - * more, then we will wait to receive the data. Otherwise return the number - * of bytes read from the read-ahead buffer (already in 'ret'). - * - * NOTE: that inet_udp_readahead() may set state.ir_recvlen == -1. - */ - - else if (state.ir_recvlen <= 0) - { - /* Get the device that will handle the packet transfers. This may be - * NULL if the UDP socket is bound to INADDR_ANY. In that case, no - * NETDEV_DOWN notifications will be received. - */ - - dev = udp_find_laddr_device(conn); - - /* Set up the callback in the connection */ - - state.ir_cb = udp_callback_alloc(dev, conn); - if (state.ir_cb) - { - /* Set up the callback in the connection */ - - state.ir_cb->flags = (UDP_NEWDATA | NETDEV_DOWN); - state.ir_cb->priv = (FAR void *)&state; - state.ir_cb->event = inet_udp_eventhandler; - - /* Wait for either the receive to complete or for an error/timeout - * to occur. net_timedwait will also terminate if a signal is - * received. - */ - - ret = net_timedwait(&state. ir_sem, _SO_TIMEOUT(psock->s_rcvtimeo)); - if (ret == -ETIMEDOUT) - { - ret = -EAGAIN; - } - - /* Make sure that no further events are processed */ - - udp_callback_free(dev, conn, state.ir_cb); - ret = inet_recvfrom_result(ret, &state); - } - else - { - ret = -EBUSY; - } - } - - net_unlock(); - inet_recvfrom_uninitialize(&state); - return ret; -} -#endif /* NET_UDP_HAVE_STACK */ - /**************************************************************************** - * Name: inet_tcp_recvfrom + * Name: psock_tcp_recvfrom * * Description: * Perform the recvfrom operation for a TCP/IP SOCK_STREAM @@ -1128,11 +640,11 @@ static ssize_t inet_udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t * ****************************************************************************/ -#ifdef NET_TCP_HAVE_STACK -static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - FAR struct sockaddr *from, FAR socklen_t *fromlen) +ssize_t psock_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *from, + FAR socklen_t *fromlen) { - struct inet_recvfrom_s state; + struct tcp_recvfrom_s state; int ret; /* Initialize the state structure. This is done with the network locked @@ -1140,14 +652,14 @@ static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t */ net_lock(); - inet_recvfrom_initialize(psock, buf, len, from, fromlen, &state); + tcp_recvfrom_initialize(psock, buf, len, from, fromlen, &state); /* Handle any any TCP data already buffered in a read-ahead buffer. NOTE * that there may be read-ahead data to be retrieved even after the * socket has been disconnected. */ - inet_tcp_readahead(&state); + tcp_readahead(&state); /* The default return value is the number of bytes that we just copied * into the user buffer. We will return this if the socket has become @@ -1232,7 +744,7 @@ static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t { state.ir_cb->flags = (TCP_NEWDATA | TCP_DISCONN_EVENTS); state.ir_cb->priv = (FAR void *)&state; - state.ir_cb->event = inet_tcp_eventhandler; + state.ir_cb->event = tcp_eventhandler; /* Wait for either the receive to complete or for an error/timeout * to occur. net_timedwait will also terminate if a signal isi @@ -1248,7 +760,7 @@ static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t /* Make sure that no further events are processed */ tcp_callback_free(conn, state.ir_cb); - ret = inet_recvfrom_result(ret, &state); + ret = tcp_recvfrom_result(ret, &state); } else { @@ -1257,130 +769,8 @@ static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t } net_unlock(); - inet_recvfrom_uninitialize(&state); + tcp_recvfrom_uninitialize(&state); return (ssize_t)ret; } -#endif /* NET_TCP_HAVE_STACK */ -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: inet_recvfrom - * - * Description: - * Implements the socket recvfrom interface for the case of the AF_INET - * and AF_INET6 address families. inet_recvfrom() receives messages from - * a socket, and may be used to receive data on a socket whether or not it - * is connection-oriented. - * - * If 'from' is not NULL, and the underlying protocol provides the source - * address, this source address is filled in. The argument 'fromlen' is - * initialized to the size of the buffer associated with from, and - * modified on return to indicate the actual size of the address stored - * there. - * - * Input Parameters: - * psock A pointer to a NuttX-specific, internal socket structure - * buf Buffer to receive data - * len Length of buffer - * flags Receive flags - * from Address of source (may be NULL) - * fromlen The length of the address structure - * - * Returned Value: - * On success, returns the number of characters received. If no data is - * available to be received and the peer has performed an orderly shutdown, - * recv() will return 0. Otherwise, on errors, a negated errno value is - * returned (see recvfrom() for the list of appropriate error values). - * - ****************************************************************************/ - -ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - int flags, FAR struct sockaddr *from, - FAR socklen_t *fromlen) -{ - ssize_t ret; - - /* If a 'from' address has been provided, verify that it is large - * enough to hold this address family. - */ - - if (from) - { - socklen_t minlen; - - /* Get the minimum socket length */ - - switch (psock->s_domain) - { -#ifdef CONFIG_NET_IPv4 - case PF_INET: - { - minlen = sizeof(struct sockaddr_in); - } - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case PF_INET6: - { - minlen = sizeof(struct sockaddr_in6); - } - break; -#endif - - default: - DEBUGPANIC(); - return -EINVAL; - } - - if (*fromlen < minlen) - { - return -EINVAL; - } - } - - /* Read from the network interface driver buffer. - * Or perform the TCP/IP or UDP recv() operation. - */ - - switch (psock->s_type) - { -#ifdef CONFIG_NET_TCP - case SOCK_STREAM: - { -#ifdef NET_TCP_HAVE_STACK - ret = inet_tcp_recvfrom(psock, buf, len, from, fromlen); -#else - ret = -ENOSYS; -#endif - } - break; #endif /* CONFIG_NET_TCP */ - -#ifdef CONFIG_NET_UDP - case SOCK_DGRAM: - { -#ifdef NET_UDP_HAVE_STACK - ret = inet_udp_recvfrom(psock, buf, len, from, fromlen); -#else - ret = -ENOSYS; -#endif - } - break; -#endif /* CONFIG_NET_UDP */ - - default: - { - nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); - ret = -ENOSYS; - } - break; - } - - return ret; -} - -#endif /* CONFIG_NET */ diff --git a/net/udp/Make.defs b/net/udp/Make.defs index 1c9487a558..39ca2939d9 100644 --- a/net/udp/Make.defs +++ b/net/udp/Make.defs @@ -40,6 +40,8 @@ ifneq ($(CONFIG_NET_UDP_NO_STACK),y) # Socket layer +SOCK_CSRCS += udp_recvfrom.c + ifeq ($(CONFIG_NET_UDPPROTO_OPTIONS),y) SOCK_CSRCS += udp_setsockopt.c endif diff --git a/net/udp/udp.h b/net/udp/udp.h index 40d3a97a99..454b29f177 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -637,6 +637,30 @@ FAR struct net_driver_s *udp_find_raddr_device(FAR struct udp_conn_s *conn); uint16_t udp_callback(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn, uint16_t flags); +/**************************************************************************** + * Name: psock_udp_recvfrom + * + * Description: + * Perform the recvfrom operation for a UDP SOCK_DGRAM + * + * Input Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * buf Buffer to receive data + * len Length of buffer + * from INET address of source (may be NULL) + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t psock_udp_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *from, + FAR socklen_t *fromlen); + /**************************************************************************** * Name: psock_udp_sendto * diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c new file mode 100644 index 0000000000..30601621dc --- /dev/null +++ b/net/udp/udp_recvfrom.c @@ -0,0 +1,707 @@ +/**************************************************************************** + * net/udp/udp_recvfrom.c + * + * Copyright (C) 2020 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "udp/udp.h" +#include "socket/socket.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) + +#define UDPIPv4BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) +#define UDPIPv6BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct udp_recvfrom_s +{ + FAR struct socket *ir_sock; /* The parent socket structure */ + FAR struct devif_callback_s *ir_cb; /* Reference to callback instance */ + sem_t ir_sem; /* Semaphore signals recv completion */ + size_t ir_buflen; /* Length of receive buffer */ + uint8_t *ir_buffer; /* Pointer to receive buffer */ + FAR struct sockaddr *ir_from; /* Address of sender */ + FAR socklen_t *ir_fromlen; /* Number of bytes allocated for address of sender */ + ssize_t ir_recvlen; /* The received length */ + int ir_result; /* Success:OK, failure:negated errno */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: udp_update_recvlen + * + * Description: + * Update information about space available for new data and update size + * of data in buffer, This logic accounts for the case where + * udp_readahead() sets state.ir_recvlen == -1 . + * + * Input Parameters: + * pstate recvfrom state structure + * recvlen size of new data appended to buffer + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void udp_update_recvlen(FAR struct udp_recvfrom_s *pstate, + size_t recvlen) +{ + if (pstate->ir_recvlen < 0) + { + pstate->ir_recvlen = 0; + } + + pstate->ir_recvlen += recvlen; + pstate->ir_buffer += recvlen; + pstate->ir_buflen -= recvlen; +} + +/**************************************************************************** + * Name: udp_recvfrom_newdata + * + * Description: + * Copy the read data from the packet + * + * Input Parameters: + * dev The structure of the network driver that generated the event. + * pstate recvfrom state structure + * + * Returned Value: + * The number of bytes taken from the packet. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev, + FAR struct udp_recvfrom_s *pstate) +{ + size_t recvlen; + + /* Get the length of the data to return */ + + if (dev->d_len > pstate->ir_buflen) + { + recvlen = pstate->ir_buflen; + } + else + { + recvlen = dev->d_len; + } + + /* Copy the new appdata into the user buffer */ + + memcpy(pstate->ir_buffer, dev->d_appdata, recvlen); + ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); + + /* Update the accumulated size of the data read */ + + udp_update_recvlen(pstate, recvlen); + + return recvlen; +} + +/**************************************************************************** + * Name: udp_newdata + * + * Description: + * Copy the read data from the packet + * + * Input Parameters: + * dev The sructure of the network driver that generated the event + * pstate recvfrom state structure + * + * Returned Value: + * None. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static inline void udp_newdata(FAR struct net_driver_s *dev, + FAR struct udp_recvfrom_s *pstate) +{ + /* Take as much data from the packet as we can */ + + udp_recvfrom_newdata(dev, pstate); + + /* Indicate no data in the buffer */ + + dev->d_len = 0; +} + +static inline void udp_readahead(struct udp_recvfrom_s *pstate) +{ + FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; + FAR struct iob_s *iob; + int recvlen; + + /* Check there is any UDP datagram already buffered in a read-ahead + * buffer. + */ + + pstate->ir_recvlen = -1; + + if ((iob = iob_peek_queue(&conn->readahead)) != NULL) + { + FAR struct iob_s *tmp; + uint8_t src_addr_size; + + DEBUGASSERT(iob->io_pktlen > 0); + + /* Transfer that buffered data from the I/O buffer chain into + * the user buffer. + */ + + recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), 0); + if (recvlen != sizeof(uint8_t)) + { + goto out; + } + + if (0 +#ifdef CONFIG_NET_IPv6 + || src_addr_size == sizeof(struct sockaddr_in6) +#endif +#ifdef CONFIG_NET_IPv4 + || src_addr_size == sizeof(struct sockaddr_in) +#endif + ) + { + if (pstate->ir_from) + { + socklen_t len = *pstate->ir_fromlen; + len = (socklen_t)src_addr_size > len ? len : (socklen_t)src_addr_size; + + recvlen = iob_copyout((FAR uint8_t *)pstate->ir_from, iob, + len, sizeof(uint8_t)); + if (recvlen != len) + { + goto out; + } + } + } + + if (pstate->ir_buflen > 0) + { + recvlen = iob_copyout(pstate->ir_buffer, iob, pstate->ir_buflen, + src_addr_size + sizeof(uint8_t)); + + ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); + + /* Update the accumulated size of the data read */ + + pstate->ir_recvlen = recvlen; + pstate->ir_buffer += recvlen; + pstate->ir_buflen -= recvlen; + } + else + { + pstate->ir_recvlen = 0; + } + +out: + /* Remove the I/O buffer chain from the head of the read-ahead + * buffer queue. + */ + + tmp = iob_remove_queue(&conn->readahead); + DEBUGASSERT(tmp == iob); + UNUSED(tmp); + + /* And free the I/O buffer chain */ + + iob_free_chain(iob, IOBUSER_NET_UDP_READAHEAD); + } +} + +/**************************************************************************** + * Name: udp_sender + * + * Description: + * Getting the sender's address from the UDP packet + * + * Input Parameters: + * dev - The device driver data structure + * pstate - the recvfrom state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static inline void udp_sender(FAR struct net_driver_s *dev, + FAR struct udp_recvfrom_s *pstate) +{ + /* Get the family from the packet type, IP address from the IP header, and + * the port number from the UDP header. + */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv6(dev->d_flags)) +#endif + { + FAR struct sockaddr_in6 *infrom = + (FAR struct sockaddr_in6 *)pstate->ir_from; + FAR socklen_t *fromlen = pstate->ir_fromlen; + + if (infrom) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF; + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + + infrom->sin6_family = AF_INET6; + infrom->sin6_port = udp->srcport; + *fromlen = sizeof(struct sockaddr_in6); + + net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); + } + } +#endif /* CONFIG_NET_IPv6 */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + else +#endif + { + FAR struct sockaddr_in *infrom = + (FAR struct sockaddr_in *)pstate->ir_from; + + if (infrom) + { +#ifdef CONFIG_NET_IPv6 + FAR struct udp_conn_s *conn = + (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; + + /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special + * class of addresses, the IPv4-mapped IPv6 addresses. + */ + + if (conn->domain == PF_INET6) + { + FAR struct sockaddr_in6 *infrom6 = (FAR struct sockaddr_in6 *)infrom; + FAR socklen_t *fromlen = pstate->ir_fromlen; + FAR struct udp_hdr_s *udp = UDPIPv6BUF; + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + in_addr_t ipv4addr; + + /* Encode the IPv4 address as an IPv4-mapped IPv6 address */ + + infrom6->sin6_family = AF_INET6; + infrom6->sin6_port = udp->srcport; + *fromlen = sizeof(struct sockaddr_in6); + + ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr); + ip6_map_ipv4addr(ipv4addr, infrom6->sin6_addr.s6_addr16); + } + else +#endif + { + FAR struct udp_hdr_s *udp = UDPIPv4BUF; + FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; + + infrom->sin_family = AF_INET; + infrom->sin_port = udp->srcport; + + net_ipv4addr_copy(infrom->sin_addr.s_addr, + net_ip4addr_conv32(ipv4->srcipaddr)); + } + } + } +#endif /* CONFIG_NET_IPv4 */ +} + +/**************************************************************************** + * Name: udp_terminate + * + * Description: + * Terminate the UDP transfer. + * + * Input Parameters: + * pstate - The recvfrom state structure + * result - The result of the operation + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void udp_terminate(FAR struct udp_recvfrom_s *pstate, int result) +{ + /* Don't allow any further UDP call backs. */ + + pstate->ir_cb->flags = 0; + pstate->ir_cb->priv = NULL; + pstate->ir_cb->event = NULL; + + /* Save the result of the transfer */ + + pstate->ir_result = result; + + /* Wake up the waiting thread, returning the number of bytes + * actually read. + */ + + nxsem_post(&pstate->ir_sem); +} + +/**************************************************************************** + * Name: udp_eventhandler + * + * Description: + * This function is called with the network locked to perform the actual + * UDP receive operation via by the lower, device interfacing layer. + * + * Input Parameters: + * dev The structure of the network driver that generated the event. + * pvconn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static uint16_t udp_eventhandler(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + FAR struct udp_recvfrom_s *pstate = (FAR struct udp_recvfrom_s *)pvpriv; + + ninfo("flags: %04x\n", flags); + + /* 'priv' might be null in some race conditions (?) */ + + if (pstate) + { + /* If the network device has gone down, then we will have terminate + * the wait now with an error. + */ + + if ((flags & NETDEV_DOWN) != 0) + { + /* Terminate the transfer with an error. */ + + nerr("ERROR: Network is down\n"); + udp_terminate(pstate, -ENETUNREACH); + } + + /* If new data is available, then complete the read action. */ + + else if ((flags & UDP_NEWDATA) != 0) + { + /* Copy the data from the packet */ + + udp_newdata(dev, pstate); + + /* We are finished. */ + + ninfo("UDP done\n"); + + /* Save the sender's address in the caller's 'from' location */ + + udp_sender(dev, pstate); + + /* Don't allow any further UDP call backs. */ + + udp_terminate(pstate, OK); + + /* Indicate that the data has been consumed */ + + flags &= ~UDP_NEWDATA; + } + } + + return flags; +} + +/**************************************************************************** + * Name: udp_recvfrom_initialize + * + * Description: + * Initialize the state structure + * + * Input Parameters: + * psock Pointer to the socket structure for the socket + * buf Buffer to receive data + * len Length of buffer + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void udp_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *infrom, + FAR socklen_t *fromlen, + FAR struct udp_recvfrom_s *pstate) +{ + /* Initialize the state structure. */ + + memset(pstate, 0, sizeof(struct udp_recvfrom_s)); + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + nxsem_init(&pstate->ir_sem, 0, 0); /* Doesn't really fail */ + nxsem_setprotocol(&pstate->ir_sem, SEM_PRIO_NONE); + + pstate->ir_buflen = len; + pstate->ir_buffer = buf; + pstate->ir_from = infrom; + pstate->ir_fromlen = fromlen; + + /* Set up the start time for the timeout */ + + pstate->ir_sock = psock; +} + +/* The only un-initialization that has to be performed is destroying the + * semaphore. + */ + +#define udp_recvfrom_uninitialize(s) nxsem_destroy(&(s)->ir_sem) + +/**************************************************************************** + * Name: udp_recvfrom_result + * + * Description: + * Evaluate the result of the recv operations + * + * Input Parameters: + * result The result of the net_timedwait operation (may indicate EINTR) + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * The result of the recv operation with errno set appropriately + * + * Assumptions: + * + ****************************************************************************/ + +static ssize_t udp_recvfrom_result(int result, struct udp_recvfrom_s *pstate) +{ + /* Check for a error/timeout detected by the event handler. Errors are + * signaled by negative errno values for the rcv length + */ + + if (pstate->ir_result < 0) + { + /* This might return EAGAIN on a timeout or ENOTCONN on loss of + * connection (TCP only) + */ + + return pstate->ir_result; + } + + /* If net_timedwait failed, then we were probably reawakened by a signal. In + * this case, net_timedwait will have returned negated errno appropriately. + */ + + if (result < 0) + { + return result; + } + + return pstate->ir_recvlen; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_udp_recvfrom + * + * Description: + * Perform the recvfrom operation for a UDP SOCK_DGRAM + * + * Input Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * buf Buffer to receive data + * len Length of buffer + * from INET address of source (may be NULL) + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t psock_udp_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)psock->s_conn; + FAR struct net_driver_s *dev; + struct udp_recvfrom_s state; + int ret; + + /* Perform the UDP recvfrom() operation */ + + /* Initialize the state structure. This is done with the network locked + * because we don't want anything to happen until we are ready. + */ + + net_lock(); + udp_recvfrom_initialize(psock, buf, len, from, fromlen, &state); + + /* Copy the read-ahead data from the packet */ + + udp_readahead(&state); + + /* The default return value is the number of bytes that we just copied + * into the user buffer. We will return this if the socket has become + * disconnected or if the user request was completely satisfied with + * data from the readahead buffers. + */ + + ret = state.ir_recvlen; + + /* Handle non-blocking UDP sockets */ + + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Return the number of bytes read from the read-ahead buffer if + * something was received (already in 'ret'); EAGAIN if not. + */ + + if (ret < 0) + { + /* Nothing was received */ + + ret = -EAGAIN; + } + } + + /* It is okay to block if we need to. If there is space to receive anything + * more, then we will wait to receive the data. Otherwise return the number + * of bytes read from the read-ahead buffer (already in 'ret'). + * + * NOTE: that udp_readahead() may set state.ir_recvlen == -1. + */ + + else if (state.ir_recvlen <= 0) + { + /* Get the device that will handle the packet transfers. This may be + * NULL if the UDP socket is bound to INADDR_ANY. In that case, no + * NETDEV_DOWN notifications will be received. + */ + + dev = udp_find_laddr_device(conn); + + /* Set up the callback in the connection */ + + state.ir_cb = udp_callback_alloc(dev, conn); + if (state.ir_cb) + { + /* Set up the callback in the connection */ + + state.ir_cb->flags = (UDP_NEWDATA | NETDEV_DOWN); + state.ir_cb->priv = (FAR void *)&state; + state.ir_cb->event = udp_eventhandler; + + /* Wait for either the receive to complete or for an error/timeout + * to occur. net_timedwait will also terminate if a signal is + * received. + */ + + ret = net_timedwait(&state. ir_sem, _SO_TIMEOUT(psock->s_rcvtimeo)); + if (ret == -ETIMEDOUT) + { + ret = -EAGAIN; + } + + /* Make sure that no further events are processed */ + + udp_callback_free(dev, conn, state.ir_cb); + ret = udp_recvfrom_result(ret, &state); + } + else + { + ret = -EBUSY; + } + } + + net_unlock(); + udp_recvfrom_uninitialize(&state); + return ret; +} + +#endif /* CONFIG_NET && CONFIG_NET_UDP */