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