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