e6e5db845a
This adds the support to the config keyword (vlan operation mode), ports flags, prints the vlan mode and vlan capabilities. It also adds some basic information to usage() and support the keyword 'help' as a shortcut to usage(). The manual page is also updated with the new options. Submitted by: Luiz Otavio O Souza <loos.br@gmail.com> Reviewed by: ray
706 lines
18 KiB
C
706 lines
18 KiB
C
/*-
|
|
* 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[];
|
|
|
|
|
|
/*
|
|
* 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)");
|
|
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_vlangroup_vid(struct cfg *cfg, char *argv[])
|
|
{
|
|
int v;
|
|
etherswitch_vlangroup_t 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;
|
|
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 = strtol(c+1, NULL, 0);
|
|
write_register(cfg, a, v);
|
|
}
|
|
printf("\treg 0x%04x=0x%04x\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 = strtol(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");
|
|
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 == 0 && vg.es_member_ports == 0)
|
|
return;
|
|
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\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);
|
|
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, "port unit must be between 0 and %d", cfg.info.es_nvlangroups);
|
|
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 an argument\n", cmds[i].name);
|
|
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, "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 }
|
|
};
|