]>
Commit | Line | Data |
---|---|---|
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> | |
76eb7423 | 19 | #include <limits.h> |
ad02a0eb SH |
20 | |
21 | #include "ctl_socket_client.h" | |
22 | #include "ctl_functions.h" | |
23 | ||
24 | #ifndef False | |
25 | # define False 0 | |
26 | # define True 1 | |
27 | #endif | |
28 | ||
29 | #define STP_IN_get_error_explanation CTL_error_explanation | |
30 | ||
11904a35 SH |
31 | static void print_bridge_id(UID_BRIDGE_ID_T * bridge_id, unsigned char cr) |
32 | { | |
33 | printf("%04lX-%02x%02x%02x%02x%02x%02x", | |
34 | (unsigned long)bridge_id->prio, | |
35 | (unsigned char)bridge_id->addr[0], | |
36 | (unsigned char)bridge_id->addr[1], | |
37 | (unsigned char)bridge_id->addr[2], | |
38 | (unsigned char)bridge_id->addr[3], | |
39 | (unsigned char)bridge_id->addr[4], | |
40 | (unsigned char)bridge_id->addr[5]); | |
41 | if (cr) | |
42 | printf("\n"); | |
43 | } | |
44 | ||
45 | static char *stp_state2str(RSTP_PORT_STATE stp_port_state, int detail) | |
46 | { | |
47 | if (detail) { | |
48 | switch (stp_port_state) { | |
49 | case UID_PORT_DISABLED: | |
50 | return "Disabled"; | |
51 | case UID_PORT_DISCARDING: | |
52 | return "Discarding"; | |
53 | case UID_PORT_LEARNING: | |
54 | return "Learning"; | |
55 | case UID_PORT_FORWARDING: | |
56 | return "Forwarding"; | |
57 | case UID_PORT_NON_STP: | |
58 | return "NoStp"; | |
59 | default: | |
60 | return "Unknown"; | |
61 | } | |
62 | } | |
63 | ||
64 | switch (stp_port_state) { | |
65 | case UID_PORT_DISABLED: | |
66 | return "Dis"; | |
67 | case UID_PORT_DISCARDING: | |
68 | return "Blk"; | |
69 | case UID_PORT_LEARNING: | |
70 | return "Lrn"; | |
71 | case UID_PORT_FORWARDING: | |
72 | return "Fwd"; | |
73 | case UID_PORT_NON_STP: | |
74 | return "Non"; | |
75 | default: | |
76 | return "Unk"; | |
77 | } | |
78 | } | |
79 | ||
80 | static void CLI_out_port_id(int port, unsigned char cr) | |
81 | { | |
82 | static char ifname[IFNAMSIZ]; | |
83 | if (if_indextoname(port, ifname)) | |
84 | printf("%s", ifname); | |
85 | else | |
86 | printf("Ifindex %02d", port); | |
87 | if (cr) | |
88 | printf("\n"); | |
ad02a0eb SH |
89 | } |
90 | ||
91 | int get_index_die(const char *ifname, const char *doc, int die) | |
92 | { | |
11904a35 SH |
93 | int r = if_nametoindex(ifname); |
94 | if (r == 0) { | |
95 | fprintf(stderr, | |
96 | "Can't find index for %s %s. Not a valid interface.\n", | |
97 | doc, ifname); | |
98 | if (die) | |
99 | exit(1); | |
100 | return -1; | |
101 | } | |
102 | return r; | |
ad02a0eb SH |
103 | } |
104 | ||
105 | int get_index(const char *ifname, const char *doc) | |
106 | { | |
11904a35 | 107 | return get_index_die(ifname, doc, 1); |
ad02a0eb SH |
108 | } |
109 | ||
11904a35 | 110 | static int cmd_rstp(int argc, char *const *argv) |
ad02a0eb | 111 | { |
11904a35 SH |
112 | int stp, r; |
113 | int br_index = get_index(argv[1], "bridge"); | |
ad02a0eb | 114 | |
11904a35 SH |
115 | if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes") |
116 | || !strcmp(argv[2], "1")) | |
117 | stp = 1; | |
118 | else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no") | |
119 | || !strcmp(argv[2], "0")) | |
120 | stp = 0; | |
121 | else { | |
122 | fprintf(stderr, "expect on/off for argument\n"); | |
123 | return 1; | |
124 | } | |
125 | r = CTL_enable_bridge_rstp(br_index, stp); | |
126 | if (r) { | |
127 | fprintf(stderr, "Failed to enable/disable RSTP: %s\n", | |
128 | CTL_error_explanation(r)); | |
129 | return -1; | |
130 | } | |
131 | return 0; | |
ad02a0eb SH |
132 | } |
133 | ||
134 | static int do_showbridge(const char *br_name) | |
135 | { | |
11904a35 SH |
136 | UID_STP_STATE_T uid_state; |
137 | UID_STP_CFG_T uid_cfg; | |
138 | ||
139 | int br_index = get_index_die(br_name, "bridge", 0); | |
140 | if (br_index < 0) | |
141 | return -1; | |
142 | ||
143 | int r = CTL_get_bridge_state(br_index, &uid_cfg, &uid_state); | |
144 | if (r) { | |
145 | fprintf(stderr, "Failed to get bridge state: %s\n", | |
146 | CTL_error_explanation(r)); | |
147 | return -1; | |
148 | } | |
ad02a0eb | 149 | #if 0 |
11904a35 SH |
150 | printf("Interface: %-7s (tag:%d) State: ", |
151 | uid_state.vlan_name, (int)uid_state.vlan_id); | |
ad02a0eb | 152 | #else |
11904a35 SH |
153 | printf("Bridge: %-7s State:", |
154 | uid_state.vlan_name); | |
ad02a0eb | 155 | #endif |
11904a35 SH |
156 | switch (uid_state.stp_enabled) { |
157 | case STP_ENABLED: | |
158 | printf("enabled\n"); | |
159 | break; | |
160 | case STP_DISABLED: | |
161 | printf("disabled\n"); | |
162 | break; | |
163 | default: | |
164 | printf("unknown\n"); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | printf("BridgeId: "); | |
169 | print_bridge_id(&uid_state.bridge_id, 0); | |
170 | printf(" Bridge Proirity: %lu (0x%lX)\n", | |
171 | (unsigned long)uid_state.bridge_id.prio, | |
172 | (unsigned long)uid_state.bridge_id.prio); | |
173 | if (uid_cfg.force_version < 2) | |
174 | printf("Force Version: stp\n"); | |
175 | ||
176 | printf("Designated Root: "); | |
177 | print_bridge_id(&uid_state.designated_root, 1); | |
178 | if (uid_state.root_port) { | |
179 | printf("Root Port: %04lx", | |
180 | (unsigned long)uid_state.root_port); | |
181 | // CLI_out_port_id (uid_state.root_port & 0xfff, False); | |
182 | // printf("not implemented"); // XXX | |
183 | printf(", Root Cost: %-lu\n", | |
184 | (unsigned long)uid_state.root_path_cost); | |
185 | } else { | |
186 | printf("Root Port: none\n"); | |
187 | } | |
188 | ||
189 | if (uid_state.Topo_Change) | |
190 | printf("Topology Change Count: %lu\n", | |
191 | uid_state.Topo_Change_Count); | |
192 | else | |
193 | printf("Time Since Topology Change: %lu\n", | |
194 | uid_state.timeSince_Topo_Change); | |
195 | ||
196 | printf("Max Age: %2d Bridge Max Age: %-2d\n", | |
197 | (int)uid_state.max_age, (int)uid_cfg.max_age); | |
198 | printf("Hello Time: %2d Bridge Hello Time: %-2d\n", | |
199 | (int)uid_state.hello_time, (int)uid_cfg.hello_time); | |
200 | printf("Forward Delay: %2d Bridge Forward Delay: %-2d\n", | |
201 | (int)uid_state.forward_delay, (int)uid_cfg.forward_delay); | |
202 | printf("Hold Time: %2d\n", (int)uid_cfg.hold_time); | |
203 | ||
204 | return 0; | |
ad02a0eb SH |
205 | } |
206 | ||
207 | #define SYSFS_PATH_MAX 256 | |
208 | #define SYSFS_CLASS_NET "/sys/class/net" | |
209 | ||
210 | static int isbridge(const struct dirent *entry) | |
211 | { | |
11904a35 SH |
212 | char path[SYSFS_PATH_MAX]; |
213 | struct stat st; | |
214 | ||
215 | snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge", | |
216 | entry->d_name); | |
217 | return stat(path, &st) == 0 && S_ISDIR(st.st_mode); | |
218 | } | |
219 | ||
220 | static int cmd_showbridge(int argc, char *const *argv) | |
221 | { | |
222 | int i, count = 0; | |
223 | int r = 0; | |
224 | struct dirent **namelist; | |
225 | ||
226 | if (argc > 1) { | |
227 | count = argc - 1; | |
228 | } else { | |
229 | count = | |
230 | scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort); | |
231 | if (count < 0) { | |
232 | fprintf(stderr, "Error getting list of all bridges\n"); | |
233 | return -1; | |
234 | } | |
235 | } | |
236 | ||
237 | for (i = 0; i < count; i++) { | |
238 | const char *name; | |
239 | if (argc > 1) | |
240 | name = argv[i + 1]; | |
241 | else | |
242 | name = namelist[i]->d_name; | |
243 | ||
244 | int err = do_showbridge(name); | |
245 | if (err) | |
246 | r = err; | |
247 | } | |
248 | ||
249 | if (argc <= 1) { | |
250 | for (i = 0; i < count; i++) | |
251 | free(namelist[i]); | |
252 | free(namelist); | |
253 | } | |
254 | ||
255 | return r; | |
ad02a0eb SH |
256 | } |
257 | ||
258 | int detail = 0; | |
259 | ||
260 | static int do_showport(int br_index, const char *port_name, | |
11904a35 SH |
261 | UID_STP_STATE_T * uid_state) |
262 | { | |
263 | UID_STP_PORT_STATE_T uid_port; | |
264 | UID_STP_PORT_CFG_T uid_cfg; | |
265 | int r = 0; | |
266 | int port_index = get_index_die(port_name, "port", 0); | |
267 | if (port_index < 0) | |
268 | return -1; | |
269 | ||
270 | memset(&uid_cfg, 0, sizeof(UID_STP_PORT_CFG_T)); | |
271 | r = CTL_get_port_state(br_index, port_index, &uid_cfg, &uid_port); | |
272 | if (r) { | |
273 | fprintf(stderr, "Failed to get port state for port %d: %s\n", | |
274 | port_index, CTL_error_explanation(r)); | |
275 | return -1; | |
276 | } | |
277 | ||
278 | if (detail) { | |
279 | printf("Stp Port "); | |
280 | CLI_out_port_id(port_index, False); | |
ad02a0eb | 281 | #if 0 |
11904a35 SH |
282 | printf(": PortId: %04lx in vlan '%s' with tag %d:\n", |
283 | (unsigned long)uid_port.port_id, uid_state->vlan_name, | |
284 | (int)uid_state->vlan_id); | |
ad02a0eb | 285 | #else |
11904a35 SH |
286 | printf(": PortId: %04lx in Bridge '%s':\n", |
287 | (unsigned long)uid_port.port_id, uid_state->vlan_name); | |
ad02a0eb | 288 | #endif |
11904a35 SH |
289 | printf("Priority: %-d\n", |
290 | (int)(uid_port.port_id >> 8)); | |
291 | printf("State: %-16s", | |
292 | stp_state2str(uid_port.state, 1)); | |
293 | printf(" Uptime: %-9lu\n", uid_port.uptime); | |
294 | printf("PortPathCost: admin: "); | |
295 | if (ADMIN_PORT_PATH_COST_AUTO == uid_cfg.admin_port_path_cost) | |
296 | printf("%-9s", "Auto"); | |
297 | else | |
298 | printf("%-9lu", uid_cfg.admin_port_path_cost); | |
299 | printf(" oper: %-9lu\n", uid_port.oper_port_path_cost); | |
300 | ||
301 | printf("Point2Point: admin: "); | |
302 | switch (uid_cfg.admin_point2point) { | |
303 | case P2P_FORCE_TRUE: | |
304 | printf("%-9s", "ForceYes"); | |
305 | break; | |
306 | case P2P_FORCE_FALSE: | |
307 | printf("%-9s", "ForceNo"); | |
308 | break; | |
309 | case P2P_AUTO: | |
310 | printf("%-9s", "Auto"); | |
311 | break; | |
312 | } | |
313 | printf(" oper: %-9s\n", | |
314 | uid_port.oper_point2point ? "Yes" : "No"); | |
315 | printf("Edge: admin: %-9s oper: %-9s\n", | |
316 | uid_cfg.admin_edge ? "Y" : "N", | |
317 | uid_port.oper_edge ? "Y" : "N"); | |
318 | printf("Partner: oper: %-9s\n", | |
319 | uid_port.oper_stp_neigb ? "Slow" : "Rapid"); | |
320 | ||
321 | if (' ' != uid_port.role) { | |
322 | if ('-' != uid_port.role) { | |
323 | printf("PathCost: %-lu\n", | |
324 | (unsigned long)(uid_port.path_cost)); | |
325 | printf("Designated Root: "); | |
326 | print_bridge_id(&uid_port.designated_root, 1); | |
327 | printf("Designated Cost: %-ld\n", | |
328 | (unsigned long)uid_port.designated_cost); | |
329 | printf("Designated Bridge: "); | |
330 | print_bridge_id(&uid_port.designated_bridge, 1); | |
331 | printf("Designated Port: %-4lx\n\r", | |
332 | (unsigned long)uid_port.designated_port); | |
333 | } | |
334 | printf("Role: "); | |
335 | switch (uid_port.role) { | |
336 | case 'A': | |
337 | printf("Alternate\n"); | |
338 | break; | |
339 | case 'B': | |
340 | printf("Backup\n"); | |
341 | break; | |
342 | case 'R': | |
343 | printf("Root\n"); | |
344 | break; | |
345 | case 'D': | |
346 | printf("Designated\n"); | |
347 | break; | |
348 | case '-': | |
349 | printf("NonStp\n"); | |
350 | break; | |
351 | default: | |
352 | printf("Unknown(%c)\n", uid_port.role); | |
353 | break; | |
354 | } | |
355 | ||
356 | if ('R' == uid_port.role || 'D' == uid_port.role) { | |
357 | /* printf("Tc: %c ", uid_port.tc ? 'Y' : 'n'); */ | |
358 | printf("TcAck: %c ", | |
359 | uid_port.top_change_ack ? 'Y' : 'N'); | |
360 | printf("TcWhile: %3d\n", | |
361 | (int)uid_port.tcWhile); | |
362 | } | |
363 | } | |
364 | ||
365 | if (UID_PORT_DISABLED == uid_port.state || '-' == uid_port.role) { | |
ad02a0eb | 366 | #if 0 |
11904a35 SH |
367 | printf("helloWhen: %3d ", |
368 | (int)uid_port.helloWhen); | |
369 | printf("lnkWhile: %3d\n", (int)uid_port.lnkWhile); | |
370 | printf("fdWhile: %3d\n", (int)uid_port.fdWhile); | |
ad02a0eb | 371 | #endif |
11904a35 SH |
372 | } else if ('-' != uid_port.role) { |
373 | printf("fdWhile: %3d ", (int)uid_port.fdWhile); | |
374 | printf("rcvdInfoWhile: %3d\n", | |
375 | (int)uid_port.rcvdInfoWhile); | |
376 | printf("rbWhile: %3d ", (int)uid_port.rbWhile); | |
377 | printf("rrWhile: %3d\n", (int)uid_port.rrWhile); | |
ad02a0eb | 378 | #if 0 |
11904a35 SH |
379 | printf("mdelayWhile: %3d ", |
380 | (int)uid_port.mdelayWhile); | |
381 | printf("lnkWhile: %3d\n", (int)uid_port.lnkWhile); | |
382 | printf("helloWhen: %3d ", | |
383 | (int)uid_port.helloWhen); | |
384 | printf("txCount: %3d\n", (int)uid_port.txCount); | |
ad02a0eb | 385 | #endif |
11904a35 SH |
386 | } |
387 | ||
388 | printf("RSTP BPDU rx: %lu\n", | |
389 | (unsigned long)uid_port.rx_rstp_bpdu_cnt); | |
390 | printf("CONFIG BPDU rx: %lu\n", | |
391 | (unsigned long)uid_port.rx_cfg_bpdu_cnt); | |
392 | printf("TCN BPDU rx: %lu\n", | |
393 | (unsigned long)uid_port.rx_tcn_bpdu_cnt); | |
394 | } else { | |
395 | printf("%c%c%c ", | |
396 | (uid_port.oper_point2point) ? ' ' : '*', | |
397 | (uid_port.oper_edge) ? 'E' : ' ', | |
398 | (uid_port.oper_stp_neigb) ? 's' : ' '); | |
399 | CLI_out_port_id(port_index, False); | |
400 | printf(" %04lx %3s ", (unsigned long)uid_port.port_id, | |
401 | stp_state2str(uid_port.state, 0)); | |
402 | printf(" "); | |
403 | print_bridge_id(&uid_port.designated_root, 0); | |
404 | printf(" "); | |
405 | print_bridge_id(&uid_port.designated_bridge, 0); | |
406 | printf(" %4lx %c", (unsigned long)uid_port.designated_port, | |
407 | uid_port.role); | |
408 | printf("\n"); | |
409 | } | |
410 | return 0; | |
ad02a0eb SH |
411 | } |
412 | ||
413 | static int not_dot_dotdot(const struct dirent *entry) | |
414 | { | |
11904a35 SH |
415 | const char *n = entry->d_name; |
416 | ||
417 | return !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0))); | |
ad02a0eb SH |
418 | } |
419 | ||
11904a35 | 420 | static int cmd_showport(int argc, char *const *argv) |
ad02a0eb | 421 | { |
11904a35 SH |
422 | UID_STP_STATE_T uid_state; |
423 | UID_STP_CFG_T uid_br_cfg; | |
424 | int r = 0; | |
425 | ||
426 | int br_index = get_index(argv[1], "bridge"); | |
427 | ||
428 | r = CTL_get_bridge_state(br_index, &uid_br_cfg, &uid_state); | |
429 | if (r) { | |
430 | fprintf(stderr, "Failed to get bridge state: %s\n", | |
431 | CTL_error_explanation(r)); | |
432 | return -1; | |
433 | } | |
434 | ||
435 | int i, count = 0; | |
436 | struct dirent **namelist; | |
437 | ||
438 | if (argc > 2) { | |
439 | count = argc - 2; | |
440 | } else { | |
441 | char buf[SYSFS_PATH_MAX]; | |
442 | snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]); | |
443 | count = scandir(buf, &namelist, not_dot_dotdot, alphasort); | |
444 | if (count < 0) { | |
445 | fprintf(stderr, | |
446 | "Error getting list of all ports of bridge %s\n", | |
447 | argv[1]); | |
448 | return -1; | |
449 | } | |
450 | } | |
451 | ||
452 | for (i = 0; i < count; i++) { | |
453 | const char *name; | |
454 | if (argc > 2) | |
455 | name = argv[i + 2]; | |
456 | else | |
457 | name = namelist[i]->d_name; | |
458 | ||
459 | int err = do_showport(br_index, name, &uid_state); | |
460 | if (err) | |
461 | r = err; | |
462 | } | |
463 | ||
464 | if (argc <= 2) { | |
465 | for (i = 0; i < count; i++) | |
466 | free(namelist[i]); | |
467 | free(namelist); | |
468 | } | |
469 | ||
470 | return r; | |
ad02a0eb SH |
471 | } |
472 | ||
11904a35 | 473 | static int cmd_showportdetail(int argc, char *const *argv) |
ad02a0eb | 474 | { |
11904a35 SH |
475 | detail = 1; |
476 | return cmd_showport(argc, argv); | |
ad02a0eb SH |
477 | } |
478 | ||
11904a35 | 479 | unsigned int getuint(const char *s) |
ad02a0eb | 480 | { |
11904a35 SH |
481 | char *end; |
482 | long l; | |
483 | l = strtoul(s, &end, 0); | |
484 | if (*s == 0 || *end != 0 || l > INT_MAX) { | |
485 | fprintf(stderr, "Invalid unsigned int arg %s\n", s); | |
486 | exit(1); | |
487 | } | |
488 | return l; | |
ad02a0eb SH |
489 | } |
490 | ||
11904a35 | 491 | int getenum(const char *s, const char *opt[]) |
ad02a0eb | 492 | { |
11904a35 SH |
493 | int i; |
494 | for (i = 0; opt[i] != NULL; i++) | |
495 | if (strcmp(s, opt[i]) == 0) | |
496 | return i; | |
497 | ||
498 | fprintf(stderr, "Invalid argument %s: expecting one of ", s); | |
499 | for (i = 0; opt[i] != NULL; i++) | |
500 | fprintf(stderr, "%s%s", opt[i], (opt[i + 1] ? ", " : "\n")); | |
501 | ||
502 | exit(1); | |
503 | } | |
504 | ||
505 | int getyesno(const char *s, const char *yes, const char *no) | |
506 | { | |
507 | /* Reverse yes and no so error message looks more normal */ | |
508 | const char *opt[] = { yes, no, NULL }; | |
509 | return 1 - getenum(s, opt); | |
510 | } | |
511 | ||
512 | static int set_bridge_cfg_value(int br_index, unsigned long value, | |
513 | unsigned long val_mask) | |
514 | { | |
515 | UID_STP_CFG_T uid_cfg; | |
516 | char *val_name; | |
517 | int rc; | |
518 | ||
519 | uid_cfg.field_mask = val_mask; | |
520 | switch (val_mask) { | |
521 | case BR_CFG_STATE: | |
522 | uid_cfg.stp_enabled = value; | |
523 | val_name = "state"; | |
524 | break; | |
525 | case BR_CFG_PRIO: | |
526 | uid_cfg.bridge_priority = value; | |
527 | val_name = "priority"; | |
528 | break; | |
529 | case BR_CFG_AGE: | |
530 | uid_cfg.max_age = value; | |
531 | val_name = "max_age"; | |
532 | break; | |
533 | case BR_CFG_HELLO: | |
534 | uid_cfg.hello_time = value; | |
535 | val_name = "hello_time"; | |
536 | break; | |
537 | case BR_CFG_DELAY: | |
538 | uid_cfg.forward_delay = value; | |
539 | val_name = "forward_delay"; | |
540 | break; | |
541 | case BR_CFG_FORCE_VER: | |
542 | uid_cfg.force_version = value; | |
543 | val_name = "force_version"; | |
544 | break; | |
545 | case BR_CFG_AGE_MODE: | |
546 | case BR_CFG_AGE_TIME: | |
547 | default: | |
548 | printf("Invalid value mask 0X%lx\n", val_mask); | |
549 | return -1; | |
550 | break; | |
551 | } | |
ad02a0eb | 552 | |
11904a35 | 553 | rc = CTL_set_bridge_config(br_index, &uid_cfg); |
ad02a0eb | 554 | |
11904a35 SH |
555 | if (0 != rc) { |
556 | printf("Can't change rstp bridge %s:%s\n", val_name, | |
557 | STP_IN_get_error_explanation(rc)); | |
558 | return -1; | |
559 | } | |
560 | return 0; | |
ad02a0eb SH |
561 | } |
562 | ||
11904a35 | 563 | static int cmd_setbridgestate(int argc, char *const *argv) |
ad02a0eb SH |
564 | { |
565 | ||
11904a35 SH |
566 | int br_index = get_index(argv[1], "bridge"); |
567 | return set_bridge_cfg_value(br_index, | |
568 | getyesno(argv[2], "on", "off"), | |
569 | BR_CFG_STATE); | |
ad02a0eb SH |
570 | } |
571 | ||
11904a35 | 572 | static int cmd_setbridgeprio(int argc, char *const *argv) |
ad02a0eb SH |
573 | { |
574 | ||
11904a35 SH |
575 | int br_index = get_index(argv[1], "bridge"); |
576 | return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_PRIO); | |
ad02a0eb SH |
577 | } |
578 | ||
11904a35 | 579 | static int cmd_setbridgemaxage(int argc, char *const *argv) |
ad02a0eb SH |
580 | { |
581 | ||
11904a35 SH |
582 | int br_index = get_index(argv[1], "bridge"); |
583 | return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_AGE); | |
ad02a0eb SH |
584 | } |
585 | ||
11904a35 | 586 | static int cmd_setbridgehello(int argc, char *const *argv) |
ad02a0eb SH |
587 | { |
588 | ||
11904a35 SH |
589 | int br_index = get_index(argv[1], "bridge"); |
590 | return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_HELLO); | |
ad02a0eb SH |
591 | } |
592 | ||
11904a35 | 593 | static int cmd_setbridgefdelay(int argc, char *const *argv) |
ad02a0eb SH |
594 | { |
595 | ||
11904a35 SH |
596 | int br_index = get_index(argv[1], "bridge"); |
597 | return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_DELAY); | |
ad02a0eb SH |
598 | } |
599 | ||
11904a35 | 600 | static int cmd_setbridgeforcevers(int argc, char *const *argv) |
ad02a0eb SH |
601 | { |
602 | ||
11904a35 SH |
603 | int br_index = get_index(argv[1], "bridge"); |
604 | return set_bridge_cfg_value(br_index, | |
605 | 2 * getyesno(argv[2], "normal", "slow"), | |
606 | BR_CFG_FORCE_VER); | |
ad02a0eb SH |
607 | } |
608 | ||
ad02a0eb | 609 | static int |
11904a35 SH |
610 | set_port_cfg_value(int br_index, int port_index, |
611 | unsigned long value, unsigned long val_mask) | |
612 | { | |
613 | UID_STP_PORT_CFG_T uid_cfg; | |
614 | int rc; | |
615 | char *val_name; | |
616 | ||
617 | BitmapClear(&uid_cfg.port_bmp); | |
618 | uid_cfg.field_mask = val_mask; | |
619 | switch (val_mask) { | |
620 | case PT_CFG_MCHECK: | |
621 | val_name = "mcheck"; | |
622 | break; | |
623 | case PT_CFG_COST: | |
624 | uid_cfg.admin_port_path_cost = value; | |
625 | val_name = "path cost"; | |
626 | break; | |
627 | case PT_CFG_PRIO: | |
628 | uid_cfg.port_priority = value; | |
629 | val_name = "priority"; | |
630 | break; | |
631 | case PT_CFG_P2P: | |
632 | uid_cfg.admin_point2point = (ADMIN_P2P_T) value; | |
633 | val_name = "p2p flag"; | |
634 | break; | |
635 | case PT_CFG_EDGE: | |
636 | uid_cfg.admin_edge = value; | |
637 | val_name = "adminEdge"; | |
638 | break; | |
639 | case PT_CFG_NON_STP: | |
640 | uid_cfg.admin_non_stp = value; | |
641 | val_name = "adminNonStp"; | |
642 | break; | |
ad02a0eb | 643 | #ifdef STP_DBG |
11904a35 SH |
644 | case PT_CFG_DBG_SKIP_TX: |
645 | uid_cfg.skip_tx = value; | |
646 | val_name = "skip tx"; | |
647 | break; | |
648 | case PT_CFG_DBG_SKIP_RX: | |
649 | uid_cfg.skip_rx = value; | |
650 | val_name = "skip rx"; | |
651 | break; | |
ad02a0eb | 652 | #endif |
11904a35 SH |
653 | case PT_CFG_STATE: |
654 | default: | |
655 | printf("Invalid value mask 0X%lx\n", val_mask); | |
656 | return -1; | |
657 | } | |
ad02a0eb | 658 | |
11904a35 | 659 | rc = CTL_set_port_config(br_index, port_index, &uid_cfg); |
ad02a0eb | 660 | |
11904a35 SH |
661 | if (0 != rc) { |
662 | printf("can't change rstp port[s] %s: %s\n", | |
663 | val_name, STP_IN_get_error_explanation(rc)); | |
664 | return -1; | |
665 | } | |
666 | return 0; | |
ad02a0eb SH |
667 | } |
668 | ||
11904a35 | 669 | static int cmd_setportprio(int argc, char *const *argv) |
ad02a0eb SH |
670 | { |
671 | ||
11904a35 SH |
672 | int br_index = get_index(argv[1], "bridge"); |
673 | int port_index = get_index(argv[2], "port"); | |
674 | return set_port_cfg_value(br_index, port_index, | |
675 | getuint(argv[3]), PT_CFG_PRIO); | |
ad02a0eb SH |
676 | } |
677 | ||
11904a35 | 678 | static int cmd_setportpathcost(int argc, char *const *argv) |
ad02a0eb SH |
679 | { |
680 | ||
11904a35 SH |
681 | int br_index = get_index(argv[1], "bridge"); |
682 | int port_index = get_index(argv[2], "port"); | |
683 | return set_port_cfg_value(br_index, port_index, | |
684 | getuint(argv[3]), PT_CFG_COST); | |
ad02a0eb SH |
685 | } |
686 | ||
11904a35 | 687 | static int cmd_setportedge(int argc, char *const *argv) |
ad02a0eb SH |
688 | { |
689 | ||
11904a35 SH |
690 | int br_index = get_index(argv[1], "bridge"); |
691 | int port_index = get_index(argv[2], "port"); | |
692 | return set_port_cfg_value(br_index, port_index, | |
693 | getyesno(argv[3], "yes", "no"), PT_CFG_EDGE); | |
ad02a0eb SH |
694 | } |
695 | ||
11904a35 | 696 | static int cmd_setportnonstp(int argc, char *const *argv) |
ad02a0eb SH |
697 | { |
698 | ||
11904a35 SH |
699 | int br_index = get_index(argv[1], "bridge"); |
700 | int port_index = get_index(argv[2], "port"); | |
701 | return set_port_cfg_value(br_index, port_index, | |
702 | getyesno(argv[3], "yes", "no"), | |
703 | PT_CFG_NON_STP); | |
ad02a0eb SH |
704 | } |
705 | ||
11904a35 | 706 | static int cmd_setportp2p(int argc, char *const *argv) |
ad02a0eb SH |
707 | { |
708 | ||
11904a35 SH |
709 | int br_index = get_index(argv[1], "bridge"); |
710 | int port_index = get_index(argv[2], "port"); | |
711 | const char *opts[] = { "yes", "no", "auto", NULL }; | |
712 | int vals[] = { P2P_FORCE_TRUE, P2P_FORCE_FALSE, P2P_AUTO }; | |
ad02a0eb | 713 | |
11904a35 SH |
714 | return set_port_cfg_value(br_index, port_index, |
715 | vals[getenum(argv[3], opts)], PT_CFG_P2P); | |
ad02a0eb SH |
716 | } |
717 | ||
11904a35 | 718 | static int cmd_portmcheck(int argc, char *const *argv) |
ad02a0eb SH |
719 | { |
720 | ||
11904a35 SH |
721 | int br_index = get_index(argv[1], "bridge"); |
722 | int port_index = get_index(argv[2], "port"); | |
723 | return set_port_cfg_value(br_index, port_index, 0, PT_CFG_MCHECK); | |
ad02a0eb SH |
724 | } |
725 | ||
11904a35 | 726 | static int cmd_debuglevel(int argc, char *const *argv) |
ad02a0eb | 727 | { |
11904a35 | 728 | return CTL_set_debug_level(getuint(argv[1])); |
ad02a0eb SH |
729 | } |
730 | ||
11904a35 SH |
731 | struct command { |
732 | int nargs; | |
733 | int optargs; | |
734 | const char *name; | |
735 | int (*func) (int argc, char *const *argv); | |
736 | const char *help; | |
ad02a0eb SH |
737 | }; |
738 | ||
739 | static const struct command commands[] = { | |
11904a35 SH |
740 | {0, 32, "showbridge", cmd_showbridge, |
741 | "[<bridge> ... ]\t\tshow bridge state"}, | |
742 | {1, 32, "showport", cmd_showport, | |
743 | "<bridge> [<port> ... ]\tshow port state"}, | |
744 | {1, 32, "showportdetail", cmd_showportdetail, | |
745 | "<bridge> [<port> ... ]\tshow port state (detail)"}, | |
746 | {2, 0, "rstp", cmd_rstp, | |
747 | "<bridge> {on|off}\tenable/disable rstpd control"}, | |
748 | {2, 0, "setbridgestate", cmd_setbridgestate, | |
749 | "<bridge> {on|off}\tstart/stop rstp (when enabled)"}, | |
750 | {2, 0, "setbridgeprio", cmd_setbridgeprio, | |
751 | "<bridge> <priority>\tset bridge priority (0-61440)"}, | |
752 | {2, 0, "sethello", cmd_setbridgehello, | |
753 | "<bridge> <hellotime>\tset bridge hello time (1-10)"}, | |
754 | {2, 0, "setmaxage", cmd_setbridgemaxage, | |
755 | "<bridge> <maxage>\tset bridge max age (6-40)"}, | |
756 | {2, 0, "setfdelay", cmd_setbridgefdelay, | |
757 | "<bridge> <fwd_delay>\tset bridge forward delay (4-30)"}, | |
758 | {2, 0, "setforcevers", cmd_setbridgeforcevers, | |
759 | "<bridge> {normal|slow}\tnormal RSTP or force to STP"}, | |
760 | {3, 0, "setportprio", cmd_setportprio, | |
761 | "<bridge> <port> <priority>\tset port priority (0-240)"}, | |
762 | {3, 0, "setportpathcost", cmd_setportpathcost, | |
763 | "<bridge> <port> <cost>\tset port path cost"}, | |
764 | {3, 0, "setportedge", cmd_setportedge, | |
765 | "<bridge> <port> {yes|no}\tconfigure if it is an edge port"}, | |
766 | {3, 0, "setportnonstp", cmd_setportnonstp, | |
767 | "<bridge> <port> {yes|no}\tdisable STP for the port"}, | |
768 | {3, 0, "setportp2p", cmd_setportp2p, | |
769 | "<bridge> <port> {yes|no|auto}\tset whether p2p connection"}, | |
770 | {2, 0, "portmcheck", cmd_portmcheck, | |
771 | "<bridge> <port>\ttry to get back from STP to RSTP mode"}, | |
772 | {1, 0, "debuglevel", cmd_debuglevel, "<level>\t\tLevel of verbosity"}, | |
ad02a0eb SH |
773 | }; |
774 | ||
775 | const struct command *command_lookup(const char *cmd) | |
776 | { | |
777 | int i; | |
778 | ||
11904a35 | 779 | for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { |
ad02a0eb SH |
780 | if (!strcmp(cmd, commands[i].name)) |
781 | return &commands[i]; | |
782 | } | |
783 | ||
784 | return NULL; | |
785 | } | |
786 | ||
787 | void command_helpall(void) | |
788 | { | |
789 | int i; | |
790 | ||
11904a35 | 791 | for (i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) { |
ad02a0eb SH |
792 | printf("\t%-10s\t%s\n", commands[i].name, commands[i].help); |
793 | } | |
794 | } | |
795 | ||
796 | static void help() | |
797 | { | |
798 | printf("Usage: rstpctl [commands]\n"); | |
799 | printf("commands:\n"); | |
800 | command_helpall(); | |
801 | } | |
802 | ||
803 | #define PACKAGE_VERSION2(v, b) "rstp, " #v "-" #b | |
804 | #define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b) | |
805 | ||
11904a35 | 806 | int main(int argc, char *const *argv) |
ad02a0eb SH |
807 | { |
808 | const struct command *cmd; | |
809 | int f; | |
810 | static const struct option options[] = { | |
11904a35 SH |
811 | {.name = "help",.val = 'h'}, |
812 | {.name = "version",.val = 'V'}, | |
813 | {0} | |
ad02a0eb SH |
814 | }; |
815 | ||
11904a35 SH |
816 | while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF) |
817 | switch (f) { | |
ad02a0eb SH |
818 | case 'h': |
819 | help(); | |
820 | return 0; | |
821 | case 'V': | |
822 | printf("%s\n", PACKAGE_VERSION(VERSION, BUILD)); | |
823 | return 0; | |
824 | default: | |
825 | fprintf(stderr, "Unknown option '%c'\n", f); | |
826 | goto help; | |
827 | } | |
11904a35 | 828 | |
ad02a0eb SH |
829 | if (argc == optind) |
830 | goto help; | |
11904a35 | 831 | |
ad02a0eb SH |
832 | if (ctl_client_init()) { |
833 | fprintf(stderr, "can't setup control connection\n"); | |
834 | return 1; | |
835 | } | |
836 | ||
837 | argc -= optind; | |
838 | argv += optind; | |
839 | if ((cmd = command_lookup(argv[0])) == NULL) { | |
840 | fprintf(stderr, "never heard of command [%s]\n", argv[0]); | |
841 | goto help; | |
842 | } | |
11904a35 | 843 | |
ad02a0eb SH |
844 | if (argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1) { |
845 | printf("Incorrect number of arguments for command\n"); | |
846 | printf("Usage: rstpctl %s %s\n", cmd->name, cmd->help); | |
847 | return 1; | |
848 | } | |
849 | ||
850 | return cmd->func(argc, argv); | |
851 | ||
11904a35 | 852 | help: |
ad02a0eb SH |
853 | help(); |
854 | return 1; | |
855 | } |