1 /*
2 * Copyright (c) 2023 Nordic Semiconductor
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_echo_server_svc_sample, LOG_LEVEL_DBG);
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <errno.h>
13
14 #include <zephyr/kernel.h>
15 #include <zephyr/posix/unistd.h>
16 #include <zephyr/posix/poll.h>
17 #include <zephyr/posix/arpa/inet.h>
18 #include <zephyr/posix/sys/socket.h>
19 #include <zephyr/net/socket_service.h>
20
21 #define MY_PORT 4242
22
23 static char addr_str[INET6_ADDRSTRLEN];
24
25 static struct pollfd sockfd_udp[1] = {
26 [0] = { .fd = -1 }, /* UDP socket */
27 };
28 static struct pollfd sockfd_tcp[1] = {
29 [0] = { .fd = -1 }, /* TCP socket */
30 };
31
32 #define MAX_SERVICES 1
33
34 static void receive_data(bool is_udp, struct net_socket_service_event *pev,
35 char *buf, size_t buflen);
36
tcp_service_handler(struct net_socket_service_event * pev)37 static void tcp_service_handler(struct net_socket_service_event *pev)
38 {
39 static char buf[1500];
40
41 /* Note that in this application we receive / send data from
42 * system work queue. In proper application the socket reading and data
43 * sending should be done so that the system work queue is not blocked.
44 * It is possible to create a socket service that uses own work queue.
45 */
46 receive_data(false, pev, buf, sizeof(buf));
47 }
48
udp_service_handler(struct net_socket_service_event * pev)49 static void udp_service_handler(struct net_socket_service_event *pev)
50 {
51 static char buf[1500];
52
53 receive_data(true, pev, buf, sizeof(buf));
54 }
55
56 NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(service_udp, udp_service_handler, MAX_SERVICES);
57 NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(service_tcp, tcp_service_handler, MAX_SERVICES);
58
receive_data(bool is_udp,struct net_socket_service_event * pev,char * buf,size_t buflen)59 static void receive_data(bool is_udp, struct net_socket_service_event *pev,
60 char *buf, size_t buflen)
61 {
62 struct pollfd *pfd = &pev->event;
63 int client = pfd->fd;
64 struct sockaddr_in6 addr;
65 socklen_t addrlen = sizeof(addr);
66 int len, out_len;
67 char *p;
68
69 len = recvfrom(client, buf, buflen, 0,
70 (struct sockaddr *)&addr, &addrlen);
71 if (len <= 0) {
72 if (len < 0) {
73 LOG_ERR("recv: %d", -errno);
74 }
75
76 /* If the TCP socket is closed, mark it as non pollable */
77 if (!is_udp && sockfd_tcp[0].fd == client) {
78 sockfd_tcp[0].fd = -1;
79
80 /* Update the handler so that client connection is
81 * not monitored any more.
82 */
83 (void)net_socket_service_register(&service_tcp, sockfd_tcp,
84 ARRAY_SIZE(sockfd_tcp), NULL);
85 close(client);
86
87 LOG_INF("Connection from %s closed", addr_str);
88 }
89
90 return;
91 }
92
93 p = buf;
94 do {
95 if (is_udp) {
96 out_len = sendto(client, p, len, 0,
97 (struct sockaddr *)&addr, addrlen);
98 } else {
99 out_len = send(client, p, len, 0);
100 }
101
102 if (out_len < 0) {
103 LOG_ERR("sendto: %d", -errno);
104 break;
105 }
106
107 p += out_len;
108 len -= out_len;
109 } while (len);
110 }
111
setup_tcp_socket(struct sockaddr_in6 * addr)112 static int setup_tcp_socket(struct sockaddr_in6 *addr)
113 {
114 socklen_t optlen = sizeof(int);
115 int ret, sock, opt;
116
117 sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
118 if (sock < 0) {
119 LOG_ERR("socket: %d", -errno);
120 return -errno;
121 }
122
123 ret = getsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, &optlen);
124 if (ret == 0 && opt) {
125 LOG_INF("IPV6_V6ONLY option is on, turning it off.");
126
127 opt = 0;
128 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, optlen);
129 if (ret < 0) {
130 LOG_WRN("Cannot turn off IPV6_V6ONLY option");
131 } else {
132 LOG_INF("Sharing same socket between IPv6 and IPv4");
133 }
134 }
135
136 if (bind(sock, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
137 LOG_ERR("bind: %d", -errno);
138 return -errno;
139 }
140
141 if (listen(sock, 5) < 0) {
142 LOG_ERR("listen: %d", -errno);
143 return -errno;
144 }
145
146 return sock;
147 }
148
setup_udp_socket(struct sockaddr_in6 * addr)149 static int setup_udp_socket(struct sockaddr_in6 *addr)
150 {
151 socklen_t optlen = sizeof(int);
152 int ret, sock, opt;
153
154 sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
155 if (sock < 0) {
156 LOG_ERR("socket: %d", -errno);
157 return -errno;
158 }
159
160 ret = getsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, &optlen);
161 if (ret == 0 && opt) {
162 LOG_INF("IPV6_V6ONLY option is on, turning it off.");
163
164 opt = 0;
165 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, optlen);
166 if (ret < 0) {
167 LOG_WRN("Cannot turn off IPV6_V6ONLY option");
168 } else {
169 LOG_INF("Sharing same socket between IPv6 and IPv4");
170 }
171 }
172
173 if (bind(sock, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
174 LOG_ERR("bind: %d", -errno);
175 return -errno;
176 }
177
178 return sock;
179 }
180
main(void)181 int main(void)
182 {
183 int tcp_sock, udp_sock, ret;
184 struct sockaddr_in6 addr = {
185 .sin6_family = AF_INET6,
186 .sin6_addr = IN6ADDR_ANY_INIT,
187 .sin6_port = htons(MY_PORT),
188 };
189 static int counter;
190
191 tcp_sock = setup_tcp_socket(&addr);
192 if (tcp_sock < 0) {
193 return tcp_sock;
194 }
195
196 udp_sock = setup_udp_socket(&addr);
197 if (udp_sock < 0) {
198 return udp_sock;
199 }
200
201 sockfd_udp[0].fd = udp_sock;
202 sockfd_udp[0].events = POLLIN;
203
204 /* Register UDP socket to service handler */
205 ret = net_socket_service_register(&service_udp, sockfd_udp,
206 ARRAY_SIZE(sockfd_udp), NULL);
207 if (ret < 0) {
208 LOG_ERR("Cannot register socket service handler (%d)", ret);
209 }
210
211 LOG_INF("Single-threaded TCP/UDP echo server waits "
212 "for a connection on port %d", MY_PORT);
213
214 while (1) {
215 struct sockaddr_in6 client_addr;
216 socklen_t client_addr_len = sizeof(client_addr);
217 int client;
218
219 client = accept(tcp_sock, (struct sockaddr *)&client_addr,
220 &client_addr_len);
221 if (client < 0) {
222 LOG_ERR("accept: %d", -errno);
223 continue;
224 }
225
226 inet_ntop(client_addr.sin6_family, &client_addr.sin6_addr,
227 addr_str, sizeof(addr_str));
228 LOG_INF("Connection #%d from %s (%d)", counter++, addr_str, client);
229
230 sockfd_tcp[0].fd = client;
231 sockfd_tcp[0].events = POLLIN;
232
233 /* Register all the sockets to service handler */
234 ret = net_socket_service_register(&service_tcp, sockfd_tcp,
235 ARRAY_SIZE(sockfd_tcp), NULL);
236 if (ret < 0) {
237 LOG_ERR("Cannot register socket service handler (%d)",
238 ret);
239 break;
240 }
241 }
242
243 (void)net_socket_service_unregister(&service_tcp);
244 (void)net_socket_service_unregister(&service_udp);
245
246 close(tcp_sock);
247 close(udp_sock);
248
249 return 0;
250 }
251