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"
29 #define STP_IN_get_error_explanation CTL_error_explanation
31 static void print_bridge_id(UID_BRIDGE_ID_T
* bridge_id
, unsigned char cr
)
33 printf("%04lX-%02x%02x%02x%02x%02x%02x",
34 (unsigned long)bridge_id
->prio
,
35 (unsigned char)bridge_id
->addr
[0],
36 (unsigned char)bridge_id
->addr
[1],
37 (unsigned char)bridge_id
->addr
[2],
38 (unsigned char)bridge_id
->addr
[3],
39 (unsigned char)bridge_id
->addr
[4],
40 (unsigned char)bridge_id
->addr
[5]);
45 static char *stp_state2str(RSTP_PORT_STATE stp_port_state
, int detail
)
48 switch (stp_port_state
) {
49 case UID_PORT_DISABLED
:
51 case UID_PORT_DISCARDING
:
53 case UID_PORT_LEARNING
:
55 case UID_PORT_FORWARDING
:
57 case UID_PORT_NON_STP
:
64 switch (stp_port_state
) {
65 case UID_PORT_DISABLED
:
67 case UID_PORT_DISCARDING
:
69 case UID_PORT_LEARNING
:
71 case UID_PORT_FORWARDING
:
73 case UID_PORT_NON_STP
:
80 static void CLI_out_port_id(int port
, unsigned char cr
)
82 static char ifname
[IFNAMSIZ
];
83 if (if_indextoname(port
, ifname
))
86 printf("Ifindex %02d", port
);
91 int get_index_die(const char *ifname
, const char *doc
, int die
)
93 int r
= if_nametoindex(ifname
);
96 "Can't find index for %s %s. Not a valid interface.\n",
105 int get_index(const char *ifname
, const char *doc
)
107 return get_index_die(ifname
, doc
, 1);
110 static int cmd_rstp(int argc
, char *const *argv
)
113 int br_index
= get_index(argv
[1], "bridge");
115 if (!strcmp(argv
[2], "on") || !strcmp(argv
[2], "yes")
116 || !strcmp(argv
[2], "1"))
118 else if (!strcmp(argv
[2], "off") || !strcmp(argv
[2], "no")
119 || !strcmp(argv
[2], "0"))
122 fprintf(stderr
, "expect on/off for argument\n");
125 r
= CTL_enable_bridge_rstp(br_index
, stp
);
127 fprintf(stderr
, "Failed to enable/disable RSTP: %s\n",
128 CTL_error_explanation(r
));
134 static int do_showbridge(const char *br_name
)
136 UID_STP_STATE_T uid_state
;
137 UID_STP_CFG_T uid_cfg
;
139 int br_index
= get_index_die(br_name
, "bridge", 0);
143 int r
= CTL_get_bridge_state(br_index
, &uid_cfg
, &uid_state
);
145 fprintf(stderr
, "Failed to get bridge state: %s\n",
146 CTL_error_explanation(r
));
150 printf("Interface: %-7s (tag:%d) State: ",
151 uid_state
.vlan_name
, (int)uid_state
.vlan_id
);
153 printf("Bridge: %-7s State:",
154 uid_state
.vlan_name
);
156 switch (uid_state
.stp_enabled
) {
161 printf("disabled\n");
168 printf("BridgeId: ");
169 print_bridge_id(&uid_state
.bridge_id
, 0);
170 printf(" Bridge Proirity: %lu (0x%lX)\n",
171 (unsigned long)uid_state
.bridge_id
.prio
,
172 (unsigned long)uid_state
.bridge_id
.prio
);
173 if (uid_cfg
.force_version
< 2)
174 printf("Force Version: stp\n");
176 printf("Designated Root: ");
177 print_bridge_id(&uid_state
.designated_root
, 1);
178 if (uid_state
.root_port
) {
179 printf("Root Port: %04lx",
180 (unsigned long)uid_state
.root_port
);
181 // CLI_out_port_id (uid_state.root_port & 0xfff, False);
182 // printf("not implemented"); // XXX
183 printf(", Root Cost: %-lu\n",
184 (unsigned long)uid_state
.root_path_cost
);
186 printf("Root Port: none\n");
189 if (uid_state
.Topo_Change
)
190 printf("Topology Change Count: %lu\n",
191 uid_state
.Topo_Change_Count
);
193 printf("Time Since Topology Change: %lu\n",
194 uid_state
.timeSince_Topo_Change
);
196 printf("Max Age: %2d Bridge Max Age: %-2d\n",
197 (int)uid_state
.max_age
, (int)uid_cfg
.max_age
);
198 printf("Hello Time: %2d Bridge Hello Time: %-2d\n",
199 (int)uid_state
.hello_time
, (int)uid_cfg
.hello_time
);
200 printf("Forward Delay: %2d Bridge Forward Delay: %-2d\n",
201 (int)uid_state
.forward_delay
, (int)uid_cfg
.forward_delay
);
202 printf("Hold Time: %2d\n", (int)uid_cfg
.hold_time
);
207 #define SYSFS_PATH_MAX 256
208 #define SYSFS_CLASS_NET "/sys/class/net"
210 static int isbridge(const struct dirent
*entry
)
212 char path
[SYSFS_PATH_MAX
];
215 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"/%s/bridge",
217 return stat(path
, &st
) == 0 && S_ISDIR(st
.st_mode
);
220 static int cmd_showbridge(int argc
, char *const *argv
)
224 struct dirent
**namelist
;
230 scandir(SYSFS_CLASS_NET
, &namelist
, isbridge
, alphasort
);
232 fprintf(stderr
, "Error getting list of all bridges\n");
237 for (i
= 0; i
< count
; i
++) {
242 name
= namelist
[i
]->d_name
;
244 int err
= do_showbridge(name
);
250 for (i
= 0; i
< count
; i
++)
260 static int do_showport(int br_index
, const char *port_name
,
261 UID_STP_STATE_T
* uid_state
)
263 UID_STP_PORT_STATE_T uid_port
;
264 UID_STP_PORT_CFG_T uid_cfg
;
266 int port_index
= get_index_die(port_name
, "port", 0);
270 memset(&uid_cfg
, 0, sizeof(UID_STP_PORT_CFG_T
));
271 r
= CTL_get_port_state(br_index
, port_index
, &uid_cfg
, &uid_port
);
273 fprintf(stderr
, "Failed to get port state for port %d: %s\n",
274 port_index
, CTL_error_explanation(r
));
280 CLI_out_port_id(port_index
, False
);
282 printf(": PortId: %04lx in vlan '%s' with tag %d:\n",
283 (unsigned long)uid_port
.port_id
, uid_state
->vlan_name
,
284 (int)uid_state
->vlan_id
);
286 printf(": PortId: %04lx in Bridge '%s':\n",
287 (unsigned long)uid_port
.port_id
, uid_state
->vlan_name
);
289 printf("Priority: %-d\n",
290 (int)(uid_port
.port_id
>> 8));
291 printf("State: %-16s",
292 stp_state2str(uid_port
.state
, 1));
293 printf(" Uptime: %-9lu\n", uid_port
.uptime
);
294 printf("PortPathCost: admin: ");
295 if (ADMIN_PORT_PATH_COST_AUTO
== uid_cfg
.admin_port_path_cost
)
296 printf("%-9s", "Auto");
298 printf("%-9lu", uid_cfg
.admin_port_path_cost
);
299 printf(" oper: %-9lu\n", uid_port
.oper_port_path_cost
);
301 printf("Point2Point: admin: ");
302 switch (uid_cfg
.admin_point2point
) {
304 printf("%-9s", "ForceYes");
306 case P2P_FORCE_FALSE
:
307 printf("%-9s", "ForceNo");
310 printf("%-9s", "Auto");
313 printf(" oper: %-9s\n",
314 uid_port
.oper_point2point
? "Yes" : "No");
315 printf("Edge: admin: %-9s oper: %-9s\n",
316 uid_cfg
.admin_edge
? "Y" : "N",
317 uid_port
.oper_edge
? "Y" : "N");
318 printf("Partner: oper: %-9s\n",
319 uid_port
.oper_stp_neigb
? "Slow" : "Rapid");
321 if (' ' != uid_port
.role
) {
322 if ('-' != uid_port
.role
) {
323 printf("PathCost: %-lu\n",
324 (unsigned long)(uid_port
.path_cost
));
325 printf("Designated Root: ");
326 print_bridge_id(&uid_port
.designated_root
, 1);
327 printf("Designated Cost: %-ld\n",
328 (unsigned long)uid_port
.designated_cost
);
329 printf("Designated Bridge: ");
330 print_bridge_id(&uid_port
.designated_bridge
, 1);
331 printf("Designated Port: %-4lx\n\r",
332 (unsigned long)uid_port
.designated_port
);
335 switch (uid_port
.role
) {
337 printf("Alternate\n");
346 printf("Designated\n");
352 printf("Unknown(%c)\n", uid_port
.role
);
356 if ('R' == uid_port
.role
|| 'D' == uid_port
.role
) {
357 /* printf("Tc: %c ", uid_port.tc ? 'Y' : 'n'); */
359 uid_port
.top_change_ack
? 'Y' : 'N');
360 printf("TcWhile: %3d\n",
361 (int)uid_port
.tcWhile
);
365 if (UID_PORT_DISABLED
== uid_port
.state
|| '-' == uid_port
.role
) {
367 printf("helloWhen: %3d ",
368 (int)uid_port
.helloWhen
);
369 printf("lnkWhile: %3d\n", (int)uid_port
.lnkWhile
);
370 printf("fdWhile: %3d\n", (int)uid_port
.fdWhile
);
372 } else if ('-' != uid_port
.role
) {
373 printf("fdWhile: %3d ", (int)uid_port
.fdWhile
);
374 printf("rcvdInfoWhile: %3d\n",
375 (int)uid_port
.rcvdInfoWhile
);
376 printf("rbWhile: %3d ", (int)uid_port
.rbWhile
);
377 printf("rrWhile: %3d\n", (int)uid_port
.rrWhile
);
379 printf("mdelayWhile: %3d ",
380 (int)uid_port
.mdelayWhile
);
381 printf("lnkWhile: %3d\n", (int)uid_port
.lnkWhile
);
382 printf("helloWhen: %3d ",
383 (int)uid_port
.helloWhen
);
384 printf("txCount: %3d\n", (int)uid_port
.txCount
);
388 printf("RSTP BPDU rx: %lu\n",
389 (unsigned long)uid_port
.rx_rstp_bpdu_cnt
);
390 printf("CONFIG BPDU rx: %lu\n",
391 (unsigned long)uid_port
.rx_cfg_bpdu_cnt
);
392 printf("TCN BPDU rx: %lu\n",
393 (unsigned long)uid_port
.rx_tcn_bpdu_cnt
);
396 (uid_port
.oper_point2point
) ? ' ' : '*',
397 (uid_port
.oper_edge
) ? 'E' : ' ',
398 (uid_port
.oper_stp_neigb
) ? 's' : ' ');
399 CLI_out_port_id(port_index
, False
);
400 printf(" %04lx %3s ", (unsigned long)uid_port
.port_id
,
401 stp_state2str(uid_port
.state
, 0));
403 print_bridge_id(&uid_port
.designated_root
, 0);
405 print_bridge_id(&uid_port
.designated_bridge
, 0);
406 printf(" %4lx %c", (unsigned long)uid_port
.designated_port
,
413 static int not_dot_dotdot(const struct dirent
*entry
)
415 const char *n
= entry
->d_name
;
417 return !(n
[0] == '.' && (n
[1] == 0 || (n
[1] == '.' && n
[2] == 0)));
420 static int cmd_showport(int argc
, char *const *argv
)
422 UID_STP_STATE_T uid_state
;
423 UID_STP_CFG_T uid_br_cfg
;
426 int br_index
= get_index(argv
[1], "bridge");
428 r
= CTL_get_bridge_state(br_index
, &uid_br_cfg
, &uid_state
);
430 fprintf(stderr
, "Failed to get bridge state: %s\n",
431 CTL_error_explanation(r
));
436 struct dirent
**namelist
;
441 char buf
[SYSFS_PATH_MAX
];
442 snprintf(buf
, sizeof(buf
), SYSFS_CLASS_NET
"/%s/brif", argv
[1]);
443 count
= scandir(buf
, &namelist
, not_dot_dotdot
, alphasort
);
446 "Error getting list of all ports of bridge %s\n",
452 for (i
= 0; i
< count
; i
++) {
457 name
= namelist
[i
]->d_name
;
459 int err
= do_showport(br_index
, name
, &uid_state
);
465 for (i
= 0; i
< count
; i
++)
473 static int cmd_showportdetail(int argc
, char *const *argv
)
476 return cmd_showport(argc
, argv
);
479 unsigned int getuint(const char *s
)
483 l
= strtoul(s
, &end
, 0);
484 if (*s
== 0 || *end
!= 0 || l
> INT_MAX
) {
485 fprintf(stderr
, "Invalid unsigned int arg %s\n", s
);
491 int getenum(const char *s
, const char *opt
[])
494 for (i
= 0; opt
[i
] != NULL
; i
++)
495 if (strcmp(s
, opt
[i
]) == 0)
498 fprintf(stderr
, "Invalid argument %s: expecting one of ", s
);
499 for (i
= 0; opt
[i
] != NULL
; i
++)
500 fprintf(stderr
, "%s%s", opt
[i
], (opt
[i
+ 1] ? ", " : "\n"));
505 int getyesno(const char *s
, const char *yes
, const char *no
)
507 /* Reverse yes and no so error message looks more normal */
508 const char *opt
[] = { yes
, no
, NULL
};
509 return 1 - getenum(s
, opt
);
512 static int set_bridge_cfg_value(int br_index
, unsigned long value
,
513 unsigned long val_mask
)
515 UID_STP_CFG_T uid_cfg
;
519 uid_cfg
.field_mask
= val_mask
;
522 uid_cfg
.stp_enabled
= value
;
526 uid_cfg
.bridge_priority
= value
;
527 val_name
= "priority";
530 uid_cfg
.max_age
= value
;
531 val_name
= "max_age";
534 uid_cfg
.hello_time
= value
;
535 val_name
= "hello_time";
538 uid_cfg
.forward_delay
= value
;
539 val_name
= "forward_delay";
541 case BR_CFG_FORCE_VER
:
542 uid_cfg
.force_version
= value
;
543 val_name
= "force_version";
545 case BR_CFG_AGE_MODE
:
546 case BR_CFG_AGE_TIME
:
548 printf("Invalid value mask 0X%lx\n", val_mask
);
553 rc
= CTL_set_bridge_config(br_index
, &uid_cfg
);
556 printf("Can't change rstp bridge %s:%s\n", val_name
,
557 STP_IN_get_error_explanation(rc
));
563 static int cmd_setbridgestate(int argc
, char *const *argv
)
566 int br_index
= get_index(argv
[1], "bridge");
567 return set_bridge_cfg_value(br_index
,
568 getyesno(argv
[2], "on", "off"),
572 static int cmd_setbridgeprio(int argc
, char *const *argv
)
575 int br_index
= get_index(argv
[1], "bridge");
576 return set_bridge_cfg_value(br_index
, getuint(argv
[2]), BR_CFG_PRIO
);
579 static int cmd_setbridgemaxage(int argc
, char *const *argv
)
582 int br_index
= get_index(argv
[1], "bridge");
583 return set_bridge_cfg_value(br_index
, getuint(argv
[2]), BR_CFG_AGE
);
586 static int cmd_setbridgehello(int argc
, char *const *argv
)
589 int br_index
= get_index(argv
[1], "bridge");
590 return set_bridge_cfg_value(br_index
, getuint(argv
[2]), BR_CFG_HELLO
);
593 static int cmd_setbridgefdelay(int argc
, char *const *argv
)
596 int br_index
= get_index(argv
[1], "bridge");
597 return set_bridge_cfg_value(br_index
, getuint(argv
[2]), BR_CFG_DELAY
);
600 static int cmd_setbridgeforcevers(int argc
, char *const *argv
)
603 int br_index
= get_index(argv
[1], "bridge");
604 return set_bridge_cfg_value(br_index
,
605 2 * getyesno(argv
[2], "normal", "slow"),
610 set_port_cfg_value(int br_index
, int port_index
,
611 unsigned long value
, unsigned long val_mask
)
613 UID_STP_PORT_CFG_T uid_cfg
;
617 BitmapClear(&uid_cfg
.port_bmp
);
618 uid_cfg
.field_mask
= val_mask
;
624 uid_cfg
.admin_port_path_cost
= value
;
625 val_name
= "path cost";
628 uid_cfg
.port_priority
= value
;
629 val_name
= "priority";
632 uid_cfg
.admin_point2point
= (ADMIN_P2P_T
) value
;
633 val_name
= "p2p flag";
636 uid_cfg
.admin_edge
= value
;
637 val_name
= "adminEdge";
640 uid_cfg
.admin_non_stp
= value
;
641 val_name
= "adminNonStp";
644 case PT_CFG_DBG_SKIP_TX
:
645 uid_cfg
.skip_tx
= value
;
646 val_name
= "skip tx";
648 case PT_CFG_DBG_SKIP_RX
:
649 uid_cfg
.skip_rx
= value
;
650 val_name
= "skip rx";
655 printf("Invalid value mask 0X%lx\n", val_mask
);
659 rc
= CTL_set_port_config(br_index
, port_index
, &uid_cfg
);
662 printf("can't change rstp port[s] %s: %s\n",
663 val_name
, STP_IN_get_error_explanation(rc
));
669 static int cmd_setportprio(int argc
, char *const *argv
)
672 int br_index
= get_index(argv
[1], "bridge");
673 int port_index
= get_index(argv
[2], "port");
674 return set_port_cfg_value(br_index
, port_index
,
675 getuint(argv
[3]), PT_CFG_PRIO
);
678 static int cmd_setportpathcost(int argc
, char *const *argv
)
681 int br_index
= get_index(argv
[1], "bridge");
682 int port_index
= get_index(argv
[2], "port");
683 return set_port_cfg_value(br_index
, port_index
,
684 getuint(argv
[3]), PT_CFG_COST
);
687 static int cmd_setportedge(int argc
, char *const *argv
)
690 int br_index
= get_index(argv
[1], "bridge");
691 int port_index
= get_index(argv
[2], "port");
692 return set_port_cfg_value(br_index
, port_index
,
693 getyesno(argv
[3], "yes", "no"), PT_CFG_EDGE
);
696 static int cmd_setportnonstp(int argc
, char *const *argv
)
699 int br_index
= get_index(argv
[1], "bridge");
700 int port_index
= get_index(argv
[2], "port");
701 return set_port_cfg_value(br_index
, port_index
,
702 getyesno(argv
[3], "yes", "no"),
706 static int cmd_setportp2p(int argc
, char *const *argv
)
709 int br_index
= get_index(argv
[1], "bridge");
710 int port_index
= get_index(argv
[2], "port");
711 const char *opts
[] = { "yes", "no", "auto", NULL
};
712 int vals
[] = { P2P_FORCE_TRUE
, P2P_FORCE_FALSE
, P2P_AUTO
};
714 return set_port_cfg_value(br_index
, port_index
,
715 vals
[getenum(argv
[3], opts
)], PT_CFG_P2P
);
718 static int cmd_portmcheck(int argc
, char *const *argv
)
721 int br_index
= get_index(argv
[1], "bridge");
722 int port_index
= get_index(argv
[2], "port");
723 return set_port_cfg_value(br_index
, port_index
, 0, PT_CFG_MCHECK
);
726 static int cmd_debuglevel(int argc
, char *const *argv
)
728 return CTL_set_debug_level(getuint(argv
[1]));
735 int (*func
) (int argc
, char *const *argv
);
739 static const struct command commands
[] = {
740 {0, 32, "showbridge", cmd_showbridge
,
741 "[<bridge> ... ]\t\tshow bridge state"},
742 {1, 32, "showport", cmd_showport
,
743 "<bridge> [<port> ... ]\tshow port state"},
744 {1, 32, "showportdetail", cmd_showportdetail
,
745 "<bridge> [<port> ... ]\tshow port state (detail)"},
746 {2, 0, "rstp", cmd_rstp
,
747 "<bridge> {on|off}\tenable/disable rstpd control"},
748 {2, 0, "setbridgestate", cmd_setbridgestate
,
749 "<bridge> {on|off}\tstart/stop rstp (when enabled)"},
750 {2, 0, "setbridgeprio", cmd_setbridgeprio
,
751 "<bridge> <priority>\tset bridge priority (0-61440)"},
752 {2, 0, "sethello", cmd_setbridgehello
,
753 "<bridge> <hellotime>\tset bridge hello time (1-10)"},
754 {2, 0, "setmaxage", cmd_setbridgemaxage
,
755 "<bridge> <maxage>\tset bridge max age (6-40)"},
756 {2, 0, "setfdelay", cmd_setbridgefdelay
,
757 "<bridge> <fwd_delay>\tset bridge forward delay (4-30)"},
758 {2, 0, "setforcevers", cmd_setbridgeforcevers
,
759 "<bridge> {normal|slow}\tnormal RSTP or force to STP"},
760 {3, 0, "setportprio", cmd_setportprio
,
761 "<bridge> <port> <priority>\tset port priority (0-240)"},
762 {3, 0, "setportpathcost", cmd_setportpathcost
,
763 "<bridge> <port> <cost>\tset port path cost"},
764 {3, 0, "setportedge", cmd_setportedge
,
765 "<bridge> <port> {yes|no}\tconfigure if it is an edge port"},
766 {3, 0, "setportnonstp", cmd_setportnonstp
,
767 "<bridge> <port> {yes|no}\tdisable STP for the port"},
768 {3, 0, "setportp2p", cmd_setportp2p
,
769 "<bridge> <port> {yes|no|auto}\tset whether p2p connection"},
770 {2, 0, "portmcheck", cmd_portmcheck
,
771 "<bridge> <port>\ttry to get back from STP to RSTP mode"},
772 {1, 0, "debuglevel", cmd_debuglevel
, "<level>\t\tLevel of verbosity"},
775 const struct command
*command_lookup(const char *cmd
)
779 for (i
= 0; i
< sizeof(commands
) / sizeof(commands
[0]); i
++) {
780 if (!strcmp(cmd
, commands
[i
].name
))
787 void command_helpall(void)
791 for (i
= 0; i
< sizeof(commands
) / sizeof(commands
[0]); i
++) {
792 printf("\t%-10s\t%s\n", commands
[i
].name
, commands
[i
].help
);
798 printf("Usage: rstpctl [commands]\n");
799 printf("commands:\n");
803 #define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b
804 #define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
806 int main(int argc
, char *const *argv
)
808 const struct command
*cmd
;
810 static const struct option options
[] = {
811 {.name
= "help",.val
= 'h'},
812 {.name
= "version",.val
= 'V'},
816 while ((f
= getopt_long(argc
, argv
, "Vh", options
, NULL
)) != EOF
)
822 printf("%s\n", PACKAGE_VERSION(VERSION
, BUILD
));
825 fprintf(stderr
, "Unknown option '%c'\n", f
);
832 if (ctl_client_init()) {
833 fprintf(stderr
, "can't setup control connection\n");
839 if ((cmd
= command_lookup(argv
[0])) == NULL
) {
840 fprintf(stderr
, "never heard of command [%s]\n", argv
[0]);
844 if (argc
< cmd
->nargs
+ 1 || argc
> cmd
->nargs
+ cmd
->optargs
+ 1) {
845 printf("Incorrect number of arguments for command\n");
846 printf("Usage: rstpctl %s %s\n", cmd
->name
, cmd
->help
);
850 return cmd
->func(argc
, argv
);