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(&reg->mbox, &timeout_msg);
+}
+#endif
+
+void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx)
+{
+    mbox_init(&reg->mbox, reg->mbox_queue, SOCK_MBOX_SIZE);
+    gnrc_netreg_entry_init_mbox(&reg->entry, demux_ctx, &reg->mbox);
+    gnrc_netreg_register(type, &reg->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(&reg->mbox, &msg);
+    }
+    else {
+        if (!mbox_try_get(&reg->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