1 /*- 2 * Copyright (c) 2011-2012 Stefan Bethke. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <ctype.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sysexits.h> 40 #include <unistd.h> 41 #include <sys/types.h> 42 #include <sys/ioctl.h> 43 #include <net/if.h> 44 #include <net/if_media.h> 45 #include <dev/etherswitch/etherswitch.h> 46 47 int get_media_subtype(int, const char *); 48 int get_media_mode(int, const char *); 49 int get_media_options(int, const char *); 50 int lookup_media_word(struct ifmedia_description *, const char *); 51 void print_media_word(int, int); 52 void print_media_word_ifconfig(int); 53 54 /* some constants */ 55 #define IEEE802DOT1Q_VID_MAX 4094 56 #define IFMEDIAREQ_NULISTENTRIES 256 57 58 enum cmdmode { 59 MODE_NONE = 0, 60 MODE_PORT, 61 MODE_VLANGROUP, 62 MODE_REGISTER, 63 MODE_PHYREG 64 }; 65 66 struct cfg { 67 int fd; 68 int verbose; 69 int mediatypes; 70 const char *controlfile; 71 etherswitch_info_t info; 72 enum cmdmode mode; 73 int unit; 74 }; 75 76 struct cmds { 77 enum cmdmode mode; 78 const char *name; 79 int args; 80 void (*f)(struct cfg *, char *argv[]); 81 }; 82 struct cmds cmds[]; 83 84 85 static void usage(void); 86 87 static int 88 read_register(struct cfg *cfg, int r) 89 { 90 struct etherswitch_reg er; 91 92 er.reg = r; 93 if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0) 94 err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)"); 95 return (er.val); 96 } 97 98 static void 99 write_register(struct cfg *cfg, int r, int v) 100 { 101 struct etherswitch_reg er; 102 103 er.reg = r; 104 er.val = v; 105 if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0) 106 err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)"); 107 } 108 109 static int 110 read_phyregister(struct cfg *cfg, int phy, int reg) 111 { 112 struct etherswitch_phyreg er; 113 114 er.phy = phy; 115 er.reg = reg; 116 if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0) 117 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)"); 118 return (er.val); 119 } 120 121 static void 122 write_phyregister(struct cfg *cfg, int phy, int reg, int val) 123 { 124 struct etherswitch_phyreg er; 125 126 er.phy = phy; 127 er.reg = reg; 128 er.val = val; 129 if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0) 130 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)"); 131 } 132 133 static void 134 set_port_vlangroup(struct cfg *cfg, char *argv[]) 135 { 136 int v; 137 etherswitch_port_t p; 138 139 v = strtol(argv[1], NULL, 0); 140 if (v < 0 || v >= cfg->info.es_nvlangroups) 141 errx(EX_USAGE, "vlangroup must be between 0 and %d", cfg->info.es_nvlangroups-1); 142 p.es_port = cfg->unit; 143 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 144 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 145 p.es_vlangroup = v; 146 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 147 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 148 } 149 150 static void 151 set_port_media(struct cfg *cfg, char *argv[]) 152 { 153 etherswitch_port_t p; 154 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 155 int subtype; 156 157 bzero(&p, sizeof(p)); 158 p.es_port = cfg->unit; 159 p.es_ifmr.ifm_ulist = ifm_ulist; 160 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 161 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 162 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 163 subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]); 164 p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) | 165 IFM_TYPE(ifm_ulist[0]) | subtype; 166 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 167 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 168 } 169 170 static void 171 set_port_mediaopt(struct cfg *cfg, char *argv[]) 172 { 173 etherswitch_port_t p; 174 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 175 int options; 176 177 bzero(&p, sizeof(p)); 178 p.es_port = cfg->unit; 179 p.es_ifmr.ifm_ulist = ifm_ulist; 180 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 181 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 182 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 183 options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]); 184 if (options == -1) 185 errx(EX_USAGE, "invalid media options \"%s\"", argv[1]); 186 if (options & IFM_HDX) { 187 p.es_ifr.ifr_media &= ~IFM_FDX; 188 options &= ~IFM_HDX; 189 } 190 p.es_ifr.ifr_media |= options; 191 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 192 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 193 } 194 195 static void 196 set_vlangroup_vid(struct cfg *cfg, char *argv[]) 197 { 198 int v; 199 etherswitch_vlangroup_t vg; 200 201 v = strtol(argv[1], NULL, 0); 202 if (v < 0 || v >= IEEE802DOT1Q_VID_MAX) 203 errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX); 204 vg.es_vlangroup = cfg->unit; 205 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 206 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 207 vg.es_vid = v; 208 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 209 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 210 } 211 212 static void 213 set_vlangroup_members(struct cfg *cfg, char *argv[]) 214 { 215 etherswitch_vlangroup_t vg; 216 int member, untagged; 217 char *c, *d; 218 int v; 219 220 member = untagged = 0; 221 if (strcmp(argv[1], "none") != 0) { 222 for (c=argv[1]; *c; c=d) { 223 v = strtol(c, &d, 0); 224 if (d == c) 225 break; 226 if (v < 0 || v >= cfg->info.es_nports) 227 errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1); 228 if (d[0] == ',' || d[0] == '\0' || 229 ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) { 230 if (d[0] == 't' || d[0] == 'T') { 231 untagged &= ~ETHERSWITCH_PORTMASK(v); 232 d++; 233 } else 234 untagged |= ETHERSWITCH_PORTMASK(v); 235 member |= ETHERSWITCH_PORTMASK(v); 236 d++; 237 } else 238 errx(EX_USAGE, "Invalid members specification \"%s\"", d); 239 } 240 } 241 vg.es_vlangroup = cfg->unit; 242 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 243 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 244 vg.es_member_ports = member; 245 vg.es_untagged_ports = untagged; 246 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 247 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 248 } 249 250 static int 251 set_register(struct cfg *cfg, char *arg) 252 { 253 int a, v; 254 char *c; 255 256 a = strtol(arg, &c, 0); 257 if (c==arg) 258 return (1); 259 if (*c == '=') { 260 v = strtol(c+1, NULL, 0); 261 write_register(cfg, a, v); 262 } 263 printf("\treg 0x%04x=0x%04x\n", a, read_register(cfg, a)); 264 return (0); 265 } 266 267 static int 268 set_phyregister(struct cfg *cfg, char *arg) 269 { 270 int phy, reg, val; 271 char *c, *d; 272 273 phy = strtol(arg, &c, 0); 274 if (c==arg) 275 return (1); 276 if (*c != '.') 277 return (1); 278 d = c+1; 279 reg = strtol(d, &c, 0); 280 if (d == c) 281 return (1); 282 if (*c == '=') { 283 val = strtol(c+1, NULL, 0); 284 write_phyregister(cfg, phy, reg, val); 285 } 286 printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); 287 return (0); 288 } 289 290 static void 291 print_port(struct cfg *cfg, int port) 292 { 293 etherswitch_port_t p; 294 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 295 int i; 296 297 bzero(&p, sizeof(p)); 298 p.es_port = port; 299 p.es_ifmr.ifm_ulist = ifm_ulist; 300 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 301 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 302 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 303 printf("port%d:\n", port); 304 printf("\tvlangroup: %d\n", p.es_vlangroup); 305 printf("\tmedia: "); 306 print_media_word(p.es_ifmr.ifm_current, 1); 307 if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) { 308 putchar(' '); 309 putchar('('); 310 print_media_word(p.es_ifmr.ifm_active, 0); 311 putchar(')'); 312 } 313 putchar('\n'); 314 printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier"); 315 if (cfg->mediatypes) { 316 printf("\tsupported media:\n"); 317 if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES) 318 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 319 for (i=0; i<p.es_ifmr.ifm_count; i++) { 320 printf("\t\tmedia "); 321 print_media_word(ifm_ulist[i], 0); 322 putchar('\n'); 323 } 324 } 325 } 326 327 static void 328 print_vlangroup(struct cfg *cfg, int vlangroup) 329 { 330 etherswitch_vlangroup_t vg; 331 int i, comma; 332 333 vg.es_vlangroup = vlangroup; 334 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 335 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 336 if (cfg->verbose == 0 && vg.es_member_ports == 0) 337 return; 338 printf("vlangroup%d:\n", vlangroup); 339 printf("\tvlan: %d\n", vg.es_vid); 340 printf("\tmembers "); 341 comma = 0; 342 if (vg.es_member_ports != 0) 343 for (i=0; i<cfg->info.es_nports; i++) { 344 if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) { 345 if (comma) 346 printf(","); 347 printf("%d", i); 348 if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0) 349 printf("t"); 350 comma = 1; 351 } 352 } 353 else 354 printf("none"); 355 printf("\n"); 356 } 357 358 static void 359 print_info(struct cfg *cfg) 360 { 361 const char *c; 362 int i; 363 364 c = strrchr(cfg->controlfile, '/'); 365 if (c != NULL) 366 c = c + 1; 367 else 368 c = cfg->controlfile; 369 if (cfg->verbose) 370 printf("%s: %s with %d ports and %d VLAN groups\n", 371 c, cfg->info.es_name, cfg->info.es_nports, cfg->info.es_nvlangroups); 372 for (i=0; i<cfg->info.es_nports; i++) { 373 print_port(cfg, i); 374 } 375 for (i=0; i<cfg->info.es_nvlangroups; i++) { 376 print_vlangroup(cfg, i); 377 } 378 } 379 380 static void 381 usage(void) 382 { 383 fprintf(stderr, "usage: etherswitchctl\n"); 384 exit(EX_USAGE); 385 } 386 387 static void 388 newmode(struct cfg *cfg, enum cmdmode mode) 389 { 390 if (mode == cfg->mode) 391 return; 392 switch (cfg->mode) { 393 case MODE_NONE: 394 break; 395 case MODE_PORT: 396 print_port(cfg, cfg->unit); 397 break; 398 case MODE_VLANGROUP: 399 print_vlangroup(cfg, cfg->unit); 400 break; 401 case MODE_REGISTER: 402 case MODE_PHYREG: 403 break; 404 } 405 cfg->mode = mode; 406 } 407 408 int 409 main(int argc, char *argv[]) 410 { 411 int ch; 412 struct cfg cfg; 413 int i; 414 415 bzero(&cfg, sizeof(cfg)); 416 cfg.controlfile = "/dev/etherswitch0"; 417 while ((ch = getopt(argc, argv, "f:mv?")) != -1) 418 switch(ch) { 419 case 'f': 420 cfg.controlfile = optarg; 421 break; 422 case 'm': 423 cfg.mediatypes++; 424 break; 425 case 'v': 426 cfg.verbose++; 427 break; 428 case '?': 429 /* FALLTHROUGH */ 430 default: 431 usage(); 432 } 433 argc -= optind; 434 argv += optind; 435 cfg.fd = open(cfg.controlfile, O_RDONLY); 436 if (cfg.fd < 0) 437 err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile); 438 if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0) 439 err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)"); 440 if (argc == 0) { 441 print_info(&cfg); 442 return (0); 443 } 444 cfg.mode = MODE_NONE; 445 while (argc > 0) { 446 switch(cfg.mode) { 447 case MODE_NONE: 448 if (strcmp(argv[0], "info") == 0) { 449 print_info(&cfg); 450 } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) { 451 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports) 452 errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports); 453 newmode(&cfg, MODE_PORT); 454 } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) { 455 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) 456 errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nvlangroups); 457 newmode(&cfg, MODE_VLANGROUP); 458 } else if (strcmp(argv[0], "phy") == 0) { 459 newmode(&cfg, MODE_PHYREG); 460 } else if (strcmp(argv[0], "reg") == 0) { 461 newmode(&cfg, MODE_REGISTER); 462 } else { 463 errx(EX_USAGE, "Unknown command \"%s\"", argv[0]); 464 } 465 break; 466 case MODE_PORT: 467 case MODE_VLANGROUP: 468 for(i=0; cmds[i].name != NULL; i++) { 469 if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0 470 && argc >= cmds[i].args) { 471 (cmds[i].f)(&cfg, argv); 472 argc -= cmds[i].args; 473 argv += cmds[i].args; 474 break; 475 } 476 } 477 if (cmds[i].name == NULL) { 478 newmode(&cfg, MODE_NONE); 479 continue; 480 } 481 break; 482 case MODE_REGISTER: 483 if (set_register(&cfg, argv[0]) != 0) { 484 newmode(&cfg, MODE_NONE); 485 continue; 486 } 487 break; 488 case MODE_PHYREG: 489 if (set_phyregister(&cfg, argv[0]) != 0) { 490 newmode(&cfg, MODE_NONE); 491 continue; 492 } 493 break; 494 } 495 argc--; 496 argv++; 497 } 498 /* switch back to command mode to print configuration for last command */ 499 newmode(&cfg, MODE_NONE); 500 close(cfg.fd); 501 return (0); 502 } 503 504 struct cmds cmds[] = { 505 { MODE_PORT, "vlangroup", 1, set_port_vlangroup }, 506 { MODE_PORT, "media", 1, set_port_media }, 507 { MODE_PORT, "mediaopt", 1, set_port_mediaopt }, 508 { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid }, 509 { MODE_VLANGROUP, "members", 1, set_vlangroup_members }, 510 { 0, NULL, 0, NULL } 511 }; 512