From acbb32ed38e4c8e17420cea9219390d14c87495e Mon Sep 17 00:00:00 2001 From: Tymoteusz Bloch Date: Wed, 30 Jun 2021 20:09:41 +0200 Subject: [PATCH] New feature: send/recv message implementation added to network stack --- .../cellular/framework/AT/AT_CellularStack.h | 15 ++ .../lwipstack/include/lwipstack/LWIPStack.h | 50 +++++- .../lwipstack/include/lwipstack/lwipopts.h | 3 + .../lwipstack/lwip/test/unit/lwipopts.h | 2 +- .../lwipstack/source/LWIPInterface.cpp | 26 ++- connectivity/lwipstack/source/LWIPStack.cpp | 148 +++++++++++++++--- .../include/nanostack-interface/Nanostack.h | 21 ++- .../source/NanostackEMACInterface.cpp | 6 +- .../include/netsocket/CellularNonIPSocket.h | 8 + .../netsocket/InternetDatagramSocket.h | 55 ++++++- .../netsocket/include/netsocket/MsgHeader.h | 74 +++++++++ .../include/netsocket/NetworkStack.h | 45 ++++++ .../include/netsocket/OnboardNetworkStack.h | 5 +- .../netsocket/include/netsocket/Socket.h | 49 ++++++ .../netsocket/include/netsocket/TCPSocket.h | 47 ++++++ .../include/netsocket/TLSSocketWrapper.h | 8 + .../netsocket/include/netsocket/nsapi_types.h | 31 +++- .../netsocket/source/CellularNonIPSocket.cpp | 14 ++ .../netsocket/source/EMACInterface.cpp | 4 +- .../source/InternetDatagramSocket.cpp | 20 ++- .../netsocket/source/NetworkStack.cpp | 38 +++++ connectivity/netsocket/source/TCPSocket.cpp | 22 +++ .../netsocket/source/TLSSocketWrapper.cpp | 12 ++ .../network/emac/emac_TestNetworkStack.cpp | 2 +- .../network/emac/emac_TestNetworkStack.h | 2 +- .../UNITTESTS/doubles/NetworkStack_stub.h | 12 ++ 26 files changed, 671 insertions(+), 48 deletions(-) create mode 100644 connectivity/netsocket/include/netsocket/MsgHeader.h diff --git a/connectivity/cellular/include/cellular/framework/AT/AT_CellularStack.h b/connectivity/cellular/include/cellular/framework/AT/AT_CellularStack.h index 11308f87cda9..0a96b9b1b972 100644 --- a/connectivity/cellular/include/cellular/framework/AT/AT_CellularStack.h +++ b/connectivity/cellular/include/cellular/framework/AT/AT_CellularStack.h @@ -88,6 +88,21 @@ class AT_CellularStack : public NetworkStack { virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data); + + nsapi_size_or_error_t socket_sendmsg(nsapi_socket_t handle, const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override + { + return NSAPI_ERROR_UNSUPPORTED; + } + + nsapi_size_or_error_t socket_recvmsg(nsapi_socket_t handle, SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override + { + return NSAPI_ERROR_UNSUPPORTED; + } + protected: class CellularSocket { public: diff --git a/connectivity/lwipstack/include/lwipstack/LWIPStack.h b/connectivity/lwipstack/include/lwipstack/LWIPStack.h index 994a4a0af3bb..df47d5f065fb 100644 --- a/connectivity/lwipstack/include/lwipstack/LWIPStack.h +++ b/connectivity/lwipstack/include/lwipstack/LWIPStack.h @@ -163,6 +163,9 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { static void netif_status_irq(struct netif *netif); static Interface *our_if_from_netif(struct netif *netif); static void delete_interface(OnboardNetworkStack::Interface **interface_out); + NetworkInterface *network_if_from_netif_id(int id); + int netif_id_from_network_if(NetworkInterface *userInterface); + #if LWIP_ETHERNET static err_t emac_low_level_output(struct netif *netif, struct pbuf *p); @@ -222,6 +225,8 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { void *hw; /**< alternative implementation pointer - used for PPP */ }; + NetworkInterface *user_network_interface; + mbed_rtos_storage_semaphore_t remove_interface_sem; osSemaphoreId_t remove_interface; mbed_rtos_storage_semaphore_t linked_sem; @@ -265,7 +270,7 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { * @param[out] interface_out pointer to stack interface object controlling the EMAC * @return NSAPI_ERROR_OK on success, or error code */ - nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out) override; + nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface = NULL) override; /** Register a network interface with the IP stack * @@ -450,6 +455,27 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { nsapi_size_or_error_t socket_send(nsapi_socket_t handle, const void *data, nsapi_size_t size) override; + /** Send a packet with ancillary data over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * This call is non-blocking. If sendto would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @param control Ancillary data storage + * @param control_size Size of the Ancillary data in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + nsapi_size_or_error_t socket_sendmsg(nsapi_socket_t handle, const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + /** Receive data over a TCP socket * * The socket must be connected to a remote host. Returns the number of @@ -493,6 +519,7 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { * This call is non-blocking. If recvfrom would block, * NSAPI_ERROR_WOULD_BLOCK is returned immediately. * + * It uses socket_recvmsg with zero ancillary data. * @param handle Socket handle * @param address Destination for the source address or NULL * @param buffer Destination buffer for data received from the host @@ -503,6 +530,27 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable { nsapi_size_or_error_t socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *buffer, nsapi_size_t size) override; + /** Receive a packet with ancillary data over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * This call is non-blocking. If recvfrom would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address Destination for the source address or NULL + * @param buffer Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @param control Ancillary data storage + * @param control_size Size of the Ancillary data in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + nsapi_size_or_error_t socket_recvmsg(nsapi_socket_t handle, SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + /** Register a callback on state change of the socket * * The specified callback will be called on state changes such as when diff --git a/connectivity/lwipstack/include/lwipstack/lwipopts.h b/connectivity/lwipstack/include/lwipstack/lwipopts.h index 0a0c2cf7a445..25cbb1d8dbd9 100644 --- a/connectivity/lwipstack/include/lwipstack/lwipopts.h +++ b/connectivity/lwipstack/include/lwipstack/lwipopts.h @@ -311,6 +311,9 @@ #endif +// FIXME: Add compile time configuration and define conditionaly +#define LWIP_NETBUF_RECVINFO MBED_CONF_LWIP_NETBUF_RECVINFO_ENABLED + // Make sure we default these to off, so // LWIP doesn't default to on #ifndef LWIP_ARP diff --git a/connectivity/lwipstack/lwip/test/unit/lwipopts.h b/connectivity/lwipstack/lwip/test/unit/lwipopts.h index c00ace1561f7..3905e91afaac 100644 --- a/connectivity/lwipstack/lwip/test/unit/lwipopts.h +++ b/connectivity/lwipstack/lwip/test/unit/lwipopts.h @@ -46,7 +46,7 @@ #define LWIP_NETCONN !NO_SYS #define LWIP_SOCKET !NO_SYS #define LWIP_NETCONN_FULLDUPLEX LWIP_SOCKET -#define LWIP_NETBUF_RECVINFO 1 +#define LWIP_NETBUF_RECVINFO MBED_CONF_NETBUF_RECVINFO_ENABLED #define LWIP_HAVE_LOOPIF 1 #define TCPIP_THREAD_TEST diff --git a/connectivity/lwipstack/source/LWIPInterface.cpp b/connectivity/lwipstack/source/LWIPInterface.cpp index 164ff03e6113..a1cfcf31c41e 100644 --- a/connectivity/lwipstack/source/LWIPInterface.cpp +++ b/connectivity/lwipstack/source/LWIPInterface.cpp @@ -42,6 +42,28 @@ LWIP::Interface *LWIP::Interface::list; +NetworkInterface *LWIP::Interface::network_if_from_netif_id(int id) +{ + for (Interface *interface = list; interface; interface = interface->next) { + if (id == netif_get_index(&interface->netif)) { + return interface->user_network_interface; + } + } + return NULL; +} + +int LWIP::Interface::netif_id_from_network_if(NetworkInterface *userInterface) +{ + if (userInterface != NULL) { + for (Interface *interface = list; interface; interface = interface->next) { + if (userInterface == interface->user_network_interface) { + return netif_get_index(&interface->netif); + } + } + } + return 0; +} + LWIP::Interface *LWIP::Interface::our_if_from_netif(struct netif *netif) { for (Interface *interface = list; interface; interface = interface->next) { @@ -408,7 +430,7 @@ LWIP::Interface::Interface() : list = this; } -nsapi_error_t LWIP::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out) +nsapi_error_t LWIP::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface) { #if LWIP_ETHERNET Interface *interface = new (std::nothrow) Interface(); @@ -431,7 +453,7 @@ nsapi_error_t LWIP::add_ethernet_interface(EMAC &emac, bool default_if, OnboardN #endif interface->netif.hwaddr_len = 6; - + interface->user_network_interface = user_network_interface; if (!netif_add(&interface->netif, #if LWIP_IPV4 0, 0, 0, diff --git a/connectivity/lwipstack/source/LWIPStack.cpp b/connectivity/lwipstack/source/LWIPStack.cpp index db4adafb891c..0645ae00c10d 100644 --- a/connectivity/lwipstack/source/LWIPStack.cpp +++ b/connectivity/lwipstack/source/LWIPStack.cpp @@ -15,10 +15,10 @@ * limitations under the License. */ #include "nsapi.h" +#include "netsocket/MsgHeader.h" #include "mbed_interface.h" #include "mbed_assert.h" #include "Semaphore.h" -#include #include #include @@ -37,6 +37,7 @@ #include "lwip/raw.h" #include "lwip/netif.h" #include "lwip/lwip_errno.h" +#include "lwip/ip_addr.h" #include "lwip-sys/arch/sys_arch.h" #include "LWIPStack.h" @@ -271,7 +272,9 @@ nsapi_error_t LWIP::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto) arena_dealloc(s); return NSAPI_ERROR_NO_SOCKET; } - +#if LWIP_NETBUF_RECVINFO + s->conn->flags &= ~NETCONN_FLAG_PKTINFO; +#endif netconn_set_nonblocking(s->conn, true); *(struct mbed_lwip_socket **)handle = s; return 0; @@ -439,24 +442,111 @@ nsapi_size_or_error_t LWIP::socket_recv(nsapi_socket_t handle, void *data, nsapi } nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAddress &address, const void *data, nsapi_size_t size) +{ + return socket_sendmsg(handle, address, data, size, NULL, 0); +} + +nsapi_size_or_error_t LWIP::socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size) +{ + return socket_recvmsg(handle, address, data, size, NULL, 0); + +} + +nsapi_size_or_error_t LWIP::socket_recvmsg(nsapi_socket_t handle, SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; - ip_addr_t ip_addr; + struct netbuf *buf; + + err_t err = netconn_recv(s->conn, &buf); + if (err != ERR_OK) { + return err_remap(err); + } + + if (address) { + nsapi_addr_t addr; + convert_lwip_addr_to_mbed(&addr, netbuf_fromaddr(buf)); + address->set_addr(addr); + address->set_port(netbuf_fromport(buf)); + } +#if LWIP_NETBUF_RECVINFO + if ((s->conn->flags & NETCONN_FLAG_PKTINFO) && control && control_size >= sizeof(nsapi_pktinfo_t)) { + nsapi_pktinfo_t *pkt_info = reinterpret_cast(control); + memset(control, 0, control_size); + // Not optimal but sufficient. It should help the caller in not iterating over + // the control data structure + control->len = control_size; + control->level = NSAPI_SOCKET; + control->type = NSAPI_PKTINFO; + // retrieve the destination + convert_lwip_addr_to_mbed(&pkt_info->ipi_addr, netbuf_destaddr(buf)); + // retrieve the interface id + pkt_info->network_interface = default_interface->network_if_from_netif_id(buf->p->if_idx); + } +#endif + u16_t recv = netbuf_copy(buf, data, (u16_t)size); + netbuf_delete(buf); + + return recv; +} + +nsapi_size_or_error_t LWIP::socket_sendmsg(nsapi_socket_t handle, const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; + ip_addr_t ip_addr = {}; + + // Used for backup the bound address if the packet must be sent from a specific address, + ip_addr_t bound_addr = {}; + ip_addr_t src_addr = {}; + + nsapi_pktinfo_t *pkt_info = nullptr; nsapi_addr_t addr = address.get_addr(); if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) { return NSAPI_ERROR_PARAMETER; } - struct netif *netif_ = netif_get_by_index(s->conn->pcb.ip->netif_idx); + + // We try to extract the pktinfo from the header + + if (control) { + MsgHeaderIterator it(control, control_size); + while (it.has_next()) { + auto *hdr = it.next(); + if (hdr->level == NSAPI_SOCKET && hdr->type == NSAPI_PKTINFO) { + pkt_info = reinterpret_cast(hdr); + break; + } + } + } + + if (pkt_info) { + if (!convert_mbed_addr_to_lwip(&src_addr, &pkt_info->ipi_addr)) { + return NSAPI_ERROR_PARAMETER; + } + } + + struct netif *netif_ = nullptr; + + if (pkt_info) { + int index = default_interface->netif_id_from_network_if((NetworkInterface *)pkt_info->network_interface); + netif_ = netif_get_by_index(index); + } else { + netif_ = netif_get_by_index(s->conn->pcb.ip->netif_idx); + } if (!netif_) { netif_ = &default_interface->netif; } + if (netif_) { if ((addr.version == NSAPI_IPv4 && !get_ipv4_addr(netif_)) || (addr.version == NSAPI_IPv6 && !get_ipv6_addr(netif_) && !get_ipv6_link_local_addr(netif_))) { return NSAPI_ERROR_PARAMETER; } } + struct netbuf *buf = netbuf_new(); err_t err = netbuf_ref(buf, data, (u16_t)size); @@ -465,36 +555,29 @@ nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAdd return err_remap(err); } - err = netconn_sendto(s->conn, buf, &ip_addr, address.get_port()); - netbuf_delete(buf); - if (err != ERR_OK) { - return err_remap(err); + // handle src destination if required + if (pkt_info) { + // Backup the bound address + ip_addr_copy(bound_addr, s->conn->pcb.udp->local_ip); + // replace it with the source address + if (!ip_addr_isany(&src_addr)) { + ip_addr_copy(s->conn->pcb.udp->local_ip, src_addr); + } } - return size; -} + err = netconn_sendto(s->conn, buf, &ip_addr, address.get_port()); -nsapi_size_or_error_t LWIP::socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size) -{ - struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; - struct netbuf *buf; + if (pkt_info) { + // restore bound address + ip_addr_copy(s->conn->pcb.udp->local_ip, bound_addr); + } - err_t err = netconn_recv(s->conn, &buf); + netbuf_delete(buf); if (err != ERR_OK) { return err_remap(err); } - if (address) { - nsapi_addr_t addr; - convert_lwip_addr_to_mbed(&addr, netbuf_fromaddr(buf)); - address->set_addr(addr); - address->set_port(netbuf_fromport(buf)); - } - - u16_t recv = netbuf_copy(buf, data, (u16_t)size); - netbuf_delete(buf); - - return recv; + return size; } int32_t LWIP::find_multicast_member(const struct mbed_lwip_socket *s, const nsapi_ip_mreq_t *imr) @@ -687,6 +770,19 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co } s->conn->pcb.ip->tos = (u8_t)(*(const int *)optval); return 0; + + case NSAPI_PKTINFO: +#if LWIP_NETBUF_RECVINFO + if (optlen != sizeof(int)) { + return NSAPI_ERROR_UNSUPPORTED; + } + if (*(const int *)optval) { + s->conn->flags |= NETCONN_FLAG_PKTINFO; + } else { + s->conn->flags &= ~NETCONN_FLAG_PKTINFO; + } + return 0; +#endif default: return NSAPI_ERROR_UNSUPPORTED; } diff --git a/connectivity/nanostack/include/nanostack-interface/Nanostack.h b/connectivity/nanostack/include/nanostack-interface/Nanostack.h index 53894a1f10b8..b9cbee4f6388 100644 --- a/connectivity/nanostack/include/nanostack-interface/Nanostack.h +++ b/connectivity/nanostack/include/nanostack-interface/Nanostack.h @@ -40,11 +40,11 @@ class Nanostack : public OnboardNetworkStack, private mbed::NonCopyablelen < sizeof(*current)) { + return false; + } + + if ((reinterpret_cast(current) + current->len) >= (reinterpret_cast(start) + size)) { + return false; + } + + return true; + } + + nsapi_msghdr_t *next() + { + if (!has_next()) { + return nullptr; + } + + if (current == nullptr) { + current = start; + } else { + current = reinterpret_cast(reinterpret_cast(current) + current->len); + } + + return current; + } + +private: + nsapi_msghdr_t *start; + nsapi_msghdr_t *current; + nsapi_size_t size; +}; + + +#endif + diff --git a/connectivity/netsocket/include/netsocket/NetworkStack.h b/connectivity/netsocket/include/netsocket/NetworkStack.h index 2f6bb78cbc45..8118372095d1 100644 --- a/connectivity/netsocket/include/netsocket/NetworkStack.h +++ b/connectivity/netsocket/include/netsocket/NetworkStack.h @@ -395,6 +395,51 @@ class NetworkStack : public DNS { virtual nsapi_size_or_error_t socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *buffer, nsapi_size_t size) = 0; + /** Send a packet with ancillary data over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * This call is non-blocking. If sendto would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @param control Storage for ancillary data + * @param control_size Size of ancillary data + * @return Number of sent bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t socket_sendmsg(nsapi_socket_t handle, const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) = 0; + + /** Receive a packet with ancillary data over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * Additional information related to the message can be retrieved with + * the control data. + * + * This call is non-blocking. If recvfrom would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address Destination for the source address or NULL + * @param buffer Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @param control Storage for ancillary data + * @param control_size Size of ancillary data + * @return Number of received bytes on success, negative error + * code on failure + */ + virtual nsapi_size_or_error_t socket_recvmsg(nsapi_socket_t handle, SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) = 0; + /** Register a callback on state change of the socket * * The specified callback will be called on state changes such as when diff --git a/connectivity/netsocket/include/netsocket/OnboardNetworkStack.h b/connectivity/netsocket/include/netsocket/OnboardNetworkStack.h index 8c48c7b75fd7..2c1608a71235 100644 --- a/connectivity/netsocket/include/netsocket/OnboardNetworkStack.h +++ b/connectivity/netsocket/include/netsocket/OnboardNetworkStack.h @@ -160,9 +160,10 @@ class OnboardNetworkStack : public NetworkStack { * @param[out] interface_out pointer to stack interface object controlling the EMAC * @return NSAPI_ERROR_OK on success, or error code */ - virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Interface **interface_out) = 0; + virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Interface **interface_out, NetworkInterface *user_network_interface = NULL) = 0; + + virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Interface **interface_out, const uint8_t *mac_addr, NetworkInterface *user_network_interface = NULL) - virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Interface **interface_out, const uint8_t *mac_addr) { return NSAPI_ERROR_UNSUPPORTED; } diff --git a/connectivity/netsocket/include/netsocket/Socket.h b/connectivity/netsocket/include/netsocket/Socket.h index de493c7bd6e2..5d10fdcbd288 100644 --- a/connectivity/netsocket/include/netsocket/Socket.h +++ b/connectivity/netsocket/include/netsocket/Socket.h @@ -157,6 +157,55 @@ class Socket { virtual nsapi_size_or_error_t recvfrom(SocketAddress *address, void *data, nsapi_size_t size) = 0; + /** Send a message on a socket. + * + * The sendmsg() function sends a message through a connection-mode or connectionless-mode socket. + * If the socket is a connectionless-mode socket, the message is sent to the address specified. + * If the socket is a connected-mode socket, address is ignored. + * + * Additional control information can be passed to the stack for specific operations. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Remote address + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative subclass-dependent error + * code on failure + */ + virtual nsapi_size_or_error_t sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) = 0; + + + /** Receive a data from a socket + * + * Receives a data and stores the source address in address if address + * is not NULL. Returns the number of bytes written into the buffer. + * + * If socket is connected, only packets coming from connected peer address + * are accepted. + * + * Additional information related to the message can be retrieved with the control data. + * + * @note recvmsg() is allowed write to address and data buffers even if error occurs. + * + * By default, recvfrom blocks until a datagram is received. If socket is set to + * non-blocking or times out with no data, NSAPI_ERROR_WOULD_BLOCK + * is returned. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for datagram received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative subclass-dependent + * error code on failure + */ + virtual nsapi_size_or_error_t recvmsg(SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) = 0; + /** Bind a specific address to a socket. * * Binding a socket specifies the address and port on which to receive diff --git a/connectivity/netsocket/include/netsocket/TCPSocket.h b/connectivity/netsocket/include/netsocket/TCPSocket.h index 37897f231a05..877bfe26cb1a 100644 --- a/connectivity/netsocket/include/netsocket/TCPSocket.h +++ b/connectivity/netsocket/include/netsocket/TCPSocket.h @@ -145,6 +145,53 @@ class TCPSocket : public InternetSocket { nsapi_size_or_error_t recvfrom(SocketAddress *address, void *data, nsapi_size_t size) override; + /** Send data on a packet with ancillary datasocket. + * + * TCP socket is connection oriented protocol, so address is ignored. + * + * By default, sendmsg blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Remote address + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @retval int Number of sent bytes on success + * @retval NSAPI_ERROR_NO_SOCKET in case socket was not created correctly + * @retval NSAPI_ERROR_WOULD_BLOCK in case non-blocking mode is enabled + * and send cannot be performed immediately + * @retval int Other negative error codes for stack-related failures. + * See @ref NetworkStack::socket_send. + */ + nsapi_size_or_error_t sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + + /** Receive a packet with ancillary data from a socket + * + * Receives a data and stores the source address in address if address + * is not NULL. Returns the number of bytes written into the buffer. + * + * By default, recvmsg blocks until a data is received. If socket is set to + * non-blocking or times out with no datagram, NSAPI_ERROR_WOULD_BLOCK + * is returned. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for datagram received from the host + * @param size Size of the buffer in bytes + * @control Pointer to the control buffer + * @control_size Size of the control buffer in bytes + * @retval int Number of received bytes on success + * @retval NSAPI_ERROR_NO_SOCKET in case socket was not created correctly + * @retval NSAPI_ERROR_WOULD_BLOCK in case non-blocking mode is enabled + * and send cannot be performed immediately + * @retval int Other negative error codes for stack-related failures. + * See @ref NetworkStack::socket_recv. + */ + nsapi_size_or_error_t recvmsg(SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + /** Accepts a connection on a socket. * * The server socket must be bound and set to listen for connections. diff --git a/connectivity/netsocket/include/netsocket/TLSSocketWrapper.h b/connectivity/netsocket/include/netsocket/TLSSocketWrapper.h index 8f6af7f8ed62..bcf2c335ee4e 100644 --- a/connectivity/netsocket/include/netsocket/TLSSocketWrapper.h +++ b/connectivity/netsocket/include/netsocket/TLSSocketWrapper.h @@ -185,6 +185,14 @@ class TLSSocketWrapper : public Socket { nsapi_size_or_error_t sendto(const SocketAddress &address, const void *data, nsapi_size_t size) override; nsapi_size_or_error_t recvfrom(SocketAddress *address, void *data, nsapi_size_t size) override; + + nsapi_size_or_error_t sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + nsapi_size_or_error_t recvmsg(SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) override; + nsapi_error_t bind(const SocketAddress &address) override; void set_blocking(bool blocking) override; void set_timeout(int timeout) override; diff --git a/connectivity/netsocket/include/netsocket/nsapi_types.h b/connectivity/netsocket/include/netsocket/nsapi_types.h index eef8f34cc57c..77ae8cd487ca 100644 --- a/connectivity/netsocket/include/netsocket/nsapi_types.h +++ b/connectivity/netsocket/include/netsocket/nsapi_types.h @@ -318,7 +318,8 @@ typedef enum nsapi_socket_option { NSAPI_LATENCY, /*!< Read estimated latency to destination */ NSAPI_STAGGER, /*!< Read estimated stagger value to destination */ NSAPI_IPTOS, /*!< Set IP type of service to set specific precedence */ - NSAPI_BROADCAST /*!< Set broadcast flag for UDP socket */ + NSAPI_BROADCAST, /*!< Set broadcast flag for UDP socket */ + NSAPI_PKTINFO /*!< Get additional information when using sendmsg/recvmsg */ } nsapi_socket_option_t; typedef enum nsapi_tlssocket_level { @@ -406,6 +407,23 @@ typedef struct nsapi_stagger_req { uint16_t stagger_rand; /* [OUT] Randomized stagger value in seconds */ } nsapi_stagger_req_t; +/** nsapi_msghdr + */ +typedef struct nsapi_msghdr { + nsapi_size_t len; /* Data byte count, including header */ + int level; /* Originating protocol */ + int type; /* Protocol-specific type */ +} nsapi_msghdr_t; + +/** nsapi_pktinfo structure + */ +typedef struct nsapi_pktinfo { + nsapi_msghdr_t hdr; /* Header identifying the message control structure */ + nsapi_addr_t ipi_addr; /* Address associated with the packet */ + int ipi_ifindex; /* Interface associated with the packet */ + void *network_interface; /* Network interface pointer*/ +} nsapi_pktinfo_t; + /** nsapi_stack_api structure * * Common api structure for network stack operations. A network stack @@ -644,6 +662,17 @@ typedef struct nsapi_stack_api { nsapi_size_or_error_t (*socket_recvfrom)(nsapi_stack_t *stack, nsapi_socket_t socket, nsapi_addr_t *addr, uint16_t *port, void *buffer, nsapi_size_t size); + // TODO: Documentation + nsapi_size_or_error_t (*socket_sendmsg)(nsapi_stack_t *stack, nsapi_socket_t socket, + nsapi_addr_t addr, uint16_t port, + const void *data, nsapi_size_t size, + const nsapi_msghdr_t *control, nsapi_size_t control_size); + + nsapi_size_or_error_t (*socket_recvmsg)(nsapi_stack_t *stack, nsapi_socket_t socket, + nsapi_addr_t *addr, uint16_t *port, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size); + /** Register a callback on state change of the socket * * The specified callback will be called on state changes such as when diff --git a/connectivity/netsocket/source/CellularNonIPSocket.cpp b/connectivity/netsocket/source/CellularNonIPSocket.cpp index f0925f7835a3..622acc1a30cc 100644 --- a/connectivity/netsocket/source/CellularNonIPSocket.cpp +++ b/connectivity/netsocket/source/CellularNonIPSocket.cpp @@ -244,12 +244,26 @@ nsapi_size_or_error_t CellularNonIPSocket::sendto(const SocketAddress &address, { return NSAPI_ERROR_UNSUPPORTED; } +nsapi_size_or_error_t CellularNonIPSocket::sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + nsapi_size_or_error_t CellularNonIPSocket::recvfrom(SocketAddress *address, void *data, nsapi_size_t size) { return NSAPI_ERROR_UNSUPPORTED; } +nsapi_size_or_error_t CellularNonIPSocket::recvmsg(SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + nsapi_error_t CellularNonIPSocket::setsockopt(int level, int optname, const void *optval, unsigned optlen) { return NSAPI_ERROR_UNSUPPORTED; diff --git a/connectivity/netsocket/source/EMACInterface.cpp b/connectivity/netsocket/source/EMACInterface.cpp index 6474b6a570ed..f48bc0a185a1 100644 --- a/connectivity/netsocket/source/EMACInterface.cpp +++ b/connectivity/netsocket/source/EMACInterface.cpp @@ -55,14 +55,14 @@ nsapi_error_t EMACInterface::connect() nsapi_error_t err = NSAPI_ERROR_UNSUPPORTED; if (_hw_mac_addr_set) { - err = _stack.add_ethernet_interface(_emac, true, &_interface, _hw_mac_addr); + err = _stack.add_ethernet_interface(_emac, true, &_interface, _hw_mac_addr, this); if (err == NSAPI_ERROR_UNSUPPORTED) { tr_error("Failed to set user MAC address"); } } if (err == NSAPI_ERROR_UNSUPPORTED) { - err = _stack.add_ethernet_interface(_emac, true, &_interface); + err = _stack.add_ethernet_interface(_emac, true, &_interface, this); } if (err != NSAPI_ERROR_OK) { diff --git a/connectivity/netsocket/source/InternetDatagramSocket.cpp b/connectivity/netsocket/source/InternetDatagramSocket.cpp index 4c0010de0330..3794b85113c7 100644 --- a/connectivity/netsocket/source/InternetDatagramSocket.cpp +++ b/connectivity/netsocket/source/InternetDatagramSocket.cpp @@ -27,7 +27,7 @@ nsapi_error_t InternetDatagramSocket::connect(const SocketAddress &address) return NSAPI_ERROR_OK; } -nsapi_size_or_error_t InternetDatagramSocket::sendto(const SocketAddress &address, const void *data, nsapi_size_t size) +nsapi_size_or_error_t InternetDatagramSocket::sendmsg(const SocketAddress &address, const void *data, nsapi_size_t size, nsapi_msghdr_t *control, nsapi_size_t control_size) { _lock.lock(); nsapi_size_or_error_t ret; @@ -44,7 +44,7 @@ nsapi_size_or_error_t InternetDatagramSocket::sendto(const SocketAddress &addres } core_util_atomic_flag_clear(&_pending); - nsapi_size_or_error_t sent = _stack->socket_sendto(_socket, address, data, size); + nsapi_size_or_error_t sent = _stack->socket_sendmsg(_socket, address, data, size, control, control_size); if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) { _socket_stats.stats_update_sent_bytes(this, sent); ret = sent; @@ -74,6 +74,12 @@ nsapi_size_or_error_t InternetDatagramSocket::sendto(const SocketAddress &addres return ret; } +nsapi_size_or_error_t InternetDatagramSocket::sendto(const SocketAddress &address, const void *data, nsapi_size_t size) +{ + + return sendmsg(address, data, size, NULL, 0); +} + nsapi_size_or_error_t InternetDatagramSocket::send(const void *data, nsapi_size_t size) { if (!_remote_peer) { @@ -82,7 +88,8 @@ nsapi_size_or_error_t InternetDatagramSocket::send(const void *data, nsapi_size_ return sendto(_remote_peer, data, size); } -nsapi_size_or_error_t InternetDatagramSocket::recvfrom(SocketAddress *address, void *buffer, nsapi_size_t size) + +nsapi_size_or_error_t InternetDatagramSocket::recvmsg(SocketAddress *address, void *buffer, nsapi_size_t size, nsapi_msghdr_t *control, nsapi_size_t control_size) { _lock.lock(); nsapi_size_or_error_t ret; @@ -104,7 +111,7 @@ nsapi_size_or_error_t InternetDatagramSocket::recvfrom(SocketAddress *address, v } core_util_atomic_flag_clear(&_pending); - nsapi_size_or_error_t recv = _stack->socket_recvfrom(_socket, address, buffer, size); + nsapi_size_or_error_t recv = _stack->socket_recvmsg(_socket, address, buffer, size, control, control_size); // Filter incomming packets using connected peer address if (recv >= 0 && _remote_peer && _remote_peer != *address) { @@ -143,6 +150,11 @@ nsapi_size_or_error_t InternetDatagramSocket::recvfrom(SocketAddress *address, v return ret; } +nsapi_size_or_error_t InternetDatagramSocket::recvfrom(SocketAddress *address, void *buffer, nsapi_size_t size) +{ + return recvmsg(address, buffer, size, NULL, 0); +} + nsapi_size_or_error_t InternetDatagramSocket::recv(void *buffer, nsapi_size_t size) { return recvfrom(NULL, buffer, size); diff --git a/connectivity/netsocket/source/NetworkStack.cpp b/connectivity/netsocket/source/NetworkStack.cpp index 99b17cff13a9..83949f00556b 100644 --- a/connectivity/netsocket/source/NetworkStack.cpp +++ b/connectivity/netsocket/source/NetworkStack.cpp @@ -399,6 +399,44 @@ class NetworkStackWrapper : public NetworkStack { return err; } + nsapi_size_or_error_t socket_sendmsg(nsapi_socket_t socket, const SocketAddress &address, const void *data, nsapi_size_t size, nsapi_msghdr_t *control, nsapi_size_t control_size) override + { + if (control != NULL) { + return NSAPI_ERROR_UNSUPPORTED; + } + + if (!_stack_api()->socket_sendmsg) { + return NSAPI_ERROR_UNSUPPORTED; + } + + return _stack_api()->socket_sendmsg(_stack(), socket, address.get_addr(), address.get_port(), data, size, control, control_size); + } + + nsapi_size_or_error_t socket_recvmsg(nsapi_socket_t socket, SocketAddress *address, void *data, nsapi_size_t size, nsapi_msghdr_t *control, nsapi_size_t control_size) override + { + if (control != NULL) { + return NSAPI_ERROR_UNSUPPORTED; + } + + if (!_stack_api()->socket_recvmsg) { + return NSAPI_ERROR_UNSUPPORTED; + } + + nsapi_addr_t addr = {NSAPI_IPv4, 0}; + uint16_t port = 0; + + nsapi_size_or_error_t err = _stack_api()->socket_recvmsg(_stack(), socket, &addr, &port, data, size, control, control_size); + + if (address) { + address->set_addr(addr); + address->set_port(port); + } + + return err; + } + + + void socket_attach(nsapi_socket_t socket, void (*callback)(void *), void *data) override { if (!_stack_api()->socket_attach) { diff --git a/connectivity/netsocket/source/TCPSocket.cpp b/connectivity/netsocket/source/TCPSocket.cpp index a85c8eb26cdf..f749aff97f8a 100644 --- a/connectivity/netsocket/source/TCPSocket.cpp +++ b/connectivity/netsocket/source/TCPSocket.cpp @@ -173,6 +173,17 @@ nsapi_size_or_error_t TCPSocket::sendto(const SocketAddress &address, const void return send(data, size); } +nsapi_size_or_error_t TCPSocket::sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + // FIXME: Implement + if (control) { + return NSAPI_ERROR_UNSUPPORTED; + } + return sendto(address, data, size); +} + nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size) { _lock.lock(); @@ -229,6 +240,17 @@ nsapi_size_or_error_t TCPSocket::recvfrom(SocketAddress *address, void *data, ns return recv(data, size); } +nsapi_size_or_error_t TCPSocket::recvmsg(SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + // FIXME: Implement + if (control) { + return NSAPI_ERROR_UNSUPPORTED; + } + return recvfrom(address, data, size); +} + nsapi_error_t TCPSocket::listen(int backlog) { _lock.lock(); diff --git a/connectivity/netsocket/source/TLSSocketWrapper.cpp b/connectivity/netsocket/source/TLSSocketWrapper.cpp index 9879af9e913f..44af1feaf632 100644 --- a/connectivity/netsocket/source/TLSSocketWrapper.cpp +++ b/connectivity/netsocket/source/TLSSocketWrapper.cpp @@ -385,6 +385,13 @@ nsapi_size_or_error_t TLSSocketWrapper::sendto(const SocketAddress &, const void return send(data, size); } +nsapi_size_or_error_t TLSSocketWrapper::sendmsg(const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + return sendto(address, data, size); +} + nsapi_size_or_error_t TLSSocketWrapper::recv(void *data, nsapi_size_t size) { int ret; @@ -445,6 +452,11 @@ nsapi_size_or_error_t TLSSocketWrapper::recvfrom(SocketAddress *address, void *d return recv(data, size); } +nsapi_size_or_error_t TLSSocketWrapper::recvmsg(SocketAddress *address, void *data, nsapi_size_t size, nsapi_msghdr_t *control, nsapi_size_t control_size) +{ + return recvfrom(address, data, size); +} + void TLSSocketWrapper::print_mbedtls_error(MBED_UNUSED const char *name, MBED_UNUSED int err) { // Avoid pulling in mbedtls_strerror when trace is not enabled diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.cpp b/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.cpp index 6f8afc4cdd99..7a0069ad1dd4 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.cpp +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.cpp @@ -115,7 +115,7 @@ void EmacTestNetworkStack::socket_attach(nsapi_socket_t handle, void (*callback) } -nsapi_error_t EmacTestNetworkStack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out) +nsapi_error_t EmacTestNetworkStack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface) { // Test network stack supports only one interface TEST_ASSERT_MESSAGE(!m_interface, "Only one interface supported!"); diff --git a/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.h b/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.h index 95eba068a40e..125392b7472d 100644 --- a/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.h +++ b/connectivity/netsocket/tests/TESTS/network/emac/emac_TestNetworkStack.h @@ -148,7 +148,7 @@ class EmacTestNetworkStack : public OnboardNetworkStack, private mbed::NonCopyab * @param[out] interface_out pointer to stack interface object controlling the EMAC * @return NSAPI_ERROR_OK on success, or error code */ - virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out); + virtual nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface); /** Translates a hostname to an IP address with specific version * diff --git a/connectivity/netsocket/tests/UNITTESTS/doubles/NetworkStack_stub.h b/connectivity/netsocket/tests/UNITTESTS/doubles/NetworkStack_stub.h index d6b630a1d80e..356e3bea4e03 100644 --- a/connectivity/netsocket/tests/UNITTESTS/doubles/NetworkStack_stub.h +++ b/connectivity/netsocket/tests/UNITTESTS/doubles/NetworkStack_stub.h @@ -136,6 +136,12 @@ class NetworkStackstub : public NetworkStack { { return return_value; }; + virtual nsapi_size_or_error_t socket_sendmsg(nsapi_socket_t handle, const SocketAddress &address, + const void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) + { + return return_value; + }; virtual nsapi_size_or_error_t socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *buffer, nsapi_size_t size) { @@ -149,6 +155,12 @@ class NetworkStackstub : public NetworkStack { } return return_value; }; + virtual nsapi_size_or_error_t socket_recvmsg(nsapi_socket_t handle, SocketAddress *address, + void *data, nsapi_size_t size, + nsapi_msghdr_t *control, nsapi_size_t control_size) + { + return return_value; + }; virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data) {}; private: