]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Add bridge command
authorStephen Hemminger <shemminger@vyatta.com>
Wed, 1 Aug 2012 22:23:49 +0000 (15:23 -0700)
committerStephen Hemminger <shemminger@vyatta.com>
Wed, 1 Aug 2012 22:23:49 +0000 (15:23 -0700)
New tool to allow manipulating forwarding entries and monitoring
bridge events.

Makefile
bridge/.gitignore [new file with mode: 0644]
bridge/Makefile [new file with mode: 0644]
bridge/br_common.h [new file with mode: 0644]
bridge/bridge.c [new file with mode: 0644]
bridge/fdb.c [new file with mode: 0644]
bridge/link.c [new file with mode: 0644]
bridge/monitor.c [new file with mode: 0644]
man/man8/Makefile
man/man8/bridge.8 [new file with mode: 0644]

index c10795593c356ba483afc54dbb03c398bcbe80a4..917862b05ddc0b6651a20d9e9e3d2a620ef2548d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -38,7 +38,7 @@ WFLAGS = -Wall -Wstrict-prototypes
 CFLAGS = $(WFLAGS) $(CCOPTS) -I../include $(DEFINES)
 YACCFLAGS = -d -t -v
 
-SUBDIRS=lib ip tc misc netem genl man
+SUBDIRS=lib ip tc bridge misc netem genl man
 
 LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
 LDLIBS += $(LIBNETLINK)
diff --git a/bridge/.gitignore b/bridge/.gitignore
new file mode 100644 (file)
index 0000000..7096907
--- /dev/null
@@ -0,0 +1 @@
+bridge
diff --git a/bridge/Makefile b/bridge/Makefile
new file mode 100644 (file)
index 0000000..0c333bf
--- /dev/null
@@ -0,0 +1,14 @@
+BROBJ = bridge.o fdb.o monitor.o link.o
+
+include ../Config
+
+all: bridge
+
+bridge: $(BROBJ) $(LIBNETLINK) 
+
+install: all
+       install -m 0755 br $(DESTDIR)$(SBINDIR)
+
+clean:
+       rm -f $(BROBJ) br
+
diff --git a/bridge/br_common.h b/bridge/br_common.h
new file mode 100644 (file)
index 0000000..ec1671d
--- /dev/null
@@ -0,0 +1,13 @@
+extern int print_linkinfo(const struct sockaddr_nl *who,
+                         struct nlmsghdr *n,
+                         void *arg);
+extern int print_fdb(const struct sockaddr_nl *who,
+                    struct nlmsghdr *n, void *arg);
+
+extern int do_fdb(int argc, char **argv);
+extern int do_monitor(int argc, char **argv);
+
+extern int show_stats;
+extern int show_detail;
+extern int timestamp;
+extern struct rtnl_handle rth;
diff --git a/bridge/bridge.c b/bridge/bridge.c
new file mode 100644 (file)
index 0000000..9e5f69c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Get/set/delete bridge with netlink
+ *
+ * Authors:    Stephen Hemminger <shemminger@vyatta.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#include "SNAPSHOT.h"
+#include "utils.h"
+#include "br_common.h"
+
+struct rtnl_handle rth = { .fd = -1 };
+int resolve_hosts;
+int show_stats;
+int show_details;
+int timestamp;
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+       fprintf(stderr,
+"Usage: br [ OPTIONS ] OBJECT { COMMAND | help }\n"
+"where  OBJECT := { fdb |  monitor }\n"
+"       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails]\n" );
+       exit(-1);
+}
+
+static int do_help(int argc, char **argv)
+{
+       usage();
+}
+
+
+static const struct cmd {
+       const char *cmd;
+       int (*func)(int argc, char **argv);
+} cmds[] = {
+       { "fdb",        do_fdb },
+       { "monitor",    do_monitor },
+       { "help",       do_help },
+       { 0 }
+};
+
+static int do_cmd(const char *argv0, int argc, char **argv)
+{
+       const struct cmd *c;
+
+       for (c = cmds; c->cmd; ++c) {
+               if (matches(argv0, c->cmd) == 0)
+                       return c->func(argc-1, argv+1);
+       }
+
+       fprintf(stderr, "Object \"%s\" is unknown, try \"br help\".\n", argv0);
+       return -1;
+}
+
+int
+main(int argc, char **argv)
+{
+       while (argc > 1) {
+               char *opt = argv[1];
+               if (strcmp(opt,"--") == 0) {
+                       argc--; argv++;
+                       break;
+               }
+               if (opt[0] != '-')
+                       break;
+               if (opt[1] == '-')
+                       opt++;
+
+               if (matches(opt, "-help") == 0) {
+                       usage();
+               } else if (matches(opt, "-Version") == 0) {
+                       printf("br utility, 0.0\n");
+                       exit(0);
+               } else if (matches(opt, "-stats") == 0 ||
+                          matches(opt, "-statistics") == 0) {
+                       ++show_stats;
+               } else if (matches(opt, "-details") == 0) {
+                       ++show_details;
+               } else if (matches(opt, "-timestamp") == 0) {
+                       ++timestamp;
+               } else {
+                       fprintf(stderr, "Option \"%s\" is unknown, try \"br -help\".\n", opt);
+                       exit(-1);
+               }
+               argc--; argv++;
+       }
+
+       if (rtnl_open(&rth, 0) < 0)
+               exit(1);
+
+       if (argc > 1)
+               return do_cmd(argv[1], argc-1, argv+1);
+
+       rtnl_close(&rth);
+       usage();
+}
diff --git a/bridge/fdb.c b/bridge/fdb.c
new file mode 100644 (file)
index 0000000..04984f1
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Get/set/delete fdb table with netlink
+ *
+ * Authors:    Stephen Hemminger <shemminger@vyatta.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+#include <linux/if_ether.h>
+#include <linux/neighbour.h>
+#include <string.h>
+
+#include "libnetlink.h"
+#include "br_common.h"
+#include "utils.h"
+
+int filter_index;
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: br fdb { add | del | replace } ADDR dev DEV\n");
+       fprintf(stderr, "       br fdb {show} [ dev DEV ]\n");
+       exit(-1);
+}
+
+static const char *state_n2a(unsigned s)
+{
+       static char buf[32];
+
+       if (s & NUD_PERMANENT) 
+               return "local";
+
+       if (s & NUD_NOARP)
+               return "static";
+
+       if (s & NUD_STALE)
+               return "stale";
+       
+       if (s & NUD_REACHABLE)
+               return "";
+
+       sprintf(buf, "state=%#x", s);
+       return buf;
+}
+
+static char *fmt_time(char *b, size_t l, unsigned long tick)
+{
+       static int hz;
+       
+       if (hz == 0)
+               hz = __get_user_hz();
+
+       snprintf(b, l, "%lu.%02lu", tick / hz, ((tick % hz) * hz) / 100);
+       return b;
+}
+
+int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+       struct ndmsg *r = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       struct rtattr * tb[NDA_MAX+1];
+       const __u8 *addr = NULL;
+       char b1[32];
+
+       len -= NLMSG_LENGTH(sizeof(*r));
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       if (r->ndm_family != AF_BRIDGE)
+               return 0;
+
+       if (filter_index && filter_index != r->ndm_ifindex)
+               return 0;
+
+       parse_rtattr(tb, NDA_MAX, NDA_RTA(r),
+                    n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+       if (n->nlmsg_type == RTM_DELNEIGH)
+               printf("Deleted ");
+
+       if (tb[NDA_LLADDR])
+               addr = RTA_DATA(tb[NDA_LLADDR]);
+       else {
+               fprintf(stderr, "missing lladdr\n");
+               return -1;
+       }
+
+       printf("%s\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t%s",
+               ll_index_to_name(r->ndm_ifindex),
+               addr[0], addr[1], addr[2],
+               addr[3], addr[4], addr[5],
+               state_n2a(r->ndm_state));
+
+       if (show_stats && tb[NDA_CACHEINFO]) {
+               struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+
+               printf("\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated));
+               printf(" %8s", fmt_time(b1, sizeof(b1), ci->ndm_used));
+       }
+       printf("\n");
+
+       return 0;
+}
+
+static int fdb_show(int argc, char **argv)
+{
+       char *filter_dev = NULL;
+       
+       while (argc > 0) {
+               if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       if (filter_dev)
+                               duparg("dev", *argv);
+                       filter_dev = *argv;
+               }
+               argc--; argv++;
+       }
+
+       if (filter_dev) {
+               if ((filter_index = if_nametoindex(filter_dev)) == 0) {
+                       fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
+                       return -1;
+               }
+       }
+
+       if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETNEIGH) < 0) {
+               perror("Cannot send dump request");
+               exit(1);
+       }
+       
+       printf("port\tmac addr\t\tflags%s\n",
+              show_stats ? "\t updated     used" : "");
+
+       if (rtnl_dump_filter(&rth, print_fdb, NULL) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               exit(1);
+       }
+
+       return 0;
+}
+
+static int fdb_modify(int cmd, int flags, int argc, char **argv)
+{
+       struct {
+               struct nlmsghdr         n;
+               struct ndmsg            ndm;
+               char                    buf[256];
+       } req;
+       char *addr = NULL;
+       char *d = NULL;
+       char abuf[ETH_ALEN];
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+       req.n.nlmsg_type = cmd;
+       req.ndm.ndm_family = PF_BRIDGE;
+       req.ndm.ndm_state = NUD_NOARP;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "dev") == 0) {
+                       NEXT_ARG();
+                       d = *argv;
+               } else if (strcmp(*argv, "local") == 0) {
+                       req.ndm.ndm_state = NUD_PERMANENT;
+               } else if (strcmp(*argv, "temp") == 0) {
+                       req.ndm.ndm_state = NUD_REACHABLE;
+               } else {
+                       if (strcmp(*argv, "to") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (matches(*argv, "help") == 0) {
+                               NEXT_ARG();
+                       }
+                       if (addr)
+                               duparg2("to", *argv);
+                       addr = *argv;
+               }
+               argc--; argv++;
+       }
+
+       if (d == NULL || addr == NULL) {
+               fprintf(stderr, "Device and address are required arguments.\n");
+               exit(-1);
+       }
+
+       if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 
+                  abuf, abuf+1, abuf+2,
+                  abuf+3, abuf+4, abuf+5) != 6) {
+               fprintf(stderr, "Invalid mac address %s\n", addr);
+               exit(-1);
+       }
+
+       addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
+
+       req.ndm.ndm_ifindex = ll_name_to_index(d);
+       if (req.ndm.ndm_ifindex == 0) {
+               fprintf(stderr, "Cannot find device \"%s\"\n", d);
+               return -1;
+       }
+
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
+               exit(2);
+
+       return 0;
+}
+
+int do_fdb(int argc, char **argv)
+{
+       ll_init_map(&rth);
+
+       if (argc > 0) {
+               if (matches(*argv, "add") == 0)
+                       return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
+               if (matches(*argv, "change") == 0)
+                       return fdb_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
+
+               if (matches(*argv, "replace") == 0)
+                       return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
+               if (matches(*argv, "delete") == 0)
+                       return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
+               if (matches(*argv, "show") == 0 ||
+                   matches(*argv, "lst") == 0 ||
+                   matches(*argv, "list") == 0)
+                       return fdb_show(argc-1, argv+1);
+               if (matches(*argv, "help") == 0)
+                       usage();
+       } else
+               return fdb_show(0, NULL);
+
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
+       exit(-1);
+}
diff --git a/bridge/link.c b/bridge/link.c
new file mode 100644 (file)
index 0000000..1b9541d
--- /dev/null
@@ -0,0 +1,142 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/if_bridge.h>
+#include <string.h>
+
+#include "utils.h"
+#include "br_common.h"
+
+static const char *port_states[] = {
+       [BR_STATE_DISABLED] = "disabled",
+       [BR_STATE_LISTENING] = "listening",
+       [BR_STATE_LEARNING] = "learning",
+       [BR_STATE_FORWARDING] = "forwarding",
+       [BR_STATE_BLOCKING] = "blocking",
+};
+
+extern char *if_indextoname (unsigned int __ifindex, char *__ifname);
+
+static void print_link_flags(FILE *fp, unsigned flags)
+{
+       fprintf(fp, "<");
+       if (flags & IFF_UP && !(flags & IFF_RUNNING))
+               fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
+       flags &= ~IFF_RUNNING;
+#define _PF(f) if (flags&IFF_##f) { \
+                  flags &= ~IFF_##f ; \
+                  fprintf(fp, #f "%s", flags ? "," : ""); }
+       _PF(LOOPBACK);
+       _PF(BROADCAST);
+       _PF(POINTOPOINT);
+       _PF(MULTICAST);
+       _PF(NOARP);
+       _PF(ALLMULTI);
+       _PF(PROMISC);
+       _PF(MASTER);
+       _PF(SLAVE);
+       _PF(DEBUG);
+       _PF(DYNAMIC);
+       _PF(AUTOMEDIA);
+       _PF(PORTSEL);
+       _PF(NOTRAILERS);
+       _PF(UP);
+       _PF(LOWER_UP);
+       _PF(DORMANT);
+       _PF(ECHO);
+#undef _PF
+        if (flags)
+               fprintf(fp, "%x", flags);
+       fprintf(fp, "> ");
+}
+
+static const char *oper_states[] = {
+       "UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN", 
+       "TESTING", "DORMANT",    "UP"
+};
+
+static void print_operstate(FILE *f, __u8 state)
+{
+       if (state >= sizeof(oper_states)/sizeof(oper_states[0]))
+               fprintf(f, "state %#x ", state);
+       else
+               fprintf(f, "state %s ", oper_states[state]);
+}
+
+int print_linkinfo(const struct sockaddr_nl *who,
+                  struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = arg;
+       int len = n->nlmsg_len;
+       struct ifinfomsg *ifi = NLMSG_DATA(n);
+       struct rtattr * tb[IFLA_MAX+1];
+       char b1[IFNAMSIZ];
+
+       len -= NLMSG_LENGTH(sizeof(*ifi));
+       if (len < 0) {
+               fprintf(stderr, "Message too short!\n");
+               return -1;
+        }
+
+       if (!(ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_UNSPEC))
+               return 0;
+
+       parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+       if (tb[IFLA_IFNAME] == NULL) {
+               fprintf(stderr, "BUG: nil ifname\n");
+               return -1;
+       }
+
+       if (n->nlmsg_type == RTM_DELLINK)
+               fprintf(fp, "Deleted ");
+
+       fprintf(fp, "%d: %s ", ifi->ifi_index,
+               tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
+
+       if (tb[IFLA_OPERSTATE]) 
+               print_operstate(fp, *(__u8 *)RTA_DATA(tb[IFLA_OPERSTATE]));
+       
+       if (tb[IFLA_LINK]) {
+               SPRINT_BUF(b1);
+               int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+               
+               if (iflink == 0)
+                       fprintf(fp, "@NONE: ");
+               else {
+                       fprintf(fp, "@%s: ", 
+                               if_indextoname(iflink, b1));
+               }
+       } else {
+               fprintf(fp, ": ");
+       }
+
+       print_link_flags(fp, ifi->ifi_flags);
+
+       if (tb[IFLA_MTU])
+               fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+
+       if (tb[IFLA_MASTER]) {
+               fprintf(fp, "master %s ", 
+                       if_indextoname(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+       }
+
+       if (tb[IFLA_PROTINFO]) {
+               uint8_t state = *(uint8_t *)RTA_DATA(tb[IFLA_PROTINFO]);
+               if (state <= BR_STATE_BLOCKING)
+                       fprintf(fp, "state %s", port_states[state]);
+               else
+                       fprintf(fp, "state (%d)", state);
+       }
+
+
+       fprintf(fp, "\n");
+       fflush(fp);
+       return 0;
+}
diff --git a/bridge/monitor.c b/bridge/monitor.c
new file mode 100644 (file)
index 0000000..37468e6
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * brmonitor.c         "br monitor"
+ *
+ *             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.
+ *
+ * Authors:    Stephen Hemminger <shemminger@vyatta.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+#include <linux/neighbour.h>
+#include <string.h>
+
+#include "utils.h"
+#include "br_common.h"
+
+
+static void usage(void) __attribute__((noreturn));
+int prefix_banner;
+
+static void usage(void)
+{
+       fprintf(stderr, "Usage: br monitor\n");
+       exit(-1);
+}
+
+static int show_mark(FILE *fp, const struct nlmsghdr *n)
+{
+       char *tstr;
+       time_t secs = ((__u32*)NLMSG_DATA(n))[0];
+       long usecs = ((__u32*)NLMSG_DATA(n))[1];
+       tstr = asctime(localtime(&secs));
+       tstr[strlen(tstr)-1] = 0;
+       fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs);
+       return 0;
+}
+
+int accept_msg(const struct sockaddr_nl *who,
+              struct nlmsghdr *n, void *arg)
+{
+       FILE *fp = arg;
+
+       if (timestamp)
+               print_timestamp(fp);
+
+       switch (n->nlmsg_type) {
+       case RTM_NEWLINK:
+       case RTM_DELLINK:
+               if (prefix_banner)
+                       fprintf(fp, "[LINK]");
+
+               return print_linkinfo(who, n, arg);
+
+       case RTM_NEWNEIGH:
+       case RTM_DELNEIGH:
+               if (prefix_banner)
+                       fprintf(fp, "[NEIGH]");
+               return print_fdb(who, n, arg);
+
+       case 15:
+               return show_mark(fp, n);
+
+       default:
+               return 0;
+       }
+       
+
+}
+
+int do_monitor(int argc, char **argv)
+{
+       char *file = NULL;
+       unsigned groups = ~RTMGRP_TC;
+       int llink=0;
+       int lneigh=0;
+
+       rtnl_close(&rth);
+
+       while (argc > 0) {
+               if (matches(*argv, "file") == 0) {
+                       NEXT_ARG();
+                       file = *argv;
+               } else if (matches(*argv, "link") == 0) {
+                       llink=1;
+                       groups = 0;
+               } else if (matches(*argv, "fdb") == 0) {
+                       lneigh = 1;
+                       groups = 0;
+               } else if (strcmp(*argv, "all") == 0) {
+                       groups = ~RTMGRP_TC;
+                       prefix_banner=1;
+               } else if (matches(*argv, "help") == 0) {
+                       usage();
+               } else {
+                       fprintf(stderr, "Argument \"%s\" is unknown, try \"br monitor help\".\n", *argv);
+                       exit(-1);
+               }
+               argc--; argv++;
+       }
+
+       if (llink)
+               groups |= nl_mgrp(RTNLGRP_LINK);
+
+       if (lneigh) {
+               groups |= nl_mgrp(RTNLGRP_NEIGH);
+       }
+
+       if (file) {
+               FILE *fp;
+               fp = fopen(file, "r");
+               if (fp == NULL) {
+                       perror("Cannot fopen");
+                       exit(-1);
+               }
+               return rtnl_from_file(fp, accept_msg, stdout);
+       }
+
+       if (rtnl_open(&rth, groups) < 0)
+               exit(1);
+       ll_init_map(&rth);
+
+       if (rtnl_listen(&rth, accept_msg, stdout) < 0)
+               exit(2);
+
+       return 0;
+}
+
index d4e1dd5c3fd555d2227398840346f48fbcf9a867..1b671a42b6691381a95a1804b6f2d0bdee5fd62f 100644 (file)
@@ -4,7 +4,7 @@ MAN8PAGES = $(TARGETS) ip.8 arpd.8 lnstat.8 routel.8 rtacct.8 rtmon.8 ss.8 \
        tc-bfifo.8 tc-cbq-details.8 tc-cbq.8 tc-drr.8 tc-htb.8 \
        tc-pfifo.8 tc-pfifo_fast.8 tc-prio.8 tc-red.8 tc-sfq.8 \
        tc-tbf.8 tc.8tc-codel.8 tc-fq_codel.8 tc-sfb.8 tc-netem.8 tc-choke.8 \
-       rtstat.8 ctstat.8 nstat.8 routef.8 \
+       bridge.8 rtstat.8 ctstat.8 nstat.8 routef.8 \
        ip-tunnel.8 ip-rule.8 ip-ntable.8 \
        ip-monitor.8 tc-stab.8 tc-hfsc.8 ip-xfrm.8 ip-netns.8 \
        ip-neighbour.8 ip-mroute.8 ip-maddress.8 ip-addrlabel.8 
diff --git a/man/man8/bridge.8 b/man/man8/bridge.8
new file mode 100644 (file)
index 0000000..63d166b
--- /dev/null
@@ -0,0 +1,186 @@
+.TH BRIDGE 8 "1 August 2012" "iproute2" "Linux"
+.SH NAME
+bridge \- show / manipulate bridge addresses and devices
+.SH SYNOPSIS
+
+.ad l
+.in +8
+.ti -8
+.B bridge
+.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.IR OBJECT " := { "
+.BR fdb " | " monitor " }"
+.sp
+
+.ti -8
+.IR OPTIONS " := { "
+\fB\-V\fR[\fIersion\fR] |
+\fB\-s\fR[\fItatistics\fR]
+
+.ti -8
+.BR "bridge fdb" " { " add " | " del " | " change " | " replace " } "
+.I LLADDR
+.B  dev
+.IR DEV " { "
+.BR local " | " temp " }"
+
+.ti -8
+.BR "bridge fdb" " [ " show " ] [ "
+.B  dev
+.IR DEV " ]"
+
+.ti -8
+.BR "bridge monitor" " [ " all " | " neigh " | " link " ]"
+
+.SH OPTIONS
+
+.TP
+.BR "\-V" , " -Version"
+print the version of the
+.B bridge
+utility and exit.
+
+.TP
+.BR "\-s" , " \-stats", " \-statistics"
+output more information.  If the option
+appears twice or more, the amount of information increases.
+As a rule, the information is statistics or some time values.
+
+
+.SH BRIDGE - COMMAND SYNTAX
+
+.SS
+.I OBJECT
+
+.TP
+.B fdb 
+- Forwarding Database entry.
+
+.SS
+.I COMMAND
+
+Specifies the action to perform on the object.
+The set of possible actions depends on the object type.
+As a rule, it is possible to
+.BR "add" , " delete"
+and
+.B show
+(or
+.B list
+) objects, but some objects do not allow all of these operations
+or have some additional commands.  The
+.B help
+command is available for all objects.  It prints
+out a list of available commands and argument syntax conventions.
+.sp
+If no command is given, some default command is assumed.
+Usually it is
+.B list
+or, if the objects of this class cannot be listed,
+.BR "help" .
+
+.SH bridge fdb - forwarding database management
+
+.B fdb
+objects contain known Ethernet addresses on a  link.
+
+.P
+The corresponding commands display fdb entries, add new entries,
+and delete old ones.
+
+.SS bridge fdb add - add a new neighbor entry
+.SS bridge fdb change - change an existing entry
+.SS bridge fdb replace - add a new entry or change an existing one
+
+These commands create new neighbor records or update existing ones.
+
+.TP
+.BI "ADDRESS"
+the Ethernet MAC address.
+
+.TP
+.BI dev " NAME"
+the interface to which this address is associated.
+
+.TP
+.in +8
+.B local
+- the address is associated with a local interface on the system
+and is never forwarded.
+.sp
+
+.B temp
+- the address is a dynamic entry, and will be removed if not used.
+.sp
+
+.in -8
+
+.SS bridge fdb delete - delete a forwarding database entry
+This command removes an existing fdb entry.
+
+.PP
+The arguments are the same as with
+.BR "bridge fdb add" ,
+
+.SS bridge fdb show - list forwarding entries.
+
+This commands displays current forwarding table.
+
+.PP
+With the
+.B -statistics
+option, the command becomes verbose.  It prints out the last updated
+and last used time for each entry.
+
+.SH bridge monitor - state monitoring
+
+The
+.B bridge
+utility can monitor the state of devices and  addresses
+continuously.  This option has a slightly different format.
+Namely, the
+.B monitor
+command is the first in the command line and then the object list follows:
+
+.BR "bridge monitor" " [ " all " |"
+.IR LISTofOBJECTS " ]"
+
+.I OBJECT-LIST
+is the list of object types that we want to monitor.
+It may contain
+.BR link ",  and " fdb "."
+If no
+.B file
+argument is given,
+.B bridge
+opens RTNETLINK, listens on it and dumps state changes in the format
+described in previous sections.
+
+.P
+If a file name is given, it does not listen on RTNETLINK,
+but opens the file containing RTNETLINK messages saved in binary format
+and dumps them.  Such a history file can be generated with the
+
+
+.SH NOTES
+This command uses facilities added in Linux 3.0.
+
+Although the forwarding table is maintained on a per-bridge device basis
+the bridge device is not part of the syntax. This is a limitation of the
+underlying netlink neighbour message protocol. When displaying the
+forwarding table, entries for all bridges are displayed.
+Add/delete/modify commands determine the underlying bridge device
+based on the bridge to which the coresponding ethernet device is attached. 
+
+
+.SH SEE ALSO
+.BR ip (8)
+.br
+.RB "Please direct bugreports and patches to: " <netdev@vger.kernel.org>
+
+.SH AUTHOR
+Original Manpage by Stephen Hemminger