7551d83c35
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts. No functional change intended.
757 lines
19 KiB
C
757 lines
19 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2011-2012 Stefan Bethke.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#include <net/if_media.h>
|
|
#include <dev/etherswitch/etherswitch.h>
|
|
|
|
int get_media_subtype(int, const char *);
|
|
int get_media_mode(int, const char *);
|
|
int get_media_options(int, const char *);
|
|
int lookup_media_word(struct ifmedia_description *, const char *);
|
|
void print_media_word(int, int);
|
|
void print_media_word_ifconfig(int);
|
|
|
|
/* some constants */
|
|
#define IEEE802DOT1Q_VID_MAX 4094
|
|
#define IFMEDIAREQ_NULISTENTRIES 256
|
|
|
|
enum cmdmode {
|
|
MODE_NONE = 0,
|
|
MODE_PORT,
|
|
MODE_CONFIG,
|
|
MODE_VLANGROUP,
|
|
MODE_REGISTER,
|
|
MODE_PHYREG
|
|
};
|
|
|
|
struct cfg {
|
|
int fd;
|
|
int verbose;
|
|
int mediatypes;
|
|
const char *controlfile;
|
|
etherswitch_conf_t conf;
|
|
etherswitch_info_t info;
|
|
enum cmdmode mode;
|
|
int unit;
|
|
};
|
|
|
|
struct cmds {
|
|
enum cmdmode mode;
|
|
const char *name;
|
|
int args;
|
|
void (*f)(struct cfg *, char *argv[]);
|
|
};
|
|
static struct cmds cmds[];
|
|
|
|
/* Must match the ETHERSWITCH_PORT_LED_* enum order */
|
|
static const char *ledstyles[] = { "default", "on", "off", "blink", NULL };
|
|
|
|
/*
|
|
* Print a value a la the %b format of the kernel's printf.
|
|
* Stolen from ifconfig.c.
|
|
*/
|
|
static void
|
|
printb(const char *s, unsigned v, const char *bits)
|
|
{
|
|
int i, any = 0;
|
|
char c;
|
|
|
|
if (bits && *bits == 8)
|
|
printf("%s=%o", s, v);
|
|
else
|
|
printf("%s=%x", s, v);
|
|
bits++;
|
|
if (bits) {
|
|
putchar('<');
|
|
while ((i = *bits++) != '\0') {
|
|
if (v & (1 << (i-1))) {
|
|
if (any)
|
|
putchar(',');
|
|
any = 1;
|
|
for (; (c = *bits) > 32; bits++)
|
|
putchar(c);
|
|
} else
|
|
for (; *bits > 32; bits++)
|
|
;
|
|
}
|
|
putchar('>');
|
|
}
|
|
}
|
|
|
|
static int
|
|
read_register(struct cfg *cfg, int r)
|
|
{
|
|
struct etherswitch_reg er;
|
|
|
|
er.reg = r;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)");
|
|
return (er.val);
|
|
}
|
|
|
|
static void
|
|
write_register(struct cfg *cfg, int r, int v)
|
|
{
|
|
struct etherswitch_reg er;
|
|
|
|
er.reg = r;
|
|
er.val = v;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)");
|
|
}
|
|
|
|
static int
|
|
read_phyregister(struct cfg *cfg, int phy, int reg)
|
|
{
|
|
struct etherswitch_phyreg er;
|
|
|
|
er.phy = phy;
|
|
er.reg = reg;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)");
|
|
return (er.val);
|
|
}
|
|
|
|
static void
|
|
write_phyregister(struct cfg *cfg, int phy, int reg, int val)
|
|
{
|
|
struct etherswitch_phyreg er;
|
|
|
|
er.phy = phy;
|
|
er.reg = reg;
|
|
er.val = val;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)");
|
|
}
|
|
|
|
static void
|
|
set_port_vid(struct cfg *cfg, char *argv[])
|
|
{
|
|
int v;
|
|
etherswitch_port_t p;
|
|
|
|
v = strtol(argv[1], NULL, 0);
|
|
if (v < 0 || v > IEEE802DOT1Q_VID_MAX)
|
|
errx(EX_USAGE, "pvid must be between 0 and %d",
|
|
IEEE802DOT1Q_VID_MAX);
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = cfg->unit;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
p.es_pvid = v;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
|
|
}
|
|
|
|
static void
|
|
set_port_flag(struct cfg *cfg, char *argv[])
|
|
{
|
|
char *flag;
|
|
int n;
|
|
uint32_t f;
|
|
etherswitch_port_t p;
|
|
|
|
n = 0;
|
|
f = 0;
|
|
flag = argv[0];
|
|
if (strcmp(flag, "none") != 0) {
|
|
if (*flag == '-') {
|
|
n++;
|
|
flag++;
|
|
}
|
|
if (strcasecmp(flag, "striptag") == 0)
|
|
f = ETHERSWITCH_PORT_STRIPTAG;
|
|
else if (strcasecmp(flag, "addtag") == 0)
|
|
f = ETHERSWITCH_PORT_ADDTAG;
|
|
else if (strcasecmp(flag, "firstlock") == 0)
|
|
f = ETHERSWITCH_PORT_FIRSTLOCK;
|
|
else if (strcasecmp(flag, "dropuntagged") == 0)
|
|
f = ETHERSWITCH_PORT_DROPUNTAGGED;
|
|
else if (strcasecmp(flag, "doubletag") == 0)
|
|
f = ETHERSWITCH_PORT_DOUBLE_TAG;
|
|
else if (strcasecmp(flag, "ingress") == 0)
|
|
f = ETHERSWITCH_PORT_INGRESS;
|
|
}
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = cfg->unit;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
if (n)
|
|
p.es_flags &= ~f;
|
|
else
|
|
p.es_flags |= f;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
|
|
}
|
|
|
|
static void
|
|
set_port_media(struct cfg *cfg, char *argv[])
|
|
{
|
|
etherswitch_port_t p;
|
|
int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
|
|
int subtype;
|
|
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = cfg->unit;
|
|
p.es_ifmr.ifm_ulist = ifm_ulist;
|
|
p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
if (p.es_ifmr.ifm_count == 0)
|
|
return;
|
|
subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]);
|
|
p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) |
|
|
IFM_TYPE(ifm_ulist[0]) | subtype;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
|
|
}
|
|
|
|
static void
|
|
set_port_mediaopt(struct cfg *cfg, char *argv[])
|
|
{
|
|
etherswitch_port_t p;
|
|
int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
|
|
int options;
|
|
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = cfg->unit;
|
|
p.es_ifmr.ifm_ulist = ifm_ulist;
|
|
p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]);
|
|
if (options == -1)
|
|
errx(EX_USAGE, "invalid media options \"%s\"", argv[1]);
|
|
if (options & IFM_HDX) {
|
|
p.es_ifr.ifr_media &= ~IFM_FDX;
|
|
options &= ~IFM_HDX;
|
|
}
|
|
p.es_ifr.ifr_media |= options;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
|
|
}
|
|
|
|
static void
|
|
set_port_led(struct cfg *cfg, char *argv[])
|
|
{
|
|
etherswitch_port_t p;
|
|
int led;
|
|
int i;
|
|
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = cfg->unit;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
|
|
led = strtol(argv[1], NULL, 0);
|
|
if (led < 1 || led > p.es_nleds)
|
|
errx(EX_USAGE, "invalid led number %s; must be between 1 and %d",
|
|
argv[1], p.es_nleds);
|
|
|
|
led--;
|
|
|
|
for (i=0; ledstyles[i] != NULL; i++) {
|
|
if (strcmp(argv[2], ledstyles[i]) == 0) {
|
|
p.es_led[led] = i;
|
|
break;
|
|
}
|
|
}
|
|
if (ledstyles[i] == NULL)
|
|
errx(EX_USAGE, "invalid led style \"%s\"", argv[2]);
|
|
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)");
|
|
}
|
|
|
|
static void
|
|
set_vlangroup_vid(struct cfg *cfg, char *argv[])
|
|
{
|
|
int v;
|
|
etherswitch_vlangroup_t vg;
|
|
|
|
memset(&vg, 0, sizeof(vg));
|
|
v = strtol(argv[1], NULL, 0);
|
|
if (v < 0 || v > IEEE802DOT1Q_VID_MAX)
|
|
errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX);
|
|
vg.es_vlangroup = cfg->unit;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
|
|
vg.es_vid = v;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
|
|
}
|
|
|
|
static void
|
|
set_vlangroup_members(struct cfg *cfg, char *argv[])
|
|
{
|
|
etherswitch_vlangroup_t vg;
|
|
int member, untagged;
|
|
char *c, *d;
|
|
int v;
|
|
|
|
member = untagged = 0;
|
|
memset(&vg, 0, sizeof(vg));
|
|
if (strcmp(argv[1], "none") != 0) {
|
|
for (c=argv[1]; *c; c=d) {
|
|
v = strtol(c, &d, 0);
|
|
if (d == c)
|
|
break;
|
|
if (v < 0 || v >= cfg->info.es_nports)
|
|
errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1);
|
|
if (d[0] == ',' || d[0] == '\0' ||
|
|
((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) {
|
|
if (d[0] == 't' || d[0] == 'T') {
|
|
untagged &= ~ETHERSWITCH_PORTMASK(v);
|
|
d++;
|
|
} else
|
|
untagged |= ETHERSWITCH_PORTMASK(v);
|
|
member |= ETHERSWITCH_PORTMASK(v);
|
|
d++;
|
|
} else
|
|
errx(EX_USAGE, "Invalid members specification \"%s\"", d);
|
|
}
|
|
}
|
|
vg.es_vlangroup = cfg->unit;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
|
|
vg.es_member_ports = member;
|
|
vg.es_untagged_ports = untagged;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)");
|
|
}
|
|
|
|
static int
|
|
set_register(struct cfg *cfg, char *arg)
|
|
{
|
|
int a, v;
|
|
char *c;
|
|
|
|
a = strtol(arg, &c, 0);
|
|
if (c==arg)
|
|
return (1);
|
|
if (*c == '=') {
|
|
v = strtoul(c+1, NULL, 0);
|
|
write_register(cfg, a, v);
|
|
}
|
|
printf("\treg 0x%04x=0x%08x\n", a, read_register(cfg, a));
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
set_phyregister(struct cfg *cfg, char *arg)
|
|
{
|
|
int phy, reg, val;
|
|
char *c, *d;
|
|
|
|
phy = strtol(arg, &c, 0);
|
|
if (c==arg)
|
|
return (1);
|
|
if (*c != '.')
|
|
return (1);
|
|
d = c+1;
|
|
reg = strtol(d, &c, 0);
|
|
if (d == c)
|
|
return (1);
|
|
if (*c == '=') {
|
|
val = strtoul(c+1, NULL, 0);
|
|
write_phyregister(cfg, phy, reg, val);
|
|
}
|
|
printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg));
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
set_vlan_mode(struct cfg *cfg, char *argv[])
|
|
{
|
|
etherswitch_conf_t conf;
|
|
|
|
bzero(&conf, sizeof(conf));
|
|
conf.cmd = ETHERSWITCH_CONF_VLAN_MODE;
|
|
if (strcasecmp(argv[1], "isl") == 0)
|
|
conf.vlan_mode = ETHERSWITCH_VLAN_ISL;
|
|
else if (strcasecmp(argv[1], "port") == 0)
|
|
conf.vlan_mode = ETHERSWITCH_VLAN_PORT;
|
|
else if (strcasecmp(argv[1], "dot1q") == 0)
|
|
conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
|
|
else if (strcasecmp(argv[1], "dot1q4k") == 0)
|
|
conf.vlan_mode = ETHERSWITCH_VLAN_DOT1Q_4K;
|
|
else if (strcasecmp(argv[1], "qinq") == 0)
|
|
conf.vlan_mode = ETHERSWITCH_VLAN_DOUBLE_TAG;
|
|
else
|
|
conf.vlan_mode = 0;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHSETCONF, &conf) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHSETCONF)");
|
|
}
|
|
|
|
static void
|
|
print_config(struct cfg *cfg)
|
|
{
|
|
const char *c;
|
|
|
|
/* Get the device name. */
|
|
c = strrchr(cfg->controlfile, '/');
|
|
if (c != NULL)
|
|
c = c + 1;
|
|
else
|
|
c = cfg->controlfile;
|
|
|
|
/* Print VLAN mode. */
|
|
if (cfg->conf.cmd & ETHERSWITCH_CONF_VLAN_MODE) {
|
|
printf("%s: VLAN mode: ", c);
|
|
switch (cfg->conf.vlan_mode) {
|
|
case ETHERSWITCH_VLAN_ISL:
|
|
printf("ISL\n");
|
|
break;
|
|
case ETHERSWITCH_VLAN_PORT:
|
|
printf("PORT\n");
|
|
break;
|
|
case ETHERSWITCH_VLAN_DOT1Q:
|
|
printf("DOT1Q\n");
|
|
break;
|
|
case ETHERSWITCH_VLAN_DOT1Q_4K:
|
|
printf("DOT1Q4K\n");
|
|
break;
|
|
case ETHERSWITCH_VLAN_DOUBLE_TAG:
|
|
printf("QinQ\n");
|
|
break;
|
|
default:
|
|
printf("none\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_port(struct cfg *cfg, int port)
|
|
{
|
|
etherswitch_port_t p;
|
|
int ifm_ulist[IFMEDIAREQ_NULISTENTRIES];
|
|
int i;
|
|
|
|
bzero(&p, sizeof(p));
|
|
p.es_port = port;
|
|
p.es_ifmr.ifm_ulist = ifm_ulist;
|
|
p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)");
|
|
printf("port%d:\n", port);
|
|
if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_DOT1Q)
|
|
printf("\tpvid: %d\n", p.es_pvid);
|
|
printb("\tflags", p.es_flags, ETHERSWITCH_PORT_FLAGS_BITS);
|
|
printf("\n");
|
|
if (p.es_nleds) {
|
|
printf("\tled: ");
|
|
for (i = 0; i < p.es_nleds; i++) {
|
|
printf("%d:%s%s", i+1, ledstyles[p.es_led[i]], (i==p.es_nleds-1)?"":" ");
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("\tmedia: ");
|
|
print_media_word(p.es_ifmr.ifm_current, 1);
|
|
if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) {
|
|
putchar(' ');
|
|
putchar('(');
|
|
print_media_word(p.es_ifmr.ifm_active, 0);
|
|
putchar(')');
|
|
}
|
|
putchar('\n');
|
|
printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier");
|
|
if (cfg->mediatypes) {
|
|
printf("\tsupported media:\n");
|
|
if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES)
|
|
p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES;
|
|
for (i=0; i<p.es_ifmr.ifm_count; i++) {
|
|
printf("\t\tmedia ");
|
|
print_media_word(ifm_ulist[i], 0);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_vlangroup(struct cfg *cfg, int vlangroup)
|
|
{
|
|
etherswitch_vlangroup_t vg;
|
|
int i, comma;
|
|
|
|
vg.es_vlangroup = vlangroup;
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)");
|
|
if ((vg.es_vid & ETHERSWITCH_VID_VALID) == 0)
|
|
return;
|
|
vg.es_vid &= ETHERSWITCH_VID_MASK;
|
|
printf("vlangroup%d:\n", vlangroup);
|
|
if (cfg->conf.vlan_mode == ETHERSWITCH_VLAN_PORT)
|
|
printf("\tport: %d\n", vg.es_vid);
|
|
else
|
|
printf("\tvlan: %d\n", vg.es_vid);
|
|
printf("\tmembers ");
|
|
comma = 0;
|
|
if (vg.es_member_ports != 0)
|
|
for (i=0; i<cfg->info.es_nports; i++) {
|
|
if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) {
|
|
if (comma)
|
|
printf(",");
|
|
printf("%d", i);
|
|
if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0)
|
|
printf("t");
|
|
comma = 1;
|
|
}
|
|
}
|
|
else
|
|
printf("none");
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
print_info(struct cfg *cfg)
|
|
{
|
|
const char *c;
|
|
int i;
|
|
|
|
c = strrchr(cfg->controlfile, '/');
|
|
if (c != NULL)
|
|
c = c + 1;
|
|
else
|
|
c = cfg->controlfile;
|
|
if (cfg->verbose) {
|
|
printf("%s: %s with %d ports and %d VLAN groups\n", c,
|
|
cfg->info.es_name, cfg->info.es_nports,
|
|
cfg->info.es_nvlangroups);
|
|
printf("%s: ", c);
|
|
printb("VLAN capabilities", cfg->info.es_vlan_caps,
|
|
ETHERSWITCH_VLAN_CAPS_BITS);
|
|
printf("\n");
|
|
}
|
|
print_config(cfg);
|
|
for (i=0; i<cfg->info.es_nports; i++) {
|
|
print_port(cfg, i);
|
|
}
|
|
for (i=0; i<cfg->info.es_nvlangroups; i++) {
|
|
print_vlangroup(cfg, i);
|
|
}
|
|
}
|
|
|
|
static void
|
|
usage(struct cfg *cfg __unused, char *argv[] __unused)
|
|
{
|
|
fprintf(stderr, "usage: etherswitchctl\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] info\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] config "
|
|
"command parameter\n");
|
|
fprintf(stderr, "\t\tconfig commands: vlan_mode\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] phy "
|
|
"phy.register[=value]\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] portX "
|
|
"[flags] command parameter\n");
|
|
fprintf(stderr, "\t\tport commands: pvid, media, mediaopt, led\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] reg "
|
|
"register[=value]\n");
|
|
fprintf(stderr, "\tetherswitchcfg [-f control file] vlangroupX "
|
|
"command parameter\n");
|
|
fprintf(stderr, "\t\tvlangroup commands: vlan, members\n");
|
|
exit(EX_USAGE);
|
|
}
|
|
|
|
static void
|
|
newmode(struct cfg *cfg, enum cmdmode mode)
|
|
{
|
|
if (mode == cfg->mode)
|
|
return;
|
|
switch (cfg->mode) {
|
|
case MODE_NONE:
|
|
break;
|
|
case MODE_CONFIG:
|
|
/*
|
|
* Read the updated the configuration (it can be different
|
|
* from the last time we read it).
|
|
*/
|
|
if (ioctl(cfg->fd, IOETHERSWITCHGETCONF, &cfg->conf) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
|
|
print_config(cfg);
|
|
break;
|
|
case MODE_PORT:
|
|
print_port(cfg, cfg->unit);
|
|
break;
|
|
case MODE_VLANGROUP:
|
|
print_vlangroup(cfg, cfg->unit);
|
|
break;
|
|
case MODE_REGISTER:
|
|
case MODE_PHYREG:
|
|
break;
|
|
}
|
|
cfg->mode = mode;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int ch;
|
|
struct cfg cfg;
|
|
int i;
|
|
|
|
bzero(&cfg, sizeof(cfg));
|
|
cfg.controlfile = "/dev/etherswitch0";
|
|
while ((ch = getopt(argc, argv, "f:mv?")) != -1)
|
|
switch(ch) {
|
|
case 'f':
|
|
cfg.controlfile = optarg;
|
|
break;
|
|
case 'm':
|
|
cfg.mediatypes++;
|
|
break;
|
|
case 'v':
|
|
cfg.verbose++;
|
|
break;
|
|
case '?':
|
|
/* FALLTHROUGH */
|
|
default:
|
|
usage(&cfg, argv);
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
cfg.fd = open(cfg.controlfile, O_RDONLY);
|
|
if (cfg.fd < 0)
|
|
err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile);
|
|
if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)");
|
|
if (ioctl(cfg.fd, IOETHERSWITCHGETCONF, &cfg.conf) != 0)
|
|
err(EX_OSERR, "ioctl(IOETHERSWITCHGETCONF)");
|
|
if (argc == 0) {
|
|
print_info(&cfg);
|
|
return (0);
|
|
}
|
|
cfg.mode = MODE_NONE;
|
|
while (argc > 0) {
|
|
switch(cfg.mode) {
|
|
case MODE_NONE:
|
|
if (strcmp(argv[0], "info") == 0) {
|
|
print_info(&cfg);
|
|
} else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) {
|
|
if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports)
|
|
errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports - 1);
|
|
newmode(&cfg, MODE_PORT);
|
|
} else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) {
|
|
if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups)
|
|
errx(EX_USAGE,
|
|
"vlangroup unit must be between 0 and %d",
|
|
cfg.info.es_nvlangroups - 1);
|
|
newmode(&cfg, MODE_VLANGROUP);
|
|
} else if (strcmp(argv[0], "config") == 0) {
|
|
newmode(&cfg, MODE_CONFIG);
|
|
} else if (strcmp(argv[0], "phy") == 0) {
|
|
newmode(&cfg, MODE_PHYREG);
|
|
} else if (strcmp(argv[0], "reg") == 0) {
|
|
newmode(&cfg, MODE_REGISTER);
|
|
} else if (strcmp(argv[0], "help") == 0) {
|
|
usage(&cfg, argv);
|
|
} else {
|
|
errx(EX_USAGE, "Unknown command \"%s\"", argv[0]);
|
|
}
|
|
break;
|
|
case MODE_PORT:
|
|
case MODE_CONFIG:
|
|
case MODE_VLANGROUP:
|
|
for(i=0; cmds[i].name != NULL; i++) {
|
|
if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0) {
|
|
if (argc < (cmds[i].args + 1)) {
|
|
printf("%s needs %d argument%s\n", cmds[i].name, cmds[i].args, (cmds[i].args==1)?"":",");
|
|
break;
|
|
}
|
|
(cmds[i].f)(&cfg, argv);
|
|
argc -= cmds[i].args;
|
|
argv += cmds[i].args;
|
|
break;
|
|
}
|
|
}
|
|
if (cmds[i].name == NULL) {
|
|
newmode(&cfg, MODE_NONE);
|
|
continue;
|
|
}
|
|
break;
|
|
case MODE_REGISTER:
|
|
if (set_register(&cfg, argv[0]) != 0) {
|
|
newmode(&cfg, MODE_NONE);
|
|
continue;
|
|
}
|
|
break;
|
|
case MODE_PHYREG:
|
|
if (set_phyregister(&cfg, argv[0]) != 0) {
|
|
newmode(&cfg, MODE_NONE);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
argc--;
|
|
argv++;
|
|
}
|
|
/* switch back to command mode to print configuration for last command */
|
|
newmode(&cfg, MODE_NONE);
|
|
close(cfg.fd);
|
|
return (0);
|
|
}
|
|
|
|
static struct cmds cmds[] = {
|
|
{ MODE_PORT, "pvid", 1, set_port_vid },
|
|
{ MODE_PORT, "media", 1, set_port_media },
|
|
{ MODE_PORT, "mediaopt", 1, set_port_mediaopt },
|
|
{ MODE_PORT, "led", 2, set_port_led },
|
|
{ MODE_PORT, "addtag", 0, set_port_flag },
|
|
{ MODE_PORT, "-addtag", 0, set_port_flag },
|
|
{ MODE_PORT, "ingress", 0, set_port_flag },
|
|
{ MODE_PORT, "-ingress", 0, set_port_flag },
|
|
{ MODE_PORT, "striptag", 0, set_port_flag },
|
|
{ MODE_PORT, "-striptag", 0, set_port_flag },
|
|
{ MODE_PORT, "doubletag", 0, set_port_flag },
|
|
{ MODE_PORT, "-doubletag", 0, set_port_flag },
|
|
{ MODE_PORT, "firstlock", 0, set_port_flag },
|
|
{ MODE_PORT, "-firstlock", 0, set_port_flag },
|
|
{ MODE_PORT, "dropuntagged", 0, set_port_flag },
|
|
{ MODE_PORT, "-dropuntagged", 0, set_port_flag },
|
|
{ MODE_CONFIG, "vlan_mode", 1, set_vlan_mode },
|
|
{ MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid },
|
|
{ MODE_VLANGROUP, "members", 1, set_vlangroup_members },
|
|
{ 0, NULL, 0, NULL }
|
|
};
|