From f6a0263fe3a6ec46ae8e77458c6da8a2de374ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Mi=C5=9B?= Date: Tue, 19 Jan 2021 07:50:06 +0100 Subject: [PATCH 1/2] net: socket send timeout option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds SO_SNDTIMEO option used to time out socket sending operations. Signed-off-by: Hubert Miś --- include/net/net_context.h | 4 +++ include/net/socket.h | 2 ++ subsys/net/ip/Kconfig | 8 +++++ subsys/net/ip/net_context.c | 41 +++++++++++++++++++++++++ subsys/net/lib/sockets/sockets.c | 34 ++++++++++++++++++++ subsys/net/lib/sockets/sockets_can.c | 2 ++ subsys/net/lib/sockets/sockets_packet.c | 4 +++ 7 files changed, 95 insertions(+) diff --git a/include/net/net_context.h b/include/net/net_context.h index 5999a4bdf9bca..574ae5e2ab680 100644 --- a/include/net/net_context.h +++ b/include/net/net_context.h @@ -307,6 +307,9 @@ __net_socket struct net_context { #endif #if defined(CONFIG_NET_CONTEXT_RCVTIMEO) k_timeout_t rcvtimeo; +#endif +#if defined(CONFIG_NET_CONTEXT_SNDTIMEO) + k_timeout_t sndtimeo; #endif } options; @@ -1048,6 +1051,7 @@ enum net_context_option { NET_OPT_TXTIME = 3, NET_OPT_SOCKS5 = 4, NET_OPT_RCVTIMEO = 5, + NET_OPT_SNDTIMEO = 6, }; /** diff --git a/include/net/socket.h b/include/net/socket.h index 2e8a0882c4329..20eb5e1333898 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -805,6 +805,8 @@ static inline char *inet_ntop(sa_family_t family, const void *src, char *dst, * Applies to receive functions like recv(), but not to connect() */ #define SO_RCVTIMEO 20 +/** sockopt: Send timeout */ +#define SO_SNDTIMEO 21 /** sockopt: Timestamp TX packets */ #define SO_TIMESTAMPING 37 diff --git a/subsys/net/ip/Kconfig b/subsys/net/ip/Kconfig index f041276767d87..cb41fb8e22db9 100644 --- a/subsys/net/ip/Kconfig +++ b/subsys/net/ip/Kconfig @@ -567,6 +567,14 @@ config NET_CONTEXT_RCVTIMEO sockets timeout is configured per socket with setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, ...) function. +config NET_CONTEXT_SNDTIMEO + bool "Add SNDTIMEO support to net_context" + help + It is possible to time out sending a network packet. The timeout + time is configurable run-time in the application code. For network + sockets timeout is configured per socket with + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, ...) function. + config NET_TEST bool "Network Testing" help diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 3b745e5970fd0..bfc794c38b396 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -269,6 +269,9 @@ int net_context_get(sa_family_t family, #if defined(CONFIG_NET_CONTEXT_RCVTIMEO) contexts[i].options.rcvtimeo = K_FOREVER; #endif +#if defined(CONFIG_NET_CONTEXT_SNDTIMEO) + contexts[i].options.sndtimeo = K_FOREVER; +#endif if (IS_ENABLED(CONFIG_NET_IPV6) || IS_ENABLED(CONFIG_NET_IPV4)) { @@ -1235,6 +1238,22 @@ static int get_context_rcvtimeo(struct net_context *context, #endif } +static int get_context_sndtimeo(struct net_context *context, + void *value, size_t *len) +{ +#if defined(CONFIG_NET_CONTEXT_SNDTIMEO) + *((k_timeout_t *)value) = context->options.sndtimeo; + + if (len) { + *len = sizeof(k_timeout_t); + } + + return 0; +#else + return -ENOTSUP; +#endif +} + /* If buf is not NULL, then use it. Otherwise read the data to be written * to net_pkt from msghdr. */ @@ -2186,6 +2205,22 @@ static int set_context_rcvtimeo(struct net_context *context, #endif } +static int set_context_sndtimeo(struct net_context *context, + const void *value, size_t len) +{ +#if defined(CONFIG_NET_CONTEXT_SNDTIMEO) + if (len != sizeof(k_timeout_t)) { + return -EINVAL; + } + + context->options.sndtimeo = *((k_timeout_t *)value); + + return 0; +#else + return -ENOTSUP; +#endif +} + int net_context_set_option(struct net_context *context, enum net_context_option option, const void *value, size_t len) @@ -2216,6 +2251,9 @@ int net_context_set_option(struct net_context *context, case NET_OPT_RCVTIMEO: ret = set_context_rcvtimeo(context, value, len); break; + case NET_OPT_SNDTIMEO: + ret = set_context_sndtimeo(context, value, len); + break; } k_mutex_unlock(&context->lock); @@ -2253,6 +2291,9 @@ int net_context_get_option(struct net_context *context, case NET_OPT_RCVTIMEO: ret = get_context_rcvtimeo(context, value, len); break; + case NET_OPT_SNDTIMEO: + ret = get_context_sndtimeo(context, value, len); + break; } k_mutex_unlock(&context->lock); diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index dcd0d9331e55b..a124891e0b91d 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -591,6 +591,7 @@ ssize_t zsock_sendto_ctx(struct net_context *ctx, const void *buf, size_t len, if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; } else { + net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL); buf_timeout = z_timeout_end_calc(MAX_WAIT_BUFS); } @@ -684,6 +685,8 @@ ssize_t zsock_sendmsg_ctx(struct net_context *ctx, const struct msghdr *msg, if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; + } else { + net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL); } status = net_context_sendmsg(ctx, msg, flags, NULL, timeout, NULL); @@ -1659,6 +1662,37 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname, break; + case SO_SNDTIMEO: + if (IS_ENABLED(CONFIG_NET_CONTEXT_SNDTIMEO)) { + const struct zsock_timeval *tv = optval; + k_timeout_t timeout; + + if (optlen != sizeof(struct zsock_timeval)) { + errno = EINVAL; + return -1; + } + + if (tv->tv_sec == 0 && tv->tv_usec == 0) { + timeout = K_FOREVER; + } else { + timeout = K_USEC(tv->tv_sec * 1000000ULL + + tv->tv_usec); + } + + ret = net_context_set_option(ctx, + NET_OPT_SNDTIMEO, + &timeout, + sizeof(timeout)); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + } + + break; + case SO_TXTIME: if (IS_ENABLED(CONFIG_NET_CONTEXT_TXTIME)) { ret = net_context_set_option(ctx, diff --git a/subsys/net/lib/sockets/sockets_can.c b/subsys/net/lib/sockets/sockets_can.c index 0f4a6950a9b2d..4ff17c39a1f06 100644 --- a/subsys/net/lib/sockets/sockets_can.c +++ b/subsys/net/lib/sockets/sockets_can.c @@ -221,6 +221,8 @@ ssize_t zcan_sendto_ctx(struct net_context *ctx, const void *buf, size_t len, if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; + } else { + net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL); } if (addrlen == 0) { diff --git a/subsys/net/lib/sockets/sockets_packet.c b/subsys/net/lib/sockets/sockets_packet.c index 24404e36ecd98..07e034ab0ed52 100644 --- a/subsys/net/lib/sockets/sockets_packet.c +++ b/subsys/net/lib/sockets/sockets_packet.c @@ -150,6 +150,8 @@ ssize_t zpacket_sendto_ctx(struct net_context *ctx, const void *buf, size_t len, if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; + } else { + net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL); } /* Register the callback before sending in order to receive the response @@ -181,6 +183,8 @@ ssize_t zpacket_sendmsg_ctx(struct net_context *ctx, const struct msghdr *msg, if ((flags & ZSOCK_MSG_DONTWAIT) || sock_is_nonblock(ctx)) { timeout = K_NO_WAIT; + } else { + net_context_get_option(ctx, NET_OPT_SNDTIMEO, &timeout, NULL); } status = net_context_sendmsg(ctx, msg, flags, NULL, timeout, NULL); From 98371bbd8138545fe865c6cd7d10328c793bcd0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Mi=C5=9B?= Date: Tue, 19 Jan 2021 07:51:17 +0100 Subject: [PATCH 2/2] tests: Socket send timeout option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds tests for SO_SNDTIMEO socket option. Signed-off-by: Hubert Miś --- tests/net/socket/udp/prj.conf | 1 + tests/net/socket/udp/src/main.c | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/tests/net/socket/udp/prj.conf b/tests/net/socket/udp/prj.conf index b5b91ab37637c..1cc65bd743351 100644 --- a/tests/net/socket/udp/prj.conf +++ b/tests/net/socket/udp/prj.conf @@ -32,3 +32,4 @@ CONFIG_TEST_USERSPACE=y CONFIG_NET_CONTEXT_PRIORITY=y CONFIG_NET_CONTEXT_TXTIME=y CONFIG_NET_CONTEXT_RCVTIMEO=y +CONFIG_NET_CONTEXT_SNDTIMEO=y diff --git a/tests/net/socket/udp/src/main.c b/tests/net/socket/udp/src/main.c index 542c03997fd5c..0da8e8b4b9ce1 100644 --- a/tests/net/socket/udp/src/main.c +++ b/tests/net/socket/udp/src/main.c @@ -845,6 +845,43 @@ void test_so_rcvtimeo(void) zassert_equal(rv, 0, "close failed"); } +void test_so_sndtimeo(void) +{ + struct sockaddr_in bind_addr4; + struct sockaddr_in6 bind_addr6; + int sock1, sock2, rv; + + struct timeval optval = { + .tv_sec = 2, + .tv_usec = 500000, + }; + + prepare_sock_udp_v4(CONFIG_NET_CONFIG_MY_IPV4_ADDR, 55555, + &sock1, &bind_addr4); + prepare_sock_udp_v6(CONFIG_NET_CONFIG_MY_IPV6_ADDR, 55555, + &sock2, &bind_addr6); + + rv = bind(sock1, (struct sockaddr *)&bind_addr4, sizeof(bind_addr4)); + zassert_equal(rv, 0, "bind failed"); + + rv = bind(sock2, (struct sockaddr *)&bind_addr6, sizeof(bind_addr6)); + zassert_equal(rv, 0, "bind failed"); + + rv = setsockopt(sock1, SOL_SOCKET, SO_SNDTIMEO, &optval, + sizeof(optval)); + zassert_equal(rv, 0, "setsockopt failed (%d)", errno); + + optval.tv_usec = 0; + rv = setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, &optval, + sizeof(optval)); + zassert_equal(rv, 0, "setsockopt failed"); + + rv = close(sock1); + zassert_equal(rv, 0, "close failed"); + rv = close(sock2); + zassert_equal(rv, 0, "close failed"); +} + void test_so_protocol(void) { struct sockaddr_in bind_addr4; @@ -1090,6 +1127,7 @@ void test_main(void) ztest_unit_test(test_so_priority), ztest_unit_test(test_so_txtime), ztest_unit_test(test_so_rcvtimeo), + ztest_unit_test(test_so_sndtimeo), ztest_unit_test(test_so_protocol), ztest_unit_test(test_v4_sendmsg_recvfrom), ztest_user_unit_test(test_v4_sendmsg_recvfrom),