1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_shell);
9 
10 #include <zephyr/net/net_if.h>
11 #include <strings.h>
12 #include <ctype.h>
13 
14 #include "net_shell_private.h"
15 
16 #if defined(CONFIG_NET_CONNECTION_MANAGER)
17 
18 #include "conn_mgr_private.h"
19 #include <zephyr/net/conn_mgr_connectivity.h>
20 #include <zephyr/net/conn_mgr_monitor.h>
21 
22 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
23 
24 
25 #define CM_IF_NAME_NONE "unnamed"
26 
27 #if defined(CONFIG_NET_INTERFACE_NAME)
28 #define CM_MAX_IF_NAME MAX(sizeof(CM_IF_NAME_NONE), CONFIG_NET_INTERFACE_NAME_LEN)
29 #else
30 #define CM_MAX_IF_NAME sizeof(CM_IF_NAME_NONE)
31 #endif
32 
33 #define CM_MAX_IF_INFO (CM_MAX_IF_NAME + 40)
34 
35 
36 /* Parsing and printing helpers. None of these are used unless CONFIG_NET_CONNECTION_MANAGER
37  * is enabled.
38  */
39 
40 #if defined(CONFIG_NET_CONNECTION_MANAGER)
41 
42 enum cm_type {
43 	CM_TARG_IFACE,
44 	CM_TARG_NONE,
45 	CM_TARG_ALL,
46 	CM_TARG_INVALID,
47 };
48 struct cm_target {
49 	enum cm_type type;
50 	struct net_if *iface;
51 };
52 enum cm_gs_type {
53 	CM_GS_GET,
54 	CM_GS_SET
55 };
56 struct cm_flag_string {
57 	const char *const name;
58 	enum conn_mgr_if_flag flag;
59 };
60 static const struct cm_flag_string flag_strings[] = {
61 	{"PERSISTENT",		CONN_MGR_IF_PERSISTENT},
62 	{"NO_AUTO_CONNECT",	CONN_MGR_IF_NO_AUTO_CONNECT},
63 	{"NO_AUTO_DOWN",	CONN_MGR_IF_NO_AUTO_DOWN},
64 };
65 
flag_name(enum conn_mgr_if_flag flag)66 static const char *flag_name(enum conn_mgr_if_flag flag)
67 {
68 	/* Scan over predefined flag strings, and return the name
69 	 * of the first one of matching flag.
70 	 */
71 	for (int i = 0; i < ARRAY_SIZE(flag_strings); i++) {
72 		if (flag_strings[i].flag == flag) {
73 			return flag_strings[i].name;
74 		}
75 	}
76 
77 	/* No matches found, it's invalid. */
78 	return "INVALID";
79 }
80 
cm_print_flags(const struct shell * sh)81 static void cm_print_flags(const struct shell *sh)
82 {
83 	PR("Valid flag keywords are:\n");
84 	for (int i = 0; i < ARRAY_SIZE(flag_strings); i++) {
85 		PR("\t%s,\n", flag_strings[i].name);
86 	}
87 }
88 
89 /* Verify that a provided string consists only of the characters 0-9*/
check_numeric(char * str)90 static bool check_numeric(char *str)
91 {
92 	int i;
93 	int len = strlen(str);
94 
95 	for (i = 0; i < len; i++) {
96 		if (!isdigit((int)str[i])) {
97 			return false;
98 		}
99 	}
100 
101 	return true;
102 }
103 
cm_target_help(const struct shell * sh)104 static void cm_target_help(const struct shell *sh)
105 {
106 	PR("Valid target specifiers are 'ifi [index]', 'if [name]', or '[index]'.\n");
107 }
108 
109 /* These parsers treat argv as a tokenstream, and increment *argidx by the number of
110  * tokens parsed.
111  */
parse_ifi_target(const struct shell * sh,size_t argc,char * argv[],int * argidx,struct cm_target * target)112 static int parse_ifi_target(const struct shell *sh, size_t argc, char *argv[], int *argidx,
113 			    struct cm_target *target)
114 {
115 	char *arg;
116 	int err = 0;
117 	unsigned long iface_index;
118 
119 	/* At least one remaining argument is required to specify a target index */
120 	if (*argidx >= argc) {
121 		PR_ERROR("Please specify the target iface index.\n");
122 		goto error;
123 	}
124 
125 	arg = argv[*argidx];
126 
127 	iface_index = shell_strtoul(arg, 10, &err);
128 
129 	if (err) {
130 		PR_ERROR("\"%s\" is not a valid iface index.\n", arg);
131 		goto error;
132 	}
133 
134 	target->iface = net_if_get_by_index(iface_index);
135 
136 	if (target->iface == NULL) {
137 		PR_ERROR("iface with index \"%s\" does not exist.\n", arg);
138 		goto error;
139 	}
140 
141 	*argidx += 1;
142 	target->type = CM_TARG_IFACE;
143 	return 0;
144 
145 error:
146 	target->type = CM_TARG_INVALID;
147 	return -EINVAL;
148 }
149 
parse_if_target(const struct shell * sh,size_t argc,char * argv[],int * argidx,struct cm_target * target)150 static int parse_if_target(const struct shell *sh, size_t argc, char *argv[], int *argidx,
151 			   struct cm_target *target)
152 {
153 #if defined(CONFIG_NET_INTERFACE_NAME)
154 	char *arg;
155 	/* At least one remaining argument is required to specify a target name */
156 	if (*argidx >= argc) {
157 		PR_ERROR("Please specify the target iface name.\n");
158 		goto error;
159 	}
160 
161 	arg = argv[*argidx];
162 
163 	target->iface = net_if_get_by_index(net_if_get_by_name(arg));
164 
165 	if (target->iface == NULL) {
166 		PR_ERROR("iface with name \"%s\" does not exist.\n", arg);
167 		goto error;
168 	}
169 
170 	*argidx += 1;
171 	target->type = CM_TARG_IFACE;
172 	return 0;
173 #else
174 	PR_ERROR("iface name lookup requires CONFIG_NET_INTERFACE_NAME.\n");
175 	goto error;
176 #endif
177 
178 error:
179 	target->type = CM_TARG_INVALID;
180 	return -EINVAL;
181 }
182 
183 /* parse `if [iface name]`, `ifi [iface index]`, `[iface index]`, or `all` */
parse_target(const struct shell * sh,size_t argc,char * argv[],int * argidx,struct cm_target * target)184 static int parse_target(const struct shell *sh, size_t argc, char *argv[], int *argidx,
185 			struct cm_target *target)
186 {
187 	char *arg;
188 
189 	/* At least one argument is required to specify a target */
190 	if (*argidx >= argc) {
191 		target->type = CM_TARG_NONE;
192 		return 0;
193 	}
194 
195 	arg = argv[*argidx];
196 
197 	/* At least one argument provided. Is it "all" or "none"? */
198 	if (strcasecmp(arg, "all") == 0) {
199 		*argidx += 1;
200 		target->type = CM_TARG_ALL;
201 		return 0;
202 	}
203 
204 	if (strcasecmp(arg, "none") == 0) {
205 		*argidx += 1;
206 		target->type = CM_TARG_NONE;
207 		return 0;
208 	}
209 
210 	/* If not, interpret as an iface index if it is also numeric */
211 	if (check_numeric(arg)) {
212 		return parse_ifi_target(sh, argc, argv, argidx, target);
213 	}
214 
215 	/* Otherwise, arg must be a target type specifier */
216 	if (strcasecmp(arg, "if") == 0) {
217 		*argidx += 1;
218 		return parse_if_target(sh, argc, argv, argidx, target);
219 	}
220 
221 	if (strcasecmp(arg, "ifi") == 0) {
222 		*argidx += 1;
223 		return parse_ifi_target(sh, argc, argv, argidx, target);
224 	}
225 
226 	PR_ERROR("%s is not a valid target type or target specifier.\n", arg);
227 	cm_target_help(sh);
228 	target->type = CM_TARG_INVALID;
229 
230 	return -EINVAL;
231 }
232 
parse_getset(const struct shell * sh,size_t argc,char * argv[],int * argidx,enum cm_gs_type * result)233 static int parse_getset(const struct shell *sh, size_t argc, char *argv[], int *argidx,
234 			enum cm_gs_type *result)
235 {
236 	char *arg;
237 
238 	/* At least one argument is required to specify get or set */
239 	if (*argidx >= argc) {
240 		goto error;
241 	}
242 
243 	arg = argv[*argidx];
244 
245 	if (strcasecmp(arg, "get") == 0) {
246 		*argidx += 1;
247 		*result = CM_GS_GET;
248 		return 0;
249 	}
250 
251 	if (strcasecmp(arg, "set") == 0) {
252 		*argidx += 1;
253 		*result = CM_GS_SET;
254 		return 0;
255 	}
256 
257 error:
258 	PR_ERROR("Please specify get or set\n");
259 	return -EINVAL;
260 }
261 
parse_flag(const struct shell * sh,size_t argc,char * argv[],int * argidx,enum conn_mgr_if_flag * result)262 static int parse_flag(const struct shell *sh, size_t argc, char *argv[], int *argidx,
263 		      enum conn_mgr_if_flag *result)
264 {
265 	char *arg;
266 
267 	/* At least one argument is required to specify get or set */
268 	if (*argidx >= argc) {
269 		PR_ERROR("Please specify a flag.\n");
270 		cm_print_flags(sh);
271 
272 		return -EINVAL;
273 	}
274 
275 	arg = argv[*argidx];
276 
277 	for (int i = 0; i < ARRAY_SIZE(flag_strings); i++) {
278 		if (strcasecmp(arg, flag_strings[i].name) == 0) {
279 			*argidx += 1;
280 			*result = flag_strings[i].flag;
281 			return 0;
282 		}
283 	}
284 
285 	PR_ERROR("%s is not a valid flag.\n", arg);
286 	return -EINVAL;
287 }
288 
parse_bool(const struct shell * sh,size_t argc,char * argv[],int * argidx,bool * result)289 static int parse_bool(const struct shell *sh, size_t argc, char *argv[], int *argidx, bool *result)
290 {
291 	char *arg;
292 
293 	/* At least one argument is required to specify a boolean */
294 	if (*argidx >= argc) {
295 		goto error;
296 	}
297 
298 	arg = argv[*argidx];
299 
300 	if (strcasecmp(arg, "yes") == 0 ||
301 	    strcasecmp(arg, "y") == 0 ||
302 	    strcasecmp(arg, "1") == 0 ||
303 	    strcasecmp(arg, "true") == 0) {
304 		*argidx += 1;
305 		*result = true;
306 		return 0;
307 	}
308 
309 	if (strcasecmp(arg, "no") == 0 ||
310 	    strcasecmp(arg, "n") == 0 ||
311 	    strcasecmp(arg, "0") == 0 ||
312 	    strcasecmp(arg, "false") == 0) {
313 		*argidx += 1;
314 		*result = false;
315 		return 0;
316 	}
317 
318 error:
319 	PR_ERROR("Please specify true or false.\n");
320 	return -EINVAL;
321 }
322 
parse_timeout(const struct shell * sh,size_t argc,char * argv[],int * argidx,int * result)323 static int parse_timeout(const struct shell *sh, size_t argc, char *argv[], int *argidx,
324 			 int *result)
325 {
326 	char *arg;
327 	int err = 0;
328 	unsigned long value;
329 
330 	/* At least one argument is required to specify a timeout */
331 	if (*argidx >= argc) {
332 		PR_ERROR("Please specify a timeout (in seconds).\n");
333 		return -EINVAL;
334 	}
335 
336 	arg = argv[*argidx];
337 
338 	/* Check for special keyword "none" */
339 	if (strcasecmp(arg, "none") == 0) {
340 		*argidx += 1;
341 		*result = CONN_MGR_IF_NO_TIMEOUT;
342 		return 0;
343 	}
344 
345 	/* Otherwise, try to parse integer timeout (seconds). */
346 	if (!check_numeric(arg)) {
347 		PR_ERROR("%s is not a valid timeout.\n", arg);
348 		return -EINVAL;
349 	}
350 
351 	value = shell_strtoul(arg, 10, &err);
352 	if (err) {
353 		PR_ERROR("%s is not a valid timeout.\n", arg);
354 		return -EINVAL;
355 	}
356 
357 	*argidx += 1;
358 	*result = value;
359 	return 0;
360 }
361 
cm_get_iface_info(struct net_if * iface,char * buf,size_t len)362 static void cm_get_iface_info(struct net_if *iface, char *buf, size_t len)
363 {
364 #if  defined(CONFIG_NET_INTERFACE_NAME)
365 	char name[CM_MAX_IF_NAME];
366 
367 	if (net_if_get_name(iface, name, sizeof(name))) {
368 		strcpy(name, CM_IF_NAME_NONE);
369 	}
370 
371 	snprintk(buf, len, "%d (%p - %s - %s)", net_if_get_by_iface(iface), iface, name,
372 						iface2str(iface, NULL));
373 #else
374 	snprintk(buf, len, "%d (%p - %s)", net_if_get_by_iface(iface), iface,
375 					   iface2str(iface, NULL));
376 #endif
377 }
378 
379 /* bulk iface actions */
cm_iface_status(struct net_if * iface,void * user_data)380 static void cm_iface_status(struct net_if *iface, void *user_data)
381 {
382 	const struct shell *sh = user_data;
383 	uint16_t state = conn_mgr_if_state(iface);
384 	bool ignored;
385 	bool bound;
386 	bool admin_up;
387 	bool oper_up;
388 	bool has_ipv4;
389 	bool has_ipv6;
390 	bool connected;
391 	char iface_info[CM_MAX_IF_INFO];
392 	char *ip_state;
393 
394 	cm_get_iface_info(iface, iface_info, sizeof(iface_info));
395 
396 	if (state == CONN_MGR_IF_STATE_INVALID) {
397 		PR("iface %s not tracked.\n", iface_info);
398 	} else {
399 		ignored = state & CONN_MGR_IF_IGNORED;
400 		bound = conn_mgr_if_is_bound(iface);
401 		admin_up = net_if_is_admin_up(iface);
402 		oper_up = state & CONN_MGR_IF_UP;
403 		has_ipv4 = state & CONN_MGR_IF_IPV4_SET;
404 		has_ipv6 = state & CONN_MGR_IF_IPV6_SET;
405 		connected = state & CONN_MGR_IF_READY;
406 
407 		if (has_ipv4 && has_ipv6) {
408 			ip_state = "IPv4 + IPv6";
409 		} else if (has_ipv4) {
410 			ip_state = "IPv4";
411 		} else if (has_ipv6) {
412 			ip_state = "IPv6";
413 		} else {
414 			ip_state = "no IP";
415 		}
416 
417 		PR("iface %s status: %s, %s, %s, %s, %s, %s.\n", iface_info,
418 		   ignored ?	"ignored"	: "watched",
419 		   bound ?	"bound"		: "not bound",
420 		   admin_up ?	"admin-up"	: "admin-down",
421 		   oper_up ?	"oper-up"	: "oper-down",
422 		   ip_state,
423 		   connected ?	"connected"	: "not connected");
424 	}
425 }
426 
cm_iface_ignore(struct net_if * iface,void * user_data)427 static void cm_iface_ignore(struct net_if *iface, void *user_data)
428 {
429 	const struct shell *sh = user_data;
430 	char iface_info[CM_MAX_IF_INFO];
431 
432 	cm_get_iface_info(iface, iface_info, sizeof(iface_info));
433 
434 	conn_mgr_ignore_iface(iface);
435 	PR("iface %s now ignored.\n", iface_info);
436 }
437 
cm_iface_watch(struct net_if * iface,void * user_data)438 static void cm_iface_watch(struct net_if *iface, void *user_data)
439 {
440 	const struct shell *sh = user_data;
441 	char iface_info[CM_MAX_IF_INFO];
442 
443 	cm_get_iface_info(iface, iface_info, sizeof(iface_info));
444 
445 	conn_mgr_watch_iface(iface);
446 	PR("iface %s now watched.\n", iface_info);
447 }
448 
449 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
450 
not_available(const struct shell * sh)451 static void not_available(const struct shell *sh)
452 {
453 	PR_INFO("This command is not available unless CONFIG_NET_CONNECTION_MANAGER is enabled.\n");
454 }
455 
456 #endif /* !defined(CONFIG_NET_CONNECTION_MANAGER) */
457 
458 
459 /* Commands */
460 
cmd_net_cm_status(const struct shell * sh,size_t argc,char * argv[])461 static int cmd_net_cm_status(const struct shell *sh, size_t argc, char *argv[])
462 {
463 #if defined(CONFIG_NET_CONNECTION_MANAGER)
464 	int argidx = 1;
465 	struct cm_target target = {
466 		.type = CM_TARG_INVALID
467 	};
468 
469 	if (parse_target(sh, argc, argv, &argidx, &target)) {
470 		/* no need to print anything, parse_target already explained the issue */
471 		return 0;
472 	}
473 
474 	if (argidx != argc) {
475 		PR_ERROR("Too many args.\n");
476 		return 0;
477 	}
478 
479 	if (target.type == CM_TARG_NONE || target.type == CM_TARG_ALL) {
480 		net_if_foreach(cm_iface_status, (void *)sh);
481 		return 0;
482 	}
483 
484 	if (target.type == CM_TARG_IFACE) {
485 		cm_iface_status(target.iface, (void *)sh);
486 		return 0;
487 	}
488 
489 	PR_ERROR("Invalid target selected.\n");
490 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
491 	PR_INFO("conn_mgr is not enabled. Enable by setting CONFIG_NET_CONNECTION_MANAGER=y.\n");
492 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
493 	return 0;
494 }
495 
cmd_net_cm_ignore(const struct shell * sh,size_t argc,char * argv[])496 static int cmd_net_cm_ignore(const struct shell *sh, size_t argc, char *argv[])
497 {
498 #if defined(CONFIG_NET_CONNECTION_MANAGER)
499 	int argidx = 1;
500 	struct cm_target target = {
501 		.type = CM_TARG_INVALID
502 	};
503 
504 	if (parse_target(sh, argc, argv, &argidx, &target)) {
505 		/* no need to print anything, parse_target already explained the issue */
506 		return 0;
507 	}
508 
509 	if (argidx != argc) {
510 		PR_ERROR("Too many args.\n");
511 		return 0;
512 	}
513 
514 	if (target.type == CM_TARG_NONE) {
515 		PR_ERROR("Please specify a target.\n");
516 		cm_target_help(sh);
517 		return 0;
518 	}
519 
520 	if (target.type == CM_TARG_ALL) {
521 		PR("Ignoring all ifaces.\n");
522 		net_if_foreach(cm_iface_ignore, (void *)sh);
523 		return 0;
524 	}
525 
526 	if (target.type == CM_TARG_IFACE) {
527 		cm_iface_ignore(target.iface, (void *)sh);
528 		return 0;
529 	}
530 
531 	PR_ERROR("Invalid target selected.\n");
532 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
533 	not_available(sh);
534 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
535 	return 0;
536 }
537 
cmd_net_cm_watch(const struct shell * sh,size_t argc,char * argv[])538 static int cmd_net_cm_watch(const struct shell *sh, size_t argc, char *argv[])
539 {
540 #if defined(CONFIG_NET_CONNECTION_MANAGER)
541 	int argidx = 1;
542 	struct cm_target target = {
543 		.type = CM_TARG_INVALID
544 	};
545 
546 	if (parse_target(sh, argc, argv, &argidx, &target)) {
547 		/* no need to print anything, parse_target already explained the issue */
548 		return 0;
549 	}
550 
551 	if (argidx != argc) {
552 		PR_ERROR("Too many args.\n");
553 		return 0;
554 	}
555 
556 	if (target.type == CM_TARG_NONE) {
557 		PR_ERROR("Please specify a target.\n");
558 		cm_target_help(sh);
559 		return 0;
560 	}
561 
562 	if (target.type == CM_TARG_ALL) {
563 		PR("Watching all ifaces.\n");
564 		net_if_foreach(cm_iface_watch, (void *)sh);
565 		return 0;
566 	}
567 
568 	if (target.type == CM_TARG_IFACE) {
569 		cm_iface_watch(target.iface, (void *)sh);
570 		return 0;
571 	}
572 
573 	PR_ERROR("Invalid target selected.\n");
574 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
575 	not_available(sh);
576 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
577 	return 0;
578 }
579 
cmd_net_cm_connect(const struct shell * sh,size_t argc,char * argv[])580 static int cmd_net_cm_connect(const struct shell *sh, size_t argc, char *argv[])
581 {
582 #if defined(CONFIG_NET_CONNECTION_MANAGER)
583 	int argidx = 1;
584 	struct cm_target target = {
585 		.type = CM_TARG_INVALID
586 	};
587 	char iface_info[CM_MAX_IF_INFO];
588 
589 	if (parse_target(sh, argc, argv, &argidx, &target)) {
590 		/* no need to print anything, parse_target already explained the issue */
591 		return 0;
592 	}
593 
594 	if (argidx != argc) {
595 		PR_ERROR("Too many args.\n");
596 		return 0;
597 	}
598 
599 	if (target.type == CM_TARG_NONE) {
600 		PR_ERROR("Please specify a target.\n");
601 		cm_target_help(sh);
602 		return 0;
603 	}
604 
605 	if (target.type == CM_TARG_ALL) {
606 		PR("Instructing all non-ignored ifaces to connect.\n");
607 		conn_mgr_all_if_connect(true);
608 		return 0;
609 	}
610 
611 	if (target.type == CM_TARG_IFACE) {
612 		cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
613 
614 		if (!conn_mgr_if_is_bound(target.iface)) {
615 			PR_ERROR("iface %s is not bound to a connectivity implementation, cannot "
616 				 "connect.\n", iface_info);
617 			return 0;
618 		}
619 
620 		PR("Instructing iface %s to connect.\n", iface_info);
621 		conn_mgr_if_connect(target.iface);
622 		return 0;
623 	}
624 
625 	PR_ERROR("Invalid target selected.\n");
626 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
627 	not_available(sh);
628 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
629 	return 0;
630 }
631 
cmd_net_cm_disconnect(const struct shell * sh,size_t argc,char * argv[])632 static int cmd_net_cm_disconnect(const struct shell *sh, size_t argc, char *argv[])
633 {
634 #if defined(CONFIG_NET_CONNECTION_MANAGER)
635 	int argidx = 1;
636 	struct cm_target target = {
637 		.type = CM_TARG_INVALID
638 	};
639 	char iface_info[CM_MAX_IF_INFO];
640 
641 	if (parse_target(sh, argc, argv, &argidx, &target)) {
642 		/* no need to print anything, parse_target already explained the issue */
643 		return 0;
644 	}
645 
646 	if (argidx != argc) {
647 		PR_ERROR("Too many args.\n");
648 		return 0;
649 	}
650 
651 	if (target.type == CM_TARG_NONE) {
652 		PR_ERROR("Please specify a target.\n");
653 		cm_target_help(sh);
654 		return 0;
655 	}
656 
657 	if (target.type == CM_TARG_ALL) {
658 		PR("Instructing all non-ignored ifaces to disconnect.\n");
659 		conn_mgr_all_if_disconnect(true);
660 		return 0;
661 	}
662 
663 	if (target.type == CM_TARG_IFACE) {
664 		cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
665 
666 		if (!conn_mgr_if_is_bound(target.iface)) {
667 			PR_ERROR("iface %s is not bound to a connectivity implementation, cannot "
668 				 "disconnect.\n", iface_info);
669 			return 0;
670 		}
671 
672 		PR("Instructing iface %s to disonnect.\n", iface_info);
673 		conn_mgr_if_disconnect(target.iface);
674 		return 0;
675 	}
676 
677 	PR_ERROR("Invalid target selected.\n");
678 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
679 	not_available(sh);
680 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
681 	return 0;
682 }
683 
cmd_net_cm_up(const struct shell * sh,size_t argc,char * argv[])684 static int cmd_net_cm_up(const struct shell *sh, size_t argc, char *argv[])
685 {
686 #if defined(CONFIG_NET_CONNECTION_MANAGER)
687 	int argidx = 1;
688 	struct cm_target target = {
689 		.type = CM_TARG_INVALID
690 	};
691 	char iface_info[CM_MAX_IF_INFO];
692 
693 	if (parse_target(sh, argc, argv, &argidx, &target)) {
694 		/* no need to print anything, parse_target already explained the issue */
695 		return 0;
696 	}
697 
698 	if (argidx != argc) {
699 		PR_ERROR("Too many args.\n");
700 		return 0;
701 	}
702 
703 	if (target.type == CM_TARG_NONE) {
704 		PR_ERROR("Please specify a target.\n");
705 		return 0;
706 	}
707 
708 	if (target.type == CM_TARG_ALL) {
709 		PR("Taking all non-ignored ifaces admin-up.\n");
710 		conn_mgr_all_if_up(true);
711 		return 0;
712 	}
713 
714 	if (target.type == CM_TARG_IFACE) {
715 		cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
716 		PR("Taking iface %s admin-up.\n", iface_info);
717 		PR_WARNING("This command duplicates 'net iface up' if [target] != all.\n");
718 
719 		net_if_up(target.iface);
720 		return 0;
721 	}
722 
723 	PR_ERROR("Invalid target selected.\n");
724 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
725 	not_available(sh);
726 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
727 	return 0;
728 }
729 
cmd_net_cm_down(const struct shell * sh,size_t argc,char * argv[])730 static int cmd_net_cm_down(const struct shell *sh, size_t argc, char *argv[])
731 {
732 #if defined(CONFIG_NET_CONNECTION_MANAGER)
733 	int argidx = 1;
734 	struct cm_target target = {
735 		.type = CM_TARG_INVALID
736 	};
737 	char iface_info[CM_MAX_IF_INFO];
738 
739 	if (parse_target(sh, argc, argv, &argidx, &target)) {
740 		/* no need to print anything, parse_target already explained the issue */
741 		return 0;
742 	}
743 
744 	if (argidx != argc) {
745 		PR_ERROR("Too many args.\n");
746 		return 0;
747 	}
748 
749 	if (target.type == CM_TARG_NONE) {
750 		PR_ERROR("Please specify a target.\n");
751 		cm_target_help(sh);
752 		return 0;
753 	}
754 
755 	if (target.type == CM_TARG_ALL) {
756 		PR("Taking all non-ignored ifaces admin-down.\n");
757 		conn_mgr_all_if_down(true);
758 		return 0;
759 	}
760 
761 	if (target.type == CM_TARG_IFACE) {
762 		cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
763 		PR("Taking iface %s admin-down.\n", iface_info);
764 		PR_WARNING("This command duplicates 'net iface down' if [target] != all.\n");
765 
766 		net_if_down(target.iface);
767 		return 0;
768 	}
769 
770 	PR_ERROR("Invalid target selected.\n");
771 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
772 	not_available(sh);
773 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
774 	return 0;
775 }
776 
cmd_net_cm_flag(const struct shell * sh,size_t argc,char * argv[])777 static int cmd_net_cm_flag(const struct shell *sh, size_t argc, char *argv[])
778 {
779 #if defined(CONFIG_NET_CONNECTION_MANAGER)
780 	int argidx = 1;
781 	enum cm_gs_type getset = CM_GS_GET;
782 	enum conn_mgr_if_flag flag = CONN_MGR_IF_PERSISTENT;
783 	bool value = false;
784 	struct cm_target target = {
785 		.type = CM_TARG_INVALID
786 	};
787 	char iface_info[CM_MAX_IF_INFO];
788 
789 	if (parse_target(sh, argc, argv, &argidx, &target)) {
790 		return 0;
791 	}
792 
793 	if (target.type == CM_TARG_NONE) {
794 		PR_ERROR("Please specify a target.\n");
795 		cm_target_help(sh);
796 		return 0;
797 	}
798 
799 	if (target.type == CM_TARG_ALL) {
800 		PR_ERROR("Cannot get/set flags for all ifaces.\n");
801 		return 0;
802 	}
803 
804 	if (target.type != CM_TARG_IFACE) {
805 		PR_ERROR("Invalid target selected.\n");
806 		return 0;
807 	}
808 
809 	if (parse_getset(sh, argc, argv, &argidx, &getset)) {
810 		return 0;
811 	}
812 
813 	if (parse_flag(sh, argc, argv, &argidx, &flag)) {
814 		return 0;
815 	}
816 
817 	/* If we are in set mode, expect the value to be provided. */
818 	if (getset == CM_GS_SET && parse_bool(sh, argc, argv, &argidx, &value)) {
819 		return 0;
820 	}
821 
822 	if (argidx != argc) {
823 		PR_ERROR("Too many args.\n");
824 		return 0;
825 	}
826 
827 	cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
828 
829 	if (!conn_mgr_if_is_bound(target.iface)) {
830 		PR_ERROR("iface %s is not bound to a connectivity implementation, cannot "
831 			 "get/set connectivity flag.\n", iface_info);
832 		return 0;
833 	}
834 
835 	if (getset == CM_GS_SET) {
836 		(void)conn_mgr_if_set_flag(target.iface, flag, value);
837 		PR("Set the connectivity %s flag to %s on iface %s.\n", flag_name(flag),
838 		   value?"y":"n", iface_info);
839 	} else {
840 		value = conn_mgr_if_get_flag(target.iface, flag);
841 		PR("The current value of the %s connectivity flag on iface %s is %s.\n",
842 		   flag_name(flag), iface_info, value?"y":"n");
843 	}
844 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
845 	not_available(sh);
846 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
847 	return 0;
848 }
849 
cmd_net_cm_timeout(const struct shell * sh,size_t argc,char * argv[])850 static int cmd_net_cm_timeout(const struct shell *sh, size_t argc, char *argv[])
851 {
852 #if defined(CONFIG_NET_CONNECTION_MANAGER)
853 	int argidx = 1;
854 	enum cm_gs_type getset = CM_GS_GET;
855 	int value = CONN_MGR_IF_NO_TIMEOUT;
856 	struct cm_target target = {
857 		.type = CM_TARG_INVALID
858 	};
859 	char iface_info[CM_MAX_IF_INFO];
860 
861 	if (parse_target(sh, argc, argv, &argidx, &target)) {
862 		return 0;
863 	}
864 
865 	if (target.type == CM_TARG_NONE) {
866 		PR_ERROR("Please specify a target.\n");
867 		cm_target_help(sh);
868 		return 0;
869 	}
870 
871 	if (target.type == CM_TARG_ALL) {
872 		PR_ERROR("Cannot get/set timeout for all ifaces.\n");
873 		return 0;
874 	}
875 
876 	if (target.type != CM_TARG_IFACE) {
877 		PR_ERROR("Invalid target selected.\n");
878 		return 0;
879 	}
880 
881 	if (parse_getset(sh, argc, argv, &argidx, &getset)) {
882 		return 0;
883 	}
884 
885 	/* If we are in set mode, expect the value to be provided. */
886 	if (getset == CM_GS_SET && parse_timeout(sh, argc, argv, &argidx, &value)) {
887 		return 0;
888 	}
889 
890 	if (argidx != argc) {
891 		PR_ERROR("Too many args.\n");
892 		return 0;
893 	}
894 
895 	cm_get_iface_info(target.iface, iface_info, sizeof(iface_info));
896 
897 	if (!conn_mgr_if_is_bound(target.iface)) {
898 		PR_ERROR("iface %s is not bound to a connectivity implementation, cannot "
899 			 "get/set connectivity timeout.\n", iface_info);
900 		return 0;
901 	}
902 
903 	if (getset == CM_GS_SET) {
904 		(void)conn_mgr_if_set_timeout(target.iface, value);
905 		PR("Set the connectivity timeout for iface %s to %d%s.\n", iface_info, value,
906 		   value == 0 ? " (no timeout)":" seconds");
907 	} else {
908 		value = conn_mgr_if_get_timeout(target.iface);
909 		PR("The connectivity timeout for iface %s is %d%s.\n", iface_info, value,
910 		   value == 0 ? " (no timeout)":" seconds");
911 	}
912 #else /* defined(CONFIG_NET_CONNECTION_MANAGER) */
913 	not_available(sh);
914 #endif /* defined(CONFIG_NET_CONNECTION_MANAGER) */
915 	return 0;
916 }
917 
918 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_cm,
919 	SHELL_CMD_ARG(status, NULL,
920 		  "'net cm status [target]' shows the connectivity status of the specified "
921 		  "iface(s).",
922 		  cmd_net_cm_status, 1, 2),
923 	SHELL_CMD_ARG(ignore, NULL,
924 		  "'net cm ignore [target]' ignores the specified iface(s).",
925 		  cmd_net_cm_ignore, 1, 2),
926 	SHELL_CMD_ARG(watch, NULL,
927 		  "'net cm watch [target]' watches the specified iface(s).",
928 		  cmd_net_cm_watch, 1, 2),
929 	SHELL_CMD_ARG(connect, NULL,
930 		  "'net cm connect [target]' connects the specified iface(s).",
931 		  cmd_net_cm_connect, 1, 2),
932 	SHELL_CMD_ARG(disconnect, NULL,
933 		  "'net cm disconnect [target]' disconnects the specified iface(s).",
934 		  cmd_net_cm_disconnect, 1, 2),
935 	SHELL_CMD_ARG(up, NULL,
936 		  "'net cm up [target]' takes the specified iface(s) admin-up.",
937 		  cmd_net_cm_up, 1, 2),
938 	SHELL_CMD_ARG(down, NULL,
939 		  "'net cm down [target]' takes the specified iface(s) admin-down.",
940 		  cmd_net_cm_down, 1, 2),
941 	SHELL_CMD_ARG(flag, NULL,
942 		  "'net cm flag [target] [get/set] [flag] [value]' gets or sets a flag "
943 		  "for the specified iface.",
944 		  cmd_net_cm_flag, 1, 5),
945 	SHELL_CMD_ARG(timeout, NULL,
946 		  "'net cm timeout [target] [get/set] [value]' gets or sets the timeout "
947 		  "for the specified iface.",
948 		  cmd_net_cm_timeout, 1, 4),
949 	SHELL_SUBCMD_SET_END
950 );
951 
952 SHELL_SUBCMD_ADD((net), cm, &net_cmd_cm, "Control conn_mgr.", NULL, 1, 0);
953