diff --git a/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c b/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c
index 20664c80b01793d38e434f250ec97e31edd0ca15..4693a4636a919968d7e67f67dfa89b4c889bfcf9 100644
--- a/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c
+++ b/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c
@@ -84,7 +84,6 @@ int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local,
 
     (void)flags;
     assert((sock != NULL));
-    assert((local == NULL) || (local->port != 0));
     assert((remote == NULL) || (remote->port != 0));
     if (sock->sock.input_callback != NULL) {
         sock_udp_close(sock);
diff --git a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c
index 96a49536ef52c1c1b6a26037da8afeabdb0caf65..e947849857596e1e21a58395e7a5b0eaf873069b 100644
--- a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c
+++ b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c
@@ -29,7 +29,6 @@ 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 != NULL);
-    assert(local == NULL || local->port != 0);
     assert(remote == NULL || remote->port != 0);
 
     int res;
diff --git a/sys/include/net/sock/udp.h b/sys/include/net/sock/udp.h
index 0b9e30e81028e958b334d422a74882e6d2f31b16..f8b978e62974a0b4f56bae7ee1f3ac814576f54b 100644
--- a/sys/include/net/sock/udp.h
+++ b/sys/include/net/sock/udp.h
@@ -293,17 +293,17 @@ typedef struct sock_udp sock_udp_t;
  * @brief   Creates a new UDP sock object
  *
  * @pre `(sock != NULL)`
- * @pre `(local == NULL) || (local->port != 0)`
  * @pre `(remote == NULL) || (remote->port != 0)`
  *
  * @param[out] sock     The resulting sock object.
  * @param[in] local     Local end point for the sock object.
  *                      May be NULL.
- *                      sock_udp_ep_t::port must not be 0 if `local != NULL`.
  *                      sock_udp_ep_t::netif must either be
  *                      @ref SOCK_ADDR_ANY_NETIF or equal to
  *                      sock_udp_ep_t::netif of @p remote if `remote != NULL`.
  *                      If NULL @ref sock_udp_send() may bind implicitly.
+ *                      sock_udp_ep_t::port may also be 0 to bind the `sock` to
+ *                      an ephemeral port.
  * @param[in] remote    Remote end point for the sock object.
  *                      May be `NULL` but then the `remote` parameter of
  *                      @ref sock_udp_send() may not be `NULL` or it will
@@ -318,7 +318,8 @@ typedef struct sock_udp sock_udp_t;
  *
  * @return  0 on success.
  * @return  -EADDRINUSE, if `local != NULL` and @p local is already used
- *         elsewhere
+ *          elsewhere or if `local->port == 0` but the pool of ephemeral ports
+ *          is depleted
  * @return  -EAFNOSUPPORT, if `local != NULL` or `remote != NULL` and
  *          sock_udp_ep_t::family of @p local or @p remote is not supported.
  * @return  -EINVAL, if sock_udp_ep_t::addr of @p remote is an invalid address.
@@ -419,6 +420,8 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len,
  *                      sock_udp_ep_t::port may not be 0.
  *
  * @return  The number of bytes sent on success.
+ * @return  -EADDRINUSE, if `sock` has no local end-point or was `NULL` and the
+ *          pool of available ephemeral ports is depleted.
  * @return  -EAFNOSUPPORT, if `remote != NULL` and sock_udp_ep_t::family of
  *          @p remote is != AF_UNSPEC and not supported.
  * @return  -EHOSTUNREACH, if @p remote or remote end point of @p sock is not
diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c
index 27168242173190cbd2f26f6b6d1d561598cd8841..c2367e242d664d8af9509238b1311993d953df84 100644
--- a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c
+++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c
@@ -87,7 +87,6 @@ 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) &&
@@ -97,8 +96,19 @@ int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local,
     }
     memset(&sock->local, 0, sizeof(sock_udp_ep_t));
     if (local != NULL) {
+        uint16_t port = local->port;
+
+        if (gnrc_af_not_supported(local->family)) {
+            return -EAFNOSUPPORT;
+        }
+        if (port == 0U) {
+            port = _get_dyn_port(sock);
+            if (port == GNRC_SOCK_DYN_PORTRANGE_ERR) {
+                return -EADDRINUSE;
+            }
+        }
 #ifdef MODULE_GNRC_SOCK_CHECK_REUSE
-        if (!(flags & SOCK_FLAGS_REUSE_EP)) {
+        else 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) {
@@ -110,10 +120,8 @@ int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local,
         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));
+        sock->local.port = port;
     }
     memset(&sock->remote, 0, sizeof(sock_udp_ep_t));
     if (remote != NULL) {
@@ -250,7 +258,7 @@ ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len,
         /* no sock or sock currently unbound */
         memset(&local, 0, sizeof(local));
         if ((src_port = _get_dyn_port(sock)) == GNRC_SOCK_DYN_PORTRANGE_ERR) {
-            return -EINVAL;
+            return -EADDRINUSE;
         }
         /* cppcheck-suppress nullPointer
          * (reason: sock *can* be NULL at this place, cppcheck is weird here as
diff --git a/tests/gnrc_sock_udp/main.c b/tests/gnrc_sock_udp/main.c
index d91205171b9dca3636160f8a7f730588afccd6c7..df4961b2789e698d0d137510ff4322f6dfd77f6c 100644
--- a/tests/gnrc_sock_udp/main.c
+++ b/tests/gnrc_sock_udp/main.c
@@ -117,6 +117,22 @@ static void test_sock_udp_create__only_local(void)
     assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
 }
 
+static void test_sock_udp_create__only_local_port0(void)
+{
+    static const sock_udp_ep_t local = { .family = AF_INET6,
+                                         .port = 0U };
+    sock_udp_ep_t ep;
+
+    assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_udp_get_local(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6,
+                  sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(0U != ep.port);
+    assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
+}
+
 static void test_sock_udp_create__only_local_reuse_ep(void)
 {
     static const sock_udp_ep_t local = { .family = AF_INET6,
@@ -663,6 +679,7 @@ int main(void)
     CALL(test_sock_udp_create__EINVAL_netif());
     CALL(test_sock_udp_create__no_endpoints());
     CALL(test_sock_udp_create__only_local());
+    CALL(test_sock_udp_create__only_local_port0());
     CALL(test_sock_udp_create__only_local_reuse_ep());
     CALL(test_sock_udp_create__only_remote());
     CALL(test_sock_udp_create__full());
diff --git a/tests/lwip_sock_udp/main.c b/tests/lwip_sock_udp/main.c
index 77416057ba97fddb95c2d3c5836b033e01d6853f..202b23627bb789e09e0d1be9364a6638c79d06bb 100644
--- a/tests/lwip_sock_udp/main.c
+++ b/tests/lwip_sock_udp/main.c
@@ -117,6 +117,21 @@ static void test_sock_udp_create4__only_local(void)
     assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
 }
 
+static void test_sock_udp_create4__only_local_port0(void)
+{
+    static const sock_udp_ep_t local = { .family = AF_INET,
+                                         .port = 0U };
+    sock_udp_ep_t ep;
+
+    assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_udp_get_local(&_sock, &ep));
+    assert(AF_INET == ep.family);
+    assert(0 == ep.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(0U != ep.port);
+    assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
+}
+
 static void test_sock_udp_create4__only_local_reuse_ep(void)
 {
     static const sock_udp_ep_t local = { .family = AF_INET,
@@ -694,6 +709,22 @@ static void test_sock_udp_create6__only_local(void)
     assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
 }
 
+static void test_sock_udp_create6__only_local_port0(void)
+{
+    static const sock_udp_ep_t local = { .family = AF_INET6,
+                                         .port = 0U };
+    sock_udp_ep_t ep;
+
+    assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_udp_get_local(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6,
+                  sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(0U != ep.port);
+    assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep));
+}
+
 static void test_sock_udp_create6__only_local_reuse_ep(void)
 {
     static const sock_udp_ep_t local = { .family = AF_INET6,
@@ -1256,6 +1287,7 @@ int main(void)
     CALL(test_sock_udp_create4__EINVAL_netif());
     CALL(test_sock_udp_create4__no_endpoints());
     CALL(test_sock_udp_create4__only_local());
+    CALL(test_sock_udp_create4__only_local_port0());
     CALL(test_sock_udp_create4__only_local_reuse_ep());
     CALL(test_sock_udp_create4__only_remote());
     CALL(test_sock_udp_create4__full());
@@ -1301,6 +1333,7 @@ int main(void)
     CALL(test_sock_udp_create6__EINVAL_netif());
     CALL(test_sock_udp_create6__no_endpoints());
     CALL(test_sock_udp_create6__only_local());
+    CALL(test_sock_udp_create6__only_local_port0());
     CALL(test_sock_udp_create6__only_local_reuse_ep());
     CALL(test_sock_udp_create6__only_remote());
     CALL(test_sock_udp_create6__full());