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