diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 05358c868d..a40e41d80c 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -116,6 +116,8 @@ PSEUDOMODULES += shell_hooks PSEUDOMODULES += slipdev_stdio PSEUDOMODULES += sock PSEUDOMODULES += sock_async +PSEUDOMODULES += sock_aux_local +PSEUDOMODULES += sock_aux_timestamp PSEUDOMODULES += sock_dtls PSEUDOMODULES += sock_ip PSEUDOMODULES += sock_tcp diff --git a/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c index d75fdc72f7..bb5a02a7dc 100644 --- a/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c +++ b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c @@ -151,8 +151,9 @@ static int _parse_iphdr(struct netbuf *buf, void **data, void **ctx, return (ssize_t)data_len; } -ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, - uint32_t timeout, sock_ip_ep_t *remote) +ssize_t sock_ip_recv_aux(sock_ip_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux) { void *pkt = NULL; struct netbuf *ctx = NULL; @@ -161,8 +162,8 @@ ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, bool nobufs = false; assert((sock != NULL) && (data != NULL) && (max_len > 0)); - while ((res = sock_ip_recv_buf(sock, &pkt, (void **)&ctx, timeout, - remote)) > 0) { + while ((res = sock_ip_recv_buf_aux(sock, &pkt, (void **)&ctx, timeout, + remote, aux)) > 0) { if (ctx->p->tot_len > (ssize_t)max_len) { nobufs = true; /* progress context to last element */ @@ -176,9 +177,11 @@ ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); } -ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **ctx, - uint32_t timeout, sock_ip_ep_t *remote) +ssize_t sock_ip_recv_buf_aux(sock_ip_t *sock, void **data, void **ctx, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux) { + (void)aux; struct netbuf *buf; int res; @@ -203,9 +206,11 @@ ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **ctx, return res; } -ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len, - uint8_t proto, const sock_ip_ep_t *remote) +ssize_t sock_ip_send_aux(sock_ip_t *sock, const void *data, size_t len, + uint8_t proto, const sock_ip_ep_t *remote, + sock_ip_aux_tx_t *aux) { + (void)aux; assert((sock != NULL) || (remote != NULL)); assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ return lwip_sock_send(sock ? sock->base.conn : NULL, data, len, proto, diff --git a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c index ead05d2e7b..e65a810bad 100644 --- a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c +++ b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c @@ -69,8 +69,9 @@ int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep) 0)) ? -ENOTCONN : 0; } -ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, - uint32_t timeout, sock_udp_ep_t *remote) +ssize_t sock_udp_recv_aux(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux) { void *pkt = NULL; void *ctx = NULL; @@ -79,8 +80,8 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, bool nobufs = false; assert((sock != NULL) && (data != NULL) && (max_len > 0)); - while ((res = sock_udp_recv_buf(sock, &pkt, &ctx, timeout, - remote)) > 0) { + while ((res = sock_udp_recv_buf_aux(sock, &pkt, &ctx, timeout, + remote, aux)) > 0) { struct netbuf *buf = ctx; if (buf->p->tot_len > (ssize_t)max_len) { nobufs = true; @@ -95,9 +96,11 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); } -ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **ctx, - uint32_t timeout, sock_udp_ep_t *remote) +ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **ctx, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux) { + (void)aux; struct netbuf *buf; int res; @@ -147,9 +150,10 @@ ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **ctx, return (ssize_t)buf->ptr->len; } -ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, - const sock_udp_ep_t *remote) +ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux) { + (void)aux; assert((sock != NULL) || (remote != NULL)); assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ diff --git a/pkg/tinydtls/contrib/sock_dtls.c b/pkg/tinydtls/contrib/sock_dtls.c index 952b2be1d1..3692f36a13 100644 --- a/pkg/tinydtls/contrib/sock_dtls.c +++ b/pkg/tinydtls/contrib/sock_dtls.c @@ -351,9 +351,11 @@ void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote) dtls_close(sock->dtls_ctx, &remote->dtls_session); } -ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote, - const void *data, size_t len, uint32_t timeout) +ssize_t sock_dtls_send_aux(sock_dtls_t *sock, sock_dtls_session_t *remote, + const void *data, size_t len, uint32_t timeout, + sock_dtls_aux_tx_t *aux) { + (void)aux; int res; assert(sock); @@ -509,9 +511,11 @@ static ssize_t _complete_handshake(sock_dtls_t *sock, return -SOCK_DTLS_HANDSHAKE; } -ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, - void *data, size_t max_len, uint32_t timeout) +ssize_t sock_dtls_recv_aux(sock_dtls_t *sock, sock_dtls_session_t *remote, + void *data, size_t max_len, uint32_t timeout, + sock_dtls_aux_rx_t *aux) { + (void)aux; assert(sock); assert(data); assert(remote); diff --git a/sys/include/net/sock.h b/sys/include/net/sock.h index ebf9481c06..451bf26e2e 100644 --- a/sys/include/net/sock.h +++ b/sys/include/net/sock.h @@ -245,6 +245,61 @@ struct _sock_tl_ep { uint16_t port; /**< transport layer port (in host byte order) */ }; +/** + * @brief Flags used to request auxiliary data + */ +enum { + /** + * @brief Flag to request the local address/endpoint + * + * @note Select module `sock_aux_local` and a compatible network stack + * to use this + * + * This is the address/endpoint the packet/datagram/segment was received on. + * This flag will be cleared if the network stack stored the local + * address/endpoint as requested, otherwise the bit remains set. + * + * Depending on the family of the socket, the timestamp will be stored in + * @ref sock_udp_aux_rx_t::local, @ref sock_ip_aux_rx_t::local, or in + * @ref sock_dtls_aux_rx_t::local. + */ + SOCK_AUX_GET_LOCAL = (1LU << 0), + /** + * @brief Flag to request the time stamp of transmission / reception + * + * @note Select module `sock_aux_timestamp` and a compatible network + * stack to use this + * + * Unless otherwise noted, the time stamp is the current system time in + * nanoseconds on which the start of frame delimiter or preamble was + * sent / received. + * + * Set this flag in the auxiliary data structure prior to the call of + * @ref sock_udp_recv_aux / @ref sock_udp_send_aux / @ref sock_ip_recv_aux + * / etc. to request the time stamp of reception / transmission. This flag + * will be cleared if the timestamp was stored, otherwise it remains set. + * + * Depending on the family of the socket, the timestamp will be stored in + * for reception in @ref sock_udp_aux_rx_t::timestamp, + * @ref sock_ip_aux_rx_t::timestamp, or @ref sock_dtls_aux_rx_t::timestamp. + * For transmission it will be stored in @ref sock_udp_aux_tx_t::timestamp, + * @ref sock_ip_aux_tx_t::timestamp, or @ref sock_dtls_aux_tx_t::timestamp. + */ + SOCK_AUX_GET_TIMESTAMP = (1LU << 1), +}; + +/** + * @brief Type holding the flags used to request specific auxiliary data + * + * This is a bitmask of `SOCK_AUX_GET_...`, e.g. if the mask contains + * @ref SOCK_AUX_GET_LOCAL, the local address/endpoint is requested + * + * @details The underlying type can be changed without further notice, if more + * flags are needed. Thus, only the `typedef`ed type should be used + * to store the flags. + */ +typedef uint8_t sock_aux_flags_t; + #ifdef __cplusplus } #endif diff --git a/sys/include/net/sock/dtls.h b/sys/include/net/sock/dtls.h index abc5f3e728..345319f6cc 100644 --- a/sys/include/net/sock/dtls.h +++ b/sys/include/net/sock/dtls.h @@ -561,6 +561,26 @@ typedef struct sock_dtls sock_dtls_t; */ typedef struct sock_dtls_session sock_dtls_session_t; +/** + * @brief Auxiliary data provided when receiving using an DTLS sock object + * + * @warning Implementations of this API may rely on this type to be compatible + * with @ref sock_udp_aux_rx_t. These implementations need to be + * updated, if this is no longer the case. Users of this API should + * not rely on this compatibility + */ +typedef sock_udp_aux_rx_t sock_dtls_aux_rx_t; + +/** + * @brief Auxiliary data provided when sending using an DTLS sock object + * + * @warning Implementations of this API may rely on this type to be compatible + * with @ref sock_udp_aux_rx_t. These implementations need to be + * updated, if this is no longer the case. Users of this API should + * not rely on this compatibility + */ +typedef sock_udp_aux_tx_t sock_dtls_aux_tx_t; + /** * @brief Called exactly once during `auto_init`. * @@ -632,6 +652,39 @@ int sock_dtls_session_init(sock_dtls_t *sock, const sock_udp_ep_t *ep, */ void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote); +/** + * @brief Receive handshake messages and application data from remote peer. + * + * @param[in] sock DTLS sock to use. + * @param[out] remote Remote DTLS session of the received data. + * Cannot be NULL. + * @param[out] data Pointer where the received data should be stored. + * @param[in] maxlen Maximum space available at @p data. + * @param[in] timeout Receive timeout in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be SOCK_NO_TIMEOUT to wait until data + * is available. + * @param[out] aux Auxiliary data about the received datagram. + * May be `NULL`, if it is not required by the application. + * + * @note Function may block if data is not available and @p timeout != 0 + * + * @return The number of bytes received on success + * @return -SOCK_DTLS_HANDSHAKE when new handshake is completed + * @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_dtls_recv() blocks). + * @return -ENOBUFS, if buffer space is not large enough to store received + * data. + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -ETIMEDOUT, if @p timeout expired. + */ +ssize_t sock_dtls_recv_aux(sock_dtls_t *sock, sock_dtls_session_t *remote, + void *data, size_t maxlen, uint32_t timeout, + sock_dtls_aux_rx_t *aux); + /** * @brief Receive handshake messages and application data from remote peer. * @@ -659,8 +712,59 @@ void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote); * @return -ENOMEM, if no memory was available to receive @p data. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, - void *data, size_t maxlen, uint32_t timeout); +static inline ssize_t sock_dtls_recv(sock_dtls_t *sock, + sock_dtls_session_t *remote, + void *data, size_t maxlen, + uint32_t timeout) +{ + return sock_dtls_recv_aux(sock, remote, data, maxlen, timeout, NULL); +} + +/** + * @brief Decrypts and provides stack-internal buffer space containing a + * message from a remote peer. + * + * @param[in] sock DTLS sock to use. + * @param[out] remote Remote DTLS session of the received data. + * Cannot be NULL. + * @param[out] data Pointer to a stack-internal buffer space containing the + * received data. + * @param[in,out] buf_ctx Stack-internal buffer context. If it points to a + * `NULL` pointer, the stack returns a new buffer space for + * a new packet. If it does not point to a `NULL` pointer, + * an existing context is assumed to get a next segment in + * a buffer. + * @param[in] timeout Receive timeout in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be SOCK_NO_TIMEOUT to wait until data + * is available. + * @param[out] aux Auxiliary data about the received datagram. + * May be `NULL`, if it is not required by the application. + * + * @experimental This function is quite new, not implemented for all stacks + * yet, and may be subject to sudden API changes. Do not use in + * production if this is unacceptable. + * + * @note Function may block if data is not available and @p timeout != 0 + * + * @note Function blocks if no packet is currently waiting. + * + * @return The number of bytes received on success. May not be the complete + * payload. Continue calling with the returned @p buf_ctx to get more + * buffers until result is 0 or an error. + * @return 0, if no received data is available, but everything is in order. + * If @p buf_ctx was provided, it was released. + * @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_dtls_recv() blocks). + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -ETIMEDOUT, if @p timeout expired. + */ +ssize_t sock_dtls_recv_buf_aux(sock_dtls_t *sock, sock_dtls_session_t *remote, + void **data, void **buf_ctx, uint32_t timeout, + sock_dtls_aux_rx_t *aux); /** * @brief Decrypts and provides stack-internal buffer space containing a @@ -702,8 +806,51 @@ ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, * @return -ENOMEM, if no memory was available to receive @p data. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_dtls_recv_buf(sock_dtls_t *sock, sock_dtls_session_t *remote, - void **data, void **buf_ctx, uint32_t timeout); +static inline ssize_t sock_dtls_recv_buf(sock_dtls_t *sock, + sock_dtls_session_t *remote, + void **data, void **buf_ctx, + uint32_t timeout) +{ + return sock_dtls_recv_buf_aux(sock, remote, data, buf_ctx, timeout, NULL); +} + +/** + * @brief Encrypts and sends a message to a remote peer + * + * @param[in] sock DTLS sock to use + * @param[in] remote DTLS session to use. A new session will be created + * if no session exist between client and server. + * @param[in] data Pointer where the data to be send are stored + * @param[in] len Length of @p data to be send + * @param[in] timeout Handshake timeout in microseconds. + * If `timeout > 0`, will start a new handshake if no + * session exists yet. The function will block until + * handshake completed or timed out. + * May be SOCK_NO_TIMEOUT to block indefinitely until + * handshake complete. + * @param[out] aux Auxiliary data about the transmission. + * May be `NULL`, if it is not required by the application. + * + * @note When blocking, we will need an extra thread to call + * @ref sock_dtls_recv() function to handle the incoming handshake + * messages. + * + * @return The number of bytes sent on success + * @return -ENOTCONN, if `timeout == 0` and no existing session exists with + * @p remote + * @return -EADDRINUSE, if sock_dtls_t::udp_sock has no local end-point. + * @return -EAFNOSUPPORT, if `remote->ep != NULL` and + * sock_dtls_session_t::ep::family of @p remote is != AF_UNSPEC and + * not supported. + * @return -EINVAL, if sock_udp_ep_t::addr of @p remote->ep is an + * invalid address. + * @return -EINVAL, if sock_udp_ep_t::port of @p remote->ep is 0. + * @return -ENOMEM, if no memory was available to send @p data. + * @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out. + */ +ssize_t sock_dtls_send_aux(sock_dtls_t *sock, sock_dtls_session_t *remote, + const void *data, size_t len, uint32_t timeout, + sock_dtls_aux_tx_t *aux); /** * @brief Encrypts and sends a message to a remote peer @@ -737,8 +884,13 @@ ssize_t sock_dtls_recv_buf(sock_dtls_t *sock, sock_dtls_session_t *remote, * @return -ENOMEM, if no memory was available to send @p data. * @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out. */ -ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote, - const void *data, size_t len, uint32_t timeout); +static inline ssize_t sock_dtls_send(sock_dtls_t *sock, + sock_dtls_session_t *remote, + const void *data, size_t len, + uint32_t timeout) +{ + return sock_dtls_send_aux(sock, remote, data, len, timeout, NULL); +} /** * @brief Closes a DTLS sock diff --git a/sys/include/net/sock/ip.h b/sys/include/net/sock/ip.h index 93f5f2d5ad..46e73add53 100644 --- a/sys/include/net/sock/ip.h +++ b/sys/include/net/sock/ip.h @@ -297,6 +297,51 @@ typedef struct sock_ip sock_ip_t; # pragma clang diagnostic pop #endif +/** + * @brief Auxiliary data provided when receiving using an IP sock object + */ +typedef struct { +#if defined(MODULE_SOCK_AUX_LOCAL) || defined(DOXYGEN) + /** + * @brief The local address the packet was received on + * + * @see SOCK_AUX_GET_LOCAL + */ + sock_ip_ep_t local; +#endif /* MODULE_SOCK_AUX_ENDPOINT */ +#if defined(MODULE_SOCK_AUX_TIMESTAMP) || defined(DOXYGEN) + /** + * @brief System time the packet was received + * + * @see SOCK_AUX_GET_TIMESTAMP + */ + uint64_t timestamp; +#endif /* MODULE_SOCK_AUX_TIMESTAP*/ + sock_aux_flags_t flags; /**< Flags used request information */ +} sock_ip_aux_rx_t; + +/** + * @brief Auxiliary data provided when sending using an IP sock object + */ +typedef struct { +#if defined(MODULE_SOCK_AUX_TIMESTAMP) || defined(DOXYGEN) + /** + * @brief System time the packet was send + * + * Add @ref SOCK_AUX_GET_TIMESTAMP to the bitmask in + * @ref sock_ip_aux_tx_t::flags to request a transmission timestamp. This + * bit will be cleared by @ref sock_ip_send_aux if and only if the timestamp + * was provided. The module `sock_aux_timestamp` needs to be selected to use + * this. The timestamp refers to the transmission of start of frame + * delimiter or preamble of the frame carrying the IP packet and is given in + * nanoseconds since epoch, unless otherwise documented by the underlying + * implementation. + */ + uint64_t timestamp; +#endif /* MODULE_SOCK_AUX_TIMESTAP*/ + sock_aux_flags_t flags; /**< Flags used request information */ +} sock_ip_aux_tx_t; + /** * @brief Creates a new raw IPv4/IPv6 sock object * @@ -406,6 +451,8 @@ int sock_ip_get_remote(sock_ip_t *sock, sock_ip_ep_t *ep); * data is available). * @param[out] remote Remote end point of the received data. * May be NULL, if it is not required by the application. + * @param[out] aux Auxiliary data of the reception. + * May be NULL, if it is not required by the application. * * @note Function blocks if no packet is currently waiting. * @@ -422,8 +469,94 @@ int sock_ip_get_remote(sock_ip_t *sock, sock_ip_ep_t *ep); * the remote of @p sock. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, - uint32_t timeout, sock_ip_ep_t *remote); +ssize_t sock_ip_recv_aux(sock_ip_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux); + +/** + * @brief Receives a message over IPv4/IPv6 from remote end point + * + * @pre `(sock != NULL) && (data != NULL) && (max_len > 0)` + * + * @param[in] sock A raw IPv4/IPv6 sock object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * @param[in] timeout Timeout for receive in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be @ref SOCK_NO_TIMEOUT for no timeout (wait until + * data is available). + * @param[out] remote Remote end point of the received data. + * May be NULL, if it is not required by the application. + * + * @note Function blocks if no packet is currently waiting. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return -EADDRNOTAVAIL, if local of @p sock is not given. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_ip_recv() blocks). + * @return -ENOBUFS, if buffer space is not large enough to store received + * data. + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -EPROTO, if source address of received packet did not equal + * the remote of @p sock. + * @return -ETIMEDOUT, if @p timeout expired. + */ +static inline ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_ip_ep_t *remote) +{ + return sock_ip_recv_aux(sock, data, max_len, timeout, remote, NULL); +} + +/** + * @brief Provides stack-internal buffer space containing an IPv4/IPv6 + * message from remote end point + * + * @pre `(sock != NULL) && (data != NULL) && (buf_ctx != NULL)` + * + * @param[in] sock A raw IPv4/IPv6 sock object. + * @param[out] data Pointer to a stack-internal buffer space containing the + * received data. + * @param[in,out] buf_ctx Stack-internal buffer context. If it points to a + * `NULL` pointer, the stack returns a new buffer space + * for a new packet. If it does not point to a `NULL` + * pointer, an existing context is assumed to get a next + * segment in a buffer. + * @param[in] timeout Timeout for receive in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be @ref SOCK_NO_TIMEOUT for no timeout (wait until + * data is available). + * @param[out] remote Remote end point of the received data. + * May be NULL, if it is not required by the application. + * @param[out] aux Auxiliary data of the reception. + * May be NULL, if it is not required by the application. + * + * @experimental This function is quite new, not implemented for all stacks + * yet, and may be subject to sudden API changes. Do not use in + * production if this is unacceptable. + * + * @note Function blocks if no packet is currently waiting. + * + * @return The number of bytes received on success. May not be the complete + * payload. Continue calling with the returned `buf_ctx` to get more + * buffers until result is 0 or an error. + * @return 0, if no received data is available, but everything is in order. + * If @p buf_ctx was provided, it was released. + * @return -EADDRNOTAVAIL, if local of @p sock is not given. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_ip_recv() blocks). + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -EPROTO, if source address of received packet did not equal + * the remote of @p sock. + * @return -ETIMEDOUT, if @p timeout expired. + */ +ssize_t sock_ip_recv_buf_aux(sock_ip_t *sock, void **data, void **buf_ctx, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux); /** * @brief Provides stack-internal buffer space containing an IPv4/IPv6 @@ -467,8 +600,50 @@ ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, * the remote of @p sock. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **buf_ctx, - uint32_t timeout, sock_ip_ep_t *remote); +static inline ssize_t sock_ip_recv_buf(sock_ip_t *sock, + void **data, void **buf_ctx, + uint32_t timeout, sock_ip_ep_t *remote) +{ + return sock_ip_recv_buf_aux(sock, data, buf_ctx, timeout, remote, NULL); +} + +/** + * @brief Sends a message over IPv4/IPv6 to remote end point + * + * @pre `((sock != NULL || remote != NULL)) && (if (len != 0): (data != NULL))` + * + * @param[in] sock A raw IPv4/IPv6 sock object. May be NULL. + * A sensible local end point should be selected by the + * implementation in that case. + * @param[in] data Pointer where the received data should be stored. + * May be `NULL` if `len == 0`. + * @param[in] len Maximum space available at @p data. + * @param[in] proto Protocol to use in the packet sent, in case + * `sock == NULL`. If `sock != NULL` this parameter will be + * ignored. + * @param[in] remote Remote end point for the sent data. + * May be `NULL`, if @p sock has a remote end point. + * sock_ip_ep_t::family may be AF_UNSPEC, if local + * end point of @p sock provides this information. + * @param[out] aux Auxiliary data for the transmission. + * May be `NULL` if not needed by the caller. + * + * @return The number of bytes sent on success. + * @return -EAFNOSUPPORT, if `remote != NULL` and sock_ip_ep_t::family of + * @p remote is != AF_UNSPEC and not supported. + * @return -EINVAL, if sock_ip_ep_t::addr of @p remote is an invalid address. + * @return -EINVAL, if sock_ip_ep_t::netif of @p remote is not a + * valid interface or contradicts the local interface of @p sock. + * @return -EHOSTUNREACH, if @p remote or remote end point of @p sock is not + * reachable. + * @return -ENOMEM, if no memory was available to send @p data. + * @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point. + * @return -EPROTOTYPE, if `sock == NULL` and @p proto is not by + * sock_ip_ep_t::family of @p remote. + */ +ssize_t sock_ip_send_aux(sock_ip_t *sock, const void *data, size_t len, + uint8_t proto, const sock_ip_ep_t *remote, + sock_ip_aux_tx_t *aux); /** * @brief Sends a message over IPv4/IPv6 to remote end point @@ -502,8 +677,12 @@ ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **buf_ctx, * @return -EPROTOTYPE, if `sock == NULL` and @p proto is not by * sock_ip_ep_t::family of @p remote. */ -ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len, - uint8_t proto, const sock_ip_ep_t *remote); +static inline ssize_t sock_ip_send(sock_ip_t *sock, + const void *data, size_t len, + uint8_t proto, const sock_ip_ep_t *remote) +{ + return sock_ip_send_aux(sock, data, len, proto, remote, NULL); +} #include "sock_types.h" diff --git a/sys/include/net/sock/udp.h b/sys/include/net/sock/udp.h index 5a4360fffc..8c071ba483 100644 --- a/sys/include/net/sock/udp.h +++ b/sys/include/net/sock/udp.h @@ -299,6 +299,51 @@ typedef struct sock_udp sock_udp_t; # pragma clang diagnostic pop #endif +/** + * @brief Auxiliary data provided when receiving using an UDP sock object + */ +typedef struct { +#if defined(MODULE_SOCK_AUX_LOCAL) || defined(DOXYGEN) + /** + * @brief The local endpoint the datagram was received on + * + * @see SOCK_AUX_GET_LOCAL + */ + sock_udp_ep_t local; +#endif /* MODULE_SOCK_AUX_ENDPOINT */ +#if defined(MODULE_SOCK_AUX_TIMESTAMP) || defined(DOXYGEN) + /** + * @brief System time the datagram was received + * + * @see SOCK_AUX_GET_TIMESTAMP + */ + uint64_t timestamp; +#endif /* MODULE_SOCK_AUX_TIMESTAP*/ + sock_aux_flags_t flags; /**< Flags used request information */ +} sock_udp_aux_rx_t; + +/** + * @brief Auxiliary data provided when sending using an UDP sock object + */ +typedef struct { +#if defined(MODULE_SOCK_AUX_TIMESTAMP) || defined(DOXYGEN) + /** + * @brief System time the datagram was send + * + * Add @ref SOCK_AUX_GET_TIMESTAMP to the bitmask in + * @ref sock_udp_aux_tx_t::flags to request a transmission timestamp. This + * bit will be cleared by @ref sock_udp_send_aux if and only if the + * timestamp was provided. The module `sock_aux_timestamp` needs to be + * selected to use this. The timestamp refers to the transmission of start + * of frame delimiter or preamble of the frame carrying the datagram and is + * given in nanoseconds since epoch, unless otherwise documented by the + * underlying implementation. + */ + uint64_t timestamp; +#endif /* MODULE_SOCK_AUX_TIMESTAP*/ + sock_aux_flags_t flags; /**< Flags used request information */ +} sock_udp_aux_tx_t; + /** * @brief Creates a new UDP sock object * @@ -393,6 +438,8 @@ int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep); * data is available). * @param[out] remote Remote end point of the received data. * May be `NULL`, if it is not required by the application. + * @param[out] aux Auxiliary data about the received datagram. + * May be `NULL`, if it is not required by the application. * * @note Function blocks if no packet is currently waiting. * @@ -409,8 +456,95 @@ int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep); * the remote of @p sock. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, - uint32_t timeout, sock_udp_ep_t *remote); +ssize_t sock_udp_recv_aux(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux); + +/** + * @brief Receives a UDP message from a remote end point + * + * @pre `(sock != NULL) && (data != NULL) && (max_len > 0)` + * + * @param[in] sock A UDP sock object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * @param[in] timeout Timeout for receive in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be @ref SOCK_NO_TIMEOUT for no timeout (wait until + * data is available). + * @param[out] remote Remote end point of the received data. + * May be `NULL`, if it is not required by the application. + * + * @note Function blocks if no packet is currently waiting. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return -EADDRNOTAVAIL, if local of @p sock is not given. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_udp_recv() blocks). + * @return -ENOBUFS, if buffer space is not large enough to store received + * data. + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -EPROTO, if source address of received packet did not equal + * the remote of @p sock. + * @return -ETIMEDOUT, if @p timeout expired. + */ +static inline ssize_t sock_udp_recv(sock_udp_t *sock, + void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote) +{ + return sock_udp_recv_aux(sock, data, max_len, timeout, remote, NULL); +} + +/** + * @brief Provides stack-internal buffer space containing a UDP message from + * a remote end point + * + * @pre `(sock != NULL) && (data != NULL) && (buf_ctx != NULL)` + * + * @param[in] sock A UDP sock object. + * @param[out] data Pointer to a stack-internal buffer space containing the + * received data. + * @param[in,out] buf_ctx Stack-internal buffer context. If it points to a + * `NULL` pointer, the stack returns a new buffer space + * for a new packet. If it does not point to a `NULL` + * pointer, an existing context is assumed to get a next + * segment in a buffer. + * @param[in] timeout Timeout for receive in microseconds. + * If 0 and no data is available, the function returns + * immediately. + * May be @ref SOCK_NO_TIMEOUT for no timeout (wait until + * data is available). + * @param[out] remote Remote end point of the received data. + * May be `NULL`, if it is not required by the application. + * @param[out] aux Auxiliary data about the received datagram. + * May be `NULL`, if it is not required by the application. + * + * @experimental This function is quite new, not implemented for all stacks + * yet, and may be subject to sudden API changes. Do not use in + * production if this is unacceptable. + * + * @note Function blocks if no packet is currently waiting. + * + * @return The number of bytes received on success. May not be the complete + * payload. Continue calling with the returned `buf_ctx` to get more + * buffers until result is 0 or an error. + * @return 0, if no received data is available, but everything is in order. + * If @p buf_ctx was provided, it was released. + * @return -EADDRNOTAVAIL, if local of @p sock is not given. + * @return -EAGAIN, if @p timeout is `0` and no data is available. + * @return -EINVAL, if @p remote is invalid or @p sock is not properly + * initialized (or closed while sock_udp_recv() blocks). + * @return -ENOMEM, if no memory was available to receive @p data. + * @return -EPROTO, if source address of received packet did not equal + * the remote of @p sock. + * @return -ETIMEDOUT, if @p timeout expired. + */ +ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **buf_ctx, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux); /** * @brief Provides stack-internal buffer space containing a UDP message from @@ -454,8 +588,51 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, * the remote of @p sock. * @return -ETIMEDOUT, if @p timeout expired. */ -ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **buf_ctx, - uint32_t timeout, sock_udp_ep_t *remote); +static inline ssize_t sock_udp_recv_buf(sock_udp_t *sock, + void **data, void **buf_ctx, + uint32_t timeout, + sock_udp_ep_t *remote) +{ + return sock_udp_recv_buf_aux(sock, data, buf_ctx, timeout, remote, NULL); +} + +/** + * @brief Sends a UDP message to remote end point + * + * @pre `((sock != NULL || remote != NULL)) && (if (len != 0): (data != NULL))` + * + * @param[in] sock A UDP sock object. May be `NULL`. + * A sensible local end point should be selected by the + * implementation in that case. + * @param[in] data Pointer where the received data should be stored. + * May be `NULL` if `len == 0`. + * @param[in] len Maximum space available at @p data. + * @param[in] remote Remote end point for the sent data. + * May be `NULL`, if @p sock has a remote end point. + * sock_udp_ep_t::family may be AF_UNSPEC, if local + * end point of @p sock provides this information. + * sock_udp_ep_t::port may not be 0. + * @param[out] aux Auxiliary data about the transmission. + * May be `NULL`, if it is not required by the application. + * + * @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 + * reachable. + * @return -EINVAL, if sock_udp_ep_t::addr of @p remote is an invalid address. + * @return -EINVAL, if sock_udp_ep_t::netif of @p remote is not a valid + * interface or contradicts the given local interface (i.e. + * neither the local end point of `sock` nor remote are assigned to + * `SOCK_ADDR_ANY_NETIF` but are nevertheless different. + * @return -EINVAL, if sock_udp_ep_t::port of @p remote is 0. + * @return -ENOMEM, if no memory was available to send @p data. + * @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point. + */ +ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux); /** * @brief Sends a UDP message to remote end point @@ -490,8 +667,12 @@ ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **buf_ctx, * @return -ENOMEM, if no memory was available to send @p data. * @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point. */ -ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, - const sock_udp_ep_t *remote); +static inline ssize_t sock_udp_send(sock_udp_t *sock, + const void *data, size_t len, + const sock_udp_ep_t *remote) +{ + return sock_udp_send_aux(sock, data, len, remote, NULL); +} #include "sock_types.h" diff --git a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c index 7785720b34..461966a013 100644 --- a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c +++ b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c @@ -87,8 +87,9 @@ int sock_ip_get_remote(sock_ip_t *sock, sock_ip_ep_t *remote) return 0; } -ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, - uint32_t timeout, sock_ip_ep_t *remote) +ssize_t sock_ip_recv_aux(sock_ip_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux) { void *pkt = NULL, *ctx = NULL; uint8_t *ptr = data; @@ -96,7 +97,7 @@ ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, bool nobufs = false; assert((sock != NULL) && (data != NULL) && (max_len > 0)); - while ((res = sock_ip_recv_buf(sock, &pkt, &ctx, timeout, remote)) > 0) { + while ((res = sock_ip_recv_buf_aux(sock, &pkt, &ctx, timeout, remote, aux)) > 0) { if (res > (ssize_t)max_len) { nobufs = true; continue; @@ -108,9 +109,11 @@ ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); } -ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **buf_ctx, - uint32_t timeout, sock_ip_ep_t *remote) +ssize_t sock_ip_recv_buf_aux(sock_ip_t *sock, void **data, void **buf_ctx, + uint32_t timeout, sock_ip_ep_t *remote, + sock_ip_aux_rx_t *aux) { + (void)aux; gnrc_pktsnip_t *pkt; sock_ip_ep_t tmp; int res; @@ -149,9 +152,11 @@ ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **buf_ctx, return res; } -ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len, - uint8_t proto, const sock_ip_ep_t *remote) +ssize_t sock_ip_send_aux(sock_ip_t *sock, const void *data, size_t len, + uint8_t proto, const sock_ip_ep_t *remote, + sock_ip_aux_tx_t *aux) { + (void)aux; int res; gnrc_pktsnip_t *pkt; sock_ip_ep_t local; diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c index dd3b4b466e..8c5b1d9b50 100644 --- a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -174,8 +174,9 @@ int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *remote) 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) +ssize_t sock_udp_recv_aux(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux) { void *pkt = NULL, *ctx = NULL; uint8_t *ptr = data; @@ -183,7 +184,8 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, bool nobufs = false; assert((sock != NULL) && (data != NULL) && (max_len > 0)); - while ((res = sock_udp_recv_buf(sock, &pkt, &ctx, timeout, remote)) > 0) { + while ((res = sock_udp_recv_buf_aux(sock, &pkt, &ctx, timeout, remote, + aux)) > 0) { if (res > (ssize_t)max_len) { nobufs = true; continue; @@ -195,9 +197,11 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); } -ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **buf_ctx, - uint32_t timeout, sock_udp_ep_t *remote) +ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **buf_ctx, + uint32_t timeout, sock_udp_ep_t *remote, + sock_udp_aux_rx_t *aux) { + (void)aux; gnrc_pktsnip_t *pkt, *udp; udp_hdr_t *hdr; sock_ip_ep_t tmp; @@ -242,9 +246,10 @@ ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **buf_ctx, return res; } -ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, - const sock_udp_ep_t *remote) +ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux) { + (void)aux; int res; gnrc_pktsnip_t *payload, *pkt; uint16_t src_port = 0, dst_port;