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