From 4d3c4bdfb898ba4869aa132b1f252dbb44e36422 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 11 May 2017 13:47:27 +0300 Subject: [PATCH 01/12] net: sockets: Bootstrap Sockets API implementation This adds Kconfig and build infrastructure and implements zsock_socket() and zsock_close() functions. Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 21 +++++++++++++++++++++ subsys/net/lib/Kbuild | 1 + subsys/net/lib/Kconfig | 2 ++ subsys/net/lib/Makefile | 4 ++++ subsys/net/lib/sockets/Kconfig | 17 +++++++++++++++++ subsys/net/lib/sockets/Makefile | 3 +++ subsys/net/lib/sockets/sockets.c | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+) create mode 100644 include/net/socket.h create mode 100644 subsys/net/lib/sockets/Kconfig create mode 100644 subsys/net/lib/sockets/Makefile create mode 100644 subsys/net/lib/sockets/sockets.c diff --git a/include/net/socket.h b/include/net/socket.h new file mode 100644 index 0000000000000..64d7059a05dde --- /dev/null +++ b/include/net/socket.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __NET_SOCKET_H +#define __NET_SOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif + +int zsock_socket(int family, int type, int proto); +int zsock_close(int sock); + +#ifdef __cplusplus +} +#endif + +#endif /* __NET_SOCKET_H */ diff --git a/subsys/net/lib/Kbuild b/subsys/net/lib/Kbuild index 0cec56ccde4a3..625b341412599 100644 --- a/subsys/net/lib/Kbuild +++ b/subsys/net/lib/Kbuild @@ -1,3 +1,4 @@ +obj-$(CONFIG_NET_SOCKETS) += sockets/ obj-$(CONFIG_ZOAP) += zoap/ obj-$(CONFIG_DNS_RESOLVER) += dns/ obj-$(CONFIG_MQTT_LIB) += mqtt/ diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index c3ea03d615c26..cf5942dd8c299 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -6,6 +6,8 @@ menu "Network Protocols" +source "subsys/net/lib/sockets/Kconfig" + source "subsys/net/lib/zoap/Kconfig" source "subsys/net/lib/dns/Kconfig" diff --git a/subsys/net/lib/Makefile b/subsys/net/lib/Makefile index 0625b848ec9fe..ae191af75c03d 100644 --- a/subsys/net/lib/Makefile +++ b/subsys/net/lib/Makefile @@ -1,3 +1,7 @@ +ifdef CONFIG_NET_SOCKETS +include $(srctree)/subsys/net/lib/sockets/Makefile +endif + ifdef CONFIG_ZOAP include $(srctree)/subsys/net/lib/zoap/Makefile endif diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig new file mode 100644 index 0000000000000..ce62bcc3b6ea2 --- /dev/null +++ b/subsys/net/lib/sockets/Kconfig @@ -0,0 +1,17 @@ +# Kconfig - BSD Sockets like API + +# +# Copyright (c) 2017 Linaro Limited. +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig NET_SOCKETS + bool "BSD Sockets like API" + default n + help + Provide BSD Sockets like API on top of native Zephyr networking API. + +if NET_SOCKETS + +endif # NET_SOCKETS diff --git a/subsys/net/lib/sockets/Makefile b/subsys/net/lib/sockets/Makefile new file mode 100644 index 0000000000000..f1ec5711fe00a --- /dev/null +++ b/subsys/net/lib/sockets/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I$(srctree)/subsys/net/lib/sockets + +obj-$(CONFIG_NET_SOCKETS) += sockets.o diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c new file mode 100644 index 0000000000000..3132aeaad71bc --- /dev/null +++ b/subsys/net/lib/sockets/sockets.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define SET_ERRNO(x) \ + { int _err = x; if (_err < 0) { errno = -_err; return -1; } } + +int zsock_socket(int family, int type, int proto) +{ + struct net_context *ctx; + + SET_ERRNO(net_context_get(family, type, proto, &ctx)); + /* recv_q and accept_q are in union */ + k_fifo_init(&ctx->recv_q); + + /* TODO: Ensure non-negative */ + return POINTER_TO_INT(ctx); +} + +int zsock_close(int sock) +{ + struct net_context *ctx = INT_TO_POINTER(sock); + + SET_ERRNO(net_context_put(ctx)); + return 0; +} From e0ec24ff4fab93d574ba3a4ac777e1b7a8858f20 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 11 May 2017 13:49:16 +0300 Subject: [PATCH 02/12] net: context, pkt: Changes for Sockets API Two changes are required so far: * There's unavoidable need to have a per-socket queue of packets (for data sockets) or pending connections (for listening sockets). These queues share the same space (as a C union). * There's a need to track "EOF" status of connection, synchronized with a queue of pending packets (i.e. EOF status should be processed only when all pending packets are processed). A natural place to store it per-packet then, and we had a "sent" bit which was used only for outgoing packets, recast it as "eof" for incoming socket packets. Signed-off-by: Paul Sokolovsky --- include/net/net_context.h | 8 ++++++++ include/net/net_pkt.h | 20 +++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/net/net_context.h b/include/net/net_context.h index e0d1c7cdf6ecd..e7d2027901202 100644 --- a/include/net/net_context.h +++ b/include/net/net_context.h @@ -237,6 +237,14 @@ struct net_context { /** TCP connection information */ struct net_tcp *tcp; #endif /* CONFIG_NET_TCP */ + +#if defined(CONFIG_NET_SOCKETS) + /** Per-socket packet or connection queues */ + union { + struct k_fifo recv_q; + struct k_fifo accept_q; + }; +#endif /* CONFIG_NET_SOCKETS */ }; static inline bool net_context_is_used(struct net_context *context) diff --git a/include/net/net_pkt.h b/include/net/net_pkt.h index 3d38349f06ab9..da4fa6b6a214b 100644 --- a/include/net/net_pkt.h +++ b/include/net/net_pkt.h @@ -71,7 +71,9 @@ struct net_pkt { sys_snode_t sent_list; #endif - u8_t sent : 1; /* Is this sent or not + u8_t sent_or_eof: 1; /* For outgoing packet: is this sent or not + * For incoming packet of a socket: last + * packet before EOF * Used only if defined(CONFIG_NET_TCP) */ u8_t forwarding : 1; /* Are we forwarding this pkt @@ -188,13 +190,25 @@ static inline void net_pkt_set_next_hdr(struct net_pkt *pkt, u8_t *hdr) #if defined(CONFIG_NET_TCP) static inline u8_t net_pkt_sent(struct net_pkt *pkt) { - return pkt->sent; + return pkt->sent_or_eof; } static inline void net_pkt_set_sent(struct net_pkt *pkt, bool sent) { - pkt->sent = sent; + pkt->sent_or_eof = sent; } + +#if defined(CONFIG_NET_SOCKETS) +static inline u8_t net_pkt_eof(struct net_pkt *pkt) +{ + return pkt->sent_or_eof; +} + +static inline void net_pkt_set_eof(struct net_pkt *pkt, bool eof) +{ + pkt->sent_or_eof = eof; +} +#endif #endif #if defined(CONFIG_NET_ROUTE) From 23bf8f29bd27eceb095a52e22eaa2ff2988de937 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 12 May 2017 16:52:34 +0300 Subject: [PATCH 03/12] net: sockets: Add configurable option to provide raw POSIX API names With CONFIG_NET_SOCKETS_POSIX_NAMES=y, "raw" POSIX names like socket(), recv(), close() will be exposed (using macro defines). The close() is the biggest culprit here, because in POSIX it applies to any file descriptor, but in this implementation - only to sockets. Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 8 ++++++++ subsys/net/lib/sockets/Kconfig | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/net/socket.h b/include/net/socket.h index 64d7059a05dde..d570b4b87e92b 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -7,6 +7,9 @@ #ifndef __NET_SOCKET_H #define __NET_SOCKET_H +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -14,6 +17,11 @@ extern "C" { int zsock_socket(int family, int type, int proto); int zsock_close(int sock); +#if defined(CONFIG_NET_SOCKETS_POSIX_NAMES) +#define socket zsock_socket +#define close zsock_close +#endif + #ifdef __cplusplus } #endif diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig index ce62bcc3b6ea2..ae503a0ee5103 100644 --- a/subsys/net/lib/sockets/Kconfig +++ b/subsys/net/lib/sockets/Kconfig @@ -14,4 +14,15 @@ menuconfig NET_SOCKETS if NET_SOCKETS +config NET_SOCKETS_POSIX_NAMES + bool "Standard POSIX names for Sockets API" + default n + help + By default, Sockets API function are prefixed with "zsock_" to avoid + namespacing issues. If this option is enabled, they will be provided + with standard POSIX names like socket(), recv(), and close(), to help + with porting existing code. Note that close() may require a special + attention, as in POSIX it closes any file descriptor, while with this + option enaled, it will still apply only to sockets. + endif # NET_SOCKETS From 6db54c87a6e3ad5c0492099e84aee54455172043 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 3 Jun 2017 20:38:57 +0300 Subject: [PATCH 04/12] net: context: Allow to put context into FIFO at expense of user_data By moving user_data member at the beginning of structure. With refcount at the beginning, reliable passsing of contexts via FIFO was just impossible. (Queuing contexts to a FIFO is required for BSD Sockets API). Signed-off-by: Paul Sokolovsky --- include/net/net_context.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/net/net_context.h b/include/net/net_context.h index e7d2027901202..d8ab952f22ceb 100644 --- a/include/net/net_context.h +++ b/include/net/net_context.h @@ -176,6 +176,13 @@ struct net_conn_handle; * anyway. This saves 12 bytes / context in IPv6. */ struct net_context { + /** User data. + * + * First member of the structure to let users either have user data + * associated with a context, or put contexts into a FIFO. + */ + void *user_data; + /** Reference count */ atomic_t refcount; @@ -206,10 +213,6 @@ struct net_context { */ net_context_connect_cb_t connect_cb; - /** User data. - */ - void *user_data; - #if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL) /** Get TX net_buf pool for this context. */ From f284be2fe6c446c6ff0d4c3497f41b813e289809 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 8 Jun 2017 17:13:03 +0300 Subject: [PATCH 05/12] WIP: kernel: fifo: Add peek_head/peek_tail accessors. TODO: Should really go to kernel.h. Signed-off-by: Paul Sokolovsky --- subsys/net/lib/sockets/sockets.c | 1 + subsys/net/lib/sockets/sockets_int.h | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 subsys/net/lib/sockets/sockets_int.h diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 3132aeaad71bc..fef6f07833b32 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -7,6 +7,7 @@ #include #include #include +#include "sockets_int.h" #define SET_ERRNO(x) \ { int _err = x; if (_err < 0) { errno = -_err; return -1; } } diff --git a/subsys/net/lib/sockets/sockets_int.h b/subsys/net/lib/sockets/sockets_int.h new file mode 100644 index 0000000000000..f9358367bf9f0 --- /dev/null +++ b/subsys/net/lib/sockets/sockets_int.h @@ -0,0 +1,11 @@ +#include + +static inline void *_k_fifo_peek_head(struct k_fifo *fifo) +{ + return sys_slist_peek_head(&fifo->_queue.data_q); +} + +static inline void *_k_fifo_peek_tail(struct k_fifo *fifo) +{ + return sys_slist_peek_tail(&fifo->_queue.data_q); +} From 656b31b8c9ead335665e99fa78ce19aaffcd128e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 8 Jun 2017 17:14:08 +0300 Subject: [PATCH 06/12] net: sockets: Implement bind(), connect(), listen(), accept() Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 8 +++ subsys/net/lib/sockets/sockets.c | 106 +++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/include/net/socket.h b/include/net/socket.h index d570b4b87e92b..30db92aeaf628 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -16,10 +16,18 @@ extern "C" { int zsock_socket(int family, int type, int proto); int zsock_close(int sock); +int zsock_bind(int sock, const struct sockaddr *addr, socklen_t addrlen); +int zsock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen); +int zsock_listen(int sock, int backlog); +int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); #if defined(CONFIG_NET_SOCKETS_POSIX_NAMES) #define socket zsock_socket #define close zsock_close +#define bind zsock_bind +#define connect zsock_connect +#define listen zsock_listen +#define accept zsock_accept #endif #ifdef __cplusplus diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index fef6f07833b32..753a898882059 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -12,6 +12,9 @@ #define SET_ERRNO(x) \ { int _err = x; if (_err < 0) { errno = -_err; return -1; } } +#define sock_is_eof(ctx) ((ctx)->user_data != NULL) +#define sock_set_eof(ctx) { (ctx)->user_data = INT_TO_POINTER(1); } + int zsock_socket(int family, int type, int proto) { struct net_context *ctx; @@ -31,3 +34,106 @@ int zsock_close(int sock) SET_ERRNO(net_context_put(ctx)); return 0; } + +static void zsock_accepted_cb(struct net_context *new_ctx, + struct sockaddr *addr, socklen_t addrlen, + int status, void *user_data) { + struct net_context *parent = user_data; + + NET_DBG("parent=%p, ctx=%p, st=%d", parent, new_ctx, status); + + k_fifo_put(&parent->accept_q, new_ctx); +} + +static void zsock_received_cb(struct net_context *ctx, struct net_pkt *pkt, + int status, void *user_data) { + unsigned int header_len; + + NET_DBG("ctx=%p, pkt=%p, st=%d, user_data=%p", ctx, pkt, status, + user_data); + + /* if pkt == NULL, EOF */ + if (pkt == NULL) { + struct net_pkt *last_pkt = _k_fifo_peek_tail(&ctx->recv_q); + + if (last_pkt == NULL) { + /* If there're no packets in the queue, recv() may + * be blocked waiting on it to become non-empty, + * so cancel that wait. + */ + sock_set_eof(ctx); + k_fifo_cancel_wait(&ctx->recv_q); + NET_DBG("Marked socket %p as peer-closed\n", ctx); + } else { + net_pkt_set_eof(last_pkt, true); + NET_DBG("Set EOF flag on pkt %p\n", ctx); + } + return; + } + + /* Normal packet */ + net_pkt_set_eof(pkt, false); + + /* We don't care about packet header, so get rid of it asap */ + header_len = net_pkt_appdata(pkt) - pkt->frags->data; + net_buf_pull(pkt->frags, header_len); + + k_fifo_put(&ctx->recv_q, pkt); +} + +int zsock_bind(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + struct net_context *ctx = INT_TO_POINTER(sock); + + SET_ERRNO(net_context_bind(ctx, addr, addrlen)); + /* For DGRAM socket, we expect to receive packets after call to + * bind(), but for STREAM socket, next expected operation is + * listen(), which doesn't work if recv callback is set. + */ + if (net_context_get_type(ctx) == SOCK_DGRAM) { + SET_ERRNO(net_context_recv(ctx, zsock_received_cb, K_NO_WAIT, + NULL)); + } + + return 0; +} + +int zsock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen) +{ + struct net_context *ctx = INT_TO_POINTER(sock); + + SET_ERRNO(net_context_connect(ctx, addr, addrlen, NULL, K_FOREVER, + NULL)); + SET_ERRNO(net_context_recv(ctx, zsock_received_cb, K_NO_WAIT, NULL)); + + return 0; +} + +int zsock_listen(int sock, int backlog) +{ + struct net_context *ctx = INT_TO_POINTER(sock); + + SET_ERRNO(net_context_listen(ctx, backlog)); + SET_ERRNO(net_context_accept(ctx, zsock_accepted_cb, K_NO_WAIT, ctx)); + + return 0; +} + +int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen) +{ + struct net_context *parent = INT_TO_POINTER(sock); + + struct net_context *ctx = k_fifo_get(&parent->accept_q, K_FOREVER); + + SET_ERRNO(net_context_recv(ctx, zsock_received_cb, K_NO_WAIT, NULL)); + + if (addr != NULL && addrlen != NULL) { + int len = min(*addrlen, sizeof(ctx->remote)); + + memcpy(addr, &ctx->remote, len); + *addrlen = sizeof(ctx->remote); + } + + /* TODO: Ensure non-negative */ + return POINTER_TO_INT(ctx); +} From 4a601857d414df3a33ed6fadbd0166b7c79b784e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 9 Jun 2017 19:56:26 +0300 Subject: [PATCH 07/12] net: sockets: Add POSIX compat defines for inet_ntop, inet_pton Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/net/socket.h b/include/net/socket.h index 30db92aeaf628..5fcb8b2363e1f 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -28,6 +28,9 @@ int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); #define connect zsock_connect #define listen zsock_listen #define accept zsock_accept + +#define inet_ntop net_addr_ntop +#define inet_pton net_addr_pton #endif #ifdef __cplusplus From 1bf69331f642603391f126162e1c090a96775fec Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 10 Jun 2017 21:53:03 +0300 Subject: [PATCH 08/12] net: sockets: Implement send() Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 3 +++ subsys/net/lib/sockets/sockets.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/net/socket.h b/include/net/socket.h index 5fcb8b2363e1f..70db77f947748 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -7,6 +7,7 @@ #ifndef __NET_SOCKET_H #define __NET_SOCKET_H +#include #include #include @@ -20,6 +21,7 @@ int zsock_bind(int sock, const struct sockaddr *addr, socklen_t addrlen); int zsock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen); int zsock_listen(int sock, int backlog); int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); +ssize_t zsock_send(int sock, const void *buf, size_t len, int flags); #if defined(CONFIG_NET_SOCKETS_POSIX_NAMES) #define socket zsock_socket @@ -28,6 +30,7 @@ int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); #define connect zsock_connect #define listen zsock_listen #define accept zsock_accept +#define send zsock_send #define inet_ntop net_addr_ntop #define inet_pton net_addr_pton diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 753a898882059..8fc518c499ef7 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -137,3 +137,34 @@ int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen) /* TODO: Ensure non-negative */ return POINTER_TO_INT(ctx); } + +ssize_t zsock_send(int sock, const void *buf, size_t len, int flags) +{ + (void)flags; + int err; + struct net_context *ctx = INT_TO_POINTER(sock); + struct net_pkt *send_pkt = net_pkt_get_tx(ctx, K_FOREVER); + size_t max_len = net_if_get_mtu(net_context_get_iface(ctx)); + + /* Make sure we don't send more data in one packet than + * MTU allows. Optimize for number of branches in the code. + */ + max_len -= NET_IPV4TCPH_LEN; + if (net_context_get_family(ctx) != AF_INET) { + max_len -= NET_IPV6TCPH_LEN - NET_IPV4TCPH_LEN; + } + + if (len > max_len) { + len = max_len; + } + + len = net_pkt_append(send_pkt, len, buf, K_FOREVER); + err = net_context_send(send_pkt, /*cb*/NULL, K_FOREVER, NULL, NULL); + if (err < 0) { + net_pkt_unref(send_pkt); + errno = -err; + return -1; + } + + return len; +} From 2919b531963b830a3f2786512687c8e6aabeaaab Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 12 Jun 2017 22:18:16 +0300 Subject: [PATCH 09/12] net: sockets: Implement recv() for STREAM sockets Signed-off-by: Paul Sokolovsky --- include/net/socket.h | 2 + subsys/net/lib/sockets/sockets.c | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/include/net/socket.h b/include/net/socket.h index 70db77f947748..961e086d98342 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -22,6 +22,7 @@ int zsock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen); int zsock_listen(int sock, int backlog); int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); ssize_t zsock_send(int sock, const void *buf, size_t len, int flags); +ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags); #if defined(CONFIG_NET_SOCKETS_POSIX_NAMES) #define socket zsock_socket @@ -31,6 +32,7 @@ ssize_t zsock_send(int sock, const void *buf, size_t len, int flags); #define listen zsock_listen #define accept zsock_accept #define send zsock_send +#define recv zsock_recv #define inet_ntop net_addr_ntop #define inet_pton net_addr_pton diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 8fc518c499ef7..8a2293b7cdf7f 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -15,6 +15,16 @@ #define sock_is_eof(ctx) ((ctx)->user_data != NULL) #define sock_set_eof(ctx) { (ctx)->user_data = INT_TO_POINTER(1); } +static inline void _k_fifo_wait_non_empty(struct k_fifo *fifo, int32_t timeout) +{ + struct k_poll_event events[] = { + K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, + K_POLL_MODE_NOTIFY_ONLY, fifo), + }; + + k_poll(events, ARRAY_SIZE(events), timeout); +} + int zsock_socket(int family, int type, int proto) { struct net_context *ctx; @@ -168,3 +178,67 @@ ssize_t zsock_send(int sock, const void *buf, size_t len, int flags) return len; } + +ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags) +{ + (void)flags; + struct net_context *ctx = INT_TO_POINTER(sock); + enum net_sock_type sock_type = net_context_get_type(ctx); + size_t recv_len = 0; + + if (sock_type == SOCK_DGRAM) { + __ASSERT(0, "DGRAM is not yet handled"); + } else if (sock_type == SOCK_STREAM) { + do { + struct net_pkt *pkt; + struct net_buf *frag; + u32_t frag_len; + + if (sock_is_eof(ctx)) { + return 0; + } + + _k_fifo_wait_non_empty(&ctx->recv_q, K_FOREVER); + pkt = _k_fifo_peek_head(&ctx->recv_q); + if (pkt == NULL) { + /* An expected reason is that wait was + * cancelled due to connection closure by peer. + */ + NET_DBG("NULL return from fifo"); + continue; + } + + frag = pkt->frags; + __ASSERT(frag != NULL, + "net_pkt has empty fragments on start!"); + frag_len = frag->len; + recv_len = frag_len; + if (recv_len > max_len) { + recv_len = max_len; + } + + /* Actually copy data to application buffer */ + memcpy(buf, frag->data, recv_len); + + if (recv_len != frag_len) { + net_buf_pull(frag, recv_len); + } else { + frag = net_pkt_frag_del(pkt, NULL, frag); + if (frag == NULL) { + /* Finished processing head pkt in + * the fifo. Drop it from there. + */ + k_fifo_get(&ctx->recv_q, K_NO_WAIT); + if (net_pkt_eof(pkt)) { + sock_set_eof(ctx); + } + net_pkt_unref(pkt); + } + } + } while (recv_len == 0); + } else { + __ASSERT(0, "Unknown socket type"); + } + + return recv_len; +} From f73a3b8a05b58fa05cabdfacb20b959465aa5be8 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 9 Jun 2017 21:09:19 +0300 Subject: [PATCH 10/12] samples: net: Add socket-based echo server example Signed-off-by: Paul Sokolovsky --- samples/net/socket_echo/Makefile | 18 ++++++ samples/net/socket_echo/prj_qemu_x86.conf | 26 ++++++++ samples/net/socket_echo/src/Makefile | 3 + samples/net/socket_echo/src/socket_echo.c | 76 +++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 samples/net/socket_echo/Makefile create mode 100644 samples/net/socket_echo/prj_qemu_x86.conf create mode 100644 samples/net/socket_echo/src/Makefile create mode 100644 samples/net/socket_echo/src/socket_echo.c diff --git a/samples/net/socket_echo/Makefile b/samples/net/socket_echo/Makefile new file mode 100644 index 0000000000000..7f9b4a97a88db --- /dev/null +++ b/samples/net/socket_echo/Makefile @@ -0,0 +1,18 @@ +# Makefile - simple socket-based echo server + +# +# Copyright (c) 2017 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +BOARD ?= qemu_x86 +CONF_FILE ?= prj_$(BOARD).conf + +include $(ZEPHYR_BASE)/Makefile.inc + +ifeq ($(CONFIG_NET_L2_BLUETOOTH), y) + QEMU_EXTRA_FLAGS = -serial unix:/tmp/bt-server-bredr +else + include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack +endif diff --git a/samples/net/socket_echo/prj_qemu_x86.conf b/samples/net/socket_echo/prj_qemu_x86.conf new file mode 100644 index 0000000000000..2435a9e405842 --- /dev/null +++ b/samples/net/socket_echo/prj_qemu_x86.conf @@ -0,0 +1,26 @@ +# General config +CONFIG_NEWLIB_LIBC=y + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=n +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +# Network driver config +CONFIG_NET_SLIP_TAP=y +CONFIG_TEST_RANDOM_GENERATOR=y + +# Without CONFIG_NET_BUF_LOG printf() doesn't work +CONFIG_NET_BUF_LOG=y + +# Network address config +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" + +# Network debug config +#CONFIG_NET_DEBUG_SOCKETS=y +CONFIG_SYS_LOG_NET_LEVEL=2 diff --git a/samples/net/socket_echo/src/Makefile b/samples/net/socket_echo/src/Makefile new file mode 100644 index 0000000000000..073377e184fb7 --- /dev/null +++ b/samples/net/socket_echo/src/Makefile @@ -0,0 +1,3 @@ +include $(ZEPHYR_BASE)/samples/net/common/Makefile.common + +obj-y += socket_echo.o diff --git a/samples/net/socket_echo/src/socket_echo.c b/samples/net/socket_echo/src/socket_echo.c new file mode 100644 index 0000000000000..c9fa4c8b57290 --- /dev/null +++ b/samples/net/socket_echo/src/socket_echo.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifndef __ZEPHYR__ + +#include +#include +#include +#include + +#define init_net() + +#else + +#include +#include +#include + +void init_net(void) +{ + int ret = net_sample_app_init("socket_echo", NET_SAMPLE_NEED_IPV4, + K_SECONDS(3)); + + if (ret < 0) { + printf("Application init failed\n"); + k_panic(); + } +} + +#endif + +int main(void) +{ + int serv; + struct sockaddr_in bind_addr; + + init_net(); + + serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); + bind_addr.sin_port = htons(4242); + bind(serv, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); + + listen(serv, 5); + + while (1) { + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + char addr_str[32]; + int client = accept(serv, (struct sockaddr *)&client_addr, + &client_addr_len); + inet_ntop(client_addr.sin_family, &client_addr.sin_addr, + addr_str, sizeof(addr_str)); + printf("Connection from %s\n", addr_str); + + while (1) { + char buf[128]; + int len = recv(client, buf, sizeof(buf), 0); + + if (len == 0) { + break; + } + send(client, buf, len, 0); + } + + close(client); + printf("Connection from %s closed\n", addr_str); + } +} From ac6ec7fe21aef5f0428febc13aeeccbcccc97609 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 13 Jun 2017 18:21:51 +0300 Subject: [PATCH 11/12] net: sockets: Add debug logging config Signed-off-by: Paul Sokolovsky --- subsys/net/lib/sockets/Kconfig | 7 +++++++ subsys/net/lib/sockets/sockets.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig index ae503a0ee5103..2fae525fe3760 100644 --- a/subsys/net/lib/sockets/Kconfig +++ b/subsys/net/lib/sockets/Kconfig @@ -25,4 +25,11 @@ config NET_SOCKETS_POSIX_NAMES attention, as in POSIX it closes any file descriptor, while with this option enaled, it will still apply only to sockets. +config NET_DEBUG_SOCKETS + bool "Debug BSD Sockets like API calls" + default n + help + Enables logging for sockets code. (Logging level is defined by + SYS_LOG_NET_LEVEL setting). + endif # NET_SOCKETS diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 8a2293b7cdf7f..1fe7f1ccfe02b 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -4,6 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if defined(CONFIG_NET_DEBUG_SOCKETS) +#define SYS_LOG_DOMAIN "net/sock" +#define NET_LOG_ENABLED 1 +#endif + #include #include #include From 287230f8a7389f83dec9f39d2ff9c43b0386497c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 13 Jun 2017 20:19:08 +0300 Subject: [PATCH 12/12] net: sockets: Explicitly flush conn/pkt queue on close() If a socket is closed without reading all data from peer or accepting all pending connection, they will be leaked. So, flush queues explicitly. Signed-off-by: Paul Sokolovsky --- subsys/net/lib/sockets/sockets.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 1fe7f1ccfe02b..ee0c93e7828e4 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -30,6 +30,23 @@ static inline void _k_fifo_wait_non_empty(struct k_fifo *fifo, int32_t timeout) k_poll(events, ARRAY_SIZE(events), timeout); } +static void zsock_flush_queue(struct net_context *ctx) +{ + bool is_listen = net_context_get_state(ctx) == NET_CONTEXT_LISTENING; + void *p; + + /* recv_q and accept_q are shared via a union */ + while ((p = k_fifo_get(&ctx->recv_q, K_NO_WAIT)) != NULL) { + if (is_listen) { + NET_DBG("discarding ctx %p", p); + net_context_put(p); + } else { + NET_DBG("discarding pkt %p", p); + net_pkt_unref(p); + } + } +} + int zsock_socket(int family, int type, int proto) { struct net_context *ctx; @@ -46,6 +63,14 @@ int zsock_close(int sock) { struct net_context *ctx = INT_TO_POINTER(sock); + /* Reset callbacks to avoid any race conditions while + * flushing queues. + */ + net_context_accept(ctx, NULL, K_NO_WAIT, NULL); + net_context_recv(ctx, NULL, K_NO_WAIT, NULL); + + zsock_flush_queue(ctx); + SET_ERRNO(net_context_put(ctx)); return 0; }