From b971c575ad0c7c57742bc2551ad4255fc5431d80 Mon Sep 17 00:00:00 2001 From: Martine Lenders <mlenders@inf.fu-berlin.de> Date: Thu, 9 Jun 2016 11:08:53 +0200 Subject: [PATCH] gnrc_sock_udp: provide port for sock_udp --- Makefile.dep | 13 + Makefile.pseudomodules | 5 + sys/Makefile.include | 6 + sys/net/gnrc/Makefile | 9 + sys/net/gnrc/sock/Makefile | 3 + sys/net/gnrc/sock/gnrc_sock.c | 197 +++++++++++++ .../gnrc/sock/include/gnrc_sock_internal.h | 99 +++++++ sys/net/gnrc/sock/include/sock_types.h | 70 +++++ sys/net/gnrc/sock/udp/Makefile | 3 + sys/net/gnrc/sock/udp/gnrc_sock_udp.c | 277 ++++++++++++++++++ 10 files changed, 682 insertions(+) create mode 100644 sys/net/gnrc/sock/Makefile create mode 100644 sys/net/gnrc/sock/gnrc_sock.c create mode 100644 sys/net/gnrc/sock/include/gnrc_sock_internal.h create mode 100644 sys/net/gnrc/sock/include/sock_types.h create mode 100644 sys/net/gnrc/sock/udp/Makefile create mode 100644 sys/net/gnrc/sock/udp/gnrc_sock_udp.c diff --git a/Makefile.dep b/Makefile.dep index 802f98b2aa..ccd736ad55 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -57,6 +57,19 @@ ifneq (,$(filter gnrc_conn_udp,$(USEMODULE))) USEMODULE += gnrc_udp endif +ifneq (,$(filter gnrc_sock_%,$(USEMODULE))) + USEMODULE += gnrc_sock +endif + +ifneq (,$(filter gnrc_sock_udp,$(USEMODULE))) + USEMODULE += gnrc_udp + USEMODULE += random # to generate random ports +endif + +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + USEMODULE += gnrc_netapi_mbox +endif + ifneq (,$(filter gnrc_netapi_mbox,$(USEMODULE))) USEMODULE += core_mbox endif diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules index 27976a1dab..cf0349561d 100644 --- a/Makefile.pseudomodules +++ b/Makefile.pseudomodules @@ -21,6 +21,7 @@ PSEUDOMODULES += gnrc_sixlowpan_iphc_nhc PSEUDOMODULES += gnrc_sixlowpan_nd_border_router PSEUDOMODULES += gnrc_sixlowpan_router PSEUDOMODULES += gnrc_sixlowpan_router_default +PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += log PSEUDOMODULES += log_printfnoformat PSEUDOMODULES += lwip_arp @@ -51,6 +52,10 @@ PSEUDOMODULES += saul_adc PSEUDOMODULES += saul_default PSEUDOMODULES += saul_gpio PSEUDOMODULES += schedstatistics +PSEUDOMODULES += sock +PSEUDOMODULES += sock_ip +PSEUDOMODULES += sock_tcp +PSEUDOMODULES += sock_udp # include variants of the AT86RF2xx drivers as pseudo modules PSEUDOMODULES += at86rf23% diff --git a/sys/Makefile.include b/sys/Makefile.include index 51790bc3ec..7a460af0f8 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -9,6 +9,12 @@ endif ifneq (,$(filter fib,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include endif +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/gnrc/sock/include + ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) + CFLAGS += -DSOCK_HAS_IPV6 + endif +endif ifneq (,$(filter posix,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include endif diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index a4b69955ff..0eb7f61ef7 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -112,6 +112,15 @@ endif ifneq (,$(filter gnrc_slip,$(USEMODULE))) DIRS += link_layer/slip endif +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + DIRS += sock +endif +ifneq (,$(filter gnrc_sock_ip,$(USEMODULE))) + DIRS += sock/ip +endif +ifneq (,$(filter gnrc_sock_udp,$(USEMODULE))) + DIRS += sock/udp +endif ifneq (,$(filter gnrc_udp,$(USEMODULE))) DIRS += transport_layer/udp endif diff --git a/sys/net/gnrc/sock/Makefile b/sys/net/gnrc/sock/Makefile new file mode 100644 index 0000000000..667aec4086 --- /dev/null +++ b/sys/net/gnrc/sock/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sock + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/sock/gnrc_sock.c b/sys/net/gnrc/sock/gnrc_sock.c new file mode 100644 index 0000000000..8b81758c07 --- /dev/null +++ b/sys/net/gnrc/sock/gnrc_sock.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ + +#include <errno.h> + +#include "net/af.h" +#include "net/ipv6/hdr.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/gnrc/netreg.h" +#include "net/udp.h" +#include "utlist.h" +#include "xtimer.h" + +#include "sock_types.h" +#include "gnrc_sock_internal.h" + +#ifdef MODULE_XTIMER +#define _TIMEOUT_MAGIC (0xF38A0B63U) +#define _TIMEOUT_MSG_TYPE (0x8474) + +static void _callback_put(void *arg) +{ + msg_t timeout_msg = { .sender_pid = KERNEL_PID_UNDEF, + .type = _TIMEOUT_MSG_TYPE, + .content = { .value = _TIMEOUT_MAGIC } }; + gnrc_sock_reg_t *reg = arg; + + /* should be safe, because otherwise if mbox were filled this callback is + * senseless */ + mbox_try_put(®->mbox, &timeout_msg); +} +#endif + +void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx) +{ + mbox_init(®->mbox, reg->mbox_queue, SOCK_MBOX_SIZE); + gnrc_netreg_entry_init_mbox(®->entry, demux_ctx, ®->mbox); + gnrc_netreg_register(type, ®->entry); +} + +ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt_out, + uint32_t timeout, sock_ip_ep_t *remote) +{ + gnrc_pktsnip_t *pkt, *ip, *netif; + msg_t msg; + +#ifdef MODULE_XTIMER + xtimer_t timeout_timer; + + if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) { + timeout_timer.callback = _callback_put; + timeout_timer.arg = reg; + xtimer_set(&timeout_timer, timeout); + } +#endif + if (timeout != 0) { + mbox_get(®->mbox, &msg); + } + else { + if (!mbox_try_get(®->mbox, &msg)) { + return -EAGAIN; + } + } +#ifdef MODULE_XTIMER + xtimer_remove(&timeout_timer); +#endif + switch (msg.type) { + case GNRC_NETAPI_MSG_TYPE_RCV: + pkt = msg.content.ptr; + break; +#ifdef MODULE_XTIMER + case _TIMEOUT_MSG_TYPE: + if (msg.content.value == _TIMEOUT_MAGIC) { + return -ETIMEDOUT; + } +#endif + default: + return -EINTR; + } + /* TODO: discern NETTYPE from remote->family (set in caller), when IPv4 + * was implemented */ + ipv6_hdr_t *ipv6_hdr; + ip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + assert((ip != NULL) && (ip->size >= 40)); + ipv6_hdr = ip->data; + memcpy(&remote->addr, &ipv6_hdr->src, sizeof(ipv6_addr_t)); + remote->family = AF_INET6; + netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif == NULL) { + remote->netif = SOCK_ADDR_ANY_NETIF; + } + else { + gnrc_netif_hdr_t *netif_hdr = netif->data; + /* TODO: use API in #5511 */ + remote->netif = (uint16_t)netif_hdr->if_pid; + } + *pkt_out = pkt; /* set out parameter */ + return 0; +} + +ssize_t gnrc_sock_send(gnrc_pktsnip_t *payload, sock_ip_ep_t *local, + const sock_ip_ep_t *remote, uint8_t nh) +{ + gnrc_pktsnip_t *pkt; + kernel_pid_t iface = KERNEL_PID_UNDEF; + gnrc_nettype_t type; + size_t payload_len = gnrc_pkt_len(payload); + + if (local->family != remote->family) { + gnrc_pktbuf_release(payload); + return -EAFNOSUPPORT; + } + switch (local->family) { +#ifdef SOCK_HAS_IPV6 + case AF_INET6: { + ipv6_hdr_t *hdr; + pkt = gnrc_ipv6_hdr_build(payload, (ipv6_addr_t *)&local->addr.ipv6, + (ipv6_addr_t *)&remote->addr.ipv6); + if (pkt == NULL) { + return -ENOMEM; + } + if (payload->type == GNRC_NETTYPE_UNDEF) { + payload->type = GNRC_NETTYPE_IPV6; + type = GNRC_NETTYPE_IPV6; + } + else { + type = payload->type; + } + hdr = pkt->data; + hdr->nh = nh; + break; + } +#endif + default: + (void)nh; + gnrc_pktbuf_release(payload); + return -EAFNOSUPPORT; + } + if (local->netif != SOCK_ADDR_ANY_NETIF) { + /* TODO: use API in #5511 */ + iface = (kernel_pid_t)local->netif; + } + else if (remote->netif != SOCK_ADDR_ANY_NETIF) { + /* TODO: use API in #5511 */ + iface = (kernel_pid_t)remote->netif; + } + if (iface != KERNEL_PID_UNDEF) { + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + gnrc_netif_hdr_t *netif_hdr; + + if (netif == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + netif_hdr = netif->data; + netif_hdr->if_pid = iface; + LL_PREPEND(pkt, netif); + } +#ifdef MODULE_GNRC_NETERR + gnrc_neterr_reg(pkt); /* no error should occur since pkt was created here */ +#endif + if (!gnrc_netapi_dispatch_send(type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { + /* this should not happen, but just in case */ + gnrc_pktbuf_release(pkt); + return -EBADMSG; + } +#ifdef MODULE_GNRC_NETERR + msg_t err_report; + err_report.type = 0; + + while (err_report.type != GNRC_NETERR_MSG_TYPE) { + msg_try_receive(err_report); + if (err_report.type != GNRC_NETERR_MSG_TYPE) { + msg_try_send(err_report, sched_active_pid); + } + } + if (err_report.content.value != GNRC_NETERR_SUCCESS) { + return (int)(-err_report.content.value); + } +#endif + return payload_len; +} + +/** @} */ diff --git a/sys/net/gnrc/sock/include/gnrc_sock_internal.h b/sys/net/gnrc/sock/include/gnrc_sock_internal.h new file mode 100644 index 0000000000..49cb476385 --- /dev/null +++ b/sys/net/gnrc/sock/include/gnrc_sock_internal.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_gnrc_sock GNRC-specific implementation of the sock API + * @ingroup net_gnrc + * @brief Provides an implementation of the @ref net_sock by the + * @ref net_gnrc + * + * @{ + * + * @file + * @brief GNRC-specific types and function definitions + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef GNRC_SOCK_INTERNAL_H_ +#define GNRC_SOCK_INTERNAL_H_ + +#include <stdbool.h> +#include <stdint.h> +#include "mbox.h" +#include "net/af.h" +#include "net/gnrc.h" +#include "net/gnrc/netreg.h" +#include "net/sock/ip.h" + +#include "sock_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Internal helper functions for GNRC + * @internal + * @{ + */ +/** + * @brief Checks if address family is not supported + * @internal + */ +static inline bool gnrc_af_not_supported(int af) +{ + /* TODO: add AF_INET support */ + return (af != AF_INET6); +} + +/** + * @brief Check if end point points to any address + * @internal + */ +static inline bool gnrc_ep_addr_any(const sock_ip_ep_t *ep) +{ + assert(ep != NULL); + const uint8_t *p = (uint8_t *)&ep->addr; + for (uint8_t i = 0; i < sizeof(ep->addr); i++) { + if (p[i] != 0) { + return false; + } + } + return true; +} + +/** + * @brief Create a sock internally + * @internal + */ +void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx); + +/** + * @brief Receive a packet internally + * @internal + */ +ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt, uint32_t timeout, + sock_ip_ep_t *remote); + +/** + * @brief Send a packet internally + * @internal + */ +ssize_t gnrc_sock_send(gnrc_pktsnip_t *payload, sock_ip_ep_t *local, + const sock_ip_ep_t *remote, uint8_t nh); +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_SOCK_INTERNAL_H_ */ +/** @} */ diff --git a/sys/net/gnrc/sock/include/sock_types.h b/sys/net/gnrc/sock/include/sock_types.h new file mode 100644 index 0000000000..17fcbf8fd4 --- /dev/null +++ b/sys/net/gnrc/sock/include/sock_types.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_gnrc_sock GNRC-specific implementation of the sock API + * @ingroup net_gnrc + * @brief Provides an implementation of the @ref net_sock by the + * @ref net_gnrc + * + * @{ + * + * @file + * @brief GNRC-specific types and function definitions + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef SOCK_TYPES_H_ +#define SOCK_TYPES_H_ + +#include <stdbool.h> +#include <stdint.h> +#include "mbox.h" +#include "net/gnrc.h" +#include "net/gnrc/netreg.h" +#include "net/sock/ip.h" +#include "net/sock/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SOCK_MBOX_SIZE +#define SOCK_MBOX_SIZE (8) /**< Size for gnrc_sock_reg_t::mbox_queue */ +#endif + +/** + * @brief sock @ref net_gnrc_netreg info + * @internal + */ +typedef struct gnrc_sock_reg { +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + struct gnrc_sock_reg *next; /**< list-like for internal storage */ +#endif + gnrc_netreg_entry_t entry; /**< @ref net_gnrc_netreg entry for mbox */ + mbox_t mbox; /**< @ref core_mbox target for the sock */ + msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for gnrc_sock_reg_t::mbox */ +} gnrc_sock_reg_t; + +/** + * @brief UDP sock type + * @internal + */ +struct sock_udp { + gnrc_sock_reg_t reg; /**< netreg info */ + sock_udp_ep_t local; /**< local end-point */ + sock_udp_ep_t remote; /**< remote end-point */ + uint16_t flags; /**< option flags */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_TYPES_H_ */ +/** @} */ diff --git a/sys/net/gnrc/sock/udp/Makefile b/sys/net/gnrc/sock/udp/Makefile new file mode 100644 index 0000000000..257f780a5c --- /dev/null +++ b/sys/net/gnrc/sock/udp/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sock_udp + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c new file mode 100644 index 0000000000..31bb5897b8 --- /dev/null +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief GNRC implementation of @ref net_sock_udp + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ + +#include <errno.h> + +#include "byteorder.h" +#include "net/af.h" +#include "net/protnum.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/udp.h" +#include "net/sock/udp.h" +#include "net/udp.h" +#include "random.h" + +#include "gnrc_sock_internal.h" + +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE +static sock_udp_t *_udp_socks = NULL; +#endif + +int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local, + const sock_udp_ep_t *remote, uint16_t flags) +{ + assert(sock); + assert(local == NULL || local->port != 0); + assert(remote == NULL || remote->port != 0); + if ((local != NULL) && (remote != NULL) && + (local->netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (local->netif != remote->netif)) { + return -EINVAL; + } + memset(&sock->local, 0, sizeof(sock_udp_ep_t)); + if (local != NULL) { +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if (!(flags & SOCK_FLAGS_REUSE_EP)) { + for (sock_udp_t *ptr = _udp_socks; ptr != NULL; + ptr = (sock_udp_t *)ptr->reg.next) { + if (memcmp(&ptr->local, local, sizeof(sock_udp_ep_t)) == 0) { + return -EADDRINUSE; + } + } + } + /* prepend to current socks */ + sock->reg.next = (gnrc_sock_reg_t *)_udp_socks; + _udp_socks = sock; +#endif + if (gnrc_af_not_supported(local->family)) { + return -EAFNOSUPPORT; + } + memcpy(&sock->local, local, sizeof(sock_udp_ep_t)); + } + memset(&sock->remote, 0, sizeof(sock_udp_ep_t)); + if (remote != NULL) { + if (gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + if (gnrc_ep_addr_any((const sock_ip_ep_t *)remote)) { + return -EINVAL; + } + memcpy(&sock->remote, remote, sizeof(sock_udp_ep_t)); + } + if (local != NULL) { + /* listen only with local given */ + gnrc_sock_create(&sock->reg, GNRC_NETTYPE_UDP, + local->port); + } + sock->flags = flags; + return 0; +} + +void sock_udp_close(sock_udp_t *sock) +{ + assert(sock != NULL); + gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &sock->reg.entry); +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if (_udp_socks != NULL) { + gnrc_sock_reg_t *head = (gnrc_sock_reg_t *)_udp_socks; + LL_DELETE(head, (gnrc_sock_reg_t *)sock); + } +#endif +} + +int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *local) +{ + assert(sock && local); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + memcpy(local, &sock->local, sizeof(sock_udp_ep_t)); + return 0; +} + +int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *remote) +{ + assert(sock && remote); + if (sock->remote.family == AF_UNSPEC) { + return -ENOTCONN; + } + memcpy(remote, &sock->remote, sizeof(sock_udp_ep_t)); + return 0; +} + +ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote) +{ + gnrc_pktsnip_t *pkt, *udp; + udp_hdr_t *hdr; + sock_ip_ep_t tmp; + int res; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + tmp.family = sock->local.family; + res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp); + if (res < 0) { + return res; + } + if (pkt->size > max_len) { + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + udp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); + assert(udp); + hdr = udp->data; + if (remote != NULL) { + /* return remote to possibly block if wrong remote */ + memcpy(remote, &tmp, sizeof(tmp)); + remote->port = byteorder_ntohs(hdr->src_port); + } + if ((sock->remote.family != AF_UNSPEC) && /* check remote end-point if set */ + ((sock->remote.port != byteorder_ntohs(hdr->src_port)) || + /* We only have IPv6 for now, so just comparing the whole end point + * should suffice */ + ((memcmp(&sock->remote.addr, &ipv6_addr_unspecified, + sizeof(ipv6_addr_t)) != 0) && + (memcmp(&sock->remote.addr, &tmp.addr, sizeof(ipv6_addr_t)) != 0)))) { + gnrc_pktbuf_release(pkt); + return -EPROTO; + } + memcpy(data, pkt->data, pkt->size); + gnrc_pktbuf_release(pkt); + return (int)pkt->size; +} + +ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote) +{ + int res; + gnrc_pktsnip_t *payload, *pkt; + uint16_t src_port = 0, dst_port; + sock_ip_ep_t local; + sock_ip_ep_t rem; + + assert((sock != NULL) || (remote != NULL)); + assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ + if ((remote != NULL) && (sock != NULL) && + (sock->local.netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (sock->local.netif != remote->netif)) { + return -EINVAL; + } + if ((remote != NULL) && ((remote->port == 0) || + gnrc_ep_addr_any((const sock_ip_ep_t *)remote))) { + return -EINVAL; + } + if ((remote == NULL) && + /* sock can't be NULL as per assertion above */ + (sock->remote.port == AF_UNSPEC)) { + return -ENOTCONN; + } + /* compiler evaluates lazily so this isn't a redundundant check and cppcheck + * is being weird here anyways */ + /* cppcheck-suppress nullPointerRedundantCheck */ + /* cppcheck-suppress nullPointer */ + if ((sock == NULL) || (sock->local.family == AF_UNSPEC)) { + /* no sock or sock currently unbound */ + while (src_port == 0) { + src_port = (uint16_t)(random_uint32() & UINT16_MAX); +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if ((sock == NULL) || !(sock->flags & SOCK_FLAGS_REUSE_EP)) { + /* check if port already registered somewhere */ + for (sock_udp_t *ptr = _udp_socks; ptr != NULL; + ptr = (sock_udp_t *)ptr->reg.next) { + bool spec_addr = false; + for (unsigned i = 0; i < sizeof(ptr->local.addr); i++) { + const uint8_t *const p = (uint8_t *)&ptr->local.addr; + if (p[i] != 0) { + spec_addr = true; + } + } + if (spec_addr) { + continue; + } + if (ptr->local.port == src_port) { + /* we already have one of this port registered + * => generate a new one */ + src_port = 0; + } + } + } +#endif + } + memset(&local, 0, sizeof(local)); + if (sock != NULL) { + /* bind sock object implicitly */ + sock->local.port = src_port; +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + /* prepend to current socks */ + sock->reg.next = (gnrc_sock_reg_t *)_udp_socks; + _udp_socks = sock; +#endif + } + } + else { + src_port = sock->local.port; + memcpy(&local, &sock->local, sizeof(local)); + } + if (remote == NULL) { + /* sock can't be NULL at this point */ + memcpy(&rem, &sock->remote, sizeof(rem)); + dst_port = sock->remote.port; + } + else { + memcpy(&rem, remote, sizeof(rem)); + dst_port = remote->port; + } + if ((remote != NULL) && (remote->family == AF_UNSPEC) && + (sock->remote.family != AF_UNSPEC)) { + /* remote was set on create so take its family */ + rem.family = sock->remote.family; + } + else if ((remote != NULL) && gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + else if ((local.family == AF_UNSPEC) && (rem.family != AF_UNSPEC)) { + /* local was set to 0 above */ + local.family = rem.family; + } + else if ((local.family != AF_UNSPEC) && (rem.family == AF_UNSPEC)) { + /* local was given on create, but remote family wasn't given by user and + * there was no remote given on create, take from local */ + rem.family = local.family; + } + payload = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF); + if (payload == NULL) { + return -ENOMEM; + } + pkt = gnrc_udp_hdr_build(payload, src_port, dst_port); + if (pkt == NULL) { + gnrc_pktbuf_release(payload); + return -ENOMEM; + } + res = gnrc_sock_send(pkt, &local, &rem, PROTNUM_UDP); + if (res <= 0) { + return res; + } + return res - sizeof(udp_hdr_t); +} + +/** @} */ -- GitLab