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