diff options
Diffstat (limited to 'brctl')
-rw-r--r-- | brctl/Makefile.in | 44 | ||||
-rw-r--r-- | brctl/brctl.c | 87 | ||||
-rw-r--r-- | brctl/brctl.h | 39 | ||||
-rw-r--r-- | brctl/brctl_cmd.c | 493 | ||||
-rw-r--r-- | brctl/brctl_disp.c | 148 |
5 files changed, 811 insertions, 0 deletions
diff --git a/brctl/Makefile.in b/brctl/Makefile.in new file mode 100644 index 0000000..e1956d6 --- /dev/null +++ b/brctl/Makefile.in @@ -0,0 +1,44 @@ + +KERNEL_HEADERS=-I@KERNEL_HEADERS@ + +CC=@CC@ +CFLAGS= -Wall @CFLAGS@ +LDFLAGS=@LDFLAGS@ +INCLUDE=-I../libbridge $(KERNEL_HEADERS) +LIBS= -L ../libbridge -lbridge @LIBS@ + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +sbindir=@sbindir@ +mandir=@mandir@ + +INSTALL=@INSTALL@ + + +common_SOURCES= brctl_cmd.c brctl_disp.c +brctl_SOURCES= brctl.c $(common_SOURCES) + +common_OBJECTS= $(common_SOURCES:.c=.o) +brctl_OBJECTS= $(brctl_SOURCES:.c=.o) + +OBJECTS= $(common_OBJECTS) $(brctl_OBJECTS) + +PROGRAMS= brctl + + +all: $(PROGRAMS) + +install: $(PROGRAMS) + mkdir -p $(DESTDIR)$(sbindir) + $(INSTALL) -m 755 $(PROGRAMS) $(DESTDIR)$(sbindir) + +brctl: $(brctl_OBJECTS) ../libbridge/libbridge.a + $(CC) $(LDFLAGS) $(brctl_OBJECTS) $(LIBS) -o brctl + +%.o: %.c brctl.h + $(CC) $(CFLAGS) $(INCLUDE) -c $< + +clean: + rm -f *.o brctl core + diff --git a/brctl/brctl.c b/brctl/brctl.c new file mode 100644 index 0000000..46ca352 --- /dev/null +++ b/brctl/brctl.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/errno.h> +#include <getopt.h> + +#include "libbridge.h" +#include "config.h" + +#include "brctl.h" + +static void help() +{ + printf("Usage: brctl [commands]\n"); + printf("commands:\n"); + command_helpall(); +} + +int main(int argc, char *const* argv) +{ + const struct command *cmd; + int f; + static const struct option options[] = { + { .name = "help", .val = 'h' }, + { .name = "version", .val = 'V' }, + { 0 } + }; + + while ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF) + switch(f) { + case 'h': + help(); + return 0; + case 'V': + printf("%s, %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return 0; + default: + fprintf(stderr, "Unknown option '%c'\n", f); + goto help; + } + + if (argc == optind) + goto help; + + if (br_init()) { + fprintf(stderr, "can't setup bridge control: %s\n", + strerror(errno)); + return 1; + } + + argc -= optind; + argv += optind; + if ((cmd = command_lookup(*argv)) == NULL) { + fprintf(stderr, "never heard of command [%s]\n", *argv); + goto help; + } + + if (argc < cmd->nargs + 1) { + printf("Incorrect number of arguments for command\n"); + printf("Usage: brctl %s %s\n", cmd->name, cmd->help); + return 1; + } + + return cmd->func(argc, argv); + +help: + help(); + return 1; +} diff --git a/brctl/brctl.h b/brctl/brctl.h new file mode 100644 index 0000000..55b7897 --- /dev/null +++ b/brctl/brctl.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _BRCTL_H +#define _BRCTL_H + +struct command +{ + int nargs; + const char *name; + int (*func)(int argc, char *const* argv); + const char *help; +}; + +const struct command *command_lookup(const char *cmd); +void command_help(const struct command *); +void command_helpall(void); + +void br_dump_bridge_id(const unsigned char *x); +void br_show_timer(const struct timeval *tv); +void br_dump_interface_list(const char *br); +void br_dump_info(const char *br, const struct bridge_info *bri); + +#endif diff --git a/brctl/brctl_cmd.c b/brctl/brctl_cmd.c new file mode 100644 index 0000000..b4ed104 --- /dev/null +++ b/brctl/brctl_cmd.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <errno.h> +#include <asm/param.h> +#include "libbridge.h" +#include "brctl.h" + +static int strtotimeval(struct timeval *tv, const char *time) +{ + double secs; + if (sscanf(time, "%lf", &secs) != 1) + return -1; + tv->tv_sec = secs; + tv->tv_usec = 1000000 * (secs - tv->tv_sec); + return 0; +} + +static int br_cmd_addbr(int argc, char*const* argv) +{ + int err; + + switch (err = br_add_bridge(argv[1])) { + case 0: + return 0; + + case EEXIST: + fprintf(stderr, "device %s already exists; can't create " + "bridge with the same name\n", argv[1]); + return 1; + default: + fprintf(stderr, "add bridge failed: %s\n", + strerror(err)); + return 1; + } +} + +static int br_cmd_delbr(int argc, char*const* argv) +{ + int err; + + switch (err = br_del_bridge(argv[1])){ + case 0: + return 0; + + case ENXIO: + fprintf(stderr, "bridge %s doesn't exist; can't delete it\n", + argv[1]); + return 1; + + case EBUSY: + fprintf(stderr, "bridge %s is still up; can't delete it\n", + argv[1]); + return 1; + + default: + fprintf(stderr, "can't delete bridge %s: %s\n", + argv[1], strerror(err)); + return 1; + } +} + +static int br_cmd_addif(int argc, char *const* argv) +{ + const char *brname; + int err; + + argc -= 2; + brname = *++argv; + + while (argc-- > 0) { + const char *ifname = *++argv; + err = br_add_interface(brname, ifname); + + switch(err) { + case 0: + continue; + + case ENODEV: + if (if_nametoindex(ifname) == 0) + fprintf(stderr, "interface %s does not exist!\n", ifname); + else + fprintf(stderr, "bridge %s does not exist!\n", brname); + break; + + case EBUSY: + fprintf(stderr, "device %s is already a member of a bridge; " + "can't enslave it to bridge %s.\n", ifname, + brname); + break; + + case ELOOP: + fprintf(stderr, "device %s is a bridge device itself; " + "can't enslave a bridge device to a bridge device.\n", + ifname); + break; + + default: + fprintf(stderr, "can't add %s to bridge %s: %s\n", + ifname, brname, strerror(err)); + } + return 1; + } + return 0; +} + +static int br_cmd_delif(int argc, char *const* argv) +{ + const char *brname; + int err; + + argc -= 2; + brname = *++argv; + + while (argc-- > 0) { + const char *ifname = *++argv; + err = br_del_interface(brname, ifname); + switch (err) { + case 0: + continue; + + case ENODEV: + if (if_nametoindex(ifname) == 0) + fprintf(stderr, "interface %s does not exist!\n", ifname); + else + fprintf(stderr, "bridge %s does not exist!\n", brname); + break; + + case EINVAL: + fprintf(stderr, "device %s is not a slave of %s\n", + ifname, brname); + break; + + default: + fprintf(stderr, "can't delete %s from %s: %s\n", + ifname, brname, strerror(err)); + } + return 1; + } + return 0; +} + +static int br_cmd_setageing(int argc, char *const* argv) +{ + int err; + struct timeval tv; + + if (strtotimeval(&tv, argv[2])) { + fprintf(stderr, "bad ageing time value\n"); + return 1; + } + + err = br_set_ageing_time(argv[1], &tv); + if (err) + fprintf(stderr, "set ageing time failed: %s\n", + strerror(err)); + + return err != 0; +} + +static int br_cmd_setbridgeprio(int argc, char *const* argv) +{ + int prio; + int err; + + if (sscanf(argv[2], "%i", &prio) != 1) { + fprintf(stderr,"bad priority\n"); + return 1; + } + + err = br_set_bridge_priority(argv[1], prio); + if (err) + fprintf(stderr, "set bridge priority failed: %s\n", + strerror(err)); + return err != 0; +} + +static int br_cmd_setfd(int argc, char *const* argv) +{ + struct timeval tv; + int err; + + if (strtotimeval(&tv, argv[2])) { + fprintf(stderr, "bad forward delay value\n"); + return 1; + } + + err = br_set_bridge_forward_delay(argv[1], &tv); + if (err) + fprintf(stderr, "set forward delay failed: %s\n", + strerror(err)); + + return err != 0; +} + +static int br_cmd_sethello(int argc, char *const* argv) +{ + struct timeval tv; + int err; + + if (strtotimeval(&tv, argv[2])) { + fprintf(stderr, "bad hello timer value\n"); + return 1; + } + + err = br_set_bridge_hello_time(argv[1], &tv); + if (err) + fprintf(stderr, "set hello timer failed: %s\n", + strerror(err)); + + return err != 0; +} + +static int br_cmd_setmaxage(int argc, char *const* argv) +{ + struct timeval tv; + int err; + + if (strtotimeval(&tv, argv[2])) { + fprintf(stderr, "bad max age value\n"); + return 1; + } + err = br_set_bridge_max_age(argv[1], &tv); + if (err) + fprintf(stderr, "set max age failed: %s\n", + strerror(err)); + + return err != 0; +} + +static int br_cmd_setpathcost(int argc, char *const* argv) +{ + int cost, err; + + if (sscanf(argv[3], "%i", &cost) != 1) { + fprintf(stderr, "bad path cost value\n"); + return 1; + } + + err = br_set_path_cost(argv[1], argv[2], cost); + if (err) + fprintf(stderr, "set path cost failed: %s\n", + strerror(err)); + return err != 0; +} + +static int br_cmd_setportprio(int argc, char *const* argv) +{ + int cost, err; + + if (sscanf(argv[3], "%i", &cost) != 1) { + fprintf(stderr, "bad path priority value\n"); + return 1; + } + + err = br_set_port_priority(argv[1], argv[2], cost); + if (err) + fprintf(stderr, "set port priority failed: %s\n", + strerror(errno)); + + return err != 0; +} + +static int br_cmd_stp(int argc, char *const* argv) +{ + int stp, err; + + if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes") + || !strcmp(argv[2], "1")) + stp = 1; + else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no") + || !strcmp(argv[2], "0")) + stp = 0; + else { + fprintf(stderr, "expect on/off for argument\n"); + return 1; + } + + err = br_set_stp_state(argv[1], stp); + if (err) + fprintf(stderr, "set stp status failed: %s\n", + strerror(errno)); + return err != 0; +} + +static int br_cmd_showstp(int argc, char *const* argv) +{ + struct bridge_info info; + + if (br_get_bridge_info(argv[1], &info)) { + fprintf(stderr, "%s: can't get info %s\n", argv[1], + strerror(errno)); + return 1; + } + + br_dump_info(argv[1], &info); + return 0; +} + +static int show_bridge(const char *name, void *arg) +{ + struct bridge_info info; + + printf("%s\t\t", name); + fflush(stdout); + + if (br_get_bridge_info(name, &info)) { + fprintf(stderr, "can't get info %s\n", + strerror(errno)); + return 1; + } + + br_dump_bridge_id((unsigned char *)&info.bridge_id); + printf("\t%s\t\t", info.stp_enabled?"yes":"no"); + + br_dump_interface_list(name); + return 0; +} + +static int br_cmd_show(int argc, char *const* argv) +{ + int i; + + printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n"); + if (argc == 1) + br_foreach_bridge(show_bridge, NULL); + else + for(i = 2; i <= argc; i++) + show_bridge(argv[i - 1], NULL); + + return 0; +} + +static int compare_fdbs(const void *_f0, const void *_f1) +{ + const struct fdb_entry *f0 = _f0; + const struct fdb_entry *f1 = _f1; + + return memcmp(f0->mac_addr, f1->mac_addr, 6); +} + +static int br_cmd_showmacs(int argc, char *const* argv) +{ + const char *brname = argv[1]; +#define CHUNK 128 + int i, n; + struct fdb_entry *fdb = NULL; + int offset = 0; + + for(;;) { + fdb = realloc(fdb, (offset + CHUNK) * sizeof(struct fdb_entry)); + if (!fdb) { + fprintf(stderr, "Out of memory\n"); + return 1; + } + + n = br_read_fdb(brname, fdb+offset, offset, CHUNK); + if (n == 0) + break; + + if (n < 0) { + fprintf(stderr, "read of forward table failed: %s\n", + strerror(errno)); + return 1; + } + + offset += n; + } + + qsort(fdb, offset, sizeof(struct fdb_entry), compare_fdbs); + + printf("port no\tmac addr\t\tis local?\tageing timer\n"); + for (i = 0; i < offset; i++) { + const struct fdb_entry *f = fdb + i; + printf("%3i\t", f->port_no); + printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t", + f->mac_addr[0], f->mac_addr[1], f->mac_addr[2], + f->mac_addr[3], f->mac_addr[4], f->mac_addr[5]); + printf("%s\t\t", f->is_local?"yes":"no"); + br_show_timer(&f->ageing_timer_value); + printf("\n"); + } + return 0; +} + +static int br_cmd_hairpin(int argc, char *const* argv) +{ + int hairpin, err; + const char *brname = *++argv; + const char *ifname = *++argv; + const char *hpmode = *++argv; + + if (!strcmp(hpmode, "on") || !strcmp(hpmode, "yes") + || !strcmp(hpmode, "1")) + hairpin = 1; + else if (!strcmp(hpmode, "off") || !strcmp(hpmode, "no") + || !strcmp(hpmode, "0")) + hairpin = 0; + else { + fprintf(stderr, "expect on/off for argument\n"); + return 1; + } + if (if_nametoindex(ifname) == 0) { + fprintf(stderr, "interface %s does not exist!\n", + ifname); + return 1; + } else if (if_nametoindex(brname) == 0) { + fprintf(stderr, "bridge %s does not exist!\n", + brname); + return 1; + } + + err = br_set_hairpin_mode(brname, ifname, hairpin); + + if (err) { + fprintf(stderr, "can't set %s to hairpin on bridge %s: %s\n", + ifname, brname, strerror(err)); + } + return err != 0; +} + +static const struct command commands[] = { + { 1, "addbr", br_cmd_addbr, "<bridge>\t\tadd bridge" }, + { 1, "delbr", br_cmd_delbr, "<bridge>\t\tdelete bridge" }, + { 2, "addif", br_cmd_addif, + "<bridge> <device>\tadd interface to bridge" }, + { 2, "delif", br_cmd_delif, + "<bridge> <device>\tdelete interface from bridge" }, + { 3, "hairpin", br_cmd_hairpin, + "<bridge> <port> {on|off}\tturn hairpin on/off" }, + { 2, "setageing", br_cmd_setageing, + "<bridge> <time>\t\tset ageing time" }, + { 2, "setbridgeprio", br_cmd_setbridgeprio, + "<bridge> <prio>\t\tset bridge priority" }, + { 2, "setfd", br_cmd_setfd, + "<bridge> <time>\t\tset bridge forward delay" }, + { 2, "sethello", br_cmd_sethello, + "<bridge> <time>\t\tset hello time" }, + { 2, "setmaxage", br_cmd_setmaxage, + "<bridge> <time>\t\tset max message age" }, + { 3, "setpathcost", br_cmd_setpathcost, + "<bridge> <port> <cost>\tset path cost" }, + { 3, "setportprio", br_cmd_setportprio, + "<bridge> <port> <prio>\tset port priority" }, + { 0, "show", br_cmd_show, + "[ <bridge> ]\t\tshow a list of bridges" }, + { 1, "showmacs", br_cmd_showmacs, + "<bridge>\t\tshow a list of mac addrs"}, + { 1, "showstp", br_cmd_showstp, + "<bridge>\t\tshow bridge stp info"}, + { 2, "stp", br_cmd_stp, + "<bridge> {on|off}\tturn stp on/off" }, +}; + +const struct command *command_lookup(const char *cmd) +{ + int i; + + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + if (!strcmp(cmd, commands[i].name)) + return &commands[i]; + } + + return NULL; +} + +void command_helpall(void) +{ + int i; + + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + printf("\t%-10s\t%s\n", commands[i].name, commands[i].help); + } +} diff --git a/brctl/brctl_disp.c b/brctl/brctl_disp.c new file mode 100644 index 0000000..3e81241 --- /dev/null +++ b/brctl/brctl_disp.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2000 Lennert Buytenhek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> + +#include "libbridge.h" +#include "brctl.h" + +void br_dump_bridge_id(const unsigned char *x) +{ + printf("%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x", x[0], x[1], x[2], x[3], + x[4], x[5], x[6], x[7]); +} + +void br_show_timer(const struct timeval *tv) +{ + printf("%4i.%.2i", (int)tv->tv_sec, (int)tv->tv_usec/10000); +} + +static int first; + +static int dump_interface(const char *b, const char *p, void *arg) +{ + + if (first) + first = 0; + else + printf("\n\t\t\t\t\t\t\t"); + + printf("%s", p); + + return 0; +} + +void br_dump_interface_list(const char *br) +{ + int err; + + first = 1; + err = br_foreach_port(br, dump_interface, NULL); + if (err < 0) + printf(" can't get port info: %s\n", strerror(-err)); + else + printf("\n"); +} + +static int dump_port_info(const char *br, const char *p, void *arg) +{ + struct port_info pinfo; + + if (br_get_port_info(br, p, &pinfo)) { + printf("Can't get info for %p",p); + return 1; + } + + printf("%s (%d)\n", p, pinfo.port_no); + printf(" port id\t\t%.4x", pinfo.port_id); + printf("\t\t\tstate\t\t%15s\n", br_get_state_name(pinfo.state)); + printf(" designated root\t"); + br_dump_bridge_id((unsigned char *)&pinfo.designated_root); + printf("\tpath cost\t\t%4i\n", pinfo.path_cost); + + printf(" designated bridge\t"); + br_dump_bridge_id((unsigned char *)&pinfo.designated_bridge); + printf("\tmessage age timer\t"); + br_show_timer(&pinfo.message_age_timer_value); + printf("\n designated port\t%.4x", pinfo.designated_port); + printf("\t\t\tforward delay timer\t"); + br_show_timer(&pinfo.forward_delay_timer_value); + printf("\n designated cost\t%4i", pinfo.designated_cost); + printf("\t\t\thold timer\t\t"); + br_show_timer(&pinfo.hold_timer_value); + printf("\n flags\t\t\t"); + if (pinfo.config_pending) + printf("CONFIG_PENDING "); + if (pinfo.top_change_ack) + printf("TOPOLOGY_CHANGE_ACK "); + if (pinfo.hairpin_mode) + printf("\n hairpin mode\t\t\%4i", pinfo.hairpin_mode); + printf("\n"); + printf("\n"); + return 0; +} + +void br_dump_info(const char *br, const struct bridge_info *bri) +{ + int err; + + printf("%s\n", br); + printf(" bridge id\t\t"); + br_dump_bridge_id((unsigned char *)&bri->bridge_id); + printf("\n designated root\t"); + br_dump_bridge_id((unsigned char *)&bri->designated_root); + printf("\n root port\t\t%4i\t\t\t", bri->root_port); + printf("path cost\t\t%4i\n", bri->root_path_cost); + printf(" max age\t\t"); + br_show_timer(&bri->max_age); + printf("\t\t\tbridge max age\t\t"); + br_show_timer(&bri->bridge_max_age); + printf("\n hello time\t\t"); + br_show_timer(&bri->hello_time); + printf("\t\t\tbridge hello time\t"); + br_show_timer(&bri->bridge_hello_time); + printf("\n forward delay\t\t"); + br_show_timer(&bri->forward_delay); + printf("\t\t\tbridge forward delay\t"); + br_show_timer(&bri->bridge_forward_delay); + printf("\n ageing time\t\t"); + br_show_timer(&bri->ageing_time); + printf("\n hello timer\t\t"); + br_show_timer(&bri->hello_timer_value); + printf("\t\t\ttcn timer\t\t"); + br_show_timer(&bri->tcn_timer_value); + printf("\n topology change timer\t"); + br_show_timer(&bri->topology_change_timer_value); + printf("\t\t\tgc timer\t\t"); + br_show_timer(&bri->gc_timer_value); + printf("\n flags\t\t\t"); + if (bri->topology_change) + printf("TOPOLOGY_CHANGE "); + if (bri->topology_change_detected) + printf("TOPOLOGY_CHANGE_DETECTED "); + printf("\n"); + printf("\n"); + printf("\n"); + + err = br_foreach_port(br, dump_port_info, NULL); + if (err < 0) + printf("can't get ports: %s\n", strerror(-err)); +} |