Merge pull request #15040 from mat-kalinowski/sendrecv_message

New feature: send/recv message implementation added to network stack
pull/15061/head
Martin Kojtal 2021-09-09 08:35:19 +01:00 committed by GitHub
commit f89655f2da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 936 additions and 62 deletions

View File

@ -88,6 +88,21 @@ protected: // NetworkStack
virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data);
nsapi_size_or_error_t socket_sendto_control(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_recvfrom_control(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:

View File

@ -163,6 +163,9 @@ public:
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 @@ public:
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 @@ public:
* @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 @@ protected:
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_sendto_control(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 @@ protected:
* This call is non-blocking. If recvfrom would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* It uses socket_recvfrom_control 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 @@ protected:
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_recvfrom_control(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

View File

@ -311,6 +311,8 @@
#endif
#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

View File

@ -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

View File

@ -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,

View File

@ -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 <stdio.h>
#include <stdbool.h>
#include <string.h>
@ -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;
@ -440,41 +443,18 @@ 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)
{
struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle;
ip_addr_t ip_addr;
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);
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);
if (err != ERR_OK) {
netbuf_free(buf);
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);
}
return size;
return socket_sendto_control(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_recvfrom_control(handle, address, data, size, NULL, 0);
}
nsapi_size_or_error_t LWIP::socket_recvfrom_control(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;
struct netbuf *buf;
@ -490,13 +470,116 @@ nsapi_size_or_error_t LWIP::socket_recvfrom(nsapi_socket_t handle, SocketAddress
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<nsapi_pktinfo *>(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_sendto_control(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;
}
// 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<nsapi_pktinfo_t *>(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);
if (err != ERR_OK) {
netbuf_free(buf);
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);
}
}
err = netconn_sendto(s->conn, buf, &ip_addr, address.get_port());
if (pkt_info) {
// restore bound address
ip_addr_copy(s->conn->pcb.udp->local_ip, bound_addr);
}
netbuf_delete(buf);
if (err != ERR_OK) {
return err_remap(err);
}
return size;
}
int32_t LWIP::find_multicast_member(const struct mbed_lwip_socket *s, const nsapi_ip_mreq_t *imr)
{
uint32_t count = 0;
@ -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;
}

View File

@ -40,11 +40,11 @@ public:
class PPPInterface;
/* Implement OnboardNetworkStack method */
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, const uint8_t *mac_addr) override;
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface = NULL) override;
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, const uint8_t *mac_addr, NetworkInterface *user_network_interface = NULL) override;
/* Local variant with stronger typing and manual address specification */
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr = NULL);
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr = NULL, NetworkInterface *user_network_interface = NULL);
nsapi_error_t add_ppp_interface(PPP &ppp, bool default_if, OnboardNetworkStack::Interface **interface_out) override;
@ -302,6 +302,20 @@ protected:
*/
nsapi_error_t getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) override;
nsapi_size_or_error_t socket_sendto_control(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_recvfrom_control(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;
}
private:
/** Call in callback

View File

@ -191,7 +191,7 @@ void EMACPhy::set_mac_address(uint8_t *mac)
memcpy(mac_addr, mac, sizeof mac_addr);
}
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr)
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Nanostack::EthernetInterface **interface_out, const uint8_t *mac_addr, NetworkInterface *user_network_interface)
{
if (single_phy) {
return NSAPI_ERROR_DEVICE_ERROR;
@ -224,7 +224,7 @@ nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Nan
return NSAPI_ERROR_OK;
}
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, const uint8_t *mac_addr)
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, const uint8_t *mac_addr, NetworkInterface *user_network_interface)
{
Nanostack::EthernetInterface *interface;
nsapi_error_t err = add_ethernet_interface(emac, default_if, &interface, mac_addr);
@ -232,7 +232,7 @@ nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Onb
return err;
}
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out)
nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out, NetworkInterface *user_network_interface)
{
Nanostack::EthernetInterface *interface;
nsapi_error_t err = add_ethernet_interface(emac, default_if, &interface);

View File

@ -133,6 +133,14 @@ public:
nsapi_size_or_error_t recvfrom(SocketAddress *address,
void *data, nsapi_size_t size) override;
/// NOT APPLICABLE
nsapi_size_or_error_t sendto_control(const SocketAddress &address,
const void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size) override;
/// NOT APPLICABLE
nsapi_size_or_error_t recvfrom_control(SocketAddress *address,
void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size) override;
/// NOT APPLICABLE
nsapi_error_t bind(const SocketAddress &address) override;
protected:

View File

@ -36,6 +36,7 @@ public:
* nonblocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* It uses sendto_control with zero ancillary data
* @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.
@ -60,7 +61,7 @@ public:
* are accepted.
*
* @note recvfrom() is allowed write to address and data buffers even if error occurs.
*
* It uses recvfrom_control with zero ancillary data
* @param address Destination for the source address or NULL.
* @param data Destination buffer for RAW data to be received from the host.
* @param size Size of the buffer in bytes.
@ -74,6 +75,58 @@ public:
nsapi_size_or_error_t recvfrom(SocketAddress *address,
void *data, nsapi_size_t size) override;
/** Send datagram and ancillary data to the specified address.
*
* By default, sendto blocks until data is sent. If socket is set to
* nonblocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* It uses sendto_control with zero ancillary data
* @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 Size of the buffer in bytes.
* @param control_size Size of the buffer in bytes.
* @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 sendto_control(const SocketAddress &address,
const void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size) override;
/** Receive a datagram with ancillary data and store the source address in address if it's not NULL.
*
* By default, recvfrom blocks until a datagram is received. If socket is set to
* nonblocking or times out with no datagram, NSAPI_ERROR_WOULD_BLOCK
* is returned.
* Ancillary data is stored in msghdr struct
* @note If the datagram is larger than the buffer, the excess data is silently discarded.
*
* @note If socket is connected, only packets coming from connected peer address
* are accepted.
*
* @note recvfrom_control() is allowed write to address and data buffers even if error occurs.
*
* @param address Destination for the source address or NULL.
* @param data Destination buffer for RAW data to be received from the host.
* @param size Size of the buffer in bytes.
* @param control Size of the buffer in bytes.
* @param control_size Size of the 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 recvfrom_control(SocketAddress *address,
void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size) override;
/** Set the remote address for next send() call and filtering
* of incoming packets. To reset the address, zero initialized
* SocketAddress must be in the address parameter.

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2021 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MSG_HEADER_H
#define MSG_HEADER_H
#include "netsocket/nsapi_types.h"
/**
* Allows iteration through the list of message headers received in the control parameter of the
* socket_sendto_control / socket_recvfrom_control methods.
*
* @par Members types
*
* MsgHeaderIterator works on the list which members are of type nsapi_msghdr_t or other types
* extending this struct. For example nsapi_pktinfo:
*
* @code
typedef struct nsapi_pktinfo {
nsapi_msghdr_t hdr;
nsapi_addr_t ipi_addr;
int ipi_ifindex;
void *network_interface;
} nsapi_pktinfo_t;
* @endcode
*
* There are two requirements for such structures to work well with MsgHeaderIterator.
* - First element needs to be of type nsapi_msghdr_t.
* - Value of the field len of the nsapi_msghdr_t needs to bet set to the size of the whole extending type.
* For example:
*
* @code
nsapi_pktinfo_t pkt_info;
pkt_info.hdr.len = sizeof(nsapi_pktinfo_t);
* @endcode
*
* This value is used in the MsgHeaderIterator to calculate proper addresses of the list elements.
*
* @par Example
*
* Code presenting minimal usage example.
*
* @code
struct default_buffer_t {
default_buffer_t()
{
el1.hdr.len = sizeof(nsapi_pktinfo_t);
el2.len = sizeof(nsapi_msghdr_t);
el3.len = sizeof(nsapi_msghdr_t);
el4.hdr.len = sizeof(nsapi_pktinfo_t);
}
nsapi_pktinfo_t el1;
nsapi_msghdr_t el2;
nsapi_msghdr_t el3;
nsapi_pktinfo_t el4;
};
default_buffer buff;
nsapi_msghdr_t *hdr_p = reinterpret_cast<nsapi_msghdr_t *>(&buff);
MsgHeaderIterator it(hdr_p, sizeof(buff));
it.has_next() // returns true
auto p1 = it.next() // returns pointer to el1
auto p2 = it.next() // returns pointer to el2
auto p3 = it.next() // returns pointer to el3
auto p4 = it.next() // returns pointer to el4
it.has_next() // returns false
auto p5 = it.next() // returns nullptr
* @endcode
*
* @note More usage examples are implemented in the MsgHeaderIterator unit test
* in netsocket/tests/UNITTESTS/NetworkStack/test_MsgHeaderIterator.cpp
*/
struct MsgHeaderIterator {
/** Create a MsgHeaderIterator over given nsapi_msghdr_t list.
*
* @param hdr Pointer to the first list element.
* @param size Size of the whole list.
*/
MsgHeaderIterator(nsapi_msghdr_t *hdr, nsapi_size_t size) :
start(hdr),
current(nullptr),
size(size)
{}
/** Checks if the next address of the iterator is a valid list member.
*
* @retval True if the next address is a valid member.
* @retval False otherwise.
*/
bool has_next()
{
if (current == nullptr) {
if (start != nullptr && start->len <= size && start->len >= sizeof(*start)) {
return true;
} else {
return false;
}
}
if (current->len < sizeof(*current)) {
return false;
}
if (get_next_aligned_addr() >= (reinterpret_cast<uint8_t *>(start) + size)) {
return false;
}
return true;
}
/** Returns next element of the list.
*
* @retval nullptr if the list doesn't contain next element.
* @retval Pointer to the next element otherwise.
*/
nsapi_msghdr_t *next()
{
if (!has_next()) {
return nullptr;
}
if (current == nullptr) {
current = start;
} else {
current = reinterpret_cast<nsapi_msghdr *>(get_next_aligned_addr());
}
return current;
}
private:
// Get address of the next member aligned to the size of msghdr_t.
void *get_next_aligned_addr()
{
size_t remaining_size = size - (reinterpret_cast<uintptr_t>(current) - reinterpret_cast<uintptr_t>(start));
void *next = reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(current) + current->len);
next = std::align(
alignof(nsapi_msghdr_t),
sizeof(nsapi_msghdr_t),
next,
remaining_size
);
return next;
}
nsapi_msghdr_t *start;
nsapi_msghdr_t *current;
nsapi_size_t size;
};
#endif

View File

@ -395,6 +395,65 @@ protected:
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_sendto_control(nsapi_socket_t handle, const SocketAddress &address,
const void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size)
{
if (control != NULL) {
return NSAPI_ERROR_UNSUPPORTED;
}
return socket_sendto(handle, address, data, size);
}
/** 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_recvfrom_control(nsapi_socket_t handle, SocketAddress *address,
void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size)
{
if (control != NULL) {
return NSAPI_ERROR_UNSUPPORTED;
}
return socket_recvfrom(handle, address, data, size);
}
/** Register a callback on state change of the socket
*
* The specified callback will be called on state changes such as when

View File

@ -160,9 +160,10 @@ public:
* @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;
}

View File

@ -157,6 +157,55 @@ public:
virtual nsapi_size_or_error_t recvfrom(SocketAddress *address,
void *data, nsapi_size_t size) = 0;
/** Send a message on a socket.
*
* The sendto_control() 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 sendto_control(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 recvfrom_control() 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 recvfrom_control(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

View File

@ -145,6 +145,53 @@ public:
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, sendto_control 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 sendto_control(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, recvfrom_control 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 recvfrom_control(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.

View File

@ -185,6 +185,14 @@ public:
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 sendto_control(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 recvfrom_control(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;

View File

@ -22,6 +22,8 @@
#define NSAPI_TYPES_H
#include <stdint.h>
#include <stddef.h>
#include "mbed_toolchain.h"
#ifdef __cplusplus
extern "C" {
@ -318,7 +320,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 sendto_control/recvfrom_control */
} nsapi_socket_option_t;
typedef enum nsapi_tlssocket_level {
@ -406,6 +409,26 @@ 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 {
/* In C alignment can't be specified in the type declaration.
* Specifying it in the first member definition will affect whole structure.
*/
MBED_ALIGN(max_align_t) 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 +667,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_sendto_control)(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_recvfrom_control)(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

View File

@ -244,12 +244,26 @@ nsapi_size_or_error_t CellularNonIPSocket::sendto(const SocketAddress &address,
{
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_size_or_error_t CellularNonIPSocket::sendto_control(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::recvfrom_control(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;

View File

@ -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) {

View File

@ -27,7 +27,9 @@ 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::sendto_control(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 +46,8 @@ 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_sendto_control(_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 +77,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 sendto_control(address, data, size, NULL, 0);
}
nsapi_size_or_error_t InternetDatagramSocket::send(const void *data, nsapi_size_t size)
{
if (!_remote_peer) {
@ -82,7 +91,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::recvfrom_control(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 +114,8 @@ 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_recvfrom_control(_socket, address, buffer, size, control,
control_size);
// Filter incomming packets using connected peer address
if (recv >= 0 && _remote_peer && _remote_peer != *address) {
@ -143,6 +154,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 recvfrom_control(address, buffer, size, NULL, 0);
}
nsapi_size_or_error_t InternetDatagramSocket::recv(void *buffer, nsapi_size_t size)
{
return recvfrom(NULL, buffer, size);

View File

@ -399,6 +399,50 @@ protected:
return err;
}
nsapi_size_or_error_t socket_sendto_control(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_sendto_control) {
return NSAPI_ERROR_UNSUPPORTED;
}
return _stack_api()->socket_sendto_control(_stack(), socket, address.get_addr(), address.get_port(), data,
size, control, control_size);
}
nsapi_size_or_error_t socket_recvfrom_control(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_recvfrom_control) {
return NSAPI_ERROR_UNSUPPORTED;
}
nsapi_addr_t addr = {NSAPI_IPv4, 0};
uint16_t port = 0;
nsapi_size_or_error_t err = _stack_api()->socket_recvfrom_control(_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) {

View File

@ -173,6 +173,15 @@ nsapi_size_or_error_t TCPSocket::sendto(const SocketAddress &address, const void
return send(data, size);
}
nsapi_size_or_error_t TCPSocket::sendto_control(const SocketAddress &address, const void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size)
{
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 +238,15 @@ nsapi_size_or_error_t TCPSocket::recvfrom(SocketAddress *address, void *data, ns
return recv(data, size);
}
nsapi_size_or_error_t TCPSocket::recvfrom_control(SocketAddress *address, void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size)
{
if (control) {
return NSAPI_ERROR_UNSUPPORTED;
}
return recvfrom(address, data, size);
}
nsapi_error_t TCPSocket::listen(int backlog)
{
_lock.lock();

View File

@ -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::sendto_control(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,12 @@ nsapi_size_or_error_t TLSSocketWrapper::recvfrom(SocketAddress *address, void *d
return recv(data, size);
}
nsapi_size_or_error_t TLSSocketWrapper::recvfrom_control(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

View File

@ -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!");

View File

@ -148,7 +148,7 @@ public:
* @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
*

View File

@ -136,6 +136,12 @@ protected:
{
return return_value;
};
virtual nsapi_size_or_error_t socket_sendto_control(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 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,16 @@ protected:
}
return return_value;
};
virtual nsapi_size_or_error_t socket_recvfrom_control(nsapi_socket_t handle, 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;
}
return socket_recvfrom(handle, address, data, size);
};
virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data) {};
private:

View File

@ -47,9 +47,18 @@ public:
MOCK_METHOD5(setsockopt, nsapi_error_t(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen));
MOCK_METHOD5(getsockopt, nsapi_error_t(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned *optlen));
// MOCK_METHOD3(socket_attach, void(nsapi_socket_t handle, void (*callback)(void *), void *data));
MOCK_METHOD3(add_ethernet_interface, nsapi_error_t(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out));
MOCK_METHOD3(add_ppp_interface, nsapi_error_t(PPP &ppp, bool default_if, OnboardNetworkStack::Interface **interface_out));
MOCK_METHOD1(set_default_interface, void (OnboardNetworkStack::Interface *interface));
MOCK_METHOD4(add_ethernet_interface_mock, nsapi_error_t(EMAC &emac, bool default_if,
OnboardNetworkStack::Interface **interface_out,
NetworkInterface *user_network_interface));
// Wrapper written to handle function with the default argument in the gmock.
nsapi_error_t add_ethernet_interface(EMAC &emac, bool default_if, Interface **interface_out, NetworkInterface *user_network_interface = NULL)
{
return add_ethernet_interface_mock(emac, default_if, interface_out, user_network_interface);
}
void *socket_cb;
// No need to mock socket_attach really.
void socket_attach(nsapi_socket_t handle, void (*callback)(void *), void *data)
@ -57,7 +66,6 @@ public:
socket_cb = data;
};
static OnboardNetworkStackMock &get_instance()
{
static OnboardNetworkStackMock stackMock1;

View File

@ -68,7 +68,7 @@ protected:
/* Enclose the heavily-used connection procedure to improve code redability */
void doConnect(bool dhcp = true, bool blocking = true)
{
EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _))
EXPECT_CALL(*stackMock, add_ethernet_interface_mock(testing::Ref(*emacMock), true, _, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_OK)));
EXPECT_CALL(*netStackIface, attach(_))
@ -113,7 +113,7 @@ TEST_F(TestEthernetInterface, connect)
TEST_F(TestEthernetInterface, connect_failure)
{
EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _))
EXPECT_CALL(*stackMock, add_ethernet_interface_mock(testing::Ref(*emacMock), true, _, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_NO_MEMORY)));
EXPECT_EQ(NSAPI_ERROR_NO_MEMORY, iface->connect());
@ -158,7 +158,7 @@ TEST_F(TestEthernetInterface, set_network)
EXPECT_EQ(NSAPI_ERROR_OK, iface->set_network(ipAddress, netmask, gateway));
// Now the bringup should have different arguments. We can't use doConnect method.
EXPECT_CALL(*stackMock, add_ethernet_interface(testing::Ref(*emacMock), true, _))
EXPECT_CALL(*stackMock, add_ethernet_interface_mock(testing::Ref(*emacMock), true, _, _))
.Times(1)
.WillOnce(DoAll(SetArgPointee<2>(netStackIface), Return(NSAPI_ERROR_OK)));
EXPECT_CALL(*netStackIface, attach(_))

View File

@ -56,6 +56,16 @@ public:
{
return return_value;
}
virtual nsapi_size_or_error_t sendto_control(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 recvfrom_control(SocketAddress *address, void *data, nsapi_size_t size,
nsapi_msghdr_t *control, nsapi_size_t control_size)
{
return return_value;
}
virtual Socket *accept(nsapi_error_t *error = NULL)
{
return NULL;

View File

@ -22,6 +22,7 @@ target_sources(${TEST_NAME}
${mbed-os_SOURCE_DIR}/connectivity/libraries/nanostack-libservice/source/libip6string/stoip6.c
${mbed-os_SOURCE_DIR}/connectivity/libraries/nanostack-libservice/source/libBits/common_functions.c
test_NetworkStack.cpp
test_MsgHeaderIterator.cpp
)
target_link_libraries(${TEST_NAME}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021, Arm Limited and affiliates
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gtest/gtest.h"
#include "netsocket/MsgHeader.h"
#include "netsocket/nsapi_types.h"
struct custom_type_t {
nsapi_msghdr_t hdr;
uint64_t custom_field1;
uint64_t custom_field2;
uint64_t custom_field3;
uint64_t custom_field4;
};
struct pktinfo_buffer_t {
pktinfo_buffer_t()
{
pkt_info_1.hdr.len = sizeof(nsapi_pktinfo_t);
pkt_info_2.hdr.len = sizeof(nsapi_pktinfo_t);
}
nsapi_pktinfo_t pkt_info_1;
nsapi_pktinfo_t pkt_info_2;
};
struct custom_buffer_t {
custom_buffer_t()
{
pkt_info.hdr.len = sizeof(nsapi_pktinfo_t);
msg_hdr.len = sizeof(nsapi_msghdr_t);
custom.hdr.len = sizeof(custom_type_t);
}
nsapi_pktinfo_t pkt_info;
nsapi_msghdr_t msg_hdr;
custom_type_t custom;
};
class TestMsgHeaderIterator : public testing::Test {
protected:
pktinfo_buffer_t p_buff;
custom_buffer_t c_buff;
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(TestMsgHeaderIterator, pktinfo_list)
{
nsapi_msghdr_t *hdr = reinterpret_cast<nsapi_msghdr_t *>(&p_buff);
MsgHeaderIterator it(hdr, sizeof(p_buff));
EXPECT_EQ(it.next(), &p_buff.pkt_info_1.hdr);
EXPECT_EQ(it.next(), &p_buff.pkt_info_2.hdr);
EXPECT_TRUE(it.next() == nullptr);
}
TEST_F(TestMsgHeaderIterator, custom_list)
{
nsapi_msghdr_t *hdr_c = reinterpret_cast<nsapi_msghdr_t *>(&c_buff);
MsgHeaderIterator it(hdr_c, sizeof(c_buff));
EXPECT_EQ(it.next(), &c_buff.pkt_info.hdr);
EXPECT_EQ(it.next(), &c_buff.msg_hdr);
EXPECT_EQ(it.next(), &c_buff.custom.hdr);
EXPECT_TRUE(it.next() == nullptr);
}
TEST_F(TestMsgHeaderIterator, null_list)
{
MsgHeaderIterator it(nullptr, sizeof(p_buff));
EXPECT_TRUE(it.next() == nullptr);
}
TEST_F(TestMsgHeaderIterator, wrong_list_size)
{
nsapi_msghdr_t *hdr = reinterpret_cast<nsapi_msghdr_t *>(&p_buff);
MsgHeaderIterator it(hdr, 0);
EXPECT_TRUE(it.next() == nullptr);
}
TEST_F(TestMsgHeaderIterator, wrong_member_len)
{
nsapi_msghdr_t *hdr = reinterpret_cast<nsapi_msghdr_t *>(&p_buff);
MsgHeaderIterator it(hdr, sizeof(p_buff));
p_buff.pkt_info_1.hdr.len = 0;
EXPECT_TRUE(it.next() == nullptr);
}