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