Path: blob/main/sbin/etherswitchcfg/etherswitchcfg.c
39478 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2011-2012 Stefan Bethke.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/cdefs.h>29#include <ctype.h>30#include <err.h>31#include <errno.h>32#include <fcntl.h>33#include <stdio.h>34#include <stdlib.h>35#include <string.h>36#include <sysexits.h>37#include <unistd.h>38#include <sys/types.h>39#include <sys/ioctl.h>40#include <net/if.h>41#include <net/if_media.h>42#include <dev/etherswitch/etherswitch.h>4344int get_media_subtype(int, const char *);45int get_media_mode(int, const char *);46int get_media_options(int, const char *);47int lookup_media_word(struct ifmedia_description *, const char *);48void print_media_word(int, int);49void print_media_word_ifconfig(int);5051/* some constants */52#define IEEE802DOT1Q_VID_MAX 409453#define IFMEDIAREQ_NULISTENTRIES 2565455enum cmdmode {56MODE_NONE = 0,57MODE_PORT,58MODE_CONFIG,59MODE_VLANGROUP,60MODE_REGISTER,61MODE_PHYREG,62MODE_ATU63};6465struct cfg {66int fd;67int verbose;68int mediatypes;69const char *controlfile;70etherswitch_conf_t conf;71etherswitch_info_t info;72enum cmdmode mode;73int unit;74};7576struct cmds {77enum cmdmode mode;78const char *name;79int args;80int (*f)(struct cfg *, int argc, char *argv[]);81};82static struct cmds cmds[];8384/* Must match the ETHERSWITCH_PORT_LED_* enum order */85static const char *ledstyles[] = { "default", "on", "off", "blink", NULL };8687/*88* Print a value a la the %b format of the kernel's printf.89* Stolen from ifconfig.c.90*/91static void92printb(const char *s, unsigned v, const char *bits)93{94int i, any = 0;95char c;9697if (bits && *bits == 8)98printf("%s=%o", s, v);99else100printf("%s=%x", s, v);101bits++;102if (bits) {103putchar('<');104while ((i = *bits++) != '\0') {105if (v & (1 << (i-1))) {106if (any)107putchar(',');108any = 1;109for (; (c = *bits) > 32; bits++)110putchar(c);111} else112for (; *bits > 32; bits++)113;114}115putchar('>');116}117}118119static int120read_register(struct cfg *cfg, int r)121{122struct etherswitch_reg er;123124er.reg = r;125if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0)126err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)");127return (er.val);128}129130static void131write_register(struct cfg *cfg, int r, int v)132{133struct etherswitch_reg er;134135er.reg = r;136er.val = v;137if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0)138err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)");139}140141static int142read_phyregister(struct cfg *cfg, int phy, int reg)143{144struct etherswitch_phyreg er;145146er.phy = phy;147er.reg = reg;148if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0)149err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)");150return (er.val);151}152153static void154write_phyregister(struct cfg *cfg, int phy, int reg, int val)155{156struct etherswitch_phyreg er;157158er.phy = phy;159er.reg = reg;160er.val = val;161if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0)162err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)");163}164165static int166set_port_vid(struct cfg *cfg, int argc, char *argv[])167{168int v;169etherswitch_port_t p;170171if (argc < 2)172return (-1);173174v = strtol(argv[1], NULL, 0);175if (v < 0 || v > IEEE802DOT1Q_VID_MAX)176errx(EX_USAGE, "pvid must be between 0 and %d",177IEEE802DOT1Q_VID_MAX);178bzero(&p, sizeof(p));179p.es_port = cfg->unit;180if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)181err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");182p.es_pvid = v;183if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)184err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");185return (0);186}187188static int189set_port_flag(struct cfg *cfg, int argc, char *argv[])190{191char *flag;192int n;193uint32_t f;194etherswitch_port_t p;195196if (argc < 1)197return (-1);198199n = 0;200f = 0;201flag = argv[0];202if (strcmp(flag, "none") != 0) {203if (*flag == '-') {204n++;205flag++;206}207if (strcasecmp(flag, "striptag") == 0)208f = ETHERSWITCH_PORT_STRIPTAG;209else if (strcasecmp(flag, "addtag") == 0)210f = ETHERSWITCH_PORT_ADDTAG;211else if (strcasecmp(flag, "firstlock") == 0)212f = ETHERSWITCH_PORT_FIRSTLOCK;213else if (strcasecmp(flag, "droptagged") == 0)214f = ETHERSWITCH_PORT_DROPTAGGED;215else if (strcasecmp(flag, "dropuntagged") == 0)216f = ETHERSWITCH_PORT_DROPUNTAGGED;217else if (strcasecmp(flag, "doubletag") == 0)218f = ETHERSWITCH_PORT_DOUBLE_TAG;219else if (strcasecmp(flag, "ingress") == 0)220f = ETHERSWITCH_PORT_INGRESS;221else if (strcasecmp(flag, "striptagingress") == 0)222f = ETHERSWITCH_PORT_STRIPTAGINGRESS;223}224bzero(&p, sizeof(p));225p.es_port = cfg->unit;226if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)227err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");228if (n)229p.es_flags &= ~f;230else231p.es_flags |= f;232if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)233err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");234return (0);235}236237static int238set_port_media(struct cfg *cfg, int argc, char *argv[])239{240etherswitch_port_t p;241int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];242int subtype;243244if (argc < 2)245return (-1);246247bzero(&p, sizeof(p));248p.es_port = cfg->unit;249p.es_ifmr.ifm_ulist = ifm_ulist;250p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;251if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)252err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");253if (p.es_ifmr.ifm_count == 0)254return (0);255subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]);256p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) |257IFM_TYPE(ifm_ulist[0]) | subtype;258if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)259err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");260return (0);261}262263static int264set_port_mediaopt(struct cfg *cfg, int argc, char *argv[])265{266etherswitch_port_t p;267int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];268int options;269270if (argc < 2)271return (-1);272273bzero(&p, sizeof(p));274p.es_port = cfg->unit;275p.es_ifmr.ifm_ulist = ifm_ulist;276p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;277if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)278err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");279options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]);280if (options == -1)281errx(EX_USAGE, "invalid media options \"%s\"", argv[1]);282if (options & IFM_HDX) {283p.es_ifr.ifr_media &= ~IFM_FDX;284options &= ~IFM_HDX;285}286p.es_ifr.ifr_media |= options;287if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)288err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");289return (0);290}291292static int293set_port_led(struct cfg *cfg, int argc, char *argv[])294{295etherswitch_port_t p;296int led;297int i;298299if (argc < 3)300return (-1);301302bzero(&p, sizeof(p));303p.es_port = cfg->unit;304if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)305err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");306307led = strtol(argv[1], NULL, 0);308if (led < 1 || led > p.es_nleds)309errx(EX_USAGE, "invalid led number %s; must be between 1 and %d",310argv[1], p.es_nleds);311312led--;313314for (i=0; ledstyles[i] != NULL; i++) {315if (strcmp(argv[2], ledstyles[i]) == 0) {316p.es_led[led] = i;317break;318}319}320if (ledstyles[i] == NULL)321errx(EX_USAGE, "invalid led style \"%s\"", argv[2]);322323if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)324err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");325326return (0);327}328329static int330set_vlangroup_vid(struct cfg *cfg, int argc, char *argv[])331{332int v;333etherswitch_vlangroup_t vg;334335if (argc < 2)336return (-1);337338memset(&vg, 0, sizeof(vg));339v = strtol(argv[1], NULL, 0);340if (v < 0 || v > IEEE802DOT1Q_VID_MAX)341errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX);342vg.es_vlangroup = cfg->unit;343if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)344err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");345vg.es_vid = v;346if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)347err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");348return (0);349}350351static int352set_vlangroup_members(struct cfg *cfg, int argc, char *argv[])353{354etherswitch_vlangroup_t vg;355int member, untagged;356char *c, *d;357int v;358359if (argc < 2)360return (-1);361362member = untagged = 0;363memset(&vg, 0, sizeof(vg));364if (strcmp(argv[1], "none") != 0) {365for (c=argv[1]; *c; c=d) {366v = strtol(c, &d, 0);367if (d == c)368break;369if (v < 0 || v >= cfg->info.es_nports)370errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1);371if (d[0] == ',' || d[0] == '\0' ||372((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) {373if (d[0] == 't' || d[0] == 'T') {374untagged &= ~ETHERSWITCH_PORTMASK(v);375d++;376} else377untagged |= ETHERSWITCH_PORTMASK(v);378member |= ETHERSWITCH_PORTMASK(v);379d++;380} else381errx(EX_USAGE, "Invalid members specification \"%s\"", d);382}383}384vg.es_vlangroup = cfg->unit;385if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)386err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");387vg.es_member_ports = member;388vg.es_untagged_ports = untagged;389if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)390err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");391return (0);392}393394static int395set_register(struct cfg *cfg, char *arg)396{397int a, v;398char *c;399400a = strtol(arg, &c, 0);401if (c==arg)402return (1);403if (*c == '=') {404v = strtoul(c+1, NULL, 0);405write_register(cfg, a, v);406}407printf("\treg 0x%04x=0x%08x\n", a, read_register(cfg, a));408return (0);409}410411static int412set_phyregister(struct cfg *cfg, char *arg)413{414int phy, reg, val;415char *c, *d;416417phy = strtol(arg, &c, 0);418if (c==arg)419return (1);420if (*c != '.')421return (1);422d = c+1;423reg = strtol(d, &c, 0);424if (d == c)425return (1);426if (*c == '=') {427val = strtoul(c+1, NULL, 0);428write_phyregister(cfg, phy, reg, val);429}430printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg));431return (0);432}433434static int435set_vlan_mode(struct cfg *cfg, int argc, char *argv[])436{437etherswitch_conf_t conf;438439if (argc < 2)440return (-1);441442bzero(&conf, sizeof(conf));443conf.cmd = ETHERSWITCH_CONF_VLAN_MODE;444if (strcasecmp(argv[1], "isl") == 0)445conf.vlan_mode = ETHERSWITCH_VLAN_ISL;446else if (strcasecmp(argv[1], "port") == 0)447conf.vlan_mode = ETHERSWITCH_VLAN_PORT;448else if (strcasecmp(argv[1], "dot1q") == 0)449conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q;450else if (strcasecmp(argv[1], "dot1q4k") == 0)451conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q_4K;452else if (strcasecmp(argv[1], "qinq") == 0)453conf.vlan_mode = ETHERSWITCH_VLAN_DOUBLE_TAG;454else455conf.vlan_mode = 0;456if (ioctl(cfg->fd, IOETHERSWITCHSETCONF, &conf) != 0)457err(EX_OSERR, "ioctl(IOETHERSWITCHSETCONF)");458459return (0);460}461462static int463atu_flush(struct cfg *cfg, int argc, char *argv[])464{465etherswitch_portid_t p;466int i, r;467468bzero(&p, sizeof(p));469470/* note: argv[0] is "flush" */471if (argc > 2 && strcasecmp(argv[1], "port") == 0) {472p.es_port = atoi(argv[2]);473i = IOETHERSWITCHFLUSHPORT;474r = 3;475} else if (argc > 1 && strcasecmp(argv[1], "all") == 0) {476p.es_port = 0;477r = 2;478i = IOETHERSWITCHFLUSHALL;479} else {480fprintf(stderr,481"%s: invalid verb (port <x> or all) (got %s)\n",482__func__, argv[1]);483return (-1);484}485486if (ioctl(cfg->fd, i, &p) != 0)487err(EX_OSERR, "ioctl(ATU flush (ioctl %d, port %d))",488i, p.es_port);489return (r);490}491492static int493atu_dump(struct cfg *cfg, int argc, char *argv[])494{495etherswitch_atu_table_t p;496etherswitch_atu_entry_t e;497uint32_t i;498499(void) argc;500(void) argv;501502/* Note: argv[0] is "dump" */503bzero(&p, sizeof(p));504505if (ioctl(cfg->fd, IOETHERSWITCHGETTABLE, &p) != 0)506err(EX_OSERR, "ioctl(IOETHERSWITCHGETTABLE)");507508/* And now, iterate to get entries */509for (i = 0; i < p.es_nitems; i++) {510bzero(&e, sizeof(e));511e.id = i;512if (ioctl(cfg->fd, IOETHERSWITCHGETTABLEENTRY, &e) != 0)513break;514515printf(" [%d] %s: portmask 0x%08x\n", i,516ether_ntoa((void *) &e.es_macaddr),517e.es_portmask);518}519520return (1);521}522523static void524print_config(struct cfg *cfg)525{526const char *c;527528/* Get the device name. */529c = strrchr(cfg->controlfile, '/');530if (c != NULL)531c = c + 1;532else533c = cfg->controlfile;534535/* Print VLAN mode. */536if (cfg->conf.cmd & ETHERSWITCH_CONF_VLAN_MODE) {537printf("%s: VLAN mode: ", c);538switch (cfg->conf.vlan_mode) {539case ETHERSWITCH_VLAN_ISL:540printf("ISL\n");541break;542case ETHERSWITCH_VLAN_PORT:543printf("PORT\n");544break;545case ETHERSWITCH_VLAN_DOT1Q:546printf("DOT1Q\n");547break;548case ETHERSWITCH_VLAN_DOT1Q_4K:549printf("DOT1Q4K\n");550break;551case ETHERSWITCH_VLAN_DOUBLE_TAG:552printf("QinQ\n");553break;554default:555printf("none\n");556}557}558559/* Print switch MAC address. */560if (cfg->conf.cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) {561printf("%s: Switch MAC address: %s\n",562c,563ether_ntoa(&cfg->conf.switch_macaddr));564}565}566567static void568print_port(struct cfg *cfg, int port)569{570etherswitch_port_t p;571int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];572int i;573574bzero(&p, sizeof(p));575p.es_port = port;576p.es_ifmr.ifm_ulist = ifm_ulist;577p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;578if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)579err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");580printf("port%d:\n", port);581if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_DOT1Q)582printf("\tpvid: %d\n", p.es_pvid);583printb("\tflags", p.es_flags, ETHERSWITCH_PORT_FLAGS_BITS);584printf("\n");585if (p.es_nleds) {586printf("\tled: ");587for (i = 0; i < p.es_nleds; i++) {588printf("%d:%s%s", i+1, ledstyles[p.es_led[i]], (i==p.es_nleds-1)?"":" ");589}590printf("\n");591}592printf("\tmedia: ");593print_media_word(p.es_ifmr.ifm_current, 1);594if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) {595putchar(' ');596putchar('(');597print_media_word(p.es_ifmr.ifm_active, 0);598putchar(')');599}600putchar('\n');601printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier");602if (cfg->mediatypes) {603printf("\tsupported media:\n");604if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES)605p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;606for (i=0; i<p.es_ifmr.ifm_count; i++) {607printf("\t\tmedia ");608print_media_word(ifm_ulist[i], 0);609putchar('\n');610}611}612}613614static void615print_vlangroup(struct cfg *cfg, int vlangroup)616{617etherswitch_vlangroup_t vg;618int i, comma;619620vg.es_vlangroup = vlangroup;621if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)622err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");623if ((vg.es_vid & ETHERSWITCH_VID_VALID) == 0)624return;625vg.es_vid &= ETHERSWITCH_VID_MASK;626printf("vlangroup%d:\n", vlangroup);627if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_PORT)628printf("\tport: %d\n", vg.es_vid);629else630printf("\tvlan: %d\n", vg.es_vid);631printf("\tmembers ");632comma = 0;633if (vg.es_member_ports != 0)634for (i=0; i<cfg->info.es_nports; i++) {635if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) {636if (comma)637printf(",");638printf("%d", i);639if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0)640printf("t");641comma = 1;642}643}644else645printf("none");646printf("\n");647}648649static void650print_info(struct cfg *cfg)651{652const char *c;653int i;654655c = strrchr(cfg->controlfile, '/');656if (c != NULL)657c = c + 1;658else659c = cfg->controlfile;660if (cfg->verbose) {661printf("%s: %s with %d ports and %d VLAN groups\n", c,662cfg->info.es_name, cfg->info.es_nports,663cfg->info.es_nvlangroups);664printf("%s: ", c);665printb("VLAN capabilities", cfg->info.es_vlan_caps,666ETHERSWITCH_VLAN_CAPS_BITS);667printf("\n");668}669print_config(cfg);670for (i=0; i<cfg->info.es_nports; i++) {671print_port(cfg, i);672}673for (i=0; i<cfg->info.es_nvlangroups; i++) {674print_vlangroup(cfg, i);675}676}677678static void679usage(struct cfg *cfg __unused, char *argv[] __unused)680{681fprintf(stderr, "usage: etherswitchctl\n");682fprintf(stderr, "\tetherswitchcfg [-f control file] info\n");683fprintf(stderr, "\tetherswitchcfg [-f control file] config "684"command parameter\n");685fprintf(stderr, "\t\tconfig commands: vlan_mode\n");686fprintf(stderr, "\tetherswitchcfg [-f control file] phy "687"phy.register[=value]\n");688fprintf(stderr, "\tetherswitchcfg [-f control file] portX "689"[flags] command parameter\n");690fprintf(stderr, "\t\tport commands: pvid, media, mediaopt, led\n");691fprintf(stderr, "\tetherswitchcfg [-f control file] reg "692"register[=value]\n");693fprintf(stderr, "\tetherswitchcfg [-f control file] vlangroupX "694"command parameter\n");695fprintf(stderr, "\t\tvlangroup commands: vlan, members\n");696exit(EX_USAGE);697}698699static void700newmode(struct cfg *cfg, enum cmdmode mode)701{702if (mode == cfg->mode)703return;704switch (cfg->mode) {705case MODE_NONE:706break;707case MODE_CONFIG:708/*709* Read the updated the configuration (it can be different710* from the last time we read it).711*/712if (ioctl(cfg->fd, IOETHERSWITCHGETCONF, &cfg->conf) != 0)713err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");714print_config(cfg);715break;716case MODE_PORT:717print_port(cfg, cfg->unit);718break;719case MODE_VLANGROUP:720print_vlangroup(cfg, cfg->unit);721break;722case MODE_REGISTER:723case MODE_PHYREG:724case MODE_ATU:725break;726}727cfg->mode = mode;728}729730int731main(int argc, char *argv[])732{733int ch;734struct cfg cfg;735int i;736737bzero(&cfg, sizeof(cfg));738cfg.controlfile = "/dev/etherswitch0";739while ((ch = getopt(argc, argv, "f:mv?")) != -1)740switch(ch) {741case 'f':742cfg.controlfile = optarg;743break;744case 'm':745cfg.mediatypes++;746break;747case 'v':748cfg.verbose++;749break;750case '?':751/* FALLTHROUGH */752default:753usage(&cfg, argv);754}755argc -= optind;756argv += optind;757cfg.fd = open(cfg.controlfile, O_RDONLY);758if (cfg.fd < 0)759err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile);760if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0)761err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)");762if (ioctl(cfg.fd, IOETHERSWITCHGETCONF, &cfg.conf) != 0)763err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");764if (argc == 0) {765print_info(&cfg);766return (0);767}768cfg.mode = MODE_NONE;769while (argc > 0) {770switch(cfg.mode) {771case MODE_NONE:772if (strcmp(argv[0], "info") == 0) {773print_info(&cfg);774} else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) {775if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports)776errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports - 1);777newmode(&cfg, MODE_PORT);778} else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) {779if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups)780errx(EX_USAGE,781"vlangroup unit must be between 0 and %d",782cfg.info.es_nvlangroups - 1);783newmode(&cfg, MODE_VLANGROUP);784} else if (strcmp(argv[0], "config") == 0) {785newmode(&cfg, MODE_CONFIG);786} else if (strcmp(argv[0], "phy") == 0) {787newmode(&cfg, MODE_PHYREG);788} else if (strcmp(argv[0], "reg") == 0) {789newmode(&cfg, MODE_REGISTER);790} else if (strcmp(argv[0], "help") == 0) {791usage(&cfg, argv);792} else if (strcmp(argv[0], "atu") == 0) {793newmode(&cfg, MODE_ATU);794} else {795errx(EX_USAGE, "Unknown command \"%s\"", argv[0]);796}797break;798case MODE_PORT:799case MODE_CONFIG:800case MODE_VLANGROUP:801case MODE_ATU:802for(i=0; cmds[i].name != NULL; i++) {803int r;804if (cfg.mode == cmds[i].mode &&805strcmp(argv[0], cmds[i].name) == 0) {806if ((cmds[i].args != -1) &&807(argc < (cmds[i].args + 1))) {808printf("%s needs %d argument%s\n",809cmds[i].name, cmds[i].args,810(cmds[i].args==1)?"":",");811break;812}813814r = (cmds[i].f)(&cfg, argc, argv);815816/* -1 here means "error" */817if (r == -1) {818argc = 0;819break;820}821822/* Legacy return value */823if (r == 0)824r = cmds[i].args;825826argc -= r;827argv += r;828break;829}830}831if (cmds[i].name == NULL) {832newmode(&cfg, MODE_NONE);833continue;834}835break;836case MODE_REGISTER:837if (set_register(&cfg, argv[0]) != 0) {838newmode(&cfg, MODE_NONE);839continue;840}841break;842case MODE_PHYREG:843if (set_phyregister(&cfg, argv[0]) != 0) {844newmode(&cfg, MODE_NONE);845continue;846}847break;848}849argc--;850argv++;851}852/* switch back to command mode to print configuration for last command */853newmode(&cfg, MODE_NONE);854close(cfg.fd);855return (0);856}857858static struct cmds cmds[] = {859{ MODE_PORT, "pvid", 1, set_port_vid },860{ MODE_PORT, "media", 1, set_port_media },861{ MODE_PORT, "mediaopt", 1, set_port_mediaopt },862{ MODE_PORT, "led", 2, set_port_led },863{ MODE_PORT, "addtag", 0, set_port_flag },864{ MODE_PORT, "-addtag", 0, set_port_flag },865{ MODE_PORT, "ingress", 0, set_port_flag },866{ MODE_PORT, "-ingress", 0, set_port_flag },867{ MODE_PORT, "striptag", 0, set_port_flag },868{ MODE_PORT, "-striptag", 0, set_port_flag },869{ MODE_PORT, "striptagingress", 0, set_port_flag },870{ MODE_PORT, "-striptagingress", 0, set_port_flag },871{ MODE_PORT, "doubletag", 0, set_port_flag },872{ MODE_PORT, "-doubletag", 0, set_port_flag },873{ MODE_PORT, "firstlock", 0, set_port_flag },874{ MODE_PORT, "-firstlock", 0, set_port_flag },875{ MODE_PORT, "droptagged", 0, set_port_flag },876{ MODE_PORT, "-droptagged", 0, set_port_flag },877{ MODE_PORT, "dropuntagged", 0, set_port_flag },878{ MODE_PORT, "-dropuntagged", 0, set_port_flag },879{ MODE_CONFIG, "vlan_mode", 1, set_vlan_mode },880{ MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid },881{ MODE_VLANGROUP, "members", 1, set_vlangroup_members },882{ MODE_ATU, "flush", -1, atu_flush },883{ MODE_ATU, "dump", -1, atu_dump },884{ 0, NULL, 0, NULL }885};886887888