2 Command parsing taken from brctl utility.
3 Display code from stp_cli.c in rstplib.
14 /* For scanning through sysfs directories */
16 #include <sys/types.h>
21 #include "ctl_socket_client.h"
22 #include "ctl_functions.h"
24 int get_index_die(const char *ifname
, const char *doc
, int die
)
26 int r
= if_nametoindex(ifname
);
29 "Can't find index for %s %s. Not a valid interface.\n",
38 int get_index(const char *ifname
, const char *doc
)
40 return get_index_die(ifname
, doc
, 1);
43 #define BR_ID_FMT "%02x%02x.%02x%02x%02x%02x%02x%02x"
44 #define BR_ID_ARGS(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]
46 #define BOOL_STR(x) ((x) ? "yes" : "no")
48 static int do_showbridge(const char *br_name
)
52 int br_index
= get_index_die(br_name
, "bridge", 0);
56 int r
= CTL_get_bridge_status(br_index
, &s
);
60 printf("%s\n", br_name
);
61 printf(" enabled\t\t%4s\n", BOOL_STR(s
.enabled
));
62 printf(" bridge id\t\t" BR_ID_FMT
"\n", BR_ID_ARGS(s
.bridge_id
));
63 printf(" designated root\t" BR_ID_FMT
"\n",
64 BR_ID_ARGS(s
.designated_root
));
65 //printf(" designated bridge\t\t", BR_ID_FMT "\n",
66 // BR_ID_ARGS(s.designated_bridge));
67 printf(" root port\t\t%4u", s
.root_port
);
68 printf("\t\t\tpath cost\t\t%4u\n", s
.root_path_cost
);
69 printf(" max age\t\t%4u", s
.max_age
);
70 printf("\t\t\tbridge max age\t\t%4u\n", s
.bridge_max_age
);
71 printf(" hello time\t\t%4u", s
.hello_time
);
72 printf("\t\t\tbridge hello time\t%4u\n", s
.bridge_hello_time
);
73 printf(" forward delay\t\t%4u", s
.forward_delay
);
74 printf("\t\t\tbridge forward delay\t%4u\n", s
.bridge_forward_delay
);
75 printf(" tx hold count\t\t%4u\n", s
.tx_hold_count
);
76 printf(" protocol version\t%4u\n", s
.protocol_version
);
77 printf(" time since topology change\t%4u\n",
78 s
.time_since_topology_change
);
79 printf(" toplogy change count\t\t%4u\n", s
.topology_change_count
);
80 printf(" topology change\t\t%4s\n", BOOL_STR(s
.topology_change
));
85 #define SYSFS_PATH_MAX 256
86 #define SYSFS_CLASS_NET "/sys/class/net"
88 static int isbridge(const struct dirent
*entry
)
90 char path
[SYSFS_PATH_MAX
];
93 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"/%s/bridge",
95 return stat(path
, &st
) == 0 && S_ISDIR(st
.st_mode
);
98 static int cmd_showbridge(int argc
, char *const *argv
)
102 struct dirent
**namelist
;
108 scandir(SYSFS_CLASS_NET
, &namelist
, isbridge
, alphasort
);
110 fprintf(stderr
, "Error getting list of all bridges\n");
115 for (i
= 0; i
< count
; i
++) {
120 name
= namelist
[i
]->d_name
;
122 int err
= do_showbridge(name
);
128 for (i
= 0; i
< count
; i
++)
137 #define STATE_STR(_state) \
140 char *_str = "unknown"; \
143 case STP_PORT_STATE_DISCARDING: _str = "discarding"; break; \
144 case STP_PORT_STATE_LEARNING: _str = "learning"; break; \
145 case STP_PORT_STATE_FORWARDING: _str = "forwarding"; break; \
150 #define SHORT_STATE_STR(_state) \
153 char *_str = "unkn"; \
156 case STP_PORT_STATE_DISCARDING: _str = "disc"; break; \
157 case STP_PORT_STATE_LEARNING: _str = "lear"; break; \
158 case STP_PORT_STATE_FORWARDING: _str = "forw"; break; \
163 #define ADMIN_P2P_STR(_state) \
166 char *_str = "unkn"; \
169 case STP_ADMIN_P2P_FORCE_FALSE: _str = "no"; break; \
170 case STP_ADMIN_P2P_FORCE_TRUE: _str = "yes"; break; \
171 case STP_ADMIN_P2P_AUTO: _str = "auto"; break; \
179 static int do_showport(int br_index
, const char *port_name
)
183 int port_index
= get_index_die(port_name
, "port", 0);
187 r
= CTL_get_port_status(br_index
, port_index
, &s
);
189 fprintf(stderr
, "Failed to get port state for port %d\n",
195 printf("%s (%u)\n", port_name
, (s
.id
& 0xfff));
196 printf(" enabled\t\t%4s\n", BOOL_STR(s
.enabled
));
197 printf(" port id\t\t%04x\t\t\tstate\t\t%15s\n",
198 s
.id
, STATE_STR(s
.state
));
199 printf(" path cost\t%12d\t\t\tadmin path cost\t%12d\n",
200 s
.path_cost
, s
.admin_path_cost
);
201 printf(" designated root\t" BR_ID_FMT
,
202 BR_ID_ARGS(s
.designated_root
));
203 printf("\tdesignated cost\t%12u\n", s
.designated_cost
);
204 printf(" designated bridge\t" BR_ID_FMT
,
205 BR_ID_ARGS(s
.designated_bridge
));
206 printf("\tdesignated port\t\t%04x\n", s
.designated_port
);
207 printf(" admin edge port\t%4s", BOOL_STR(s
.admin_edge_port
));
208 printf("\t\t\tauto edge port\t\t%4s\n",
209 BOOL_STR(s
.auto_edge_port
));
210 printf(" oper edge port\t\t%4s", BOOL_STR(s
.oper_edge_port
));
211 printf("\t\t\ttoplogy change ack\t%4s\n", BOOL_STR(s
.tc_ack
));
212 printf(" point to point\t\t%4s", BOOL_STR(s
.oper_p2p
));
213 printf("\t\t\tadmin point to point\t%4s\n",
214 ADMIN_P2P_STR(s
.admin_p2p
));
216 printf("%c%c %4s %04x %4s " BR_ID_FMT
" " BR_ID_FMT
" %04x\n",
217 (s
.oper_p2p
) ? ' ' : '*',
218 (s
.oper_edge_port
) ? 'E' : ' ',
221 s
.enabled
?SHORT_STATE_STR(s
.state
):"down",
222 BR_ID_ARGS(s
.designated_root
),
223 BR_ID_ARGS(s
.designated_bridge
),
229 static int not_dot_dotdot(const struct dirent
*entry
)
231 const char *n
= entry
->d_name
;
233 return !(n
[0] == '.' && (n
[1] == 0 || (n
[1] == '.' && n
[2] == 0)));
236 static int cmd_showport(int argc
, char *const *argv
)
240 int br_index
= get_index(argv
[1], "bridge");
243 struct dirent
**namelist
;
248 char buf
[SYSFS_PATH_MAX
];
249 snprintf(buf
, sizeof(buf
), SYSFS_CLASS_NET
"/%s/brif", argv
[1]);
250 count
= scandir(buf
, &namelist
, not_dot_dotdot
, alphasort
);
253 "Error getting list of all ports of bridge %s\n",
259 for (i
= 0; i
< count
; i
++) {
264 name
= namelist
[i
]->d_name
;
266 int err
= do_showport(br_index
, name
);
272 for (i
= 0; i
< count
; i
++)
280 static int cmd_showportdetail(int argc
, char *const *argv
)
283 return cmd_showport(argc
, argv
);
286 unsigned int getuint(const char *s
)
290 l
= strtoul(s
, &end
, 0);
291 if (*s
== 0 || *end
!= 0 || l
> INT_MAX
) {
292 fprintf(stderr
, "Invalid unsigned int arg %s\n", s
);
298 int getenum(const char *s
, const char *opt
[])
301 for (i
= 0; opt
[i
] != NULL
; i
++)
302 if (strcmp(s
, opt
[i
]) == 0)
305 fprintf(stderr
, "Invalid argument %s: expecting one of ", s
);
306 for (i
= 0; opt
[i
] != NULL
; i
++)
307 fprintf(stderr
, "%s%s", opt
[i
], (opt
[i
+ 1] ? ", " : "\n"));
312 int getyesno(const char *s
, const char *yes
, const char *no
)
314 /* Reverse yes and no so error message looks more normal */
315 const char *opt
[] = { yes
, no
, NULL
};
316 return 1 - getenum(s
, opt
);
319 #define set_bridge_cfg(field, value) \
321 STP_BridgeConfig c; \
322 memset(&c, 0, sizeof(c)); \
324 c.set_ ## field = 1; \
325 int r = CTL_set_bridge_config(br_index, &c); \
327 printf("Couldn't change bridge " #field "\n"); \
331 #define set_port_cfg(field, value) \
334 memset(&c, 0, sizeof(c)); \
336 c.set_ ## field = 1; \
337 int r = CTL_set_port_config(br_index, port_index, &c); \
339 printf("Couldn't change port " #field "\n"); \
345 static int cmd_setbridgeprio(int argc
, char *const *argv
)
348 int br_index
= get_index(argv
[1], "bridge");
349 return set_bridge_cfg(bridge_priority
, getuint(argv
[2]));
352 static int cmd_setbridgemaxage(int argc
, char *const *argv
)
355 int br_index
= get_index(argv
[1], "bridge");
356 return set_bridge_cfg(bridge_max_age
, getuint(argv
[2]));
359 static int cmd_setbridgehello(int argc
, char *const *argv
)
362 int br_index
= get_index(argv
[1], "bridge");
363 return set_bridge_cfg(bridge_hello_time
, getuint(argv
[2]));
366 static int cmd_setbridgefdelay(int argc
, char *const *argv
)
369 int br_index
= get_index(argv
[1], "bridge");
370 return set_bridge_cfg(bridge_forward_delay
, getuint(argv
[2]));
373 static int cmd_setbridgeforcevers(int argc
, char *const *argv
)
376 int br_index
= get_index(argv
[1], "bridge");
377 return set_bridge_cfg(bridge_protocol_version
,
378 2 * getyesno(argv
[2], "normal", "slow"));
381 static int cmd_setbridgetxholdcount(int argc
, char *const *argv
)
384 int br_index
= get_index(argv
[1], "bridge");
385 return set_bridge_cfg(bridge_tx_hold_count
, getuint(argv
[2]));
389 static int cmd_setportprio(int argc
, char *const *argv
)
392 int br_index
= get_index(argv
[1], "bridge");
393 int port_index
= get_index(argv
[2], "port");
394 return set_port_cfg(port_priority
, getuint(argv
[3]));
397 static int cmd_setportpathcost(int argc
, char *const *argv
)
400 int br_index
= get_index(argv
[1], "bridge");
401 int port_index
= get_index(argv
[2], "port");
402 return set_port_cfg(port_pathcost
, getuint(argv
[3]));
405 static int cmd_setportadminedge(int argc
, char *const *argv
)
408 int br_index
= get_index(argv
[1], "bridge");
409 int port_index
= get_index(argv
[2], "port");
410 return set_port_cfg(port_admin_edge
, getyesno(argv
[3], "yes", "no"));
413 static int cmd_setportautoedge(int argc
, char *const *argv
)
416 int br_index
= get_index(argv
[1], "bridge");
417 int port_index
= get_index(argv
[2], "port");
418 return set_port_cfg(port_auto_edge
, getyesno(argv
[3], "yes", "no"));
421 static int cmd_setportp2p(int argc
, char *const *argv
)
424 int br_index
= get_index(argv
[1], "bridge");
425 int port_index
= get_index(argv
[2], "port");
426 const char *opts
[] = { "no", "yes", "auto", NULL
};
427 int vals
[] = { STP_ADMIN_P2P_FORCE_FALSE
, STP_ADMIN_P2P_FORCE_TRUE
,
428 STP_ADMIN_P2P_AUTO
};
430 return set_port_cfg(port_admin_p2p
, vals
[getenum(argv
[3], opts
)]);
433 static int cmd_portmcheck(int argc
, char *const *argv
)
436 int br_index
= get_index(argv
[1], "bridge");
437 int port_index
= get_index(argv
[2], "port");
438 return CTL_port_mcheck(br_index
, port_index
);
441 static int cmd_debuglevel(int argc
, char *const *argv
)
443 return CTL_set_debug_level(getuint(argv
[1]));
446 #define DUMP_FMT(br_name, key) "%-8s %-26s ", br_name, key
448 static int do_dumpbridge(const char *br_name
) {
451 int br_index
= get_index_die(br_name
, "bridge", 0);
455 int r
= CTL_get_bridge_status(br_index
, &s
);
459 /* bridge forward delay */
460 printf(DUMP_FMT(br_name
, "bridge_forward_delay"));
461 printf("%u\n", s
.bridge_forward_delay
);
463 /* bridge hello time */
464 printf(DUMP_FMT(br_name
, "bridge_hello_time"));
465 printf("%u\n", s
.bridge_hello_time
);
468 printf(DUMP_FMT(br_name
, "bridge_max_age"));
469 printf("%u\n", s
.bridge_max_age
);
471 /* designated root */
472 printf(DUMP_FMT(br_name
, "designated_root"));
473 printf(BR_ID_FMT
"\n", BR_ID_ARGS(s
.designated_root
));
476 printf(DUMP_FMT(br_name
, "enabled"));
477 printf("%s\n", BOOL_STR(s
.enabled
));
480 printf(DUMP_FMT(br_name
, "forward_delay"));
481 printf("%u\n", s
.forward_delay
);
484 printf(DUMP_FMT(br_name
, "hello_time"));
485 printf("%u\n", s
.hello_time
);
488 printf(DUMP_FMT(br_name
, "id"));
489 printf(BR_ID_FMT
"\n", BR_ID_ARGS(s
.bridge_id
));
492 printf(DUMP_FMT(br_name
, "max_age"));
493 printf("%u\n", s
.max_age
);
496 printf(DUMP_FMT(br_name
, "root_path_cost"));
497 printf("%u\n", s
.root_path_cost
);
500 printf(DUMP_FMT(br_name
, "root_port"));
501 printf("%u\n", s
.root_port
);
503 /* time since topology change */
504 printf(DUMP_FMT(br_name
, "time_since_topology_change"));
505 printf("%u\n", s
.time_since_topology_change
);
507 /* topology change */
508 printf(DUMP_FMT(br_name
, "topology_change"));
509 printf("%u\n", s
.topology_change
);
511 /* topology change count */
512 printf(DUMP_FMT(br_name
, "topology_change_count"));
513 printf("%u\n", s
.topology_change_count
);
516 printf(DUMP_FMT(br_name
, "tx_hold_count"));
517 printf("%u\n", s
.tx_hold_count
);
522 static int do_dumpbridgeport(int br_index
, const char *pt_name
) {
525 int port_index
= get_index_die(pt_name
, "port", 0);
529 r
= CTL_get_port_status(br_index
, port_index
, &p
);
531 fprintf(stderr
, "Failed to get port state for port %d\n", port_index
);
535 /* admin edge port */
536 printf(DUMP_FMT(pt_name
, "admin_edge_port"));
537 printf("%s\n", BOOL_STR(p
.admin_edge_port
));
539 /* admin point to point */
540 printf(DUMP_FMT(pt_name
, "admin_point_to_point"));
541 printf("%s\n", BOOL_STR(p
.admin_p2p
));
544 printf(DUMP_FMT(pt_name
, "auto_edge_port"));
545 printf("%s\n", BOOL_STR(p
.auto_edge_port
));
548 printf(DUMP_FMT(pt_name
, "admin_path_cost"));
549 printf("%d\n", p
.admin_path_cost
);
551 /* designated bridge */
552 printf(DUMP_FMT(pt_name
, "designated_bridge"));
553 printf(BR_ID_FMT
"\n", BR_ID_ARGS(p
.designated_bridge
));
555 /* designated cost */
556 printf(DUMP_FMT(pt_name
, "designated_cost"));
557 printf("%d\n", p
.designated_cost
);
559 /* designated port */
560 printf(DUMP_FMT(pt_name
, "designated_port"));
561 printf("%0x\n", p
.designated_port
);
563 /* designated root */
564 printf(DUMP_FMT(pt_name
, "designated_root"));
565 printf(BR_ID_FMT
"\n", BR_ID_ARGS(p
.designated_root
));
568 printf(DUMP_FMT(pt_name
, "enabled"));
569 printf("%s\n", BOOL_STR(p
.enabled
));
572 printf(DUMP_FMT(pt_name
, "id"));
573 printf("%u\n", p
.id
& 0xfff);
576 printf(DUMP_FMT(pt_name
, "oper_edge_port"));
577 printf("%s\n", BOOL_STR(p
.oper_edge_port
));
580 printf(DUMP_FMT(pt_name
, "path_cost"));
581 printf("%d\n", p
.path_cost
);
584 printf(DUMP_FMT(pt_name
, "point_to_point"));
585 printf("%s\n", BOOL_STR(p
.oper_p2p
));
588 printf(DUMP_FMT(pt_name
, "state"));
589 printf(STATE_STR(p
.state
));
592 /* topology change ack */
593 printf(DUMP_FMT(pt_name
, "topology_change_ack"));
594 printf("%s\n", BOOL_STR(p
.tc_ack
));
599 static int cmd_dumpbridge(int argc
, char *const *argv
)
603 struct dirent
**namelist
;
609 scandir(SYSFS_CLASS_NET
, &namelist
, isbridge
, alphasort
);
611 fprintf(stderr
, "Error getting list of all bridges\n");
616 for (i
= 0; i
< count
; i
++) {
621 name
= namelist
[i
]->d_name
;
623 int err
= do_dumpbridge(name
);
629 for (i
= 0; i
< count
; i
++)
637 static int cmd_dumpbridgeports(int argc
, char *const *argv
)
641 int br_index
= get_index(argv
[1], "bridge");
644 struct dirent
**namelist
;
649 char buf
[SYSFS_PATH_MAX
];
650 snprintf(buf
, sizeof(buf
), SYSFS_CLASS_NET
"/%s/brif", argv
[1]);
651 count
= scandir(buf
, &namelist
, not_dot_dotdot
, alphasort
);
654 "Error getting list of all ports of bridge %s\n",
660 for (i
= 0; i
< count
; i
++) {
662 name
= namelist
[i
]->d_name
;
664 int err
= do_dumpbridgeport(br_index
, name
);
669 for (i
= 0; i
< count
; i
++)
680 int (*func
) (int argc
, char *const *argv
);
684 static const struct command commands
[] = {
685 {0, 32, "showbridge", cmd_showbridge
,
686 "[<bridge> ... ]\t\tshow bridge state"},
687 {1, 32, "showport", cmd_showport
,
688 "<bridge> [<port> ... ]\tshow port state"},
689 {1, 32, "showportdetail", cmd_showportdetail
,
690 "<bridge> [<port> ... ]\tshow port state (detail)"},
691 {2, 0, "setbridgeprio", cmd_setbridgeprio
,
692 "<bridge> <priority>\tset bridge priority (0-61440)"},
693 {2, 0, "sethello", cmd_setbridgehello
,
694 "<bridge> <hellotime>\tset bridge hello time (1-10)"},
695 {2, 0, "setmaxage", cmd_setbridgemaxage
,
696 "<bridge> <maxage>\tset bridge max age (6-40)"},
697 {2, 0, "setfdelay", cmd_setbridgefdelay
,
698 "<bridge> <fwd_delay>\tset bridge forward delay (4-30)"},
699 {2, 0, "setforcevers", cmd_setbridgeforcevers
,
700 "<bridge> {normal|slow}\tnormal RSTP or force to STP"},
701 {2, 0, "settxholdcount", cmd_setbridgetxholdcount
,
702 "<bridge> <tx_hold_count>\tset bridge transmit hold count (1-10)"},
703 {3, 0, "setportprio", cmd_setportprio
,
704 "<bridge> <port> <priority>\tset port priority (0-240)"},
705 {3, 0, "setportpathcost", cmd_setportpathcost
,
706 "<bridge> <port> <cost>\tset port path cost"},
707 {3, 0, "setportadminedge", cmd_setportadminedge
,
708 "<bridge> <port> {yes|no}\tconfigure if it is an admin edge port"},
709 {3, 0, "setportautoedge", cmd_setportautoedge
,
710 "<bridge> <port> {yes|no}\tconfigure if it is an auto edge port"},
711 {3, 0, "setportp2p", cmd_setportp2p
,
712 "<bridge> <port> {yes|no|auto}\tset whether p2p connection"},
713 {2, 0, "portmcheck", cmd_portmcheck
,
714 "<bridge> <port>\ttry to get back from STP to RSTP mode"},
715 {1, 0, "debuglevel", cmd_debuglevel
, "<level>\t\tLevel of verbosity"},
716 {0, 32, "dumpbridge", cmd_dumpbridge
,
717 "Dump all information about a bridge in machine parseable format"},
718 {1, 0, "dumpports", cmd_dumpbridgeports
,
719 "Dump all port information in machine parseable format"},
722 const struct command
*command_lookup(const char *cmd
)
726 for (i
= 0; i
< sizeof(commands
) / sizeof(commands
[0]); i
++) {
727 if (!strcmp(cmd
, commands
[i
].name
))
734 void command_helpall(void)
738 for (i
= 0; i
< sizeof(commands
) / sizeof(commands
[0]); i
++) {
739 printf("\t%-10s\t%s\n", commands
[i
].name
, commands
[i
].help
);
745 printf("Usage: rstpctl [commands]\n");
746 printf("commands:\n");
750 #define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b
751 #define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
753 int main(int argc
, char *const *argv
)
755 const struct command
*cmd
;
757 static const struct option options
[] = {
758 {.name
= "help",.val
= 'h'},
759 {.name
= "version",.val
= 'V'},
763 while ((f
= getopt_long(argc
, argv
, "Vh", options
, NULL
)) != EOF
)
769 printf("%s\n", PACKAGE_VERSION(VERSION
, BUILD
));
772 fprintf(stderr
, "Unknown option '%c'\n", f
);
779 if (ctl_client_init()) {
780 fprintf(stderr
, "can't setup control connection\n");
786 if ((cmd
= command_lookup(argv
[0])) == NULL
) {
787 fprintf(stderr
, "never heard of command [%s]\n", argv
[0]);
791 if (argc
< cmd
->nargs
+ 1 || argc
> cmd
->nargs
+ cmd
->optargs
+ 1) {
792 printf("Incorrect number of arguments for command\n");
793 printf("Usage: rstpctl %s %s\n", cmd
->name
, cmd
->help
);
797 return cmd
->func(argc
, argv
);