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