]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/etherswitchcfg/etherswitchcfg.c
cdn-patch: offer option to mount /etc/keys before attaching geli devices
[FreeBSD/FreeBSD.git] / sbin / etherswitchcfg / etherswitchcfg.c
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, "dropuntagged") == 0)
219                         f = ETHERSWITCH_PORT_DROPUNTAGGED;
220                 else if (strcasecmp(flag, "doubletag") == 0)
221                         f = ETHERSWITCH_PORT_DOUBLE_TAG;
222                 else if (strcasecmp(flag, "ingress") == 0)
223                         f = ETHERSWITCH_PORT_INGRESS;
224         }
225         bzero(&p, sizeof(p));
226         p.es_port = cfg->unit;
227         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
228                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
229         if (n)
230                 p.es_flags &= ~f;
231         else
232                 p.es_flags |= f;
233         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
234                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
235         return (0);
236 }
237
238 static int
239 set_port_media(struct cfg *cfg, int argc, char *argv[])
240 {
241         etherswitch_port_t p;
242         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
243         int subtype;
244
245         if (argc < 2)
246                 return (-1);
247
248         bzero(&p, sizeof(p));
249         p.es_port = cfg->unit;
250         p.es_ifmr.ifm_ulist = ifm_ulist;
251         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
252         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
253                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
254         if (p.es_ifmr.ifm_count == 0)
255                 return (0);
256         subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]);
257         p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) |
258                 IFM_TYPE(ifm_ulist[0]) | subtype;
259         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
260                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
261         return (0);
262 }
263
264 static int
265 set_port_mediaopt(struct cfg *cfg, int argc, char *argv[])
266 {
267         etherswitch_port_t p;
268         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
269         int options;
270
271         if (argc < 2)
272                 return (-1);
273
274         bzero(&p, sizeof(p));
275         p.es_port = cfg->unit;
276         p.es_ifmr.ifm_ulist = ifm_ulist;
277         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
278         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
279                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
280         options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]);
281         if (options == -1)
282                 errx(EX_USAGE, "invalid media options \"%s\"", argv[1]);
283         if (options & IFM_HDX) {
284                 p.es_ifr.ifr_media &= ~IFM_FDX;
285                 options &= ~IFM_HDX;
286         }
287         p.es_ifr.ifr_media |= options;
288         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
289                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
290         return (0);
291 }
292
293 static int
294 set_port_led(struct cfg *cfg, int argc, char *argv[])
295 {
296         etherswitch_port_t p;
297         int led;
298         int i;
299
300         if (argc < 3)
301                 return (-1);
302
303         bzero(&p, sizeof(p));
304         p.es_port = cfg->unit;
305         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
306                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
307
308         led = strtol(argv[1], NULL, 0);
309         if (led < 1 || led > p.es_nleds)
310                 errx(EX_USAGE, "invalid led number %s; must be between 1 and %d",
311                         argv[1], p.es_nleds);
312
313         led--;
314
315         for (i=0; ledstyles[i] != NULL; i++) {
316                 if (strcmp(argv[2], ledstyles[i]) == 0) {
317                         p.es_led[led] = i;
318                         break;
319                 }
320         } 
321         if (ledstyles[i] == NULL)
322                 errx(EX_USAGE, "invalid led style \"%s\"", argv[2]);
323
324         if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
325                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
326
327         return (0);
328 }
329
330 static int
331 set_vlangroup_vid(struct cfg *cfg, int argc, char *argv[])
332 {
333         int v;
334         etherswitch_vlangroup_t vg;
335
336         if (argc < 2)
337                 return (-1);
338
339         memset(&vg, 0, sizeof(vg));
340         v = strtol(argv[1], NULL, 0);
341         if (v < 0 || v > IEEE802DOT1Q_VID_MAX)
342                 errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX);
343         vg.es_vlangroup = cfg->unit;
344         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
345                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
346         vg.es_vid = v;
347         if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
348                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
349         return (0);
350 }
351
352 static int
353 set_vlangroup_members(struct cfg *cfg, int argc, char *argv[])
354 {
355         etherswitch_vlangroup_t vg;
356         int member, untagged;
357         char *c, *d;
358         int v;
359
360         if (argc < 2)
361                 return (-1);
362
363         member = untagged = 0;
364         memset(&vg, 0, sizeof(vg));
365         if (strcmp(argv[1], "none") != 0) {
366                 for (c=argv[1]; *c; c=d) {
367                         v = strtol(c, &d, 0);
368                         if (d == c)
369                                 break;
370                         if (v < 0 || v >= cfg->info.es_nports)
371                                 errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1);
372                         if (d[0] == ',' || d[0] == '\0' ||
373                                 ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) {
374                                 if (d[0] == 't' || d[0] == 'T') {
375                                         untagged &= ~ETHERSWITCH_PORTMASK(v);
376                                         d++;
377                                 } else
378                                         untagged |= ETHERSWITCH_PORTMASK(v);
379                                 member |= ETHERSWITCH_PORTMASK(v);
380                                 d++;
381                         } else
382                                 errx(EX_USAGE, "Invalid members specification \"%s\"", d);
383                 }
384         }
385         vg.es_vlangroup = cfg->unit;
386         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
387                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
388         vg.es_member_ports = member;
389         vg.es_untagged_ports = untagged;
390         if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
391                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
392         return (0);
393 }
394
395 static int
396 set_register(struct cfg *cfg, char *arg)
397 {
398         int a, v;
399         char *c;
400         
401         a = strtol(arg, &c, 0);
402         if (c==arg)
403                 return (1);
404         if (*c == '=') {
405                 v = strtoul(c+1, NULL, 0);
406                 write_register(cfg, a, v);
407         }
408         printf("\treg 0x%04x=0x%08x\n", a, read_register(cfg, a));
409         return (0);
410 }
411
412 static int
413 set_phyregister(struct cfg *cfg, char *arg)
414 {
415         int phy, reg, val;
416         char *c, *d;
417         
418         phy = strtol(arg, &c, 0);
419         if (c==arg)
420                 return (1);
421         if (*c != '.')
422                 return (1);
423         d = c+1;
424         reg = strtol(d, &c, 0);
425         if (d == c)
426                 return (1);
427         if (*c == '=') {
428                 val = strtoul(c+1, NULL, 0);
429                 write_phyregister(cfg, phy, reg, val);
430         }
431         printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg));
432         return (0);
433 }
434
435 static int
436 set_vlan_mode(struct cfg *cfg, int argc, char *argv[])
437 {
438         etherswitch_conf_t conf;
439
440         if (argc < 2)
441                 return (-1);
442
443         bzero(&conf, sizeof(conf));
444         conf.cmd = ETHERSWITCH_CONF_VLAN_MODE;
445         if (strcasecmp(argv[1], "isl") == 0)
446                 conf.vlan_mode = ETHERSWITCH_VLAN_ISL;
447         else if (strcasecmp(argv[1], "port") == 0)
448                 conf.vlan_mode = ETHERSWITCH_VLAN_PORT;
449         else if (strcasecmp(argv[1], "dot1q") == 0)
450                 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
451         else if (strcasecmp(argv[1], "dot1q4k") == 0)
452                 conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q_4K;
453         else if (strcasecmp(argv[1], "qinq") == 0)
454                 conf.vlan_mode = ETHERSWITCH_VLAN_DOUBLE_TAG;
455         else
456                 conf.vlan_mode = 0;
457         if (ioctl(cfg->fd, IOETHERSWITCHSETCONF, &conf) != 0)
458                 err(EX_OSERR, "ioctl(IOETHERSWITCHSETCONF)");
459
460         return (0);
461 }
462
463 static int
464 atu_flush(struct cfg *cfg, int argc, char *argv[])
465 {
466         etherswitch_portid_t p;
467         int i, r;
468
469         bzero(&p, sizeof(p));
470
471         /* note: argv[0] is "flush" */
472         if (argc > 2 && strcasecmp(argv[1], "port") == 0) {
473                 p.es_port = atoi(argv[2]);
474                 i = IOETHERSWITCHFLUSHPORT;
475                 r = 3;
476         } else if (argc > 1 && strcasecmp(argv[1], "all") == 0) {
477                 p.es_port = 0;
478                 r = 2;
479                 i = IOETHERSWITCHFLUSHALL;
480         } else {
481                 fprintf(stderr,
482                     "%s: invalid verb (port <x> or all) (got %s)\n",
483                     __func__, argv[1]);
484                 return (-1);
485         }
486
487         if (ioctl(cfg->fd, i, &p) != 0)
488                 err(EX_OSERR, "ioctl(ATU flush (ioctl %d, port %d))",
489                     i, p.es_port);
490         return (r);
491 }
492
493 static int
494 atu_dump(struct cfg *cfg, int argc, char *argv[])
495 {
496         etherswitch_atu_table_t p;
497         etherswitch_atu_entry_t e;
498         uint32_t i;
499
500         (void) argc;
501         (void) argv;
502
503         /* Note: argv[0] is "dump" */
504         bzero(&p, sizeof(p));
505
506         if (ioctl(cfg->fd, IOETHERSWITCHGETTABLE, &p) != 0)
507                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETTABLE)");
508
509         /* And now, iterate to get entries */
510         for (i = 0; i < p.es_nitems; i++) {
511                 bzero(&e, sizeof(e));
512                 e.id = i;
513                 if (ioctl(cfg->fd, IOETHERSWITCHGETTABLEENTRY, &e) != 0)
514                         break;
515
516                 printf(" [%d] %s: portmask 0x%08x\n", i,
517                     ether_ntoa((void *) &e.es_macaddr),
518                     e.es_portmask);
519         }
520
521         return (1);
522 }
523
524 static void
525 print_config(struct cfg *cfg)
526 {
527         const char *c;
528
529         /* Get the device name. */
530         c = strrchr(cfg->controlfile, '/');
531         if (c != NULL)
532                 c = c + 1;
533         else
534                 c = cfg->controlfile;
535
536         /* Print VLAN mode. */
537         if (cfg->conf.cmd & ETHERSWITCH_CONF_VLAN_MODE) {
538                 printf("%s: VLAN mode: ", c);
539                 switch (cfg->conf.vlan_mode) {
540                 case ETHERSWITCH_VLAN_ISL:
541                         printf("ISL\n");
542                         break;
543                 case ETHERSWITCH_VLAN_PORT:
544                         printf("PORT\n");
545                         break;
546                 case ETHERSWITCH_VLAN_DOT1Q:
547                         printf("DOT1Q\n");
548                         break;
549                 case ETHERSWITCH_VLAN_DOT1Q_4K:
550                         printf("DOT1Q4K\n");
551                         break;
552                 case ETHERSWITCH_VLAN_DOUBLE_TAG:
553                         printf("QinQ\n");
554                         break;
555                 default:
556                         printf("none\n");
557                 }
558         }
559
560         /* Print switch MAC address. */
561         if (cfg->conf.cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) {
562                 printf("%s: Switch MAC address: %s\n",
563                     c,
564                     ether_ntoa(&cfg->conf.switch_macaddr));
565         }
566 }
567
568 static void
569 print_port(struct cfg *cfg, int port)
570 {
571         etherswitch_port_t p;
572         int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
573         int i;
574
575         bzero(&p, sizeof(p));
576         p.es_port = port;
577         p.es_ifmr.ifm_ulist = ifm_ulist;
578         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
579         if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
580                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
581         printf("port%d:\n", port);
582         if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
583                 printf("\tpvid: %d\n", p.es_pvid);
584         printb("\tflags", p.es_flags, ETHERSWITCH_PORT_FLAGS_BITS);
585         printf("\n");
586         if (p.es_nleds) {
587                 printf("\tled: ");
588                 for (i = 0; i < p.es_nleds; i++) {
589                         printf("%d:%s%s", i+1, ledstyles[p.es_led[i]], (i==p.es_nleds-1)?"":" ");
590                 }
591                 printf("\n");
592         }
593         printf("\tmedia: ");
594         print_media_word(p.es_ifmr.ifm_current, 1);
595         if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) {
596                 putchar(' ');
597                 putchar('(');
598                 print_media_word(p.es_ifmr.ifm_active, 0);
599                 putchar(')');
600         }
601         putchar('\n');
602         printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier");
603         if (cfg->mediatypes) {
604                 printf("\tsupported media:\n");
605                 if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES)
606                         p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
607                 for (i=0; i<p.es_ifmr.ifm_count; i++) {
608                         printf("\t\tmedia ");
609                         print_media_word(ifm_ulist[i], 0);
610                         putchar('\n');
611                 }
612         }
613 }
614
615 static void
616 print_vlangroup(struct cfg *cfg, int vlangroup)
617 {
618         etherswitch_vlangroup_t vg;
619         int i, comma;
620         
621         vg.es_vlangroup = vlangroup;
622         if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
623                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
624         if ((vg.es_vid & ETHERSWITCH_VID_VALID) == 0)
625                 return;
626         vg.es_vid &= ETHERSWITCH_VID_MASK;
627         printf("vlangroup%d:\n", vlangroup);
628         if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_PORT)
629                 printf("\tport: %d\n", vg.es_vid);
630         else
631                 printf("\tvlan: %d\n", vg.es_vid);
632         printf("\tmembers ");
633         comma = 0;
634         if (vg.es_member_ports != 0)
635                 for (i=0; i<cfg->info.es_nports; i++) {
636                         if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) {
637                                 if (comma)
638                                         printf(",");
639                                 printf("%d", i);
640                                 if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0)
641                                         printf("t");
642                                 comma = 1;
643                         }
644                 }
645         else
646                 printf("none");
647         printf("\n");
648 }
649
650 static void
651 print_info(struct cfg *cfg)
652 {
653         const char *c;
654         int i;
655         
656         c = strrchr(cfg->controlfile, '/');
657         if (c != NULL)
658                 c = c + 1;
659         else
660                 c = cfg->controlfile;
661         if (cfg->verbose) {
662                 printf("%s: %s with %d ports and %d VLAN groups\n", c,
663                     cfg->info.es_name, cfg->info.es_nports,
664                     cfg->info.es_nvlangroups);
665                 printf("%s: ", c);
666                 printb("VLAN capabilities",  cfg->info.es_vlan_caps,
667                     ETHERSWITCH_VLAN_CAPS_BITS);
668                 printf("\n");
669         }
670         print_config(cfg);
671         for (i=0; i<cfg->info.es_nports; i++) {
672                 print_port(cfg, i);
673         }
674         for (i=0; i<cfg->info.es_nvlangroups; i++) {
675                 print_vlangroup(cfg, i);
676         }
677 }
678
679 static void
680 usage(struct cfg *cfg __unused, char *argv[] __unused)
681 {
682         fprintf(stderr, "usage: etherswitchctl\n");
683         fprintf(stderr, "\tetherswitchcfg [-f control file] info\n");
684         fprintf(stderr, "\tetherswitchcfg [-f control file] config "
685             "command parameter\n");
686         fprintf(stderr, "\t\tconfig commands: vlan_mode\n");
687         fprintf(stderr, "\tetherswitchcfg [-f control file] phy "
688             "phy.register[=value]\n");
689         fprintf(stderr, "\tetherswitchcfg [-f control file] portX "
690             "[flags] command parameter\n");
691         fprintf(stderr, "\t\tport commands: pvid, media, mediaopt, led\n");
692         fprintf(stderr, "\tetherswitchcfg [-f control file] reg "
693             "register[=value]\n");
694         fprintf(stderr, "\tetherswitchcfg [-f control file] vlangroupX "
695             "command parameter\n");
696         fprintf(stderr, "\t\tvlangroup commands: vlan, members\n");
697         exit(EX_USAGE);
698 }
699
700 static void
701 newmode(struct cfg *cfg, enum cmdmode mode)
702 {
703         if (mode == cfg->mode)
704                 return;
705         switch (cfg->mode) {
706         case MODE_NONE:
707                 break;
708         case MODE_CONFIG:
709                 /*
710                  * Read the updated the configuration (it can be different
711                  * from the last time we read it).
712                  */
713                 if (ioctl(cfg->fd, IOETHERSWITCHGETCONF, &cfg->conf) != 0)
714                         err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
715                 print_config(cfg);
716                 break;
717         case MODE_PORT:
718                 print_port(cfg, cfg->unit);
719                 break;
720         case MODE_VLANGROUP:
721                 print_vlangroup(cfg, cfg->unit);
722                 break;
723         case MODE_REGISTER:
724         case MODE_PHYREG:
725         case MODE_ATU:
726                 break;
727         }
728         cfg->mode = mode;
729 }
730
731 int
732 main(int argc, char *argv[])
733 {
734         int ch;
735         struct cfg cfg;
736         int i;
737         
738         bzero(&cfg, sizeof(cfg));
739         cfg.controlfile = "/dev/etherswitch0";
740         while ((ch = getopt(argc, argv, "f:mv?")) != -1)
741                 switch(ch) {
742                 case 'f':
743                         cfg.controlfile = optarg;
744                         break;
745                 case 'm':
746                         cfg.mediatypes++;
747                         break;
748                 case 'v':
749                         cfg.verbose++;
750                         break;
751                 case '?':
752                         /* FALLTHROUGH */
753                 default:
754                         usage(&cfg, argv);
755                 }
756         argc -= optind;
757         argv += optind;
758         cfg.fd = open(cfg.controlfile, O_RDONLY);
759         if (cfg.fd < 0)
760                 err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile);
761         if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0)
762                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)");
763         if (ioctl(cfg.fd, IOETHERSWITCHGETCONF, &cfg.conf) != 0)
764                 err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
765         if (argc == 0) {
766                 print_info(&cfg);
767                 return (0);
768         }
769         cfg.mode = MODE_NONE;
770         while (argc > 0) {
771                 switch(cfg.mode) {
772                 case MODE_NONE:
773                         if (strcmp(argv[0], "info") == 0) {
774                                 print_info(&cfg);
775                         } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) {
776                                 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports)
777                                         errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports - 1);
778                                 newmode(&cfg, MODE_PORT);
779                         } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) {
780                                 if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups)
781                                         errx(EX_USAGE,
782                                             "vlangroup unit must be between 0 and %d",
783                                             cfg.info.es_nvlangroups - 1);
784                                 newmode(&cfg, MODE_VLANGROUP);
785                         } else if (strcmp(argv[0], "config") == 0) {
786                                 newmode(&cfg, MODE_CONFIG);
787                         } else if (strcmp(argv[0], "phy") == 0) {
788                                 newmode(&cfg, MODE_PHYREG);
789                         } else if (strcmp(argv[0], "reg") == 0) {
790                                 newmode(&cfg, MODE_REGISTER);
791                         } else if (strcmp(argv[0], "help") == 0) {
792                                 usage(&cfg, argv);
793                         } else if (strcmp(argv[0], "atu") == 0) {
794                                 newmode(&cfg, MODE_ATU);
795                         } else {
796                                 errx(EX_USAGE, "Unknown command \"%s\"", argv[0]);
797                         }
798                         break;
799                 case MODE_PORT:
800                 case MODE_CONFIG:
801                 case MODE_VLANGROUP:
802                 case MODE_ATU:
803                         for(i=0; cmds[i].name != NULL; i++) {
804                                 int r;
805                                 if (cfg.mode == cmds[i].mode &&
806                                     strcmp(argv[0], cmds[i].name) == 0) {
807                                         if ((cmds[i].args != -1) &&
808                                             (argc < (cmds[i].args + 1))) {
809                                                 printf("%s needs %d argument%s\n",
810                                                     cmds[i].name, cmds[i].args,
811                                                     (cmds[i].args==1)?"":",");
812                                                 break;
813                                         }
814
815                                         r = (cmds[i].f)(&cfg, argc, argv);
816
817                                         /* -1 here means "error" */
818                                         if (r == -1) {
819                                                 argc = 0;
820                                                 break;
821                                         }
822
823                                         /* Legacy return value */
824                                         if (r == 0)
825                                                 r = cmds[i].args;
826
827                                         argc -= r;
828                                         argv += r;
829                                         break;
830                                 }
831                         }
832                         if (cmds[i].name == NULL) {
833                                 newmode(&cfg, MODE_NONE);
834                                 continue;
835                         }
836                         break;
837                 case MODE_REGISTER:
838                         if (set_register(&cfg, argv[0]) != 0) {
839                                 newmode(&cfg, MODE_NONE);
840                                 continue;
841                         }
842                         break;
843                 case MODE_PHYREG:
844                         if (set_phyregister(&cfg, argv[0]) != 0) {
845                                 newmode(&cfg, MODE_NONE);
846                                 continue;
847                         }
848                         break;
849                 }
850                 argc--;
851                 argv++;
852         }
853         /* switch back to command mode to print configuration for last command */
854         newmode(&cfg, MODE_NONE);
855         close(cfg.fd);
856         return (0);
857 }
858
859 static struct cmds cmds[] = {
860         { MODE_PORT, "pvid", 1, set_port_vid },
861         { MODE_PORT, "media", 1, set_port_media },
862         { MODE_PORT, "mediaopt", 1, set_port_mediaopt },
863         { MODE_PORT, "led", 2, set_port_led },
864         { MODE_PORT, "addtag", 0, set_port_flag },
865         { MODE_PORT, "-addtag", 0, set_port_flag },
866         { MODE_PORT, "ingress", 0, set_port_flag },
867         { MODE_PORT, "-ingress", 0, set_port_flag },
868         { MODE_PORT, "striptag", 0, set_port_flag },
869         { MODE_PORT, "-striptag", 0, set_port_flag },
870         { MODE_PORT, "doubletag", 0, set_port_flag },
871         { MODE_PORT, "-doubletag", 0, set_port_flag },
872         { MODE_PORT, "firstlock", 0, set_port_flag },
873         { MODE_PORT, "-firstlock", 0, set_port_flag },
874         { MODE_PORT, "dropuntagged", 0, set_port_flag },
875         { MODE_PORT, "-dropuntagged", 0, set_port_flag },
876         { MODE_CONFIG, "vlan_mode", 1, set_vlan_mode },
877         { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid },
878         { MODE_VLANGROUP, "members", 1, set_vlangroup_members },
879         { MODE_ATU, "flush", -1, atu_flush },
880         { MODE_ATU, "dump", -1, atu_dump },
881         { 0, NULL, 0, NULL }
882 };