]> git.ipfire.org Git - people/ms/mstpd.git/blob - ctl_main.c
Fix bug: infinite recursion in TCSM
[people/ms/mstpd.git] / ctl_main.c
1 /*****************************************************************************
2 Copyright (c) 2011 Factor-SPE
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2 of the License, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc., 59
16 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 The full GNU General Public License is included in this distribution in the
19 file called LICENSE.
20
21 Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
22
23 ******************************************************************************/
24
25 #include <string.h>
26 #include <getopt.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <sys/stat.h>
31
32 #include "ctl_socket_client.h"
33 #include "log.h"
34
35 static int get_index_die(const char *ifname, const char *doc, bool die)
36 {
37 int r = if_nametoindex(ifname);
38 if(0 == r)
39 {
40 fprintf(stderr,
41 "Can't find index for %s %s. Not a valid interface.\n",
42 doc, ifname);
43 if(die)
44 exit(1);
45 return -1;
46 }
47 return r;
48 }
49
50 static inline int get_index(const char *ifname, const char *doc)
51 {
52 return get_index_die(ifname, doc, true);
53 }
54
55 static inline int get_id(const char *str, const char *doc, unsigned int max_id)
56 {
57 int id = strtol(str, NULL, 10);
58 if((0 > id) || (max_id < id)
59 || ((0 == id) && ('0' != str[0]))
60 )
61 {
62 fprintf(stderr, "Bad %s %s\n", doc, str);
63 return -1;
64 }
65 return id;
66 }
67
68 #define GET_NUM_FROM_PRIO(p) (__be16_to_cpu(p) & 0x0FFF)
69
70 #define BR_ID_FMT "%01hhX.%03hX.%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX"
71 #define BR_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
72 GET_NUM_FROM_PRIO((x).s.priority), \
73 x.s.mac_address[0], x.s.mac_address[1], x.s.mac_address[2], \
74 x.s.mac_address[3], x.s.mac_address[4], x.s.mac_address[5]
75
76 #define PRT_ID_FMT "%01hhX.%03hX"
77 #define PRT_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
78 GET_NUM_FROM_PRIO(x)
79
80 #define BOOL_STR(x) ((x) ? "yes" : "no")
81
82 static int do_showbridge(const char *br_name)
83 {
84 CIST_BridgeStatus s;
85 char root_port_name[IFNAMSIZ];
86 unsigned int root_portno;
87 int br_index = get_index_die(br_name, "bridge", false);
88 if(0 > br_index)
89 return br_index;
90
91 if(CTL_get_cist_bridge_status(br_index, &s, root_port_name))
92 return -1;
93 printf("%s CIST info\n", br_name);
94 printf(" enabled %s\n", BOOL_STR(s.enabled));
95 printf(" bridge id "BR_ID_FMT"\n", BR_ID_ARGS(s.bridge_id));
96 printf(" designated root "BR_ID_FMT"\n", BR_ID_ARGS(s.designated_root));
97 printf(" regional root "BR_ID_FMT"\n", BR_ID_ARGS(s.regional_root));
98 printf(" root port ");
99 if(0 != (root_portno = GET_NUM_FROM_PRIO(s.root_port_id)))
100 printf("%s (#%u)\n", root_port_name, root_portno);
101 else
102 printf("none\n");
103 printf(" path cost %-10u ", s.root_path_cost);
104 printf("internal path cost %u\n", s.internal_path_cost);
105 printf(" max age %-10hhu ", s.root_max_age);
106 printf("bridge max age %hhu\n", s.bridge_max_age);
107 printf(" forward delay %-10hhu ", s.root_forward_delay);
108 printf("bridge forward delay %hhu\n", s.bridge_forward_delay);
109 printf(" tx hold count %-10u ", s.tx_hold_count);
110 printf("max hops %hhu\n", s.max_hops);
111 printf(" force protocol version %u\n", s.protocol_version);
112 printf(" time since topology change %u\n", s.time_since_topology_change);
113 printf(" toplogy change count %u\n", s.topology_change_count);
114 printf(" topology change %s\n", BOOL_STR(s.topology_change));
115
116 return 0;
117 }
118
119 #define SYSFS_PATH_MAX 256
120 #define SYSFS_CLASS_NET "/sys/class/net"
121
122 static int isbridge(const struct dirent *entry)
123 {
124 char path[SYSFS_PATH_MAX];
125 int save_errno;
126 bool result;
127 struct stat st;
128
129 snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge",
130 entry->d_name);
131 save_errno = errno;
132 result = (0 == stat(path, &st)) && S_ISDIR(st.st_mode);
133 errno = save_errno;
134 return result;
135 }
136
137 static int cmd_showbridge(int argc, char *const *argv)
138 {
139 int i, count = 0;
140 int r = 0;
141 struct dirent **namelist;
142
143 if(1 < argc)
144 {
145 count = argc - 1;
146 }
147 else
148 {
149 /* TODO: use versionsort, if available */
150 count = scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort);
151 if(0 > count)
152 {
153 fprintf(stderr, "Error getting list of all bridges\n");
154 return -1;
155 }
156 }
157
158 for(i = 0; i < count; ++i)
159 {
160 const char *name;
161 if(1 < argc)
162 name = argv[i + 1];
163 else
164 name = namelist[i]->d_name;
165
166 int err = do_showbridge(name);
167 if(err)
168 r = err;
169 }
170
171 if(1 >= argc)
172 {
173 for(i = 0; i < count; ++i)
174 free(namelist[i]);
175 free(namelist);
176 }
177
178 return r;
179 }
180
181 static int cmd_showtree(int argc, char *const *argv)
182 {
183 MSTI_BridgeStatus s;
184 char root_port_name[IFNAMSIZ];
185 unsigned int root_portno;
186 int br_index = get_index(argv[1], "bridge");
187 if(0 > br_index)
188 return br_index;
189 int mstid = get_id(argv[2], "mstid", MAX_MSTID);
190 if(0 > mstid)
191 return mstid;
192
193 if(CTL_get_msti_bridge_status(br_index, mstid, &s, root_port_name))
194 return -1;
195
196 printf("%s MSTI %hu info\n", argv[1], mstid);
197 printf(" bridge id "BR_ID_FMT"\n", BR_ID_ARGS(s.bridge_id));
198 printf(" regional root "BR_ID_FMT"\n", BR_ID_ARGS(s.regional_root));
199 printf(" root port ");
200 if(0 != (root_portno = GET_NUM_FROM_PRIO(s.root_port_id)))
201 printf("%s (#%u)\n", root_port_name, root_portno);
202 else
203 printf("none\n");
204 printf(" internal path cost %u\n", s.internal_path_cost);
205 printf(" time since topology change %u\n", s.time_since_topology_change);
206 printf(" toplogy change count %u\n", s.topology_change_count);
207 printf(" topology change %s\n", BOOL_STR(s.topology_change));
208
209 return 0;
210 }
211
212 #define STATE_STR(_state) \
213 ({ \
214 int _s = _state; \
215 char *_str = "unknown"; \
216 switch(_s) \
217 { \
218 case BR_STATE_DISABLED: \
219 case BR_STATE_BLOCKING: \
220 case BR_STATE_LISTENING: _str = "discarding"; break; \
221 case BR_STATE_LEARNING: _str = "learning"; break; \
222 case BR_STATE_FORWARDING:_str = "forwarding"; break; \
223 } \
224 _str; \
225 })
226
227 #define SHORT_STATE_STR(_state) \
228 ({ \
229 int _s = _state; \
230 char *_str = "unkn"; \
231 switch(_s) \
232 { \
233 case BR_STATE_DISABLED: \
234 case BR_STATE_BLOCKING: \
235 case BR_STATE_LISTENING: _str = "disc"; break; \
236 case BR_STATE_LEARNING: _str = "lear"; break; \
237 case BR_STATE_FORWARDING:_str = "forw"; break; \
238 } \
239 _str; \
240 })
241
242 #define ADMIN_P2P_STR(_state) \
243 ({ \
244 admin_p2p_t _s = _state; \
245 char *_str = "unkn"; \
246 switch(_s) \
247 { \
248 case p2pForceFalse:_str = "no"; break; \
249 case p2pForceTrue: _str = "yes"; break; \
250 case p2pAuto: _str = "auto"; break; \
251 } \
252 _str; \
253 })
254
255 #define ROLE_STR(_role) \
256 ({ \
257 port_role_t _r = _role; \
258 char *_str = "Unknown"; \
259 switch(_r) \
260 { \
261 case roleRoot: _str = "Root"; break; \
262 case roleDesignated:_str = "Designated"; break; \
263 case roleAlternate: _str = "Alternate"; break; \
264 case roleBackup: _str = "Backup"; break; \
265 case roleMaster: _str = "Master"; break; \
266 case roleDisabled: _str = "Disabled"; break; \
267 } \
268 _str; \
269 })
270
271 #define SHORT_ROLE_STR(_role) \
272 ({ \
273 port_role_t _r = _role; \
274 char *_str = "Unkn"; \
275 switch(_r) \
276 { \
277 case roleRoot: _str = "Root"; break; \
278 case roleDesignated:_str = "Desg"; break; \
279 case roleAlternate: _str = "Altn"; break; \
280 case roleBackup: _str = "Back"; break; \
281 case roleMaster: _str = "Mstr"; break; \
282 case roleDisabled: _str = "Disa"; break; \
283 } \
284 _str; \
285 })
286
287
288 static int detail = 0;
289
290 static int do_showport(int br_index, const char *bridge_name,
291 const char *port_name)
292 {
293 CIST_PortStatus s;
294 int r = 0;
295 int port_index = get_index_die(port_name, "port", false);
296 if(0 > port_index)
297 return port_index;
298
299 if((r = CTL_get_cist_port_status(br_index, port_index, &s)))
300 {
301 fprintf(stderr, "%s:%s Failed to get port state\n",
302 bridge_name, port_name);
303 return -1;
304 }
305
306 if(detail)
307 {
308 printf("%s:%s CIST info\n", bridge_name, port_name);
309 printf(" enabled %-23s ", BOOL_STR(s.enabled));
310 printf("role %s\n", ROLE_STR(s.role));
311 printf(" port id "PRT_ID_FMT" ",
312 PRT_ID_ARGS(s.port_id));
313 printf("state %s\n", STATE_STR(s.state));
314 printf(" external port cost %-23u ", s.external_port_path_cost);
315 printf("admin external cost %u\n", s.admin_external_port_path_cost);
316 printf(" internal port cost %-23u ", s.internal_port_path_cost);
317 printf("admin internal cost %u\n", s.admin_internal_port_path_cost);
318 printf(" designated root "BR_ID_FMT" ",
319 BR_ID_ARGS(s.designated_root));
320 printf("dsgn external cost %u\n", s.designated_external_cost);
321 printf(" dsgn regional root "BR_ID_FMT" ",
322 BR_ID_ARGS(s.designated_regional_root));
323 printf("dsgn internal cost %u\n", s.designated_internal_cost);
324 printf(" designated bridge "BR_ID_FMT" ",
325 BR_ID_ARGS(s.designated_bridge));
326 printf("designated port "PRT_ID_FMT"\n",
327 PRT_ID_ARGS(s.designated_port));
328 printf(" admin edge port %-23s ", BOOL_STR(s.admin_edge_port));
329 printf("auto edge port %s\n", BOOL_STR(s.auto_edge_port));
330 printf(" oper edge port %-23s ", BOOL_STR(s.oper_edge_port));
331 printf("toplogy change ack %s\n", BOOL_STR(s.tc_ack));
332 printf(" point-to-point %-23s ", BOOL_STR(s.oper_p2p));
333 printf("admin point-to-point %s\n", ADMIN_P2P_STR(s.admin_p2p));
334 printf(" restricted role %-23s ", BOOL_STR(s.restricted_role));
335 printf("restricted TCN %s\n", BOOL_STR(s.restricted_tcn));
336 printf(" port hello time %-23hhu ", s.port_hello_time);
337 printf("disputed %s\n", BOOL_STR(s.disputed));
338 }
339 else
340 {
341 printf("%c%c %-5s "PRT_ID_FMT" %4s "BR_ID_FMT" "BR_ID_FMT" "PRT_ID_FMT" %s\n",
342 (s.oper_p2p) ? ' ' : '*',
343 (s.oper_edge_port) ? 'E' : ' ',
344 port_name,
345 PRT_ID_ARGS(s.port_id),
346 s.enabled ? SHORT_STATE_STR(s.state) : "down",
347 BR_ID_ARGS(s.designated_root),
348 BR_ID_ARGS(s.designated_bridge),
349 PRT_ID_ARGS(s.designated_port),
350 SHORT_ROLE_STR(s.role));
351 }
352 return 0;
353 }
354
355 static int not_dot_dotdot(const struct dirent *entry)
356 {
357 const char *n = entry->d_name;
358
359 return !('.' == n[0] && (0 == n[1] || ('.' == n[1] && 0 == n[2])));
360 }
361
362 static int cmd_showport(int argc, char *const *argv)
363 {
364 int r = 0;
365
366 int br_index = get_index(argv[1], "bridge");
367 if(0 > br_index)
368 return br_index;
369
370 int i, count = 0;
371 struct dirent **namelist;
372
373 if(2 < argc)
374 {
375 count = argc - 2;
376 }
377 else
378 {
379 char buf[SYSFS_PATH_MAX];
380 snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]);
381 /* TODO: use versionsort, if available */
382 count = scandir(buf, &namelist, not_dot_dotdot, alphasort);
383 if(0 > count)
384 {
385 fprintf(stderr, "Error getting list of all ports of bridge %s\n",
386 argv[1]);
387 return -1;
388 }
389 }
390
391 for(i = 0; i < count; ++i)
392 {
393 const char *name;
394 if(2 < argc)
395 name = argv[i + 2];
396 else
397 name = namelist[i]->d_name;
398
399 int err = do_showport(br_index, argv[1], name);
400 if(err)
401 r = err;
402 }
403
404 if(2 >= argc)
405 {
406 for(i = 0; i < count; ++i)
407 free(namelist[i]);
408 free(namelist);
409 }
410
411 return r;
412 }
413
414 static int cmd_showportdetail(int argc, char *const *argv)
415 {
416 detail = 1;
417 return cmd_showport(argc, argv);
418 }
419
420 static int cmd_showtreeport(int argc, char *const *argv)
421 {
422 MSTI_PortStatus s;
423 int br_index = get_index(argv[1], "bridge");
424 if(0 > br_index)
425 return br_index;
426 int port_index = get_index(argv[2], "port");
427 if(0 > port_index)
428 return port_index;
429 int mstid = get_id(argv[3], "mstid", MAX_MSTID);
430 if(0 > mstid)
431 return mstid;
432
433 if(CTL_get_msti_port_status(br_index, port_index, mstid, &s))
434 return -1;
435
436 printf("%s:%s MSTI %hu info\n", argv[1], argv[2], mstid);
437 printf(" role %-23s ", ROLE_STR(s.role));
438 printf("port id "PRT_ID_FMT"\n", PRT_ID_ARGS(s.port_id));
439 printf(" state %-23s ", STATE_STR(s.state));
440 printf("disputed %s\n", BOOL_STR(s.disputed));
441 printf(" internal port cost %-23u ", s.internal_port_path_cost);
442 printf("admin internal cost %u\n", s.admin_internal_port_path_cost);
443 printf(" dsgn regional root "BR_ID_FMT" ",
444 BR_ID_ARGS(s.designated_regional_root));
445 printf("dsgn internal cost %u\n", s.designated_internal_cost);
446 printf(" designated bridge "BR_ID_FMT" ",
447 BR_ID_ARGS(s.designated_bridge));
448 printf("designated port "PRT_ID_FMT"\n",
449 PRT_ID_ARGS(s.designated_port));
450
451 return 0;
452 }
453
454 static unsigned int getuint(const char *s)
455 {
456 char *end;
457 long l;
458 l = strtoul(s, &end, 0);
459 if(0 == *s || 0 != *end || INT_MAX < l)
460 {
461 fprintf(stderr, "Invalid unsigned int arg %s\n", s);
462 exit(1);
463 }
464 return l;
465 }
466
467 static int getenum(const char *s, const char *opt[])
468 {
469 int i;
470 for(i = 0; opt[i] != NULL; ++i)
471 if(0 == strcmp(s, opt[i]))
472 return i;
473
474 fprintf(stderr, "Invalid argument %s: expecting one of ", s);
475 for(i = 0; opt[i] != NULL; ++i)
476 fprintf(stderr, "%s%s", opt[i], (opt[i + 1] ? ", " : "\n"));
477
478 exit(1);
479 }
480
481 static int getyesno(const char *s, const char *yes, const char *no)
482 {
483 /* Reverse yes and no so error message looks more normal */
484 const char *opt[] = { yes, no, NULL };
485 return 1 - getenum(s, opt);
486 }
487
488 static int cmd_setmstconfid(int argc, char *const *argv)
489 {
490 int br_index = get_index(argv[1], "bridge");
491 if(0 > br_index)
492 return br_index;
493 unsigned int revision = getuint(argv[2]);
494 if(revision > 0xFFFF)
495 {
496 fprintf(stderr, "Bad revision %s\n", argv[2]);
497 return -1;
498 }
499 return CTL_set_mstconfid(br_index, revision, argv[3]);
500 }
501
502 #define set_bridge_cfg(field, value) \
503 ({ \
504 CIST_BridgeConfig c; \
505 memset(&c, 0, sizeof(c)); \
506 c.field = value; \
507 c.set_ ## field = true; \
508 int r = CTL_set_cist_bridge_config(br_index, &c); \
509 if(r) \
510 printf("Couldn't change bridge " #field "\n"); \
511 r; \
512 })
513
514 #define set_port_cfg(field, value) \
515 ({ \
516 CIST_PortConfig c; \
517 memset(&c, 0, sizeof(c)); \
518 c.field = value; \
519 c.set_ ## field = true; \
520 int r = CTL_set_cist_port_config(br_index, port_index, &c); \
521 if(r) \
522 printf("Couldn't change port " #field "\n"); \
523 r; \
524 })
525
526 #define set_tree_port_cfg(field, value) \
527 ({ \
528 MSTI_PortConfig c; \
529 memset(&c, 0, sizeof(c)); \
530 c.field = value; \
531 c.set_ ## field = true; \
532 int r = CTL_set_msti_port_config(br_index, port_index, mstid, &c); \
533 if(r) \
534 printf("Couldn't change per-tree port " #field "\n"); \
535 r; \
536 })
537
538 static int cmd_setbridgemaxage(int argc, char *const *argv)
539 {
540 int br_index = get_index(argv[1], "bridge");
541 if(0 > br_index)
542 return br_index;
543 unsigned int max_age = getuint(argv[2]);
544 if(max_age > 255)
545 max_age = 255;
546 return set_bridge_cfg(bridge_max_age, max_age);
547 }
548
549 static int cmd_setbridgefdelay(int argc, char *const *argv)
550 {
551 int br_index = get_index(argv[1], "bridge");
552 if(0 > br_index)
553 return br_index;
554 unsigned int forward_delay = getuint(argv[2]);
555 if(forward_delay > 255)
556 forward_delay = 255;
557 return set_bridge_cfg(bridge_forward_delay, forward_delay);
558 }
559
560 static int cmd_setbridgemaxhops(int argc, char *const *argv)
561 {
562 int br_index = get_index(argv[1], "bridge");
563 if(0 > br_index)
564 return br_index;
565 unsigned int max_hops = getuint(argv[2]);
566 if(max_hops > 255)
567 max_hops = 255;
568 return set_bridge_cfg(max_hops, max_hops);
569 }
570
571 static int cmd_setbridgeforcevers(int argc, char *const *argv)
572 {
573 int br_index = get_index(argv[1], "bridge");
574 if(0 > br_index)
575 return br_index;
576 const char *opts[] = { "stp", "rstp", "mstp", NULL };
577 int vals[] = { protoSTP, protoRSTP, protoMSTP };
578 return set_bridge_cfg(protocol_version, vals[getenum(argv[2], opts)]);
579 }
580
581 static int cmd_setbridgetxholdcount(int argc, char *const *argv)
582 {
583 int br_index = get_index(argv[1], "bridge");
584 if(0 > br_index)
585 return br_index;
586 return set_bridge_cfg(tx_hold_count, getuint(argv[2]));
587 }
588
589 static int cmd_settreeprio(int argc, char *const *argv)
590 {
591 int br_index = get_index(argv[1], "bridge");
592 if(0 > br_index)
593 return br_index;
594 int mstid = get_id(argv[2], "mstid", MAX_MSTID);
595 if(0 > mstid)
596 return mstid;
597 unsigned int prio = getuint(argv[3]);
598 if(prio > 255)
599 prio = 255;
600 return CTL_set_msti_bridge_config(br_index, mstid, prio);
601 }
602
603 static int cmd_setportpathcost(int argc, char *const *argv)
604 {
605 int br_index = get_index(argv[1], "bridge");
606 if(0 > br_index)
607 return br_index;
608 int port_index = get_index(argv[2], "port");
609 if(0 > port_index)
610 return port_index;
611 return set_port_cfg(admin_external_port_path_cost, getuint(argv[3]));
612 }
613
614 static int cmd_setportadminedge(int argc, char *const *argv)
615 {
616 int br_index = get_index(argv[1], "bridge");
617 if(0 > br_index)
618 return br_index;
619 int port_index = get_index(argv[2], "port");
620 if(0 > port_index)
621 return port_index;
622 return set_port_cfg(admin_edge_port, getyesno(argv[3], "yes", "no"));
623 }
624
625 static int cmd_setportautoedge(int argc, char *const *argv)
626 {
627 int br_index = get_index(argv[1], "bridge");
628 if(0 > br_index)
629 return br_index;
630 int port_index = get_index(argv[2], "port");
631 if(0 > port_index)
632 return port_index;
633 return set_port_cfg(auto_edge_port, getyesno(argv[3], "yes", "no"));
634 }
635
636 static int cmd_setportp2p(int argc, char *const *argv)
637 {
638 int br_index = get_index(argv[1], "bridge");
639 if(0 > br_index)
640 return br_index;
641 int port_index = get_index(argv[2], "port");
642 if(0 > port_index)
643 return port_index;
644 const char *opts[] = { "no", "yes", "auto", NULL };
645 int vals[] = { p2pForceFalse, p2pForceTrue, p2pAuto };
646 return set_port_cfg(admin_p2p, vals[getenum(argv[3], opts)]);
647 }
648
649 static int cmd_setportrestrrole(int argc, char *const *argv)
650 {
651 int br_index = get_index(argv[1], "bridge");
652 if(0 > br_index)
653 return br_index;
654 int port_index = get_index(argv[2], "port");
655 if(0 > port_index)
656 return port_index;
657 return set_port_cfg(restricted_role, getyesno(argv[3], "yes", "no"));
658 }
659
660 static int cmd_setportrestrtcn(int argc, char *const *argv)
661 {
662 int br_index = get_index(argv[1], "bridge");
663 if(0 > br_index)
664 return br_index;
665 int port_index = get_index(argv[2], "port");
666 if(0 > port_index)
667 return port_index;
668 return set_port_cfg(restricted_tcn, getyesno(argv[3], "yes", "no"));
669 }
670
671 static int cmd_settreeportprio(int argc, char *const *argv)
672 {
673 int br_index = get_index(argv[1], "bridge");
674 if(0 > br_index)
675 return br_index;
676 int port_index = get_index(argv[2], "port");
677 if(0 > port_index)
678 return port_index;
679 int mstid = get_id(argv[3], "mstid", MAX_MSTID);
680 if(0 > mstid)
681 return mstid;
682 unsigned int prio = getuint(argv[4]);
683 if(prio > 255)
684 prio = 255;
685 return set_tree_port_cfg(port_priority, prio);
686 }
687
688 static int cmd_settreeportcost(int argc, char *const *argv)
689 {
690 int br_index = get_index(argv[1], "bridge");
691 if(0 > br_index)
692 return br_index;
693 int port_index = get_index(argv[2], "port");
694 if(0 > port_index)
695 return port_index;
696 int mstid = get_id(argv[3], "mstid", MAX_MSTID);
697 if(0 > mstid)
698 return mstid;
699 return set_tree_port_cfg(admin_internal_port_path_cost, getuint(argv[4]));
700 }
701
702 static int cmd_portmcheck(int argc, char *const *argv)
703 {
704 int br_index = get_index(argv[1], "bridge");
705 if(0 > br_index)
706 return br_index;
707 int port_index = get_index(argv[2], "port");
708 if(0 > port_index)
709 return port_index;
710 return CTL_port_mcheck(br_index, port_index);
711 }
712
713 static int cmd_debuglevel(int argc, char *const *argv)
714 {
715 return CTL_set_debug_level(getuint(argv[1]));
716 }
717
718 static int cmd_showmstilist(int argc, char *const *argv)
719 {
720 int br_index = get_index(argv[1], "bridge");
721 if(0 > br_index)
722 return br_index;
723 int num_mstis = 0, i;
724 __u16 mstids[MAX_IMPLEMENTATION_MSTIS + 1]; /* +1 - for the CIST */
725
726 if(CTL_get_mstilist(br_index, &num_mstis, mstids))
727 return -1;
728
729 printf("%s list of known MSTIs:\n", argv[1]);
730 for(i = 0; i < num_mstis; ++i)
731 printf(" %hu", mstids[i]);
732 printf("\n");
733
734 return 0;
735 }
736
737 static int cmd_showmstconfid(int argc, char *const *argv)
738 {
739 mst_configuration_identifier_t cfgid;
740 int br_index = get_index(argv[1], "bridge");
741 if(0 > br_index)
742 return br_index;
743 int i;
744
745 if(CTL_get_mstconfid(br_index, &cfgid))
746 return -1;
747
748 printf("%s MST Configuration Identifier:\n", argv[1]);
749 printf(" Format Selector: %hhu\n", cfgid.s.selector);
750 printf(" Configuration Name: %.*s\n", CONFIGURATION_NAME_LEN,
751 cfgid.s.configuration_name);
752 printf(" Revision Level: %hu\n",
753 __be16_to_cpu(cfgid.s.revision_level));
754 printf(" Configuration Digest: ");
755 for(i = 0; i < CONFIGURATION_DIGEST_LEN; ++i)
756 printf("%02hhX", cfgid.s.configuration_digest[i]);
757 printf("\n");
758
759 return 0;
760 }
761
762 static int cmd_createtree(int argc, char *const *argv)
763 {
764 int br_index = get_index(argv[1], "bridge");
765 if(0 > br_index)
766 return br_index;
767 int mstid = get_id(argv[2], "mstid", MAX_MSTID);
768 if(0 > mstid)
769 return mstid;
770 return CTL_create_msti(br_index, mstid);
771 }
772
773 static int cmd_deletetree(int argc, char *const *argv)
774 {
775 int br_index = get_index(argv[1], "bridge");
776 if(0 > br_index)
777 return br_index;
778 int mstid = get_id(argv[2], "mstid", MAX_MSTID);
779 if(0 > mstid)
780 return mstid;
781 return CTL_delete_msti(br_index, mstid);
782 }
783
784 static int cmd_showvid2fid(int argc, char *const *argv)
785 {
786 __u16 vid2fid[MAX_VID + 2];
787 int br_index = get_index(argv[1], "bridge");
788 if(0 > br_index)
789 return br_index;
790
791 if(CTL_get_vids2fids(br_index, vid2fid))
792 return -1;
793
794 printf("%s VID-to-FID allocation table:\n", argv[1]);
795 int i, cur_fid;
796 int interval_count;
797 vid2fid[MAX_VID + 1] = 0xFFFF; /* helps to finalize last interval */
798 do{
799 cur_fid = vid2fid[0];
800 for(i = 1; i <= MAX_VID; ++i)
801 if(cur_fid > vid2fid[i])
802 cur_fid = vid2fid[i];
803 if(cur_fid > MAX_FID)
804 break;
805 printf(" FID %u:", cur_fid);
806 for(i = 0, interval_count = 0; i <= (MAX_VID + 1); ++i)
807 {
808 if(cur_fid != vid2fid[i])
809 {
810 if(interval_count)
811 {
812 printf(" %u", i - interval_count);
813 if(1 < interval_count)
814 printf("-%u", i - 1);
815 interval_count = 0;
816 }
817 continue;
818 }
819 vid2fid[i] = 0xFFFF;
820 ++interval_count;
821 }
822 printf("\n");
823 }while(true);
824
825 return 0;
826 }
827
828 static int cmd_showfid2mstid(int argc, char *const *argv)
829 {
830 __u16 fid2mstid[MAX_FID + 2];
831 int br_index = get_index(argv[1], "bridge");
832 if(0 > br_index)
833 return br_index;
834
835 if(CTL_get_fids2mstids(br_index, fid2mstid))
836 return -1;
837
838 printf("%s FID-to-MSTID allocation table:\n", argv[1]);
839 int i, cur_mstid;
840 int interval_count;
841 fid2mstid[MAX_FID + 1] = 0xFFFF; /* helps to finalize last interval */
842 do{
843 cur_mstid = fid2mstid[0];
844 for(i = 1; i <= MAX_FID; ++i)
845 if(cur_mstid > fid2mstid[i])
846 cur_mstid = fid2mstid[i];
847 if(cur_mstid > MAX_MSTID)
848 break;
849 printf(" MSTID %u:", cur_mstid);
850 for(i = 0, interval_count = 0; i <= (MAX_FID + 1); ++i)
851 {
852 if(cur_mstid != fid2mstid[i])
853 {
854 if(interval_count)
855 {
856 printf(" %u", i - interval_count);
857 if(1 < interval_count)
858 printf("-%u", i - 1);
859 interval_count = 0;
860 }
861 continue;
862 }
863 fid2mstid[i] = 0xFFFF;
864 ++interval_count;
865 }
866 printf("\n");
867 }while(true);
868
869 return 0;
870 }
871
872 static int cmd_setvid2fid(int argc, char *const *argv)
873 {
874 int br_index = get_index(argv[1], "bridge");
875 if(0 > br_index)
876 return br_index;
877 int vid = get_id(argv[2], "VID", MAX_VID);
878 if(0 > vid)
879 return vid;
880 if(0 == vid)
881 {
882 fprintf(stderr, "Bad VID %s\n", argv[2]);
883 return -1;
884 }
885 int fid = get_id(argv[3], "FID", MAX_FID);
886 if(0 > fid)
887 return fid;
888 return CTL_set_vid2fid(br_index, vid, fid);
889 }
890
891 static int cmd_setfid2mstid(int argc, char *const *argv)
892 {
893 int br_index = get_index(argv[1], "bridge");
894 if(0 > br_index)
895 return br_index;
896 int fid = get_id(argv[2], "FID", MAX_FID);
897 if(0 > fid)
898 return fid;
899 int mstid = get_id(argv[3], "mstid", MAX_MSTID);
900 if(0 > mstid)
901 return mstid;
902 return CTL_set_fid2mstid(br_index, fid, mstid);
903 }
904
905 struct command
906 {
907 int nargs;
908 int optargs;
909 const char *name;
910 int (*func) (int argc, char *const *argv);
911 const char *format;
912 const char *help;
913 };
914
915 static const struct command commands[] =
916 {
917 /* Show global bridge */
918 {0, 32, "showbridge", cmd_showbridge,
919 "[<bridge> ... ]", "Show bridge state for the CIST"},
920 {1, 0, "showmstilist", cmd_showmstilist,
921 "<bridge>", "Show list of registered MSTIs"},
922 {1, 0, "showmstconfid", cmd_showmstconfid,
923 "<bridge>", "Show MST ConfigId"},
924 {1, 0, "showvid2fid", cmd_showvid2fid,
925 "<bridge>", "Show VID-to-FID allocation table"},
926 {1, 0, "showfid2mstid", cmd_showfid2mstid,
927 "<bridge>", "Show FID-to-MSTID allocation table"},
928 /* Show global port */
929 {1, 32, "showport", cmd_showport,
930 "<bridge> [<port> ... ]", "Show port state for the CIST"},
931 {1, 32, "showportdetail", cmd_showportdetail,
932 "<bridge> [<port> ... ]", "Show port detailed state for the CIST"},
933 /* Show tree bridge */
934 {2, 0, "showtree", cmd_showtree,
935 "<bridge> <mstid>", "Show bridge state for the given MSTI"},
936 /* Show tree port */
937 {3, 0, "showtreeport", cmd_showtreeport,
938 "<bridge> <port> <mstid>", "Show port detailed state for the given MSTI"},
939
940 /* Set global bridge */
941 {3, 0, "setmstconfid", cmd_setmstconfid,
942 "<bridge> <revision> <name>",
943 "Set MST ConfigId elements: Revision Level (0-65535) and Name"},
944 {3, 0, "setvid2fid", cmd_setvid2fid,
945 "<bridge> <VID> <FID>", "Set VID-to-FID allocation"},
946 {3, 0, "setfid2mstid", cmd_setfid2mstid,
947 "<bridge> <FID> <mstid>", "Set FID-to-MSTID allocation"},
948 {2, 0, "setmaxage", cmd_setbridgemaxage,
949 "<bridge> <max_age>", "Set bridge max age (6-40)"},
950 {2, 0, "setfdelay", cmd_setbridgefdelay,
951 "<bridge> <fwd_delay>", "Set bridge forward delay (4-30)"},
952 {2, 0, "setmaxhops", cmd_setbridgemaxhops,
953 "<bridge> <max_hops>", "Set bridge max hops (6-40)"},
954 {2, 0, "setforcevers", cmd_setbridgeforcevers,
955 "<bridge> {mstp|rstp|stp}", "Force Spanning Tree protocol version"},
956 {2, 0, "settxholdcount", cmd_setbridgetxholdcount,
957 "<bridge> <tx_hold_count>", "Set bridge transmit hold count (1-10)"},
958 /* Set tree bridge */
959 {2, 0, "createtree", cmd_createtree,
960 "<bridge> <mstid>", "Create new MSTI"},
961 {2, 0, "deletetree", cmd_deletetree,
962 "<bridge> <mstid>", "Delete existing MSTI"},
963 {3, 0, "settreeprio", cmd_settreeprio,
964 "<bridge> <mstid> <priority>",
965 "Set bridge priority (0-15) for the given MSTI"},
966 /* Set global port */
967 {3, 0, "setportpathcost", cmd_setportpathcost,
968 "<bridge> <port> <cost>",
969 "Set port external path cost for the CIST (0 = auto)"},
970 {3, 0, "setportadminedge", cmd_setportadminedge,
971 "<bridge> <port> {yes|no}", "Set initial edge state"},
972 {3, 0, "setportautoedge", cmd_setportautoedge,
973 "<bridge> <port> {yes|no}", "Enable auto transition to/from edge state"},
974 {3, 0, "setportp2p", cmd_setportp2p,
975 "<bridge> <port> {yes|no|auto}", "Set p2p detection mode"},
976 {3, 0, "setportrestrrole", cmd_setportrestrrole,
977 "<bridge> <port> {yes|no}", "Restrict port ability to take Root role"},
978 {3, 0, "setportrestrtcn", cmd_setportrestrtcn,
979 "<bridge> <port> {yes|no}",
980 "Restrict port ability to propagate received TCNs"},
981 {2, 0, "portmcheck", cmd_portmcheck,
982 "<bridge> <port>", "Try to get back from STP to rapid (RSTP/MSTP) mode"},
983 /* Set tree port */
984 {4, 0, "settreeportprio", cmd_settreeportprio,
985 "<bridge> <port> <mstid> <priority>",
986 "Set port priority (0-15) for the given MSTI"},
987 {4, 0, "settreeportcost", cmd_settreeportcost,
988 "<bridge> <port> <mstid> <cost>",
989 "Set port internal path cost for the given MSTI (0 = auto)"},
990
991 /* Other */
992 {1, 0, "debuglevel", cmd_debuglevel, "<level>", "Level of verbosity"},
993 };
994
995 static const struct command *command_lookup(const char *cmd)
996 {
997 int i;
998
999 for(i = 0; i < COUNT_OF(commands); ++i)
1000 {
1001 if(!strcmp(cmd, commands[i].name))
1002 return &commands[i];
1003 }
1004
1005 return NULL;
1006 }
1007
1008 static void command_helpall(void)
1009 {
1010 int i;
1011
1012 for(i = 0; i < COUNT_OF(commands); ++i)
1013 {
1014 printf("-%s:\n %-16s %s\n", commands[i].help, commands[i].name,
1015 commands[i].format);
1016 }
1017 }
1018
1019 static void help(void)
1020 {
1021 printf("Usage: mstpctl [commands]\n");
1022 printf("commands:\n");
1023 command_helpall();
1024 }
1025
1026 #define PACKAGE_VERSION2(v, b) "mstp, " #v "-" #b
1027 #define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
1028
1029 int main(int argc, char *const *argv)
1030 {
1031 const struct command *cmd;
1032 int f;
1033 static const struct option options[] =
1034 {
1035 {.name = "help", .val = 'h'},
1036 {.name = "version", .val = 'V'},
1037 {0}
1038 };
1039
1040 while(EOF != (f = getopt_long(argc, argv, "Vh", options, NULL)))
1041 switch(f)
1042 {
1043 case 'h':
1044 help();
1045 return 0;
1046 case 'V':
1047 printf("%s\n", PACKAGE_VERSION(VERSION, BUILD));
1048 return 0;
1049 default:
1050 fprintf(stderr, "Unknown option '%c'\n", f);
1051 goto help;
1052 }
1053
1054 if(argc == optind)
1055 goto help;
1056
1057 if(ctl_client_init())
1058 {
1059 fprintf(stderr, "can't setup control connection\n");
1060 return 1;
1061 }
1062
1063 argc -= optind;
1064 argv += optind;
1065 if(NULL == (cmd = command_lookup(argv[0])))
1066 {
1067 fprintf(stderr, "never heard of command [%s]\n", argv[0]);
1068 goto help;
1069 }
1070
1071 if(argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1)
1072 {
1073 printf("Incorrect number of arguments for command\n");
1074 printf("Usage: mstpctl %s %s\n %s\n",
1075 cmd->name, cmd->format, cmd->help);
1076 return 1;
1077 }
1078
1079 return cmd->func(argc, argv);
1080
1081 help:
1082 help();
1083 return 1;
1084 }
1085
1086 /* Implementation of client-side functions */
1087 CLIENT_SIDE_FUNCTION(get_cist_bridge_status)
1088 CLIENT_SIDE_FUNCTION(get_msti_bridge_status)
1089 CLIENT_SIDE_FUNCTION(set_cist_bridge_config)
1090 CLIENT_SIDE_FUNCTION(set_msti_bridge_config)
1091 CLIENT_SIDE_FUNCTION(get_cist_port_status)
1092 CLIENT_SIDE_FUNCTION(get_msti_port_status)
1093 CLIENT_SIDE_FUNCTION(set_cist_port_config)
1094 CLIENT_SIDE_FUNCTION(set_msti_port_config)
1095 CLIENT_SIDE_FUNCTION(port_mcheck)
1096 CLIENT_SIDE_FUNCTION(set_debug_level)
1097 CLIENT_SIDE_FUNCTION(get_mstilist)
1098 CLIENT_SIDE_FUNCTION(create_msti)
1099 CLIENT_SIDE_FUNCTION(delete_msti)
1100 CLIENT_SIDE_FUNCTION(get_mstconfid)
1101 CLIENT_SIDE_FUNCTION(set_mstconfid)
1102 CLIENT_SIDE_FUNCTION(get_vids2fids)
1103 CLIENT_SIDE_FUNCTION(get_fids2mstids)
1104 CLIENT_SIDE_FUNCTION(set_vid2fid)
1105 CLIENT_SIDE_FUNCTION(set_fid2mstid)
1106 CLIENT_SIDE_FUNCTION(set_vids2fids)
1107 CLIENT_SIDE_FUNCTION(set_fids2mstids)
1108
1109 /*********************** Logging *********************/
1110
1111 void Dprintf(int level, const char *fmt, ...)
1112 {
1113 char logbuf[LOG_STRING_LEN];
1114 logbuf[sizeof(logbuf) - 1] = 0;
1115 va_list ap;
1116 va_start(ap, fmt);
1117 vsnprintf(logbuf, sizeof(logbuf) - 1, fmt, ap);
1118 va_end(ap);
1119 printf("%s\n", logbuf);
1120 }