1 /*
2 * Copyright (c) 2016 Intel Corporation
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_shell);
10
11 #include <stdlib.h>
12
13 #include "net_shell_private.h"
14
15 #if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
16 static struct net_context *tcp_ctx;
17 static const struct shell *tcp_shell;
18
19 #define TCP_CONNECT_TIMEOUT K_SECONDS(5) /* ms */
20 #define TCP_TIMEOUT K_SECONDS(2) /* ms */
21
tcp_connected(struct net_context * context,int status,void * user_data)22 static void tcp_connected(struct net_context *context,
23 int status,
24 void *user_data)
25 {
26 if (status < 0) {
27 PR_SHELL(tcp_shell, "TCP connection failed (%d)\n", status);
28 } else {
29 PR_SHELL(tcp_shell, "TCP connected\n");
30 }
31 }
32
get_my_ipv6_addr(struct net_if * iface,struct sockaddr * myaddr)33 static void get_my_ipv6_addr(struct net_if *iface,
34 struct sockaddr *myaddr)
35 {
36 #if defined(CONFIG_NET_IPV6)
37 const struct in6_addr *my6addr;
38
39 my6addr = net_if_ipv6_select_src_addr(iface,
40 &net_sin6(myaddr)->sin6_addr);
41
42 memcpy(&net_sin6(myaddr)->sin6_addr, my6addr, sizeof(struct in6_addr));
43
44 net_sin6(myaddr)->sin6_port = 0U; /* let the IP stack to select */
45 #endif
46 }
47
get_my_ipv4_addr(struct net_if * iface,struct sockaddr * myaddr)48 static void get_my_ipv4_addr(struct net_if *iface,
49 struct sockaddr *myaddr)
50 {
51 #if defined(CONFIG_NET_NATIVE_IPV4)
52 /* Just take the first IPv4 address of an interface. */
53 memcpy(&net_sin(myaddr)->sin_addr,
54 &iface->config.ip.ipv4->unicast[0].ipv4.address.in_addr,
55 sizeof(struct in_addr));
56
57 net_sin(myaddr)->sin_port = 0U; /* let the IP stack to select */
58 #endif
59 }
60
print_connect_info(const struct shell * sh,int family,struct sockaddr * myaddr,struct sockaddr * addr)61 static void print_connect_info(const struct shell *sh,
62 int family,
63 struct sockaddr *myaddr,
64 struct sockaddr *addr)
65 {
66 switch (family) {
67 case AF_INET:
68 if (IS_ENABLED(CONFIG_NET_IPV4)) {
69 PR("Connecting from %s:%u ",
70 net_sprint_ipv4_addr(&net_sin(myaddr)->sin_addr),
71 ntohs(net_sin(myaddr)->sin_port));
72 PR("to %s:%u\n",
73 net_sprint_ipv4_addr(&net_sin(addr)->sin_addr),
74 ntohs(net_sin(addr)->sin_port));
75 } else {
76 PR_INFO("IPv4 not supported\n");
77 }
78
79 break;
80
81 case AF_INET6:
82 if (IS_ENABLED(CONFIG_NET_IPV6)) {
83 PR("Connecting from [%s]:%u ",
84 net_sprint_ipv6_addr(&net_sin6(myaddr)->sin6_addr),
85 ntohs(net_sin6(myaddr)->sin6_port));
86 PR("to [%s]:%u\n",
87 net_sprint_ipv6_addr(&net_sin6(addr)->sin6_addr),
88 ntohs(net_sin6(addr)->sin6_port));
89 } else {
90 PR_INFO("IPv6 not supported\n");
91 }
92
93 break;
94
95 default:
96 PR_WARNING("Unknown protocol family (%d)\n", family);
97 break;
98 }
99 }
100
tcp_connect(const struct shell * sh,char * host,uint16_t port,struct net_context ** ctx)101 static void tcp_connect(const struct shell *sh, char *host, uint16_t port,
102 struct net_context **ctx)
103 {
104 struct net_if *iface = net_if_get_default();
105 struct sockaddr myaddr;
106 struct sockaddr addr;
107 struct net_nbr *nbr;
108 int addrlen;
109 int family;
110 int ret;
111
112 if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
113 ret = net_addr_pton(AF_INET6, host,
114 &net_sin6(&addr)->sin6_addr);
115 if (ret < 0) {
116 PR_WARNING("Invalid IPv6 address\n");
117 return;
118 }
119
120 net_sin6(&addr)->sin6_port = htons(port);
121 addrlen = sizeof(struct sockaddr_in6);
122
123 nbr = net_ipv6_nbr_lookup(NULL, &net_sin6(&addr)->sin6_addr);
124 if (nbr) {
125 iface = nbr->iface;
126 }
127
128 get_my_ipv6_addr(iface, &myaddr);
129 family = addr.sa_family = myaddr.sa_family = AF_INET6;
130
131 } else if (IS_ENABLED(CONFIG_NET_IPV4) &&
132 !IS_ENABLED(CONFIG_NET_IPV6)) {
133 ARG_UNUSED(nbr);
134
135 ret = net_addr_pton(AF_INET, host, &net_sin(&addr)->sin_addr);
136 if (ret < 0) {
137 PR_WARNING("Invalid IPv4 address\n");
138 return;
139 }
140
141 get_my_ipv4_addr(iface, &myaddr);
142 net_sin(&addr)->sin_port = htons(port);
143 addrlen = sizeof(struct sockaddr_in);
144 family = addr.sa_family = myaddr.sa_family = AF_INET;
145 } else if (IS_ENABLED(CONFIG_NET_IPV6) &&
146 IS_ENABLED(CONFIG_NET_IPV4)) {
147 ret = net_addr_pton(AF_INET6, host,
148 &net_sin6(&addr)->sin6_addr);
149 if (ret < 0) {
150 ret = net_addr_pton(AF_INET, host,
151 &net_sin(&addr)->sin_addr);
152 if (ret < 0) {
153 PR_WARNING("Invalid IP address\n");
154 return;
155 }
156
157 net_sin(&addr)->sin_port = htons(port);
158 addrlen = sizeof(struct sockaddr_in);
159
160 get_my_ipv4_addr(iface, &myaddr);
161 family = addr.sa_family = myaddr.sa_family = AF_INET;
162 } else {
163 net_sin6(&addr)->sin6_port = htons(port);
164 addrlen = sizeof(struct sockaddr_in6);
165
166 nbr = net_ipv6_nbr_lookup(NULL,
167 &net_sin6(&addr)->sin6_addr);
168 if (nbr) {
169 iface = nbr->iface;
170 }
171
172 get_my_ipv6_addr(iface, &myaddr);
173 family = addr.sa_family = myaddr.sa_family = AF_INET6;
174 }
175 } else {
176 PR_WARNING("No IPv6 nor IPv4 is enabled\n");
177 return;
178 }
179
180 print_connect_info(sh, family, &myaddr, &addr);
181
182 ret = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, ctx);
183 if (ret < 0) {
184 PR_WARNING("Cannot get TCP context (%d)\n", ret);
185 return;
186 }
187
188 ret = net_context_bind(*ctx, &myaddr, addrlen);
189 if (ret < 0) {
190 PR_WARNING("Cannot bind TCP (%d)\n", ret);
191 return;
192 }
193
194 /* Note that we cannot put shell as a user_data when connecting
195 * because the tcp_connected() will be called much later and
196 * all local stack variables are lost at that point.
197 */
198 tcp_shell = sh;
199
200 #if defined(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)
201 #define CONNECT_TIMEOUT K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)
202 #else
203 #define CONNECT_TIMEOUT K_SECONDS(3)
204 #endif
205
206 net_context_ref(*ctx);
207
208 ret = net_context_connect(*ctx, &addr, addrlen, tcp_connected,
209 CONNECT_TIMEOUT, NULL);
210 if (ret < 0) {
211 PR_WARNING("Connect failed!\n");
212 net_context_put(*ctx);
213 tcp_ctx = NULL;
214 }
215 }
216
tcp_sent_cb(struct net_context * context,int status,void * user_data)217 static void tcp_sent_cb(struct net_context *context,
218 int status, void *user_data)
219 {
220 PR_SHELL(tcp_shell, "Message sent\n");
221 }
222
tcp_recv_cb(struct net_context * context,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,int status,void * user_data)223 static void tcp_recv_cb(struct net_context *context, struct net_pkt *pkt,
224 union net_ip_header *ip_hdr,
225 union net_proto_header *proto_hdr,
226 int status, void *user_data)
227 {
228 int ret, len;
229
230 if (pkt == NULL) {
231 if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
232 return;
233 }
234
235 ret = net_context_put(tcp_ctx);
236 if (ret < 0) {
237 PR_SHELL(tcp_shell,
238 "Cannot close the connection (%d)\n", ret);
239 return;
240 }
241
242 PR_SHELL(tcp_shell, "Connection closed by remote peer.\n");
243 tcp_ctx = NULL;
244
245 return;
246 }
247
248 len = net_pkt_remaining_data(pkt);
249
250 (void)net_context_update_recv_wnd(context, len);
251
252 PR_SHELL(tcp_shell, "%zu bytes received\n", net_pkt_get_len(pkt));
253
254 net_pkt_unref(pkt);
255 }
256 #endif
257
cmd_net_tcp_connect(const struct shell * sh,size_t argc,char * argv[])258 static int cmd_net_tcp_connect(const struct shell *sh, size_t argc, char *argv[])
259 {
260 #if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
261 int arg = 0;
262
263 /* tcp connect <ip> port */
264 char *endptr;
265 char *ip;
266 uint16_t port;
267
268 /* tcp connect <ip> port */
269 if (tcp_ctx && net_context_is_used(tcp_ctx)) {
270 PR("Already connected\n");
271 return -ENOEXEC;
272 }
273
274 if (!argv[++arg]) {
275 PR_WARNING("Peer IP address missing.\n");
276 return -ENOEXEC;
277 }
278
279 ip = argv[arg];
280
281 if (!argv[++arg]) {
282 PR_WARNING("Peer port missing.\n");
283 return -ENOEXEC;
284 }
285
286 port = strtol(argv[arg], &endptr, 10);
287 if (*endptr != '\0') {
288 PR_WARNING("Invalid port %s\n", argv[arg]);
289 return -ENOEXEC;
290 }
291
292 tcp_connect(sh, ip, port, &tcp_ctx);
293 #else
294 PR_INFO("Set %s to enable %s support.\n",
295 "CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
296 #endif /* CONFIG_NET_NATIVE_TCP */
297
298 return 0;
299 }
300
cmd_net_tcp_send(const struct shell * sh,size_t argc,char * argv[])301 static int cmd_net_tcp_send(const struct shell *sh, size_t argc, char *argv[])
302 {
303 #if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
304 int arg = 0;
305 int ret;
306 struct net_shell_user_data user_data;
307
308 /* tcp send <data> */
309 if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
310 PR_WARNING("Not connected\n");
311 return -ENOEXEC;
312 }
313
314 if (!argv[++arg]) {
315 PR_WARNING("No data to send.\n");
316 return -ENOEXEC;
317 }
318
319 user_data.sh = sh;
320
321 ret = net_context_send(tcp_ctx, (uint8_t *)argv[arg],
322 strlen(argv[arg]), tcp_sent_cb,
323 TCP_TIMEOUT, &user_data);
324 if (ret < 0) {
325 PR_WARNING("Cannot send msg (%d)\n", ret);
326 return -ENOEXEC;
327 }
328
329 #else
330 PR_INFO("Set %s to enable %s support.\n",
331 "CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
332 #endif /* CONFIG_NET_NATIVE_TCP */
333
334 return 0;
335 }
336
cmd_net_tcp_recv(const struct shell * sh,size_t argc,char * argv[])337 static int cmd_net_tcp_recv(const struct shell *sh, size_t argc, char *argv[])
338 {
339 #if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
340 int ret;
341 struct net_shell_user_data user_data;
342
343 /* tcp recv */
344 if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
345 PR_WARNING("Not connected\n");
346 return -ENOEXEC;
347 }
348
349 user_data.sh = sh;
350
351 ret = net_context_recv(tcp_ctx, tcp_recv_cb, K_NO_WAIT, &user_data);
352 if (ret < 0) {
353 PR_WARNING("Cannot recv data (%d)\n", ret);
354 return -ENOEXEC;
355 }
356
357 #else
358 PR_INFO("Set %s to enable %s support.\n",
359 "CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
360 #endif /* CONFIG_NET_NATIVE_TCP */
361
362 return 0;
363 }
364
cmd_net_tcp_close(const struct shell * sh,size_t argc,char * argv[])365 static int cmd_net_tcp_close(const struct shell *sh, size_t argc, char *argv[])
366 {
367 #if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
368 int ret;
369
370 /* tcp close */
371 if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
372 PR_WARNING("Not connected\n");
373 return -ENOEXEC;
374 }
375
376 ret = net_context_put(tcp_ctx);
377 if (ret < 0) {
378 PR_WARNING("Cannot close the connection (%d)\n", ret);
379 return -ENOEXEC;
380 }
381
382 PR("Connection closed.\n");
383 tcp_ctx = NULL;
384 #else
385 PR_INFO("Set %s to enable %s support.\n",
386 "CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
387 #endif /* CONFIG_NET_TCP */
388
389 return 0;
390 }
391
cmd_net_tcp(const struct shell * sh,size_t argc,char * argv[])392 static int cmd_net_tcp(const struct shell *sh, size_t argc, char *argv[])
393 {
394 ARG_UNUSED(argc);
395 ARG_UNUSED(argv);
396
397 return 0;
398 }
399
400 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_tcp,
401 SHELL_CMD(connect, NULL,
402 "'net tcp connect <address> <port>' connects to TCP peer.",
403 cmd_net_tcp_connect),
404 SHELL_CMD(send, NULL,
405 "'net tcp send <data>' sends data to peer using TCP.",
406 cmd_net_tcp_send),
407 SHELL_CMD(recv, NULL,
408 "'net tcp recv' receives data using TCP.",
409 cmd_net_tcp_recv),
410 SHELL_CMD(close, NULL,
411 "'net tcp close' closes TCP connection.", cmd_net_tcp_close),
412 SHELL_SUBCMD_SET_END
413 );
414
415 SHELL_SUBCMD_ADD((net), tcp, &net_cmd_tcp,
416 "Connect/send/close TCP connection.",
417 cmd_net_tcp, 1, 0);
418