]> git.ipfire.org Git - people/ms/rstp.git/blame - ctl_main.c
Merge remote-tracking branch 'upstream/master'
[people/ms/rstp.git] / ctl_main.c
CommitLineData
ad02a0eb
SH
1/*
2 Command parsing taken from brctl utility.
3 Display code from stp_cli.c in rstplib.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/errno.h>
10#include <getopt.h>
11
12#include <net/if.h>
13
14/* For scanning through sysfs directories */
15#include <dirent.h>
16#include <sys/types.h>
17#include <sys/stat.h>
18#include <unistd.h>
c768cf44 19#include <limits.h>
ad02a0eb
SH
20
21#include "ctl_socket_client.h"
22#include "ctl_functions.h"
23
ad02a0eb
SH
24int get_index_die(const char *ifname, const char *doc, int die)
25{
11904a35
SH
26 int r = if_nametoindex(ifname);
27 if (r == 0) {
28 fprintf(stderr,
29 "Can't find index for %s %s. Not a valid interface.\n",
30 doc, ifname);
31 if (die)
32 exit(1);
33 return -1;
34 }
35 return r;
ad02a0eb
SH
36}
37
38int get_index(const char *ifname, const char *doc)
39{
11904a35 40 return get_index_die(ifname, doc, 1);
ad02a0eb
SH
41}
42
b600a2c3
SA
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]
ad02a0eb 45
b600a2c3 46#define BOOL_STR(x) ((x) ? "yes" : "no")
ad02a0eb
SH
47
48static int do_showbridge(const char *br_name)
49{
b600a2c3 50 STP_BridgeStatus s;
11904a35
SH
51
52 int br_index = get_index_die(br_name, "bridge", 0);
53 if (br_index < 0)
54 return -1;
55
b600a2c3 56 int r = CTL_get_bridge_status(br_index, &s);
11904a35 57 if (r) {
11904a35
SH
58 return -1;
59 }
b600a2c3
SA
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));
11904a35
SH
81
82 return 0;
ad02a0eb
SH
83}
84
85#define SYSFS_PATH_MAX 256
86#define SYSFS_CLASS_NET "/sys/class/net"
87
88static int isbridge(const struct dirent *entry)
89{
11904a35
SH
90 char path[SYSFS_PATH_MAX];
91 struct stat st;
92
93 snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge",
94 entry->d_name);
95 return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
96}
97
98static int cmd_showbridge(int argc, char *const *argv)
99{
100 int i, count = 0;
101 int r = 0;
102 struct dirent **namelist;
103
104 if (argc > 1) {
105 count = argc - 1;
106 } else {
107 count =
108 scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort);
109 if (count < 0) {
110 fprintf(stderr, "Error getting list of all bridges\n");
111 return -1;
112 }
113 }
114
115 for (i = 0; i < count; i++) {
116 const char *name;
117 if (argc > 1)
118 name = argv[i + 1];
119 else
120 name = namelist[i]->d_name;
121
122 int err = do_showbridge(name);
123 if (err)
124 r = err;
125 }
126
127 if (argc <= 1) {
128 for (i = 0; i < count; i++)
129 free(namelist[i]);
130 free(namelist);
131 }
132
133 return r;
ad02a0eb
SH
134}
135
b600a2c3
SA
136
137#define STATE_STR(_state) \
138 ({ \
139 int _s = _state; \
140 char *_str = "unknown"; \
141 switch (_s) \
142 { \
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; \
146 } \
147 _str; \
148 })
149
150#define SHORT_STATE_STR(_state) \
151 ({ \
152 int _s = _state; \
153 char *_str = "unkn"; \
154 switch (_s) \
155 { \
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; \
159 } \
160 _str; \
161 })
162
163#define ADMIN_P2P_STR(_state) \
164 ({ \
165 int _s = _state; \
166 char *_str = "unkn"; \
167 switch (_s) \
168 { \
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; \
172 } \
173 _str; \
174 })
175
176
ad02a0eb
SH
177int detail = 0;
178
b600a2c3 179static int do_showport(int br_index, const char *port_name)
11904a35 180{
b600a2c3 181 STP_PortStatus s;
11904a35
SH
182 int r = 0;
183 int port_index = get_index_die(port_name, "port", 0);
184 if (port_index < 0)
185 return -1;
186
b600a2c3 187 r = CTL_get_port_status(br_index, port_index, &s);
11904a35 188 if (r) {
b600a2c3
SA
189 fprintf(stderr, "Failed to get port state for port %d\n",
190 port_index);
11904a35
SH
191 return -1;
192 }
193
194 if (detail) {
b600a2c3
SA
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));
11904a35 215 } else {
b600a2c3
SA
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' : ' ',
219 port_name,
220 s.id,
221 s.enabled?SHORT_STATE_STR(s.state):"down",
222 BR_ID_ARGS(s.designated_root),
223 BR_ID_ARGS(s.designated_bridge),
224 s.designated_port);
11904a35
SH
225 }
226 return 0;
ad02a0eb
SH
227}
228
229static int not_dot_dotdot(const struct dirent *entry)
230{
11904a35
SH
231 const char *n = entry->d_name;
232
233 return !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0)));
ad02a0eb
SH
234}
235
11904a35 236static int cmd_showport(int argc, char *const *argv)
ad02a0eb 237{
11904a35
SH
238 int r = 0;
239
240 int br_index = get_index(argv[1], "bridge");
241
11904a35
SH
242 int i, count = 0;
243 struct dirent **namelist;
244
245 if (argc > 2) {
246 count = argc - 2;
247 } else {
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);
251 if (count < 0) {
252 fprintf(stderr,
253 "Error getting list of all ports of bridge %s\n",
254 argv[1]);
255 return -1;
256 }
257 }
258
259 for (i = 0; i < count; i++) {
260 const char *name;
261 if (argc > 2)
262 name = argv[i + 2];
263 else
264 name = namelist[i]->d_name;
265
b600a2c3 266 int err = do_showport(br_index, name);
11904a35
SH
267 if (err)
268 r = err;
269 }
270
271 if (argc <= 2) {
272 for (i = 0; i < count; i++)
273 free(namelist[i]);
274 free(namelist);
275 }
276
277 return r;
ad02a0eb
SH
278}
279
11904a35 280static int cmd_showportdetail(int argc, char *const *argv)
ad02a0eb 281{
11904a35
SH
282 detail = 1;
283 return cmd_showport(argc, argv);
ad02a0eb
SH
284}
285
11904a35 286unsigned int getuint(const char *s)
ad02a0eb 287{
11904a35
SH
288 char *end;
289 long l;
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);
293 exit(1);
294 }
295 return l;
ad02a0eb
SH
296}
297
11904a35 298int getenum(const char *s, const char *opt[])
ad02a0eb 299{
11904a35
SH
300 int i;
301 for (i = 0; opt[i] != NULL; i++)
302 if (strcmp(s, opt[i]) == 0)
303 return i;
304
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"));
308
309 exit(1);
310}
311
312int getyesno(const char *s, const char *yes, const char *no)
313{
314 /* Reverse yes and no so error message looks more normal */
315 const char *opt[] = { yes, no, NULL };
316 return 1 - getenum(s, opt);
317}
318
b600a2c3
SA
319#define set_bridge_cfg(field, value) \
320({ \
321 STP_BridgeConfig c; \
322 memset(&c, 0, sizeof(c)); \
323 c.field = value; \
324 c.set_ ## field = 1; \
325 int r = CTL_set_bridge_config(br_index, &c); \
326 if (r) \
327 printf("Couldn't change bridge " #field "\n"); \
328 r; \
329})
330
331#define set_port_cfg(field, value) \
332({ \
333 STP_PortConfig c; \
334 memset(&c, 0, sizeof(c)); \
335 c.field = value; \
336 c.set_ ## field = 1; \
337 int r = CTL_set_port_config(br_index, port_index, &c); \
338 if (r) \
339 printf("Couldn't change port " #field "\n"); \
340 r; \
341})
342
ad02a0eb 343
ad02a0eb 344
11904a35 345static int cmd_setbridgeprio(int argc, char *const *argv)
ad02a0eb
SH
346{
347
11904a35 348 int br_index = get_index(argv[1], "bridge");
b600a2c3 349 return set_bridge_cfg(bridge_priority, getuint(argv[2]));
ad02a0eb
SH
350}
351
11904a35 352static int cmd_setbridgemaxage(int argc, char *const *argv)
ad02a0eb
SH
353{
354
11904a35 355 int br_index = get_index(argv[1], "bridge");
b600a2c3 356 return set_bridge_cfg(bridge_max_age, getuint(argv[2]));
ad02a0eb
SH
357}
358
11904a35 359static int cmd_setbridgehello(int argc, char *const *argv)
ad02a0eb
SH
360{
361
11904a35 362 int br_index = get_index(argv[1], "bridge");
b600a2c3 363 return set_bridge_cfg(bridge_hello_time, getuint(argv[2]));
ad02a0eb
SH
364}
365
11904a35 366static int cmd_setbridgefdelay(int argc, char *const *argv)
ad02a0eb
SH
367{
368
11904a35 369 int br_index = get_index(argv[1], "bridge");
b600a2c3 370 return set_bridge_cfg(bridge_forward_delay, getuint(argv[2]));
ad02a0eb
SH
371}
372
11904a35 373static int cmd_setbridgeforcevers(int argc, char *const *argv)
ad02a0eb
SH
374{
375
11904a35 376 int br_index = get_index(argv[1], "bridge");
b600a2c3
SA
377 return set_bridge_cfg(bridge_protocol_version,
378 2 * getyesno(argv[2], "normal", "slow"));
ad02a0eb
SH
379}
380
b600a2c3 381static int cmd_setbridgetxholdcount(int argc, char *const *argv)
11904a35 382{
ad02a0eb 383
b600a2c3
SA
384 int br_index = get_index(argv[1], "bridge");
385 return set_bridge_cfg(bridge_tx_hold_count, getuint(argv[2]));
ad02a0eb
SH
386}
387
b600a2c3 388
11904a35 389static int cmd_setportprio(int argc, char *const *argv)
ad02a0eb
SH
390{
391
11904a35
SH
392 int br_index = get_index(argv[1], "bridge");
393 int port_index = get_index(argv[2], "port");
b600a2c3 394 return set_port_cfg(port_priority, getuint(argv[3]));
ad02a0eb
SH
395}
396
11904a35 397static int cmd_setportpathcost(int argc, char *const *argv)
ad02a0eb
SH
398{
399
11904a35
SH
400 int br_index = get_index(argv[1], "bridge");
401 int port_index = get_index(argv[2], "port");
b600a2c3 402 return set_port_cfg(port_pathcost, getuint(argv[3]));
ad02a0eb
SH
403}
404
b600a2c3 405static int cmd_setportadminedge(int argc, char *const *argv)
ad02a0eb
SH
406{
407
11904a35
SH
408 int br_index = get_index(argv[1], "bridge");
409 int port_index = get_index(argv[2], "port");
b600a2c3 410 return set_port_cfg(port_admin_edge, getyesno(argv[3], "yes", "no"));
ad02a0eb
SH
411}
412
b600a2c3 413static int cmd_setportautoedge(int argc, char *const *argv)
ad02a0eb
SH
414{
415
11904a35
SH
416 int br_index = get_index(argv[1], "bridge");
417 int port_index = get_index(argv[2], "port");
b600a2c3 418 return set_port_cfg(port_auto_edge, getyesno(argv[3], "yes", "no"));
ad02a0eb
SH
419}
420
11904a35 421static int cmd_setportp2p(int argc, char *const *argv)
ad02a0eb
SH
422{
423
11904a35
SH
424 int br_index = get_index(argv[1], "bridge");
425 int port_index = get_index(argv[2], "port");
b600a2c3
SA
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 };
ad02a0eb 429
b600a2c3 430 return set_port_cfg(port_admin_p2p, vals[getenum(argv[3], opts)]);
ad02a0eb
SH
431}
432
11904a35 433static int cmd_portmcheck(int argc, char *const *argv)
ad02a0eb
SH
434{
435
11904a35
SH
436 int br_index = get_index(argv[1], "bridge");
437 int port_index = get_index(argv[2], "port");
b600a2c3 438 return CTL_port_mcheck(br_index, port_index);
ad02a0eb
SH
439}
440
11904a35 441static int cmd_debuglevel(int argc, char *const *argv)
ad02a0eb 442{
11904a35 443 return CTL_set_debug_level(getuint(argv[1]));
ad02a0eb
SH
444}
445
11904a35
SH
446struct command {
447 int nargs;
448 int optargs;
449 const char *name;
450 int (*func) (int argc, char *const *argv);
451 const char *help;
ad02a0eb
SH
452};
453
454static const struct command commands[] = {
11904a35
SH
455 {0, 32, "showbridge", cmd_showbridge,
456 "[<bridge> ... ]\t\tshow bridge state"},
457 {1, 32, "showport", cmd_showport,
458 "<bridge> [<port> ... ]\tshow port state"},
459 {1, 32, "showportdetail", cmd_showportdetail,
460 "<bridge> [<port> ... ]\tshow port state (detail)"},
11904a35
SH
461 {2, 0, "setbridgeprio", cmd_setbridgeprio,
462 "<bridge> <priority>\tset bridge priority (0-61440)"},
463 {2, 0, "sethello", cmd_setbridgehello,
464 "<bridge> <hellotime>\tset bridge hello time (1-10)"},
465 {2, 0, "setmaxage", cmd_setbridgemaxage,
466 "<bridge> <maxage>\tset bridge max age (6-40)"},
467 {2, 0, "setfdelay", cmd_setbridgefdelay,
468 "<bridge> <fwd_delay>\tset bridge forward delay (4-30)"},
469 {2, 0, "setforcevers", cmd_setbridgeforcevers,
470 "<bridge> {normal|slow}\tnormal RSTP or force to STP"},
b600a2c3
SA
471 {2, 0, "settxholdcount", cmd_setbridgetxholdcount,
472 "<bridge> <tx_hold_count>\tset bridge transmit hold count (1-10)"},
11904a35
SH
473 {3, 0, "setportprio", cmd_setportprio,
474 "<bridge> <port> <priority>\tset port priority (0-240)"},
475 {3, 0, "setportpathcost", cmd_setportpathcost,
476 "<bridge> <port> <cost>\tset port path cost"},
b600a2c3
SA
477 {3, 0, "setportadminedge", cmd_setportadminedge,
478 "<bridge> <port> {yes|no}\tconfigure if it is an admin edge port"},
479 {3, 0, "setportautoedge", cmd_setportautoedge,
480 "<bridge> <port> {yes|no}\tconfigure if it is an auto edge port"},
11904a35
SH
481 {3, 0, "setportp2p", cmd_setportp2p,
482 "<bridge> <port> {yes|no|auto}\tset whether p2p connection"},
483 {2, 0, "portmcheck", cmd_portmcheck,
484 "<bridge> <port>\ttry to get back from STP to RSTP mode"},
485 {1, 0, "debuglevel", cmd_debuglevel, "<level>\t\tLevel of verbosity"},
ad02a0eb
SH
486};
487
488const struct command *command_lookup(const char *cmd)
489{
490 int i;
491
11904a35 492 for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ad02a0eb
SH
493 if (!strcmp(cmd, commands[i].name))
494 return &commands[i];
495 }
496
497 return NULL;
498}
499
500void command_helpall(void)
501{
502 int i;
503
11904a35 504 for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ad02a0eb
SH
505 printf("\t%-10s\t%s\n", commands[i].name, commands[i].help);
506 }
507}
508
509static void help()
510{
511 printf("Usage: rstpctl [commands]\n");
512 printf("commands:\n");
513 command_helpall();
514}
515
516#define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b
517#define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
518
11904a35 519int main(int argc, char *const *argv)
ad02a0eb
SH
520{
521 const struct command *cmd;
522 int f;
523 static const struct option options[] = {
11904a35
SH
524 {.name = "help",.val = 'h'},
525 {.name = "version",.val = 'V'},
526 {0}
ad02a0eb
SH
527 };
528
11904a35
SH
529 while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF)
530 switch (f) {
ad02a0eb
SH
531 case 'h':
532 help();
533 return 0;
534 case 'V':
535 printf("%s\n", PACKAGE_VERSION(VERSION, BUILD));
536 return 0;
537 default:
538 fprintf(stderr, "Unknown option '%c'\n", f);
539 goto help;
540 }
11904a35 541
ad02a0eb
SH
542 if (argc == optind)
543 goto help;
11904a35 544
ad02a0eb
SH
545 if (ctl_client_init()) {
546 fprintf(stderr, "can't setup control connection\n");
547 return 1;
548 }
549
550 argc -= optind;
551 argv += optind;
552 if ((cmd = command_lookup(argv[0])) == NULL) {
553 fprintf(stderr, "never heard of command [%s]\n", argv[0]);
554 goto help;
555 }
11904a35 556
ad02a0eb
SH
557 if (argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1) {
558 printf("Incorrect number of arguments for command\n");
559 printf("Usage: rstpctl %s %s\n", cmd->name, cmd->help);
560 return 1;
561 }
562
563 return cmd->func(argc, argv);
564
11904a35 565 help:
ad02a0eb
SH
566 help();
567 return 1;
568}