etherswitchcfg.c revision 235289
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: head/sbin/etherswitchcfg/etherswitchcfg.c 235289 2012-05-11 20:56:04Z adrian $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sbin/etherswitchcfg/etherswitchcfg.c 235289 2012-05-11 20:56:04Z adrian $"); 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 47int get_media_subtype(int, const char *); 48int get_media_mode(int, const char *); 49int get_media_options(int, const char *); 50int lookup_media_word(struct ifmedia_description *, const char *); 51void print_media_word(int, int); 52void print_media_word_ifconfig(int); 53 54/* some constants */ 55#define IEEE802DOT1Q_VID_MAX 4094 56#define IFMEDIAREQ_NULISTENTRIES 256 57 58enum cmdmode { 59 MODE_NONE = 0, 60 MODE_PORT, 61 MODE_VLANGROUP, 62 MODE_REGISTER, 63 MODE_PHYREG 64}; 65 66struct 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 76struct cmds { 77 enum cmdmode mode; 78 const char *name; 79 int args; 80 void (*f)(struct cfg *, char *argv[]); 81}; 82struct cmds cmds[]; 83 84 85static void usage(void); 86 87static int 88read_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 98static void 99write_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 109static int 110read_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 121static void 122write_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 133static void 134set_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 150static void 151set_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 170static void 171set_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 195static void 196set_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 212static void 213set_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 250static int 251set_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 267static int 268set_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 290static void 291print_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 327static void 328print_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 358static void 359print_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 380static void 381usage(void) 382{ 383 fprintf(stderr, "usage: etherswitchctl\n"); 384 exit(EX_USAGE); 385} 386 387static void 388newmode(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 408int 409main(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 504struct 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