etherswitchcfg.c revision 249752
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 249752 2013-04-22 05:52:18Z adrian $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sbin/etherswitchcfg/etherswitchcfg.c 249752 2013-04-22 05:52:18Z 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}; 82static struct 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_vid(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 > IEEE802DOT1Q_VID_MAX) 141 errx(EX_USAGE, "pvid must be between 0 and %d", 142 IEEE802DOT1Q_VID_MAX); 143 bzero(&p, sizeof(p)); 144 p.es_port = cfg->unit; 145 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 146 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 147 p.es_pvid = v; 148 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 149 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 150} 151 152static void 153set_port_media(struct cfg *cfg, char *argv[]) 154{ 155 etherswitch_port_t p; 156 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 157 int subtype; 158 159 bzero(&p, sizeof(p)); 160 p.es_port = cfg->unit; 161 p.es_ifmr.ifm_ulist = ifm_ulist; 162 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 163 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 164 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 165 subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]); 166 p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) | 167 IFM_TYPE(ifm_ulist[0]) | subtype; 168 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 169 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 170} 171 172static void 173set_port_mediaopt(struct cfg *cfg, char *argv[]) 174{ 175 etherswitch_port_t p; 176 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 177 int options; 178 179 bzero(&p, sizeof(p)); 180 p.es_port = cfg->unit; 181 p.es_ifmr.ifm_ulist = ifm_ulist; 182 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 183 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 184 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 185 options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]); 186 if (options == -1) 187 errx(EX_USAGE, "invalid media options \"%s\"", argv[1]); 188 if (options & IFM_HDX) { 189 p.es_ifr.ifr_media &= ~IFM_FDX; 190 options &= ~IFM_HDX; 191 } 192 p.es_ifr.ifr_media |= options; 193 if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) 194 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); 195} 196 197static void 198set_vlangroup_vid(struct cfg *cfg, char *argv[]) 199{ 200 int v; 201 etherswitch_vlangroup_t vg; 202 203 v = strtol(argv[1], NULL, 0); 204 if (v < 0 || v >= IEEE802DOT1Q_VID_MAX) 205 errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX); 206 vg.es_vlangroup = cfg->unit; 207 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 208 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 209 vg.es_vid = v; 210 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 211 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 212} 213 214static void 215set_vlangroup_members(struct cfg *cfg, char *argv[]) 216{ 217 etherswitch_vlangroup_t vg; 218 int member, untagged; 219 char *c, *d; 220 int v; 221 222 member = untagged = 0; 223 if (strcmp(argv[1], "none") != 0) { 224 for (c=argv[1]; *c; c=d) { 225 v = strtol(c, &d, 0); 226 if (d == c) 227 break; 228 if (v < 0 || v >= cfg->info.es_nports) 229 errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1); 230 if (d[0] == ',' || d[0] == '\0' || 231 ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) { 232 if (d[0] == 't' || d[0] == 'T') { 233 untagged &= ~ETHERSWITCH_PORTMASK(v); 234 d++; 235 } else 236 untagged |= ETHERSWITCH_PORTMASK(v); 237 member |= ETHERSWITCH_PORTMASK(v); 238 d++; 239 } else 240 errx(EX_USAGE, "Invalid members specification \"%s\"", d); 241 } 242 } 243 vg.es_vlangroup = cfg->unit; 244 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 245 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 246 vg.es_member_ports = member; 247 vg.es_untagged_ports = untagged; 248 if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) 249 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); 250} 251 252static int 253set_register(struct cfg *cfg, char *arg) 254{ 255 int a, v; 256 char *c; 257 258 a = strtol(arg, &c, 0); 259 if (c==arg) 260 return (1); 261 if (*c == '=') { 262 v = strtol(c+1, NULL, 0); 263 write_register(cfg, a, v); 264 } 265 printf("\treg 0x%04x=0x%04x\n", a, read_register(cfg, a)); 266 return (0); 267} 268 269static int 270set_phyregister(struct cfg *cfg, char *arg) 271{ 272 int phy, reg, val; 273 char *c, *d; 274 275 phy = strtol(arg, &c, 0); 276 if (c==arg) 277 return (1); 278 if (*c != '.') 279 return (1); 280 d = c+1; 281 reg = strtol(d, &c, 0); 282 if (d == c) 283 return (1); 284 if (*c == '=') { 285 val = strtol(c+1, NULL, 0); 286 write_phyregister(cfg, phy, reg, val); 287 } 288 printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); 289 return (0); 290} 291 292static void 293print_port(struct cfg *cfg, int port) 294{ 295 etherswitch_port_t p; 296 int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; 297 int i; 298 299 bzero(&p, sizeof(p)); 300 p.es_port = port; 301 p.es_ifmr.ifm_ulist = ifm_ulist; 302 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 303 if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) 304 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); 305 printf("port%d:\n", port); 306 printf("\tpvid: %d\n", p.es_pvid); 307 printf("\tmedia: "); 308 print_media_word(p.es_ifmr.ifm_current, 1); 309 if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) { 310 putchar(' '); 311 putchar('('); 312 print_media_word(p.es_ifmr.ifm_active, 0); 313 putchar(')'); 314 } 315 putchar('\n'); 316 printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier"); 317 if (cfg->mediatypes) { 318 printf("\tsupported media:\n"); 319 if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES) 320 p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; 321 for (i=0; i<p.es_ifmr.ifm_count; i++) { 322 printf("\t\tmedia "); 323 print_media_word(ifm_ulist[i], 0); 324 putchar('\n'); 325 } 326 } 327} 328 329static void 330print_vlangroup(struct cfg *cfg, int vlangroup) 331{ 332 etherswitch_vlangroup_t vg; 333 int i, comma; 334 335 vg.es_vlangroup = vlangroup; 336 if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) 337 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); 338 if (cfg->verbose == 0 && vg.es_member_ports == 0) 339 return; 340 printf("vlangroup%d:\n", vlangroup); 341 printf("\tvlan: %d\n", vg.es_vid); 342 printf("\tmembers "); 343 comma = 0; 344 if (vg.es_member_ports != 0) 345 for (i=0; i<cfg->info.es_nports; i++) { 346 if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) { 347 if (comma) 348 printf(","); 349 printf("%d", i); 350 if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0) 351 printf("t"); 352 comma = 1; 353 } 354 } 355 else 356 printf("none"); 357 printf("\n"); 358} 359 360static void 361print_info(struct cfg *cfg) 362{ 363 const char *c; 364 int i; 365 366 c = strrchr(cfg->controlfile, '/'); 367 if (c != NULL) 368 c = c + 1; 369 else 370 c = cfg->controlfile; 371 if (cfg->verbose) 372 printf("%s: %s with %d ports and %d VLAN groups\n", 373 c, cfg->info.es_name, cfg->info.es_nports, cfg->info.es_nvlangroups); 374 for (i=0; i<cfg->info.es_nports; i++) { 375 print_port(cfg, i); 376 } 377 for (i=0; i<cfg->info.es_nvlangroups; i++) { 378 print_vlangroup(cfg, i); 379 } 380} 381 382static void 383usage(void) 384{ 385 fprintf(stderr, "usage: etherswitchctl\n"); 386 exit(EX_USAGE); 387} 388 389static void 390newmode(struct cfg *cfg, enum cmdmode mode) 391{ 392 if (mode == cfg->mode) 393 return; 394 switch (cfg->mode) { 395 case MODE_NONE: 396 break; 397 case MODE_PORT: 398 print_port(cfg, cfg->unit); 399 break; 400 case MODE_VLANGROUP: 401 print_vlangroup(cfg, cfg->unit); 402 break; 403 case MODE_REGISTER: 404 case MODE_PHYREG: 405 break; 406 } 407 cfg->mode = mode; 408} 409 410int 411main(int argc, char *argv[]) 412{ 413 int ch; 414 struct cfg cfg; 415 int i; 416 417 bzero(&cfg, sizeof(cfg)); 418 cfg.controlfile = "/dev/etherswitch0"; 419 while ((ch = getopt(argc, argv, "f:mv?")) != -1) 420 switch(ch) { 421 case 'f': 422 cfg.controlfile = optarg; 423 break; 424 case 'm': 425 cfg.mediatypes++; 426 break; 427 case 'v': 428 cfg.verbose++; 429 break; 430 case '?': 431 /* FALLTHROUGH */ 432 default: 433 usage(); 434 } 435 argc -= optind; 436 argv += optind; 437 cfg.fd = open(cfg.controlfile, O_RDONLY); 438 if (cfg.fd < 0) 439 err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile); 440 if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0) 441 err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)"); 442 if (argc == 0) { 443 print_info(&cfg); 444 return (0); 445 } 446 cfg.mode = MODE_NONE; 447 while (argc > 0) { 448 switch(cfg.mode) { 449 case MODE_NONE: 450 if (strcmp(argv[0], "info") == 0) { 451 print_info(&cfg); 452 } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) { 453 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports) 454 errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports); 455 newmode(&cfg, MODE_PORT); 456 } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) { 457 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) 458 errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nvlangroups); 459 newmode(&cfg, MODE_VLANGROUP); 460 } else if (strcmp(argv[0], "phy") == 0) { 461 newmode(&cfg, MODE_PHYREG); 462 } else if (strcmp(argv[0], "reg") == 0) { 463 newmode(&cfg, MODE_REGISTER); 464 } else { 465 errx(EX_USAGE, "Unknown command \"%s\"", argv[0]); 466 } 467 break; 468 case MODE_PORT: 469 case MODE_VLANGROUP: 470 for(i=0; cmds[i].name != NULL; i++) { 471 if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0) { 472 if (argc < (cmds[i].args + 1)) { 473 printf("%s needs an argument\n", cmds[i].name); 474 break; 475 } 476 (cmds[i].f)(&cfg, argv); 477 argc -= cmds[i].args; 478 argv += cmds[i].args; 479 break; 480 } 481 } 482 if (cmds[i].name == NULL) { 483 newmode(&cfg, MODE_NONE); 484 continue; 485 } 486 break; 487 case MODE_REGISTER: 488 if (set_register(&cfg, argv[0]) != 0) { 489 newmode(&cfg, MODE_NONE); 490 continue; 491 } 492 break; 493 case MODE_PHYREG: 494 if (set_phyregister(&cfg, argv[0]) != 0) { 495 newmode(&cfg, MODE_NONE); 496 continue; 497 } 498 break; 499 } 500 argc--; 501 argv++; 502 } 503 /* switch back to command mode to print configuration for last command */ 504 newmode(&cfg, MODE_NONE); 505 close(cfg.fd); 506 return (0); 507} 508 509static struct cmds cmds[] = { 510 { MODE_PORT, "pvid", 1, set_port_vid }, 511 { MODE_PORT, "media", 1, set_port_media }, 512 { MODE_PORT, "mediaopt", 1, set_port_mediaopt }, 513 { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid }, 514 { MODE_VLANGROUP, "members", 1, set_vlangroup_members }, 515 { 0, NULL, 0, NULL } 516}; 517