Patchwork [RFC] iproute2: add br command

login
register
mail settings
Submitter stephen hemminger
Date Oct. 4, 2011, 4:34 p.m.
Message ID <20111004093413.2f6bbd67@nehalam.linuxnetplumber.net>
Download mbox | patch
Permalink /patch/117664/
State Accepted
Delegated to: stephen hemminger
Headers show

Comments

stephen hemminger - Oct. 4, 2011, 4:34 p.m.
This adds a new 'br' command which is the bridging equivalent of
the ip command. More of a demo of how to use netlink and bridging
at this point.


---
 Makefile       |    2 +-
 br/.gitignore  |    1 +
 br/Makefile    |   14 +++
 br/br.c        |  104 ++++++++++++++++++++++++
 br/br_common.h |   13 +++
 br/fdb.c       |  245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 br/link.c      |  142 ++++++++++++++++++++++++++++++++
 br/monitor.c   |  138 +++++++++++++++++++++++++++++++
 man/man8/br.8  |  177 ++++++++++++++++++++++++++++++++++++++++
 9 files changed, 835 insertions(+), 1 deletions(-)
 create mode 100644 br/.gitignore
 create mode 100644 br/Makefile
 create mode 100644 br/br.c
 create mode 100644 br/br_common.h
 create mode 100644 br/fdb.c
 create mode 100644 br/link.c
 create mode 100644 br/monitor.c
 create mode 100644 man/man8/br.8
Ben Hutchings - Oct. 4, 2011, 4:58 p.m.
On Tue, 2011-10-04 at 09:34 -0700, Stephen Hemminger wrote:
> This adds a new 'br' command which is the bridging equivalent of
> the ip command. More of a demo of how to use netlink and bridging
> at this point.
[...]

This command name is already in use:
http://packages.debian.org/sid/bottlerocket

Ben.
Andi Kleen - Oct. 4, 2011, 4:58 p.m.
Stephen Hemminger <shemminger@vyatta.com> writes:

> This adds a new 'br' command which is the bridging equivalent of
> the ip command. More of a demo of how to use netlink and bridging
> at this point.

Please name it "bridge", not "br"

-Andi
stephen hemminger - Oct. 4, 2011, 9:07 p.m.
On Tue, 04 Oct 2011 09:58:55 -0700
Andi Kleen <andi@firstfloor.org> wrote:

> Stephen Hemminger <shemminger@vyatta.com> writes:
> 
> > This adds a new 'br' command which is the bridging equivalent of
> > the ip command. More of a demo of how to use netlink and bridging
> > at this point.
> 
> Please name it "bridge", not "br"
> 
> -Andi

Ok, but it breaks the existing pattern.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
stephen hemminger - Oct. 4, 2011, 9:08 p.m.
On Tue, 04 Oct 2011 17:58:10 +0100
Ben Hutchings <bhutchings@solarflare.com> wrote:

> On Tue, 2011-10-04 at 09:34 -0700, Stephen Hemminger wrote:
> > This adds a new 'br' command which is the bridging equivalent of
> > the ip command. More of a demo of how to use netlink and bridging
> > at this point.
> [...]
> 
> This command name is already in use:
> http://packages.debian.org/sid/bottlerocket

Damn name squatters.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sridhar Samudrala - Oct. 4, 2011, 10:17 p.m.
On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
> On Tue, 04 Oct 2011 09:58:55 -0700
> Andi Kleen <andi@firstfloor.org> wrote:
> 
> > Stephen Hemminger <shemminger@vyatta.com> writes:
> > 
> > > This adds a new 'br' command which is the bridging equivalent of
> > > the ip command. More of a demo of how to use netlink and bridging
> > > at this point.
> > 
> > Please name it "bridge", not "br"
> > 
> > -Andi
> 
> Ok, but it breaks the existing pattern.

Is this supposed to replace brctl utility?

Can we add/delete a bridge and add/delete interfaces to a bridge using
this command?

If so, why not make it
   ip bridge add/del <brname> 
   ip bridge addif/delif <brname> <ifname>

Thanks
Sridhar

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
stephen hemminger - Oct. 4, 2011, 10:27 p.m.
On Tue, 04 Oct 2011 15:17:18 -0700
Sridhar Samudrala <sri@us.ibm.com> wrote:

> On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
> > On Tue, 04 Oct 2011 09:58:55 -0700
> > Andi Kleen <andi@firstfloor.org> wrote:
> > 
> > > Stephen Hemminger <shemminger@vyatta.com> writes:
> > > 
> > > > This adds a new 'br' command which is the bridging equivalent of
> > > > the ip command. More of a demo of how to use netlink and bridging
> > > > at this point.
> > > 
> > > Please name it "bridge", not "br"
> > > 
> > > -Andi
> > 
> > Ok, but it breaks the existing pattern.
> 
> Is this supposed to replace brctl utility?
> 
> Can we add/delete a bridge and add/delete interfaces to a bridge using
> this command?
> 
> If so, why not make it
>    ip bridge add/del <brname> 
>    ip bridge addif/delif <brname> <ifname>
> 

You can already use 'ip link' to add/delete bridge, and add interface
to bridge by setting master.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
=?ISO-8859-2?Q?Micha=B3_Miros=B3aw?= - Oct. 4, 2011, 10:32 p.m.
2011/10/5 Sridhar Samudrala <sri@us.ibm.com>:
> On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
>> On Tue, 04 Oct 2011 09:58:55 -0700
>> Andi Kleen <andi@firstfloor.org> wrote:
>> > Stephen Hemminger <shemminger@vyatta.com> writes:
>> > > This adds a new 'br' command which is the bridging equivalent of
>> > > the ip command. More of a demo of how to use netlink and bridging
>> > > at this point.
>> > Please name it "bridge", not "br"
>> Ok, but it breaks the existing pattern.
> Is this supposed to replace brctl utility?
>
> Can we add/delete a bridge and add/delete interfaces to a bridge using
> this command?
>
> If so, why not make it
>   ip bridge add/del <brname>
>   ip bridge addif/delif <brname> <ifname>

I'll add one more idea:

ip link add/del <brname> type bridge
ip bridge add/del <brname> <ifname>
ip bridge fdb ...

Best Regards,
Michał Mirosław
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
stephen hemminger - Oct. 4, 2011, 10:42 p.m.
On Wed, 5 Oct 2011 00:32:31 +0200
Michał Mirosław <mirqus@gmail.com> wrote:

> 2011/10/5 Sridhar Samudrala <sri@us.ibm.com>:
> > On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
> >> On Tue, 04 Oct 2011 09:58:55 -0700
> >> Andi Kleen <andi@firstfloor.org> wrote:
> >> > Stephen Hemminger <shemminger@vyatta.com> writes:
> >> > > This adds a new 'br' command which is the bridging equivalent of
> >> > > the ip command. More of a demo of how to use netlink and bridging
> >> > > at this point.
> >> > Please name it "bridge", not "br"
> >> Ok, but it breaks the existing pattern.
> > Is this supposed to replace brctl utility?
> >
> > Can we add/delete a bridge and add/delete interfaces to a bridge using
> > this command?
> >
> > If so, why not make it
> >   ip bridge add/del <brname>
> >   ip bridge addif/delif <brname> <ifname>
> 
> I'll add one more idea:
> 
> ip link add/del <brname> type bridge
> ip bridge add/del <brname> <ifname>
> ip bridge fdb ...

In 3.0 you can already do:
# ip link add dev br3 type bridge
# ip link set dev eth3 master br3

I see no reason to add 'ip bridge' syntax wrappers.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sridhar Samudrala - Oct. 4, 2011, 10:54 p.m.
On Tue, 2011-10-04 at 15:42 -0700, Stephen Hemminger wrote:
> On Wed, 5 Oct 2011 00:32:31 +0200
> Michał Mirosław <mirqus@gmail.com> wrote:
> 
> > 2011/10/5 Sridhar Samudrala <sri@us.ibm.com>:
> > > On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
> > >> On Tue, 04 Oct 2011 09:58:55 -0700
> > >> Andi Kleen <andi@firstfloor.org> wrote:
> > >> > Stephen Hemminger <shemminger@vyatta.com> writes:
> > >> > > This adds a new 'br' command which is the bridging equivalent of
> > >> > > the ip command. More of a demo of how to use netlink and bridging
> > >> > > at this point.
> > >> > Please name it "bridge", not "br"
> > >> Ok, but it breaks the existing pattern.
> > > Is this supposed to replace brctl utility?
> > >
> > > Can we add/delete a bridge and add/delete interfaces to a bridge using
> > > this command?
> > >
> > > If so, why not make it
> > >   ip bridge add/del <brname>
> > >   ip bridge addif/delif <brname> <ifname>
> > 
> > I'll add one more idea:
> > 
> > ip link add/del <brname> type bridge
> > ip bridge add/del <brname> <ifname>
> > ip bridge fdb ...
> 
> In 3.0 you can already do:
> # ip link add dev br3 type bridge
> # ip link set dev eth3 master br3

I just pulled iproute2 tree from 
git://github.com/shemminger/iproute2.git

But i don't see these updates there. Isn't this your latest iproute2 tree?
 
> 
> I see no reason to add 'ip bridge' syntax wrappers.

If we can make all bridge related commands use 'ip bridge' prefix , 
i think we wouldn't need a new 'br' command.

Thanks
Sridhar


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
stephen hemminger - Oct. 4, 2011, 11 p.m.
On Tue, 04 Oct 2011 15:54:07 -0700
Sridhar Samudrala <sri@us.ibm.com> wrote:

> On Tue, 2011-10-04 at 15:42 -0700, Stephen Hemminger wrote:
> > On Wed, 5 Oct 2011 00:32:31 +0200
> > Michał Mirosław <mirqus@gmail.com> wrote:
> > 
> > > 2011/10/5 Sridhar Samudrala <sri@us.ibm.com>:
> > > > On Tue, 2011-10-04 at 14:07 -0700, Stephen Hemminger wrote:
> > > >> On Tue, 04 Oct 2011 09:58:55 -0700
> > > >> Andi Kleen <andi@firstfloor.org> wrote:
> > > >> > Stephen Hemminger <shemminger@vyatta.com> writes:
> > > >> > > This adds a new 'br' command which is the bridging equivalent of
> > > >> > > the ip command. More of a demo of how to use netlink and bridging
> > > >> > > at this point.
> > > >> > Please name it "bridge", not "br"
> > > >> Ok, but it breaks the existing pattern.
> > > > Is this supposed to replace brctl utility?
> > > >
> > > > Can we add/delete a bridge and add/delete interfaces to a bridge using
> > > > this command?
> > > >
> > > > If so, why not make it
> > > >   ip bridge add/del <brname>
> > > >   ip bridge addif/delif <brname> <ifname>
> > > 
> > > I'll add one more idea:
> > > 
> > > ip link add/del <brname> type bridge
> > > ip bridge add/del <brname> <ifname>
> > > ip bridge fdb ...
> > 
> > In 3.0 you can already do:
> > # ip link add dev br3 type bridge
> > # ip link set dev eth3 master br3
> 
> I just pulled iproute2 tree from 
> git://github.com/shemminger/iproute2.git
> 
> But i don't see these updates there. Isn't this your latest iproute2 tree?

There is no special code for doing ip link stuff. it just works with
correct kernel.
 
> > 
> > I see no reason to add 'ip bridge' syntax wrappers.
> 
> If we can make all bridge related commands use 'ip bridge' prefix , 
> i think we wouldn't need a new 'br' command.

Maybe but there is no IP protocol function or requirement for bridging.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/Makefile b/Makefile
index d1ace1f..f1d360a 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@  CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
 CFLAGS = $(CCOPTS) -I../include $(DEFINES)
 YACCFLAGS = -d -t -v
 
-SUBDIRS=lib ip tc misc netem genl
+SUBDIRS=lib ip tc br misc netem genl
 
 LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
 LDLIBS += $(LIBNETLINK)
diff --git a/br/.gitignore b/br/.gitignore
new file mode 100644
index 0000000..b9a724f
--- /dev/null
+++ b/br/.gitignore
@@ -0,0 +1 @@ 
+br
diff --git a/br/Makefile b/br/Makefile
new file mode 100644
index 0000000..2bb18a7
--- /dev/null
+++ b/br/Makefile
@@ -0,0 +1,14 @@ 
+BROBJ = br.o fdb.o monitor.o link.o
+
+include ../Config
+
+all: br
+
+br: $(BROBJ) $(LIBNETLINK) 
+
+install: all
+	install -m 0755 br $(DESTDIR)$(SBINDIR)
+
+clean:
+	rm -f $(BROBJ) br
+
diff --git a/br/br.c b/br/br.c
new file mode 100644
index 0000000..9e5f69c
--- /dev/null
+++ b/br/br.c
@@ -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/br/br_common.h b/br/br_common.h
new file mode 100644
index 0000000..ec1671d
--- /dev/null
+++ b/br/br_common.h
@@ -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/br/fdb.c b/br/fdb.c
new file mode 100644
index 0000000..d849f97
--- /dev/null
+++ b/br/fdb.c
@@ -0,0 +1,245 @@ 
+/*
+ * 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)
+{
+	FILE *fp = 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)
+		fprintf(fp, "Deleted ");
+
+	if (tb[NDA_LLADDR])
+		addr = RTA_DATA(tb[NDA_LLADDR]);
+	else {
+		fprintf(stderr, "missing lladdr\n");
+		return -1;
+	}
+
+	fprintf(fp, "%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]);
+
+		fprintf(fp, "\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated));
+		fprintf(fp, " %8s", fmt_time(b1, sizeof(b1), ci->ndm_used));
+	}
+	fprintf(fp, "\n");
+	fflush(fp);
+	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, stdout, NULL, 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, NULL, 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/br/link.c b/br/link.c
new file mode 100644
index 0000000..1b9541d
--- /dev/null
+++ b/br/link.c
@@ -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/br/monitor.c b/br/monitor.c
new file mode 100644
index 0000000..37468e6
--- /dev/null
+++ b/br/monitor.c
@@ -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;
+}
+
diff --git a/man/man8/br.8 b/man/man8/br.8
new file mode 100644
index 0000000..df2ad29
--- /dev/null
+++ b/man/man8/br.8
@@ -0,0 +1,177 @@ 
+.TH BR 8 "4 October 2011" "iproute2" "Linux"
+.SH NAME
+br \- show / manipulate bridge addresses and devices
+.SH SYNOPSIS
+
+.ad l
+.in +8
+.ti -8
+.B br
+.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 "br fdb" " { " add " | " del " | " change " | " replace " } "
+.I LLADDR
+.B  dev
+.IR DEV " { "
+.BR local " | " temp " }"
+
+.ti -8
+.BR "br fdb" " [ " show " ] [ "
+.B  dev
+.IR DEV " ]"
+
+.ti -8
+.BR "br monitor" " [ " all " | " neigh " | " link " ]"
+
+.SH OPTIONS
+
+.TP
+.BR "\-V" , " -Version"
+print the version of the
+.B ip
+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 BR - 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 br fdb - forwarding database management
+
+.B fdb
+objects contain known ethernet addresses fona  link.
+
+.P
+The corresponding commands display fdb entries, add new entries,
+and delete old ones.
+
+.SS br fdb add - add a new neighbour entry
+.SS br fdb change - change an existing entry
+.SS br fdb replace - add a new entry or change an existing one
+
+These commands create new neighbour 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 br fdb delete - delete a forwarding database entry
+This command removes an existing fdb entry.
+
+.PP
+The arguments are the same as with
+.BR "br fdb add" ,
+
+.SS br 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 br monitor - state monitoring
+
+The
+.B br
+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 "br 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 ip
+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 HISTORY
+.B br
+was written by Stephen Hemminger and uses kernel facilities added in Linux 3.0
+.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