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