1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011-2012 Stefan Bethke. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include <ctype.h> 31 #include <err.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sysexits.h> 38 #include <unistd.h> 39 #include <sys/types.h> 40 #include <sys/ioctl.h> 41 #include <net/if.h> 42 #include <net/if_media.h> 43 #include <dev/etherswitch/etherswitch.h> 44 45 int get_media_subtype(int, const char *); 46 int get_media_mode(int, const char *); 47 int get_media_options(int, const char *); 48 int lookup_media_word(struct ifmedia_description *, const char *); 49 void print_media_word(int, int); 50 void print_media_word_ifconfig(int); 51 52 /* some constants */ 53 #define IEEE802DOT1Q_VID_MAX 4094 54 #define IFMEDIAREQ_NULISTENTRIES 256 55 56 enum cmdmode { 57 MODE_NONE = 0, 58 MODE_PORT, 59 MODE_CONFIG, 60 MODE_VLANGROUP, 61 MODE_REGISTER, 62 MODE_PHYREG, 63 MODE_ATU 64 }; 65 66 struct cfg { 67 int fd; 68 int verbose; 69 int mediatypes; 70 const char *controlfile; 71 etherswitch_conf_t conf; 72 etherswitch_info_t info; 73 enum cmdmode mode; 74 int unit; 75 }; 76 77 struct cmds { 78 enum cmdmode mode; 79 const char *name; 80 int args; 81 int (*f)(struct cfg *, int argc, char *argv[]); 82 }; 83 static struct cmds cmds[]; 84 85 /* Must match the ETHERSWITCH_PORT_LED_* enum order */ 86 static const char *ledstyles[] = { "default", "on", "off", "blink", NULL }; 87 88 /* 89 * Print a value a la the %b format of the kernel's printf. 90 * Stolen from ifconfig.c. 91 */ 92 static void 93 printb(const char *s, unsigned v, const char *bits) 94 { 95 int i, any = 0; 96 char c; 97 98 if (bits && *bits == 8) 99 printf("%s=%o", s, v); 100 else 101 printf("%s=%x", s, v); 102 bits++; 103 if (bits) { 104 putchar('<'); 105 while ((i = *bits++) != '\0') { 106 if (v & (1 << (i-1))) { 107 if (any) 108 putchar(','); 109 any = 1; 110 for (; (c = *bits) > 32; bits++) 111 putchar(c); 112 } else 113 for (; *bits > 32; bits++) 114 ; 115 } 116 putchar('>'); 117 } 118 } 119 120 static int 121 read_register(struct cfg *cfg, int r) 122 { 123 struct etherswitch_reg er; 124 125 er.reg = r; 126 if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0) 127 err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)"); 128 return (er.val); 129 } 130 131 static void 132 write_register(struct cfg *cfg, int r, int v) 133 { 134 struct etherswitch_reg er; 135 136 er.reg = r; 137 er.val = v; 138 if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0) 139 err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)"); 140 } 141 142 static int 143 read_phyregister(struct cfg *cfg, int phy, int reg) 144 { 145 struct etherswitch_phyreg er; 146 147 er.phy = phy; 148 er.reg = reg; 149 if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0) 150 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)"); 151 return (er.val); 152 } 153 154 static void 155 write_phyregister(struct cfg *cfg, int phy, int reg, int val) 156 { 157 struct etherswitch_phyreg er; 158 159 er.phy = phy; 160 er.reg = reg; 161 er.val = val; 162 if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0) 163 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)"); 164 } 165 166 static int 167 set_port_vid(struct cfg *cfg, int argc, char *argv[]) 168 { 169 int v; 170 etherswitch_port_t p; 171 172 if (argc < 2) 173 return (-1); 174 175 v = strtol(argv[1], NULL, 0); 176 if (v < 0 || v > IEEE802DOT1Q_VID_MAX) 177 errx(EX_USAGE, "pvid must be between 0 and %d", 178 IEEE802DOT1Q_VID_MAX); 179 bzero(&p, sizeof(p)); 180 p.es_port = cfg->unit; 181 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 182 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 183 p.es_pvid = v; 184 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 185 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 186 return (0); 187 } 188 189 static int 190 set_port_flag(struct cfg *cfg, int argc, char *argv[]) 191 { 192 char *flag; 193 int n; 194 uint32_t f; 195 etherswitch_port_t p; 196 197 if (argc < 1) 198 return (-1); 199 200 n = 0; 201 f = 0; 202 flag = argv[0]; 203 if (strcmp(flag, "none") != 0) { 204 if (*flag == '-') { 205 n++; 206 flag++; 207 } 208 if (strcasecmp(flag, "striptag") == 0) 209 f = ETHERSWITCH_PORT_STRIPTAG; 210 else if (strcasecmp(flag, "addtag") == 0) 211 f = ETHERSWITCH_PORT_ADDTAG; 212 else if (strcasecmp(flag, "firstlock") == 0) 213 f = ETHERSWITCH_PORT_FIRSTLOCK; 214 else if (strcasecmp(flag, "droptagged") == 0) 215 f = ETHERSWITCH_PORT_DROPTAGGED; 216 else if (strcasecmp(flag, "dropuntagged") == 0) 217 f = ETHERSWITCH_PORT_DROPUNTAGGED; 218 else if (strcasecmp(flag, "doubletag") == 0) 219 f = ETHERSWITCH_PORT_DOUBLE_TAG; 220 else if (strcasecmp(flag, "ingress") == 0) 221 f = ETHERSWITCH_PORT_INGRESS; 222 else if (strcasecmp(flag, "striptagingress") == 0) 223 f = ETHERSWITCH_PORT_STRIPTAGINGRESS; 224 } 225 bzero(&p, sizeof(p)); 226 p.es_port = cfg->unit; 227 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 228 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 229 if (n) 230 p.es_flags &= ~f; 231 else 232 p.es_flags |= f; 233 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 234 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 235 return (0); 236 } 237 238 static int 239 set_port_media(struct cfg *cfg, int argc, char *argv[]) 240 { 241 etherswitch_port_t p; 242 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 243 int subtype; 244 245 if (argc < 2) 246 return (-1); 247 248 bzero(&p, sizeof(p)); 249 p.es_port = cfg->unit; 250 p.es_ifmr.ifm_ulist = ifm_ulist; 251 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 252 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 253 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 254 if (p.es_ifmr.ifm_count == 0) 255 return (0); 256 subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]); 257 p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) | 258 IFM_TYPE(ifm_ulist[0]) | subtype; 259 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 260 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 261 return (0); 262 } 263 264 static int 265 set_port_mediaopt(struct cfg *cfg, int argc, char *argv[]) 266 { 267 etherswitch_port_t p; 268 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 269 int options; 270 271 if (argc < 2) 272 return (-1); 273 274 bzero(&p, sizeof(p)); 275 p.es_port = cfg->unit; 276 p.es_ifmr.ifm_ulist = ifm_ulist; 277 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 278 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 279 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 280 options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]); 281 if (options == -1) 282 errx(EX_USAGE, "invalid media options \"%s\"", argv[1]); 283 if (options & IFM_HDX) { 284 p.es_ifr.ifr_media &= ~IFM_FDX; 285 options &= ~IFM_HDX; 286 } 287 p.es_ifr.ifr_media |= options; 288 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 289 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 290 return (0); 291 } 292 293 static int 294 set_port_led(struct cfg *cfg, int argc, char *argv[]) 295 { 296 etherswitch_port_t p; 297 int led; 298 int i; 299 300 if (argc < 3) 301 return (-1); 302 303 bzero(&p, sizeof(p)); 304 p.es_port = cfg->unit; 305 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 306 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 307 308 led = strtol(argv[1], NULL, 0); 309 if (led < 1 || led > p.es_nleds) 310 errx(EX_USAGE, "invalid led number %s; must be between 1 and %d", 311 argv[1], p.es_nleds); 312 313 led--; 314 315 for (i=0; ledstyles[i] != NULL; i++) { 316 if (strcmp(argv[2], ledstyles[i]) == 0) { 317 p.es_led[led] = i; 318 break; 319 } 320 } 321 if (ledstyles[i] == NULL) 322 errx(EX_USAGE, "invalid led style \"%s\"", argv[2]); 323 324 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 325 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 326 327 return (0); 328 } 329 330 static int 331 set_vlangroup_vid(struct cfg *cfg, int argc, char *argv[]) 332 { 333 int v; 334 etherswitch_vlangroup_t vg; 335 336 if (argc < 2) 337 return (-1); 338 339 memset(&vg, 0, sizeof(vg)); 340 v = strtol(argv[1], NULL, 0); 341 if (v < 0 || v > IEEE802DOT1Q_VID_MAX) 342 errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX); 343 vg.es_vlangroup = cfg->unit; 344 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 345 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 346 vg.es_vid = v; 347 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 348 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 349 return (0); 350 } 351 352 static int 353 set_vlangroup_members(struct cfg *cfg, int argc, char *argv[]) 354 { 355 etherswitch_vlangroup_t vg; 356 int member, untagged; 357 char *c, *d; 358 int v; 359 360 if (argc < 2) 361 return (-1); 362 363 member = untagged = 0; 364 memset(&vg, 0, sizeof(vg)); 365 if (strcmp(argv[1], "none") != 0) { 366 for (c=argv[1]; *c; c=d) { 367 v = strtol(c, &d, 0); 368 if (d == c) 369 break; 370 if (v < 0 || v >= cfg->info.es_nports) 371 errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1); 372 if (d[0] == ',' || d[0] == '\0' || 373 ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) { 374 if (d[0] == 't' || d[0] == 'T') { 375 untagged &= ~ETHERSWITCH_PORTMASK(v); 376 d++; 377 } else 378 untagged |= ETHERSWITCH_PORTMASK(v); 379 member |= ETHERSWITCH_PORTMASK(v); 380 d++; 381 } else 382 errx(EX_USAGE, "Invalid members specification \"%s\"", d); 383 } 384 } 385 vg.es_vlangroup = cfg->unit; 386 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 387 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 388 vg.es_member_ports = member; 389 vg.es_untagged_ports = untagged; 390 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 391 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 392 return (0); 393 } 394 395 static int 396 set_register(struct cfg *cfg, char *arg) 397 { 398 int a, v; 399 char *c; 400 401 a = strtol(arg, &c, 0); 402 if (c==arg) 403 return (1); 404 if (*c == '=') { 405 v = strtoul(c+1, NULL, 0); 406 write_register(cfg, a, v); 407 } 408 printf("\treg 0x%04x=0x%08x\n", a, read_register(cfg, a)); 409 return (0); 410 } 411 412 static int 413 set_phyregister(struct cfg *cfg, char *arg) 414 { 415 int phy, reg, val; 416 char *c, *d; 417 418 phy = strtol(arg, &c, 0); 419 if (c==arg) 420 return (1); 421 if (*c != '.') 422 return (1); 423 d = c+1; 424 reg = strtol(d, &c, 0); 425 if (d == c) 426 return (1); 427 if (*c == '=') { 428 val = strtoul(c+1, NULL, 0); 429 write_phyregister(cfg, phy, reg, val); 430 } 431 printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); 432 return (0); 433 } 434 435 static int 436 set_vlan_mode(struct cfg *cfg, int argc, char *argv[]) 437 { 438 etherswitch_conf_t conf; 439 440 if (argc < 2) 441 return (-1); 442 443 bzero(&conf, sizeof(conf)); 444 conf.cmd = ETHERSWITCH_CONF_VLAN_MODE; 445 if (strcasecmp(argv[1], "isl") == 0) 446 conf.vlan_mode = ETHERSWITCH_VLAN_ISL; 447 else if (strcasecmp(argv[1], "port") == 0) 448 conf.vlan_mode = ETHERSWITCH_VLAN_PORT; 449 else if (strcasecmp(argv[1], "dot1q") == 0) 450 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 451 else if (strcasecmp(argv[1], "dot1q4k") == 0) 452 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q_4K; 453 else if (strcasecmp(argv[1], "qinq") == 0) 454 conf.vlan_mode = ETHERSWITCH_VLAN_DOUBLE_TAG; 455 else 456 conf.vlan_mode = 0; 457 if (ioctl(cfg->fd, IOETHERSWITCHSETCONF, &conf) != 0) 458 err(EX_OSERR, "ioctl(IOETHERSWITCHSETCONF)"); 459 460 return (0); 461 } 462 463 static int 464 atu_flush(struct cfg *cfg, int argc, char *argv[]) 465 { 466 etherswitch_portid_t p; 467 int i, r; 468 469 bzero(&p, sizeof(p)); 470 471 /* note: argv[0] is "flush" */ 472 if (argc > 2 && strcasecmp(argv[1], "port") == 0) { 473 p.es_port = atoi(argv[2]); 474 i = IOETHERSWITCHFLUSHPORT; 475 r = 3; 476 } else if (argc > 1 && strcasecmp(argv[1], "all") == 0) { 477 p.es_port = 0; 478 r = 2; 479 i = IOETHERSWITCHFLUSHALL; 480 } else { 481 fprintf(stderr, 482 "%s: invalid verb (port <x> or all) (got %s)\n", 483 __func__, argv[1]); 484 return (-1); 485 } 486 487 if (ioctl(cfg->fd, i, &p) != 0) 488 err(EX_OSERR, "ioctl(ATU flush (ioctl %d, port %d))", 489 i, p.es_port); 490 return (r); 491 } 492 493 static int 494 atu_dump(struct cfg *cfg, int argc, char *argv[]) 495 { 496 etherswitch_atu_table_t p; 497 etherswitch_atu_entry_t e; 498 uint32_t i; 499 500 (void) argc; 501 (void) argv; 502 503 /* Note: argv[0] is "dump" */ 504 bzero(&p, sizeof(p)); 505 506 if (ioctl(cfg->fd, IOETHERSWITCHGETTABLE, &p) != 0) 507 err(EX_OSERR, "ioctl(IOETHERSWITCHGETTABLE)"); 508 509 /* And now, iterate to get entries */ 510 for (i = 0; i < p.es_nitems; i++) { 511 bzero(&e, sizeof(e)); 512 e.id = i; 513 if (ioctl(cfg->fd, IOETHERSWITCHGETTABLEENTRY, &e) != 0) 514 break; 515 516 printf(" [%d] %s: portmask 0x%08x\n", i, 517 ether_ntoa((void *) &e.es_macaddr), 518 e.es_portmask); 519 } 520 521 return (1); 522 } 523 524 static void 525 print_config(struct cfg *cfg) 526 { 527 const char *c; 528 529 /* Get the device name. */ 530 c = strrchr(cfg->controlfile, '/'); 531 if (c != NULL) 532 c = c + 1; 533 else 534 c = cfg->controlfile; 535 536 /* Print VLAN mode. */ 537 if (cfg->conf.cmd & ETHERSWITCH_CONF_VLAN_MODE) { 538 printf("%s: VLAN mode: ", c); 539 switch (cfg->conf.vlan_mode) { 540 case ETHERSWITCH_VLAN_ISL: 541 printf("ISL\n"); 542 break; 543 case ETHERSWITCH_VLAN_PORT: 544 printf("PORT\n"); 545 break; 546 case ETHERSWITCH_VLAN_DOT1Q: 547 printf("DOT1Q\n"); 548 break; 549 case ETHERSWITCH_VLAN_DOT1Q_4K: 550 printf("DOT1Q4K\n"); 551 break; 552 case ETHERSWITCH_VLAN_DOUBLE_TAG: 553 printf("QinQ\n"); 554 break; 555 default: 556 printf("none\n"); 557 } 558 } 559 560 /* Print switch MAC address. */ 561 if (cfg->conf.cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) { 562 printf("%s: Switch MAC address: %s\n", 563 c, 564 ether_ntoa(&cfg->conf.switch_macaddr)); 565 } 566 } 567 568 static void 569 print_port(struct cfg *cfg, int port) 570 { 571 etherswitch_port_t p; 572 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 573 int i; 574 575 bzero(&p, sizeof(p)); 576 p.es_port = port; 577 p.es_ifmr.ifm_ulist = ifm_ulist; 578 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 579 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 580 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 581 printf("port%d:\n", port); 582 if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_DOT1Q) 583 printf("\tpvid: %d\n", p.es_pvid); 584 printb("\tflags", p.es_flags, ETHERSWITCH_PORT_FLAGS_BITS); 585 printf("\n"); 586 if (p.es_nleds) { 587 printf("\tled: "); 588 for (i = 0; i < p.es_nleds; i++) { 589 printf("%d:%s%s", i+1, ledstyles[p.es_led[i]], (i==p.es_nleds-1)?"":" "); 590 } 591 printf("\n"); 592 } 593 printf("\tmedia: "); 594 print_media_word(p.es_ifmr.ifm_current, 1); 595 if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) { 596 putchar(' '); 597 putchar('('); 598 print_media_word(p.es_ifmr.ifm_active, 0); 599 putchar(')'); 600 } 601 putchar('\n'); 602 printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier"); 603 if (cfg->mediatypes) { 604 printf("\tsupported media:\n"); 605 if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES) 606 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 607 for (i=0; i<p.es_ifmr.ifm_count; i++) { 608 printf("\t\tmedia "); 609 print_media_word(ifm_ulist[i], 0); 610 putchar('\n'); 611 } 612 } 613 } 614 615 static void 616 print_vlangroup(struct cfg *cfg, int vlangroup) 617 { 618 etherswitch_vlangroup_t vg; 619 int i, comma; 620 621 vg.es_vlangroup = vlangroup; 622 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 623 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 624 if ((vg.es_vid & ETHERSWITCH_VID_VALID) == 0) 625 return; 626 vg.es_vid &= ETHERSWITCH_VID_MASK; 627 printf("vlangroup%d:\n", vlangroup); 628 if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_PORT) 629 printf("\tport: %d\n", vg.es_vid); 630 else 631 printf("\tvlan: %d\n", vg.es_vid); 632 printf("\tmembers "); 633 comma = 0; 634 if (vg.es_member_ports != 0) 635 for (i=0; i<cfg->info.es_nports; i++) { 636 if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) { 637 if (comma) 638 printf(","); 639 printf("%d", i); 640 if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0) 641 printf("t"); 642 comma = 1; 643 } 644 } 645 else 646 printf("none"); 647 printf("\n"); 648 } 649 650 static void 651 print_info(struct cfg *cfg) 652 { 653 const char *c; 654 int i; 655 656 c = strrchr(cfg->controlfile, '/'); 657 if (c != NULL) 658 c = c + 1; 659 else 660 c = cfg->controlfile; 661 if (cfg->verbose) { 662 printf("%s: %s with %d ports and %d VLAN groups\n", c, 663 cfg->info.es_name, cfg->info.es_nports, 664 cfg->info.es_nvlangroups); 665 printf("%s: ", c); 666 printb("VLAN capabilities", cfg->info.es_vlan_caps, 667 ETHERSWITCH_VLAN_CAPS_BITS); 668 printf("\n"); 669 } 670 print_config(cfg); 671 for (i=0; i<cfg->info.es_nports; i++) { 672 print_port(cfg, i); 673 } 674 for (i=0; i<cfg->info.es_nvlangroups; i++) { 675 print_vlangroup(cfg, i); 676 } 677 } 678 679 static void 680 usage(struct cfg *cfg __unused, char *argv[] __unused) 681 { 682 fprintf(stderr, "usage: etherswitchctl\n"); 683 fprintf(stderr, "\tetherswitchcfg [-f control file] info\n"); 684 fprintf(stderr, "\tetherswitchcfg [-f control file] config " 685 "command parameter\n"); 686 fprintf(stderr, "\t\tconfig commands: vlan_mode\n"); 687 fprintf(stderr, "\tetherswitchcfg [-f control file] phy " 688 "phy.register[=value]\n"); 689 fprintf(stderr, "\tetherswitchcfg [-f control file] portX " 690 "[flags] command parameter\n"); 691 fprintf(stderr, "\t\tport commands: pvid, media, mediaopt, led\n"); 692 fprintf(stderr, "\tetherswitchcfg [-f control file] reg " 693 "register[=value]\n"); 694 fprintf(stderr, "\tetherswitchcfg [-f control file] vlangroupX " 695 "command parameter\n"); 696 fprintf(stderr, "\t\tvlangroup commands: vlan, members\n"); 697 exit(EX_USAGE); 698 } 699 700 static void 701 newmode(struct cfg *cfg, enum cmdmode mode) 702 { 703 if (mode == cfg->mode) 704 return; 705 switch (cfg->mode) { 706 case MODE_NONE: 707 break; 708 case MODE_CONFIG: 709 /* 710 * Read the updated the configuration (it can be different 711 * from the last time we read it). 712 */ 713 if (ioctl(cfg->fd, IOETHERSWITCHGETCONF, &cfg->conf) != 0) 714 err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)"); 715 print_config(cfg); 716 break; 717 case MODE_PORT: 718 print_port(cfg, cfg->unit); 719 break; 720 case MODE_VLANGROUP: 721 print_vlangroup(cfg, cfg->unit); 722 break; 723 case MODE_REGISTER: 724 case MODE_PHYREG: 725 case MODE_ATU: 726 break; 727 } 728 cfg->mode = mode; 729 } 730 731 int 732 main(int argc, char *argv[]) 733 { 734 int ch; 735 struct cfg cfg; 736 int i; 737 738 bzero(&cfg, sizeof(cfg)); 739 cfg.controlfile = "/dev/etherswitch0"; 740 while ((ch = getopt(argc, argv, "f:mv?")) != -1) 741 switch(ch) { 742 case 'f': 743 cfg.controlfile = optarg; 744 break; 745 case 'm': 746 cfg.mediatypes++; 747 break; 748 case 'v': 749 cfg.verbose++; 750 break; 751 case '?': 752 /* FALLTHROUGH */ 753 default: 754 usage(&cfg, argv); 755 } 756 argc -= optind; 757 argv += optind; 758 cfg.fd = open(cfg.controlfile, O_RDONLY); 759 if (cfg.fd < 0) 760 err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile); 761 if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0) 762 err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)"); 763 if (ioctl(cfg.fd, IOETHERSWITCHGETCONF, &cfg.conf) != 0) 764 err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)"); 765 if (argc == 0) { 766 print_info(&cfg); 767 return (0); 768 } 769 cfg.mode = MODE_NONE; 770 while (argc > 0) { 771 switch(cfg.mode) { 772 case MODE_NONE: 773 if (strcmp(argv[0], "info") == 0) { 774 print_info(&cfg); 775 } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) { 776 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports) 777 errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports - 1); 778 newmode(&cfg, MODE_PORT); 779 } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) { 780 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) 781 errx(EX_USAGE, 782 "vlangroup unit must be between 0 and %d", 783 cfg.info.es_nvlangroups - 1); 784 newmode(&cfg, MODE_VLANGROUP); 785 } else if (strcmp(argv[0], "config") == 0) { 786 newmode(&cfg, MODE_CONFIG); 787 } else if (strcmp(argv[0], "phy") == 0) { 788 newmode(&cfg, MODE_PHYREG); 789 } else if (strcmp(argv[0], "reg") == 0) { 790 newmode(&cfg, MODE_REGISTER); 791 } else if (strcmp(argv[0], "help") == 0) { 792 usage(&cfg, argv); 793 } else if (strcmp(argv[0], "atu") == 0) { 794 newmode(&cfg, MODE_ATU); 795 } else { 796 errx(EX_USAGE, "Unknown command \"%s\"", argv[0]); 797 } 798 break; 799 case MODE_PORT: 800 case MODE_CONFIG: 801 case MODE_VLANGROUP: 802 case MODE_ATU: 803 for(i=0; cmds[i].name != NULL; i++) { 804 int r; 805 if (cfg.mode == cmds[i].mode && 806 strcmp(argv[0], cmds[i].name) == 0) { 807 if ((cmds[i].args != -1) && 808 (argc < (cmds[i].args + 1))) { 809 printf("%s needs %d argument%s\n", 810 cmds[i].name, cmds[i].args, 811 (cmds[i].args==1)?"":","); 812 break; 813 } 814 815 r = (cmds[i].f)(&cfg, argc, argv); 816 817 /* -1 here means "error" */ 818 if (r == -1) { 819 argc = 0; 820 break; 821 } 822 823 /* Legacy return value */ 824 if (r == 0) 825 r = cmds[i].args; 826 827 argc -= r; 828 argv += r; 829 break; 830 } 831 } 832 if (cmds[i].name == NULL) { 833 newmode(&cfg, MODE_NONE); 834 continue; 835 } 836 break; 837 case MODE_REGISTER: 838 if (set_register(&cfg, argv[0]) != 0) { 839 newmode(&cfg, MODE_NONE); 840 continue; 841 } 842 break; 843 case MODE_PHYREG: 844 if (set_phyregister(&cfg, argv[0]) != 0) { 845 newmode(&cfg, MODE_NONE); 846 continue; 847 } 848 break; 849 } 850 argc--; 851 argv++; 852 } 853 /* switch back to command mode to print configuration for last command */ 854 newmode(&cfg, MODE_NONE); 855 close(cfg.fd); 856 return (0); 857 } 858 859 static struct cmds cmds[] = { 860 { MODE_PORT, "pvid", 1, set_port_vid }, 861 { MODE_PORT, "media", 1, set_port_media }, 862 { MODE_PORT, "mediaopt", 1, set_port_mediaopt }, 863 { MODE_PORT, "led", 2, set_port_led }, 864 { MODE_PORT, "addtag", 0, set_port_flag }, 865 { MODE_PORT, "-addtag", 0, set_port_flag }, 866 { MODE_PORT, "ingress", 0, set_port_flag }, 867 { MODE_PORT, "-ingress", 0, set_port_flag }, 868 { MODE_PORT, "striptag", 0, set_port_flag }, 869 { MODE_PORT, "-striptag", 0, set_port_flag }, 870 { MODE_PORT, "striptagingress", 0, set_port_flag }, 871 { MODE_PORT, "-striptagingress", 0, set_port_flag }, 872 { MODE_PORT, "doubletag", 0, set_port_flag }, 873 { MODE_PORT, "-doubletag", 0, set_port_flag }, 874 { MODE_PORT, "firstlock", 0, set_port_flag }, 875 { MODE_PORT, "-firstlock", 0, set_port_flag }, 876 { MODE_PORT, "droptagged", 0, set_port_flag }, 877 { MODE_PORT, "-droptagged", 0, set_port_flag }, 878 { MODE_PORT, "dropuntagged", 0, set_port_flag }, 879 { MODE_PORT, "-dropuntagged", 0, set_port_flag }, 880 { MODE_CONFIG, "vlan_mode", 1, set_vlan_mode }, 881 { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid }, 882 { MODE_VLANGROUP, "members", 1, set_vlangroup_members }, 883 { MODE_ATU, "flush", -1, atu_flush }, 884 { MODE_ATU, "dump", -1, atu_dump }, 885 { 0, NULL, 0, NULL } 886 }; 887