]> git.ipfire.org Git - people/ms/rstp.git/blob - ctl_main.c
82bf02c2f540442fbd479c56abae74eba561474d
[people/ms/rstp.git] / ctl_main.c
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
23 int get_index_die(const char *ifname, const char *doc, int die)
24 {
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;
35 }
36
37 int get_index(const char *ifname, const char *doc)
38 {
39 return get_index_die(ifname, doc, 1);
40 }
41
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]
44
45 #define BOOL_STR(x) ((x) ? "yes" : "no")
46
47 static int do_showbridge(const char *br_name)
48 {
49 STP_BridgeStatus s;
50
51 int br_index = get_index_die(br_name, "bridge", 0);
52 if (br_index < 0)
53 return -1;
54
55 int r = CTL_get_bridge_status(br_index, &s);
56 if (r) {
57 return -1;
58 }
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));
80
81 return 0;
82 }
83
84 #define SYSFS_PATH_MAX 256
85 #define SYSFS_CLASS_NET "/sys/class/net"
86
87 static int isbridge(const struct dirent *entry)
88 {
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
97 static 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;
133 }
134
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
176 int detail = 0;
177
178 static int do_showport(int br_index, const char *port_name)
179 {
180 STP_PortStatus s;
181 int r = 0;
182 int port_index = get_index_die(port_name, "port", 0);
183 if (port_index < 0)
184 return -1;
185
186 r = CTL_get_port_status(br_index, port_index, &s);
187 if (r) {
188 fprintf(stderr, "Failed to get port state for port %d\n",
189 port_index);
190 return -1;
191 }
192
193 if (detail) {
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));
214 } else {
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);
224 }
225 return 0;
226 }
227
228 static int not_dot_dotdot(const struct dirent *entry)
229 {
230 const char *n = entry->d_name;
231
232 return !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0)));
233 }
234
235 static int cmd_showport(int argc, char *const *argv)
236 {
237 int r = 0;
238
239 int br_index = get_index(argv[1], "bridge");
240
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
265 int err = do_showport(br_index, name);
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;
277 }
278
279 static int cmd_showportdetail(int argc, char *const *argv)
280 {
281 detail = 1;
282 return cmd_showport(argc, argv);
283 }
284
285 unsigned int getuint(const char *s)
286 {
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;
295 }
296
297 int getenum(const char *s, const char *opt[])
298 {
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
311 int 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
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
342
343
344 static int cmd_setbridgeprio(int argc, char *const *argv)
345 {
346
347 int br_index = get_index(argv[1], "bridge");
348 return set_bridge_cfg(bridge_priority, getuint(argv[2]));
349 }
350
351 static int cmd_setbridgemaxage(int argc, char *const *argv)
352 {
353
354 int br_index = get_index(argv[1], "bridge");
355 return set_bridge_cfg(bridge_max_age, getuint(argv[2]));
356 }
357
358 static int cmd_setbridgehello(int argc, char *const *argv)
359 {
360
361 int br_index = get_index(argv[1], "bridge");
362 return set_bridge_cfg(bridge_hello_time, getuint(argv[2]));
363 }
364
365 static int cmd_setbridgefdelay(int argc, char *const *argv)
366 {
367
368 int br_index = get_index(argv[1], "bridge");
369 return set_bridge_cfg(bridge_forward_delay, getuint(argv[2]));
370 }
371
372 static int cmd_setbridgeforcevers(int argc, char *const *argv)
373 {
374
375 int br_index = get_index(argv[1], "bridge");
376 return set_bridge_cfg(bridge_protocol_version,
377 2 * getyesno(argv[2], "normal", "slow"));
378 }
379
380 static int cmd_setbridgetxholdcount(int argc, char *const *argv)
381 {
382
383 int br_index = get_index(argv[1], "bridge");
384 return set_bridge_cfg(bridge_tx_hold_count, getuint(argv[2]));
385 }
386
387
388 static int cmd_setportprio(int argc, char *const *argv)
389 {
390
391 int br_index = get_index(argv[1], "bridge");
392 int port_index = get_index(argv[2], "port");
393 return set_port_cfg(port_priority, getuint(argv[3]));
394 }
395
396 static int cmd_setportpathcost(int argc, char *const *argv)
397 {
398
399 int br_index = get_index(argv[1], "bridge");
400 int port_index = get_index(argv[2], "port");
401 return set_port_cfg(port_pathcost, getuint(argv[3]));
402 }
403
404 static int cmd_setportadminedge(int argc, char *const *argv)
405 {
406
407 int br_index = get_index(argv[1], "bridge");
408 int port_index = get_index(argv[2], "port");
409 return set_port_cfg(port_admin_edge, getyesno(argv[3], "yes", "no"));
410 }
411
412 static int cmd_setportautoedge(int argc, char *const *argv)
413 {
414
415 int br_index = get_index(argv[1], "bridge");
416 int port_index = get_index(argv[2], "port");
417 return set_port_cfg(port_auto_edge, getyesno(argv[3], "yes", "no"));
418 }
419
420 static int cmd_setportp2p(int argc, char *const *argv)
421 {
422
423 int br_index = get_index(argv[1], "bridge");
424 int port_index = get_index(argv[2], "port");
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 };
428
429 return set_port_cfg(port_admin_p2p, vals[getenum(argv[3], opts)]);
430 }
431
432 static int cmd_portmcheck(int argc, char *const *argv)
433 {
434
435 int br_index = get_index(argv[1], "bridge");
436 int port_index = get_index(argv[2], "port");
437 return CTL_port_mcheck(br_index, port_index);
438 }
439
440 static int cmd_debuglevel(int argc, char *const *argv)
441 {
442 return CTL_set_debug_level(getuint(argv[1]));
443 }
444
445 struct command {
446 int nargs;
447 int optargs;
448 const char *name;
449 int (*func) (int argc, char *const *argv);
450 const char *help;
451 };
452
453 static const struct command commands[] = {
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)"},
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"},
470 {2, 0, "settxholdcount", cmd_setbridgetxholdcount,
471 "<bridge> <tx_hold_count>\tset bridge transmit hold count (1-10)"},
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"},
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"},
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"},
485 };
486
487 const struct command *command_lookup(const char *cmd)
488 {
489 int i;
490
491 for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
492 if (!strcmp(cmd, commands[i].name))
493 return &commands[i];
494 }
495
496 return NULL;
497 }
498
499 void command_helpall(void)
500 {
501 int i;
502
503 for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
504 printf("\t%-10s\t%s\n", commands[i].name, commands[i].help);
505 }
506 }
507
508 static 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
518 int main(int argc, char *const *argv)
519 {
520 const struct command *cmd;
521 int f;
522 static const struct option options[] = {
523 {.name = "help",.val = 'h'},
524 {.name = "version",.val = 'V'},
525 {0}
526 };
527
528 while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF)
529 switch (f) {
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 }
540
541 if (argc == optind)
542 goto help;
543
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 }
555
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
564 help:
565 help();
566 return 1;
567 }