openbgpd (4.6-3) 0001-Import-FreeBSD-port-patches-4.9.20110612.patch

Summary

 Makefile                        |    5 
 Makefile.inc                    |    5 
 bgpctl/Makefile                 |    7 
 bgpctl/bgpctl.8                 |   49 -
 bgpctl/bgpctl.c                 |  423 +++++----
 bgpctl/irr_parser.c             |    8 
 bgpctl/irr_prefix.c             |   92 +-
 bgpctl/irrfilter.c              |    3 
 bgpctl/irrfilter.h              |   14 
 bgpctl/parser.c                 |  100 +-
 bgpctl/parser.h                 |   10 
 bgpctl/whois.c                  |    5 
 bgpd/Makefile                   |   18 
 bgpd/bgpd.8                     |   73 +
 bgpd/bgpd.c                     |  373 ++++----
 bgpd/bgpd.conf.5                |  374 +++++++-
 bgpd/bgpd.h                     |  380 ++++++--
 bgpd/buffer.c                   |  305 -------
 bgpd/carp.c                     |   13 
 bgpd/config.c                   |   51 -
 bgpd/control.c                  |   61 +
 bgpd/imsg.c                     |  268 ------
 bgpd/imsg.h                     |  108 --
 bgpd/kroute.c                   | 1716 ++++++++++++++++++++++++++++------------
 bgpd/log.c                      |   39 
 bgpd/mrt.c                      |  228 +++--
 bgpd/mrt.h                      |   15 
 bgpd/parse.y                    |  926 ++++++++++++++++-----
 bgpd/pfkey.c                    |  104 +-
 bgpd/pftable.c                  |    4 
 bgpd/printconf.c                |  230 +++--
 bgpd/rde.c                      | 1389 ++++++++++++++++++++++----------
 bgpd/rde.h                      |   82 +
 bgpd/rde_attr.c                 |  344 +++++++-
 bgpd/rde_decide.c               |   14 
 bgpd/rde_filter.c               |  118 ++
 bgpd/rde_prefix.c               |  179 ++--
 bgpd/rde_rib.c                  |  173 ++--
 bgpd/rde_update.c               |  315 +++++--
 bgpd/session.c                  |  726 ++++++++--------
 bgpd/session.h                  |   37 
 bgpd/timer.c                    |    4 
 bgpd/util.c                     |  248 +++++
 openbsd-compat/fmt_scaled.c     |  268 ++++++
 openbsd-compat/hash.h           |  127 ++
 openbsd-compat/if_media.h       |  612 ++++++++++++++
 openbsd-compat/imsg-buffer.c    |  303 +++++++
 openbsd-compat/imsg.c           |  271 ++++++
 openbsd-compat/imsg.h           |  112 ++
 openbsd-compat/openbsd-compat.h |   87 ++
 openbsd-compat/pfkey_compat.c   |   32 
 openbsd-compat/util.h           |  119 ++
 52 files changed, 8147 insertions(+), 3420 deletions(-)

    
download this patch

Patch contents

From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Bobbio?= <lunar@debian.org>
Date: Sat, 23 Jul 2011 17:37:22 +0200
Subject: [PATCH] Import FreeBSD port patches (4.9.20110612)

---
 Makefile                        |    5 +
 Makefile.inc                    |    5 +
 bgpctl/Makefile                 |    7 +-
 bgpctl/bgpctl.8                 |   49 +-
 bgpctl/bgpctl.c                 |  423 ++++++-----
 bgpctl/irr_parser.c             |    8 +-
 bgpctl/irr_prefix.c             |   92 ++-
 bgpctl/irrfilter.c              |    3 +
 bgpctl/irrfilter.h              |   14 +-
 bgpctl/parser.c                 |  100 ++-
 bgpctl/parser.h                 |   10 +-
 bgpctl/whois.c                  |    5 +-
 bgpd/Makefile                   |   18 +-
 bgpd/bgpd.8                     |   73 +-
 bgpd/bgpd.c                     |  373 +++++-----
 bgpd/bgpd.conf.5                |  374 ++++++++--
 bgpd/bgpd.h                     |  380 +++++++---
 bgpd/buffer.c                   |  305 -------
 bgpd/carp.c                     |   13 +-
 bgpd/config.c                   |   51 +-
 bgpd/control.c                  |   61 +-
 bgpd/imsg.c                     |  268 ------
 bgpd/imsg.h                     |  108 ---
 bgpd/kroute.c                   | 1716 ++++++++++++++++++++++++++++-----------
 bgpd/log.c                      |   39 +-
 bgpd/mrt.c                      |  228 ++++--
 bgpd/mrt.h                      |   15 +-
 bgpd/parse.y                    |  926 ++++++++++++++++-----
 bgpd/pfkey.c                    |  104 ++--
 bgpd/pftable.c                  |    4 +-
 bgpd/printconf.c                |  230 ++++--
 bgpd/rde.c                      | 1389 ++++++++++++++++++++++----------
 bgpd/rde.h                      |   82 ++-
 bgpd/rde_attr.c                 |  344 ++++++++-
 bgpd/rde_decide.c               |   14 +-
 bgpd/rde_filter.c               |  118 ++-
 bgpd/rde_prefix.c               |  179 ++--
 bgpd/rde_rib.c                  |  173 +++--
 bgpd/rde_update.c               |  315 +++++--
 bgpd/session.c                  |  726 +++++++++--------
 bgpd/session.h                  |   37 +-
 bgpd/timer.c                    |    4 +-
 bgpd/util.c                     |  248 ++++++-
 openbsd-compat/fmt_scaled.c     |  268 ++++++
 openbsd-compat/hash.h           |  127 +++
 openbsd-compat/if_media.h       |  612 ++++++++++++++
 openbsd-compat/imsg-buffer.c    |  303 +++++++
 openbsd-compat/imsg.c           |  271 ++++++
 openbsd-compat/imsg.h           |  112 +++
 openbsd-compat/openbsd-compat.h |   87 ++
 openbsd-compat/pfkey_compat.c   |   32 +
 openbsd-compat/util.h           |  119 +++
 52 files changed, 8147 insertions(+), 3420 deletions(-)
 create mode 100644 Makefile
 create mode 100644 Makefile.inc
 delete mode 100644 bgpd/buffer.c
 delete mode 100644 bgpd/imsg.c
 delete mode 100644 bgpd/imsg.h
 create mode 100644 openbsd-compat/fmt_scaled.c
 create mode 100644 openbsd-compat/hash.h
 create mode 100644 openbsd-compat/if_media.h
 create mode 100644 openbsd-compat/imsg-buffer.c
 create mode 100644 openbsd-compat/imsg.c
 create mode 100644 openbsd-compat/imsg.h
 create mode 100644 openbsd-compat/openbsd-compat.h
 create mode 100644 openbsd-compat/pfkey_compat.c
 create mode 100644 openbsd-compat/util.h

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..520c1df
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+# $hrs: openbgpd/Makefile,v 1.2 2009/06/30 07:07:55 hrs Exp $
+
+SUBDIR=	bgpd bgpctl
+
+.include <bsd.subdir.mk>
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..efde290
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,5 @@
+# $hrs: openbgpd/Makefile.inc,v 1.2 2009/06/30 07:19:13 hrs Exp $
+
+PREFIX?=	/usr/local
+BINDIR?=	${PREFIX}/sbin
+MANDIR?=	${PREFIX}/man/man
diff --git a/bgpctl/Makefile b/bgpctl/Makefile
index 575a423..c6d3ec5 100644
--- a/bgpctl/Makefile
+++ b/bgpctl/Makefile
@@ -1,17 +1,18 @@
 #	$OpenBSD: Makefile,v 1.10 2007/12/20 17:08:48 henning Exp $
 
-.PATH:		${.CURDIR}/../bgpd
+.PATH:		${.CURDIR}/../bgpd ${.CURDIR}/../openbsd-compat
 
 PROG=	bgpctl
-SRCS=	bgpctl.c parser.c buffer.c imsg.c util.c timer.c
+SRCS=	bgpctl.c parser.c util.c timer.c
 SRCS+=	irrfilter.c whois.c irr_asset.c irr_prefix.c irr_output.c
 SRCS+=	irr_parser.c
+SRCS+=	fmt_scaled.c imsg.c imsg-buffer.c
 CFLAGS+= -Wall
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+= -Wsign-compare
-CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../bgpd
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../bgpd -I${.CURDIR}/../openbsd-compat
 MAN=	bgpctl.8
 LDADD= -lutil
 DPADD+= ${LIBUTIL}
diff --git a/bgpctl/bgpctl.8 b/bgpctl/bgpctl.8
index 73fb8ea..26e55a1 100644
--- a/bgpctl/bgpctl.8
+++ b/bgpctl/bgpctl.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpctl.8,v 1.49 2009/06/06 06:11:17 claudio Exp $
+.\" $OpenBSD: bgpctl.8,v 1.52 2009/11/03 08:09:15 jmc Exp $
 .\"
 .\" Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: June 6 2009 $
+.Dd $Mdocdate: May 3 2010 $
 .Dt BGPCTL 8
 .Os
 .Sh NAME
@@ -32,8 +32,7 @@ The
 program controls the
 .Xr bgpd 8
 daemon.
-Commands to switch between displays may be abbreviated to the
-minimum unambiguous prefix; for example,
+Commands may be abbreviated to the minimum unambiguous prefix; for example,
 .Cm s s
 for
 .Cm show summary .
@@ -53,11 +52,19 @@ to communicate with
 .Pp
 The commands are as follows:
 .Bl -tag -width xxxxxx
-.It Cm fib couple
-Insert the learned routes into the Forwarding Information Base
+.It Xo
+.Cm fib
+.Op Cm table Ar number
+.Cm couple
+.Xc
+Insert the learned routes into the specified Forwarding Information Base
 a.k.a. the kernel routing table.
-.It Cm fib decouple
-Remove the learned routes from the Forwarding Information Base
+.It Xo
+.Cm fib
+.Op Cm table Ar number
+.Cm decouple
+.Xc
+Remove the learned routes from the specified Forwarding Information Base
 a.k.a. the kernel routing table.
 .It Xo
 .Cm irrfilter
@@ -79,7 +86,15 @@ The options are as follows:
 Use
 .Ar directory
 to write the filter files to.
+.It Fl 4
+Fetch only IPv4 prefixes from the registry.
+.It Fl 6
+Fetch only IPv6 prefixes from the registry.
 .El
+.It Cm log brief
+Disable verbose debug logging.
+.It Cm log verbose
+Enable verbose debug logging.
 .It Cm neighbor Ar peer Cm up
 Take the BGP session to the specified neighbor up.
 .Ar peer
@@ -98,8 +113,10 @@ Note that the neighbor is not obliged to re-send all routes, or any routes at
 all, even if it announced the route refresh capability.
 .Ar peer
 may be the neighbor's address or description.
-.It Cm network add Ar prefix
+.It Cm network add Ar prefix Op Ar arguments
 Add the specified prefix to the list of announced networks.
+It is possible to set various path attributes with additional
+.Ar arguments .
 .It Cm network delete Ar prefix
 Remove the specified prefix from the list of announced networks.
 .It Cm network flush
@@ -122,7 +139,7 @@ view of the Forwarding Information Base.
 can be an IP address, in which case the route to this address is shown,
 or a flag:
 .Pp
-.Bl -tag -width connected -compact
+.Bl -tag -width tableXnumber -compact
 .It Cm connected
 Show only connected routes.
 .It Cm static
@@ -133,6 +150,14 @@ Show only routes originating from
 itself.
 .It Cm nexthop
 Show only routes required to reach a BGP nexthop.
+.It Cm inet
+Show only IPv4 routes.
+.It Cm inet6
+Show only IPv6 routes.
+.It Cm table Ar number
+Show the routing table with ID
+.Ar number
+instead of the default routing table with ID 0.
 .El
 .It Cm show interfaces
 Show the interface states.
@@ -243,10 +268,12 @@ and message counters.
 .It Cm show summary terse
 Show a list of all neighbors, including information about the session state,
 in a terse format.
+.It Cm show tables
+Show a list of all currently loaded fib routing tables.
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/bgpd.sockXXX" -compact
-.It Pa /etc/bgpd.conf
+.It Pa %%PREFIX%%/etc/bgpd.conf
 default
 .Xr bgpd 8
 configuration file
diff --git a/bgpctl/bgpctl.c b/bgpctl/bgpctl.c
index b11a072..c684295 100644
--- a/bgpctl/bgpctl.c
+++ b/bgpctl/bgpctl.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: bgpctl.c,v 1.142 2009/06/06 06:33:15 eric Exp $ */
+/*	$OpenBSD: bgpctl.c,v 1.157 2010/03/08 17:02:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -16,11 +16,19 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#if defined(__FreeBSD__)	/* compat */
+#include "openbsd-compat.h"
+#endif /* defined(__FreeBSD__) */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <net/if.h>
+#if defined(__FreeBSD__)	/* net/if_media.h */
+#include "if_media.h"
+#else
 #include <net/if_media.h>
+#endif /* defined(__FreeBSD__) */
 #include <net/if_types.h>
 
 #include <err.h>
@@ -29,7 +37,11 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#if defined(__FreeBSD__)	/* util.h */
+#include "util.h"
+#else
 #include <util.h>
+#endif /* defined(__FreeBSD__) */
 
 #include "bgpd.h"
 #include "session.h"
@@ -38,6 +50,10 @@
 #include "parser.h"
 #include "irrfilter.h"
 
+#if defined(__FreeBSD__) /* FreeBSD has no LINK_STATE_IS_UP macro. */
+#define LINK_STATE_IS_UP(_s)  ((_s) >= LINK_STATE_UP)
+#endif /* defined(__FreeBSD__) */ 
+
 enum neighbor_views {
 	NV_DEFAULT,
 	NV_TIMERS
@@ -50,12 +66,13 @@ int		 show_summary_msg(struct imsg *, int);
 int		 show_summary_terse_msg(struct imsg *, int);
 int		 show_neighbor_terse(struct imsg *);
 int		 show_neighbor_msg(struct imsg *, enum neighbor_views);
-void		 print_neighbor_capa_mp_safi(u_int8_t);
+void		 print_neighbor_capa_mp(struct peer *);
 void		 print_neighbor_msgstats(struct peer *);
 void		 print_timer(const char *, time_t);
 static char	*fmt_timeframe(time_t t);
 static char	*fmt_timeframe_core(time_t t);
 void		 show_fib_head(void);
+void		 show_fib_tables_head(void);
 void		 show_network_head(void);
 void		 show_fib_flags(u_int16_t);
 int		 show_fib_msg(struct imsg *);
@@ -65,7 +82,7 @@ void		 show_interface_head(void);
 int		 ift2ifm(int);
 const char *	 get_media_descr(int);
 const char *	 get_linkstate(int, int);
-void		 print_baudrate(u_int64_t);
+const char *	 get_baudrate(u_int64_t, char *);
 int		 show_interface_msg(struct imsg *);
 void		 show_rib_summary_head(void);
 void		 print_prefix(struct bgpd_addr *, u_int8_t, u_int8_t);
@@ -74,7 +91,6 @@ void		 print_flags(u_int8_t, int);
 int		 show_rib_summary_msg(struct imsg *);
 int		 show_rib_detail_msg(struct imsg *, int);
 void		 show_community(u_char *, u_int16_t);
-const char	*get_ext_subtype(u_int8_t);
 void		 show_ext_community(u_char *, u_int16_t);
 char		*fmt_mem(int64_t);
 int		 show_rib_memory_msg(struct imsg *);
@@ -98,7 +114,7 @@ int
 main(int argc, char *argv[])
 {
 	struct sockaddr_un	 sun;
-	int			 fd, n, done, ch, nodescr = 0;
+	int			 fd, n, done, ch, nodescr = 0, verbose = 0;
 	struct imsg		 imsg;
 	struct network_config	 net;
 	struct parse_result	*res;
@@ -128,8 +144,11 @@ main(int argc, char *argv[])
 	if ((res = parse(argc, argv)) == NULL)
 		exit(1);
 
-	if (res->action == IRRFILTER)
+	if (res->action == IRRFILTER) {
+		if (!(res->flags & (F_IPV4|F_IPV6)))
+			res->flags |= (F_IPV4|F_IPV6);
 		irr_main(res->as.as, res->flags, res->irr_outdir);
+	}
 
 	memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr));
 	strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr));
@@ -164,24 +183,32 @@ main(int argc, char *argv[])
 		imsg_compose(ibuf, IMSG_CTL_SHOW_TERSE, 0, 0, -1, NULL, 0);
 		break;
 	case SHOW_FIB:
-		if (!res->addr.af) {
-			struct buf	*msg;
-
-			if ((msg = imsg_create(ibuf, IMSG_CTL_KROUTE, 0, 0,
-			    sizeof(res->flags) + sizeof(res->af))) == NULL)
+		if (!res->addr.aid) {
+			struct ibuf	*msg;
+			sa_family_t	 af;
+
+			af = aid2af(res->aid);
+			if ((msg = imsg_create(ibuf, IMSG_CTL_KROUTE,
+			    res->rtableid, 0, sizeof(res->flags) +
+			    sizeof(af))) == NULL)
 				errx(1, "imsg_create failure");
 			if (imsg_add(msg, &res->flags, sizeof(res->flags)) ==
 			    -1 ||
-			    imsg_add(msg, &res->af, sizeof(res->af)) == -1)
+			    imsg_add(msg, &af, sizeof(af)) == -1)
 				errx(1, "imsg_add failure");
 			imsg_close(ibuf, msg);
 		} else
-			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, 0, 0, -1,
-			    &res->addr, sizeof(res->addr));
+			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, res->rtableid,
+			    0, -1, &res->addr, sizeof(res->addr));
 		show_fib_head();
 		break;
+	case SHOW_FIB_TABLES:
+		imsg_compose(ibuf, IMSG_CTL_SHOW_FIB_TABLES, 0, 0, -1, NULL, 0);
+		show_fib_tables_head();
+		break;
 	case SHOW_NEXTHOP:
-		imsg_compose(ibuf, IMSG_CTL_SHOW_NEXTHOP, 0, 0, -1, NULL, 0);
+		imsg_compose(ibuf, IMSG_CTL_SHOW_NEXTHOP, res->rtableid, 0, -1,
+		    NULL, 0);
 		show_nexthop_head();
 		break;
 	case SHOW_INTERFACE:
@@ -192,7 +219,7 @@ main(int argc, char *argv[])
 	case SHOW_NEIGHBOR_TIMERS:
 	case SHOW_NEIGHBOR_TERSE:
 		neighbor.show_timers = (res->action == SHOW_NEIGHBOR_TIMERS);
-		if (res->peeraddr.af || res->peerdesc[0])
+		if (res->peeraddr.aid || res->peerdesc[0])
 			imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
 			    &neighbor, sizeof(neighbor));
 		else
@@ -206,7 +233,7 @@ main(int argc, char *argv[])
 			memcpy(&ribreq.as, &res->as, sizeof(res->as));
 			type = IMSG_CTL_SHOW_RIB_AS;
 		}
-		if (res->addr.af) {
+		if (res->addr.aid) {
 			memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr));
 			ribreq.prefixlen = res->prefixlen;
 			type = IMSG_CTL_SHOW_RIB_PREFIX;
@@ -220,7 +247,7 @@ main(int argc, char *argv[])
 		memcpy(&ribreq.neighbor, &neighbor,
 		    sizeof(ribreq.neighbor));
 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
-		ribreq.af = res->af;
+		ribreq.aid = res->aid;
 		ribreq.flags = res->flags;
 		imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq));
 		if (!(res->flags & F_CTL_DETAIL))
@@ -237,12 +264,14 @@ main(int argc, char *argv[])
 		errx(1, "action==FIB");
 		break;
 	case FIB_COUPLE:
-		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, 0, 0, -1, NULL, 0);
+		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, res->rtableid, 0, -1,
+		    NULL, 0);
 		printf("couple request sent.\n");
 		done = 1;
 		break;
 	case FIB_DECOUPLE:
-		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, 0, 0, -1, NULL, 0);
+		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, res->rtableid, 0, -1,
+		    NULL, 0);
 		printf("decouple request sent.\n");
 		done = 1;
 		break;
@@ -290,12 +319,21 @@ main(int argc, char *argv[])
 		break;
 	case NETWORK_SHOW:
 		bzero(&ribreq, sizeof(ribreq));
-		ribreq.af = res->af;
+		ribreq.aid = res->aid;
 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
 		imsg_compose(ibuf, IMSG_CTL_SHOW_NETWORK, 0, 0, -1,
 		    &ribreq, sizeof(ribreq));
 		show_network_head();
 		break;
+	case LOG_VERBOSE:
+		verbose = 1;
+		/* FALLTHROUGH */
+	case LOG_BRIEF:
+		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
+		    &verbose, sizeof(verbose));
+		printf("logging request sent.\n");
+		done = 1;
+		break;
 	}
 
 	while (ibuf->w.queued)
@@ -304,13 +342,13 @@ main(int argc, char *argv[])
 
 	while (!done) {
 		if ((n = imsg_read(ibuf)) == -1)
-			errx(1, "imsg_read error");
+			err(1, "imsg_read error");
 		if (n == 0)
 			errx(1, "pipe closed");
 
 		while (!done) {
 			if ((n = imsg_get(ibuf, &imsg)) == -1)
-				errx(1, "imsg_get error");
+				err(1, "imsg_get error");
 			if (n == 0)
 				break;
 
@@ -329,6 +367,8 @@ main(int argc, char *argv[])
 				done = show_summary_terse_msg(&imsg, nodescr);
 				break;
 			case SHOW_FIB:
+			case SHOW_FIB_TABLES:
+			case NETWORK_SHOW:
 				done = show_fib_msg(&imsg);
 				break;
 			case SHOW_NEXTHOP:
@@ -356,9 +396,6 @@ main(int argc, char *argv[])
 			case SHOW_RIB_MEM:
 				done = show_rib_memory_msg(&imsg);
 				break;
-			case NETWORK_SHOW:
-				done = show_fib_msg(&imsg);
-				break;
 			case NEIGHBOR:
 			case NEIGHBOR_UP:
 			case NEIGHBOR_DOWN:
@@ -373,6 +410,8 @@ main(int argc, char *argv[])
 			case NETWORK_REMOVE:
 			case NETWORK_FLUSH:
 			case IRRFILTER:
+			case LOG_VERBOSE:
+			case LOG_BRIEF:
 				break;
 			}
 			imsg_free(&imsg);
@@ -398,8 +437,8 @@ fmt_peer(const char *descr, const struct bgpd_addr *remote_addr,
 	}
 
 	ip = log_addr(remote_addr);
-	if (masklen != -1 && ((remote_addr->af == AF_INET && masklen != 32) ||
-	    (remote_addr->af == AF_INET6 && masklen != 128))) {
+	if (masklen != -1 && ((remote_addr->aid == AID_INET && masklen != 32) ||
+	    (remote_addr->aid == AID_INET6 && masklen != 128))) {
 		if (asprintf(&p, "%s/%u", ip, masklen) == -1)
 			err(1, NULL);
 	} else {
@@ -521,13 +560,15 @@ show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv)
 	struct ctl_timer	*t;
 	struct in_addr		 ina;
 	char			 buf[NI_MAXHOST], pbuf[NI_MAXSERV], *s;
+	int			 hascapamp = 0;
+	u_int8_t		 i;
 
 	switch (imsg->hdr.type) {
 	case IMSG_CTL_SHOW_NEIGHBOR:
 		p = imsg->data;
-		if ((p->conf.remote_addr.af == AF_INET &&
+		if ((p->conf.remote_addr.aid == AID_INET &&
 		    p->conf.remote_masklen != 32) ||
-		    (p->conf.remote_addr.af == AF_INET6 &&
+		    (p->conf.remote_addr.aid == AID_INET6 &&
 		    p->conf.remote_masklen != 128)) {
 			if (asprintf(&s, "%s/%u",
 			    log_addr(&p->conf.remote_addr),
@@ -549,6 +590,10 @@ show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv)
 			printf(", Template");
 		if (p->conf.cloned)
 			printf(", Cloned");
+		if (p->conf.passive)
+			printf(", Passive");
+		if (p->conf.ebgp && p->conf.distance > 1)
+			printf(", Multihop (%u)", (int)p->conf.distance);
 		printf("\n");
 		if (p->conf.descr[0])
 			printf(" Description: %s\n", p->conf.descr);
@@ -563,17 +608,16 @@ show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv)
 		printf("  Last read %s, holdtime %us, keepalive interval %us\n",
 		    fmt_timeframe(p->stats.last_read),
 		    p->holdtime, p->holdtime/3);
-		if (p->capa.peer.mp_v4 || p->capa.peer.mp_v6 ||
-		    p->capa.peer.refresh || p->capa.peer.restart ||
-		    p->capa.peer.as4byte) {
+		for (i = 0; i < AID_MAX; i++)
+			if (p->capa.peer.mp[i])
+				hascapamp = 1;
+		if (hascapamp || p->capa.peer.refresh ||
+		    p->capa.peer.restart || p->capa.peer.as4byte) {
 			printf("  Neighbor capabilities:\n");
-			if (p->capa.peer.mp_v4) {
-				printf("    Multiprotocol extensions: IPv4");
-				print_neighbor_capa_mp_safi(p->capa.peer.mp_v4);
-			}
-			if (p->capa.peer.mp_v6) {
-				printf("    Multiprotocol extensions: IPv6");
-				print_neighbor_capa_mp_safi(p->capa.peer.mp_v6);
+			if (hascapamp) {
+				printf("    Multiprotocol extensions: ");
+				print_neighbor_capa_mp(p);
+				printf("\n");
 			}
 			if (p->capa.peer.refresh)
 				printf("    Route Refresh\n");
@@ -633,20 +677,16 @@ show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv)
 }
 
 void
-print_neighbor_capa_mp_safi(u_int8_t safi)
+print_neighbor_capa_mp(struct peer *p)
 {
-	switch (safi) {
-	case SAFI_UNICAST:
-		printf(" Unicast");
-		break;
-	case SAFI_MULTICAST:
-		printf(" Multicast");
-		break;
-	default:
-		printf(" unknown (%u)", safi);
-		break;
-	}
-	printf("\n");
+	int		comma;
+	u_int8_t	i;
+
+	for (i = 0, comma = 0; i < AID_MAX; i++)
+		if (p->capa.peer.mp[i]) {
+			printf("%s%s", comma ? ", " : "", aid2str(i));
+			comma = 1;
+		}
 }
 
 void
@@ -680,7 +720,7 @@ print_neighbor_msgstats(struct peer *p)
 }
 
 void
-print_timer(const char *name, timer_t d)
+print_timer(const char *name, time_t d)
 {
 	printf("  %-20s ", name);
 
@@ -745,6 +785,12 @@ show_fib_head(void)
 }
 
 void
+show_fib_tables_head(void)
+{
+	printf("%-5s %-20s %-8s\n", "Table", "Description", "State");
+}
+
+void
 show_network_head(void)
 {
 	printf("flags: S = Static\n");
@@ -788,56 +834,44 @@ show_fib_flags(u_int16_t flags)
 int
 show_fib_msg(struct imsg *imsg)
 {
-	struct kroute		*k;
-	struct kroute6		*k6;
+	struct kroute_full	*kf;
+	struct ktable		*kt;
 	char			*p;
 
 	switch (imsg->hdr.type) {
 	case IMSG_CTL_KROUTE:
 	case IMSG_CTL_SHOW_NETWORK:
-		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute))
+		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf))
 			errx(1, "wrong imsg len");
-		k = imsg->data;
+		kf = imsg->data;
 
-		show_fib_flags(k->flags);
+		show_fib_flags(kf->flags);
 
-		if (asprintf(&p, "%s/%u", inet_ntoa(k->prefix), k->prefixlen) ==
-		    -1)
+		if (asprintf(&p, "%s/%u", log_addr(&kf->prefix),
+		    kf->prefixlen) == -1)
 			err(1, NULL);
-		printf("%4i %-20s ", k->priority, p);
+		printf("%4i %-20s ", kf->priority, p);
 		free(p);
 
-		if (k->nexthop.s_addr)
-			printf("%s", inet_ntoa(k->nexthop));
-		else if (k->flags & F_CONNECTED)
-			printf("link#%u", k->ifindex);
+		if (kf->flags & F_CONNECTED)
+			printf("link#%u", kf->ifindex);
+		else
+			printf("%s", log_addr(&kf->nexthop));
 		printf("\n");
 
 		break;
-	case IMSG_CTL_KROUTE6:
-	case IMSG_CTL_SHOW_NETWORK6:
-		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(struct kroute6))
+	case IMSG_CTL_SHOW_FIB_TABLES:
+		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt))
 			errx(1, "wrong imsg len");
-		k6 = imsg->data;
+		kt = imsg->data;
 
-		show_fib_flags(k6->flags);
-
-		if (asprintf(&p, "%s/%u", log_in6addr(&k6->prefix),
-		    k6->prefixlen) == -1)
-			err(1, NULL);
-		printf("%4i %-20s ", k6->priority, p);
-		free(p);
-
-		if (!IN6_IS_ADDR_UNSPECIFIED(&k6->nexthop))
-			printf("%s", log_in6addr(&k6->nexthop));
-		else if (k6->flags & F_CONNECTED)
-			printf("link#%u", k6->ifindex);
-		printf("\n");
+		printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr,
+		    kt->fib_sync ? "coupled" : "decoupled",
+		    kt->fib_sync != kt->fib_conf ? "*" : "");
 
 		break;
 	case IMSG_CTL_END:
 		return (1);
-		break;
 	default:
 		break;
 	}
@@ -848,35 +882,70 @@ show_fib_msg(struct imsg *imsg)
 void
 show_nexthop_head(void)
 {
-	printf("%-20s %-10s\n", "Nexthop", "State");
+	printf("Flags: * = nexthop valid\n");
+	printf("\n  %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route",
+	     "Prio", "Gateway", "Iface");
 }
 
 int
 show_nexthop_msg(struct imsg *imsg)
 {
 	struct ctl_show_nexthop	*p;
-	int			 ifms_type;
+	struct kroute		*k;
+	struct kroute6		*k6;
+	char			*s;
 
 	switch (imsg->hdr.type) {
 	case IMSG_CTL_SHOW_NEXTHOP:
 		p = imsg->data;
-		printf("%-20s %-10s", log_addr(&p->addr),
-		    p->valid ? "valid" : "invalid");
+		printf("%s %-15s ", p->valid ? "*" : " ", log_addr(&p->addr));
+		if (!p->krvalid) {
+			printf("\n");
+			return (0);
+		}
+		switch (p->addr.aid) {
+		case AID_INET:
+			k = &p->kr.kr4;
+			if (asprintf(&s, "%s/%u", inet_ntoa(k->prefix),
+			    k->prefixlen) == -1)
+				err(1, NULL);
+			printf("%-20s", s);
+			free(s);
+			printf("%3i %-15s ", k->priority,
+			    k->flags & F_CONNECTED ? "connected" :
+			    inet_ntoa(k->nexthop));
+			break;
+		case AID_INET6:
+			k6 = &p->kr.kr6;
+			if (asprintf(&s, "%s/%u", log_in6addr(&k6->prefix),
+			    k6->prefixlen) == -1)
+				err(1, NULL);
+			printf("%-20s", s);
+			free(s);
+			printf("%3i %-15s ", k6->priority,
+			    k6->flags & F_CONNECTED ? "connected" :
+			    log_in6addr(&k6->nexthop));
+			break;
+		default:
+			printf("unknown address family\n");
+			return (0);
+		}
 		if (p->kif.ifname[0]) {
-			printf("%-8s", p->kif.ifname);
-			if (p->kif.flags & IFF_UP) {
-				printf("UP");
-				ifms_type = ift2ifm(p->kif.media_type);
-				if (ifms_type != 0)
-					printf(", %s, %s",
-					    get_media_descr(ifms_type),
-					    get_linkstate(ifms_type,
-					    p->kif.link_state));
-				if (p->kif.baudrate) {
-					printf(", ");
-					print_baudrate(p->kif.baudrate);
-				}
-			}
+			char *s1;
+			if (p->kif.baudrate) {
+				if (asprintf(&s1, ", %s",
+				    get_baudrate(p->kif.baudrate,
+				    "bps")) == -1)
+					err(1, NULL);
+			} else if (asprintf(&s1, ", %s", get_linkstate(
+			    p->kif.media_type, p->kif.link_state)) == -1)
+					err(1, NULL);
+			if (asprintf(&s, "%s (%s%s)", p->kif.ifname,
+			    p->kif.flags & IFF_UP ? "UP" : "DOWN", s1) == -1)
+				err(1, NULL);
+			printf("%-15s", s);
+			free(s1);
+			free(s);
 		}
 		printf("\n");
 		break;
@@ -898,9 +967,8 @@ show_interface_head(void)
 	    "Link state");
 }
 
-const int	ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
-const struct ifmedia_status_description
-		ifm_status_descriptions[] = IFM_STATUS_DESCRIPTIONS;
+const struct if_status_description
+		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
 const struct ifmedia_description
 		ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS;
 
@@ -936,36 +1004,36 @@ get_media_descr(int media_type)
 const char *
 get_linkstate(int media_type, int link_state)
 {
-	const struct ifmedia_status_description	*p;
-	int					 i;
-
-	if (link_state == LINK_STATE_UNKNOWN)
-		return ("unknown");
-
-	for (i = 0; ifm_status_valid_list[i] != 0; i++)
-		for (p = ifm_status_descriptions; p->ifms_valid != 0; p++) {
-			if (p->ifms_type != media_type ||
-			    p->ifms_valid != ifm_status_valid_list[i])
-				continue;
-			if (LINK_STATE_IS_UP(link_state))
-				return (p->ifms_string[1]);
-			return (p->ifms_string[0]);
-		}
+	const struct if_status_description *p;
+	static char buf[8];
 
-	return ("unknown link state");
+	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
+		if (LINK_STATE_DESC_MATCH(p, media_type, link_state))
+			return (p->ifs_string);
+	}
+	snprintf(buf, sizeof(buf), "[#%d]", link_state);
+	return (buf);
 }
 
-void
-print_baudrate(u_int64_t baudrate)
+const char *
+get_baudrate(u_int64_t baudrate, char *unit)
 {
+	static char bbuf[16];
+
 	if (baudrate > IF_Gbps(1))
-		printf("%llu GBit/s", baudrate / IF_Gbps(1));
+		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
+		    baudrate / IF_Gbps(1), unit);
 	else if (baudrate > IF_Mbps(1))
-		printf("%llu MBit/s", baudrate / IF_Mbps(1));
+		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
+		    baudrate / IF_Mbps(1), unit);
 	else if (baudrate > IF_Kbps(1))
-		printf("%llu KBit/s", baudrate / IF_Kbps(1));
+		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
+		    baudrate / IF_Kbps(1), unit);
 	else
-		printf("%llu Bit/s", baudrate);
+		snprintf(bbuf, sizeof(bbuf), "%llu %s",
+		    baudrate, unit);
+
+	return (bbuf);
 }
 
 int
@@ -982,17 +1050,12 @@ show_interface_msg(struct imsg *imsg)
 		printf("%-15s", k->flags & IFF_UP ? "UP" : "");
 
 		if ((ifms_type = ift2ifm(k->media_type)) != 0)
-			printf("%s, %s", get_media_descr(ifms_type),
-			    get_linkstate(ifms_type, k->link_state));
-		else if (k->link_state == LINK_STATE_UNKNOWN)
-			printf("unknown");
-		else
-			printf("link state %u", k->link_state);
+			printf("%s, ", get_media_descr(ifms_type));
 
-		if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0) {
-			printf(", ");
-			print_baudrate(k->baudrate);
-		}
+		printf("%s", get_linkstate(k->media_type, k->link_state));
+
+		if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0)
+			printf(", %s", get_baudrate(k->baudrate, "Bit/s"));
 		printf("\n");
 		break;
 	case IMSG_CTL_END:
@@ -1011,7 +1074,7 @@ show_rib_summary_head(void)
 	printf(
 	    "flags: * = Valid, > = Selected, I = via IBGP, A = Announced\n");
 	printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n");
-	printf("%-5s %-20s%-15s  %5s %5s %s\n", "flags", "destination",
+	printf("%-5s %-20s %-15s  %5s %5s %s\n", "flags", "destination",
 	    "gateway", "lpref", "med", "aspath origin");
 }
 
@@ -1049,26 +1112,26 @@ print_flags(u_int8_t flags, int sum)
 	char	*p = flagstr;
 
 	if (sum) {
-		if (flags & F_RIB_ANNOUNCE)
+		if (flags & F_PREF_ANNOUNCE)
 			*p++ = 'A';
-		if (flags & F_RIB_INTERNAL)
+		if (flags & F_PREF_INTERNAL)
 			*p++ = 'I';
-		if (flags & F_RIB_ELIGIBLE)
+		if (flags & F_PREF_ELIGIBLE)
 			*p++ = '*';
-		if (flags & F_RIB_ACTIVE)
+		if (flags & F_PREF_ACTIVE)
 			*p++ = '>';
 		*p = '\0';
 		printf("%-5s ", flagstr);
 	} else {
-		if (flags & F_RIB_INTERNAL)
+		if (flags & F_PREF_INTERNAL)
 			printf("internal");
 		else
 			printf("external");
-		if (flags & F_RIB_ELIGIBLE)
+		if (flags & F_PREF_ELIGIBLE)
 			printf(", valid");
-		if (flags & F_RIB_ACTIVE)
+		if (flags & F_PREF_ACTIVE)
 			printf(", best");
-		if (flags & F_RIB_ANNOUNCE)
+		if (flags & F_PREF_ANNOUNCE)
 			printf(", announced");
 	}
 }
@@ -1085,7 +1148,7 @@ show_rib_summary_msg(struct imsg *imsg)
 		memcpy(&rib, imsg->data, sizeof(rib));
 
 		print_prefix(&rib.prefix, rib.prefixlen, rib.flags);
-		printf("%-15s ", log_addr(&rib.exit_nexthop));
+		printf(" %-15s ", log_addr(&rib.exit_nexthop));
 
 		printf(" %5u %5u ", rib.local_pref, rib.med);
 
@@ -1189,8 +1252,8 @@ show_rib_detail_msg(struct imsg *imsg, int nodescr)
 		case ATTR_AGGREGATOR:
 			memcpy(&as, data, sizeof(as));
 			memcpy(&id, data + sizeof(as), sizeof(id));
-			printf("    Aggregator: %s [%s]\n", 
-			    log_as(htonl(as)), inet_ntoa(id));
+			printf("    Aggregator: %s [%s]\n",
+			    log_as(ntohl(as)), inet_ntoa(id));
 			break;
 		case ATTR_ORIGINATOR_ID:
 			memcpy(&id, data, sizeof(id));
@@ -1236,22 +1299,27 @@ fmt_mem(int64_t num)
 	return (buf);
 }
 
+size_t  pt_sizes[AID_MAX] = AID_PTSIZE;
+
 int
 show_rib_memory_msg(struct imsg *imsg)
 {
 	struct rde_memstats	stats;
+	size_t			pts = 0;
+	int			i;
 
 	switch (imsg->hdr.type) {
 	case IMSG_CTL_SHOW_RIB_MEM:
 		memcpy(&stats, imsg->data, sizeof(stats));
 		printf("RDE memory statistics\n");
-		printf("%10lld IPv4 network entries using %s of memory\n",
-		    (long long)stats.pt4_cnt, fmt_mem(stats.pt4_cnt *
-		    sizeof(struct pt_entry4)));
-		if (stats.pt6_cnt != 0)
-			printf("%10lld IPv6 network entries using "
-			    "%s of memory\n", (long long)stats.pt6_cnt,
-			    fmt_mem(stats.pt6_cnt * sizeof(struct pt_entry6)));
+		for (i = 0; i < AID_MAX; i++) {
+			if (stats.pt_cnt[i] == 0)
+				continue;
+			pts += stats.pt_cnt[i] * pt_sizes[i];
+			printf("%10lld %s network entries using %s of memory\n",
+			    (long long)stats.pt_cnt[i], aid_vals[i].name,
+			    fmt_mem(stats.pt_cnt[i] * pt_sizes[i]));
+		}
 		printf("%10lld rib entries using %s of memory\n",
 		    (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt *
 		    sizeof(struct rib_entry)));
@@ -1272,9 +1340,7 @@ show_rib_memory_msg(struct imsg *imsg)
 		    (long long)stats.attr_refs);
 		printf("%10lld BGP attributes using %s of memory\n",
 		    (long long)stats.attr_dcnt, fmt_mem(stats.attr_data));
-		printf("RIB using %s of memory\n", fmt_mem(
-		    stats.pt4_cnt * sizeof(struct pt_entry4) +
-		    stats.pt6_cnt * sizeof(struct pt_entry6) +
+		printf("RIB using %s of memory\n", fmt_mem(pts +
 		    stats.prefix_cnt * sizeof(struct prefix) +
 		    stats.rib_cnt * sizeof(struct rib_entry) +
 		    stats.path_cnt * sizeof(struct rde_aspath) +
@@ -1328,30 +1394,6 @@ show_community(u_char *data, u_int16_t len)
 	}
 }
 
-const char *
-get_ext_subtype(u_int8_t type)
-{
-	static char etype[6];
-
-	switch (type) {
-	case EXT_COMMUNITY_ROUTE_TGT:
-		return "rt";	/* route target */
-	case EXT_CUMMUNITY_ROUTE_ORIG:
-		return "soo";	/* source of origin */
-	case EXT_COMMUNITY_OSPF_DOM_ID:
-		return "odi";	/* ospf domain id */
-	case EXT_COMMUNITY_OSPF_RTR_TYPE:
-		return "ort";	/* ospf route type */
-	case EXT_COMMUNITY_OSPF_RTR_ID:
-		return "ori";	/* ospf router id */
-	case EXT_COMMUNITY_BGP_COLLECT:
-		return "bdc";	/* bgp data collection */
-	default:
-		snprintf(etype, sizeof(etype), "[%i]", (int)type);
-		return etype;
-	}
-}
-
 void
 show_ext_community(u_char *data, u_int16_t len)
 {
@@ -1372,28 +1414,29 @@ show_ext_community(u_char *data, u_int16_t len)
 		case EXT_COMMUNITY_TWO_AS:
 			memcpy(&as2, data + i + 2, sizeof(as2));
 			memcpy(&u32, data + i + 4, sizeof(u32));
-			printf("%s %hu:%u", get_ext_subtype(subtype), as2, u32);
+			printf("%s %s:%u", log_ext_subtype(subtype),
+			    log_as(ntohs(as2)), ntohl(u32));
 			break;
 		case EXT_COMMUNITY_IPV4:
 			memcpy(&ip, data + i + 2, sizeof(ip));
 			memcpy(&u16, data + i + 6, sizeof(u16));
-			printf("%s %s:%hu", get_ext_subtype(subtype),
-			    inet_ntoa(ip), u16);
+			printf("%s %s:%hu", log_ext_subtype(subtype),
+			    inet_ntoa(ip), ntohs(u16));
 			break;
 		case EXT_COMMUNITY_FOUR_AS:
 			memcpy(&as4, data + i + 2, sizeof(as4));
 			memcpy(&u16, data + i + 6, sizeof(u16));
-			printf("%s %s:%hu", get_ext_subtype(subtype),
-			    log_as(as4), u16);
+			printf("%s %s:%hu", log_ext_subtype(subtype),
+			    log_as(ntohl(as4)), ntohs(u16));
 			break;
 		case EXT_COMMUNITY_OPAQUE:
 			memcpy(&ext, data + i, sizeof(ext));
 			ext = betoh64(ext) & 0xffffffffffffLL;
-			printf("%s 0x%llx", get_ext_subtype(subtype), ext); 
+			printf("%s 0x%llx", log_ext_subtype(subtype), ext);
 			break;
 		default:
 			memcpy(&ext, data + i, sizeof(ext));
-			printf("0x%llx", betoh64(ext)); 
+			printf("0x%llx", betoh64(ext));
 		}
 		if (i + 8 < len)
 			printf(", ");
diff --git a/bgpctl/irr_parser.c b/bgpctl/irr_parser.c
index 063faba..33e51e8 100644
--- a/bgpctl/irr_parser.c
+++ b/bgpctl/irr_parser.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: irr_parser.c,v 1.8 2007/03/05 22:34:08 henning Exp $ */
+/*	$OpenBSD: irr_parser.c,v 1.9 2009/09/08 15:40:25 claudio Exp $ */
 
 /*
  * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
@@ -81,6 +81,7 @@ parse_response(FILE *f, enum qtype qtype)
 				return (-1);
 			break;
 		case QTYPE_ROUTE:
+		case QTYPE_ROUTE6:
 			if ((n = parse_route(key, val)) == -1)
 				return (-1);
 			break;
@@ -281,7 +282,7 @@ parse_policy(char *key, char *val)
 				    !isdigit(tok[2]))
 					errx(1, "peering spec \"%s\": format "
 					    "error, AS expected", tok);
-				pi->peer_as = strtonum(tok + 2, 1, USHRT_MAX,
+				pi->peer_as = strtonum(tok + 2, 1, UINT_MAX,
 				    &errstr);
 				if (errstr)
 					errx(1, "peering spec \"%s\": format "
@@ -407,7 +408,8 @@ parse_asset(char *key, char *val)
 int
 parse_route(char *key, char *val)
 {
-	if (strcmp(key, "route"))	/* ignore everything else */
+	if (strcmp(key, "route") && strcmp(key, "route6"))
+		/* ignore everything else */
 		return (0);
 
 	/* route is single-value, but seen trailing , in the wild */
diff --git a/bgpctl/irr_prefix.c b/bgpctl/irr_prefix.c
index 8bc0d74..df85a6b 100644
--- a/bgpctl/irr_prefix.c
+++ b/bgpctl/irr_prefix.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: irr_prefix.c,v 1.15 2007/05/27 18:54:25 henning Exp $ */
+/*	$OpenBSD: irr_prefix.c,v 1.18 2010/05/10 02:00:50 krw Exp $ */
 
 /*
  * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
@@ -29,6 +29,7 @@
 #include <arpa/inet.h>
 
 #include "irrfilter.h"
+#include "bgpd.h"
 
 void	 prefixset_aggregate(struct prefix_set *);
 int	 prefix_aggregate(struct irr_prefix *, const struct irr_prefix *);
@@ -63,7 +64,11 @@ prefixset_get(char *as)
 		fflush(stdout);
 	}
 	curpfxs = pfxs;
-	if (whois(as, QTYPE_ROUTE) == -1)
+	if ((irrflags & F_IPV4) && whois(as, QTYPE_ROUTE) == -1)
+		errx(1, "whois error, prefixset_get %s", as);
+	if ((irrflags & F_IPV6) && whois(as, QTYPE_ROUTE6) == -1)
+		errx(1, "whois error, prefixset_get %s", as);
+	if (whois(as, QTYPE_ROUTE6) == -1)
 		errx(1, "whois error, prefixset_get %s", as);
 	curpfxs = NULL;
 	if (irrverbose >= 3)
@@ -80,9 +85,11 @@ prefixset_addmember(char *s)
 	void			*p;
 	u_int			 i;
 	struct irr_prefix	*pfx;
-	int			 len;
+	int			 len, ret;
+	char			*slash;
+	const char		*errstr;
 
-	if (strchr(s, '/') == NULL) {
+	if ((slash = strchr(s, '/')) == NULL) {
 		fprintf(stderr, "%s: prefix %s does not have the len "
 		    "specified, ignoring\n", curpfxs->as, s);
 		return (0);
@@ -92,17 +99,26 @@ prefixset_addmember(char *s)
 		err(1, "prefixset_addmember calloc");
 
 	if ((len = inet_net_pton(AF_INET, s, &pfx->addr.in,
-	    sizeof(pfx->addr.in))) == -1) {
-		if (errno == ENOENT) {
-			fprintf(stderr, "%s: prefix \"%s\": parse error\n",
+	    sizeof(pfx->addr.in))) != -1) {
+		pfx->af = AF_INET;
+	} else {
+		len = strtonum(slash + 1, 0, 128, &errstr);
+		if (errstr)
+			errx(1, "prefixset_addmember %s prefix %s: prefixlen "
+			    "is %s", curpfxs->as, s, errstr);
+		*slash = '\0';
+
+		if ((ret = inet_pton(AF_INET6, s, &pfx->addr.in6)) == -1)
+			err(1, "prefixset_addmember %s prefix \"%s\"",
 			    curpfxs->as, s);
+		else if (ret == 0) {
+			fprintf(stderr, "prefixset_addmember %s prefix \"%s\": "
+			    "No matching address family found", curpfxs->as, s);
+			free(pfx);
 			return (0);
-		} else
-			err(1, "prefixset_addmember %s inet_net_pton \"%s\"",
-			    curpfxs->as, s);
+		}
+		pfx->af = AF_INET6;
 	}
-
-	pfx->af = AF_INET;
 	pfx->len = pfx->maxlen = len;
 
 	/* yes, there are dupes... e. g. from multiple sources */
@@ -175,24 +191,47 @@ prefixset_aggregate(struct prefix_set *pfxs)
 int
 prefix_aggregate(struct irr_prefix *a, const struct irr_prefix *b)
 {
-	in_addr_t	mask;
+	in_addr_t	 mask;
+	struct in6_addr	 ma;
+	struct in6_addr	 mb;
 
 	if (a->len == 0)
 		return (1);
 
-	mask = htonl(0xffffffff << (32 - a->len));
+	if (a->af != b->af)
+		/* We cannot aggregate addresses of different families. */
+		return (0);
 
-	if ((a->addr.in.s_addr & mask) == (b->addr.in.s_addr & mask))
-		return (1);
+	if (a->af == AF_INET) {
+		mask = htonl(prefixlen2mask(a->len));
+		if ((a->addr.in.s_addr & mask) == (b->addr.in.s_addr & mask))
+			return (1);
+	} else if (a->af == AF_INET6) {
+		inet6applymask(&ma, &a->addr.in6, a->len);
+		inet6applymask(&mb, &b->addr.in6, a->len);
+		if (IN6_ARE_ADDR_EQUAL(&ma, &mb))
+			return (1);
+	}
 
-	/* see wether we can fold them in one */
+	/* see whether we can fold them in one */
 	if (a->len == b->len && a->len > 1) {
-		mask = htonl(0xffffffff << (32 - (a->len - 1)));
-		if ((a->addr.in.s_addr & mask) ==
-		    (b->addr.in.s_addr & mask)) {
-			a->len--;
-			a->addr.in.s_addr &= mask;
-			return (1);
+		if (a->af == AF_INET) {
+			mask = htonl(prefixlen2mask(a->len - 1));
+			if ((a->addr.in.s_addr & mask) ==
+			    (b->addr.in.s_addr & mask)) {
+				a->len--;
+				a->addr.in.s_addr &= mask;
+				return (1);
+			}
+		} else if (a->af == AF_INET6) {
+			inet6applymask(&ma, &a->addr.in6, a->len - 1);
+			inet6applymask(&mb, &b->addr.in6, a->len - 1);
+
+			if (IN6_ARE_ADDR_EQUAL(&ma, &mb)) {
+				a->len--;
+				memcpy(&a->addr.in6, &ma, sizeof(ma));
+				return (1);
+			}
 		}
 	}
 
@@ -219,6 +258,13 @@ irr_prefix_cmp(const void *a, const void *b)
 		if (ntohl(pa->addr.in.s_addr) >
 		    ntohl(pb->addr.in.s_addr))
 			return (1);
+	} else if (pa->af == AF_INET6) {
+		for (r = 0; r < 16; r++) {
+			if (pa->addr.in6.s6_addr[r] < pb->addr.in6.s6_addr[r])
+				return (-1);
+			if (pa->addr.in6.s6_addr[r] > pb->addr.in6.s6_addr[r])
+				return (1);
+		}
 	} else
 		errx(1, "irr_prefix_cmp unknown af %u", pa->af);
 
diff --git a/bgpctl/irrfilter.c b/bgpctl/irrfilter.c
index fb5b4f0..e731554 100644
--- a/bgpctl/irrfilter.c
+++ b/bgpctl/irrfilter.c
@@ -15,6 +15,9 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#if defined(__FreeBSD__)	/* compat */
+#include "openbsd-compat.h"
+#endif /* defined(__FreeBSD__) */
 
 #include <sys/types.h>
 #include <sys/param.h>
diff --git a/bgpctl/irrfilter.h b/bgpctl/irrfilter.h
index 5059240..9def255 100644
--- a/bgpctl/irrfilter.h
+++ b/bgpctl/irrfilter.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: irrfilter.h,v 1.7 2007/03/06 16:45:34 henning Exp $ */
+/*	$OpenBSD: irrfilter.h,v 1.9 2009/09/08 16:11:36 sthen Exp $ */
 
 /*
  * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
@@ -16,11 +16,17 @@
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#if defined(__FreeBSD__)	/* compat */
+#include "openbsd-compat.h"
+#endif /* defined(__FreeBSD__) */
+
 #include <sys/queue.h>
 #include <sys/tree.h>
 #include <netinet/in.h>
 
 #define	F_IMPORTONLY	0x01	/* skip export: items */
+#define	F_IPV4		0x02	/* use IPv4 items */
+#define	F_IPV6		0x04	/* use IPv6 items */
 
 int	irrflags;
 int	irrverbose;
@@ -37,7 +43,7 @@ struct policy_item {
 	char				*action;
 	char				*filter;
 	enum pdir			 dir;
-	u_int16_t			 peer_as;
+	u_int32_t			 peer_as;
 };
 
 TAILQ_HEAD(policy_head, policy_item);
@@ -55,7 +61,8 @@ enum qtype {
 	QTYPE_NONE,
 	QTYPE_OWNAS,
 	QTYPE_ASSET,
-	QTYPE_ROUTE
+	QTYPE_ROUTE,
+	QTYPE_ROUTE6
 };
 
 struct as_set {
@@ -72,6 +79,7 @@ struct as_set {
 struct irr_prefix {
 	union {
 		struct in_addr	in;
+		struct in6_addr	in6;
 	} addr;
 	sa_family_t	af;
 	u_int8_t	len;
diff --git a/bgpctl/parser.c b/bgpctl/parser.c
index bc6d078..4416f71 100644
--- a/bgpctl/parser.c
+++ b/bgpctl/parser.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parser.c,v 1.54 2009/06/12 16:44:02 claudio Exp $ */
+/*	$OpenBSD: parser.c,v 1.61 2010/03/08 17:02:19 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,6 +16,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#if defined(__FreeBSD__)        /* compat */
+#include "openbsd-compat.h"
+#endif /* defined(__FreeBSD__) */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 
@@ -52,7 +56,8 @@ enum token_type {
 	PREPSELF,
 	WEIGHT,
 	FAMILY,
-	GETOPT
+	GETOPT,
+	RTABLE
 };
 
 enum getopts {
@@ -97,6 +102,9 @@ static const struct token t_prepself[];
 static const struct token t_weight[];
 static const struct token t_irrfilter[];
 static const struct token t_irrfilter_opts[];
+static const struct token t_log[];
+static const struct token t_fib_table[];
+static const struct token t_show_fib_table[];
 
 static const struct token t_main[] = {
 	{ KEYWORD,	"reload",	RELOAD,		NULL},
@@ -105,6 +113,7 @@ static const struct token t_main[] = {
 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
 	{ KEYWORD,	"network",	NONE,		t_network},
 	{ KEYWORD,	"irrfilter",	IRRFILTER,	t_irrfilter},
+	{ KEYWORD,	"log",		NONE,		t_log},
 	{ ENDTOKEN,	"",		NONE,		NULL}
 };
 
@@ -116,6 +125,7 @@ static const struct token t_show[] = {
 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
+	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
 	{ ENDTOKEN,	"",		NONE,		NULL}
@@ -128,14 +138,15 @@ static const struct token t_show_summary[] = {
 };
 
 static const struct token t_show_fib[] = {
-	{ NOTOKEN,	"",		NONE,			NULL},
-	{ FLAG,		"connected",	F_CONNECTED,		t_show_fib},
-	{ FLAG,		"static",	F_STATIC,		t_show_fib},
-	{ FLAG,		"bgp",		F_BGPD_INSERTED,	t_show_fib},
-	{ FLAG,		"nexthop",	F_NEXTHOP,		t_show_fib},
-	{ FAMILY,	"",		NONE,			t_show_fib},
-	{ ADDRESS,	"",		NONE,			NULL},
-	{ ENDTOKEN,	"",		NONE,			NULL}
+	{ NOTOKEN,	"",		NONE,		 NULL},
+	{ FLAG,		"connected",	F_CONNECTED,	 t_show_fib},
+	{ FLAG,		"static",	F_STATIC,	 t_show_fib},
+	{ FLAG,		"bgp",		F_BGPD_INSERTED, t_show_fib},
+	{ FLAG,		"nexthop",	F_NEXTHOP,	 t_show_fib},
+	{ KEYWORD,	"table",	NONE,		 t_show_fib_table},
+	{ FAMILY,	"",		NONE,		 t_show_fib},
+	{ ADDRESS,	"",		NONE,		 NULL},
+	{ ENDTOKEN,	"",		NONE,		 NULL}
 };
 
 static const struct token t_show_rib[] = {
@@ -187,6 +198,7 @@ static const struct token t_show_neighbor_modifiers[] = {
 static const struct token t_fib[] = {
 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
+	{ KEYWORD,	"table",	NONE,		t_fib_table},
 	{ ENDTOKEN,	"",		NONE,		NULL}
 };
 
@@ -311,6 +323,22 @@ static const struct token t_irrfilter_opts[] = {
 	{ ENDTOKEN,	"",		NONE,			NULL}
 };
 
+static const struct token t_log[] = {
+	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
+	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
+	{ ENDTOKEN,	"",		NONE,		NULL}
+};
+
+static const struct token t_fib_table[] = {
+	{ RTABLE,	"",			NONE,	t_fib},
+	{ ENDTOKEN,	"",			NONE,	NULL}
+};
+
+static const struct token t_show_fib_table[] = {
+	{ RTABLE,	"",			NONE,	t_show_fib},
+	{ ENDTOKEN,	"",			NONE,	NULL}
+};
+
 static struct parse_result	res;
 
 const struct token	*match_token(int *argc, char **argv[],
@@ -404,15 +432,22 @@ match_token(int *argc, char **argv[], const struct token table[])
 		case FAMILY:
 			if (word == NULL)
 				break;
-			if (!strcmp(word, "inet") || !strcmp(word, "IPv4")) {
+			if (!strcmp(word, "inet") ||
+			    !strcasecmp(word, "IPv4")) {
+				match++;
+				t = &table[i];
+				res.aid = AID_INET;
+			}
+			if (!strcmp(word, "inet6") ||
+			    !strcasecmp(word, "IPv6")) {
 				match++;
 				t = &table[i];
-				res.af = AF_INET;
+				res.aid = AID_INET6;
 			}
-			if (!strcmp(word, "inet6") || !strcmp(word, "IPv6")) {
+			if (!strcasecmp(word, "VPNv4")) {
 				match++;
 				t = &table[i];
-				res.af = AF_INET6;
+				res.aid = AID_VPN_IPv4;
 			}
 			break;
 		case ADDRESS:
@@ -485,6 +520,7 @@ match_token(int *argc, char **argv[], const struct token table[])
 		case PREPNBR:
 		case PREPSELF:
 		case WEIGHT:
+		case RTABLE:
 			if (word != NULL && strlen(word) > 0 &&
 			    parse_number(word, &res, table[i].type)) {
 				match++;
@@ -577,6 +613,9 @@ show_valid_args(const struct token table[])
 		case WEIGHT:
 			fprintf(stderr, "  <number>\n");
 			break;
+		case RTABLE:
+			fprintf(stderr, "  <rtableid>\n");
+			break;
 		case NEXTHOP:
 			fprintf(stderr, "  <address>\n");
 			break;
@@ -584,7 +623,7 @@ show_valid_args(const struct token table[])
 			fprintf(stderr, "  <pftable>\n");
 			break;
 		case FAMILY:
-			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 ]\n");
+			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
 			break;
 		case GETOPT:
 			fprintf(stderr, "  <options>\n");
@@ -608,7 +647,7 @@ parse_addr(const char *word, struct bgpd_addr *addr)
 	bzero(&ina, sizeof(ina));
 
 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
-		addr->af = AF_INET;
+		addr->aid = AID_INET;
 		addr->v4 = ina;
 		return (1);
 	}
@@ -618,13 +657,7 @@ parse_addr(const char *word, struct bgpd_addr *addr)
 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
 	hints.ai_flags = AI_NUMERICHOST;
 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
-		addr->af = AF_INET6;
-		memcpy(&addr->v6,
-		    &((struct sockaddr_in6 *)r->ai_addr)->sin6_addr,
-		    sizeof(addr->v6));
-		addr->scope_id =
-		    ((struct sockaddr_in6 *)r->ai_addr)->sin6_scope_id;
-
+		sa2addr(r->ai_addr, addr);
 		freeaddrinfo(r);
 		return (1);
 	}
@@ -663,15 +696,15 @@ parse_prefix(const char *word, struct bgpd_addr *addr, u_int8_t *prefixlen)
 		if (parse_addr(word, addr) == 0)
 			return (0);
 
-	switch (addr->af) {
-	case AF_INET:
+	switch (addr->aid) {
+	case AID_INET:
 		if (mask == -1)
 			mask = 32;
 		if (mask > 32)
 			errx(1, "invalid netmask: too large");
 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		if (mask == -1)
 			mask = 128;
 		inet6applymask(&addr->v6, &addr->v6, mask);
@@ -706,7 +739,7 @@ parse_asnum(const char *word, u_int32_t *asnum)
 		if (errstr)
 			errx(1, "AS number is %s: %s", errstr, word);
 	} else {
-		uval = strtonum(word, 0, ASNUM_MAX - 1, &errstr);
+		uval = strtonum(word, 0, UINT_MAX, &errstr);
 		if (errstr)
 			errx(1, "AS number is %s: %s", errstr, word);
 	}
@@ -730,6 +763,11 @@ parse_number(const char *word, struct parse_result *r, enum token_type type)
 		errx(1, "number is %s: %s", errstr, word);
 
 	/* number was parseable */
+	if (type == RTABLE) {
+		r->rtableid = uval;
+		return (1);
+	}
+
 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
 		err(1, NULL);
 	switch (type) {
@@ -882,8 +920,14 @@ bgpctl_getopt(int *argc, char **argv[], int type)
 	int	  ch;
 
 	optind = optreset = 1;
-	while ((ch = getopt((*argc) + 1, (*argv) - 1, "o:")) != -1) {
+	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
 		switch (ch) {
+		case '4':
+			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
+			break;
+		case '6':
+			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
+			break;
 		case 'o':
 			res.irr_outdir = optarg;
 			break;
diff --git a/bgpctl/parser.h b/bgpctl/parser.h
index 8adb5b5..3b6ce75 100644
--- a/bgpctl/parser.h
+++ b/bgpctl/parser.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parser.h,v 1.19 2009/06/06 06:05:41 claudio Exp $ */
+/*	$OpenBSD: parser.h,v 1.22 2010/05/03 13:11:41 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -29,6 +29,7 @@ enum actions {
 	SHOW_NEIGHBOR_TIMERS,
 	SHOW_NEIGHBOR_TERSE,
 	SHOW_FIB,
+	SHOW_FIB_TABLES,
 	SHOW_RIB,
 	SHOW_RIB_MEM,
 	SHOW_NEXTHOP,
@@ -37,6 +38,8 @@ enum actions {
 	FIB,
 	FIB_COUPLE,
 	FIB_DECOUPLE,
+	LOG_VERBOSE,
+	LOG_BRIEF,
 	NEIGHBOR,
 	NEIGHBOR_UP,
 	NEIGHBOR_DOWN,
@@ -59,9 +62,10 @@ struct parse_result {
 	char			 rib[PEER_DESCR_LEN];
 	char			*irr_outdir;
 	int			 flags;
-	enum actions		action;
+	u_int			 rtableid;
+	enum actions		 action;
 	u_int8_t		 prefixlen;
-	sa_family_t		 af;
+	u_int8_t		 aid;
 };
 
 __dead void		 usage(void);
diff --git a/bgpctl/whois.c b/bgpctl/whois.c
index 839dcbb..f14d07d 100644
--- a/bgpctl/whois.c
+++ b/bgpctl/whois.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: whois.c,v 1.3 2007/03/05 16:43:24 henning Exp $ */
+/*	$OpenBSD: whois.c,v 1.4 2009/09/08 15:40:25 claudio Exp $ */
 
 /*
  * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
@@ -68,7 +68,8 @@ char *qtype_opts[] = {
 	"",
 	"-T aut-num",
 	"-K -T as-set",
-	"-K -T route -i origin"
+	"-K -T route -i origin",
+	"-K -T route6 -i origin"
 };
 
 char	*server = "whois.radb.net";
diff --git a/bgpd/Makefile b/bgpd/Makefile
index 739f5e2..adda69b 100644
--- a/bgpd/Makefile
+++ b/bgpd/Makefile
@@ -1,15 +1,25 @@
-#	$OpenBSD: Makefile,v 1.28 2009/06/25 14:14:54 deraadt Exp $
+#	$OpenBSD: Makefile,v 1.29 2010/05/26 16:44:32 nicm Exp $
+
+.PATH:		${.CURDIR}/.. ${.CURDIR}/../openbsd-compat
+
+CONFFILE?=	${PREFIX}/etc/bgpd.conf
 
 PROG=	bgpd
-SRCS=	bgpd.c buffer.c session.c log.c parse.y config.c imsg.c \
+SRCS=	bgpd.c session.c log.c parse.y config.c \
 	rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c \
-	control.c pfkey.c rde_update.c rde_attr.c printconf.c \
-	rde_filter.c pftable.c name2id.c util.c carp.c timer.c
+	control.c pfkey_compat.c rde_update.c rde_attr.c printconf.c \
+	rde_filter.c pftable.c name2id.c util.c carp.c timer.c \
+	imsg.c imsg-buffer.c
 CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/../openbsd-compat
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 CFLAGS+= -Wsign-compare
+CFLAGS+= -DCONFFILE=\"${CONFFILE}\"
+.if defined(IPV6_LINKLOCAL_PEER)
+CFLAGS+= -DIPV6_LINKLOCAL_PEER
+.endif
 YFLAGS=
 MAN= bgpd.8 bgpd.conf.5
 
diff --git a/bgpd/bgpd.8 b/bgpd/bgpd.8
index 295e2f9..e547db4 100644
--- a/bgpd/bgpd.8
+++ b/bgpd/bgpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.8,v 1.28 2009/01/13 23:01:36 sthen Exp $
+.\" $OpenBSD: bgpd.8,v 1.33 2009/12/16 15:40:55 claudio Exp $
 .\"
 .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: January 13 2009 $
+.Dd $Mdocdate: June 27 2010 $
 .Dt BGPD 8
 .Os
 .Sh NAME
@@ -24,12 +24,8 @@
 .Nm bgpd
 .Bk -words
 .Op Fl cdnv
-.Oo Xo
-.Fl D Ar macro Ns = Ns Ar value Oc
-.Xc
+.Op Fl D Ar macro Ns = Ns Ar value
 .Op Fl f Ar file
-.Op Fl r Ar path
-.Op Fl s Ar path
 .Ek
 .Sh DESCRIPTION
 .Nm
@@ -42,15 +38,15 @@ concerning
 with other BGP systems.
 .Nm
 uses the Border Gateway Protocol, Version 4,
-as described in RFC 1771.
+as described in RFC 4271.
 Please refer to that document for more information about BGP.
 .Pp
 .Nm
 is usually started at boot time, and can be enabled by
 setting the following in
-.Pa /etc/rc.conf.local :
+.Pa /etc/rc.conf :
 .Pp
-.Dl bgpd_flags=\&"\&"
+.Dl openbgpd_enable=\&"YES\&"
 .Pp
 See
 .Xr rc 8
@@ -117,25 +113,16 @@ Use
 .Ar file
 as the configuration file,
 instead of the default
-.Pa /etc/bgpd.conf .
+.Pa %%PREFIX%%/etc/bgpd.conf .
 .It Fl n
 Configtest mode.
 Only check the configuration file for validity.
-.It Fl r Ar path
-Open a second, restricted, control socket that
-.Xr bgpctl 8
-can use.
-Only
-.Em show
-requests are allowed on this socket.
-.It Fl s Ar path
-Use an alternate location for the default control socket.
 .It Fl v
 Produce more verbose output.
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/bgpd.sockXXX" -compact
-.It Pa /etc/bgpd.conf
+.It Pa %%PREFIX%%/etc/bgpd.conf
 default
 .Nm
 configuration file
@@ -150,9 +137,9 @@ control socket
 .Xr bgplg 8 ,
 .Xr bgplgsh 8
 .Rs
-.%R RFC 1771
+.%R RFC 4271
 .%T "A Border Gateway Protocol 4 (BGP-4)"
-.%D March 1995
+.%D January 2006
 .Re
 .Rs
 .%R RFC 1997
@@ -165,6 +152,11 @@ control socket
 .%D August 1998
 .Re
 .Rs
+.%R RFC 2545
+.%T "Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing"
+.%D March 1999
+.Re
+.Rs
 .%R RFC 2796
 .%T "BGP Route Reflection - An Alternative to Full Mesh IBGP"
 .%D April 2000
@@ -175,11 +167,6 @@ control socket
 .%D September 2000
 .Re
 .Rs
-.%R RFC 3392
-.%T "Capabilities Advertisement with BGP-4"
-.%D January 1999
-.Re
-.Rs
 .%R RFC 3682
 .%T "The Generalized TTL Security Mechanism (GTSM)"
 .%D February 2004
@@ -190,6 +177,21 @@ control socket
 .%D April 2004
 .Re
 .Rs
+.%R RFC 4360
+.%T "BGP Extended Communities Attribute"
+.%D February 2006
+.Re
+.Rs
+.%R RFC 4364
+.%T "BGP/MPLS IP Virtual Private Networks (VPNs)"
+.%D February 2006
+.Re
+.Rs
+.%R RFC 4486
+.%T "BGP Cease Notification Message Subcodes"
+.%D April 2006
+.Re
+.Rs
 .%R RFC 4760
 .%T "Multiprotocol Extensions for BGP-4"
 .%D January 2007
@@ -199,6 +201,21 @@ control socket
 .%T "BGP Support for Four-octet AS Number Space"
 .%D May 2007
 .Re
+.Rs
+.%R RFC 5492
+.%T "Capabilities Advertisement with BGP-4"
+.%D February 2009
+.Re
+.Rs
+.%R draft-ietf-idr-optional-transitive-00
+.%T "Error Handling for Optional Transitive BGP Attributes"
+.%D April 2009
+.Re
+.Rs
+.%R draft-ietf-idr-fsm-subcode-00
+.%T "Subcodes for BGP Finite State Machine Error"
+.%D September 2010
+.Re
 .Sh HISTORY
 The
 .Nm
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 43e284b..7a177fa 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: bgpd.c,v 1.148 2009/06/07 00:30:23 claudio Exp $ */
+/*	$OpenBSD: bgpd.c,v 1.167 2011/05/01 10:42:28 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -32,8 +32,8 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "mrt.h"
 #include "bgpd.h"
+#include "mrt.h"
 #include "session.h"
 
 void		sighdlr(int);
@@ -42,23 +42,22 @@ int		main(int, char *[]);
 int		check_child(pid_t, const char *);
 int		send_filterset(struct imsgbuf *, struct filter_set_head *);
 int		reconfigure(char *, struct bgpd_config *, struct mrt_head *,
-		    struct peer **, struct filter_head *);
+		    struct peer **);
 int		dispatch_imsg(struct imsgbuf *, int);
+int		control_setup(struct bgpd_config *);
 
 int			 rfd = -1;
-int			 cflags = 0;
-struct filter_set_head	*connectset;
-struct filter_set_head	*connectset6;
-struct filter_set_head	*staticset;
-struct filter_set_head	*staticset6;
-volatile sig_atomic_t	 mrtdump = 0;
-volatile sig_atomic_t	 quit = 0;
-volatile sig_atomic_t	 sigchld = 0;
-volatile sig_atomic_t	 reconfig = 0;
-pid_t			 reconfpid = 0;
+int			 cflags;
+volatile sig_atomic_t	 mrtdump;
+volatile sig_atomic_t	 quit;
+volatile sig_atomic_t	 sigchld;
+volatile sig_atomic_t	 reconfig;
+pid_t			 reconfpid;
 struct imsgbuf		*ibuf_se;
 struct imsgbuf		*ibuf_rde;
 struct rib_names	 ribnames = SIMPLEQ_HEAD_INITIALIZER(ribnames);
+char			*cname;
+char			*rcname;
 
 void
 sighdlr(int sig)
@@ -86,8 +85,8 @@ usage(void)
 {
 	extern char *__progname;
 
-	fprintf(stderr, "usage: %s [-cdnv] ", __progname);
-	fprintf(stderr, "[-D macro=value] [-f file] [-r path] [-s path]\n");
+	fprintf(stderr, "usage: %s [-cdnv] [-D macro=value] [-f file]\n",
+	    __progname);
 	exit(1);
 }
 
@@ -101,15 +100,10 @@ int
 main(int argc, char *argv[])
 {
 	struct bgpd_config	 conf;
-	struct peer		*peer_l, *p;
 	struct mrt_head		 mrt_l;
-	struct network_head	 net_l;
-	struct filter_head	*rules_l;
-	struct network		*net;
-	struct filter_rule	*r;
+	struct peer		*peer_l, *p;
 	struct mrt		*m;
 	struct listen_addr	*la;
-	struct rde_rib		*rr;
 	struct pollfd		 pfd[POLL_MAX];
 	pid_t			 io_pid = 0, rde_pid = 0, pid;
 	char			*conffile;
@@ -125,17 +119,11 @@ main(int argc, char *argv[])
 
 	log_init(1);		/* log to stderr until daemonized */
 
-	if ((rules_l = calloc(1, sizeof(struct filter_head))) == NULL)
-		err(1, NULL);
-
 	bzero(&conf, sizeof(conf));
 	LIST_INIT(&mrt_l);
-	TAILQ_INIT(&net_l);
-	TAILQ_INIT(rules_l);
 	peer_l = NULL;
-	conf.csock = SOCKET_NAME;
 
-	while ((ch = getopt(argc, argv, "cdD:f:nr:s:v")) != -1) {
+	while ((ch = getopt(argc, argv, "cdD:f:nv")) != -1) {
 		switch (ch) {
 		case 'c':
 			conf.opts |= BGPD_OPT_FORCE_DEMOTE;
@@ -158,12 +146,7 @@ main(int argc, char *argv[])
 			if (conf.opts & BGPD_OPT_VERBOSE)
 				conf.opts |= BGPD_OPT_VERBOSE2;
 			conf.opts |= BGPD_OPT_VERBOSE;
-			break;
-		case 'r':
-			conf.rcsock = optarg;
-			break;
-		case 's':
-			conf.csock = optarg;
+			log_verbose(1);
 			break;
 		default:
 			usage();
@@ -176,24 +159,22 @@ main(int argc, char *argv[])
 	if (argc > 0)
 		usage();
 
-	if (parse_config(conffile, &conf, &mrt_l, &peer_l, &net_l, rules_l)) {
-		free(rules_l);
-		exit(1);
-	}
-
 	if (conf.opts & BGPD_OPT_NOACTION) {
+		struct network_head	net_l;
+		struct rdomain_head	rdom_l;
+		struct filter_head	rules_l;
+
+		if (parse_config(conffile, &conf, &mrt_l, &peer_l, &net_l,
+		    &rules_l, &rdom_l))
+			exit(1);
+
 		if (conf.opts & BGPD_OPT_VERBOSE)
-			print_config(&conf, &ribnames, &net_l, peer_l, rules_l,
-			    &mrt_l);
+			print_config(&conf, &ribnames, &net_l, peer_l, &rules_l,
+			    &mrt_l, &rdom_l);
 		else
 			fprintf(stderr, "configuration OK\n");
 		exit(0);
 	}
-	cflags = conf.flags;
-	connectset = &conf.connectset;
-	staticset = &conf.staticset;
-	connectset6 = &conf.connectset6;
-	staticset6 = &conf.staticset6;
 
 	if (geteuid())
 		errx(1, "need root privileges");
@@ -225,13 +206,9 @@ main(int argc, char *argv[])
 	session_socket_blockmode(pipe_s2r_c[0], BM_NONBLOCK);
 	session_socket_blockmode(pipe_s2r_c[1], BM_NONBLOCK);
 
-	prepare_listeners(&conf);
-
 	/* fork children */
-	rde_pid = rde_main(&conf, peer_l, &net_l, rules_l, &mrt_l, &ribnames,
-	    pipe_m2r, pipe_s2r, pipe_m2s, pipe_s2r_c, debug);
-	io_pid = session_main(&conf, peer_l, &net_l, rules_l, &mrt_l, &ribnames,
-	    pipe_m2s, pipe_s2r, pipe_m2r, pipe_s2r_c);
+	rde_pid = rde_main(pipe_m2r, pipe_s2r, pipe_m2s, pipe_s2r_c, debug);
+	io_pid = session_main(pipe_m2s, pipe_s2r, pipe_m2r, pipe_s2r_c);
 
 	setproctitle("parent");
 
@@ -254,33 +231,12 @@ main(int argc, char *argv[])
 	imsg_init(ibuf_se, pipe_m2s[0]);
 	imsg_init(ibuf_rde, pipe_m2r[0]);
 	mrt_init(ibuf_rde, ibuf_se);
-	if ((rfd = kr_init(!(conf.flags & BGPD_FLAG_NO_FIB_UPDATE),
-	    conf.rtableid)) == -1)
+	if ((rfd = kr_init()) == -1)
 		quit = 1;
+	quit = reconfigure(conffile, &conf, &mrt_l, &peer_l);
 	if (pftable_clear_all() != 0)
 		quit = 1;
 
-	while ((net = TAILQ_FIRST(&net_l)) != NULL) {
-		TAILQ_REMOVE(&net_l, net, entry);
-		filterset_free(&net->net.attrset);
-		free(net);
-	}
-
-	while ((r = TAILQ_FIRST(rules_l)) != NULL) {
-		TAILQ_REMOVE(rules_l, r, entry);
-		free(r);
-	}
-	TAILQ_FOREACH(la, conf.listen_addrs, entry) {
-		close(la->fd);
-		la->fd = -1;
-	}
-	while ((rr = SIMPLEQ_FIRST(&ribnames))) {
-		SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
-		free(rr);
-	}
-
-	mrt_reconfigure(&mrt_l);
-
 	while (quit == 0) {
 		bzero(pfd, sizeof(pfd));
 		pfd[PFD_PIPE_SESSION].fd = ibuf_se->fd;
@@ -336,8 +292,7 @@ main(int argc, char *argv[])
 
 			reconfig = 0;
 			log_info("rereading config");
-			switch (reconfigure(conffile, &conf, &mrt_l, &peer_l,
-			    rules_l)) {
+			switch (reconfigure(conffile, &conf, &mrt_l, &peer_l)) {
 			case -1:	/* fatal error */
 				quit = 1;
 				break;
@@ -389,13 +344,13 @@ main(int argc, char *argv[])
 		LIST_REMOVE(m, entry);
 		free(m);
 	}
-	while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) {
-		TAILQ_REMOVE(conf.listen_addrs, la, entry);
-		close(la->fd);
-		free(la);
-	}
+	if (conf.listen_addrs)
+		while ((la = TAILQ_FIRST(conf.listen_addrs)) != NULL) {
+			TAILQ_REMOVE(conf.listen_addrs, la, entry);
+			close(la->fd);
+			free(la);
+		}
 
-	free(rules_l);
 	control_cleanup(conf.csock);
 	control_cleanup(conf.rcsock);
 	carp_demote_shutdown();
@@ -413,6 +368,8 @@ main(int argc, char *argv[])
 	free(ibuf_se);
 	msgbuf_clear(&ibuf_rde->w);
 	free(ibuf_rde);
+	free(rcname);
+	free(cname);
 
 	log_info("Terminating");
 	return (0);
@@ -452,27 +409,25 @@ send_filterset(struct imsgbuf *i, struct filter_set_head *set)
 
 int
 reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l,
-    struct peer **peer_l, struct filter_head *rules_l)
+    struct peer **peer_l)
 {
 	struct network_head	 net_l;
-	struct network		*n;
+	struct rdomain_head	 rdom_l;
+	struct filter_head	 rules_l;
 	struct peer		*p;
 	struct filter_rule	*r;
 	struct listen_addr	*la;
 	struct rde_rib		*rr;
+	struct rdomain		*rd;
 
-	if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, rules_l)) {
+	if (parse_config(conffile, conf, mrt_l, peer_l, &net_l, &rules_l,
+	    &rdom_l)) {
 		log_warnx("config file %s has errors, not reloading",
 		    conffile);
 		return (1);
 	}
 
 	cflags = conf->flags;
-	connectset = &conf->connectset;
-	staticset = &conf->staticset;
-	connectset6 = &conf->connectset6;
-	staticset6 = &conf->staticset6;
-
 	prepare_listeners(conf);
 
 	/* start reconfiguration */
@@ -483,12 +438,6 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l,
 	    conf, sizeof(struct bgpd_config)) == -1)
 		return (-1);
 
-	/* send peer list and listeners to the SE */
-	for (p = *peer_l; p != NULL; p = p->next)
-		if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1,
-		    &p->conf, sizeof(struct peer_config)) == -1)
-			return (-1);
-
 	TAILQ_FOREACH(la, conf->listen_addrs, entry) {
 		if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd,
 		    la, sizeof(struct listen_addr)) == -1)
@@ -496,51 +445,104 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct mrt_head *mrt_l,
 		la->fd = -1;
 	}
 
+	if (control_setup(conf) == -1)
+		return (-1);
+
+	/* adjust fib syncing on reload */
+	ktable_preload();
+
 	/* RIBs for the RDE */
 	while ((rr = SIMPLEQ_FIRST(&ribnames))) {
 		SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
+		if (ktable_update(rr->rtableid, rr->name, NULL,
+		    rr->flags) == -1) {
+			log_warnx("failed to load rdomain %d",
+			    rr->rtableid);
+			return (-1);
+		}
 		if (imsg_compose(ibuf_rde, IMSG_RECONF_RIB, 0, 0, -1,
 		    rr, sizeof(struct rde_rib)) == -1)
 			return (-1);
 		free(rr);
 	}
 
-	/* networks for the RDE */
-	while ((n = TAILQ_FIRST(&net_l)) != NULL) {
-		if (imsg_compose(ibuf_rde, IMSG_NETWORK_ADD, 0, 0, -1,
-		    &n->net, sizeof(struct network_config)) == -1)
-			return (-1);
-		if (send_filterset(ibuf_rde, &n->net.attrset) == -1)
+	/* send peer list and listeners to the SE and RDE */
+	for (p = *peer_l; p != NULL; p = p->next) {
+		if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1,
+		    &p->conf, sizeof(struct peer_config)) == -1)
 			return (-1);
-		if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1,
-		    NULL, 0) == -1)
+		if (imsg_compose(ibuf_rde, IMSG_RECONF_PEER, p->conf.id, 0, -1,
+		    &p->conf, sizeof(struct peer_config)) == -1)
 			return (-1);
-		TAILQ_REMOVE(&net_l, n, entry);
-		filterset_free(&n->net.attrset);
-		free(n);
 	}
 
-	/* redistribute list needs to be reloaded too */
-	if (kr_reload() == -1)
+	/* networks go via kroute to the RDE */
+	if (kr_net_reload(0, &net_l))
 		return (-1);
 
 	/* filters for the RDE */
-	while ((r = TAILQ_FIRST(rules_l)) != NULL) {
+	while ((r = TAILQ_FIRST(&rules_l)) != NULL) {
+		TAILQ_REMOVE(&rules_l, r, entry);
 		if (imsg_compose(ibuf_rde, IMSG_RECONF_FILTER, 0, 0, -1,
 		    r, sizeof(struct filter_rule)) == -1)
 			return (-1);
 		if (send_filterset(ibuf_rde, &r->set) == -1)
 			return (-1);
-		TAILQ_REMOVE(rules_l, r, entry);
 		filterset_free(&r->set);
 		free(r);
 	}
 
+	while ((rd = SIMPLEQ_FIRST(&rdom_l)) != NULL) {
+		SIMPLEQ_REMOVE_HEAD(&rdom_l, entry);
+		if (ktable_update(rd->rtableid, rd->descr, rd->ifmpe,
+		    rd->flags) == -1) {
+			log_warnx("failed to load rdomain %d",
+			    rd->rtableid);
+			return (-1);
+		}
+		/* networks go via kroute to the RDE */
+		if (kr_net_reload(rd->rtableid, &rd->net_l))
+			return (-1);
+
+		if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN, 0, 0, -1,
+		    rd, sizeof(*rd)) == -1)
+			return (-1);
+
+		/* export targets */
+		if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_EXPORT, 0, 0,
+		    -1, NULL, 0) == -1)
+			return (-1);
+		if (send_filterset(ibuf_rde, &rd->export) == -1)
+			return (-1);
+		filterset_free(&rd->export);
+
+		/* import targets */
+		if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_IMPORT, 0, 0,
+		    -1, NULL, 0) == -1)
+			return (-1);
+		if (send_filterset(ibuf_rde, &rd->import) == -1)
+			return (-1);
+		filterset_free(&rd->import);
+
+		if (imsg_compose(ibuf_rde, IMSG_RECONF_RDOMAIN_DONE, 0, 0,
+		    -1, NULL, 0) == -1)
+			return (-1);
+
+		free(rd);
+	}
+
 	/* signal both childs to replace their config */
 	if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1 ||
 	    imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1)
 		return (-1);
 
+	/* fix kroute information */
+	ktable_postload();
+
+	/* redistribute list needs to be reloaded too */
+	if (kr_reload() == -1)
+		return (-1);
+
 	/* mrt changes can be sent out of bound */
 	mrt_reconfigure(mrt_l);
 	return (0);
@@ -550,8 +552,8 @@ int
 dispatch_imsg(struct imsgbuf *ibuf, int idx)
 {
 	struct imsg		 imsg;
-	int			 n;
-	int			 rv;
+	ssize_t			 n;
+	int			 rv, verbose;
 
 	if ((n = imsg_read(ibuf)) == -1)
 		return (-1);
@@ -573,46 +575,39 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx)
 		case IMSG_KROUTE_CHANGE:
 			if (idx != PFD_PIPE_ROUTE)
 				log_warnx("route request not from RDE");
-			else if (kr_change(imsg.data))
+			else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(struct kroute_full))
+				log_warnx("wrong imsg len");
+			else if (kr_change(imsg.hdr.peerid, imsg.data))
 				rv = -1;
 			break;
 		case IMSG_KROUTE_DELETE:
 			if (idx != PFD_PIPE_ROUTE)
 				log_warnx("route request not from RDE");
-			else if (kr_delete(imsg.data))
-				rv = -1;
-			break;
-		case IMSG_KROUTE6_CHANGE:
-			if (idx != PFD_PIPE_ROUTE)
-				log_warnx("route request not from RDE");
-			else if (kr6_change(imsg.data))
-				rv = -1;
-			break;
-		case IMSG_KROUTE6_DELETE:
-			if (idx != PFD_PIPE_ROUTE)
-				log_warnx("route request not from RDE");
-			else if (kr6_delete(imsg.data))
+			else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(struct kroute_full))
+				log_warnx("wrong imsg len");
+			else if (kr_delete(imsg.hdr.peerid, imsg.data))
 				rv = -1;
 			break;
 		case IMSG_NEXTHOP_ADD:
 			if (idx != PFD_PIPE_ROUTE)
 				log_warnx("nexthop request not from RDE");
-			else
-				if (imsg.hdr.len != IMSG_HEADER_SIZE +
-				    sizeof(struct bgpd_addr))
-					log_warnx("wrong imsg len");
-				else if (kr_nexthop_add(imsg.data) == -1)
-					rv = -1;
+			else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(struct bgpd_addr))
+				log_warnx("wrong imsg len");
+			else if (kr_nexthop_add(imsg.hdr.peerid, imsg.data) ==
+			    -1)
+				rv = -1;
 			break;
 		case IMSG_NEXTHOP_REMOVE:
 			if (idx != PFD_PIPE_ROUTE)
 				log_warnx("nexthop request not from RDE");
+			else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(struct bgpd_addr))
+				log_warnx("wrong imsg len");
 			else
-				if (imsg.hdr.len != IMSG_HEADER_SIZE +
-				    sizeof(struct bgpd_addr))
-					log_warnx("wrong imsg len");
-				else
-					kr_nexthop_delete(imsg.data);
+				kr_nexthop_delete(imsg.hdr.peerid, imsg.data);
 			break;
 		case IMSG_PFTABLE_ADD:
 			if (idx != PFD_PIPE_ROUTE)
@@ -654,18 +649,19 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx)
 			if (idx != PFD_PIPE_SESSION)
 				log_warnx("couple request not from SE");
 			else
-				kr_fib_couple();
+				kr_fib_couple(imsg.hdr.peerid);
 			break;
 		case IMSG_CTL_FIB_DECOUPLE:
 			if (idx != PFD_PIPE_SESSION)
 				log_warnx("decouple request not from SE");
 			else
-				kr_fib_decouple();
+				kr_fib_decouple(imsg.hdr.peerid);
 			break;
 		case IMSG_CTL_KROUTE:
 		case IMSG_CTL_KROUTE_ADDR:
 		case IMSG_CTL_SHOW_NEXTHOP:
 		case IMSG_CTL_SHOW_INTERFACE:
+		case IMSG_CTL_SHOW_FIB_TABLES:
 			if (idx != PFD_PIPE_SESSION)
 				log_warnx("kroute request not from SE");
 			else
@@ -692,6 +688,11 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx)
 				carp_demote_set(msg->demote_group, msg->level);
 			}
 			break;
+		case IMSG_CTL_LOG_VERBOSE:
+			/* already checked by SE */
+			memcpy(&verbose, imsg.data, sizeof(verbose));
+			log_verbose(verbose);
+			break;
 		default:
 			break;
 		}
@@ -707,7 +708,7 @@ send_nexthop_update(struct kroute_nexthop *msg)
 {
 	char	*gw = NULL;
 
-	if (msg->gateway.af)
+	if (msg->gateway.aid)
 		if (asprintf(&gw, ": via %s",
 		    log_addr(&msg->gateway)) == -1) {
 			log_warn("send_nexthop_update");
@@ -717,7 +718,7 @@ send_nexthop_update(struct kroute_nexthop *msg)
 	log_info("nexthop %s now %s%s%s", log_addr(&msg->nexthop),
 	    msg->valid ? "valid" : "invalid",
 	    msg->connected ? ": directly connected" : "",
-	    msg->gateway.af ? gw : "");
+	    msg->gateway.aid ? gw : "");
 
 	free(gw);
 
@@ -733,56 +734,20 @@ send_imsg_session(int type, pid_t pid, void *data, u_int16_t datalen)
 }
 
 int
-bgpd_redistribute(int type, struct kroute *kr, struct kroute6 *kr6)
+send_network(int type, struct network_config *net, struct filter_set_head *h)
 {
-	struct network_config	 net;
-	struct filter_set_head	*h;
-
-	if ((cflags & BGPD_FLAG_REDIST_CONNECTED) && kr &&
-	    (kr->flags & F_CONNECTED))
-		h = connectset;
-	else if ((cflags & BGPD_FLAG_REDIST_STATIC) && kr &&
-	    (kr->flags & F_STATIC))
-		h = staticset;
-	else if ((cflags & BGPD_FLAG_REDIST6_CONNECTED) && kr6 &&
-	    (kr6->flags & F_CONNECTED))
-		h = connectset6;
-	else if ((cflags & BGPD_FLAG_REDIST6_STATIC) && kr6 &&
-	    (kr6->flags & F_STATIC))
-		h = staticset6;
-	else
-		return (0);
-
-	bzero(&net, sizeof(net));
-	if (kr && kr6)
-		fatalx("bgpd_redistribute: unable to redistribute v4 and v6"
-		    "together");
-	if (kr != NULL) {
-		net.prefix.af = AF_INET;
-		net.prefix.v4.s_addr = kr->prefix.s_addr;
-		net.prefixlen = kr->prefixlen;
-	}
-	if (kr6 != NULL) {
-		net.prefix.af = AF_INET6;
-		memcpy(&net.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
-		net.prefixlen = kr6->prefixlen;
-	}
-
-
-	if (imsg_compose(ibuf_rde, type, 0, 0, -1, &net,
+	if (imsg_compose(ibuf_rde, type, 0, 0, -1, net,
 	    sizeof(struct network_config)) == -1)
 		return (-1);
-
 	/* networks that get deleted don't need to send the filter set */
 	if (type == IMSG_NETWORK_REMOVE)
-		return (1);
-
+		return (0);
 	if (send_filterset(ibuf_rde, h) == -1)
 		return (-1);
 	if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0) == -1)
 		return (-1);
 
-	return (1);
+	return (0);
 }
 
 int
@@ -810,3 +775,45 @@ bgpd_filternexthop(struct kroute *kr, struct kroute6 *kr6)
 
 	return (1);
 }
+
+int
+control_setup(struct bgpd_config *conf)
+{
+	int fd, restricted;
+
+	/* control socket is outside chroot */
+	if (!cname || strcmp(cname, conf->csock)) {
+		if (cname) {
+			control_cleanup(cname);
+			free(cname);
+		}
+		if ((cname = strdup(conf->csock)) == NULL)
+			fatal("strdup");
+		if ((fd = control_init(0, cname)) == -1)
+			fatalx("control socket setup failed");
+		restricted = 0;
+		if (imsg_compose(ibuf_se, IMSG_RECONF_CTRL, 0, 0, fd,
+		    &restricted, sizeof(restricted)) == -1)
+			return (-1);
+	}
+	if (!conf->rcsock) {
+		/* remove restricted socket */
+		control_cleanup(rcname);
+		free(rcname);
+		rcname = NULL;
+	} else if (!rcname || strcmp(rcname, conf->rcsock)) {
+		if (rcname) {
+			control_cleanup(rcname);
+			free(rcname);
+		}
+		if ((rcname = strdup(conf->rcsock)) == NULL)
+			fatal("strdup");
+		if ((fd = control_init(1, rcname)) == -1)
+			fatalx("control socket setup failed");
+		restricted = 1;
+		if (imsg_compose(ibuf_se, IMSG_RECONF_CTRL, 0, 0, fd,
+		    &restricted, sizeof(restricted)) == -1)
+			return (-1);
+	}
+	return (0);
+}
diff --git a/bgpd/bgpd.conf.5 b/bgpd/bgpd.conf.5
index 484e041..f9df578 100644
--- a/bgpd/bgpd.conf.5
+++ b/bgpd/bgpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.conf.5,v 1.94 2009/06/07 00:31:22 claudio Exp $
+.\" $OpenBSD: bgpd.conf.5,v 1.104 2010/03/05 15:25:00 claudio Exp $
 .\"
 .\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
 .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,7 +16,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: June 7 2009 $
+.Dd $Mdocdate: October 23 2010 $
 .Dt BGPD.CONF 5
 .Os
 .Sh NAME
@@ -26,7 +26,7 @@
 The
 .Xr bgpd 8
 daemon implements the Border Gateway Protocol version 4 as described
-in RFC 1771.
+in RFC 4271.
 .Sh SECTIONS
 The
 .Nm
@@ -38,6 +38,8 @@ configuration file.
 .It Sy Global Configuration
 Global settings for
 .Xr bgpd 8 .
+.It Sy Routing Domain Configuration
+The definition and properties for BGP MPLS VPNs are set in this section.
 .It Sy Neighbors and Groups
 .Xr bgpd 8
 establishes sessions with
@@ -93,7 +95,7 @@ Set the local
 .Em autonomous system
 number to
 .Ar as-number .
-If the first AS number is a 4-byte AS it is possible to specifiy a secondary
+If the first AS number is a 4-byte AS it is possible to specify a secondary
 2-byte AS number which is used for neighbors which do not support 4-byte AS
 numbers.
 The default for the secondary AS is 23456.
@@ -143,13 +145,13 @@ The default is 120 seconds.
 .It Xo
 .Ic dump
 .Op Ic rib Ar name
-.Pq Ic table Ns \&| Ns Ic table-mp
+.Pq Ic table Ns | Ns Ic table-mp
 .Ar file Op Ar timeout
 .Xc
 .It Xo
 .Ic dump
-.Pq Ic all Ns \&| Ns Ic updates
-.Pq Ic in Ns \&| Ns Ic out
+.Pq Ic all Ns | Ns Ic updates
+.Pq Ic in Ns | Ns Ic out
 .Ar file Op Ar timeout
 .Xc
 Dump the RIB, a.k.a. the
@@ -195,7 +197,7 @@ dump updates out "/tmp/updates-out-%H%M" 300
 .Pp
 .It Xo
 .Ic fib-update
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic no ,
@@ -242,12 +244,12 @@ Log received and sent updates.
 .Xc
 .It Xo
 .Ic network
-.Pq Ic inet Ns \&| Ns Ic inet6
+.Pq Ic inet Ns | Ns Ic inet6
 .Ic static Op Ic set ...\&
 .Xc
 .It Xo
 .Ic network
-.Pq Ic inet Ns \&| Ns Ic inet6
+.Pq Ic inet Ns | Ns Ic inet6
 .Ic connected Op Ic set ...\&
 .Xc
 Announce the specified network as belonging to our AS.
@@ -278,7 +280,7 @@ section.
 .Ic nexthop
 .Ic qualify
 .Ic via
-.Pq Ic bgp Ns \&| Ns Ic default
+.Pq Ic bgp Ns | Ns Ic default
 .Xc
 If set to
 .Ic bgp ,
@@ -295,7 +297,7 @@ daemons like
 .Ic rde
 .Ic med
 .Ic compare
-.Pq Ic always Ns \&| Ns Ic strict
+.Pq Ic always Ns | Ns Ic strict
 .Xc
 If set to
 .Ic always ,
@@ -313,20 +315,31 @@ is only compared between peers belonging to the same AS.
 .Ic rib Ar name
 .Op Ic no Ic evaluate
 .Xc
-Creat an additional RIB named
+.It Xo
+.Ic rde
+.Ic rib Ar name
+.Op Ic rtable Ar number
+.Xc
+Create an additional RIB named
 .Ar name .
 It is possible to disable the decision process per RIB with the
 .Ic no Ic evaluate
 flag.
+If a
+.Ic rtable
+is specified, routes will be exported to the given kernel routing table.
+Currently the routing table must belong to the default routing domain and
+nexthop verification happens on table 0.
+Routes in the specified table will not be considered for nexthop verification.
 .Ic Adj-RIB-In
 and
 .Ic Loc-RIB
-are created automaticaly and used as default.
+are created automatically and used as default.
 .Pp
 .It Xo
 .Ic rde
 .Ic route-age
-.Pq Ic ignore Ns \&| Ns Ic evaluate
+.Pq Ic ignore Ns | Ns Ic evaluate
 .Xc
 If set to
 .Ic evaluate ,
@@ -339,7 +352,7 @@ The default is
 .Pp
 .It Xo
 .Ic route-collector
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic yes ,
@@ -361,13 +374,24 @@ to the local machine.
 Work with the given kernel routing table
 instead of the default table,
 .Ar 0 .
-Note that this table is used for nexthop verification as well.
-Directly connected networks are always taken into account, even though
-their routes live in table 0.
+Note that table 0 is used for nexthop verification.
+Routes in the specified table will not be considered for nexthop verification.
+This is the same as using the following syntax:
+.Bd -literal -offset indent
+rde rib Loc-RIB rtable number
+.Ed
+.Pp
+.It Ic socket Qo Ar path Qc Op Ic restricted
+Set the control socket location to
+.Ar path .
+If
+.Ic restricted
+is specified a restricted control socket will be created.
+By default /var/run/bgpd.sock is used and no restricted socket is created.
 .Pp
 .It Xo
 .Ic transparent-as
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic yes ,
@@ -376,6 +400,111 @@ to EBGP neighbors are not prepended with their own AS.
 The default is
 .Ic no .
 .El
+.Sh ROUTING DOMAIN CONFIGURATION
+.Xr bgpd 8
+supports the setup and distribution of Virtual Private Networks.
+It is possible to import and export prefixes between routing domains.
+Each routing domain is specified by an
+.Ic rdomain
+section, which allows properties to be set specifically for that rdomain:
+.Bd -literal -offset indent
+rdomain 1 {
+	descr "a rdomain"
+	rd 65002:1
+	import-target rt 65002:42
+	export-target rt 65002:42
+	network 192.168.1/24
+	depend on mpe0
+}
+.Ed
+.Pp
+There are several routing domain properties:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic depend on Ar interface
+Routes added to the rdomain will use this interface as the outgoing interface.
+Normally this will be an MPLS Provider Edge,
+.Xr mpe 4 ,
+interface that is part of the rdomain.
+Local networks will be announced with the MPLS label specified on the interface.
+.Pp
+.It Ic descr Ar description
+Add a description.
+The description is used when logging but has no further meaning to
+.Xr bgpd 8 .
+.Pp
+.It Ic export-target Ar subtype Ar as-number Ns Li : Ns Ar local
+.It Ic export-target Ar subtype Ar IP Ns Li : Ns Ar local
+Specify an extended community which will be attached to announced networks.
+More than one
+.Ic export-target
+can be specified.
+See also the
+.Sx ATTRIBUTE SET
+section for further information about the encoding.
+The
+.Ar subtype
+should be set to
+.Ar rt
+for best compatibility with other implementations.
+.Pp
+.It Xo
+.Ic fib-update
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic no ,
+do not update the Forwarding Information Base, a.k.a. the kernel
+routing table.
+The default is
+.Ic yes .
+.Pp
+.It Ic import-target Ar subtype Ar as-number Ns Li : Ns Ar local
+.It Ic import-target Ar subtype Ar IP Ns Li : Ns Ar local
+Only prefixes matching one of the specified
+.Ic import-targets
+will be imported into the rdomain.
+More than one
+.Ic import-target
+can be specified.
+See also the
+.Sx ATTRIBUTE SET
+section for further information about the encoding of extended communities.
+The
+.Ar subtype
+should be set to
+.Ar rt
+for best compatibility with other implementations.
+.Pp
+.It Ic network Ar arguments ...
+Define which networks should be exported into this VPN.
+See also the
+.Ic nexthop
+section in
+.Sx GLOBAL CONFIGURATION
+for further information about the arguments.
+.Pp
+.It Ic rd Ar as-number Ns Li : Ns Ar local
+.It Ic rd Ar IP Ns Li : Ns Ar local
+The Route Distinguishers uniquely identifies a set of VPN prefixes.
+Only prefixes matching the
+.Ic rd
+will be imported into the routing domain.
+The purpose of the
+.Ic rd
+is solely to allow one to create distinct routes to a common address prefix.
+The
+.Ar as-number
+or
+.Ar IP
+of a
+.Ic rd
+should be set to a number or IP that was assigned by an appropriate authority.
+Whereas
+.Ar local
+can be chosen by the local operator.
+.Pp
+.El
 .Sh NEIGHBORS AND GROUPS
 .Xr bgpd 8
 establishes TCP connections to other BGP speakers called
@@ -470,21 +599,35 @@ The default for IBGP peers is
 .Pp
 .It Xo
 .Ic announce
-.Pq Ic IPv4 Ns \&| Ns Ic IPv6
-.Pq Ic none Ns \&| Ns Ic unicast
+.Pq Ic IPv4 Ns | Ns Ic IPv6
+.Pq Ic none Ns | Ns Ic unicast Ns | Ns Ic vpn
 .Xc
 For the given address family, control which subsequent address families
 (at the moment, only
 .Em none ,
-which disables the announcement of that address family, and
-.Em unicast
-are supported) are announced during the capabilities negotiation.
+which disables the announcement of that address family,
+.Em unicast ,
+and
+.Em vpn ,
+which allows the distribution of BGP MPLS VPNs, are supported) are announced
+during the capabilities negotiation.
 Only routes for that address family and subsequent address family will be
 announced and processed.
 .Pp
 .It Xo
+.Ic announce as-4byte
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic no ,
+the 4-byte AS capability is not announced and so native 4-byte AS support is
+disabled.
+The default is
+.Ic yes .
+.Pp
+.It Xo
 .Ic announce capabilities
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic no ,
@@ -493,6 +636,29 @@ This can be helpful to connect to old or broken BGP implementations.
 The default is
 .Ic yes .
 .Pp
+.It Xo
+.Ic announce refresh
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic no ,
+the route refresh capability is not announced.
+The default is
+.Ic yes .
+.Pp
+.It Xo
+.Ic announce restart
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+the graceful restart capability is announced.
+Currently only the End-of-RIB marker is supported and announced by the
+.Ic restart
+capability.
+The default is
+.Ic no .
+.Pp
 .It Ic demote Ar group
 Increase the
 .Xr carp 4
@@ -504,7 +670,7 @@ The demotion counter will be increased as soon as
 .Xr bgpd 8
 starts and decreased
 60 seconds after the session went to state
-.Em ESTABLISHED.
+.Em ESTABLISHED .
 For neighbors added at runtime, the demotion counter is only increased after
 the session has been
 .Em ESTABLISHED
@@ -548,8 +714,8 @@ Do not start the session when bgpd comes up but stay in
 .Pp
 .It Xo
 .Ic dump
-.Pq Ic all Ns \&| Ns Ic updates
-.Pq Ic in Ns \&| Ns Ic out
+.Pq Ic all Ns | Ns Ic updates
+.Pq Ic in Ns | Ns Ic out
 .Ar file Op Ar timeout
 .Xc
 Do a peer specific MRT dump.
@@ -564,7 +730,7 @@ section in
 .Pp
 .It Xo
 .Ic enforce neighbor-as
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic yes ,
@@ -589,10 +755,16 @@ Inherited from the global configuration if not given.
 Set the minimal acceptable holdtime.
 Inherited from the global configuration if not given.
 .Pp
+.It Ic interface Ar interface
+Set an interface used for a nexthop with a link-local IPv6 address.
+Note that if this is not specified and a link-local IPv6 address is
+received as nexthop of the peer, it will be marked as invalid and
+ignored.
+.Pp
 .It Xo
 .Ic ipsec
-.Pq Ic ah Ns \&| Ns Ic esp
-.Pq Ic in Ns \&| Ns Ic out
+.Pq Ic ah Ns | Ns Ic esp
+.Pq Ic in Ns | Ns Ic out
 .Ic spi Ar spi-number authspec Op Ar encspec
 .Xc
 Enable IPsec with static keying.
@@ -627,7 +799,7 @@ Keys must be given in hexadecimal format.
 .Pp
 .It Xo
 .Ic ipsec
-.Pq Ic ah Ns \&| Ns Ic esp
+.Pq Ic ah Ns | Ns Ic esp
 .Ic ike
 .Xc
 Enable IPsec with dynamic keying.
@@ -639,11 +811,11 @@ is responsible for managing the session keys.
 With
 .Xr isakmpd 8 ,
 it is sufficient to copy the peer's public key, found in
-.Pa /etc/isakmpd/local.pub ,
+.Pa %%PREFIX%%/etc/isakmpd/private/local.pub ,
 to the local machine.
 It must be stored in a file
 named after the peer's IP address and must be stored in
-.Pa /etc/isakmpd/pubkeys/ipv4/ .
+.Pa %%PREFIX%%/etc/isakmpd/pubkeys/ipv4/ .
 The local public key must be copied to the peer in the same way.
 As
 .Xr bgpd 8
@@ -698,7 +870,7 @@ Do not attempt to actively open a TCP connection to the neighbor system.
 .It Ic remote-as Ar as-number
 Set the AS number of the remote system.
 .Pp
-.It rib .Ar name
+.It Ic rib Ar name
 Bind the neighbor to the specified RIB.
 .Pp
 .It Ic route-reflector Op Ar address
@@ -732,8 +904,8 @@ These sets are rewritten into filter rules and can be viewed with
 .Pp
 .It Xo
 .Ic softreconfig
-.Pq Ic in Ns \&| Ns Ic out
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic in Ns | Ns Ic out
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 Turn soft reconfiguration on or off for the specified direction.
 If soft reconfiguration is turned on, filter changes will be applied on
@@ -760,7 +932,7 @@ tcp md5sig key deadbeef
 .Pp
 .It Xo
 .Ic transparent-as
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 If set to
 .Ic yes ,
@@ -772,7 +944,7 @@ setting.
 .Pp
 .It Xo
 .Ic ttl-security
-.Pq Ic yes Ns \&| Ns Ic no
+.Pq Ic yes Ns | Ns Ic no
 .Xc
 Enable or disable ttl-security.
 When enabled,
@@ -849,6 +1021,10 @@ is matched against a part of the
 .Em AS path
 specified by the
 .Ar as-type .
+.Ar as-number
+may be set to
+.Ic neighbor-as ,
+which is expanded to the current neighbor remote AS number.
 .Ar as-type
 is one of the following operators:
 .Pp
@@ -917,7 +1093,32 @@ may be set to
 which is expanded to the current neighbor remote AS number.
 .Pp
 .It Xo
-.Pq Ic from Ns \&| Ns Ic to
+.Ic ext-community
+.Ar subtype Ar as-number Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community
+.Ar subtype Ar IP Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community
+.Ar subtype Ar numvalue
+.Xc
+This rule applies only to
+.Em UPDATES
+where the
+.Em extended community
+path attribute is present and matches.
+Extended Communities are specified by a
+.Ar subtype
+and normally two values, a globally unique part (e.g. the AS number) and a
+local part.
+See also the
+.Sx ATTRIBUTE SET
+section for further information about the encoding.
+.Pp
+.It Xo
+.Pq Ic from Ns | Ns Ic to
 .Ar peer
 .Xc
 This rule applies only to
@@ -945,7 +1146,7 @@ if enclosed in curly brackets:
 deny from { 128.251.16.1, 251.128.16.2, group hojo }
 .Ed
 .Pp
-.It Pq Ic inet Ns \&| Ns Ic inet6
+.It Pq Ic inet Ns | Ns Ic inet6
 This rule applies only to routes matching the stated address family.
 The address family needs to be set only in rules that use
 .Ic prefixlen
@@ -953,6 +1154,24 @@ without specifying a
 .Ic prefix
 beforehand.
 .Pp
+.It Ic max-as-len Ar len
+This rule applies only to
+.Em UPDATES
+where the
+.Em AS path
+has more than
+.Ar len
+elements.
+.Pp
+.It Ic max-as-seq Ar len
+This rule applies only to
+.Em UPDATES
+where a single
+.Em AS number
+is repeated more than
+.Ar len
+times.
+.Pp
 .It Xo
 .Ic prefix
 .Ar address Ns Li / Ns Ar len
@@ -1028,6 +1247,12 @@ matches a rule which has the
 option set, this rule is considered the last matching rule, and evaluation
 of subsequent rules is skipped.
 .Pp
+.It Ic rib Ar name
+Apply rule only to the specified RIB.
+This only applies for received updates, so not for rules using the
+.Ar to peer
+parameter.
+.Pp
 .It Ic set Ar attribute ...
 All matching rules can set the
 .Em AS path attributes
@@ -1079,6 +1304,48 @@ Alternately, well-known communities may be specified by name:
 or
 .Ic NO_PEER .
 .Pp
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar as-number Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar IP Ns Li : Ns Ar local
+.Xc
+.It Xo
+.Ic ext-community Op Ar delete
+.Ar subtype Ar numvalue
+.Xc
+Set or delete the
+.Em Extended Community
+AS path attribute.
+Extended Communities are specified by a
+.Ar subtype
+and normally two values, a globally unique part (e.g. the AS number) and a
+local part.
+The type is selected depending on the encoding of the global part.
+Two-octet AS Specific Extended Communities and Four-octet AS Specific Extended
+Communities are encoded as
+.Ar as-number Ns Li : Ns Ar local .
+Four-octet encoding is used if the
+.Ar as-number
+is bigger then 65535 or if the AS_DOT encoding is used.
+IPv4 Address Specific Extended Communities are encoded as
+.Ar IP Ns Li : Ns Ar local .
+Opaque Extended Communities are encoded with a single numeric value.
+Currently the following subtypes are supported:
+.Bd -literal -offset indent
+rt       Route Target
+soo      Source of Origin
+odi      OSPF Domain Identifier
+ort      OSPF Route Type
+ori      OSPF Router ID
+bdc      BGP Data Collection
+.Ed
+.Pp
+Not all type and subtype value pairs are allowed by IANA and the parser
+will ensure that no invalid combination is created.
+.Pp
 .It Ic localpref Ar number
 Set the
 .Em LOCAL_PREF
@@ -1108,6 +1375,20 @@ otherwise it will be set to
 .Ar number .
 .Pp
 .It Xo
+.Ic origin
+.Sm off
+.Po Ic igp \*(Ba
+.Ic egp \*(Ba
+.Ic incomplete Pc
+.Sm on
+.Xc
+Set the
+.Em ORIGIN
+AS path attribute to mark the source of this
+route as being injected from an igp protocol, an egp protocol
+or being an aggregated route.
+.Pp
+.It Xo
 .Ic nexthop
 .Sm off
 .Po Ar address \*(Ba
@@ -1157,9 +1438,8 @@ times to the
 .Em AS path .
 .Pp
 .It Ic rtlabel Ar label
-Add the prefix with the specified
-.Ar label
-to the kernel routing table.
+Add the prefix to the kernel routing table with the specified
+.Ar label .
 .Pp
 .It Ic weight Ar number
 The
@@ -1181,8 +1461,8 @@ For prefixes with equally long paths, the prefix with the larger weight
 is selected.
 .El
 .Sh FILES
-.Bl -tag -width "/etc/bgpd.conf" -compact
-.It Pa /etc/bgpd.conf
+.Bl -tag -width "%%PREFIX%%/etc/bgpd.conf" -compact
+.It Pa %%PREFIX%%/etc/bgpd.conf
 .Xr bgpd 8
 configuration file
 .El
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index a8dbaa4..c0a3841 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: bgpd.h,v 1.241 2009/06/12 16:42:53 claudio Exp $ */
+/*	$OpenBSD: bgpd.h,v 1.255 2010/04/06 13:25:08 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/queue.h>
+#include <sys/tree.h>
 #include <net/route.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -30,11 +31,16 @@
 #include <poll.h>
 #include <stdarg.h>
 
-#include <imsg.h>
+#if defined(__FreeBSD__)	/* compat */
+#include "openbsd-compat.h"
+#endif /* defined(__FreeBSD__) */
+#include "imsg.h"
 
 #define	BGP_VERSION			4
 #define	BGP_PORT			179
+#ifndef CONFFILE
 #define	CONFFILE			"/etc/bgpd.conf"
+#endif /* !CONFFILE */
 #define	BGPD_USER			"_bgpd"
 #define	PEER_DESCR_LEN			32
 #define	PFTABLE_LEN			16
@@ -42,8 +48,6 @@
 #define	IPSEC_ENC_KEY_LEN		32
 #define	IPSEC_AUTH_KEY_LEN		20
 
-#define	ASNUM_MAX			0xffffffff
-
 #define	MAX_PKTSIZE			4096
 #define	MIN_HOLDTIME			3
 #define	READ_BUF_SIZE			65535
@@ -55,13 +59,8 @@
 #define	BGPD_OPT_NOACTION		0x0004
 #define	BGPD_OPT_FORCE_DEMOTE		0x0008
 
-#define	BGPD_FLAG_NO_FIB_UPDATE		0x0001
 #define	BGPD_FLAG_NO_EVALUATE		0x0002
 #define	BGPD_FLAG_REFLECTOR		0x0004
-#define	BGPD_FLAG_REDIST_STATIC		0x0008
-#define	BGPD_FLAG_REDIST_CONNECTED	0x0010
-#define	BGPD_FLAG_REDIST6_STATIC	0x0020
-#define	BGPD_FLAG_REDIST6_CONNECTED	0x0040
 #define	BGPD_FLAG_NEXTHOP_BGP		0x0080
 #define	BGPD_FLAG_NEXTHOP_DEFAULT	0x1000
 #define	BGPD_FLAG_DECISION_MASK		0x0f00
@@ -83,6 +82,8 @@
 #define	F_REJECT		0x0080
 #define	F_BLACKHOLE		0x0100
 #define	F_LONGER		0x0200
+#define	F_MPLS			0x0400
+#define	F_REDISTRIBUTED		0x0800
 #define	F_CTL_DETAIL		0x1000	/* only used by bgpctl */
 #define	F_CTL_ADJ_IN		0x2000
 #define	F_CTL_ADJ_OUT		0x4000
@@ -109,18 +110,74 @@ enum reconf_action {
 	RECONF_DELETE
 };
 
+/* Address Family Numbers as per RFC 1700 */
+#define	AFI_UNSPEC	0
+#define	AFI_IPv4	1
+#define	AFI_IPv6	2
+
+/* Subsequent Address Family Identifier as per RFC 4760 */
+#define	SAFI_NONE	0
+#define	SAFI_UNICAST	1
+#define	SAFI_MULTICAST	2
+#define	SAFI_MPLS	4
+#define	SAFI_MPLSVPN	128
+
+struct aid {
+	u_int16_t	 afi;
+	sa_family_t	 af;
+	u_int8_t	 safi;
+	char		*name;
+};
+
+extern const struct aid aid_vals[];
+
+#define	AID_UNSPEC	0
+#define	AID_INET	1
+#define	AID_INET6	2
+#define	AID_VPN_IPv4	3
+#define	AID_MAX		4
+
+#define AID_VALS	{					\
+	/* afi, af, safii, name */				\
+	{ AFI_UNSPEC, AF_UNSPEC, SAFI_NONE, "unspec"},		\
+	{ AFI_IPv4, AF_INET, SAFI_UNICAST, "IPv4 unicast" },	\
+	{ AFI_IPv6, AF_INET6, SAFI_UNICAST, "IPv6 unicast" },	\
+	{ AFI_IPv4, AF_INET, SAFI_MPLSVPN, "IPv4 vpn" }		\
+}
+
+#define AID_PTSIZE	{				\
+	0,						\
+	sizeof(struct pt_entry4), 			\
+	sizeof(struct pt_entry6),			\
+	sizeof(struct pt_entry_vpn4)			\
+}
+
+struct vpn4_addr {
+	u_int64_t	rd;
+	struct in_addr	addr;
+	u_int8_t	labelstack[21];	/* max that makes sense */
+	u_int8_t	labellen;
+	u_int8_t	pad1;
+	u_int8_t	pad2;
+};
+
+#define BGP_MPLS_BOS	0x01
+
 struct bgpd_addr {
-	sa_family_t	af;
 	union {
 		struct in_addr		v4;
 		struct in6_addr		v6;
-		u_int8_t		addr8[16];
-		u_int16_t		addr16[8];
-		u_int32_t		addr32[4];
+		struct vpn4_addr	vpn4;
+		/* maximum size for a prefix is 256 bits */
+		u_int8_t		addr8[32];
+		u_int16_t		addr16[16];
+		u_int32_t		addr32[8];
 	} ba;		    /* 128-bit address */
 	u_int32_t	scope_id;	/* iface scope id for v6 */
+	u_int8_t	aid;
 #define	v4	ba.v4
 #define	v6	ba.v6
+#define	vpn4	ba.vpn4
 #define	addr8	ba.addr8
 #define	addr16	ba.addr16
 #define	addr32	ba.addr32
@@ -141,17 +198,12 @@ TAILQ_HEAD(listen_addrs, listen_addr);
 TAILQ_HEAD(filter_set_head, filter_set);
 
 struct bgpd_config {
-	struct filter_set_head			 connectset;
-	struct filter_set_head			 connectset6;
-	struct filter_set_head			 staticset;
-	struct filter_set_head			 staticset6;
 	struct listen_addrs			*listen_addrs;
 	char					*csock;
 	char					*rcsock;
 	int					 opts;
 	int					 flags;
 	int					 log;
-	u_int					 rtableid;
 	u_int32_t				 bgpid;
 	u_int32_t				 clusterid;
 	u_int32_t				 as;
@@ -205,11 +257,10 @@ struct peer_auth {
 };
 
 struct capabilities {
-	u_int8_t	mp_v4;		/* multiprotocol extensions, RFC 4760 */
-	u_int8_t	mp_v6;
-	u_int8_t	refresh;	/* route refresh, RFC 2918 */
-	u_int8_t	restart;	/* graceful restart, RFC 4724 */
-	u_int8_t	as4byte;	/* draft-ietf-idr-as4bytes-13 */
+	int8_t	mp[AID_MAX];	/* multiprotocol extensions, RFC 4760 */
+	int8_t	refresh;	/* route refresh, RFC 2918 */
+	int8_t	restart;	/* graceful restart, RFC 4724 */
+	int8_t	as4byte;	/* draft-ietf-idr-as4bytes-13 */
 };
 
 struct peer_config {
@@ -248,21 +299,31 @@ struct peer_config {
 	u_int8_t		 ttlsec;	/* TTL security hack */
 	u_int8_t		 flags;
 	u_int8_t		 pad[3];
+	char			 lliface[IFNAMSIZ];
 };
 
 #define PEERFLAG_TRANS_AS	0x01
 
+enum network_type {
+	NETWORK_DEFAULT,
+	NETWORK_STATIC,
+	NETWORK_CONNECTED
+};
+
 struct network_config {
 	struct bgpd_addr	prefix;
 	struct filter_set_head	attrset;
+	u_int			rtableid;
+	enum network_type	type;
 	u_int8_t		prefixlen;
+	u_int8_t		old;	/* used for reloading */
 };
 
 TAILQ_HEAD(network_head, network);
 
 struct network {
-	struct network_config	net;
-	TAILQ_ENTRY(network)	entry;
+	struct network_config		net;
+	TAILQ_ENTRY(network)		entry;
 };
 
 enum imsg_type {
@@ -276,7 +337,6 @@ enum imsg_type {
 	IMSG_CTL_NEIGHBOR_CLEAR,
 	IMSG_CTL_NEIGHBOR_RREFRESH,
 	IMSG_CTL_KROUTE,
-	IMSG_CTL_KROUTE6,
 	IMSG_CTL_KROUTE_ADDR,
 	IMSG_CTL_RESULT,
 	IMSG_CTL_SHOW_NEIGHBOR,
@@ -288,10 +348,11 @@ enum imsg_type {
 	IMSG_CTL_SHOW_RIB_ATTR,
 	IMSG_CTL_SHOW_RIB_COMMUNITY,
 	IMSG_CTL_SHOW_NETWORK,
-	IMSG_CTL_SHOW_NETWORK6,
 	IMSG_CTL_SHOW_RIB_MEM,
 	IMSG_CTL_SHOW_TERSE,
 	IMSG_CTL_SHOW_TIMER,
+	IMSG_CTL_LOG_VERBOSE,
+	IMSG_CTL_SHOW_FIB_TABLES,
 	IMSG_NETWORK_ADD,
 	IMSG_NETWORK_REMOVE,
 	IMSG_NETWORK_FLUSH,
@@ -302,6 +363,11 @@ enum imsg_type {
 	IMSG_RECONF_PEER,
 	IMSG_RECONF_FILTER,
 	IMSG_RECONF_LISTENER,
+	IMSG_RECONF_CTRL,
+	IMSG_RECONF_RDOMAIN,
+	IMSG_RECONF_RDOMAIN_EXPORT,
+	IMSG_RECONF_RDOMAIN_IMPORT,
+	IMSG_RECONF_RDOMAIN_DONE,
 	IMSG_RECONF_DONE,
 	IMSG_UPDATE,
 	IMSG_UPDATE_ERR,
@@ -313,8 +379,6 @@ enum imsg_type {
 	IMSG_MRT_CLOSE,
 	IMSG_KROUTE_CHANGE,
 	IMSG_KROUTE_DELETE,
-	IMSG_KROUTE6_CHANGE,
-	IMSG_KROUTE6_DELETE,
 	IMSG_NEXTHOP_ADD,
 	IMSG_NEXTHOP_REMOVE,
 	IMSG_NEXTHOP_UPDATE,
@@ -379,9 +443,43 @@ enum suberr_cease {
 	ERR_CEASE_RSRC_EXHAUST
 };
 
+struct kroute_node;
+struct kroute6_node;
+struct knexthop_node;
+RB_HEAD(kroute_tree, kroute_node);
+RB_HEAD(kroute6_tree, kroute6_node);
+RB_HEAD(knexthop_tree, knexthop_node);
+
+struct ktable {
+	char			 descr[PEER_DESCR_LEN];
+	char			 ifmpe[IFNAMSIZ];
+	struct kroute_tree	 krt;
+	struct kroute6_tree	 krt6;
+	struct knexthop_tree	 knt;
+	struct network_head	 krn;
+	u_int			 rtableid;
+	u_int			 nhtableid; /* rdomain id for nexthop lookup */
+	u_int			 ifindex;   /* ifindex of ifmpe */
+	int			 nhrefcnt;  /* refcnt for nexthop table */
+	enum reconf_action	 state;
+	u_int8_t		 fib_conf;  /* configured FIB sync flag */
+	u_int8_t		 fib_sync;  /* is FIB synced with kernel? */
+};
+
+struct kroute_full {
+	struct bgpd_addr	prefix;
+	struct bgpd_addr	nexthop;
+	char			label[RTLABEL_LEN];
+	u_int16_t		flags;
+	u_short			ifindex;
+	u_int8_t		prefixlen;
+	u_int8_t		priority;
+};
+
 struct kroute {
 	struct in_addr	prefix;
 	struct in_addr	nexthop;
+	u_int32_t	mplslabel;
 	u_int16_t	flags;
 	u_int16_t	labelid;
 	u_short		ifindex;
@@ -400,14 +498,12 @@ struct kroute6 {
 };
 
 struct kroute_nexthop {
-	union {
-		struct kroute		kr4;
-		struct kroute6		kr6;
-	} kr;
 	struct bgpd_addr	nexthop;
 	struct bgpd_addr	gateway;
+	struct bgpd_addr	net;
 	u_int8_t		valid;
 	u_int8_t		connected;
+	u_int8_t		netlen;
 };
 
 struct kif {
@@ -423,8 +519,7 @@ struct kif {
 struct session_up {
 	struct bgpd_addr	local_addr;
 	struct bgpd_addr	remote_addr;
-	struct capabilities	capa_announced;
-	struct capabilities	capa_received;
+	struct capabilities	capa;
 	u_int32_t		remote_bgpid;
 	u_int16_t		short_as;
 };
@@ -437,8 +532,13 @@ struct pftable_msg {
 
 struct ctl_show_nexthop {
 	struct bgpd_addr	addr;
-	u_int8_t		valid;
 	struct kif		kif;
+	union {
+		struct kroute		kr4;
+		struct kroute6		kr6;
+	} kr;
+	u_int8_t		valid;
+	u_int8_t		krvalid;
 };
 
 struct ctl_neighbor {
@@ -447,20 +547,10 @@ struct ctl_neighbor {
 	int			show_timers;
 };
 
-struct kroute_label {
-	struct kroute	kr;
-	char		label[RTLABEL_LEN];
-};
-
-struct kroute6_label {
-	struct kroute6	kr;
-	char		label[RTLABEL_LEN];
-};
-
-#define	F_RIB_ELIGIBLE	0x01
-#define	F_RIB_ACTIVE	0x02
-#define	F_RIB_INTERNAL	0x04
-#define	F_RIB_ANNOUNCE	0x08
+#define	F_PREF_ELIGIBLE	0x01
+#define	F_PREF_ACTIVE	0x02
+#define	F_PREF_INTERNAL	0x04
+#define	F_PREF_ANNOUNCE	0x08
 
 struct ctl_show_rib {
 	struct bgpd_addr	true_nexthop;
@@ -498,16 +588,52 @@ enum as_spec {
 	AS_EMPTY
 };
 
+enum aslen_spec {
+	ASLEN_NONE,
+	ASLEN_MAX,
+	ASLEN_SEQ
+};
+
 struct filter_as {
-	enum as_spec	type;
 	u_int32_t	as;
+	u_int16_t	flags;
+	enum as_spec	type;
+};
+
+struct filter_aslen {
+	u_int		aslen;
+	enum aslen_spec	type;
 };
 
+#define AS_FLAG_NEIGHBORAS	0x01
+
 struct filter_community {
-	int			as;
-	int			type;
+	int		as;
+	int		type;
+};
+
+struct filter_extcommunity {
+	u_int16_t	flags;
+	u_int8_t	type;
+	u_int8_t	subtype;	/* if extended type */
+	union {
+		struct ext_as {
+			u_int16_t	as;
+			u_int32_t	val;
+		}		ext_as;
+		struct ext_as4 {
+			u_int32_t	as4;
+			u_int16_t	val;
+		}		ext_as4;
+		struct ext_ip {
+			struct in_addr	addr;
+			u_int16_t	val;
+		}		ext_ip;
+		u_int64_t	ext_opaq;	/* only 48 bits */
+	}		data;
 };
 
+
 struct ctl_show_rib_request {
 	char			rib[PEER_DESCR_LEN];
 	struct ctl_neighbor	neighbor;
@@ -518,8 +644,8 @@ struct ctl_show_rib_request {
 	pid_t			pid;
 	u_int16_t		flags;
 	enum imsg_type		type;
-	sa_family_t		af;
 	u_int8_t		prefixlen;
+	u_int8_t		aid;
 };
 
 enum filter_actions {
@@ -585,6 +711,28 @@ struct filter_peers {
 #define EXT_COMMUNITY_OSPF_RTR_TYPE	6	/* RFC 4577 */
 #define EXT_COMMUNITY_OSPF_RTR_ID	7	/* RFC 4577 */
 #define EXT_COMMUNITY_BGP_COLLECT	8	/* RFC 4384 */
+/* other handy defines */
+#define EXT_COMMUNITY_OPAQUE_MAX	0xffffffffffffULL
+#define EXT_COMMUNITY_FLAG_VALID	0x01
+
+struct ext_comm_pairs {
+	u_int8_t	type;
+	u_int8_t	subtype;
+	u_int8_t	transitive;	/* transitive bit needs to be set */
+};
+
+#define IANA_EXT_COMMUNITIES	{					\
+	{ EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_ROUTE_TGT, 0 },		\
+	{ EXT_COMMUNITY_TWO_AS, EXT_CUMMUNITY_ROUTE_ORIG, 0 },		\
+	{ EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_OSPF_DOM_ID, 0 },		\
+	{ EXT_COMMUNITY_TWO_AS, EXT_COMMUNITY_BGP_COLLECT, 0 },		\
+	{ EXT_COMMUNITY_FOUR_AS, EXT_COMMUNITY_ROUTE_TGT, 0 },		\
+	{ EXT_COMMUNITY_FOUR_AS, EXT_CUMMUNITY_ROUTE_ORIG, 0 },		\
+	{ EXT_COMMUNITY_IPV4, EXT_COMMUNITY_ROUTE_TGT, 0 },		\
+	{ EXT_COMMUNITY_IPV4, EXT_CUMMUNITY_ROUTE_ORIG, 0 },		\
+	{ EXT_COMMUNITY_IPV4, EXT_COMMUNITY_OSPF_RTR_ID, 0 },		\
+	{ EXT_COMMUNITY_OPAQUE, EXT_COMMUNITY_OSPF_RTR_TYPE, 0 }	\
+}
 
 
 struct filter_prefix {
@@ -594,16 +742,18 @@ struct filter_prefix {
 
 struct filter_prefixlen {
 	enum comp_ops		op;
-	sa_family_t		af;
+	u_int8_t		aid;
 	u_int8_t		len_min;
 	u_int8_t		len_max;
 };
 
 struct filter_match {
-	struct filter_prefix	prefix;
-	struct filter_prefixlen	prefixlen;
-	struct filter_as	as;
-	struct filter_community	community;
+	struct filter_prefix		prefix;
+	struct filter_prefixlen		prefixlen;
+	struct filter_as		as;
+	struct filter_aslen		aslen;
+	struct filter_community		community;
+	struct filter_extcommunity	ext_community;
 };
 
 TAILQ_HEAD(filter_head, filter_rule);
@@ -635,10 +785,13 @@ enum action_types {
 	ACTION_SET_NEXTHOP_SELF,
 	ACTION_SET_COMMUNITY,
 	ACTION_DEL_COMMUNITY,
+	ACTION_SET_EXT_COMMUNITY,
+	ACTION_DEL_EXT_COMMUNITY,
 	ACTION_PFTABLE,
 	ACTION_PFTABLE_ID,
 	ACTION_RTLABEL,
-	ACTION_RTLABEL_ID
+	ACTION_RTLABEL_ID,
+	ACTION_SET_ORIGIN
 };
 
 struct filter_set {
@@ -650,23 +803,53 @@ struct filter_set {
 		int32_t			relative;
 		struct bgpd_addr	nexthop;
 		struct filter_community	community;
+		struct filter_extcommunity	ext_community;
 		char			pftable[PFTABLE_LEN];
 		char			rtlabel[RTLABEL_LEN];
+		u_int8_t		origin;
 	} action;
 	enum action_types		type;
 };
 
-struct rrefresh {
-	u_int16_t	afi;
-	u_int8_t	safi;
+struct rdomain {
+	SIMPLEQ_ENTRY(rdomain)		entry;
+	char				descr[PEER_DESCR_LEN];
+	char				ifmpe[IFNAMSIZ];
+	struct filter_set_head		import;
+	struct filter_set_head		export;
+	struct network_head		net_l;
+	u_int64_t			rd;
+	u_int				rtableid;
+	u_int				label;
+	int				flags;
 };
+SIMPLEQ_HEAD(rdomain_head, rdomain);
+
+struct rde_rib {
+	SIMPLEQ_ENTRY(rde_rib)	entry;
+	char			name[PEER_DESCR_LEN];
+	u_int			rtableid;
+	u_int16_t		id;
+	u_int16_t		flags;
+};
+SIMPLEQ_HEAD(rib_names, rde_rib);
+extern struct rib_names ribnames;
+
+/* rde_rib flags */
+#define F_RIB_ENTRYLOCK		0x0001
+#define F_RIB_NOEVALUATE	0x0002
+#define F_RIB_NOFIB		0x0004
+#define F_RIB_NOFIBSYNC		0x0008
+#define F_RIB_HASNOFIB		(F_RIB_NOFIB | F_RIB_NOEVALUATE)
+
+/* 4-byte magic AS number */
+#define AS_TRANS	23456
 
 struct rde_memstats {
 	int64_t		path_cnt;
 	int64_t		prefix_cnt;
 	int64_t		rib_cnt;
-	int64_t		pt4_cnt;
-	int64_t		pt6_cnt;
+	int64_t		pt_cnt[AID_MAX];
 	int64_t		nexthop_cnt;
 	int64_t		aspath_cnt;
 	int64_t		aspath_size;
@@ -677,38 +860,29 @@ struct rde_memstats {
 	int64_t		attr_dcnt;
 };
 
-struct rde_rib {
-	SIMPLEQ_ENTRY(rde_rib)	entry;
-	char			name[PEER_DESCR_LEN];
-	u_int16_t		id;
-	u_int16_t		flags;
-};
-SIMPLEQ_HEAD(rib_names, rde_rib);
-extern struct rib_names ribnames;
-
-/* Address Family Numbers as per RFC 1700 */
-#define	AFI_IPv4	1
-#define	AFI_IPv6	2
-#define	AFI_ALL		0xffff
+/* macros for IPv6 link-local address */
+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
+#define IN6_LINKLOCAL_IFINDEX(addr) \
+        ((addr).s6_addr[2] << 8 | (addr).s6_addr[3])
 
-/* Subsequent Address Family Identifier as per RFC 4760 */
-#define	SAFI_NONE	0x00
-#define	SAFI_UNICAST	0x01
-#define	SAFI_MULTICAST	0x02
-#define	SAFI_ALL	0xff
-
-/* 4-byte magic AS number */
-#define AS_TRANS	23456
+#define SET_IN6_LINKLOCAL_IFINDEX(addr, index) \
+        do { \
+                (addr).s6_addr[2] = ((index) >> 8) & 0xff; \
+                (addr).s6_addr[3] = (index) & 0xff; \
+        } while (0)
+#endif
 
 /* prototypes */
 /* bgpd.c */
 void		 send_nexthop_update(struct kroute_nexthop *);
 void		 send_imsg_session(int, pid_t, void *, u_int16_t);
-int		 bgpd_redistribute(int, struct kroute *, struct kroute6 *);
+int		 send_network(int, struct network_config *,
+		     struct filter_set_head *);
 int		 bgpd_filternexthop(struct kroute *, struct kroute6 *);
 
 /* log.c */
 void		 log_init(int);
+void		 log_verbose(int);
 void		 vlog(int, const char *, va_list);
 void		 log_peer_warn(const struct peer_config *, const char *, ...);
 void		 log_peer_warnx(const struct peer_config *, const char *, ...);
@@ -726,19 +900,22 @@ int	 cmdline_symset(char *);
 int	 host(const char *, struct bgpd_addr *, u_int8_t *);
 
 /* kroute.c */
-int		 kr_init(int, u_int);
-int		 kr_change(struct kroute_label *);
-int		 kr_delete(struct kroute_label *);
-int		 kr6_change(struct kroute6_label *);
-int		 kr6_delete(struct kroute6_label *);
+int		 kr_init(void);
+int		 ktable_update(u_int, char *, char *, int);
+void		 ktable_preload(void);
+void		 ktable_postload(void);
+int		 ktable_exists(u_int, u_int *);
+int		 kr_change(u_int, struct kroute_full *);
+int		 kr_delete(u_int, struct kroute_full *);
 void		 kr_shutdown(void);
-void		 kr_fib_couple(void);
-void		 kr_fib_decouple(void);
+void		 kr_fib_couple(u_int);
+void		 kr_fib_decouple(u_int);
 int		 kr_dispatch_msg(void);
-int		 kr_nexthop_add(struct bgpd_addr *);
-void		 kr_nexthop_delete(struct bgpd_addr *);
+int		 kr_nexthop_add(u_int32_t, struct bgpd_addr *);
+void		 kr_nexthop_delete(u_int32_t, struct bgpd_addr *);
 void		 kr_show_route(struct imsg *);
 void		 kr_ifinfo(char *);
+int		 kr_net_reload(u_int, struct network_head *);
 int		 kr_reload(void);
 struct in6_addr	*prefixlen2mask6(u_int8_t prefixlen);
 
@@ -772,6 +949,8 @@ void		 pftable_ref(u_int16_t);
 /* rde_filter.c */
 void		 filterset_free(struct filter_set_head *);
 int		 filterset_cmp(struct filter_set *, struct filter_set *);
+void		 filterset_move(struct filter_set_head *,
+		    struct filter_set_head *);
 const char	*filterset_name(enum action_types);
 
 /* util.c */
@@ -779,11 +958,20 @@ const char	*log_addr(const struct bgpd_addr *);
 const char	*log_in6addr(const struct in6_addr *);
 const char	*log_sockaddr(struct sockaddr *);
 const char	*log_as(u_int32_t);
+const char	*log_rd(u_int64_t);
+const char	*log_ext_subtype(u_int8_t);
 int		 aspath_snprint(char *, size_t, void *, u_int16_t);
 int		 aspath_asprint(char **, void *, u_int16_t);
 size_t		 aspath_strlen(void *, u_int16_t);
 in_addr_t	 prefixlen2mask(u_int8_t);
 void		 inet6applymask(struct in6_addr *, const struct in6_addr *,
 		    int);
+const char	*aid2str(u_int8_t);
+int		 aid2afi(u_int8_t, u_int16_t *, u_int8_t *);
+int		 afi2aid(u_int16_t, u_int8_t, u_int8_t *);
+sa_family_t	 aid2af(u_int8_t);
+int		 af2aid(sa_family_t, u_int8_t, u_int8_t *);
+struct sockaddr	*addr2sa(struct bgpd_addr *, u_int16_t);
+void		 sa2addr(struct sockaddr *, struct bgpd_addr *);
 
 #endif /* __BGPD_H__ */
diff --git a/bgpd/buffer.c b/bgpd/buffer.c
deleted file mode 100644
index a4b9e9d..0000000
--- a/bgpd/buffer.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*	$OpenBSD: buffer.c,v 1.43 2009/06/06 06:33:15 eric Exp $	*/
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "imsg.h"
-
-int	buf_realloc(struct buf *, size_t);
-void	buf_enqueue(struct msgbuf *, struct buf *);
-void	buf_dequeue(struct msgbuf *, struct buf *);
-
-struct buf *
-buf_open(size_t len)
-{
-	struct buf	*buf;
-
-	if ((buf = calloc(1, sizeof(struct buf))) == NULL)
-		return (NULL);
-	if ((buf->buf = malloc(len)) == NULL) {
-		free(buf);
-		return (NULL);
-	}
-	buf->size = buf->max = len;
-	buf->fd = -1;
-
-	return (buf);
-}
-
-struct buf *
-buf_dynamic(size_t len, size_t max)
-{
-	struct buf	*buf;
-
-	if (max < len)
-		return (NULL);
-
-	if ((buf = buf_open(len)) == NULL)
-		return (NULL);
-
-	if (max > 0)
-		buf->max = max;
-
-	return (buf);
-}
-
-int
-buf_realloc(struct buf *buf, size_t len)
-{
-	u_char	*b;
-
-	/* on static buffers max is eq size and so the following fails */
-	if (buf->wpos + len > buf->max) {
-		errno = ENOMEM;
-		return (-1);
-	}
-
-	b = realloc(buf->buf, buf->wpos + len);
-	if (b == NULL)
-		return (-1);
-	buf->buf = b;
-	buf->size = buf->wpos + len;
-
-	return (0);
-}
-
-int
-buf_add(struct buf *buf, const void *data, size_t len)
-{
-	if (buf->wpos + len > buf->size)
-		if (buf_realloc(buf, len) == -1)
-			return (-1);
-
-	memcpy(buf->buf + buf->wpos, data, len);
-	buf->wpos += len;
-	return (0);
-}
-
-void *
-buf_reserve(struct buf *buf, size_t len)
-{
-	void	*b;
-
-	if (buf->wpos + len > buf->size)
-		if (buf_realloc(buf, len) == -1)
-			return (NULL);
-
-	b = buf->buf + buf->wpos;
-	buf->wpos += len;
-	return (b);
-}
-
-void *
-buf_seek(struct buf *buf, size_t pos, size_t len)
-{
-	/* only allowed to seek in already written parts */
-	if (pos + len > buf->wpos)
-		return (NULL);
-
-	return (buf->buf + pos);
-}
-
-size_t
-buf_size(struct buf *buf)
-{
-	return (buf->wpos);
-}
-
-size_t
-buf_left(struct buf *buf)
-{
-	return (buf->max - buf->wpos);
-}
-
-void
-buf_close(struct msgbuf *msgbuf, struct buf *buf)
-{
-	buf_enqueue(msgbuf, buf);
-}
-
-int
-buf_write(struct msgbuf *msgbuf)
-{
-	struct iovec	 iov[IOV_MAX];
-	struct buf	*buf, *next;
-	unsigned int	 i = 0;
-	ssize_t	n;
-
-	bzero(&iov, sizeof(iov));
-	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
-		if (i >= IOV_MAX)
-			break;
-		iov[i].iov_base = buf->buf + buf->rpos;
-		iov[i].iov_len = buf->size - buf->rpos;
-		i++;
-	}
-
-	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
-		if (errno == EAGAIN || errno == ENOBUFS ||
-		    errno == EINTR)	/* try later */
-			return (0);
-		else
-			return (-1);
-	}
-
-	if (n == 0) {			/* connection closed */
-		errno = 0;
-		return (-2);
-	}
-
-	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
-	    buf = next) {
-		next = TAILQ_NEXT(buf, entry);
-		if (buf->rpos + n >= buf->size) {
-			n -= buf->size - buf->rpos;
-			buf_dequeue(msgbuf, buf);
-		} else {
-			buf->rpos += n;
-			n = 0;
-		}
-	}
-
-	return (0);
-}
-
-void
-buf_free(struct buf *buf)
-{
-	free(buf->buf);
-	free(buf);
-}
-
-void
-msgbuf_init(struct msgbuf *msgbuf)
-{
-	msgbuf->queued = 0;
-	msgbuf->fd = -1;
-	TAILQ_INIT(&msgbuf->bufs);
-}
-
-void
-msgbuf_clear(struct msgbuf *msgbuf)
-{
-	struct buf	*buf;
-
-	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
-		buf_dequeue(msgbuf, buf);
-}
-
-int
-msgbuf_write(struct msgbuf *msgbuf)
-{
-	struct iovec	 iov[IOV_MAX];
-	struct buf	*buf, *next;
-	unsigned int	 i = 0;
-	ssize_t		 n;
-	struct msghdr	 msg;
-	struct cmsghdr	*cmsg;
-	union {
-		struct cmsghdr	hdr;
-		char		buf[CMSG_SPACE(sizeof(int))];
-	} cmsgbuf;
-
-	bzero(&iov, sizeof(iov));
-	bzero(&msg, sizeof(msg));
-	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
-		if (i >= IOV_MAX)
-			break;
-		iov[i].iov_base = buf->buf + buf->rpos;
-		iov[i].iov_len = buf->wpos - buf->rpos;
-		i++;
-		if (buf->fd != -1)
-			break;
-	}
-
-	msg.msg_iov = iov;
-	msg.msg_iovlen = i;
-
-	if (buf != NULL && buf->fd != -1) {
-		msg.msg_control = (caddr_t)&cmsgbuf.buf;
-		msg.msg_controllen = sizeof(cmsgbuf.buf);
-		cmsg = CMSG_FIRSTHDR(&msg);
-		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-		cmsg->cmsg_level = SOL_SOCKET;
-		cmsg->cmsg_type = SCM_RIGHTS;
-		*(int *)CMSG_DATA(cmsg) = buf->fd;
-	}
-
-	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
-		if (errno == EAGAIN || errno == ENOBUFS ||
-		    errno == EINTR)	/* try later */
-			return (0);
-		else
-			return (-1);
-	}
-
-	if (n == 0) {			/* connection closed */
-		errno = 0;
-		return (-2);
-	}
-
-	/*
-	 * assumption: fd got sent if sendmsg sent anything
-	 * this works because fds are passed one at a time
-	 */
-	if (buf != NULL && buf->fd != -1) {
-		close(buf->fd);
-		buf->fd = -1;
-	}
-
-	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
-	    buf = next) {
-		next = TAILQ_NEXT(buf, entry);
-		if (buf->rpos + n >= buf->wpos) {
-			n -= buf->wpos - buf->rpos;
-			buf_dequeue(msgbuf, buf);
-		} else {
-			buf->rpos += n;
-			n = 0;
-		}
-	}
-
-	return (0);
-}
-
-void
-buf_enqueue(struct msgbuf *msgbuf, struct buf *buf)
-{
-	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
-	msgbuf->queued++;
-}
-
-void
-buf_dequeue(struct msgbuf *msgbuf, struct buf *buf)
-{
-	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
-
-	if (buf->fd != -1)
-		close(buf->fd);
-
-	msgbuf->queued--;
-	buf_free(buf);
-}
diff --git a/bgpd/carp.c b/bgpd/carp.c
index 655a30d..aa25e46 100644
--- a/bgpd/carp.c
+++ b/bgpd/carp.c
@@ -93,9 +93,8 @@ carp_demote_shutdown(void)
 
 	while ((c = TAILQ_FIRST(&carpgroups)) != NULL) {
 		TAILQ_REMOVE(&carpgroups, c, entry);
-		for (; c->changed_by > 0; c->changed_by--)
-			if (c->do_demote)
-				carp_demote_ioctl(c->group, -1);
+		if (c->do_demote && c->changed_by > 0)
+			carp_demote_ioctl(c->group, -c->changed_by);
 
 		free(c->group);
 		free(c);
@@ -105,6 +104,9 @@ carp_demote_shutdown(void)
 int
 carp_demote_get(char *group)
 {
+#if defined(__FreeBSD__)	/* FreeBSD does not have support for CARP */
+	return (-1);
+#else
 	int			s;
 	struct ifgroupreq	ifgr;
 
@@ -127,6 +129,7 @@ carp_demote_get(char *group)
 
 	close(s);
 	return ((int)ifgr.ifgr_attrib.ifg_carp_demoted);
+#endif /* defined(__FreeBSD__) */
 }
 
 int
@@ -159,6 +162,9 @@ carp_demote_set(char *group, int demote)
 int
 carp_demote_ioctl(char *group, int demote)
 {
+#if defined(__FreeBSD__)	/* FreeBSD does not have support for CARP */
+	return (-1);
+#else
 	int			s, res;
 	struct ifgroupreq	ifgr;
 
@@ -181,4 +187,5 @@ carp_demote_ioctl(char *group, int demote)
 
 	close(s);
 	return (res);
+#endif /* defined(__FreeBSD__) */
 }
diff --git a/bgpd/config.c b/bgpd/config.c
index e7c7d1f..51e3096 100644
--- a/bgpd/config.c
+++ b/bgpd/config.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: config.c,v 1.51 2009/01/26 23:10:02 claudio Exp $ */
+/*	$OpenBSD: config.c,v 1.56 2010/10/24 17:20:08 deraadt Exp $ */
 
 /*
  * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
@@ -20,6 +20,11 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+#include <netmpls/mpls.h>
+#endif
 
 #include <errno.h>
 #include <ifaddrs.h>
@@ -47,8 +52,6 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf,
 
 	/* preserve cmd line opts */
 	conf->opts = xconf->opts;
-	conf->csock = xconf->csock;
-	conf->rcsock = xconf->rcsock;
 
 	if (!conf->as) {
 		log_warnx("configuration error: AS not given");
@@ -64,6 +67,9 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf,
 	if ((conf->flags & BGPD_FLAG_REFLECTOR) && conf->clusterid == 0)
 		conf->clusterid = conf->bgpid;
 
+	free(xconf->csock);
+	free(xconf->rcsock);
+
 	conf->listen_addrs = xconf->listen_addrs;
 	memcpy(xconf, conf, sizeof(struct bgpd_config));
 
@@ -74,7 +80,7 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf,
 			nla->reconf = RECONF_REINIT;
 
 	} else {
-		/* 
+		/*
 		 * merge new listeners:
 		 * -flag all existing ones as to be deleted
 		 * -those that are in both new and old: flag to keep
@@ -208,7 +214,7 @@ host_v4(const char *s, struct bgpd_addr *h, u_int8_t *len)
 			return (0);
 	}
 
-	h->af = AF_INET;
+	h->aid = AID_INET;
 	h->v4.s_addr = ina.s_addr;
 	*len = bits;
 
@@ -225,13 +231,7 @@ host_v6(const char *s, struct bgpd_addr *h)
 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
 	hints.ai_flags = AI_NUMERICHOST;
 	if (getaddrinfo(s, "0", &hints, &res) == 0) {
-		h->af = AF_INET6;
-		memcpy(&h->v6,
-		    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
-		    sizeof(h->v6));
-		h->scope_id =
-		    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
-
+		sa2addr(res->ai_addr, h);
 		freeaddrinfo(res);
 		return (1);
 	}
@@ -317,3 +317,30 @@ prepare_listeners(struct bgpd_config *conf)
 		}
 	}
 }
+
+int
+get_mpe_label(struct rdomain *r)
+{
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+	struct  ifreq	ifr;
+	struct shim_hdr	shim;
+	int		s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s == -1)
+		return (-1);
+
+	bzero(&shim, sizeof(shim));
+	bzero(&ifr, sizeof(ifr));
+	strlcpy(ifr.ifr_name, r->ifmpe, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (caddr_t)&shim;
+
+	if (ioctl(s, SIOCGETLABEL, (caddr_t)&ifr) == -1) {
+		close(s);
+		return (-1);
+	}
+	close(s);
+	r->label = shim.shim_label;
+#endif
+	return (0);
+}
diff --git a/bgpd/control.c b/bgpd/control.c
index e8097dd..8ee3f50 100644
--- a/bgpd/control.c
+++ b/bgpd/control.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: control.c,v 1.61 2009/05/05 20:09:19 sthen Exp $ */
+/*	$OpenBSD: control.c,v 1.70 2010/10/29 12:51:53 henning Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -53,7 +53,7 @@ control_init(int restricted, char *path)
 
 	if (unlink(path) == -1)
 		if (errno != ENOENT) {
-			log_warn("unlink %s", path);
+			log_warn("control_init: unlink %s", path);
 			close(fd);
 			return (-1);
 		}
@@ -123,14 +123,14 @@ control_accept(int listenfd, int restricted)
 	if ((connfd = accept(listenfd,
 	    (struct sockaddr *)&sun, &len)) == -1) {
 		if (errno != EWOULDBLOCK && errno != EINTR)
-			log_warn("session_control_accept");
+			log_warn("control_accept: accept");
 		return (0);
 	}
 
 	session_socket_blockmode(connfd, BM_NONBLOCK);
 
-	if ((ctl_conn = malloc(sizeof(struct ctl_conn))) == NULL) {
-		log_warn("session_control_accept");
+	if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
+		log_warn("control_accept");
 		close(connfd);
 		return (0);
 	}
@@ -191,7 +191,8 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 {
 	struct imsg		 imsg;
 	struct ctl_conn		*c;
-	int			 n;
+	ssize_t			 n;
+	int			 verbose;
 	struct peer		*p;
 	struct ctl_neighbor	*neighbor;
 	struct ctl_show_rib_request	*ribreq;
@@ -305,7 +306,8 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 			break;
 		case IMSG_CTL_FIB_COUPLE:
 		case IMSG_CTL_FIB_DECOUPLE:
-			imsg_compose_parent(imsg.hdr.type, 0, NULL, 0);
+			imsg_compose_parent(imsg.hdr.type, imsg.hdr.peerid,
+			    0, NULL, 0);
 			break;
 		case IMSG_CTL_NEIGHBOR_UP:
 		case IMSG_CTL_NEIGHBOR_DOWN:
@@ -328,13 +330,19 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 					control_result(c, CTL_RES_OK);
 					break;
 				case IMSG_CTL_NEIGHBOR_DOWN:
-					bgp_fsm(p, EVNT_STOP);
+					session_stop(p, ERR_CEASE_ADMIN_DOWN);
 					control_result(c, CTL_RES_OK);
 					break;
 				case IMSG_CTL_NEIGHBOR_CLEAR:
-					bgp_fsm(p, EVNT_STOP);
-					timer_set(p, Timer_IdleHold,
-					    SESSION_CLEAR_DELAY);
+					if (!p->conf.down) {
+						session_stop(p,
+						    ERR_CEASE_ADMIN_RESET);
+						timer_set(p, Timer_IdleHold,
+						    SESSION_CLEAR_DELAY);
+					} else {
+						session_stop(p,
+						    ERR_CEASE_ADMIN_DOWN);
+					}
 					control_result(c, CTL_RES_OK);
 					break;
 				case IMSG_CTL_NEIGHBOR_RREFRESH:
@@ -352,13 +360,19 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 				    "wrong length");
 			break;
 		case IMSG_CTL_RELOAD:
+		case IMSG_CTL_SHOW_INTERFACE:
+		case IMSG_CTL_SHOW_FIB_TABLES:
+			c->ibuf.pid = imsg.hdr.pid;
+			imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid,
+			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+			break;
 		case IMSG_CTL_KROUTE:
 		case IMSG_CTL_KROUTE_ADDR:
 		case IMSG_CTL_SHOW_NEXTHOP:
-		case IMSG_CTL_SHOW_INTERFACE:
 			c->ibuf.pid = imsg.hdr.pid;
-			imsg_compose_parent(imsg.hdr.type, imsg.hdr.pid,
-			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+			imsg_compose_parent(imsg.hdr.type, imsg.hdr.peerid,
+			    imsg.hdr.pid, imsg.data, imsg.hdr.len -
+			    IMSG_HEADER_SIZE);
 			break;
 		case IMSG_CTL_SHOW_RIB:
 		case IMSG_CTL_SHOW_RIB_AS:
@@ -370,7 +384,7 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 				neighbor->descr[PEER_DESCR_LEN - 1] = 0;
 				ribreq->peerid = 0;
 				p = NULL;
-				if (neighbor->addr.af) {
+				if (neighbor->addr.aid) {
 					p = getpeerbyaddr(&neighbor->addr);
 					if (p == NULL) {
 						control_result(c,
@@ -397,8 +411,7 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 					break;
 				}
 				if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
-				    && (ribreq->prefix.af != AF_INET)
-				    && (ribreq->prefix.af != AF_INET6)) {
+				    && (ribreq->prefix.aid == AID_UNSPEC)) {
 					/* malformed request, must specify af */
 					control_result(c, CTL_RES_PARSE_ERROR);
 					break;
@@ -425,6 +438,20 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
 			imsg_compose_rde(imsg.hdr.type, 0,
 			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
 			break;
+		case IMSG_CTL_LOG_VERBOSE:
+			if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(verbose))
+				break;
+
+			/* forward to other processes */
+			imsg_compose_parent(imsg.hdr.type, 0, imsg.hdr.pid,
+			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+			imsg_compose_rde(imsg.hdr.type, 0,
+			    imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+
+			memcpy(&verbose, imsg.data, sizeof(verbose));
+			log_verbose(verbose);
+			break;
 		default:
 			break;
 		}
diff --git a/bgpd/imsg.c b/bgpd/imsg.c
deleted file mode 100644
index 3ba224e..0000000
--- a/bgpd/imsg.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*	$OpenBSD: imsg.c,v 1.47 2009/06/08 08:30:06 dlg Exp $	*/
-
-/*
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "imsg.h"
-
-int	 imsg_get_fd(struct imsgbuf *);
-
-void
-imsg_init(struct imsgbuf *ibuf, int fd)
-{
-	msgbuf_init(&ibuf->w);
-	bzero(&ibuf->r, sizeof(ibuf->r));
-	ibuf->fd = fd;
-	ibuf->w.fd = fd;
-	ibuf->pid = getpid();
-	TAILQ_INIT(&ibuf->fds);
-}
-
-ssize_t
-imsg_read(struct imsgbuf *ibuf)
-{
-	struct msghdr		 msg;
-	struct cmsghdr		*cmsg;
-	union {
-		struct cmsghdr hdr;
-		char	buf[CMSG_SPACE(sizeof(int) * 16)];
-	} cmsgbuf;
-	struct iovec		 iov;
-	ssize_t			 n;
-	int			 fd;
-	struct imsg_fd		*ifd;
-
-	bzero(&msg, sizeof(msg));
-
-	iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
-	iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-	msg.msg_control = &cmsgbuf.buf;
-	msg.msg_controllen = sizeof(cmsgbuf.buf);
-
-	if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
-		if (errno != EINTR && errno != EAGAIN) {
-			return (-1);
-		}
-		return (-2);
-	}
-
-	ibuf->r.wpos += n;
-
-	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
-	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-		if (cmsg->cmsg_level == SOL_SOCKET &&
-		    cmsg->cmsg_type == SCM_RIGHTS) {
-			fd = (*(int *)CMSG_DATA(cmsg));
-			if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) {
-				/* XXX: this return can leak */
-				return (-1);
-			}
-			ifd->fd = fd;
-			TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
-		}
-		/* we do not handle other ctl data level */
-	}
-
-	return (n);
-}
-
-ssize_t
-imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
-{
-	size_t			 av, left, datalen;
-
-	av = ibuf->r.wpos;
-
-	if (IMSG_HEADER_SIZE > av)
-		return (0);
-
-	memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
-	if (imsg->hdr.len < IMSG_HEADER_SIZE ||
-	    imsg->hdr.len > MAX_IMSGSIZE) {
-		errno = ERANGE;
-		return (-1);
-	}
-	if (imsg->hdr.len > av)
-		return (0);
-	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
-	ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
-	if ((imsg->data = malloc(datalen)) == NULL)
-		return (-1);
-
-	if (imsg->hdr.flags & IMSGF_HASFD)
-		imsg->fd = imsg_get_fd(ibuf);
-	else
-		imsg->fd = -1;
-
-	memcpy(imsg->data, ibuf->r.rptr, datalen);
-
-	if (imsg->hdr.len < av) {
-		left = av - imsg->hdr.len;
-		memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
-		ibuf->r.wpos = left;
-	} else
-		ibuf->r.wpos = 0;
-
-	return (datalen + IMSG_HEADER_SIZE);
-}
-
-int
-imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
-    pid_t pid, int fd, void *data, u_int16_t datalen)
-{
-	struct buf	*wbuf;
-
-	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
-		return (-1);
-
-	if (imsg_add(wbuf, data, datalen) == -1)
-		return (-1);
-
-	wbuf->fd = fd;
-
-	imsg_close(ibuf, wbuf);
-
-	return (1);
-}
-
-int
-imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
-    pid_t pid, int fd, const struct iovec *iov, int iovcnt)
-{
-	struct buf	*wbuf;
-	int		 i, datalen = 0;
-
-	for (i = 0; i < iovcnt; i++)
-		datalen += iov[i].iov_len;
-
-	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
-		return (-1);
-
-	for (i = 0; i < iovcnt; i++)
-		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
-			return (-1);
-
-	wbuf->fd = fd;
-
-	imsg_close(ibuf, wbuf);
-
-	return (1);
-}
-
-/* ARGSUSED */
-struct buf *
-imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
-    pid_t pid, u_int16_t datalen)
-{
-	struct buf	*wbuf;
-	struct imsg_hdr	 hdr;
-
-	datalen += IMSG_HEADER_SIZE;
-	if (datalen > MAX_IMSGSIZE) {
-		errno = ERANGE;
-		return (NULL);
-	}
-
-	hdr.type = type;
-	hdr.flags = 0;
-	hdr.peerid = peerid;
-	if ((hdr.pid = pid) == 0)
-		hdr.pid = ibuf->pid;
-	if ((wbuf = buf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
-		return (NULL);
-	}
-	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
-		return (NULL);
-
-	return (wbuf);
-}
-
-int
-imsg_add(struct buf *msg, void *data, u_int16_t datalen)
-{
-	if (datalen)
-		if (buf_add(msg, data, datalen) == -1) {
-			buf_free(msg);
-			return (-1);
-		}
-	return (datalen);
-}
-
-void
-imsg_close(struct imsgbuf *ibuf, struct buf *msg)
-{
-	struct imsg_hdr	*hdr;
-
-	hdr = (struct imsg_hdr *)msg->buf;
-
-	hdr->flags &= ~IMSGF_HASFD;
-	if (msg->fd != -1)
-		hdr->flags |= IMSGF_HASFD;
-
-	hdr->len = (u_int16_t)msg->wpos;
-
-	buf_close(&ibuf->w, msg);
-}
-
-void
-imsg_free(struct imsg *imsg)
-{
-	free(imsg->data);
-}
-
-int
-imsg_get_fd(struct imsgbuf *ibuf)
-{
-	int		 fd;
-	struct imsg_fd	*ifd;
-
-	if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
-		return (-1);
-
-	fd = ifd->fd;
-	TAILQ_REMOVE(&ibuf->fds, ifd, entry);
-	free(ifd);
-
-	return (fd);
-}
-
-int
-imsg_flush(struct imsgbuf *ibuf)
-{
-	while (ibuf->w.queued)
-		if (msgbuf_write(&ibuf->w) < 0)
-			return (-1);
-	return (0);
-}
-
-void
-imsg_clear(struct imsgbuf *ibuf)
-{
-	while (ibuf->w.queued)
-		msgbuf_clear(&ibuf->w);
-}
diff --git a/bgpd/imsg.h b/bgpd/imsg.h
deleted file mode 100644
index 8fa2e4f..0000000
--- a/bgpd/imsg.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*	$OpenBSD: imsg.h,v 1.3 2009/06/07 05:56:24 eric Exp $	*/
-
-/*
- * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/tree.h>
-
-#define READ_BUF_SIZE		65535
-#define IMSG_HEADER_SIZE	sizeof(struct imsg_hdr)
-#define MAX_IMSGSIZE		16384
-
-struct buf {
-	TAILQ_ENTRY(buf)	 entry;
-	u_char			*buf;
-	size_t			 size;
-	size_t			 max;
-	size_t			 wpos;
-	size_t			 rpos;
-	int			 fd;
-};
-
-struct msgbuf {
-	TAILQ_HEAD(, buf)	 bufs;
-	u_int32_t		 queued;
-	int			 fd;
-};
-
-struct buf_read {
-	u_char			 buf[READ_BUF_SIZE];
-	u_char			*rptr;
-	size_t			 wpos;
-};
-
-struct imsg_fd {
-	TAILQ_ENTRY(imsg_fd)	entry;
-	int			fd;
-};
-
-struct imsgbuf {
-	TAILQ_HEAD(, imsg_fd)	 fds;
-	struct buf_read		 r;
-	struct msgbuf		 w;
-	int			 fd;
-	pid_t			 pid;
-};
-
-#define IMSGF_HASFD	1
-
-struct imsg_hdr {
-	u_int32_t	 type;
-	u_int16_t	 len;
-	u_int16_t	 flags;
-	u_int32_t	 peerid;
-	u_int32_t	 pid;
-};
-
-struct imsg {
-	struct imsg_hdr	 hdr;
-	int		 fd;
-	void		*data;
-};
-
-
-/* buffer.c */
-struct buf	*buf_open(size_t);
-struct buf	*buf_dynamic(size_t, size_t);
-int		 buf_add(struct buf *, const void *, size_t);
-void		*buf_reserve(struct buf *, size_t);
-void		*buf_seek(struct buf *, size_t, size_t);
-size_t		 buf_size(struct buf *);
-size_t		 buf_left(struct buf *);
-void		 buf_close(struct msgbuf *, struct buf *);
-int		 buf_write(struct msgbuf *);
-void		 buf_free(struct buf *);
-void		 msgbuf_init(struct msgbuf *);
-void		 msgbuf_clear(struct msgbuf *);
-int		 msgbuf_write(struct msgbuf *);
-
-/* imsg.c */
-void	 imsg_init(struct imsgbuf *, int);
-ssize_t	 imsg_read(struct imsgbuf *);
-ssize_t	 imsg_get(struct imsgbuf *, struct imsg *);
-int	 imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
-	    int, void *, u_int16_t);
-int	 imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t,  pid_t,
-	    int, const struct iovec *, int);
-struct buf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
-	    u_int16_t);
-int	 imsg_add(struct buf *, void *, u_int16_t);
-void	 imsg_close(struct imsgbuf *, struct buf *);
-void	 imsg_free(struct imsg *);
-int	 imsg_flush(struct imsgbuf *);
-void	 imsg_clear(struct imsgbuf *);
diff --git a/bgpd/kroute.c b/bgpd/kroute.c
index d16cf3b..db8c8d4 100644
--- a/bgpd/kroute.c
+++ b/bgpd/kroute.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: kroute.c,v 1.169 2009/06/25 15:54:22 claudio Exp $ */
+/*	$OpenBSD: kroute.c,v 1.176 2010/04/06 13:25:08 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -27,6 +27,9 @@
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/route.h>
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+#include <netmpls/mpls.h>
+#endif
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -37,11 +40,12 @@
 
 #include "bgpd.h"
 
+struct ktable		**krt;
+u_int			  krt_size;
+
 struct {
 	u_int32_t		rtseq;
 	pid_t			pid;
-	u_int			rtableid;
-	int			fib_sync;
 	int			fd;
 } kr_state;
 
@@ -83,32 +87,52 @@ struct kif_node {
 	struct kif_kr6_head	 kroute6_l;
 };
 
-int	kr_redistribute(int, struct kroute *);
-int	kr_redistribute6(int, struct kroute6 *);
+int	ktable_new(u_int, u_int, char *, char *, int);
+void	ktable_free(u_int);
+void	ktable_destroy(struct ktable *);
+struct ktable	*ktable_get(u_int);
+
+int	kr4_change(struct ktable *, struct kroute_full *);
+int	kr6_change(struct ktable *, struct kroute_full *);
+int	krVPN4_change(struct ktable *, struct kroute_full *);
+int	kr4_delete(struct ktable *, struct kroute_full *);
+int	kr6_delete(struct ktable *, struct kroute_full *);
+int	krVPN4_delete(struct ktable *, struct kroute_full *);
+void	kr_net_delete(struct network *);
+struct network *kr_net_match(struct ktable *, struct kroute *);
+struct network *kr_net_match6(struct ktable *, struct kroute6 *);
+struct network *kr_net_find(struct ktable *, struct network *);
+int	kr_redistribute(int, struct ktable *, struct kroute *);
+int	kr_redistribute6(int, struct ktable *, struct kroute6 *);
+struct kroute_full *kr_tofull(struct kroute *);
+struct kroute_full *kr6_tofull(struct kroute6 *);
 int	kroute_compare(struct kroute_node *, struct kroute_node *);
 int	kroute6_compare(struct kroute6_node *, struct kroute6_node *);
 int	knexthop_compare(struct knexthop_node *, struct knexthop_node *);
 int	kif_compare(struct kif_node *, struct kif_node *);
 
-struct kroute_node	*kroute_find(in_addr_t, u_int8_t, u_int8_t);
+struct kroute_node	*kroute_find(struct ktable *, in_addr_t, u_int8_t,
+			    u_int8_t);
 struct kroute_node	*kroute_matchgw(struct kroute_node *,
 			    struct sockaddr_in *);
-int			 kroute_insert(struct kroute_node *);
-int			 kroute_remove(struct kroute_node *);
-void			 kroute_clear(void);
+int			 kroute_insert(struct ktable *, struct kroute_node *);
+int			 kroute_remove(struct ktable *, struct kroute_node *);
+void			 kroute_clear(struct ktable *);
 
-struct kroute6_node	*kroute6_find(const struct in6_addr *, u_int8_t,
-			     u_int8_t);
+struct kroute6_node	*kroute6_find(struct ktable *, const struct in6_addr *,
+			    u_int8_t, u_int8_t);
 struct kroute6_node	*kroute6_matchgw(struct kroute6_node *,
 			    struct sockaddr_in6 *);
-int			 kroute6_insert(struct kroute6_node *);
-int			 kroute6_remove(struct kroute6_node *);
-void			 kroute6_clear(void);
+int			 kroute6_insert(struct ktable *, struct kroute6_node *);
+int			 kroute6_remove(struct ktable *, struct kroute6_node *);
+void			 kroute6_clear(struct ktable *);
 
-struct knexthop_node	*knexthop_find(struct bgpd_addr *);
-int			 knexthop_insert(struct knexthop_node *);
-int			 knexthop_remove(struct knexthop_node *);
-void			 knexthop_clear(void);
+struct knexthop_node	*knexthop_find(struct ktable *, struct bgpd_addr *);
+int			 knexthop_insert(struct ktable *,
+			    struct knexthop_node *);
+int			 knexthop_remove(struct ktable *,
+			    struct knexthop_node *);
+void			 knexthop_clear(struct ktable *);
 
 struct kif_node		*kif_find(int);
 int			 kif_insert(struct kif_node *);
@@ -124,13 +148,15 @@ int			 kif_kr6_remove(struct kroute6_node *);
 int			 kif_validate(struct kif *);
 int			 kroute_validate(struct kroute *);
 int			 kroute6_validate(struct kroute6 *);
-void			 knexthop_validate(struct knexthop_node *);
-void			 knexthop_track(void *);
-struct kroute_node	*kroute_match(in_addr_t, int);
-struct kroute6_node	*kroute6_match(struct in6_addr *, int);
-void			 kroute_detach_nexthop(struct knexthop_node *);
-
-int		protect_lo(void);
+void			 knexthop_validate(struct ktable *,
+			    struct knexthop_node *);
+void			 knexthop_track(struct ktable *, void *);
+struct kroute_node	*kroute_match(struct ktable *, in_addr_t, int);
+struct kroute6_node	*kroute6_match(struct ktable *, struct in6_addr *, int);
+void			 kroute_detach_nexthop(struct ktable *,
+			    struct knexthop_node *);
+
+int		protect_lo(struct ktable *);
 u_int8_t	prefixlen_classful(in_addr_t);
 u_int8_t	mask2prefixlen(in_addr_t);
 u_int8_t	mask2prefixlen6(struct sockaddr_in6 *);
@@ -138,23 +164,20 @@ void		get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
 void		if_change(u_short, int, struct if_data *);
 void		if_announce(void *);
 
-int		send_rtmsg(int, int, struct kroute *);
-int		send_rt6msg(int, int, struct kroute6 *);
+int		send_rtmsg(int, int, struct ktable *, struct kroute *);
+int		send_rt6msg(int, int, struct ktable *, struct kroute6 *);
 int		dispatch_rtmsg(void);
-int		fetchtable(u_int, int);
+int		fetchtable(struct ktable *);
 int		fetchifs(int);
 int		dispatch_rtmsg_addr(struct rt_msghdr *,
-		    struct sockaddr *[RTAX_MAX], int);
+		    struct sockaddr *[RTAX_MAX], struct ktable *);
 
-RB_HEAD(kroute_tree, kroute_node)	krt;
 RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
 RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
 
-RB_HEAD(kroute6_tree, kroute6_node)	krt6;
 RB_PROTOTYPE(kroute6_tree, kroute6_node, entry, kroute6_compare)
 RB_GENERATE(kroute6_tree, kroute6_node, entry, kroute6_compare)
 
-RB_HEAD(knexthop_tree, knexthop_node)	knt;
 RB_PROTOTYPE(knexthop_tree, knexthop_node, entry, knexthop_compare)
 RB_GENERATE(knexthop_tree, knexthop_node, entry, knexthop_compare)
 
@@ -162,19 +185,21 @@ RB_HEAD(kif_tree, kif_node)		kit;
 RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
 RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
 
+#define KT2KNT(x)	(&(ktable_get((x)->nhtableid)->knt))
+
 /*
  * exported functions
  */
 
 int
-kr_init(int fs, u_int rtableid)
+kr_init(void)
 {
 	int		opt = 0, rcvbuf, default_rcvbuf;
+#if !defined(__FreeBSD__) /* FreeBSD does not have ROUTE_TABLEFILTER. */
+	unsigned int	tid = RTABLE_ANY;
+#endif
 	socklen_t	optlen;
 
-	kr_state.rtableid = rtableid;
-	kr_state.fib_sync = fs;
-
 	if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
 		log_warn("kr_init: socket");
 		return (-1);
@@ -198,194 +223,533 @@ kr_init(int fs, u_int rtableid)
 		    rcvbuf /= 2)
 			;	/* nothing */
 
+#if !defined(__FreeBSD__) /* FreeBSD does not have ROUTE_TABLEFILTER. */
+	if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_TABLEFILTER, &tid,
+	    sizeof(tid)) == -1) {
+		log_warn("kr_init: setsockopt AF_ROUTE ROUTE_TABLEFILTER");
+		return (-1);
+	}
+#endif
+
 	kr_state.pid = getpid();
 	kr_state.rtseq = 1;
 
-	RB_INIT(&krt);
-	RB_INIT(&krt6);
-	RB_INIT(&knt);
 	RB_INIT(&kit);
 
 	if (fetchifs(0) == -1)
 		return (-1);
 
-	if (fetchtable(kr_state.rtableid, 0) == -1)
-		return (-1);
-	if (kr_state.rtableid != 0)
-		if (fetchtable(0, 1) == -1)
+	return (kr_state.fd);
+}
+
+int
+ktable_new(u_int rtableid, u_int rdomid, char *name, char *ifname, int fs)
+{
+	struct ktable	**xkrt;
+	struct ktable	 *kt;
+	size_t		  newsize, oldsize;
+
+	/* resize index table if needed */
+	if (rtableid >= krt_size) {
+		oldsize = sizeof(struct ktable *) * krt_size;
+		newsize = sizeof(struct ktable *) * (rtableid + 1);
+		if ((xkrt = realloc(krt, newsize)) == NULL) {
+			log_warn("ktable_new");
 			return (-1);
+		}
+		krt = xkrt;
+		krt_size = rtableid + 1;
+		bzero((char *)krt + oldsize, newsize - oldsize);
+	}
+
+	if (krt[rtableid])
+		fatalx("ktable_new: table already exists.");
 
-	if (protect_lo() == -1)
+	/* allocate new element */
+	kt = krt[rtableid] = calloc(1, sizeof(struct ktable));
+	if (kt == NULL) {
+		log_warn("ktable_new");
 		return (-1);
+	}
 
-	return (kr_state.fd);
+	/* initialize structure ... */
+	strlcpy(kt->descr, name, sizeof(kt->descr));
+	RB_INIT(&kt->krt);
+	RB_INIT(&kt->krt6);
+	RB_INIT(&kt->knt);
+	TAILQ_INIT(&kt->krn);
+	kt->fib_conf = kt->fib_sync = fs;
+	kt->rtableid = rtableid;
+	kt->nhtableid = rdomid;
+	/* bump refcount of rdomain table for the nexthop lookups */
+	ktable_get(kt->nhtableid)->nhrefcnt++;
+	if (ifname) {
+		strlcpy(kt->ifmpe, ifname, IFNAMSIZ);
+		kt->ifindex = if_nametoindex(ifname);
+	}
+
+	/* ... and load it */
+	if (fetchtable(kt) == -1)
+		return (-1);
+	if (protect_lo(kt) == -1)
+		return (-1);
+
+	/* everything is up and running */
+	kt->state = RECONF_REINIT;
+	log_debug("new ktable %s for rtableid %d", name, rtableid);
+	return (0);
+}
+
+void
+ktable_free(u_int rtableid)
+{
+	struct ktable	*kt, *nkt;
+
+	if ((kt = ktable_get(rtableid)) == NULL)
+		return;
+
+	/* decouple from kernel, no new routes will be entered from here */
+	kr_fib_decouple(kt->rtableid);
+
+	/* first unhook from the nexthop table */
+	nkt = ktable_get(kt->nhtableid);
+	nkt->nhrefcnt--;
+
+	/*
+	 * Evil little details:
+	 *   If kt->nhrefcnt > 0 then kt == nkt and nothing needs to be done.
+	 *   If kt != nkt then kt->nhrefcnt must be 0 and kt must be killed.
+	 *   If nkt is no longer referenced it must be killed (possible double
+	 *   free so check that kt != nkt).
+	 */
+	if (kt != nkt && nkt->nhrefcnt <= 0)
+		ktable_destroy(nkt);
+	if (kt->nhrefcnt <= 0)
+		ktable_destroy(kt);
+}
+
+void
+ktable_destroy(struct ktable *kt)
+{
+	/* decouple just to be sure, does not hurt */
+	kr_fib_decouple(kt->rtableid);
+
+	log_debug("freeing ktable %s rtableid %u", kt->descr, kt->rtableid);
+	knexthop_clear(kt);
+	kroute_clear(kt);
+	kroute6_clear(kt);
+
+	krt[kt->rtableid] = NULL;
+	free(kt);
+}
+
+struct ktable *
+ktable_get(u_int rtableid)
+{
+	if (rtableid >= krt_size)
+		return (NULL);
+	return (krt[rtableid]);
 }
 
 int
-kr_change(struct kroute_label *kl)
+ktable_update(u_int rtableid, char *name, char *ifname, int flags)
+{
+	struct ktable	*kt, *rkt;
+	u_int		 rdomid;
+
+	if (!ktable_exists(rtableid, &rdomid))
+		fatalx("King Bula lost a table");	/* may not happen */
+
+	if (rdomid != rtableid || flags & F_RIB_NOFIB) {
+		rkt = ktable_get(rdomid);
+		if (rkt == NULL) {
+			char buf[32];
+			snprintf(buf, sizeof(buf), "rdomain_%d", rdomid);
+			if (ktable_new(rdomid, rdomid, buf, NULL, 0))
+				return (-1);
+		} else {
+			/* there is no need for full fib synchronisation if
+			 * the table is only used for nexthop lookups.
+			 */
+			if (rkt->state == RECONF_DELETE) {
+				rkt->fib_conf = 0;
+				rkt->state = RECONF_KEEP;
+			}
+		}
+	}
+
+	if (flags & (F_RIB_NOEVALUATE | F_RIB_NOFIB))
+		/* only rdomain table must exist */
+		return (0);
+
+	kt = ktable_get(rtableid);
+	if (kt == NULL) {
+		if (ktable_new(rtableid, rdomid, name, ifname,
+		    !(flags & F_RIB_NOFIBSYNC)))
+			return (-1);
+	} else {
+		/* fib sync has higher preference then no sync */
+		if (kt->state == RECONF_DELETE) {
+			kt->fib_conf = !(flags & F_RIB_NOFIBSYNC);
+			kt->state = RECONF_KEEP;
+		} else if (!kt->fib_conf)
+			kt->fib_conf = !(flags & F_RIB_NOFIBSYNC);
+
+		strlcpy(kt->descr, name, sizeof(kt->descr));
+	}
+	return (0);
+}
+
+void
+ktable_preload(void)
+{
+	struct ktable 	*kt;
+	u_int		 i;
+
+	for (i = 0; i < krt_size; i++) {
+		if ((kt = ktable_get(i)) == NULL)
+			continue;
+		kt->state = RECONF_DELETE;
+	}
+}
+
+void
+ktable_postload(void)
+{
+	struct ktable 	*kt;
+	u_int		 i;
+
+	for (i = krt_size; i > 0; i--) {
+		if ((kt = ktable_get(i - 1)) == NULL)
+			continue;
+		if (kt->state == RECONF_DELETE)
+			ktable_free(i - 1);
+		else if (kt->state == RECONF_REINIT)
+			kt->fib_sync = kt->fib_conf;
+	}
+}
+
+int
+ktable_exists(u_int rtableid, u_int *rdomid)
+{
+#if !defined(__FreeBSD__) /* FreeBSD does not have NET_RT_TABLE. */
+	size_t			 len;
+	struct rt_tableinfo	 info;
+	int			 mib[6];
+
+	mib[0] = CTL_NET;
+	mib[1] = AF_ROUTE;
+	mib[2] = 0;
+	mib[3] = 0;
+	mib[4] = NET_RT_TABLE;
+	mib[5] = rtableid;
+
+	len = sizeof(info);
+	if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) {
+		if (errno == ENOENT)
+			/* table nonexistent */
+			return (0);
+		log_warn("sysctl");
+		/* must return 0 so that the table is considered non-existent */
+		return (0);
+	}
+	if (rdomid)
+		*rdomid = info.rti_domainid;
+#else
+	*rdomid = 0;
+#endif
+	return (1);
+}
+
+int
+kr_change(u_int rtableid, struct kroute_full *kl)
+{
+	struct ktable		*kt;
+
+	if ((kt = ktable_get(rtableid)) == NULL)
+		/* too noisy during reloads, just ignore */
+		return (0);
+	switch (kl->prefix.aid) {
+	case AID_INET:
+		return (kr4_change(kt, kl));
+	case AID_INET6:
+		return (kr6_change(kt, kl));
+	case AID_VPN_IPv4:
+		return (krVPN4_change(kt, kl));
+	}
+	log_warnx("kr_change: not handled AID");
+	return (-1);
+}
+
+int
+kr4_change(struct ktable *kt, struct kroute_full *kl)
 {
 	struct kroute_node	*kr;
 	int			 action = RTM_ADD;
+	u_int16_t		 labelid;
 
-	if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
-	    != NULL)
+	if ((kr = kroute_find(kt, kl->prefix.v4.s_addr, kl->prefixlen,
+	    RTP_BGP)) != NULL)
 		action = RTM_CHANGE;
 
 	/* nexthop within 127/8 -> ignore silently */
-	if ((kl->kr.nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
+	if ((kl->nexthop.v4.s_addr & htonl(IN_CLASSA_NET)) ==
 	    htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
 		return (0);
 
-	if (kr)
-		rtlabel_unref(kr->r.labelid);
-	kl->kr.labelid = rtlabel_name2id(kl->label);
+	labelid = rtlabel_name2id(kl->label);
 
 	/* for blackhole and reject routes nexthop needs to be 127.0.0.1 */
-	if (kl->kr.flags & (F_BLACKHOLE|F_REJECT))
-		kl->kr.nexthop.s_addr = htonl(INADDR_LOOPBACK);
-
-	if (send_rtmsg(kr_state.fd, action, &kl->kr) == -1)
-		return (-1);
+	if (kl->flags & (F_BLACKHOLE|F_REJECT))
+		kl->nexthop.v4.s_addr = htonl(INADDR_LOOPBACK);
 
 	if (action == RTM_ADD) {
 		if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
 			log_warn("kr_change");
 			return (-1);
 		}
-		kr->r.prefix.s_addr = kl->kr.prefix.s_addr;
-		kr->r.prefixlen = kl->kr.prefixlen;
-		kr->r.nexthop.s_addr = kl->kr.nexthop.s_addr;
-		kr->r.flags = kl->kr.flags | F_BGPD_INSERTED;
+		kr->r.prefix.s_addr = kl->prefix.v4.s_addr;
+		kr->r.prefixlen = kl->prefixlen;
+		kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+		kr->r.flags = kl->flags | F_BGPD_INSERTED;
 		kr->r.priority = RTP_BGP;
-		kr->r.labelid = kl->kr.labelid;
+		kr->r.labelid = labelid;
 
-		if (kroute_insert(kr) == -1)
+		if (kroute_insert(kt, kr) == -1)
 			free(kr);
 	} else {
-		kr->r.nexthop.s_addr = kl->kr.nexthop.s_addr;
-		kr->r.labelid = kl->kr.labelid;
-		if (kl->kr.flags & F_BLACKHOLE)
+		kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+		rtlabel_unref(kr->r.labelid);
+		kr->r.labelid = labelid;
+		if (kl->flags & F_BLACKHOLE)
 			kr->r.flags |= F_BLACKHOLE;
 		else
 			kr->r.flags &= ~F_BLACKHOLE;
-		if (kl->kr.flags & F_REJECT)
+		if (kl->flags & F_REJECT)
 			kr->r.flags |= F_REJECT;
 		else
 			kr->r.flags &= ~F_REJECT;
 	}
 
+	if (send_rtmsg(kr_state.fd, action, kt, &kr->r) == -1)
+		return (-1);
+
 	return (0);
 }
 
 int
-kr_delete(struct kroute_label *kl)
+kr6_change(struct ktable *kt, struct kroute_full *kl)
 {
-	struct kroute_node	*kr;
+	struct kroute6_node	*kr6;
+	struct in6_addr		 lo6 = IN6ADDR_LOOPBACK_INIT;
+	int			 action = RTM_ADD;
+	u_int16_t		 labelid;
 
-	if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
-	    == NULL)
-		return (0);
+	if ((kr6 = kroute6_find(kt, &kl->prefix.v6, kl->prefixlen, RTP_BGP)) !=
+	    NULL)
+		action = RTM_CHANGE;
 
-	if (!(kr->r.flags & F_BGPD_INSERTED))
+	/* nexthop to loopback -> ignore silently */
+	if (IN6_IS_ADDR_LOOPBACK(&kl->nexthop.v6))
 		return (0);
 
-	/* nexthop within 127/8 -> ignore silently */
-	if ((kl->kr.nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
-	    htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
-		return (0);
+	labelid = rtlabel_name2id(kl->label);
 
-	if (send_rtmsg(kr_state.fd, RTM_DELETE, &kl->kr) == -1)
-		return (-1);
+	/* for blackhole and reject routes nexthop needs to be ::1 */
+	if (kl->flags & (F_BLACKHOLE|F_REJECT))
+		bcopy(&lo6, &kl->nexthop.v6, sizeof(kl->nexthop.v6));
+
+	if (action == RTM_ADD) {
+		if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) {
+			log_warn("kr_change");
+			return (-1);
+		}
+		memcpy(&kr6->r.prefix, &kl->prefix.v6, sizeof(struct in6_addr));
+		kr6->r.prefixlen = kl->prefixlen;
+		memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+		    sizeof(struct in6_addr));
+		kr6->r.flags = kl->flags | F_BGPD_INSERTED;
+		kr6->r.priority = RTP_BGP;
+		kr6->r.labelid = labelid;
 
-	rtlabel_unref(kl->kr.labelid);
+		if (kroute6_insert(kt, kr6) == -1)
+			free(kr6);
+	} else {
+		memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+		    sizeof(struct in6_addr));
+		rtlabel_unref(kr6->r.labelid);
+		kr6->r.labelid = labelid;
+		if (kl->flags & F_BLACKHOLE)
+			kr6->r.flags |= F_BLACKHOLE;
+		else
+			kr6->r.flags &= ~F_BLACKHOLE;
+		if (kl->flags & F_REJECT)
+			kr6->r.flags |= F_REJECT;
+		else
+			kr6->r.flags &= ~F_REJECT;
+	}
 
-	if (kroute_remove(kr) == -1)
+	if (send_rt6msg(kr_state.fd, action, kt, &kr6->r) == -1)
 		return (-1);
 
 	return (0);
 }
 
 int
-kr6_change(struct kroute6_label *kl)
+krVPN4_change(struct ktable *kt, struct kroute_full *kl)
 {
-	struct kroute6_node	*kr6;
+	struct kroute_node	*kr;
 	int			 action = RTM_ADD;
-	struct in6_addr		 lo6 = IN6ADDR_LOOPBACK_INIT;
+	u_int32_t		 mplslabel = 0;
+	u_int16_t		 labelid;
 
-	if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
-	     != NULL)
+	if ((kr = kroute_find(kt, kl->prefix.vpn4.addr.s_addr, kl->prefixlen,
+	    RTP_BGP)) != NULL)
 		action = RTM_CHANGE;
 
-	/* nexthop to loopback -> ignore silently */
-	if (IN6_IS_ADDR_LOOPBACK(&kl->kr.nexthop))
+	/* nexthop within 127/8 -> ignore silently */
+	if ((kl->nexthop.v4.s_addr & htonl(IN_CLASSA_NET)) ==
+	    htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
 		return (0);
 
-	if (kr6)
-		rtlabel_unref(kr6->r.labelid);
-	kl->kr.labelid = rtlabel_name2id(kl->label);
+	/* only single MPLS label are supported for now */
+	if (kl->prefix.vpn4.labellen != 3) {
+		log_warnx("krVPN4_change: %s/%u has not a single label",
+		    log_addr(&kl->prefix), kl->prefixlen);
+		return (0);
+	}
+	mplslabel = (kl->prefix.vpn4.labelstack[0] << 24) |
+	    (kl->prefix.vpn4.labelstack[1] << 16) |
+	    (kl->prefix.vpn4.labelstack[2] << 8);
+	mplslabel = htonl(mplslabel);
 
-	/* for blackhole and reject routes nexthop needs to be ::1 */
-	if (kl->kr.flags & (F_BLACKHOLE|F_REJECT))
-		bcopy(&lo6, &kl->kr.nexthop, sizeof(kl->kr.nexthop));
+	labelid = rtlabel_name2id(kl->label);
 
-	if (send_rt6msg(kr_state.fd, action, &kl->kr) == -1)
-		return (-1);
+	/* for blackhole and reject routes nexthop needs to be 127.0.0.1 */
+	if (kl->flags & (F_BLACKHOLE|F_REJECT))
+		kl->nexthop.v4.s_addr = htonl(INADDR_LOOPBACK);
 
 	if (action == RTM_ADD) {
-		if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) {
+		if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
 			log_warn("kr_change");
 			return (-1);
 		}
-		memcpy(&kr6->r.prefix, &kl->kr.prefix,
-		    sizeof(struct in6_addr));
-		kr6->r.prefixlen = kl->kr.prefixlen;
-		memcpy(&kr6->r.nexthop, &kl->kr.nexthop,
-		    sizeof(struct in6_addr));
-		kr6->r.flags = kl->kr.flags | F_BGPD_INSERTED;
-		kr6->r.priority = RTP_BGP;
-		kr6->r.labelid = kl->kr.labelid;
+		kr->r.prefix.s_addr = kl->prefix.vpn4.addr.s_addr;
+		kr->r.prefixlen = kl->prefixlen;
+		kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+		kr->r.flags = kl->flags | F_BGPD_INSERTED | F_MPLS;
+		kr->r.priority = RTP_BGP;
+		kr->r.labelid = labelid;
+		kr->r.mplslabel = mplslabel;
 
-		if (kroute6_insert(kr6) == -1)
-			free(kr6);
+		if (kroute_insert(kt, kr) == -1)
+			free(kr);
 	} else {
-		memcpy(&kr6->r.nexthop, &kl->kr.nexthop,
-		    sizeof(struct in6_addr));
-		kr6->r.labelid = kl->kr.labelid;
-		if (kl->kr.flags & F_BLACKHOLE)
-			kr6->r.flags |= F_BLACKHOLE;
+		kr->r.mplslabel = mplslabel;
+		kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+		rtlabel_unref(kr->r.labelid);
+		kr->r.labelid = labelid;
+		if (kl->flags & F_BLACKHOLE)
+			kr->r.flags |= F_BLACKHOLE;
 		else
-			kr6->r.flags &= ~F_BLACKHOLE;
-		if (kl->kr.flags & F_REJECT)
-			kr6->r.flags |= F_REJECT;
+			kr->r.flags &= ~F_BLACKHOLE;
+		if (kl->flags & F_REJECT)
+			kr->r.flags |= F_REJECT;
 		else
-			kr6->r.flags &= ~F_REJECT;
+			kr->r.flags &= ~F_REJECT;
 	}
 
+	if (send_rtmsg(kr_state.fd, action, kt, &kr->r) == -1)
+		return (-1);
+
 	return (0);
 }
 
 int
-kr6_delete(struct kroute6_label *kl)
+kr_delete(u_int rtableid, struct kroute_full *kl)
+{
+	struct ktable		*kt;
+
+	if ((kt = ktable_get(rtableid)) == NULL)
+		/* too noisy during reloads, just ignore */
+		return (0);
+
+	switch (kl->prefix.aid) {
+	case AID_INET:
+		return (kr4_delete(kt, kl));
+	case AID_INET6:
+		return (kr6_delete(kt, kl));
+	case AID_VPN_IPv4:
+		return (krVPN4_delete(kt, kl));
+	}
+	log_warnx("kr_change: not handled AID");
+	return (-1);
+}
+
+int
+kr4_delete(struct ktable *kt, struct kroute_full *kl)
+{
+	struct kroute_node	*kr;
+
+	if ((kr = kroute_find(kt, kl->prefix.v4.s_addr, kl->prefixlen,
+	    RTP_BGP)) == NULL)
+		return (0);
+
+	if (!(kr->r.flags & F_BGPD_INSERTED))
+		return (0);
+
+	if (send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r) == -1)
+		return (-1);
+
+	rtlabel_unref(kr->r.labelid);
+
+	if (kroute_remove(kt, kr) == -1)
+		return (-1);
+
+	return (0);
+}
+
+int
+kr6_delete(struct ktable *kt, struct kroute_full *kl)
 {
 	struct kroute6_node	*kr6;
 
-	if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
-	    == NULL)
+	if ((kr6 = kroute6_find(kt, &kl->prefix.v6, kl->prefixlen, RTP_BGP)) ==
+	    NULL)
 		return (0);
 
 	if (!(kr6->r.flags & F_BGPD_INSERTED))
 		return (0);
 
-	/* nexthop to loopback -> ignore silently */
-	if (IN6_IS_ADDR_LOOPBACK(&kl->kr.nexthop))
+	if (send_rt6msg(kr_state.fd, RTM_DELETE, kt, &kr6->r) == -1)
+		return (-1);
+
+	rtlabel_unref(kr6->r.labelid);
+
+	if (kroute6_remove(kt, kr6) == -1)
+		return (-1);
+
+	return (0);
+}
+
+int
+krVPN4_delete(struct ktable *kt, struct kroute_full *kl)
+{
+	struct kroute_node	*kr;
+
+	if ((kr = kroute_find(kt, kl->prefix.vpn4.addr.s_addr, kl->prefixlen,
+	    RTP_BGP)) == NULL)
 		return (0);
 
-	if (send_rt6msg(kr_state.fd, RTM_DELETE, &kl->kr) == -1)
+	if (!(kr->r.flags & F_BGPD_INSERTED))
+		return (0);
+
+	if (send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r) == -1)
 		return (-1);
 
-	rtlabel_unref(kl->kr.labelid);
+	rtlabel_unref(kr->r.labelid);
 
-	if (kroute6_remove(kr6) == -1)
+	if (kroute_remove(kt, kr) == -1)
 		return (-1);
 
 	return (0);
@@ -394,53 +758,63 @@ kr6_delete(struct kroute6_label *kl)
 void
 kr_shutdown(void)
 {
-	kr_fib_decouple();
-	knexthop_clear();
-	kroute_clear();
-	kroute6_clear();
+	u_int	i;
+
+	for (i = krt_size; i > 0; i--)
+		ktable_free(i - 1);
 	kif_clear();
 }
 
 void
-kr_fib_couple(void)
+kr_fib_couple(u_int rtableid)
 {
+	struct ktable		*kt;
 	struct kroute_node	*kr;
 	struct kroute6_node	*kr6;
 
-	if (kr_state.fib_sync == 1)	/* already coupled */
+	if ((kt = ktable_get(rtableid)) == NULL)  /* table does not exist */
 		return;
 
-	kr_state.fib_sync = 1;
+	if (kt->fib_sync)	/* already coupled */
+		return;
 
-	RB_FOREACH(kr, kroute_tree, &krt)
+	kt->fib_sync = 1;
+
+	RB_FOREACH(kr, kroute_tree, &kt->krt)
 		if ((kr->r.flags & F_BGPD_INSERTED))
-			send_rtmsg(kr_state.fd, RTM_ADD, &kr->r);
-	RB_FOREACH(kr6, kroute6_tree, &krt6)
+			send_rtmsg(kr_state.fd, RTM_ADD, kt, &kr->r);
+	RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
 		if ((kr6->r.flags & F_BGPD_INSERTED))
-			send_rt6msg(kr_state.fd, RTM_ADD, &kr6->r);
+			send_rt6msg(kr_state.fd, RTM_ADD, kt, &kr6->r);
 
-	log_info("kernel routing table coupled");
+	log_info("kernel routing table %u (%s) coupled", kt->rtableid,
+	    kt->descr);
 }
 
 void
-kr_fib_decouple(void)
+kr_fib_decouple(u_int rtableid)
 {
+	struct ktable		*kt;
 	struct kroute_node	*kr;
 	struct kroute6_node	*kr6;
 
-	if (kr_state.fib_sync == 0)	/* already decoupled */
+	if ((kt = ktable_get(rtableid)) == NULL)  /* table does not exist */
+		return;
+
+	if (!kt->fib_sync)	/* already decoupled */
 		return;
 
-	RB_FOREACH(kr, kroute_tree, &krt)
+	RB_FOREACH(kr, kroute_tree, &kt->krt)
 		if ((kr->r.flags & F_BGPD_INSERTED))
-			send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
-	RB_FOREACH(kr6, kroute6_tree, &krt6)
+			send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r);
+	RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
 		if ((kr6->r.flags & F_BGPD_INSERTED))
-			send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
+			send_rt6msg(kr_state.fd, RTM_DELETE, kt, &kr6->r);
 
-	kr_state.fib_sync = 0;
+	kt->fib_sync = 0;
 
-	log_info("kernel routing table decoupled");
+	log_info("kernel routing table %u (%s) decoupled", kt->rtableid,
+	    kt->descr);
 }
 
 int
@@ -450,11 +824,16 @@ kr_dispatch_msg(void)
 }
 
 int
-kr_nexthop_add(struct bgpd_addr *addr)
+kr_nexthop_add(u_int rtableid, struct bgpd_addr *addr)
 {
+	struct ktable		*kt;
 	struct knexthop_node	*h;
 
-	if ((h = knexthop_find(addr)) != NULL) {
+	if ((kt = ktable_get(rtableid)) == NULL) {
+		log_warnx("kr_nexthop_add: non-existent rtableid %d", rtableid);
+		return (0);
+	}
+	if ((h = knexthop_find(kt, addr)) != NULL) {
 		/* should not happen... this is actually an error path */
 		struct kroute_nexthop	 nh;
 		struct kroute_node	*k;
@@ -463,25 +842,30 @@ kr_nexthop_add(struct bgpd_addr *addr)
 		bzero(&nh, sizeof(nh));
 		memcpy(&nh.nexthop, addr, sizeof(nh.nexthop));
 		nh.valid = 1;
-		if (h->kroute != NULL && addr->af == AF_INET) {
+		if (h->kroute != NULL && addr->aid == AID_INET) {
 			k = h->kroute;
 			nh.connected = k->r.flags & F_CONNECTED;
 			if (k->r.nexthop.s_addr != 0) {
-				nh.gateway.af = AF_INET;
+				nh.gateway.aid = AID_INET;
 				nh.gateway.v4.s_addr =
 				    k->r.nexthop.s_addr;
 			}
-			memcpy(&nh.kr.kr4, &k->r, sizeof(nh.kr.kr4));
-		} else if (h->kroute != NULL && addr->af == AF_INET6) {
+			nh.net.aid = AID_INET;
+			nh.net.v4.s_addr = k->r.prefix.s_addr;
+			nh.netlen = k->r.prefixlen;
+		} else if (h->kroute != NULL && addr->aid == AID_INET6) {
 			k6 = h->kroute;
 			nh.connected = k6->r.flags & F_CONNECTED;
 			if (memcmp(&k6->r.nexthop, &in6addr_any,
 			    sizeof(struct in6_addr)) != 0) {
-				nh.gateway.af = AF_INET6;
+				nh.gateway.aid = AID_INET6;
 				memcpy(&nh.gateway.v6, &k6->r.nexthop,
 				    sizeof(struct in6_addr));
 			}
-			memcpy(&nh.kr.kr6, &k6->r, sizeof(nh.kr.kr6));
+			nh.net.aid = AID_INET6;
+			memcpy(&nh.net.v6, &k6->r.nexthop,
+			    sizeof(struct in6_addr));
+			nh.netlen = k6->r.prefixlen;
 		}
 
 		send_nexthop_update(&nh);
@@ -492,7 +876,7 @@ kr_nexthop_add(struct bgpd_addr *addr)
 		}
 		memcpy(&h->nexthop, addr, sizeof(h->nexthop));
 
-		if (knexthop_insert(h) == -1)
+		if (knexthop_insert(kt, h) == -1)
 			return (-1);
 	}
 
@@ -500,19 +884,25 @@ kr_nexthop_add(struct bgpd_addr *addr)
 }
 
 void
-kr_nexthop_delete(struct bgpd_addr *addr)
+kr_nexthop_delete(u_int rtableid, struct bgpd_addr *addr)
 {
+	struct ktable		*kt;
 	struct knexthop_node	*kn;
 
-	if ((kn = knexthop_find(addr)) == NULL)
+	if ((kt = ktable_get(rtableid)) == NULL) {
+		log_warnx("kr_nexthop_add: non-existent rtableid %d", rtableid);
+		return;
+	}
+	if ((kn = knexthop_find(kt, addr)) == NULL)
 		return;
 
-	knexthop_remove(kn);
+	knexthop_remove(kt, kn);
 }
 
 void
 kr_show_route(struct imsg *imsg)
 {
+	struct ktable		*kt;
 	struct kroute_node	*kr, *kn;
 	struct kroute6_node	*kr6, *kn6;
 	struct bgpd_addr	*addr;
@@ -521,6 +911,7 @@ kr_show_route(struct imsg *imsg)
 	struct ctl_show_nexthop	 snh;
 	struct knexthop_node	*h;
 	struct kif_node		*kif;
+	u_int			 i;
 	u_short			 ifindex = 0;
 
 	switch (imsg->hdr.type) {
@@ -528,70 +919,96 @@ kr_show_route(struct imsg *imsg)
 		if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags) +
 		    sizeof(af)) {
 			log_warnx("kr_show_route: wrong imsg len");
-			return;
+			break;
+		}
+		kt = ktable_get(imsg->hdr.peerid);
+		if (kt == NULL) {
+			log_warnx("kr_show_route: table %u does not exist",
+			    imsg->hdr.peerid);
+			break;
 		}
 		memcpy(&flags, imsg->data, sizeof(flags));
 		memcpy(&af, (char *)imsg->data + sizeof(flags), sizeof(af));
 		if (!af || af == AF_INET)
-			RB_FOREACH(kr, kroute_tree, &krt)
-				if (!flags || kr->r.flags & flags) {
-					kn = kr;
-					do {
-						send_imsg_session(
-						    IMSG_CTL_KROUTE,
-						    imsg->hdr.pid, &kn->r,
-						    sizeof(kn->r));
-					} while ((kn = kn->next) != NULL);
-				}
+			RB_FOREACH(kr, kroute_tree, &kt->krt) {
+				if (flags && (kr->r.flags & flags) == 0)
+					continue;
+				kn = kr;
+				do {
+					send_imsg_session(IMSG_CTL_KROUTE,
+					    imsg->hdr.pid, kr_tofull(&kn->r),
+					    sizeof(struct kroute_full));
+				} while ((kn = kn->next) != NULL);
+			}
 		if (!af || af == AF_INET6)
-			RB_FOREACH(kr6, kroute6_tree, &krt6)
-				if (!flags || kr6->r.flags & flags) {
-					kn6 = kr6;
-					do {
-						send_imsg_session(
-						    IMSG_CTL_KROUTE6,
-						    imsg->hdr.pid, &kn6->r,
-						    sizeof(kn6->r));
-					} while ((kn6 = kn6->next) != NULL);
-				}
+			RB_FOREACH(kr6, kroute6_tree, &kt->krt6) {
+				if (flags && (kr6->r.flags & flags) == 0)
+					continue;
+				kn6 = kr6;
+				do {
+					send_imsg_session(IMSG_CTL_KROUTE,
+					    imsg->hdr.pid, kr6_tofull(&kn6->r),
+					    sizeof(struct kroute_full));
+				} while ((kn6 = kn6->next) != NULL);
+			}
 		break;
 	case IMSG_CTL_KROUTE_ADDR:
 		if (imsg->hdr.len != IMSG_HEADER_SIZE +
 		    sizeof(struct bgpd_addr)) {
 			log_warnx("kr_show_route: wrong imsg len");
-			return;
+			break;
+		}
+		kt = ktable_get(imsg->hdr.peerid);
+		if (kt == NULL) {
+			log_warnx("kr_show_route: table %u does not exist",
+			    imsg->hdr.peerid);
+			break;
 		}
 		addr = imsg->data;
 		kr = NULL;
-		switch (addr->af) {
-		case AF_INET:
-			kr = kroute_match(addr->v4.s_addr, 1);
+		switch (addr->aid) {
+		case AID_INET:
+			kr = kroute_match(kt, addr->v4.s_addr, 1);
 			if (kr != NULL)
 				send_imsg_session(IMSG_CTL_KROUTE,
-				    imsg->hdr.pid, &kr->r, sizeof(kr->r));
+				    imsg->hdr.pid, kr_tofull(&kr->r),
+				    sizeof(struct kroute_full));
 			break;
-		case AF_INET6:
-			kr6 = kroute6_match(&addr->v6, 1);
+		case AID_INET6:
+			kr6 = kroute6_match(kt, &addr->v6, 1);
 			if (kr6 != NULL)
-				send_imsg_session(IMSG_CTL_KROUTE6,
-				    imsg->hdr.pid, &kr6->r, sizeof(kr6->r));
+				send_imsg_session(IMSG_CTL_KROUTE,
+				    imsg->hdr.pid, kr6_tofull(&kr6->r),
+				    sizeof(struct kroute_full));
 			break;
 		}
 		break;
 	case IMSG_CTL_SHOW_NEXTHOP:
-		RB_FOREACH(h, knexthop_tree, &knt) {
+		kt = ktable_get(imsg->hdr.peerid);
+		if (kt == NULL) {
+			log_warnx("kr_show_route: table %u does not exist",
+			    imsg->hdr.peerid);
+			break;
+		}
+		RB_FOREACH(h, knexthop_tree, KT2KNT(kt)) {
 			bzero(&snh, sizeof(snh));
 			memcpy(&snh.addr, &h->nexthop, sizeof(snh.addr));
 			if (h->kroute != NULL) {
-				switch (h->nexthop.af) {
-				case AF_INET:
+				switch (h->nexthop.aid) {
+				case AID_INET:
 					kr = h->kroute;
 					snh.valid = kroute_validate(&kr->r);
+					snh.krvalid = 1;
+					memcpy(&snh.kr.kr4, &kr->r,
+					    sizeof(snh.kr.kr4));
 					ifindex = kr->r.ifindex;
 					break;
-				case AF_INET6:
+				case AID_INET6:
 					kr6 = h->kroute;
 					snh.valid = kroute6_validate(&kr6->r);
+					snh.krvalid = 1;
+					memcpy(&snh.kr.kr6, &kr6->r,
+					    sizeof(snh.kr.kr6));
 					ifindex = kr6->r.ifindex;
 					break;
 				}
@@ -608,6 +1025,24 @@ kr_show_route(struct imsg *imsg)
 			send_imsg_session(IMSG_CTL_SHOW_INTERFACE,
 			    imsg->hdr.pid, &kif->k, sizeof(kif->k));
 		break;
+	case IMSG_CTL_SHOW_FIB_TABLES:
+		for (i = 0; i < krt_size; i++) {
+			struct ktable	ktab;
+
+			if ((kt = ktable_get(i)) == NULL)
+				continue;
+
+			ktab = *kt;
+			/* do not leak internal information */
+			RB_INIT(&ktab.krt);
+			RB_INIT(&ktab.krt6);
+			RB_INIT(&ktab.knt);
+			TAILQ_INIT(&ktab.krn);
+
+			send_imsg_session(IMSG_CTL_SHOW_FIB_TABLES,
+			    imsg->hdr.pid, &ktab, sizeof(ktab));
+		}
+		break;
 	default:	/* nada */
 		break;
 	}
@@ -628,21 +1063,146 @@ kr_ifinfo(char *ifname)
 		}
 }
 
-struct redist_node {
-	LIST_ENTRY(redist_node)	 entry;
-	struct kroute		*kr;
-	struct kroute6		*kr6;
-};
+void
+kr_net_delete(struct network *n)
+{
+	filterset_free(&n->net.attrset);
+	free(n);
+}
+
+struct network *
+kr_net_match(struct ktable *kt, struct kroute *kr)
+{
+	struct network		*xn;
+
+	TAILQ_FOREACH(xn, &kt->krn, entry) {
+		if (xn->net.prefix.aid != AID_INET)
+			continue;
+		switch (xn->net.type) {
+		case NETWORK_DEFAULT:
+			if (xn->net.prefixlen == kr->prefixlen &&
+			    xn->net.prefix.v4.s_addr == kr->prefix.s_addr)
+				/* static match already redistributed */
+				return (NULL);
+			break;
+		case NETWORK_STATIC:
+			if (kr->flags & F_STATIC)
+				return (xn);
+			break;
+		case NETWORK_CONNECTED:
+			if (kr->flags & F_CONNECTED)
+				return (xn);
+			break;
+		}
+	}
+	return (NULL);
+}
 
+struct network *
+kr_net_match6(struct ktable *kt, struct kroute6 *kr6)
+{
+	struct network		*xn;
+
+	TAILQ_FOREACH(xn, &kt->krn, entry) {
+		if (xn->net.prefix.aid != AID_INET6)
+			continue;
+		switch (xn->net.type) {
+		case NETWORK_DEFAULT:
+			if (xn->net.prefixlen == kr6->prefixlen &&
+			    memcmp(&xn->net.prefix.v6, &kr6->prefix,
+			    sizeof(struct in6_addr)) == 0)
+				/* static match already redistributed */
+				return (NULL);
+			break;
+		case NETWORK_STATIC:
+			if (kr6->flags & F_STATIC)
+				return (xn);
+			break;
+		case NETWORK_CONNECTED:
+			if (kr6->flags & F_CONNECTED)
+				return (xn);
+			break;
+		}
+	}
+	return (NULL);
+}
 
-LIST_HEAD(, redist_node) redistlist;
+struct network *
+kr_net_find(struct ktable *kt, struct network *n)
+{
+	struct network		*xn;
+
+	TAILQ_FOREACH(xn, &kt->krn, entry) {
+		if (n->net.type != xn->net.type ||
+		    n->net.prefixlen != xn->net.prefixlen ||
+		    n->net.rtableid != xn->net.rtableid)
+			continue;
+		if (memcmp(&n->net.prefix, &xn->net.prefix,
+		    sizeof(n->net.prefix)) == 0)
+			return (xn);
+	}
+	return (NULL);
+}
+
+int
+kr_net_reload(u_int rtableid, struct network_head *nh)
+{
+	struct network		*n, *xn;
+	struct ktable		*kt;
+
+	if ((kt = ktable_get(rtableid)) == NULL) {
+		log_warnx("kr_net_reload: non-existent rtableid %d", rtableid);
+		return (-1);
+	}
+
+	TAILQ_FOREACH(n, &kt->krn, entry)
+		n->net.old = 1;
+
+	while ((n = TAILQ_FIRST(nh)) != NULL) {
+		TAILQ_REMOVE(nh, n, entry);
+		n->net.old = 0;
+		n->net.rtableid = rtableid;
+		xn = kr_net_find(kt, n);
+		if (xn) {
+			xn->net.old = 0;
+			filterset_free(&xn->net.attrset);
+			filterset_move(&n->net.attrset, &xn->net.attrset);
+			kr_net_delete(n);
+		} else
+			TAILQ_INSERT_TAIL(&kt->krn, n, entry);
+	}
+
+	for (n = TAILQ_FIRST(&kt->krn); n != NULL; n = xn) {
+		xn = TAILQ_NEXT(n, entry);
+		if (n->net.old) {
+			if (n->net.type == NETWORK_DEFAULT)
+				if (send_network(IMSG_NETWORK_REMOVE, &n->net,
+				    NULL))
+					return (-1);
+			TAILQ_REMOVE(&kt->krn, n, entry);
+			kr_net_delete(n);
+		}
+	}
+
+	return (0);
+}
 
 int
-kr_redistribute(int type, struct kroute *kr)
+kr_redistribute(int type, struct ktable *kt, struct kroute *kr)
 {
-	struct redist_node	*rn;
+	struct network		*match;
+	struct network_config	 net;
 	u_int32_t		 a;
 
+	/* shortcut for removals */
+	if (type == IMSG_NETWORK_REMOVE) {
+		if (!(kr->flags & F_REDISTRIBUTED))
+			return (0);	/* no match, don't redistribute */
+		kr->flags &= ~F_REDISTRIBUTED;
+		match = NULL;
+		goto sendit;
+	}
+
 	if (!(kr->flags & F_KERNEL))
 		return (0);
 
@@ -670,41 +1230,40 @@ kr_redistribute(int type, struct kroute *kr)
 	if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0)
 		return (0);
 
-	/* Add or delete kr from list ... */
-	LIST_FOREACH(rn, &redistlist, entry)
-	    if (rn->kr == kr)
-		    break;
+	match = kr_net_match(kt, kr);
+	if (match == NULL) {
+		if (!(kr->flags & F_REDISTRIBUTED))
+			return (0);	/* no match, don't redistribute */
+		/* route no longer matches but is redistributed, so remove */
+		kr->flags &= ~F_REDISTRIBUTED;
+		type = IMSG_NETWORK_REMOVE;
+	} else
+		kr->flags |= F_REDISTRIBUTED;
 
-	switch (type) {
-	case IMSG_NETWORK_ADD:
-		if (rn == NULL) {
-			if ((rn = calloc(1, sizeof(struct redist_node))) ==
-			    NULL) {
-				log_warn("kr_redistribute");
-				return (-1);
-			}
-			rn->kr = kr;
-			LIST_INSERT_HEAD(&redistlist, rn, entry);
-		}
-		break;
-	case IMSG_NETWORK_REMOVE:
-		if (rn != NULL) {
-			LIST_REMOVE(rn, entry);
-			free(rn);
-		}
-		break;
-	default:
-		errno = EINVAL;
-		return (-1);
-	}
+sendit:
+	bzero(&net, sizeof(net));
+	net.prefix.aid = AID_INET;
+	net.prefix.v4.s_addr = kr->prefix.s_addr;
+	net.prefixlen = kr->prefixlen;
+	net.rtableid = kt->rtableid;
 
-	return (bgpd_redistribute(type, kr, NULL));
+	return (send_network(type, &net, match ? &match->net.attrset : NULL));
 }
 
 int
-kr_redistribute6(int type, struct kroute6 *kr6)
+kr_redistribute6(int type, struct ktable *kt, struct kroute6 *kr6)
 {
-	struct redist_node	*rn;
+	struct network		*match;
+	struct network_config	 net;
+
+	/* shortcut for removals */
+	if (type == IMSG_NETWORK_REMOVE) {
+		if (!(kr6->flags & F_REDISTRIBUTED))
+			return (0);	/* no match, don't redistribute */
+		kr6->flags &= ~F_REDISTRIBUTED;
+		match = NULL;
+		goto sendit;
+	}
 
 	if (!(kr6->flags & F_KERNEL))
 		return (0);
@@ -736,60 +1295,107 @@ kr_redistribute6(int type, struct kroute6 *kr6)
 	 * never allow ::/0 the default route can only be redistributed
 	 * with announce default.
 	 */
-	if (memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0 &&
-	    kr6->prefixlen == 0)
+	if (kr6->prefixlen == 0 &&
+	    memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0)
 		return (0);
 
-	/* Add or delete kr from list ...
-	 * using a linear list to store the redistributed networks will hurt
-	 * as soon as redistribute ospf comes but until then keep it simple.
-	 */
-	LIST_FOREACH(rn, &redistlist, entry)
-	    if (rn->kr6 == kr6)
-		    break;
-
-	switch (type) {
-	case IMSG_NETWORK_ADD:
-		if (rn == NULL) {
-			if ((rn = calloc(1, sizeof(struct redist_node))) ==
-			    NULL) {
-				log_warn("kr_redistribute");
-				return (-1);
-			}
-			rn->kr6 = kr6;
-			LIST_INSERT_HEAD(&redistlist, rn, entry);
-		}
-		break;
-	case IMSG_NETWORK_REMOVE:
-		if (rn != NULL) {
-			LIST_REMOVE(rn, entry);
-			free(rn);
-		}
-		break;
-	default:
-		errno = EINVAL;
-		return (-1);
-	}
-
-	return (bgpd_redistribute(type, NULL, kr6));
+	match = kr_net_match6(kt, kr6);
+	if (match == NULL) {
+		if (!(kr6->flags & F_REDISTRIBUTED))
+			return (0);	/* no match, don't redistribute */
+		/* route no longer matches but is redistributed, so remove */
+		kr6->flags &= ~F_REDISTRIBUTED;
+		type = IMSG_NETWORK_REMOVE;
+	} else
+		kr6->flags |= F_REDISTRIBUTED;
+sendit:
+	bzero(&net, sizeof(net));
+	net.prefix.aid = AID_INET6;
+	memcpy(&net.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
+	net.prefixlen = kr6->prefixlen;
+	net.rtableid = kt->rtableid;
+
+	return (send_network(type, &net, match ? &match->net.attrset : NULL));
 }
 
 int
 kr_reload(void)
 {
-	struct redist_node	*rn;
+	struct ktable		*kt;
+	struct kroute_node	*kr;
+	struct kroute6_node	*kr6;
 	struct knexthop_node	*nh;
+	struct network		*n;
+	u_int			 rid;
+	int			 hasdyn = 0;
 
-	LIST_FOREACH(rn, &redistlist, entry)
-		if (bgpd_redistribute(IMSG_NETWORK_ADD, rn->kr, rn->kr6) == -1)
-			return (-1);
+	for (rid = 0; rid < krt_size; rid++) {
+		if ((kt = ktable_get(rid)) == NULL)
+			continue;
 
-	RB_FOREACH(nh, knexthop_tree, &knt)
-		knexthop_validate(nh);
+		RB_FOREACH(nh, knexthop_tree, KT2KNT(kt))
+			knexthop_validate(kt, nh);
+
+		TAILQ_FOREACH(n, &kt->krn, entry)
+			if (n->net.type == NETWORK_DEFAULT) {
+				if (send_network(IMSG_NETWORK_ADD, &n->net,
+				    &n->net.attrset))
+					return (-1);
+			} else
+				hasdyn = 1;
+
+		if (hasdyn) {
+			/* only evaluate the full tree if we need */
+			RB_FOREACH(kr, kroute_tree, &kt->krt)
+				kr_redistribute(IMSG_NETWORK_ADD, kt, &kr->r);
+			RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
+				kr_redistribute6(IMSG_NETWORK_ADD, kt, &kr6->r);
+		}
+	}
 
 	return (0);
 }
 
+struct kroute_full *
+kr_tofull(struct kroute *kr)
+{
+	static struct kroute_full	kf;
+
+	bzero(&kf, sizeof(kf));
+
+	kf.prefix.aid = AID_INET;
+	kf.prefix.v4.s_addr = kr->prefix.s_addr;
+	kf.nexthop.aid = AID_INET;
+	kf.nexthop.v4.s_addr = kr->nexthop.s_addr;
+	strlcpy(kf.label, rtlabel_id2name(kr->labelid), sizeof(kf.label));
+	kf.flags = kr->flags;
+	kf.ifindex = kr->ifindex;
+	kf.prefixlen = kr->prefixlen;
+	kf.priority = kr->priority;
+
+	return (&kf);
+}
+
+struct kroute_full *
+kr6_tofull(struct kroute6 *kr6)
+{
+	static struct kroute_full	kf;
+
+	bzero(&kf, sizeof(kf));
+
+	kf.prefix.aid = AID_INET6;
+	memcpy(&kf.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
+	kf.nexthop.aid = AID_INET6;
+	memcpy(&kf.nexthop.v6, &kr6->nexthop, sizeof(struct in6_addr));
+	strlcpy(kf.label, rtlabel_id2name(kr6->labelid), sizeof(kf.label));
+	kf.flags = kr6->flags;
+	kf.ifindex = kr6->ifindex;
+	kf.prefixlen = kr6->prefixlen;
+	kf.priority = kr6->priority;
+
+	return (&kf);
+}
+
 /*
  * RB-tree compare functions
  */
@@ -846,26 +1452,28 @@ kroute6_compare(struct kroute6_node *a, struct kroute6_node *b)
 int
 knexthop_compare(struct knexthop_node *a, struct knexthop_node *b)
 {
-	u_int32_t	r;
+	int	i;
 
-	if (a->nexthop.af != b->nexthop.af)
-		return (b->nexthop.af - a->nexthop.af);
+	if (a->nexthop.aid != b->nexthop.aid)
+		return (b->nexthop.aid - a->nexthop.aid);
 
-	switch (a->nexthop.af) {
-	case AF_INET:
-		if ((r = b->nexthop.addr32[0] - a->nexthop.addr32[0]) != 0)
-			return (r);
+	switch (a->nexthop.aid) {
+	case AID_INET:
+		if (ntohl(a->nexthop.v4.s_addr) < ntohl(b->nexthop.v4.s_addr))
+			return (-1);
+		if (ntohl(a->nexthop.v4.s_addr) > ntohl(b->nexthop.v4.s_addr))
+			return (1);
 		break;
-	case AF_INET6:
-		if ((r = b->nexthop.addr32[3] - a->nexthop.addr32[3]) != 0)
-			return (r);
-		if ((r = b->nexthop.addr32[2] - a->nexthop.addr32[2]) != 0)
-			return (r);
-		if ((r = b->nexthop.addr32[1] - a->nexthop.addr32[1]) != 0)
-			return (r);
-		if ((r = b->nexthop.addr32[0] - a->nexthop.addr32[0]) != 0)
-			return (r);
+	case AID_INET6:
+		for (i = 0; i < 16; i++) {
+			if (a->nexthop.v6.s6_addr[i] < b->nexthop.v6.s6_addr[i])
+				return (-1);
+			if (a->nexthop.v6.s6_addr[i] > b->nexthop.v6.s6_addr[i])
+				return (1);
+		}
 		break;
+	default:
+		fatalx("knexthop_compare: unknown AF");
 	}
 
 	return (0);
@@ -883,7 +1491,8 @@ kif_compare(struct kif_node *a, struct kif_node *b)
  */
 
 struct kroute_node *
-kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio)
+kroute_find(struct ktable *kt, in_addr_t prefix, u_int8_t prefixlen,
+    u_int8_t prio)
 {
 	struct kroute_node	s;
 	struct kroute_node	*kn, *tmp;
@@ -892,15 +1501,15 @@ kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio)
 	s.r.prefixlen = prefixlen;
 	s.r.priority = prio;
 
-	kn = RB_FIND(kroute_tree, &krt, &s);
+	kn = RB_FIND(kroute_tree, &kt->krt, &s);
 	if (kn && prio == RTP_ANY) {
-		tmp = RB_PREV(kroute_tree, &krt, kn);
+		tmp = RB_PREV(kroute_tree, &kt->krt, kn);
 		while (tmp) {
 			if (kroute_compare(&s, tmp) == 0)
 				kn = tmp;
 			else
 				break;
-			tmp = RB_PREV(kroute_tree, &krt, kn);
+			tmp = RB_PREV(kroute_tree, &kt->krt, kn);
 		}
 	}
 	return (kn);
@@ -927,13 +1536,13 @@ kroute_matchgw(struct kroute_node *kr, struct sockaddr_in *sa_in)
 }
 
 int
-kroute_insert(struct kroute_node *kr)
+kroute_insert(struct ktable *kt, struct kroute_node *kr)
 {
 	struct kroute_node	*krm;
 	struct knexthop_node	*h;
 	in_addr_t		 mask, ina;
 
-	if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
+	if ((krm = RB_INSERT(kroute_tree, &kt->krt, kr)) != NULL) {
 		/* multipath route, add at end of list */
 		while (krm->next != NULL)
 			krm = krm->next;
@@ -944,10 +1553,10 @@ kroute_insert(struct kroute_node *kr)
 	if (kr->r.flags & F_KERNEL) {
 		mask = prefixlen2mask(kr->r.prefixlen);
 		ina = ntohl(kr->r.prefix.s_addr);
-		RB_FOREACH(h, knexthop_tree, &knt)
-			if (h->nexthop.af == AF_INET &&
+		RB_FOREACH(h, knexthop_tree, KT2KNT(kt))
+			if (h->nexthop.aid == AID_INET &&
 			    (ntohl(h->nexthop.v4.s_addr) & mask) == ina)
-				knexthop_validate(h);
+				knexthop_validate(kt, h);
 
 		if (kr->r.flags & F_CONNECTED)
 			if (kif_kr_insert(kr) == -1)
@@ -955,19 +1564,19 @@ kroute_insert(struct kroute_node *kr)
 
 		if (krm == NULL)
 			/* redistribute multipath routes only once */
-			kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
+			kr_redistribute(IMSG_NETWORK_ADD, kt, &kr->r);
 	}
 	return (0);
 }
 
 
 int
-kroute_remove(struct kroute_node *kr)
+kroute_remove(struct ktable *kt, struct kroute_node *kr)
 {
 	struct kroute_node	*krm;
 	struct knexthop_node	*s;
 
-	if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) {
+	if ((krm = RB_FIND(kroute_tree, &kt->krt, kr)) == NULL) {
 		log_warnx("kroute_remove failed to find %s/%u",
 		    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
 		return (-1);
@@ -975,13 +1584,14 @@ kroute_remove(struct kroute_node *kr)
 
 	if (krm == kr) {
 		/* head element */
-		if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
+		if (RB_REMOVE(kroute_tree, &kt->krt, kr) == NULL) {
 			log_warnx("kroute_remove failed for %s/%u",
 			    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
 			return (-1);
 		}
 	       	if (kr->next != NULL) {
-			if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) {
+			if (RB_INSERT(kroute_tree, &kt->krt, kr->next) !=
+			    NULL) {
 				log_warnx("kroute_remove failed to add %s/%u",
 				    inet_ntoa(kr->r.prefix), kr->r.prefixlen);
 				return (-1);
@@ -1002,13 +1612,13 @@ kroute_remove(struct kroute_node *kr)
 
 	/* check whether a nexthop depends on this kroute */
 	if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP))
-		RB_FOREACH(s, knexthop_tree, &knt)
+		RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
 			if (s->kroute == kr)
-				knexthop_validate(s);
+				knexthop_validate(kt, s);
 
 	if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
 		/* again remove only once */
-		kr_redistribute(IMSG_NETWORK_REMOVE, &kr->r);
+		kr_redistribute(IMSG_NETWORK_REMOVE, kt, &kr->r);
 
 	if (kr->r.flags & F_CONNECTED)
 		if (kif_kr_remove(kr) == -1) {
@@ -1021,16 +1631,17 @@ kroute_remove(struct kroute_node *kr)
 }
 
 void
-kroute_clear(void)
+kroute_clear(struct ktable *kt)
 {
 	struct kroute_node	*kr;
 
-	while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
-		kroute_remove(kr);
+	while ((kr = RB_MIN(kroute_tree, &kt->krt)) != NULL)
+		kroute_remove(kt, kr);
 }
 
 struct kroute6_node *
-kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio)
+kroute6_find(struct ktable *kt, const struct in6_addr *prefix,
+    u_int8_t prefixlen, u_int8_t prio)
 {
 	struct kroute6_node	s;
 	struct kroute6_node	*kn6, *tmp;
@@ -1039,15 +1650,15 @@ kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio)
 	s.r.prefixlen = prefixlen;
 	s.r.priority = prio;
 
-	kn6 = RB_FIND(kroute6_tree, &krt6, &s);
+	kn6 = RB_FIND(kroute6_tree, &kt->krt6, &s);
 	if (kn6 && prio == RTP_ANY) {
-		tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+		tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6);
 		while (tmp) {
 			if (kroute6_compare(&s, tmp) == 0)
 				kn6 = tmp;
-			else 
+			else
 				break;
-			tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+			tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6);
 		}
 	}
 	return (kn6);
@@ -1065,7 +1676,7 @@ kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6)
 	memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop));
 
 	while (kr) {
-		if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == NULL)
+		if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == 0)
 			return (kr);
 		kr = kr->next;
 	}
@@ -1074,13 +1685,13 @@ kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6)
 }
 
 int
-kroute6_insert(struct kroute6_node *kr)
+kroute6_insert(struct ktable *kt, struct kroute6_node *kr)
 {
 	struct kroute6_node	*krm;
 	struct knexthop_node	*h;
 	struct in6_addr		 ina, inb;
 
-	if ((krm = RB_INSERT(kroute6_tree, &krt6, kr)) != NULL) {
+	if ((krm = RB_INSERT(kroute6_tree, &kt->krt6, kr)) != NULL) {
 		/* multipath route, add at end of list */
 		while (krm->next != NULL)
 			krm = krm->next;
@@ -1090,12 +1701,12 @@ kroute6_insert(struct kroute6_node *kr)
 
 	if (kr->r.flags & F_KERNEL) {
 		inet6applymask(&ina, &kr->r.prefix, kr->r.prefixlen);
-		RB_FOREACH(h, knexthop_tree, &knt)
-			if (h->nexthop.af == AF_INET6) {
+		RB_FOREACH(h, knexthop_tree, KT2KNT(kt))
+			if (h->nexthop.aid == AID_INET6) {
 				inet6applymask(&inb, &h->nexthop.v6,
 				    kr->r.prefixlen);
 				if (memcmp(&ina, &inb, sizeof(ina)) == 0)
-					knexthop_validate(h);
+					knexthop_validate(kt, h);
 			}
 
 		if (kr->r.flags & F_CONNECTED)
@@ -1104,19 +1715,19 @@ kroute6_insert(struct kroute6_node *kr)
 
 		if (krm == NULL)
 			/* redistribute multipath routes only once */
-			kr_redistribute6(IMSG_NETWORK_ADD, &kr->r);
+			kr_redistribute6(IMSG_NETWORK_ADD, kt, &kr->r);
 	}
 
 	return (0);
 }
 
 int
-kroute6_remove(struct kroute6_node *kr)
+kroute6_remove(struct ktable *kt, struct kroute6_node *kr)
 {
 	struct kroute6_node	*krm;
 	struct knexthop_node	*s;
 
-	if ((krm = RB_FIND(kroute6_tree, &krt6, kr)) == NULL) {
+	if ((krm = RB_FIND(kroute6_tree, &kt->krt6, kr)) == NULL) {
 		log_warnx("kroute6_remove failed for %s/%u",
 		    log_in6addr(&kr->r.prefix), kr->r.prefixlen);
 		return (-1);
@@ -1124,13 +1735,14 @@ kroute6_remove(struct kroute6_node *kr)
 
 	if (krm == kr) {
 		/* head element */
-		if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) {
+		if (RB_REMOVE(kroute6_tree, &kt->krt6, kr) == NULL) {
 			log_warnx("kroute6_remove failed for %s/%u",
 			    log_in6addr(&kr->r.prefix), kr->r.prefixlen);
 			return (-1);
 		}
 	       	if (kr->next != NULL) {
-			if (RB_INSERT(kroute6_tree, &krt6, kr->next) != NULL) {
+			if (RB_INSERT(kroute6_tree, &kt->krt6, kr->next) !=
+			    NULL) {
 				log_warnx("kroute6_remove failed to add %s/%u",
 				    log_in6addr(&kr->r.prefix),
 				    kr->r.prefixlen);
@@ -1152,13 +1764,13 @@ kroute6_remove(struct kroute6_node *kr)
 
 	/* check whether a nexthop depends on this kroute */
 	if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP))
-		RB_FOREACH(s, knexthop_tree, &knt)
+		RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
 			if (s->kroute == kr)
-				knexthop_validate(s);
+				knexthop_validate(kt, s);
 
 	if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
 		/* again remove only once */
-		kr_redistribute6(IMSG_NETWORK_REMOVE, &kr->r);
+		kr_redistribute6(IMSG_NETWORK_REMOVE, kt, &kr->r);
 
 	if (kr->r.flags & F_CONNECTED)
 		if (kif_kr6_remove(kr) == -1) {
@@ -1171,45 +1783,46 @@ kroute6_remove(struct kroute6_node *kr)
 }
 
 void
-kroute6_clear(void)
+kroute6_clear(struct ktable *kt)
 {
 	struct kroute6_node	*kr;
 
-	while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL)
-		kroute6_remove(kr);
+	while ((kr = RB_MIN(kroute6_tree, &kt->krt6)) != NULL)
+		kroute6_remove(kt, kr);
 }
 
 struct knexthop_node *
-knexthop_find(struct bgpd_addr *addr)
+knexthop_find(struct ktable *kt, struct bgpd_addr *addr)
 {
 	struct knexthop_node	s;
 
+	bzero(&s, sizeof(s));
 	memcpy(&s.nexthop, addr, sizeof(s.nexthop));
 
-	return (RB_FIND(knexthop_tree, &knt, &s));
+	return (RB_FIND(knexthop_tree, KT2KNT(kt), &s));
 }
 
 int
-knexthop_insert(struct knexthop_node *kn)
+knexthop_insert(struct ktable *kt, struct knexthop_node *kn)
 {
-	if (RB_INSERT(knexthop_tree, &knt, kn) != NULL) {
+	if (RB_INSERT(knexthop_tree, KT2KNT(kt), kn) != NULL) {
 		log_warnx("knexthop_tree insert failed for %s",
 			    log_addr(&kn->nexthop));
 		free(kn);
 		return (-1);
 	}
 
-	knexthop_validate(kn);
+	knexthop_validate(kt, kn);
 
 	return (0);
 }
 
 int
-knexthop_remove(struct knexthop_node *kn)
+knexthop_remove(struct ktable *kt, struct knexthop_node *kn)
 {
-	kroute_detach_nexthop(kn);
+	kroute_detach_nexthop(kt, kn);
 
-	if (RB_REMOVE(knexthop_tree, &knt, kn) == NULL) {
+	if (RB_REMOVE(knexthop_tree, KT2KNT(kt), kn) == NULL) {
 		log_warnx("knexthop_remove failed for %s",
 		    log_addr(&kn->nexthop));
 		return (-1);
@@ -1220,12 +1833,12 @@ knexthop_remove(struct knexthop_node *kn)
 }
 
 void
-knexthop_clear(void)
+knexthop_clear(struct ktable *kt)
 {
 	struct knexthop_node	*kn;
 
-	while ((kn = RB_MIN(knexthop_tree, &knt)) != NULL)
-		knexthop_remove(kn);
+	while ((kn = RB_MIN(knexthop_tree, KT2KNT(kt))) != NULL)
+		knexthop_remove(kt, kn);
 }
 
 struct kif_node *
@@ -1257,6 +1870,7 @@ kif_insert(struct kif_node *kif)
 int
 kif_remove(struct kif_node *kif)
 {
+	struct ktable	*kt;
 	struct kif_kr	*kkr;
 	struct kif_kr6	*kkr6;
 
@@ -1265,20 +1879,23 @@ kif_remove(struct kif_node *kif)
 		return (-1);
 	}
 
+	if ((kt = ktable_get(/* XXX */ 0)) == NULL)
+		goto done;
+
 	while ((kkr = LIST_FIRST(&kif->kroute_l)) != NULL) {
 		LIST_REMOVE(kkr, entry);
 		kkr->kr->r.flags &= ~F_NEXTHOP;
-		kroute_remove(kkr->kr);
+		kroute_remove(kt, kkr->kr);
 		free(kkr);
 	}
 
 	while ((kkr6 = LIST_FIRST(&kif->kroute6_l)) != NULL) {
 		LIST_REMOVE(kkr6, entry);
 		kkr6->kr->r.flags &= ~F_NEXTHOP;
-		kroute6_remove(kkr6->kr);
+		kroute6_remove(kt, kkr6->kr);
 		free(kkr6);
 	}
-
+done:
 	free(kif);
 	return (0);
 }
@@ -1473,25 +2090,25 @@ kroute6_validate(struct kroute6 *kr)
 }
 
 void
-knexthop_validate(struct knexthop_node *kn)
+knexthop_validate(struct ktable *kt, struct knexthop_node *kn)
 {
 	struct kroute_node	*kr;
 	struct kroute6_node	*kr6;
 	struct kroute_nexthop	 n;
 	int			 was_valid = 0;
 
-	if (kn->nexthop.af == AF_INET && (kr = kn->kroute) != NULL)
+	if (kn->nexthop.aid == AID_INET && (kr = kn->kroute) != NULL)
 		was_valid = kroute_validate(&kr->r);
-	if (kn->nexthop.af == AF_INET6 && (kr6 = kn->kroute) != NULL)
+	if (kn->nexthop.aid == AID_INET6 && (kr6 = kn->kroute) != NULL)
 		was_valid = kroute6_validate(&kr6->r);
 
 	bzero(&n, sizeof(n));
 	memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
-	kroute_detach_nexthop(kn);
+	kroute_detach_nexthop(kt, kn);
 
-	switch (kn->nexthop.af) {
-	case AF_INET:
-		if ((kr = kroute_match(kn->nexthop.v4.s_addr, 0)) == NULL) {
+	switch (kn->nexthop.aid) {
+	case AID_INET:
+		if ((kr = kroute_match(kt, kn->nexthop.v4.s_addr, 0)) == NULL) {
 			if (was_valid)
 				send_nexthop_update(&n);
 		} else {					/* match */
@@ -1500,8 +2117,10 @@ knexthop_validate(struct knexthop_node *kn)
 				n.connected = kr->r.flags & F_CONNECTED;
 				if ((n.gateway.v4.s_addr =
 				    kr->r.nexthop.s_addr) != 0)
-					n.gateway.af = AF_INET;
-				memcpy(&n.kr.kr4, &kr->r, sizeof(n.kr.kr4));
+					n.gateway.aid = AID_INET;
+				n.net.aid = AID_INET;
+				n.net.v4.s_addr = kr->r.prefix.s_addr;
+				n.netlen = kr->r.prefixlen;
 				send_nexthop_update(&n);
 			} else					/* down */
 				if (was_valid)
@@ -1511,8 +2130,8 @@ knexthop_validate(struct knexthop_node *kn)
 			kr->r.flags |= F_NEXTHOP;
 		}
 		break;
-	case AF_INET6:
-		if ((kr6 = kroute6_match(&kn->nexthop.v6, 0)) == NULL) {
+	case AID_INET6:
+		if ((kr6 = kroute6_match(kt, &kn->nexthop.v6, 0)) == NULL) {
 			if (was_valid)
 				send_nexthop_update(&n);
 		} else {					/* match */
@@ -1521,11 +2140,14 @@ knexthop_validate(struct knexthop_node *kn)
 				n.connected = kr6->r.flags & F_CONNECTED;
 				if (memcmp(&kr6->r.nexthop, &in6addr_any,
 				    sizeof(struct in6_addr)) != 0) {
-					n.gateway.af = AF_INET6;
+					n.gateway.aid = AID_INET6;
 					memcpy(&n.gateway.v6, &kr6->r.nexthop,
 					    sizeof(struct in6_addr));
 				}
-				memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6));
+				n.net.aid = AID_INET6;
+				memcpy(&n.net.v6, &kr6->r.nexthop,
+				    sizeof(struct in6_addr));
+				n.netlen = kr6->r.prefixlen;
 				send_nexthop_update(&n);
 			} else					/* down */
 				if (was_valid)
@@ -1539,39 +2161,44 @@ knexthop_validate(struct knexthop_node *kn)
 }
 
 void
-knexthop_track(void *krn)
+knexthop_track(struct ktable *kt, void *krp)
 {
 	struct knexthop_node	*kn;
 	struct kroute_node	*kr;
 	struct kroute6_node	*kr6;
 	struct kroute_nexthop	 n;
 
-	RB_FOREACH(kn, knexthop_tree, &knt)
-		if (kn->kroute == krn) {
+	RB_FOREACH(kn, knexthop_tree, KT2KNT(kt))
+		if (kn->kroute == krp) {
 			bzero(&n, sizeof(n));
 			memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
 
-			switch (kn->nexthop.af) {
-			case AF_INET:
-				kr = krn;
+			switch (kn->nexthop.aid) {
+			case AID_INET:
+				kr = krp;
 				n.valid = 1;
 				n.connected = kr->r.flags & F_CONNECTED;
 				if ((n.gateway.v4.s_addr =
 				    kr->r.nexthop.s_addr) != 0)
-					n.gateway.af = AF_INET;
-				memcpy(&n.kr.kr4, &kr->r, sizeof(n.kr.kr4));
+					n.gateway.aid = AID_INET;
+				n.net.aid = AID_INET;
+				n.net.v4.s_addr = kr->r.prefix.s_addr;
+				n.netlen = kr->r.prefixlen;
 				break;
-			case AF_INET6:
-				kr6 = krn;
+			case AID_INET6:
+				kr6 = krp;
 				n.valid = 1;
 				n.connected = kr6->r.flags & F_CONNECTED;
 				if (memcmp(&kr6->r.nexthop, &in6addr_any,
 				    sizeof(struct in6_addr)) != 0) {
-					n.gateway.af = AF_INET6;
+					n.gateway.aid = AID_INET6;
 					memcpy(&n.gateway.v6, &kr6->r.nexthop,
 					    sizeof(struct in6_addr));
 				}
-				memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6));
+				n.net.aid = AID_INET6;
+				memcpy(&n.net.v6, &kr6->r.nexthop,
+				    sizeof(struct in6_addr));
+				n.netlen = kr6->r.prefixlen;
 				break;
 			}
 			send_nexthop_update(&n);
@@ -1579,7 +2206,7 @@ knexthop_track(void *krn)
 }
 
 struct kroute_node *
-kroute_match(in_addr_t key, int matchall)
+kroute_match(struct ktable *kt, in_addr_t key, int matchall)
 {
 	int			 i;
 	struct kroute_node	*kr;
@@ -1589,13 +2216,13 @@ kroute_match(in_addr_t key, int matchall)
 
 	/* we will never match the default route */
 	for (i = 32; i > 0; i--)
-		if ((kr = kroute_find(htonl(ina & prefixlen2mask(i)), i,
+		if ((kr = kroute_find(kt, htonl(ina & prefixlen2mask(i)), i,
 		    RTP_ANY)) != NULL)
 			if (matchall || bgpd_filternexthop(&kr->r, NULL) == 0)
 			    return (kr);
 
 	/* if we don't have a match yet, try to find a default route */
-	if ((kr = kroute_find(0, 0, RTP_ANY)) != NULL)
+	if ((kr = kroute_find(kt, 0, 0, RTP_ANY)) != NULL)
 		if (matchall || bgpd_filternexthop(&kr->r, NULL) == 0)
 			return (kr);
 
@@ -1603,7 +2230,7 @@ kroute_match(in_addr_t key, int matchall)
 }
 
 struct kroute6_node *
-kroute6_match(struct in6_addr *key, int matchall)
+kroute6_match(struct ktable *kt, struct in6_addr *key, int matchall)
 {
 	int			 i;
 	struct kroute6_node	*kr6;
@@ -1612,13 +2239,13 @@ kroute6_match(struct in6_addr *key, int matchall)
 	/* we will never match the default route */
 	for (i = 128; i > 0; i--) {
 		inet6applymask(&ina, key, i);
-		if ((kr6 = kroute6_find(&ina, i, RTP_ANY)) != NULL)
+		if ((kr6 = kroute6_find(kt, &ina, i, RTP_ANY)) != NULL)
 			if (matchall || bgpd_filternexthop(NULL, &kr6->r) == 0)
 				return (kr6);
 	}
 
 	/* if we don't have a match yet, try to find a default route */
-	if ((kr6 = kroute6_find(&in6addr_any, 0, RTP_ANY)) != NULL)
+	if ((kr6 = kroute6_find(kt, &in6addr_any, 0, RTP_ANY)) != NULL)
 		if (matchall || bgpd_filternexthop(NULL, &kr6->r) == 0)
 			return (kr6);
 
@@ -1626,7 +2253,7 @@ kroute6_match(struct in6_addr *key, int matchall)
 }
 
 void
-kroute_detach_nexthop(struct knexthop_node *kn)
+kroute_detach_nexthop(struct ktable *kt, struct knexthop_node *kn)
 {
 	struct knexthop_node	*s;
 	struct kroute_node	*k;
@@ -1640,17 +2267,17 @@ kroute_detach_nexthop(struct knexthop_node *kn)
 	if (kn->kroute == NULL)
 		return;
 
-	for (s = RB_MIN(knexthop_tree, &knt); s != NULL &&
-	    s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, &knt, s))
+	for (s = RB_MIN(knexthop_tree, KT2KNT(kt)); s != NULL &&
+	    s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, KT2KNT(kt), s))
 		;	/* nothing */
 
 	if (s == NULL) {
-		switch (kn->nexthop.af) {
-		case AF_INET:
+		switch (kn->nexthop.aid) {
+		case AID_INET:
 			k = kn->kroute;
 			k->r.flags &= ~F_NEXTHOP;
 			break;
-		case AF_INET6:
+		case AID_INET6:
 			k6 = kn->kroute;
 			k6->r.flags &= ~F_NEXTHOP;
 			break;
@@ -1665,7 +2292,7 @@ kroute_detach_nexthop(struct knexthop_node *kn)
  */
 
 int
-protect_lo(void)
+protect_lo(struct ktable *kt)
 {
 	struct kroute_node	*kr;
 	struct kroute6_node	*kr6;
@@ -1675,11 +2302,11 @@ protect_lo(void)
 		log_warn("protect_lo");
 		return (-1);
 	}
-	kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK);
+	kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
 	kr->r.prefixlen = 8;
 	kr->r.flags = F_KERNEL|F_CONNECTED;
 
-	if (RB_INSERT(kroute_tree, &krt, kr) != NULL)
+	if (RB_INSERT(kroute_tree, &kt->krt, kr) != NULL)
 		free(kr);	/* kernel route already there, no problem */
 
 	/* special protection for loopback */
@@ -1689,9 +2316,9 @@ protect_lo(void)
 	}
 	memcpy(&kr6->r.prefix, &in6addr_loopback, sizeof(kr6->r.prefix));
 	kr6->r.prefixlen = 128;
-	kr->r.flags = F_KERNEL|F_CONNECTED;
+	kr6->r.flags = F_KERNEL|F_CONNECTED;
 
-	if (RB_INSERT(kroute6_tree, &krt6, kr6) != NULL)
+	if (RB_INSERT(kroute6_tree, &kt->krt6, kr6) != NULL)
 		free(kr6);	/* kernel route already there, no problem */
 
 	return (0);
@@ -1726,17 +2353,17 @@ mask2prefixlen(in_addr_t ina)
 u_int8_t
 mask2prefixlen6(struct sockaddr_in6 *sa_in6)
 {
-	u_int8_t	 l = 0, i, len;
+	u_int8_t	 l = 0, *ap, *ep;
 
 	/*
 	 * sin6_len is the size of the sockaddr so substract the offset of
 	 * the possibly truncated sin6_addr struct.
 	 */
-	len = sa_in6->sin6_len -
-	    (u_int8_t)(&((struct sockaddr_in6 *)NULL)->sin6_addr);
-	for (i = 0; i < len; i++) {
+	ap = (u_int8_t *)&sa_in6->sin6_addr;
+	ep = (u_int8_t *)sa_in6 + sa_in6->sin6_len;
+	for (; ap < ep; ap++) {
 		/* this "beauty" is adopted from sbin/route/show.c ... */
-		switch (sa_in6->sin6_addr.s6_addr[i]) {
+		switch (*ap) {
 		case 0xff:
 			l += 8;
 			break;
@@ -1764,7 +2391,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_in6)
 		case 0x00:
 			return (l);
 		default:
-			fatalx("non continguous inet6 netmask");
+			fatalx("non contiguous inet6 netmask");
 		}
 	}
 
@@ -1788,7 +2415,7 @@ prefixlen2mask6(u_int8_t prefixlen)
 }
 
 #define	ROUNDUP(a)	\
-    (((a) & ((sizeof(long)) - 1)) ? (1 + ((a) | ((sizeof(long)) - 1))) : (a))
+    (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
 
 void
 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
@@ -1808,6 +2435,7 @@ get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
 void
 if_change(u_short ifindex, int flags, struct if_data *ifd)
 {
+	struct ktable		*kt;
 	struct kif_node		*kif;
 	struct kif_kr		*kkr;
 	struct kif_kr6		*kkr6;
@@ -1833,13 +2461,18 @@ if_change(u_short ifindex, int flags, struct if_data *ifd)
 
 	kif->k.nh_reachable = reachable;
 
+	kt = ktable_get(/* XXX */ 0);
+
 	LIST_FOREACH(kkr, &kif->kroute_l, entry) {
 		if (reachable)
 			kkr->kr->r.flags &= ~F_DOWN;
 		else
 			kkr->kr->r.flags |= F_DOWN;
 
-		RB_FOREACH(n, knexthop_tree, &knt)
+		if (kt == NULL)
+			continue;
+
+		RB_FOREACH(n, knexthop_tree, KT2KNT(kt))
 			if (n->kroute == kkr->kr) {
 				bzero(&nh, sizeof(nh));
 				memcpy(&nh.nexthop, &n->nexthop,
@@ -1849,10 +2482,11 @@ if_change(u_short ifindex, int flags, struct if_data *ifd)
 					nh.connected = 1;
 					if ((nh.gateway.v4.s_addr =
 					    kkr->kr->r.nexthop.s_addr) != 0)
-						nh.gateway.af = AF_INET;
+						nh.gateway.aid = AID_INET;
 				}
-				memcpy(&nh.kr.kr4, &kkr->kr->r,
-				    sizeof(nh.kr.kr4));
+				nh.net.aid = AID_INET;
+				nh.net.v4.s_addr = kkr->kr->r.prefix.s_addr;
+				nh.netlen = kkr->kr->r.prefixlen;
 				send_nexthop_update(&nh);
 			}
 	}
@@ -1862,7 +2496,9 @@ if_change(u_short ifindex, int flags, struct if_data *ifd)
 		else
 			kkr6->kr->r.flags |= F_DOWN;
 
-		RB_FOREACH(n, knexthop_tree, &knt)
+		if (kt == NULL)
+			continue;
+		RB_FOREACH(n, knexthop_tree, KT2KNT(kt))
 			if (n->kroute == kkr6->kr) {
 				bzero(&nh, sizeof(nh));
 				memcpy(&nh.nexthop, &n->nexthop,
@@ -1873,14 +2509,16 @@ if_change(u_short ifindex, int flags, struct if_data *ifd)
 					if (memcmp(&kkr6->kr->r.nexthop,
 					    &in6addr_any, sizeof(struct
 					    in6_addr))) {
-						nh.gateway.af = AF_INET6;
+						nh.gateway.aid = AID_INET6;
 						memcpy(&nh.gateway.v6,
 						    &kkr6->kr->r.nexthop,
 						    sizeof(struct in6_addr));
 					}
 				}
-				memcpy(&nh.kr.kr6, &kkr6->kr->r,
-				    sizeof(nh.kr.kr6));
+				nh.net.aid = AID_INET6;
+				memcpy(&nh.net.v6, &kkr6->kr->r.nexthop,
+				    sizeof(struct in6_addr));
+				nh.netlen = kkr6->kr->r.prefixlen;
 				send_nexthop_update(&nh);
 			}
 	}
@@ -1917,25 +2555,38 @@ if_announce(void *msg)
  */
 
 int
-send_rtmsg(int fd, int action, struct kroute *kroute)
+send_rtmsg(int fd, int action, struct ktable *kt, struct kroute *kroute)
 {
-	struct iovec		iov[5];
+	struct iovec		iov[7];
 	struct rt_msghdr	hdr;
 	struct sockaddr_in	prefix;
 	struct sockaddr_in	nexthop;
 	struct sockaddr_in	mask;
+	struct {
+		struct sockaddr_dl	dl;
+		char			pad[sizeof(long)];
+	}			ifp;
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
+	struct sockaddr_mpls	mpls;
 	struct sockaddr_rtlabel	label;
+#endif /* !defined(__FreeBSD__) */
 	int			iovcnt = 0;
 
-	if (kr_state.fib_sync == 0)
+	if (!kt->fib_sync)
 		return (0);
 
 	/* initialize header */
 	bzero(&hdr, sizeof(hdr));
 	hdr.rtm_version = RTM_VERSION;
 	hdr.rtm_type = action;
-	hdr.rtm_tableid = kr_state.rtableid;
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
+	hdr.rtm_tableid = kt->rtableid;
+#endif /* !defined(__FreeBSD__) */
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no rtm_priority */
 	hdr.rtm_priority = RTP_BGP;
+#else
+	hdr.rtm_flags = RTF_PROTO1;
+#endif /* !defined(__FreeBSD__) */
 	if (kroute->flags & F_BLACKHOLE)
 		hdr.rtm_flags |= RTF_BLACKHOLE;
 	if (kroute->flags & F_REJECT)
@@ -1984,6 +2635,37 @@ send_rtmsg(int fd, int action, struct kroute *kroute)
 	iov[iovcnt].iov_base = &mask;
 	iov[iovcnt++].iov_len = sizeof(mask);
 
+	if (kt->ifindex) {
+		bzero(&ifp, sizeof(ifp));
+		ifp.dl.sdl_len = sizeof(struct sockaddr_dl);
+		ifp.dl.sdl_family = AF_LINK;
+		ifp.dl.sdl_index = kt->ifindex;
+		/* adjust header */
+		hdr.rtm_addrs |= RTA_IFP;
+		hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_dl));
+		/* adjust iovec */
+		iov[iovcnt].iov_base = &ifp;
+		iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_dl));
+	}
+
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+	if (kroute->flags & F_MPLS) {
+		bzero(&mpls, sizeof(mpls));
+		mpls.smpls_len = sizeof(mpls);
+		mpls.smpls_family = AF_MPLS;
+		mpls.smpls_label = kroute->mplslabel;
+		/* adjust header */
+		hdr.rtm_flags |= RTF_MPLS;
+		hdr.rtm_mpls = MPLS_OP_PUSH;
+		hdr.rtm_addrs |= RTA_SRC;
+		hdr.rtm_msglen += sizeof(mpls);
+		/* adjust iovec */
+		iov[iovcnt].iov_base = &mpls;
+		iov[iovcnt++].iov_len = sizeof(mpls);
+	}
+#endif
+
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
 	if (kroute->labelid) {
 		bzero(&label, sizeof(label));
 		label.sr_len = sizeof(label);
@@ -1996,11 +2678,11 @@ send_rtmsg(int fd, int action, struct kroute *kroute)
 		iov[iovcnt].iov_base = &label;
 		iov[iovcnt++].iov_len = sizeof(label);
 	}
+#endif /* !defined(__FreeBSD__) */
 
 retry:
 	if (writev(fd, iov, iovcnt) == -1) {
-		switch (errno) {
-		case ESRCH:
+		if (errno == ESRCH) {
 			if (hdr.rtm_type == RTM_CHANGE) {
 				hdr.rtm_type = RTM_ADD;
 				goto retry;
@@ -2009,27 +2691,18 @@ retry:
 				    inet_ntoa(kroute->prefix),
 				    kroute->prefixlen);
 				return (0);
-			} else {
-				log_warnx("send_rtmsg: action %u, "
-				    "prefix %s/%u: %s", hdr.rtm_type,
-				    inet_ntoa(kroute->prefix),
-				    kroute->prefixlen, strerror(errno));
-				return (0);
 			}
-			break;
-		default:
-			log_warnx("send_rtmsg: action %u, prefix %s/%u: %s",
-			    hdr.rtm_type, inet_ntoa(kroute->prefix),
-			    kroute->prefixlen, strerror(errno));
-			return (0);
 		}
+		log_warn("send_rtmsg: action %u, prefix %s/%u", hdr.rtm_type,
+		    inet_ntoa(kroute->prefix), kroute->prefixlen);
+		return (0);
 	}
 
 	return (0);
 }
 
 int
-send_rt6msg(int fd, int action, struct kroute6 *kroute)
+send_rt6msg(int fd, int action, struct ktable *kt, struct kroute6 *kroute)
 {
 	struct iovec		iov[5];
 	struct rt_msghdr	hdr;
@@ -2037,17 +2710,23 @@ send_rt6msg(int fd, int action, struct kroute6 *kroute)
 		struct sockaddr_in6	addr;
 		char			pad[sizeof(long)];
 	} prefix, nexthop, mask;
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
 	struct sockaddr_rtlabel	label;
+#endif /* !defined(__FreeBSD__) */
 	int			iovcnt = 0;
 
-	if (kr_state.fib_sync == 0)
+	if (!kt->fib_sync)
 		return (0);
 
 	/* initialize header */
 	bzero(&hdr, sizeof(hdr));
 	hdr.rtm_version = RTM_VERSION;
 	hdr.rtm_type = action;
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */ 
 	hdr.rtm_tableid = kr_state.rtableid;
+#else
+	hdr.rtm_flags = RTF_PROTO1;
+#endif /* !defined(__FreeBSD__) */
 	if (kroute->flags & F_BLACKHOLE)
 		hdr.rtm_flags |= RTF_BLACKHOLE;
 	if (kroute->flags & F_REJECT)
@@ -2100,6 +2779,7 @@ send_rt6msg(int fd, int action, struct kroute6 *kroute)
 	iov[iovcnt].iov_base = &mask;
 	iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
 
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
 	if (kroute->labelid) {
 		bzero(&label, sizeof(label));
 		label.sr_len = sizeof(label);
@@ -2112,11 +2792,11 @@ send_rt6msg(int fd, int action, struct kroute6 *kroute)
 		iov[iovcnt].iov_base = &label;
 		iov[iovcnt++].iov_len = sizeof(label);
 	}
+#endif /* !defined(__FreeBSD__) */
 
 retry:
 	if (writev(fd, iov, iovcnt) == -1) {
-		switch (errno) {
-		case ESRCH:
+		if (errno == ESRCH) {
 			if (hdr.rtm_type == RTM_CHANGE) {
 				hdr.rtm_type = RTM_ADD;
 				goto retry;
@@ -2125,31 +2805,26 @@ retry:
 				    log_in6addr(&kroute->prefix),
 				    kroute->prefixlen);
 				return (0);
-			} else {
-				log_warnx("send_rt6msg: action %u, "
-				    "prefix %s/%u: %s", hdr.rtm_type,
-				    log_in6addr(&kroute->prefix),
-				    kroute->prefixlen, strerror(errno));
-				return (0);
 			}
-			break;
-		default:
-			log_warnx("send_rt6msg: action %u, prefix %s/%u: %s",
-			    hdr.rtm_type, log_in6addr(&kroute->prefix),
-			    kroute->prefixlen, strerror(errno));
-			return (0);
 		}
+		log_warn("send_rt6msg: action %u, prefix %s/%u", hdr.rtm_type,
+		    log_in6addr(&kroute->prefix), kroute->prefixlen);
+		return (0);
 	}
 
 	return (0);
 }
 
 int
-fetchtable(u_int rtableid, int connected_only)
+fetchtable(struct ktable *kt)
 {
 	size_t			 len;
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
 	int			 mib[7];
-	char			*buf, *next, *lim;
+#else
+	int			mib[6];
+#endif
+	char			*buf = NULL, *next, *lim;
 	struct rt_msghdr	*rtm;
 	struct sockaddr		*sa, *gw, *rti_info[RTAX_MAX];
 	struct sockaddr_in	*sa_in;
@@ -2163,22 +2838,35 @@ fetchtable(u_int rtableid, int connected_only)
 	mib[3] = 0;
 	mib[4] = NET_RT_DUMP;
 	mib[5] = 0;
-	mib[6] = rtableid;
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
+	mib[6] = kt->rtableid;
+#endif
 
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
 	if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
-		if (rtableid != 0 && errno == EINVAL)	/* table nonexistent */
+#else
+	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+#endif
+		if (kt->rtableid != 0 && errno == EINVAL)
+			/* table nonexistent */
 			return (0);
 		log_warn("sysctl");
 		return (-1);
 	}
-	if ((buf = malloc(len)) == NULL) {
-		log_warn("fetchtable");
-		return (-1);
-	}
-	if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
-		log_warn("sysctl");
-		free(buf);
-		return (-1);
+	if (len > 0) {
+		if ((buf = malloc(len)) == NULL) {
+			log_warn("fetchtable");
+			return (-1);
+		}
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
+		if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
+#else
+		if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+#endif
+			log_warn("sysctl2");
+			free(buf);
+			return (-1);
+		}
 	}
 
 	lim = buf + len;
@@ -2186,7 +2874,11 @@ fetchtable(u_int rtableid, int connected_only)
 		rtm = (struct rt_msghdr *)next;
 		if (rtm->rtm_version != RTM_VERSION)
 			continue;
+#if !defined(__FreeBSD__)
 		sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+#else
+		sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
+#endif
 		get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
 
 		if ((sa = rti_info[RTAX_DST]) == NULL)
@@ -2205,7 +2897,11 @@ fetchtable(u_int rtableid, int connected_only)
 			}
 
 			kr->r.flags = F_KERNEL;
+#if defined(__FreeBSD__)	/* no rtm_priority on FreeBSD */
+			kr->r.priority = RTP_BGP;
+#else
 			kr->r.priority = rtm->rtm_priority;
+#endif
 			kr->r.ifindex = rtm->rtm_index;
 			kr->r.prefix.s_addr =
 			    ((struct sockaddr_in *)sa)->sin_addr.s_addr;
@@ -2223,8 +2919,12 @@ fetchtable(u_int rtableid, int connected_only)
 					break;
 				kr->r.prefixlen =
 				    mask2prefixlen(sa_in->sin_addr.s_addr);
-			} else if (rtm->rtm_flags & RTF_HOST)
+			} else if (rtm->rtm_flags & RTF_HOST) {
 				kr->r.prefixlen = 32;
+#if defined(__FreeBSD__)	/* RTF_HOST means connected route */
+				kr->r.flags |= F_CONNECTED;
+#endif
+			}
 			else
 				kr->r.prefixlen =
 				    prefixlen_classful(kr->r.prefix.s_addr);
@@ -2238,7 +2938,11 @@ fetchtable(u_int rtableid, int connected_only)
 			}
 
 			kr6->r.flags = F_KERNEL;
+#if defined(__FreeBSD__)	/* no rtm_priority on FreeBSD */
+			kr6->r.priority = RTP_BGP;
+#else
 			kr6->r.priority = rtm->rtm_priority;
+#endif
 			kr6->r.ifindex = rtm->rtm_index;
 			memcpy(&kr6->r.prefix,
 			    &((struct sockaddr_in6 *)sa)->sin6_addr,
@@ -2257,8 +2961,12 @@ fetchtable(u_int rtableid, int connected_only)
 				if (sa_in6->sin6_len == 0)
 					break;
 				kr6->r.prefixlen = mask2prefixlen6(sa_in6);
-			} else if (rtm->rtm_flags & RTF_HOST)
+			} else if (rtm->rtm_flags & RTF_HOST) {
 				kr6->r.prefixlen = 128;
+#if defined(__FreeBSD__)	/* RTF_HOST means connected route */
+				kr6->r.flags |= F_CONNECTED;
+#endif
+			}
 			else
 				fatalx("INET6 route without netmask");
 			break;
@@ -2290,23 +2998,28 @@ fetchtable(u_int rtableid, int connected_only)
 			}
 
 		if (sa->sa_family == AF_INET) {
+#if !defined(__FreeBSD__)	/* no rtm_priority on FreeBSD */
 			if (rtm->rtm_priority == RTP_BGP)  {
-				send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
-				free(kr);
-			} else if (connected_only &&
-			    !(kr->r.flags & F_CONNECTED))
+#else
+			/* never delete route */
+			if (0) {
+#endif
+				send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r);
 				free(kr);
-			else
-				kroute_insert(kr);
+			} else
+				kroute_insert(kt, kr);
 		} else if (sa->sa_family == AF_INET6) {
+#if !defined(__FreeBSD__)	/* no rtm_priority on FreeBSD */
 			if (rtm->rtm_priority == RTP_BGP)  {
-				send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
-				free(kr6);
-			} else if (connected_only &&
-			    !(kr6->r.flags & F_CONNECTED))
+#else
+			/* never delete route */
+			if (0) {
+#endif
+				send_rt6msg(kr_state.fd, RTM_DELETE, kt,
+				    &kr6->r);
 				free(kr6);
-			else
-				kroute6_insert(kr6);
+			} else
+				kroute6_insert(kt, kr6);
 		}
 	}
 	free(buf);
@@ -2327,7 +3040,7 @@ fetchifs(int ifindex)
 	mib[0] = CTL_NET;
 	mib[1] = AF_ROUTE;
 	mib[2] = 0;
-	mib[3] = AF_INET;
+	mib[3] = AF_INET;	/* AF does not matter but AF_INET is shorter */
 	mib[4] = NET_RT_IFLIST;
 	mib[5] = ifindex;
 
@@ -2396,7 +3109,7 @@ dispatch_rtmsg(void)
 	struct rt_msghdr	*rtm;
 	struct if_msghdr	 ifm;
 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
-	int			 connected_only;
+	struct ktable		*kt;
 
 	if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
 		log_warn("dispatch_rtmsg: read error");
@@ -2418,7 +3131,11 @@ dispatch_rtmsg(void)
 		case RTM_ADD:
 		case RTM_CHANGE:
 		case RTM_DELETE:
+#if !defined(__FreeBSD__)
 			sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+#else
+			sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
+#endif
 			get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
 
 			if (rtm->rtm_pid == kr_state.pid) /* cause by us */
@@ -2430,16 +3147,14 @@ dispatch_rtmsg(void)
 			if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
 				continue;
 
-			connected_only = 0;
-			if (rtm->rtm_tableid != kr_state.rtableid) {
-				if (rtm->rtm_tableid == 0)
-					connected_only = 1;
-				else
-					continue;
-			}
+#if !defined(__FreeBSD__) /* FreeBSD has no rtm_tableid. */
+			if ((kt = ktable_get(rtm->rtm_tableid)) == NULL)
+#else
+			if ((kt = ktable_get(0)) == NULL)
+#endif
+				continue;
 
-			if (dispatch_rtmsg_addr(rtm, rti_info,
-			    connected_only) == -1)
+			if (dispatch_rtmsg_addr(rtm, rti_info, kt) == -1)
 				return (-1);
 			break;
 		case RTM_IFINFO:
@@ -2460,7 +3175,7 @@ dispatch_rtmsg(void)
 
 int
 dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
-    int connected_only)
+    struct ktable *kt)
 {
 	struct sockaddr		*sa;
 	struct sockaddr_in	*sa_in;
@@ -2494,31 +3209,44 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
 		mpath = 1;
 #endif
 
+#if !defined(__FreeBSD__)	/* no rtm_priority on FreeBSD */
 	prio = rtm->rtm_priority;
-	prefix.af = sa->sa_family;
-	switch (prefix.af) {
+#else
+	prio = RTP_BGP;
+#endif
+	switch (sa->sa_family) {
 	case AF_INET:
+		prefix.aid = AID_INET;
 		prefix.v4.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
 		sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
 		if (sa_in != NULL) {
 			if (sa_in->sin_len != 0)
 				prefixlen = mask2prefixlen(
 				    sa_in->sin_addr.s_addr);
-		} else if (rtm->rtm_flags & RTF_HOST)
+		} else if (rtm->rtm_flags & RTF_HOST) {
 			prefixlen = 32;
+#if defined(__FreeBSD__)	/* RTF_HOST means connected route */
+			flags |= F_CONNECTED;
+#endif
+		}
 		else
 			prefixlen =
 			    prefixlen_classful(prefix.v4.s_addr);
 		break;
 	case AF_INET6:
+		prefix.aid = AID_INET6;
 		memcpy(&prefix.v6, &((struct sockaddr_in6 *)sa)->sin6_addr,
 		    sizeof(struct in6_addr));
 		sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK];
 		if (sa_in6 != NULL) {
 			if (sa_in6->sin6_len != 0)
 				prefixlen = mask2prefixlen6(sa_in6);
-		} else if (rtm->rtm_flags & RTF_HOST)
+		} else if (rtm->rtm_flags & RTF_HOST) {
 			prefixlen = 128;
+#if defined(__FreeBSD__)	/* RTF_HOST means connected route */
+			flags |= F_CONNECTED;
+#endif
+		}
 		else
 			fatalx("in6 net addr without netmask");
 		break;
@@ -2537,10 +3265,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
 		}
 
 	if (rtm->rtm_type == RTM_DELETE) {
-		switch (prefix.af) {
-		case AF_INET:
+		switch (prefix.aid) {
+		case AID_INET:
 			sa_in = (struct sockaddr_in *)sa;
-			if ((kr = kroute_find(prefix.v4.s_addr,
+			if ((kr = kroute_find(kt, prefix.v4.s_addr,
 			    prefixlen, prio)) == NULL)
 				return (0);
 			if (!(kr->r.flags & F_KERNEL))
@@ -2554,12 +3282,12 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
 					return (0);
 				}
 
-			if (kroute_remove(kr) == -1)
+			if (kroute_remove(kt, kr) == -1)
 				return (-1);
 			break;
-		case AF_INET6:
+		case AID_INET6:
 			sa_in6 = (struct sockaddr_in6 *)sa;
-			if ((kr6 = kroute6_find(&prefix.v6, prefixlen,
+			if ((kr6 = kroute6_find(kt, &prefix.v6, prefixlen,
 			    prio)) == NULL)
 				return (0);
 			if (!(kr6->r.flags & F_KERNEL))
@@ -2574,26 +3302,23 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
 					return (0);
 				}
 
-			if (kroute6_remove(kr6) == -1)
+			if (kroute6_remove(kt, kr6) == -1)
 				return (-1);
 			break;
 		}
 		return (0);
 	}
 
-	if (connected_only && !(flags & F_CONNECTED))
-		return (0);
-
 	if (sa == NULL && !(flags & F_CONNECTED)) {
 		log_warnx("dispatch_rtmsg no nexthop for %s/%u",
 		    log_addr(&prefix), prefixlen);
 		return (0);
 	}
 
-	switch (prefix.af) {
-	case AF_INET:
+	switch (prefix.aid) {
+	case AID_INET:
 		sa_in = (struct sockaddr_in *)sa;
-		if ((kr = kroute_find(prefix.v4.s_addr, prefixlen,
+		if ((kr = kroute_find(kt, prefix.v4.s_addr, prefixlen,
 		    prio)) != NULL) {
 			if (kr->r.flags & F_KERNEL) {
 				/* get the correct route */
@@ -2619,16 +3344,16 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
 				    !(flags & F_CONNECTED)) {
 					kif_kr_remove(kr);
 					kr_redistribute(IMSG_NETWORK_REMOVE,
-					    &kr->r);
+					    kt, &kr->r);
 				}
 				if ((flags & F_CONNECTED) &&
 				    !(oflags & F_CONNECTED)) {
 					kif_kr_insert(kr);
 					kr_redistribute(IMSG_NETWORK_ADD,
-					    &kr->r);
+					    kt, &kr->r);
 				}
 				if (kr->r.flags & F_NEXTHOP)
-					knexthop_track(kr);
+					knexthop_track(kt, kr);
 			}
 		} else if (rtm->rtm_type == RTM_CHANGE) {
 			log_warnx("change req for %s/%u: not in table",
@@ -2651,12 +3376,13 @@ add4:
 			kr->r.ifindex = ifindex;
 			kr->r.priority = prio;
 
-			kroute_insert(kr);
+			kroute_insert(kt, kr);
 		}
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		sa_in6 = (struct sockaddr_in6 *)sa;
-		if ((kr6 = kroute6_find(&prefix.v6, prefixlen, prio)) != NULL) {
+		if ((kr6 = kroute6_find(kt, &prefix.v6, prefixlen, prio)) !=
+		    NULL) {
 			if (kr6->r.flags & F_KERNEL) {
 				/* get the correct route */
 				if (mpath && rtm->rtm_type == RTM_CHANGE &&
@@ -2685,16 +3411,16 @@ add4:
 				    !(flags & F_CONNECTED)) {
 					kif_kr6_remove(kr6);
 					kr_redistribute6(IMSG_NETWORK_REMOVE,
-					    &kr6->r);
+					    kt, &kr6->r);
 				}
 				if ((flags & F_CONNECTED) &&
 				    !(oflags & F_CONNECTED)) {
 					kif_kr6_insert(kr6);
 					kr_redistribute6(IMSG_NETWORK_ADD,
-					    &kr6->r);
+					    kt, &kr6->r);
 				}
 				if (kr6->r.flags & F_NEXTHOP)
-					knexthop_track(kr6);
+					knexthop_track(kt, kr6);
 			}
 		} else if (rtm->rtm_type == RTM_CHANGE) {
 			log_warnx("change req for %s/%u: not in table",
@@ -2720,7 +3446,7 @@ add6:
 			kr6->r.ifindex = ifindex;
 			kr6->r.priority = prio;
 
-			kroute6_insert(kr6);
+			kroute6_insert(kt, kr6);
 		}
 		break;
 	}
diff --git a/bgpd/log.c b/bgpd/log.c
index c426ee9..5c2d690 100644
--- a/bgpd/log.c
+++ b/bgpd/log.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: log.c,v 1.50 2007/04/23 13:04:24 claudio Exp $ */
+/*	$OpenBSD: log.c,v 1.54 2010/11/18 12:51:24 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -32,6 +32,7 @@
 #include "log.h"
 
 int	debug;
+int	verbose;
 
 void	 logit(int, const char *, ...);
 
@@ -42,8 +43,9 @@ log_fmt_peer(const struct peer_config *peer)
 	char		*pfmt, *p;
 
 	ip = log_addr(&peer->remote_addr);
-	if ((peer->remote_addr.af == AF_INET && peer->remote_masklen != 32) ||
-	    (peer->remote_addr.af == AF_INET6 && peer->remote_masklen != 128)) {
+	if ((peer->remote_addr.aid == AID_INET && peer->remote_masklen != 32) ||
+	    (peer->remote_addr.aid == AID_INET6 &&
+	    peer->remote_masklen != 128)) {
 		if (asprintf(&p, "%s/%u", ip, peer->remote_masklen) == -1)
 			fatal(NULL);
 	} else {
@@ -69,6 +71,7 @@ log_init(int n_debug)
 	extern char	*__progname;
 
 	debug = n_debug;
+	verbose = n_debug;
 
 	if (!debug)
 		openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
@@ -77,6 +80,12 @@ log_init(int n_debug)
 }
 
 void
+log_verbose(int v)
+{
+	verbose = v;
+}
+
+void
 logit(int pri, const char *fmt, ...)
 {
 	va_list	ap;
@@ -193,7 +202,7 @@ log_debug(const char *emsg, ...)
 {
 	va_list	 ap;
 
-	if (debug) {
+	if (verbose) {
 		va_start(ap, emsg);
 		vlog(LOG_DEBUG, emsg, ap);
 		va_end(ap);
@@ -250,7 +259,7 @@ log_statechange(struct peer *peer, enum session_state nstate,
 
 void
 log_notification(const struct peer *peer, u_int8_t errcode, u_int8_t subcode,
-    u_char *data, u_int16_t datalen)
+    u_char *data, u_int16_t datalen, const char *dir)
 {
 	char		*p;
 	const char	*suberrname = NULL;
@@ -287,23 +296,22 @@ log_notification(const struct peer *peer, u_int8_t errcode, u_int8_t subcode,
 		uk = 1;
 		break;
 	default:
-		logit(LOG_CRIT, "%s: received notification, unknown errcode "
-		    "%u, subcode %u", p, errcode, subcode);
+		logit(LOG_CRIT, "%s: %s notification, unknown errcode "
+		    "%u, subcode %u", p, dir, errcode, subcode);
 		free(p);
 		return;
 	}
 
 	if (uk)
-		logit(LOG_CRIT,
-		    "%s: received notification: %s, unknown subcode %u",
-		    p, errnames[errcode], subcode);
+		logit(LOG_CRIT, "%s: %s notification: %s, unknown subcode %u",
+		    p, dir, errnames[errcode], subcode);
 	else {
 		if (suberrname == NULL)
-			logit(LOG_CRIT, "%s: received notification: %s",
-			    p, errnames[errcode]);
+			logit(LOG_CRIT, "%s: %s notification: %s", p,
+			    dir, errnames[errcode]);
 		else
-			logit(LOG_CRIT, "%s: received notification: %s, %s",
-			    p, errnames[errcode], suberrname);
+			logit(LOG_CRIT, "%s: %s notification: %s, %s",
+			    p, dir, errnames[errcode], suberrname);
 	}
 	free(p);
 }
@@ -318,6 +326,9 @@ log_conn_attempt(const struct peer *peer, struct sockaddr *sa)
 		b = log_sockaddr(sa);
 		logit(LOG_INFO, "connection from non-peer %s refused", b);
 	} else {
+		/* only log if there is a chance that the session may come up */
+		if (peer->conf.down && peer->state == STATE_IDLE)
+			return;
 		p = log_fmt_peer(&peer->conf);
 		logit(LOG_INFO, "Connection attempt from %s while session is "
 		    "in state %s", p, statenames[peer->state]);
diff --git a/bgpd/mrt.c b/bgpd/mrt.c
index 5fa497a..d2bfe64 100644
--- a/bgpd/mrt.c
+++ b/bgpd/mrt.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: mrt.c,v 1.63 2009/06/29 12:22:16 claudio Exp $ */
+/*	$OpenBSD: mrt.c,v 1.70 2010/09/02 14:03:21 sobrado Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -32,20 +32,20 @@
 
 #include "mrt.h"
 
-int mrt_attr_dump(struct buf *, struct rde_aspath *, struct bgpd_addr *);
+int mrt_attr_dump(struct ibuf *, struct rde_aspath *, struct bgpd_addr *);
 int mrt_dump_entry_mp(struct mrt *, struct prefix *, u_int16_t,
     struct rde_peer*);
 int mrt_dump_entry(struct mrt *, struct prefix *, u_int16_t, struct rde_peer*);
-int mrt_dump_hdr_se(struct buf **, struct peer *, u_int16_t, u_int16_t,
+int mrt_dump_hdr_se(struct ibuf **, struct peer *, u_int16_t, u_int16_t,
     u_int32_t, int);
-int mrt_dump_hdr_rde(struct buf **, u_int16_t type, u_int16_t, u_int32_t);
+int mrt_dump_hdr_rde(struct ibuf **, u_int16_t type, u_int16_t, u_int32_t);
 int mrt_open(struct mrt *, time_t);
 
 #define DUMP_BYTE(x, b)							\
 	do {								\
 		u_char		t = (b);				\
-		if (buf_add((x), &t, sizeof(t)) == -1) {		\
-			log_warnx("mrt_dump1: buf_add error");		\
+		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
+			log_warnx("mrt_dump1: ibuf_add error");		\
 			goto fail;					\
 		}							\
 	} while (0)
@@ -54,8 +54,8 @@ int mrt_open(struct mrt *, time_t);
 	do {								\
 		u_int16_t	t;					\
 		t = htons((s));						\
-		if (buf_add((x), &t, sizeof(t)) == -1) {		\
-			log_warnx("mrt_dump2: buf_add error");		\
+		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
+			log_warnx("mrt_dump2: ibuf_add error");		\
 			goto fail;					\
 		}							\
 	} while (0)
@@ -64,8 +64,8 @@ int mrt_open(struct mrt *, time_t);
 	do {								\
 		u_int32_t	t;					\
 		t = htonl((l));						\
-		if (buf_add((x), &t, sizeof(t)) == -1) {		\
-			log_warnx("mrt_dump3: buf_add error");		\
+		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
+			log_warnx("mrt_dump3: ibuf_add error");		\
 			goto fail;					\
 		}							\
 	} while (0)
@@ -73,8 +73,8 @@ int mrt_open(struct mrt *, time_t);
 #define DUMP_NLONG(x, l)						\
 	do {								\
 		u_int32_t	t = (l);				\
-		if (buf_add((x), &t, sizeof(t)) == -1) {		\
-			log_warnx("mrt_dump4: buf_add error");		\
+		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
+			log_warnx("mrt_dump4: ibuf_add error");		\
 			goto fail;					\
 		}							\
 	} while (0)
@@ -83,55 +83,63 @@ void
 mrt_dump_bgp_msg(struct mrt *mrt, void *pkg, u_int16_t pkglen,
     struct peer *peer)
 {
-	struct buf	*buf;
+	struct ibuf	*buf;
 	int		 incoming = 0;
+	u_int16_t	 subtype = BGP4MP_MESSAGE;
+
+	if (peer->capa.neg.as4byte)
+		subtype = BGP4MP_MESSAGE_AS4;
 
 	/* get the direction of the message to swap address and AS fields */
 	if (mrt->type == MRT_ALL_IN || mrt->type == MRT_UPDATE_IN)
 		incoming = 1;
 
-	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
+	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
 	    pkglen, incoming) == -1)
 		return;
 
-	if (buf_add(buf, pkg, pkglen) == -1) {
+	if (ibuf_add(buf, pkg, pkglen) == -1) {
 		log_warnx("mrt_dump_bgp_msg: buf_add error");
-		buf_free(buf);
+		ibuf_free(buf);
 		return;
 	}
 
-	buf_close(&mrt->wbuf, buf);
+	ibuf_close(&mrt->wbuf, buf);
 }
 
 void
 mrt_dump_state(struct mrt *mrt, u_int16_t old_state, u_int16_t new_state,
     struct peer *peer)
 {
-	struct buf	*buf;
+	struct ibuf	*buf;
+	u_int16_t	 subtype = BGP4MP_STATE_CHANGE;
+
+	if (peer->capa.neg.as4byte)
+		subtype = BGP4MP_STATE_CHANGE_AS4;
 
-	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
+	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
 	    2 * sizeof(short), 0) == -1)
 		return;
 
 	DUMP_SHORT(buf, old_state);
 	DUMP_SHORT(buf, new_state);
 
-	buf_close(&mrt->wbuf, buf);
+	ibuf_close(&mrt->wbuf, buf);
 	return;
 
 fail:
-	buf_free(buf);
+	ibuf_free(buf);
 }
 
 int
-mrt_attr_dump(struct buf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop)
+mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop)
 {
 	struct attr	*oa;
 	u_char		*pdata;
 	u_int32_t	 tmp;
 	int		 neednewpath = 0;
-	u_int16_t	 plen;
-	u_int8_t	 l;
+	u_int16_t	 plen, afi;
+	u_int8_t	 l, mpattr[21];
 
 	/* origin */
 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN,
@@ -141,11 +149,14 @@ mrt_attr_dump(struct buf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop)
 	/* aspath */
 	pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
 	pdata = aspath_deflate(pdata, &plen, &neednewpath);
-	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata, plen) == -1)
+	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata,
+	    plen) == -1) {
+		free(pdata);
 		return (-1);
+	}
 	free(pdata);
 
-	if (nexthop) {
+	if (nexthop && nexthop->aid == AID_INET) {
 		/* nexthop, already network byte order */
 		if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_NEXTHOP,
 		    &nexthop->v4.s_addr, 4) ==	-1)
@@ -173,12 +184,27 @@ mrt_attr_dump(struct buf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop)
 			return (-1);
 	}
 
+	if (nexthop && nexthop->aid != AID_INET) {
+		if (aid2afi(nexthop->aid, &afi, &mpattr[2]))
+			return (-1);
+		afi = htons(afi);
+		memcpy(mpattr, &afi, sizeof(afi));
+		mpattr[3] = sizeof(struct in6_addr);
+		memcpy(&mpattr[4], &nexthop->v6, sizeof(struct in6_addr));
+		mpattr[20] = 0; /* Reserved must be 0 */
+		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI,
+		    mpattr, sizeof(mpattr)) == -1)
+			return (-1);
+	}
+
 	if (neednewpath) {
 		pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
 		if (plen != 0)
 			if (attr_writebuf(buf, ATTR_OPTIONAL|ATTR_TRANSITIVE,
-			    ATTR_AS4_PATH, pdata, plen) == -1)
+			    ATTR_AS4_PATH, pdata, plen) == -1) {
+				free(pdata);
 				return (-1);
+			}
 		free(pdata);
 	}
 
@@ -189,14 +215,14 @@ int
 mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
     struct rde_peer *peer)
 {
-	struct buf	*buf, *hbuf = NULL, *h2buf = NULL;
+	struct ibuf	*buf, *hbuf = NULL, *h2buf = NULL;
 	void		*bptr;
 	struct bgpd_addr addr, nexthop, *nh;
 	u_int16_t	 len;
 	u_int8_t	 p_len;
-	sa_family_t	 af;
+	u_int8_t	 aid;
 
-	if ((buf = buf_dynamic(0, MAX_PKTSIZE)) == NULL) {
+	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
 		log_warn("mrt_dump_entry_mp: buf_dynamic");
 		return (-1);
 	}
@@ -205,9 +231,9 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 		log_warnx("mrt_dump_entry_mp: mrt_attr_dump error");
 		goto fail;
 	}
-	len = buf_size(buf);
+	len = ibuf_size(buf);
 
-	if ((h2buf = buf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE +
+	if ((h2buf = ibuf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE +
 	    MRT_BGP4MP_IPv4_ENTRY_SIZE, MRT_BGP4MP_IPv6_HEADER_SIZE +
 	    MRT_BGP4MP_IPv6_ENTRY_SIZE + MRT_BGP4MP_MAX_PREFIXLEN)) == NULL) {
 		log_warn("mrt_dump_entry_mp: buf_dynamic");
@@ -219,25 +245,26 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 	DUMP_SHORT(h2buf, /* ifindex */ 0);
 
 	/* XXX is this for peer self? */
-	af = peer->remote_addr.af == 0 ? p->prefix->af : peer->remote_addr.af;
-	switch (af) {
-	case AF_INET:
+	aid = peer->remote_addr.aid == AID_UNSPEC ? p->prefix->aid :
+	     peer->remote_addr.aid;
+	switch (aid) {
+	case AID_INET:
 		DUMP_SHORT(h2buf, AFI_IPv4);
 		DUMP_NLONG(h2buf, peer->local_v4_addr.v4.s_addr);
 		DUMP_NLONG(h2buf, peer->remote_addr.v4.s_addr);
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		DUMP_SHORT(h2buf, AFI_IPv6);
-		if (buf_add(h2buf, &peer->local_v6_addr.v6,
+		if (ibuf_add(h2buf, &peer->local_v6_addr.v6,
 		    sizeof(struct in6_addr)) == -1 ||
-		    buf_add(h2buf, &peer->remote_addr.v6,
+		    ibuf_add(h2buf, &peer->remote_addr.v6,
 		    sizeof(struct in6_addr)) == -1) {
 			log_warnx("mrt_dump_entry_mp: buf_add error");
 			goto fail;
 		}
 		break;
 	default:
-		log_warnx("king bula found new AF %d in mrt_dump_entry_mp", af);
+		log_warnx("king bula found new AF in mrt_dump_entry_mp");
 		goto fail;
 	}
 
@@ -247,24 +274,24 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 
 	if (p->aspath->nexthop == NULL) {
 		bzero(&nexthop, sizeof(struct bgpd_addr));
-		nexthop.af = addr.af;
+		nexthop.aid = addr.aid;
 		nh = &nexthop;
 	} else
 		nh = &p->aspath->nexthop->exit_nexthop;
 
 	pt_getaddr(p->prefix, &addr);
-	switch (addr.af) {
-	case AF_INET:
+	switch (addr.aid) {
+	case AID_INET:
 		DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
 		DUMP_BYTE(h2buf, 4);		/* nhlen */
 		DUMP_NLONG(h2buf, nh->v4.s_addr);	/* nexthop */
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
 		DUMP_BYTE(h2buf, 16);		/* nhlen */
-		if (buf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
+		if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
 			log_warnx("mrt_dump_entry_mp: buf_add error");
 			goto fail;
 		}
@@ -275,7 +302,7 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 	}
 
 	p_len = PREFIX_SIZE(p->prefix->prefixlen);
-	if ((bptr = buf_reserve(h2buf, p_len)) == NULL) {
+	if ((bptr = ibuf_reserve(h2buf, p_len)) == NULL) {
 		log_warnx("mrt_dump_entry_mp: buf_reserve error");
 		goto fail;
 	}
@@ -285,24 +312,24 @@ mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 	}
 
 	DUMP_SHORT(h2buf, len);
-	len += buf_size(h2buf);
+	len += ibuf_size(h2buf);
 
 	if (mrt_dump_hdr_rde(&hbuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY,
 	    len) == -1)
 		goto fail;
 
-	buf_close(&mrt->wbuf, hbuf);
-	buf_close(&mrt->wbuf, h2buf);
-	buf_close(&mrt->wbuf, buf);
+	ibuf_close(&mrt->wbuf, hbuf);
+	ibuf_close(&mrt->wbuf, h2buf);
+	ibuf_close(&mrt->wbuf, buf);
 
 	return (len + MRT_HEADER_SIZE);
 
 fail:
 	if (hbuf)
-		buf_free(hbuf);
-	if (h2buf);
-		buf_free(h2buf);
-	buf_free(buf);
+		ibuf_free(hbuf);
+	if (h2buf)
+		ibuf_free(h2buf);
+	ibuf_free(buf);
 	return (-1);
 }
 
@@ -310,34 +337,37 @@ int
 mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum,
     struct rde_peer *peer)
 {
-	struct buf	*buf, *hbuf;
+	struct ibuf	*buf, *hbuf;
 	struct bgpd_addr addr, *nh;
 	size_t		 len;
+	u_int16_t	 subtype;
+	u_int8_t	 dummy;
 
-	if (p->prefix->af != AF_INET && peer->remote_addr.af == AF_INET)
-		/* only able to dump IPv4 */
+	if (p->prefix->aid != peer->remote_addr.aid &&
+	    p->prefix->aid != AID_INET && p->prefix->aid != AID_INET6)
+		/* only able to dump pure IPv4/IPv6 */
 		return (0);
 
-	if ((buf = buf_dynamic(0, MAX_PKTSIZE)) == NULL) {
+	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
 		log_warnx("mrt_dump_entry: buf_dynamic");
 		return (-1);
 	}
 
 	if (p->aspath->nexthop == NULL) {
 		bzero(&addr, sizeof(struct bgpd_addr));
-		addr.af = AF_INET;
+		addr.aid = p->prefix->aid;
 		nh = &addr;
 	} else
 		nh = &p->aspath->nexthop->exit_nexthop;
 	if (mrt_attr_dump(buf, p->aspath, nh) == -1) {
 		log_warnx("mrt_dump_entry: mrt_attr_dump error");
-		buf_free(buf);
+		ibuf_free(buf);
 		return (-1);
 	}
-	len = buf_size(buf);
-
-	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, AFI_IPv4, len) == -1) {
-		buf_free(buf);
+	len = ibuf_size(buf);
+	aid2afi(p->prefix->aid, &subtype, &dummy);
+	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, subtype, len) == -1) {
+		ibuf_free(buf);
 		return (-1);
 	}
 
@@ -345,23 +375,44 @@ mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum,
 	DUMP_SHORT(hbuf, snum);
 
 	pt_getaddr(p->prefix, &addr);
-	DUMP_NLONG(hbuf, addr.v4.s_addr);
+	switch (p->prefix->aid) {
+	case AID_INET:
+		DUMP_NLONG(hbuf, addr.v4.s_addr);
+		break;
+	case AID_INET6:
+		if (ibuf_add(hbuf, &addr.v6, sizeof(struct in6_addr)) == -1) {
+			log_warnx("mrt_dump_entry: buf_add error");
+			goto fail;
+		}
+		break;
+	}
 	DUMP_BYTE(hbuf, p->prefix->prefixlen);
 
 	DUMP_BYTE(hbuf, 1);		/* state */
 	DUMP_LONG(hbuf, p->lastchange);	/* originated */
-	DUMP_NLONG(hbuf, peer->remote_addr.v4.s_addr);
+	switch (p->prefix->aid) {
+	case AID_INET:
+		DUMP_NLONG(hbuf, peer->remote_addr.v4.s_addr);
+		break;
+	case AID_INET6:
+		if (ibuf_add(hbuf, &peer->remote_addr.v6,
+		    sizeof(struct in6_addr)) == -1) {
+			log_warnx("mrt_dump_entry: buf_add error");
+			goto fail;
+		}
+		break;
+	}
 	DUMP_SHORT(hbuf, peer->short_as);
 	DUMP_SHORT(hbuf, len);
 
-	buf_close(&mrt->wbuf, hbuf);
-	buf_close(&mrt->wbuf, buf);
+	ibuf_close(&mrt->wbuf, hbuf);
+	ibuf_close(&mrt->wbuf, buf);
 
 	return (len + MRT_HEADER_SIZE);
 
 fail:
-	buf_free(hbuf);
-	buf_free(buf);
+	ibuf_free(hbuf);
+	ibuf_free(buf);
 	return (-1);
 }
 
@@ -387,7 +438,7 @@ mrt_dump_upcall(struct rib_entry *re, void *ptr)
 }
 
 void
-mrt_dump_done(void *ptr)
+mrt_done(void *ptr)
 {
 	struct mrt		*mrtbuf = ptr;
 
@@ -395,12 +446,12 @@ mrt_dump_done(void *ptr)
 }
 
 int
-mrt_dump_hdr_se(struct buf ** bp, struct peer *peer, u_int16_t type,
+mrt_dump_hdr_se(struct ibuf ** bp, struct peer *peer, u_int16_t type,
     u_int16_t subtype, u_int32_t len, int swap)
 {
 	time_t	 	now;
 
-	if ((*bp = buf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
+	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + len)) == NULL) {
 		log_warnx("mrt_dump_hdr_se: buf_open error");
 		return (-1);
@@ -468,20 +519,20 @@ mrt_dump_hdr_se(struct buf ** bp, struct peer *peer, u_int16_t type,
 	case AF_INET6:
 		DUMP_SHORT(*bp, AFI_IPv6);
 		if (!swap)
-			if (buf_add(*bp, &((struct sockaddr_in6 *)
+			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
 			    &peer->sa_local)->sin6_addr,
 			    sizeof(struct in6_addr)) == -1) {
 				log_warnx("mrt_dump_hdr_se: buf_add error");
 				goto fail;
 			}
-		if (buf_add(*bp,
+		if (ibuf_add(*bp,
 		    &((struct sockaddr_in6 *)&peer->sa_remote)->sin6_addr,
 		    sizeof(struct in6_addr)) == -1) {
 			log_warnx("mrt_dump_hdr_se: buf_add error");
 			goto fail;
 		}
 		if (swap)
-			if (buf_add(*bp, &((struct sockaddr_in6 *)
+			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
 			    &peer->sa_local)->sin6_addr,
 			    sizeof(struct in6_addr)) == -1) {
 				log_warnx("mrt_dump_hdr_se: buf_add error");
@@ -493,17 +544,17 @@ mrt_dump_hdr_se(struct buf ** bp, struct peer *peer, u_int16_t type,
 	return (0);
 
 fail:
-	buf_free(*bp);
+	ibuf_free(*bp);
 	return (-1);
 }
 
 int
-mrt_dump_hdr_rde(struct buf **bp, u_int16_t type, u_int16_t subtype,
+mrt_dump_hdr_rde(struct ibuf **bp, u_int16_t type, u_int16_t subtype,
     u_int32_t len)
 {
 	time_t		 now;
 
-	if ((*bp = buf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
+	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + MRT_BGP4MP_IPv6_ENTRY_SIZE)) ==
 	    NULL) {
 		log_warnx("mrt_dump_hdr_rde: buf_dynamic error");
@@ -517,7 +568,15 @@ mrt_dump_hdr_rde(struct buf **bp, u_int16_t type, u_int16_t subtype,
 
 	switch (type) {
 	case MSG_TABLE_DUMP:
-		DUMP_LONG(*bp, MRT_DUMP_HEADER_SIZE + len);
+		switch (subtype) {
+		case AFI_IPv4:
+			len += MRT_DUMP_HEADER_SIZE;
+			break;
+		case AFI_IPv6:
+			len += MRT_DUMP_HEADER_SIZE_V6;
+			break;
+		}
+		DUMP_LONG(*bp, len);
 		break;
 	case MSG_PROTOCOL_BGP4MP:
 		DUMP_LONG(*bp, len);
@@ -525,11 +584,11 @@ mrt_dump_hdr_rde(struct buf **bp, u_int16_t type, u_int16_t subtype,
 	default:
 		log_warnx("mrt_dump_hdr_rde: unsupported type");
 		goto fail;
-	}	
+	}
 	return (0);
 
 fail:
-	buf_free(*bp);
+	ibuf_free(*bp);
 	return (-1);
 }
 
@@ -538,21 +597,22 @@ mrt_write(struct mrt *mrt)
 {
 	int	r;
 
-	if ((r = buf_write(&mrt->wbuf)) < 0) {
+	if ((r = ibuf_write(&mrt->wbuf)) < 0) {
 		log_warn("mrt dump aborted, mrt_write");
 		mrt_clean(mrt);
+		mrt_done(mrt);
 	}
 }
 
 void
 mrt_clean(struct mrt *mrt)
 {
-	struct buf	*b;
+	struct ibuf	*b;
 
 	close(mrt->wbuf.fd);
 	while ((b = TAILQ_FIRST(&mrt->wbuf.bufs))) {
 		TAILQ_REMOVE(&mrt->wbuf.bufs, b, entry);
-		buf_free(b);
+		ibuf_free(b);
 	}
 	mrt->wbuf.queued = 0;
 }
diff --git a/bgpd/mrt.h b/bgpd/mrt.h
index 4f43cd9..d88c01c 100644
--- a/bgpd/mrt.h
+++ b/bgpd/mrt.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: mrt.h,v 1.23 2009/06/29 12:22:16 claudio Exp $ */
+/*	$OpenBSD: mrt.h,v 1.27 2010/06/04 10:13:00 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -18,12 +18,10 @@
 #ifndef __MRT_H__
 #define __MRT_H__
 
-#include "bgpd.h"
-
 /*
  * MRT binary packet format
  * For more info see:
- * draft-ietf-grow-mrt-04.txt, "MRT routing information export format"
+ * draft-ietf-grow-mrt-11.txt, "MRT routing information export format"
  * http://www.quagga.net/docs/docs-multi/Packet-Binary-Dump-Format.html
  */
 
@@ -75,8 +73,10 @@ enum MRT_BGP4MP_TYPES {
 	BGP4MP_MESSAGE,		/* bgp message */
 	BGP4MP_ENTRY,		/* table dumps (deprecated) */
 	BGP4MP_SNAPSHOT,	/* file name for dump (deprecated) */
+	BGP4MP_MESSAGE_AS4,	/* same as BGP4MP_MESSAGE with 4byte AS */
 	BGP4MP_STATE_CHANGE_AS4,
-	BGP4MP_MESSAGE_AS4	/* same as BGP4MP_MESSAGE with 4byte AS */
+	BGP4MP_MESSAGE_LOCAL,	  /* same as BGP4MP_MESSAGE but for self */
+	BGP4MP_MESSAGE_AS4_LOCAL  /* originated updates. Not implemented */
 };
 
 /* size of the BGP4MP headers without payload */
@@ -184,6 +184,7 @@ enum MRT_BGP4MP_TYPES {
 
 /* size of the dump header until attr_len */
 #define MRT_DUMP_HEADER_SIZE	22
+#define MRT_DUMP_HEADER_SIZE_V6	46
 
 /*
  * OLD MRT message headers. These structs are here for completion but
@@ -235,7 +236,7 @@ enum MRT_BGP_TYPES {
  * |    new_state    |
  * +--------+--------+
  *
- * State are defined in RFC 1771.
+ * State are defined in RFC 1771/4271.
  */
 
 /*
@@ -303,7 +304,7 @@ void		 mrt_dump_state(struct mrt *, u_int16_t, u_int16_t,
 		     struct peer *);
 void		 mrt_clear_seq(void);
 void		 mrt_dump_upcall(struct rib_entry *, void *);
-void		 mrt_dump_done(void *);
+void		 mrt_done(void *);
 void		 mrt_write(struct mrt *);
 void		 mrt_clean(struct mrt *);
 void		 mrt_init(struct imsgbuf *, struct imsgbuf *);
diff --git a/bgpd/parse.y b/bgpd/parse.y
index 175a9a0..d76ac6a 100644
--- a/bgpd/parse.y
+++ b/bgpd/parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.231 2009/06/06 01:10:29 claudio Exp $ */
+/*	$OpenBSD: parse.y,v 1.250 2010/03/31 18:53:23 claudio Exp $ */
 
 /*
  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -25,7 +25,10 @@
 #include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+#include <netmpls/mpls.h>
+#endif
+	
 #include <ctype.h>
 #include <err.h>
 #include <unistd.h>
@@ -74,10 +77,12 @@ char		*symget(const char *);
 
 static struct bgpd_config	*conf;
 static struct mrt_head		*mrtconf;
-static struct network_head	*netconf;
+static struct network_head	*netconf, *gnetconf;
 static struct peer		*peer_l, *peer_l_old;
 static struct peer		*curpeer;
 static struct peer		*curgroup;
+static struct rdomain		*currdom;
+static struct rdomain_head	*rdom_l;
 static struct filter_head	*filter_l;
 static struct filter_head	*peerfilter_l;
 static struct filter_head	*groupfilter_l;
@@ -105,7 +110,7 @@ struct filter_match_l {
 	struct filter_match	 m;
 	struct filter_prefix_l	*prefix_l;
 	struct filter_as_l	*as_l;
-	sa_family_t		 af;
+	u_int8_t		 aid;
 } fmopts;
 
 struct peer	*alloc_peer(void);
@@ -113,8 +118,8 @@ struct peer	*new_peer(void);
 struct peer	*new_group(void);
 int		 add_mrtconfig(enum mrt_type, char *, time_t, struct peer *,
 		    char *);
-int		 add_rib(char *, u_int16_t);
-int		 find_rib(char *);
+int		 add_rib(char *, u_int, u_int16_t);
+struct rde_rib	*find_rib(char *);
 int		 get_id(struct peer *);
 int		 expand_rule(struct filter_rule *, struct filter_peers_l *,
 		    struct filter_match_l *, struct filter_set_head *);
@@ -123,12 +128,14 @@ int		 neighbor_consistent(struct peer *);
 int		 merge_filterset(struct filter_set_head *, struct filter_set *);
 void		 copy_filterset(struct filter_set_head *,
 		    struct filter_set_head *);
-void		 move_filterset(struct filter_set_head *,
-		    struct filter_set_head *);
 struct filter_rule	*get_rule(enum action_types);
 
 int		 getcommunity(char *);
-int		 parsecommunity(char *, int *, int *);
+int		 parsecommunity(struct filter_community *, char *);
+int		 parsesubtype(char *);
+int		 parseextvalue(char *, u_int32_t *);
+int		 parseextcommunity(struct filter_extcommunity *, char *,
+		    char *);
 
 typedef struct {
 	union {
@@ -159,29 +166,33 @@ typedef struct {
 %}
 
 %token	AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE RTABLE
+%token	RDOMAIN RD EXPORTTRGT IMPORTTRGT
 %token	RDE RIB EVALUATE IGNORE COMPARE
 %token	GROUP NEIGHBOR NETWORK
-%token	REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
-%token	ANNOUNCE DEMOTE CONNECTRETRY
-%token	ENFORCE NEIGHBORAS CAPABILITIES REFLECTOR DEPEND DOWN SOFTRECONFIG
-%token	DUMP IN OUT
+%token	REMOTEAS DESCR LLIFACE LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
+%token	ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY
+%token	DEMOTE ENFORCE NEIGHBORAS REFLECTOR DEPEND DOWN SOFTRECONFIG
+%token	DUMP IN OUT SOCKET RESTRICTED
 %token	LOG ROUTECOLL TRANSPARENT
 %token	TCP MD5SIG PASSWORD KEY TTLSECURITY
 %token	ALLOW DENY MATCH
 %token	QUICK
 %token	FROM TO ANY
 %token	CONNECTED STATIC
-%token	PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS COMMUNITY DELETE
+%token	COMMUNITY EXTCOMMUNITY
+%token	PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN MAXASSEQ
 %token	SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF
-%token	PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL
+%token	PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN
 %token	ERROR INCLUDE
 %token	IPSEC ESP AH SPI IKE
 %token	IPV4 IPV6
 %token	QUALIFY VIA
+%token	NE LE GE XRANGE
 %token	<v.string>		STRING
 %token	<v.number>		NUMBER
-%type	<v.number>		asnumber as4number optnumber yesno inout
-%type	<v.number>		espah family restart
+%type	<v.number>		asnumber as4number optnumber
+%type	<v.number>		espah family restart origincode nettype
+%type	<v.number>		yesno inout restricted
 %type	<v.string>		string filter_rib
 %type	<v.addr>		address
 %type	<v.prefix>		prefix addrspec
@@ -204,6 +215,7 @@ grammar		: /* empty */
 		| grammar include '\n'
 		| grammar conf_main '\n'
 		| grammar varset '\n'
+		| grammar rdomain '\n'
 		| grammar neighbor '\n'
 		| grammar group '\n'
 		| grammar filterrule '\n'
@@ -211,8 +223,12 @@ grammar		: /* empty */
 		;
 
 asnumber	: NUMBER			{
-			if ($1 < 0 || $1 >= ASNUM_MAX) {
-				yyerror("AS too big: max %u", ASNUM_MAX - 1);
+			/*
+			 * Accroding to iana 65535 and 4294967295 are reserved
+			 * but enforcing this is not duty of the parser.
+			 */
+			if ($1 < 0 || $1 > UINT_MAX) {
+				yyerror("AS too big: max %u", UINT_MAX);
 				YYERROR;
 			}
 		}
@@ -274,6 +290,8 @@ yesno		:  STRING			{
 			else if (!strcmp($1, "no"))
 				$$ = 0;
 			else {
+				yyerror("syntax error, "
+				    "either yes or no expected");
 				free($1);
 				YYERROR;
 			}
@@ -318,7 +336,7 @@ conf_main	: AS as4number		{
 			conf->short_as = $3;
 		}
 		| ROUTERID address		{
-			if ($2.af != AF_INET) {
+			if ($2.aid != AID_INET) {
 				yyerror("router-id must be an IPv4 address");
 				YYERROR;
 			}
@@ -342,42 +360,25 @@ conf_main	: AS as4number		{
 		}
 		| LISTEN ON address	{
 			struct listen_addr	*la;
-			struct sockaddr_in	*in;
-			struct sockaddr_in6	*in6;
 
 			if ((la = calloc(1, sizeof(struct listen_addr))) ==
 			    NULL)
 				fatal("parse conf_main listen on calloc");
 
 			la->fd = -1;
-			la->sa.ss_family = $3.af;
-			switch ($3.af) {
-			case AF_INET:
-				la->sa.ss_len = sizeof(struct sockaddr_in);
-				in = (struct sockaddr_in *)&la->sa;
-				in->sin_addr.s_addr = $3.v4.s_addr;
-				in->sin_port = htons(BGP_PORT);
-				break;
-			case AF_INET6:
-				la->sa.ss_len = sizeof(struct sockaddr_in6);
-				in6 = (struct sockaddr_in6 *)&la->sa;
-				memcpy(&in6->sin6_addr, &$3.v6,
-				    sizeof(in6->sin6_addr));
-				in6->sin6_port = htons(BGP_PORT);
-				break;
-			default:
-				yyerror("king bula does not like family %u",
-				    $3.af);
-				YYERROR;
-			}
-
+			memcpy(&la->sa, addr2sa(&$3, BGP_PORT), sizeof(la->sa));
 			TAILQ_INSERT_TAIL(listen_addrs, la, entry);
 		}
 		| FIBUPDATE yesno		{
+			struct rde_rib *rr;
+			rr = find_rib("Loc-RIB");
+			if (rr == NULL)
+				fatalx("RTABLE can not find the main RIB!");
+
 			if ($2 == 0)
-				conf->flags |= BGPD_FLAG_NO_FIB_UPDATE;
+				rr->flags |= F_RIB_NOFIBSYNC;
 			else
-				conf->flags &= ~BGPD_FLAG_NO_FIB_UPDATE;
+				rr->flags &= ~F_RIB_NOFIBSYNC;
 		}
 		| ROUTECOLL yesno	{
 			if ($2 == 1)
@@ -386,7 +387,7 @@ conf_main	: AS as4number		{
 				conf->flags &= ~BGPD_FLAG_NO_EVALUATE;
 		}
 		| RDE RIB STRING {
-			if (add_rib($3, F_RIB_NOFIB)) {
+			if (add_rib($3, 0, F_RIB_NOFIB)) {
 				free($3);
 				YYERROR;
 			}
@@ -395,9 +396,27 @@ conf_main	: AS as4number		{
 		| RDE RIB STRING yesno EVALUATE {
 			if ($4) {
 				free($3);
+				yyerror("bad rde rib definition");
 				YYERROR;
 			}
-			if (!add_rib($3, F_RIB_NOEVALUATE)) {
+			if (add_rib($3, 0, F_RIB_NOFIB | F_RIB_NOEVALUATE)) {
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		| RDE RIB STRING RTABLE NUMBER {
+			if (add_rib($3, $5, 0)) {
+				free($3);
+				YYERROR;
+			}
+			free($3);
+		}
+		| RDE RIB STRING RTABLE NUMBER FIBUPDATE yesno {
+			int	flags = 0;
+			if ($7 == 0)
+				flags = F_RIB_NOFIBSYNC;
+			if (add_rib($3, $5, flags)) {
 				free($3);
 				YYERROR;
 			}
@@ -418,59 +437,7 @@ conf_main	: AS as4number		{
 			}
 			free($2);
 		}
-		| NETWORK prefix filter_set	{
-			struct network	*n;
-
-			if ((n = calloc(1, sizeof(struct network))) == NULL)
-				fatal("new_network");
-			memcpy(&n->net.prefix, &$2.prefix,
-			    sizeof(n->net.prefix));
-			n->net.prefixlen = $2.len;
-			move_filterset($3, &n->net.attrset);
-			free($3);
-
-			TAILQ_INSERT_TAIL(netconf, n, entry);
-		}
-		| NETWORK family STATIC filter_set	{
-			if ($2 == AFI_IPv4) {
-				conf->flags |= BGPD_FLAG_REDIST_STATIC;
-				move_filterset($4, &conf->staticset);
-			} else if ($2 == AFI_IPv6) {
-				conf->flags |= BGPD_FLAG_REDIST6_STATIC;
-				move_filterset($4, &conf->staticset6);
-			} else {
-				yyerror("unknown family");
-				free($4);
-				YYERROR;
-			}
-			free($4);
-		}
-		| NETWORK family CONNECTED filter_set	{
-			if ($2 == AFI_IPv4) {
-				conf->flags |= BGPD_FLAG_REDIST_CONNECTED;
-				move_filterset($4, &conf->connectset);
-			} else if ($2 == AFI_IPv6) {
-				conf->flags |= BGPD_FLAG_REDIST6_CONNECTED;
-				move_filterset($4, &conf->connectset6);
-			} else {
-				yyerror("unknown family");
-				free($4);
-				YYERROR;
-			}
-			free($4);
-		}
-		| NETWORK STATIC filter_set	{
-			/* keep for compatibility till after next release */
-			conf->flags |= BGPD_FLAG_REDIST_STATIC;
-			move_filterset($3, &conf->staticset);
-			free($3);
-		}
-		| NETWORK CONNECTED filter_set	{
-			/* keep for compatibility till after next release */
-			conf->flags |= BGPD_FLAG_REDIST_CONNECTED;
-			move_filterset($3, &conf->connectset);
-			free($3);
-		}
+		| network
 		| DUMP STRING STRING optnumber		{
 			int action;
 
@@ -575,11 +542,20 @@ conf_main	: AS as4number		{
 			free($4);
 		}
 		| RTABLE NUMBER {
-			if ($2 > RT_TABLEID_MAX || $2 < 0) {
-				yyerror("invalid rtable id");
+#if defined(__FreeBSD__)	/* FreeBSD does not support RTABLE */
+			yyerror("rtable id not supported in FreeBSD, yet");
+			YYERROR;
+#else
+			struct rde_rib *rr;
+			if (ktable_exists($2, NULL) != 1) {
+				yyerror("rtable id %lld does not exist", $2);
 				YYERROR;
 			}
-			conf->rtableid = $2;
+			rr = find_rib("Loc-RIB");
+			if (rr == NULL)
+				fatalx("RTABLE can not find the main RIB!");
+			rr->rtableid = $2;
+#endif /* defined(__FreeBSD__) */
 		}
 		| CONNECTRETRY NUMBER {
 			if ($2 > USHRT_MAX || $2 < 1) {
@@ -588,6 +564,15 @@ conf_main	: AS as4number		{
 			}
 			conf->connectretry = $2;
 		}
+		| SOCKET STRING	restricted {
+			if ($3) {
+				free(conf->rcsock);
+				conf->rcsock = $2;
+			} else {
+				free(conf->csock);
+				conf->csock = $2;
+			}
+		}
 		;
 
 mrtdump		: DUMP STRING inout STRING optnumber	{
@@ -620,10 +605,47 @@ mrtdump		: DUMP STRING inout STRING optnumber	{
 		}
 		;
 
+network		: NETWORK prefix filter_set	{
+			struct network	*n;
+
+			if ((n = calloc(1, sizeof(struct network))) == NULL)
+				fatal("new_network");
+			memcpy(&n->net.prefix, &$2.prefix,
+			    sizeof(n->net.prefix));
+			n->net.prefixlen = $2.len;
+			filterset_move($3, &n->net.attrset);
+			free($3);
+
+			TAILQ_INSERT_TAIL(netconf, n, entry);
+		}
+		| NETWORK family nettype filter_set	{
+			struct network	*n;
+
+			if ((n = calloc(1, sizeof(struct network))) == NULL)
+				fatal("new_network");
+			if (afi2aid($2, SAFI_UNICAST, &n->net.prefix.aid) ==
+			    -1) {
+				yyerror("unknown family");
+				filterset_free($4);
+				free($4);
+				YYERROR;
+			}
+			n->net.type = $3 ? NETWORK_STATIC : NETWORK_CONNECTED;
+			filterset_move($4, &n->net.attrset);
+			free($4);
+
+			TAILQ_INSERT_TAIL(netconf, n, entry);
+		}
+		;
+
 inout		: IN		{ $$ = 1; }
 		| OUT		{ $$ = 0; }
 		;
 
+restricted	: RESTRICTED	{ $$ = 1; }
+		| /* nothing */	{ $$ = 0; }
+		;
+
 address		: STRING		{
 			u_int8_t	len;
 
@@ -635,11 +657,11 @@ address		: STRING		{
 			}
 			free($1);
 
-			if (($$.af == AF_INET && len != 32) ||
-			    ($$.af == AF_INET6 && len != 128)) {
+			if (($$.aid == AID_INET && len != 32) ||
+			    ($$.aid == AID_INET6 && len != 128)) {
 				/* unreachable */
 				yyerror("got prefixlen %u, expected %u",
-				    len, $$.af == AF_INET ? 32 : 128);
+				    len, $$.aid == AID_INET ? 32 : 128);
 				YYERROR;
 			}
 		}
@@ -653,7 +675,7 @@ prefix		: STRING '/' NUMBER	{
 				free($1);
 				YYERROR;
 			}
-			if (asprintf(&s, "%s/%lld", $1, $3) == -1)
+			if (asprintf(&s, "%s/%lld", $1, (long long int)$3) == -1)
 				fatal(NULL);
 			free($1);
 
@@ -672,7 +694,7 @@ prefix		: STRING '/' NUMBER	{
 				yyerror("bad prefix %lld/%lld", $1, $3);
 				YYERROR;
 			}
-			if (asprintf(&s, "%lld/%lld", $1, $3) == -1)
+			if (asprintf(&s, "%lld/%lld", (long long int)$1, (long long int)$3) == -1)
 				fatal(NULL);
 
 			if (!host(s, &$$.prefix, &$$.len)) {
@@ -686,7 +708,7 @@ prefix		: STRING '/' NUMBER	{
 
 addrspec	: address	{
 			memcpy(&$$.prefix, &$1, sizeof(struct bgpd_addr));
-			if ($$.prefix.af == AF_INET)
+			if ($$.prefix.aid == AID_INET)
 				$$.len = 32;
 			else
 				$$.len = 128;
@@ -705,14 +727,150 @@ optnumber	: /* empty */		{ $$ = 0; }
 		| NUMBER
 		;
 
+rdomain		: RDOMAIN NUMBER optnl '{' optnl	{
+			if (ktable_exists($2, NULL) != 1) {
+				yyerror("rdomain %lld does not exist", $2);
+				YYERROR;
+			}
+			if (!(currdom = calloc(1, sizeof(struct rdomain))))
+				fatal(NULL);
+			currdom->rtableid = $2;
+			TAILQ_INIT(&currdom->import);
+			TAILQ_INIT(&currdom->export);
+			TAILQ_INIT(&currdom->net_l);
+			netconf = &currdom->net_l;
+		}
+		    rdomainopts_l '}' {
+			/* insert into list */
+			SIMPLEQ_INSERT_TAIL(rdom_l, currdom, entry);
+			currdom = NULL;
+			netconf = gnetconf;
+		}
+
+rdomainopts_l	: rdomainopts_l rdomainoptsl
+		| rdomainoptsl
+		;
+
+rdomainoptsl	: rdomainopts nl
+		;
+
+rdomainopts	: RD STRING {
+			struct filter_extcommunity	ext;
+			u_int64_t			rd;
+
+			if (parseextcommunity(&ext, "rt", $2) == -1) {
+				free($2);
+				YYERROR;
+			}
+			free($2);
+			/*
+			 * RD is almost encode like an ext-community,
+			 * but only almost so convert here.
+			 */
+			if (community_ext_conv(&ext, 0, &rd)) {
+				yyerror("bad encoding of rd");
+				YYERROR;
+			}
+			rd = betoh64(rd) & 0xffffffffffffULL;
+			switch (ext.type) {
+			case EXT_COMMUNITY_TWO_AS:
+				rd |= (0ULL << 48);
+				break;
+			case EXT_COMMUNITY_IPV4:
+				rd |= (1ULL << 48);
+				break;
+			case EXT_COMMUNITY_FOUR_AS:
+				rd |= (2ULL << 48);
+				break;
+			default:
+				yyerror("bad encoding of rd");
+				YYERROR;
+			}
+			currdom->rd = htobe64(rd);
+		}
+		| EXPORTTRGT STRING STRING	{
+			struct filter_set	*set;
+
+			if ((set = calloc(1, sizeof(struct filter_set))) ==
+			    NULL)
+				fatal(NULL);
+			set->type = ACTION_SET_EXT_COMMUNITY;
+			if (parseextcommunity(&set->action.ext_community,
+			    $2, $3) == -1) {
+				free($3);
+				free($2);
+				free(set);
+				YYERROR;
+			}
+			free($3);
+			free($2);
+			TAILQ_INSERT_TAIL(&currdom->export, set, entry);
+		}
+		| IMPORTTRGT STRING STRING	{
+			struct filter_set	*set;
+
+			if ((set = calloc(1, sizeof(struct filter_set))) ==
+			    NULL)
+				fatal(NULL);
+			set->type = ACTION_SET_EXT_COMMUNITY;
+			if (parseextcommunity(&set->action.ext_community,
+			    $2, $3) == -1) {
+				free($3);
+				free($2);
+				free(set);
+				YYERROR;
+			}
+			free($3);
+			free($2);
+			TAILQ_INSERT_TAIL(&currdom->import, set, entry);
+		}
+		| DESCR string		{
+			if (strlcpy(currdom->descr, $2,
+			    sizeof(currdom->descr)) >=
+			    sizeof(currdom->descr)) {
+				yyerror("descr \"%s\" too long: max %u",
+				    $2, sizeof(currdom->descr) - 1);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
+		| FIBUPDATE yesno		{
+			if ($2 == 0)
+				currdom->flags |= F_RIB_NOFIBSYNC;
+			else
+				currdom->flags &= ~F_RIB_NOFIBSYNC;
+		}
+		| network
+		| DEPEND ON STRING	{
+			/* XXX this is a hack */
+			if (if_nametoindex($3) == 0) {
+				yyerror("interface %s does not exist", $3);
+				free($3);
+				YYERROR;
+			}
+			strlcpy(currdom->ifmpe, $3, IFNAMSIZ);
+			free($3);
+			if (get_mpe_label(currdom)) {
+				yyerror("failed to get mpls label from %s",
+				    currdom->ifmpe);
+				YYERROR;
+			}
+		}
+		;
+
 neighbor	: {	curpeer = new_peer(); }
 		    NEIGHBOR addrspec {
 			memcpy(&curpeer->conf.remote_addr, &$3.prefix,
 			    sizeof(curpeer->conf.remote_addr));
 			curpeer->conf.remote_masklen = $3.len;
-			if (($3.prefix.af == AF_INET && $3.len != 32) ||
-			    ($3.prefix.af == AF_INET6 && $3.len != 128))
+			if (($3.prefix.aid == AID_INET && $3.len != 32) ||
+			    ($3.prefix.aid == AID_INET6 && $3.len != 128))
 				curpeer->conf.template = 1;
+			if (curpeer->conf.capabilities.mp[
+			    curpeer->conf.remote_addr.aid] == -1)
+				curpeer->conf.capabilities.mp[
+				    curpeer->conf.remote_addr.aid] = 1;
 			if (get_id(curpeer)) {
 				yyerror("get_id failed");
 				YYERROR;
@@ -802,6 +960,17 @@ peeropts	: REMOTEAS as4number	{
 			}
 			free($2);
 		}
+		| LLIFACE string	{
+			if (strlcpy(curpeer->conf.lliface, $2,
+			    sizeof(curpeer->conf.lliface)) >=
+			    sizeof(curpeer->conf.lliface)) {
+				yyerror("lliface \"%s\" too long: max %u",
+				    $2, sizeof(curpeer->conf.lliface) - 1);
+				free($2);
+				YYERROR;
+			}
+			free($2);
+	        }
 		| LOCALADDR address	{
 			memcpy(&curpeer->conf.local_addr, &$2,
 			    sizeof(curpeer->conf.local_addr));
@@ -852,13 +1021,17 @@ peeropts	: REMOTEAS as4number	{
 			curpeer->conf.min_holdtime = $3;
 		}
 		| ANNOUNCE family STRING {
-			u_int8_t	safi;
+			u_int8_t	aid, safi;
+			int8_t		val = 1;
 
-			if (!strcmp($3, "none"))
-				safi = SAFI_NONE;
-			else if (!strcmp($3, "unicast"))
+			if (!strcmp($3, "none")) {
 				safi = SAFI_UNICAST;
-			else {
+				val = 0;
+			} else if (!strcmp($3, "unicast")) {
+				safi = SAFI_UNICAST;
+			} else if (!strcmp($3, "vpn")) {
+				safi = SAFI_MPLSVPN;
+			} else {
 				yyerror("unknown/unsupported SAFI \"%s\"",
 				    $3);
 				free($3);
@@ -866,25 +1039,31 @@ peeropts	: REMOTEAS as4number	{
 			}
 			free($3);
 
-			switch ($2) {
-			case AFI_IPv4:
-				curpeer->conf.capabilities.mp_v4 = safi;
-				break;
-			case AFI_IPv6:
-				curpeer->conf.capabilities.mp_v6 = safi;
-				break;
-			default:
-				fatal("king bula sees borked AFI");
+			if (afi2aid($2, safi, &aid) == -1) {
+				yyerror("unknown AFI/SAFI pair");
+				YYERROR;
 			}
+			curpeer->conf.capabilities.mp[aid] = val;
 		}
 		| ANNOUNCE CAPABILITIES yesno {
 			curpeer->conf.announce_capa = $3;
 		}
+		| ANNOUNCE REFRESH yesno {
+			curpeer->conf.capabilities.refresh = $3;
+		}
+		| ANNOUNCE RESTART yesno {
+			curpeer->conf.capabilities.restart = $3;
+		}
+		| ANNOUNCE AS4BYTE yesno {
+			curpeer->conf.capabilities.as4byte = $3;
+		}
 		| ANNOUNCE SELF {
 			curpeer->conf.announce_type = ANNOUNCE_SELF;
 		}
 		| ANNOUNCE STRING {
-			if (!strcmp($2, "none"))
+			if (!strcmp($2, "self"))
+				curpeer->conf.announce_type = ANNOUNCE_SELF;
+			else if (!strcmp($2, "none"))
 				curpeer->conf.announce_type = ANNOUNCE_NONE;
 			else if (!strcmp($2, "all"))
 				curpeer->conf.announce_type = ANNOUNCE_ALL;
@@ -1083,7 +1262,7 @@ peeropts	: REMOTEAS as4number	{
 			curpeer->conf.reflector_client = 1;
 		}
 		| REFLECTOR address	{
-			if ($2.af != AF_INET) {
+			if ($2.aid != AID_INET) {
 				yyerror("route reflector cluster-id must be "
 				    "an IPv4 address");
 				YYERROR;
@@ -1157,6 +1336,10 @@ family		: IPV4	{ $$ = AFI_IPv4; }
 		| IPV6	{ $$ = AFI_IPv6; }
 		;
 
+nettype		: STATIC { $$ = 1; },
+		| CONNECTED { $$ = 0; }
+		;
+
 espah		: ESP		{ $$ = 1; }
 		| AH		{ $$ = 0; }
 		;
@@ -1336,12 +1519,12 @@ filter_prefix_l	: filter_prefix				{ $$ = $1; }
 		;
 
 filter_prefix	: prefix				{
-			if (fmopts.af && fmopts.af != $1.prefix.af) {
+			if (fmopts.aid && fmopts.aid != $1.prefix.aid) {
 				yyerror("rules with mixed address families "
 				    "are not allowed");
 				YYERROR;
 			} else
-				fmopts.af = $1.prefix.af;
+				fmopts.aid = $1.prefix.aid;
 			if (($$ = calloc(1, sizeof(struct filter_prefix_l))) ==
 			    NULL)
 				fatal(NULL);
@@ -1410,6 +1593,12 @@ filter_as	: as4number		{
 				fatal(NULL);
 			$$->a.as = $1;
 		}
+		| NEIGHBORAS		{
+			if (($$ = calloc(1, sizeof(struct filter_as_l))) ==
+			    NULL)
+				fatal(NULL);
+			$$->a.flags = AS_FLAG_NEIGHBORAS;
+		}
 		;
 
 filter_match_h	: /* empty */			{
@@ -1437,18 +1626,18 @@ filter_elm	: filter_prefix_h	{
 			fmopts.prefix_l = $1;
 		}
 		| PREFIXLEN prefixlenop		{
-			if (fmopts.af == 0) {
+			if (fmopts.aid == 0) {
 				yyerror("address family needs to be specified "
 				    "before \"prefixlen\"");
 				YYERROR;
 			}
-			if (fmopts.m.prefixlen.af) {
+			if (fmopts.m.prefixlen.aid) {
 				yyerror("\"prefixlen\" already specified");
 				YYERROR;
 			}
 			memcpy(&fmopts.m.prefixlen, &$2,
 			    sizeof(fmopts.m.prefixlen));
-			fmopts.m.prefixlen.af = fmopts.af;
+			fmopts.m.prefixlen.aid = fmopts.aid;
 		}
 		| filter_as_h		{
 			if (fmopts.as_l != NULL) {
@@ -1457,32 +1646,73 @@ filter_elm	: filter_prefix_h	{
 			}
 			fmopts.as_l = $1;
 		}
+		| MAXASLEN NUMBER	{
+			if (fmopts.m.aslen.type != ASLEN_NONE) {
+				yyerror("AS length filters already specified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("bad max-as-len %lld", $2);
+				YYERROR;
+			}
+			fmopts.m.aslen.type = ASLEN_MAX;
+			fmopts.m.aslen.aslen = $2;
+		}
+		| MAXASSEQ NUMBER	{
+			if (fmopts.m.aslen.type != ASLEN_NONE) {
+				yyerror("AS length filters already specified");
+				YYERROR;
+			}
+			if ($2 < 0 || $2 > UINT_MAX) {
+				yyerror("bad max-as-seq %lld", $2);
+				YYERROR;
+			}
+			fmopts.m.aslen.type = ASLEN_SEQ;
+			fmopts.m.aslen.aslen = $2;
+		}
 		| COMMUNITY STRING	{
 			if (fmopts.m.community.as != COMMUNITY_UNSET) {
 				yyerror("\"community\" already specified");
 				free($2);
 				YYERROR;
 			}
-			if (parsecommunity($2, &fmopts.m.community.as,
-			    &fmopts.m.community.type) == -1) {
+			if (parsecommunity(&fmopts.m.community, $2) == -1) {
+				free($2);
+				YYERROR;
+			}
+			free($2);
+		}
+		| EXTCOMMUNITY STRING STRING {
+			if (fmopts.m.ext_community.flags &
+			    EXT_COMMUNITY_FLAG_VALID) {
+				yyerror("\"ext-community\" already specified");
+				free($2);
+				free($3);
+				YYERROR;
+			}
+
+			if (parseextcommunity(&fmopts.m.ext_community,
+			    $2, $3) == -1) {
 				free($2);
+				free($3);
 				YYERROR;
 			}
 			free($2);
+			free($3);
 		}
 		| IPV4			{
-			if (fmopts.af) {
+			if (fmopts.aid) {
 				yyerror("address family already specified");
 				YYERROR;
 			}
-			fmopts.af = AF_INET;
+			fmopts.aid = AID_INET;
 		}
 		| IPV6			{
-			if (fmopts.af) {
+			if (fmopts.aid) {
 				yyerror("address family already specified");
 				YYERROR;
 			}
-			fmopts.af = AF_INET6;
+			fmopts.aid = AID_INET6;
 		}
 		;
 
@@ -1782,8 +2012,7 @@ filter_set_opt	: LOCALPREF NUMBER		{
 			else
 				$$->type = ACTION_SET_COMMUNITY;
 
-			if (parsecommunity($3, &$$->action.community.as,
-			    &$$->action.community.type) == -1) {
+			if (parsecommunity(&$$->action.community, $3) == -1) {
 				free($3);
 				free($$);
 				YYERROR;
@@ -1796,40 +2025,62 @@ filter_set_opt	: LOCALPREF NUMBER		{
 				free($$);
 				YYERROR;
 			}
-			/* Don't allow setting of unknown well-known types */
-			if ($$->action.community.as == COMMUNITY_WELLKNOWN) {
-				switch ($$->action.community.type) {
-				case COMMUNITY_NO_EXPORT:
-				case COMMUNITY_NO_ADVERTISE:
-				case COMMUNITY_NO_EXPSUBCONFED:
-				case COMMUNITY_NO_PEER:
-					/* valid */
-					break;
-				default:
-					/* unknown */
-					yyerror("Invalid well-known community");
-					free($$);
-					YYERROR;
-					break;
-				}
+		}
+		| EXTCOMMUNITY delete STRING STRING {
+			if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+				fatal(NULL);
+			if ($2)
+				$$->type = ACTION_DEL_EXT_COMMUNITY;
+			else
+				$$->type = ACTION_SET_EXT_COMMUNITY;
+
+			if (parseextcommunity(&$$->action.ext_community,
+			    $3, $4) == -1) {
+				free($3);
+				free($4);
+				free($$);
+				YYERROR;
 			}
+			free($3);
+			free($4);
+		}
+		| ORIGIN origincode {
+			if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+				fatal(NULL);
+			$$->type = ACTION_SET_ORIGIN;
+			$$->action.origin = $2;
 		}
 		;
 
+origincode	: string {
+			if (!strcmp($1, "egp"))
+				$$ = ORIGIN_EGP;
+			else if (!strcmp($1, "igp"))
+				$$ = ORIGIN_IGP;
+			else if (!strcmp($1, "incomplete"))
+				$$ = ORIGIN_INCOMPLETE;
+			else {
+				yyerror("unknown origin \"%s\"", $1);
+				free($1);
+				YYERROR;
+			}
+			free($1);
+		};
+
 comma		: ","
 		| /* empty */
 		;
 
 unaryop		: '='		{ $$ = OP_EQ; }
-		| '!' '='	{ $$ = OP_NE; }
-		| '<' '='	{ $$ = OP_LE; }
+		| NE		{ $$ = OP_NE; }
+		| LE		{ $$ = OP_LE; }
 		| '<'		{ $$ = OP_LT; }
-		| '>' '='	{ $$ = OP_GE; }
+		| GE		{ $$ = OP_GE; }
 		| '>'		{ $$ = OP_GT; }
 		;
 
 binaryop	: '-'		{ $$ = OP_RANGE; }
-		| '>' '<'	{ $$ = OP_XRANGE; }
+		| XRANGE	{ $$ = OP_XRANGE; }
 		;
 
 %%
@@ -1873,6 +2124,7 @@ lookup(char *s)
 		{ "allow",		ALLOW},
 		{ "announce",		ANNOUNCE},
 		{ "any",		ANY},
+		{ "as-4byte",		AS4BYTE },
 		{ "blackhole",		BLACKHOLE},
 		{ "capabilities",	CAPABILITIES},
 		{ "community",		COMMUNITY},
@@ -1889,16 +2141,22 @@ lookup(char *s)
 		{ "enforce",		ENFORCE},
 		{ "esp",		ESP},
 		{ "evaluate",		EVALUATE},
+		{ "export-target",	EXPORTTRGT},
+		{ "ext-community",	EXTCOMMUNITY},
 		{ "fib-update",		FIBUPDATE},
 		{ "from",		FROM},
 		{ "group",		GROUP},
 		{ "holdtime",		HOLDTIME},
 		{ "ignore",		IGNORE},
 		{ "ike",		IKE},
+		{ "import-target",	IMPORTTRGT},
 		{ "in",			IN},
 		{ "include",		INCLUDE},
 		{ "inet",		IPV4},
 		{ "inet6",		IPV6},
+#if defined(IPV6_LINKLOCAL_PEER)
+		{ "interface",		LLIFACE},
+#endif
 		{ "ipsec",		IPSEC},
 		{ "key",		KEY},
 		{ "listen",		LISTEN},
@@ -1906,6 +2164,8 @@ lookup(char *s)
 		{ "localpref",		LOCALPREF},
 		{ "log",		LOG},
 		{ "match",		MATCH},
+		{ "max-as-len",		MAXASLEN},
+		{ "max-as-seq",		MAXASSEQ},
 		{ "max-prefix",		MAXPREFIX},
 		{ "md5sig",		MD5SIG},
 		{ "med",		MED},
@@ -1918,6 +2178,7 @@ lookup(char *s)
 		{ "nexthop",		NEXTHOP},
 		{ "no-modify",		NOMODIFY},
 		{ "on",			ON},
+		{ "origin",		ORIGIN},
 		{ "out",		OUT},
 		{ "passive",		PASSIVE},
 		{ "password",		PASSWORD},
@@ -1929,10 +2190,14 @@ lookup(char *s)
 		{ "prepend-self",	PREPEND_SELF},
 		{ "qualify",		QUALIFY},
 		{ "quick",		QUICK},
+		{ "rd",			RD},
 		{ "rde",		RDE},
+		{ "rdomain",		RDOMAIN},
+		{ "refresh",		REFRESH },
 		{ "reject",		REJECT},
 		{ "remote-as",		REMOTEAS},
 		{ "restart",		RESTART},
+		{ "restricted",		RESTRICTED},
 		{ "rib",		RIB},
 		{ "route-collector",	ROUTECOLL},
 		{ "route-reflector",	REFLECTOR},
@@ -1941,6 +2206,7 @@ lookup(char *s)
 		{ "rtlabel",		RTLABEL},
 		{ "self",		SELF},
 		{ "set",		SET},
+		{ "socket",		SOCKET },
 		{ "softreconfig",	SOFTRECONFIG},
 		{ "source-as",		SOURCEAS},
 		{ "spi",		SPI},
@@ -2117,9 +2383,10 @@ top:
 					return (0);
 				if (next == quotec || c == ' ' || c == '\t')
 					c = next;
-				else if (next == '\n')
+				else if (next == '\n') {
+					file->lineno++;
 					continue;
-				else
+				} else
 					lungetc(next);
 			} else if (c == quotec) {
 				*p = '\0';
@@ -2135,6 +2402,26 @@ top:
 		if (yylval.v.string == NULL)
 			fatal("yylex: strdup");
 		return (STRING);
+	case '!':
+		next = lgetc(0);
+		if (next == '=')
+			return (NE);
+		lungetc(next);
+		break;
+	case '<':
+		next = lgetc(0);
+		if (next == '=')
+			return (LE);
+		lungetc(next);
+		break;
+	case '>':
+		next = lgetc(0);
+		if (next == '<')
+			return (XRANGE);
+		else if (next == '=')
+			return (GE);
+		lungetc(next);
+		break;
 	}
 
 #define allowed_to_end_number(x) \
@@ -2274,18 +2561,21 @@ popfile(void)
 int
 parse_config(char *filename, struct bgpd_config *xconf,
     struct mrt_head *xmconf, struct peer **xpeers, struct network_head *nc,
-    struct filter_head *xfilter_l)
+    struct filter_head *xfilter_l, struct rdomain_head *xrdom_l)
 {
 	struct sym		*sym, *next;
 	struct peer		*p, *pnext;
 	struct listen_addr	*la;
 	struct network		*n;
 	struct filter_rule	*r;
+	struct rde_rib		*rr;
+	struct rdomain		*rd;
 	int			 errors = 0;
 
 	if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL)
 		fatal(NULL);
 	conf->opts = xconf->opts;
+	conf->csock = strdup(SOCKET_NAME);
 
 	if ((file = pushfile(filename, 1)) == NULL) {
 		free(conf);
@@ -2316,13 +2606,15 @@ parse_config(char *filename, struct bgpd_config *xconf,
 	id = 1;
 
 	/* network list is always empty in the parent */
-	netconf = nc;
+	gnetconf = netconf = nc;
 	TAILQ_INIT(netconf);
 	/* init the empty filter list for later */
 	TAILQ_INIT(xfilter_l);
+	SIMPLEQ_INIT(xrdom_l);
+	rdom_l = xrdom_l;
 
-	add_rib("Adj-RIB-In", F_RIB_NOEVALUATE);
-	add_rib("Loc-RIB", 0);
+	add_rib("Adj-RIB-In", 0, F_RIB_NOFIB | F_RIB_NOEVALUATE);
+	add_rib("Loc-RIB", 0, 0);
 
 	yyparse();
 	errors = file->errors;
@@ -2344,6 +2636,9 @@ parse_config(char *filename, struct bgpd_config *xconf,
 
 	if (errors) {
 		/* XXX more leaks in this case */
+		free(conf->csock);
+		free(conf->rcsock);
+
 		while ((la = TAILQ_FIRST(listen_addrs)) != NULL) {
 			TAILQ_REMOVE(listen_addrs, la, entry);
 			free(la);
@@ -2357,23 +2652,44 @@ parse_config(char *filename, struct bgpd_config *xconf,
 
 		while ((n = TAILQ_FIRST(netconf)) != NULL) {
 			TAILQ_REMOVE(netconf, n, entry);
+			filterset_free(&n->net.attrset);
 			free(n);
 		}
 
 		while ((r = TAILQ_FIRST(filter_l)) != NULL) {
 			TAILQ_REMOVE(filter_l, r, entry);
+			filterset_free(&r->set);
 			free(r);
 		}
 
 		while ((r = TAILQ_FIRST(peerfilter_l)) != NULL) {
 			TAILQ_REMOVE(peerfilter_l, r, entry);
+			filterset_free(&r->set);
 			free(r);
 		}
 
 		while ((r = TAILQ_FIRST(groupfilter_l)) != NULL) {
 			TAILQ_REMOVE(groupfilter_l, r, entry);
+			filterset_free(&r->set);
 			free(r);
 		}
+		while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) {
+			SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
+			free(rr);
+		}
+		while ((rd = SIMPLEQ_FIRST(rdom_l)) != NULL) {
+			SIMPLEQ_REMOVE_HEAD(rdom_l, entry);
+			filterset_free(&rd->export);
+			filterset_free(&rd->import);
+
+			while ((n = TAILQ_FIRST(&rd->net_l)) != NULL) {
+				TAILQ_REMOVE(&rd->net_l, n, entry);
+				filterset_free(&n->net.attrset);
+				free(n);
+			}
+
+			free(rd);
+		}
 	} else {
 		errors += merge_config(xconf, conf, peer_l, listen_addrs);
 		errors += mrt_mergeconfig(xmconf, mrtconf);
@@ -2505,27 +2821,27 @@ getcommunity(char *s)
 }
 
 int
-parsecommunity(char *s, int *as, int *type)
+parsecommunity(struct filter_community *c, char *s)
 {
 	char *p;
-	int i;
+	int i, as;
 
 	/* Well-known communities */
 	if (strcasecmp(s, "NO_EXPORT") == 0) {
-		*as = COMMUNITY_WELLKNOWN;
-		*type = COMMUNITY_NO_EXPORT;
+		c->as = COMMUNITY_WELLKNOWN;
+		c->type = COMMUNITY_NO_EXPORT;
 		return (0);
 	} else if (strcasecmp(s, "NO_ADVERTISE") == 0) {
-		*as = COMMUNITY_WELLKNOWN;
-		*type = COMMUNITY_NO_ADVERTISE;
+		c->as = COMMUNITY_WELLKNOWN;
+		c->type = COMMUNITY_NO_ADVERTISE;
 		return (0);
 	} else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) {
-		*as = COMMUNITY_WELLKNOWN;
-		*type = COMMUNITY_NO_EXPSUBCONFED;
+		c->as = COMMUNITY_WELLKNOWN;
+		c->type = COMMUNITY_NO_EXPSUBCONFED;
 		return (0);
 	} else if (strcasecmp(s, "NO_PEER") == 0) {
-		*as = COMMUNITY_WELLKNOWN;
-		*type = COMMUNITY_NO_PEER;
+		c->as = COMMUNITY_WELLKNOWN;
+		c->type = COMMUNITY_NO_PEER;
 		return (0);
 	}
 
@@ -2537,23 +2853,176 @@ parsecommunity(char *s, int *as, int *type)
 
 	if ((i = getcommunity(s)) == COMMUNITY_ERROR)
 		return (-1);
-	if (i == USHRT_MAX) {
+	if (i == COMMUNITY_WELLKNOWN) {
 		yyerror("Bad community AS number");
 		return (-1);
 	}
-	*as = i;
+	as = i;
 
 	if ((i = getcommunity(p)) == COMMUNITY_ERROR)
 		return (-1);
-	*type = i;
+	c->as = as;
+	c->type = i;
 
 	return (0);
 }
 
+int
+parsesubtype(char *type)
+{
+	/* this has to be sorted always */
+	static const struct keywords keywords[] = {
+		{ "bdc",	EXT_COMMUNITY_BGP_COLLECT },
+		{ "odi",	EXT_COMMUNITY_OSPF_DOM_ID },
+		{ "ori",	EXT_COMMUNITY_OSPF_RTR_ID },
+		{ "ort",	EXT_COMMUNITY_OSPF_RTR_TYPE },
+		{ "rt",		EXT_COMMUNITY_ROUTE_TGT },
+		{ "soo",	EXT_CUMMUNITY_ROUTE_ORIG }
+	};
+	const struct keywords	*p;
+
+	p = bsearch(type, keywords, sizeof(keywords)/sizeof(keywords[0]),
+	    sizeof(keywords[0]), kw_cmp);
+
+	if (p)
+		return (p->k_val);
+	else
+		return (-1);
+}
+
+int
+parseextvalue(char *s, u_int32_t *v)
+{
+	const char 	*errstr;
+	char		*p;
+	struct in_addr	 ip;
+	u_int32_t	 uvalh = 0, uval;
+
+	if ((p = strchr(s, '.')) == NULL) {
+		/* AS_PLAIN number (4 or 2 byte) */
+		uval = strtonum(s, 0, UINT_MAX, &errstr);
+		if (errstr) {
+			yyerror("Bad ext-community %s is %s", s, errstr);
+			return (-1);
+		}
+		*v = uval;
+		if (uval > USHRT_MAX)
+			return (EXT_COMMUNITY_FOUR_AS);
+		else
+			return (EXT_COMMUNITY_TWO_AS);
+	} else if (strchr(p + 1, '.') == NULL) {
+		/* AS_DOT number (4-byte) */
+		*p++ = '\0';
+		uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
+		if (errstr) {
+			yyerror("Bad ext-community %s is %s", s, errstr);
+			return (-1);
+		}
+		uval = strtonum(p, 0, USHRT_MAX, &errstr);
+		if (errstr) {
+			yyerror("Bad ext-community %s is %s", p, errstr);
+			return (-1);
+		}
+		*v = uval | (uvalh << 16);
+		return (EXT_COMMUNITY_FOUR_AS);
+	} else {
+		/* more then one dot -> IP address */
+		if (inet_aton(s, &ip) == 0) {
+			yyerror("Bad ext-community %s not parseable", s);
+			return (-1);
+		}
+		*v = ip.s_addr;
+		return (EXT_COMMUNITY_IPV4);
+	}
+	return (-1);
+}
+
+int
+parseextcommunity(struct filter_extcommunity *c, char *t, char *s)
+{
+	const struct ext_comm_pairs	 iana[] = IANA_EXT_COMMUNITIES;
+	const char 	*errstr;
+	u_int64_t	 ullval = 0;
+	u_int32_t	 uval;
+	char		*p, *ep;
+	unsigned int	 i;
+	int		 type, subtype;
+
+	if ((subtype = parsesubtype(t)) == -1) {
+		yyerror("Bad ext-community unknown type");
+		return (-1);
+	}
+
+	if ((p = strchr(s, ':')) == NULL) {
+		type = EXT_COMMUNITY_OPAQUE,
+		errno = 0;
+		ullval = strtoull(s, &ep, 0);
+		if (s[0] == '\0' || *ep != '\0') {
+			yyerror("Bad ext-community bad value");
+			return (-1);
+		}
+		if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
+			yyerror("Bad ext-community value to big");
+			return (-1);
+		}
+		c->data.ext_opaq = ullval;
+	} else {
+		*p++ = '\0';
+		if ((type = parseextvalue(s, &uval)) == -1)
+			return (-1);
+		switch (type) {
+		case EXT_COMMUNITY_TWO_AS:
+			ullval = strtonum(p, 0, UINT_MAX, &errstr);
+			break;
+		case EXT_COMMUNITY_IPV4:
+		case EXT_COMMUNITY_FOUR_AS:
+			ullval = strtonum(p, 0, USHRT_MAX, &errstr);
+			break;
+		default:
+			fatalx("parseextcommunity: unexpected result");
+		}
+		if (errstr) {
+			yyerror("Bad ext-community %s is %s", p,
+			    errstr);
+			return (-1);
+		}
+		switch (type) {
+		case EXT_COMMUNITY_TWO_AS:
+			c->data.ext_as.as = uval;
+			c->data.ext_as.val = ullval;
+			break;
+		case EXT_COMMUNITY_IPV4:
+			c->data.ext_ip.addr.s_addr = uval;
+			c->data.ext_ip.val = ullval;
+			break;
+		case EXT_COMMUNITY_FOUR_AS:
+			c->data.ext_as4.as4 = uval;
+			c->data.ext_as4.val = ullval;
+			break;
+		}
+	}
+	c->type = type;
+	c->subtype = subtype;
+
+	/* verify type/subtype combo */
+	for (i = 0; i < sizeof(iana)/sizeof(iana[0]); i++) {
+		if (iana[i].type == type && iana[i].subtype == subtype) {
+			if (iana[i].transitive)
+				c->type |= EXT_COMMUNITY_TRANSITIVE;
+			c->flags |= EXT_COMMUNITY_FLAG_VALID;
+			return (0);
+		}
+	}
+
+	yyerror("Bad ext-community bad format for type");
+	return (-1);
+}
+
 struct peer *
 alloc_peer(void)
 {
 	struct peer	*p;
+	u_int8_t	 i;
 
 	if ((p = calloc(1, sizeof(struct peer))) == NULL)
 		fatal("new_peer");
@@ -2564,11 +3033,11 @@ alloc_peer(void)
 	p->conf.distance = 1;
 	p->conf.announce_type = ANNOUNCE_UNDEF;
 	p->conf.announce_capa = 1;
-	p->conf.capabilities.mp_v4 = SAFI_UNICAST;
-	p->conf.capabilities.mp_v6 = SAFI_NONE;
+	for (i = 0; i < AID_MAX; i++)
+		p->conf.capabilities.mp[i] = -1;
 	p->conf.capabilities.refresh = 1;
 	p->conf.capabilities.restart = 0;
-	p->conf.capabilities.as4byte = 0;
+	p->conf.capabilities.as4byte = 1;
 	p->conf.local_as = conf->as;
 	p->conf.local_short_as = conf->short_as;
 	p->conf.softreconfig_in = 1;
@@ -2592,6 +3061,9 @@ new_peer(void)
 		if (strlcpy(p->conf.descr, curgroup->conf.descr,
 		    sizeof(p->conf.descr)) >= sizeof(p->conf.descr))
 			fatalx("new_peer descr strlcpy");
+		if (strlcpy(p->conf.lliface, curgroup->conf.lliface,
+		    sizeof(p->conf.lliface)) >= sizeof(p->conf.lliface))
+			fatalx("new_peer lliface strlcpy");
 		p->conf.groupid = curgroup->conf.id;
 		p->conf.local_as = curgroup->conf.local_as;
 		p->conf.local_short_as = curgroup->conf.local_short_as;
@@ -2674,39 +3146,52 @@ add_mrtconfig(enum mrt_type type, char *name, time_t timeout, struct peer *p,
 }
 
 int
-add_rib(char *name, u_int16_t flags)
+add_rib(char *name, u_int rtableid, u_int16_t flags)
 {
 	struct rde_rib	*rr;
+	u_int		 rdom;
 
-	if (find_rib(name)) {
-		yyerror("rib \"%s\" allready exists.", name);
-		return (-1);
-	}
-
-	if ((rr = calloc(1, sizeof(*rr))) == NULL) {
-		log_warn("add_rib");
-		return (-1);
+	if ((rr = find_rib(name)) == NULL) {
+		if ((rr = calloc(1, sizeof(*rr))) == NULL) {
+			log_warn("add_rib");
+			return (-1);
+		}
 	}
 	if (strlcpy(rr->name, name, sizeof(rr->name)) >= sizeof(rr->name)) {
 		yyerror("rib name \"%s\" too long: max %u",
 		   name, sizeof(rr->name) - 1);
+		free(rr);
 		return (-1);
 	}
 	rr->flags |= flags;
+	if ((rr->flags & F_RIB_HASNOFIB) == 0) {
+		if (ktable_exists(rtableid, &rdom) != 1) {
+			yyerror("rtable id %lld does not exist", rtableid);
+			free(rr);
+			return (-1);
+		}
+		if (rdom != 0) {
+			yyerror("rtable %lld does not belong to rdomain 0",
+			    rtableid);
+			free(rr);
+			return (-1);
+		}
+		rr->rtableid = rtableid;
+	}
 	SIMPLEQ_INSERT_TAIL(&ribnames, rr, entry);
 	return (0);
 }
 
-int
+struct rde_rib *
 find_rib(char *name)
 {
 	struct rde_rib	*rr;
 
 	SIMPLEQ_FOREACH(rr, &ribnames, entry) {
 		if (!strcmp(rr->name, name))
-			return (1);
+			return (rr);
 	}
-	return (0);
+	return (NULL);
 }
 
 int
@@ -2715,7 +3200,7 @@ get_id(struct peer *newpeer)
 	struct peer	*p;
 
 	for (p = peer_l_old; p != NULL; p = p->next)
-		if (newpeer->conf.remote_addr.af) {
+		if (newpeer->conf.remote_addr.aid) {
 			if (!memcmp(&p->conf.remote_addr,
 			    &newpeer->conf.remote_addr,
 			    sizeof(p->conf.remote_addr))) {
@@ -2856,9 +3341,11 @@ str2key(char *s, char *dest, size_t max_len)
 int
 neighbor_consistent(struct peer *p)
 {
+	u_int8_t	i;
+
 	/* local-address and peer's address: same address family */
-	if (p->conf.local_addr.af &&
-	    p->conf.local_addr.af != p->conf.remote_addr.af) {
+	if (p->conf.local_addr.aid &&
+	    p->conf.local_addr.aid != p->conf.remote_addr.aid) {
 		yyerror("local-address and neighbor address "
 		    "must be of the same address family");
 		return (-1);
@@ -2869,7 +3356,7 @@ neighbor_consistent(struct peer *p)
 	    p->conf.auth.method == AUTH_IPSEC_IKE_AH ||
 	    p->conf.auth.method == AUTH_IPSEC_MANUAL_ESP ||
 	    p->conf.auth.method == AUTH_IPSEC_MANUAL_AH) &&
-	    !p->conf.local_addr.af) {
+	    !p->conf.local_addr.aid) {
 		yyerror("neighbors with any form of IPsec configured "
 		    "need local-address to be specified");
 		return (-1);
@@ -2889,10 +3376,6 @@ neighbor_consistent(struct peer *p)
 		return (-1);
 	}
 
-	/* for testing: enable 4-byte AS number capability if necessary */
-	if (conf->as > USHRT_MAX || p->conf.remote_as > USHRT_MAX)
-		p->conf.capabilities.as4byte = 1;
-
 	/* set default values if they where undefined */
 	p->conf.ebgp = (p->conf.remote_as != conf->as);
 	if (p->conf.announce_type == ANNOUNCE_UNDEF)
@@ -2909,6 +3392,11 @@ neighbor_consistent(struct peer *p)
 		return (-1);
 	}
 
+	/* the default MP capability is NONE */
+	for (i = 0; i < AID_MAX; i++)
+		if (p->conf.capabilities.mp[i] == -1)
+			p->conf.capabilities.mp[i] = 0;
+
 	return (0);
 }
 
@@ -2927,6 +3415,11 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
 				yyerror("community is already set");
 			else if (s->type == ACTION_DEL_COMMUNITY)
 				yyerror("community will already be deleted");
+			else if (s->type == ACTION_SET_EXT_COMMUNITY)
+				yyerror("ext-community is already set");
+			else if (s->type == ACTION_DEL_EXT_COMMUNITY)
+				yyerror(
+				    "ext-community will already be deleted");
 			else
 				yyerror("redefining set parameter %s",
 				    filterset_name(s->type));
@@ -2953,9 +3446,18 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s)
 					return (0);
 				}
 				break;
+			case ACTION_SET_EXT_COMMUNITY:
+			case ACTION_DEL_EXT_COMMUNITY:
+				if (memcmp(&s->action.ext_community,
+				    &t->action.ext_community,
+				    sizeof(s->action.ext_community)) < 0) {
+					TAILQ_INSERT_BEFORE(t, s, entry);
+					return (0);
+				}
+				break;
 			case ACTION_SET_NEXTHOP:
-				if (s->action.nexthop.af <
-				    t->action.nexthop.af) {
+				if (s->action.nexthop.aid <
+				    t->action.nexthop.aid) {
 					TAILQ_INSERT_BEFORE(t, s, entry);
 					return (0);
 				}
@@ -2985,22 +3487,6 @@ copy_filterset(struct filter_set_head *source, struct filter_set_head *dest)
 	}
 }
 
-void
-move_filterset(struct filter_set_head *source, struct filter_set_head *dest)
-{
-	struct filter_set	*s;
-
-	TAILQ_INIT(dest);
-
-	if (source == NULL)
-		return;
-
-	while ((s = TAILQ_FIRST(source)) != NULL) {
-		TAILQ_REMOVE(source, s, entry);
-		TAILQ_INSERT_TAIL(dest, s, entry);
-	}
-}
-
 struct filter_rule *
 get_rule(enum action_types type)
 {
diff --git a/bgpd/pfkey.c b/bgpd/pfkey.c
index 80936f4..30ed413 100644
--- a/bgpd/pfkey.c
+++ b/bgpd/pfkey.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfkey.c,v 1.37 2009/04/21 15:25:52 henning Exp $ */
+/*	$OpenBSD: pfkey.c,v 1.41 2010/12/09 13:50:41 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -74,6 +74,7 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 	int			len = 0;
 	int			iov_cnt;
 	struct sockaddr_storage	ssrc, sdst, speer, smask, dmask;
+	struct sockaddr		*saptr;
 
 	if (!pid)
 		pid = getpid();
@@ -81,22 +82,17 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 	/* we need clean sockaddr... no ports set */
 	bzero(&ssrc, sizeof(ssrc));
 	bzero(&smask, sizeof(smask));
-	switch (src->af) {
-	case AF_INET:
-		((struct sockaddr_in *)&ssrc)->sin_addr = src->v4;
-		ssrc.ss_len = sizeof(struct sockaddr_in);
-		ssrc.ss_family = AF_INET;
+	if ((saptr = addr2sa(src, 0)))
+		memcpy(&ssrc, saptr, sizeof(ssrc));
+	switch (src->aid) {
+	case AID_INET:
 		memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8);
 		break;
-	case AF_INET6:
-		memcpy(&((struct sockaddr_in6 *)&ssrc)->sin6_addr,
-		    &src->v6, sizeof(struct in6_addr));
-		ssrc.ss_len = sizeof(struct sockaddr_in6);
-		ssrc.ss_family = AF_INET6;
+	case AID_INET6:
 		memset(&((struct sockaddr_in6 *)&smask)->sin6_addr, 0xff,
 		    128/8);
 		break;
-	case 0:
+	case AID_UNSPEC:
 		ssrc.ss_len = sizeof(struct sockaddr);
 		break;
 	default:
@@ -107,22 +103,17 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 
 	bzero(&sdst, sizeof(sdst));
 	bzero(&dmask, sizeof(dmask));
-	switch (dst->af) {
-	case AF_INET:
-		((struct sockaddr_in *)&sdst)->sin_addr = dst->v4;
-		sdst.ss_len = sizeof(struct sockaddr_in);
-		sdst.ss_family = AF_INET;
+	if ((saptr = addr2sa(dst, 0)))
+		memcpy(&sdst, saptr, sizeof(sdst));
+	switch (dst->aid) {
+	case AID_INET:
 		memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8);
 		break;
-	case AF_INET6:
-		memcpy(&((struct sockaddr_in6 *)&sdst)->sin6_addr,
-		    &dst->v6, sizeof(struct in6_addr));
-		sdst.ss_len = sizeof(struct sockaddr_in6);
-		sdst.ss_family = AF_INET6;
+	case AID_INET6:
 		memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr, 0xff,
 		    128/8);
 		break;
-	case 0:
+	case AID_UNSPEC:
 		sdst.ss_len = sizeof(struct sockaddr);
 		break;
 	default:
@@ -220,8 +211,8 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 		sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
 
 		bzero(&smask, sizeof(smask));
-		switch (src->af) {
-		case AF_INET:
+		switch (src->aid) {
+		case AID_INET:
 			smask.ss_len = sizeof(struct sockaddr_in);
 			smask.ss_family = AF_INET;
 			memset(&((struct sockaddr_in *)&smask)->sin_addr,
@@ -233,7 +224,7 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 				    htons(0xffff);
 			}
 			break;
-		case AF_INET6:
+		case AID_INET6:
 			smask.ss_len = sizeof(struct sockaddr_in6);
 			smask.ss_family = AF_INET6;
 			memset(&((struct sockaddr_in6 *)&smask)->sin6_addr,
@@ -247,8 +238,8 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 			break;
 		}
 		bzero(&dmask, sizeof(dmask));
-		switch (dst->af) {
-		case AF_INET:
+		switch (dst->aid) {
+		case AID_INET:
 			dmask.ss_len = sizeof(struct sockaddr_in);
 			dmask.ss_family = AF_INET;
 			memset(&((struct sockaddr_in *)&dmask)->sin_addr,
@@ -260,7 +251,7 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 				    htons(0xffff);
 			}
 			break;
-		case AF_INET6:
+		case AID_INET6:
 			dmask.ss_len = sizeof(struct sockaddr_in6);
 			dmask.ss_family = AF_INET6;
 			memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr,
@@ -411,6 +402,33 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
 }
 
 int
+pfkey_read(int sd, struct sadb_msg *h)
+{
+	struct sadb_msg hdr;
+
+	if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
+		log_warn("pfkey peek");
+		return (-1);
+	}
+
+	/* XXX: Only one message can be outstanding. */
+	if (hdr.sadb_msg_seq == sadb_msg_seq &&
+	    hdr.sadb_msg_pid == pid) {
+		if (h)
+			bcopy(&hdr, h, sizeof(hdr));
+		return (0);
+	}
+
+	/* not ours, discard */
+	if (read(sd, &hdr, sizeof(hdr)) == -1) {
+		log_warn("pfkey read");
+		return (-1);
+	}
+
+	return (1);
+}
+
+int
 pfkey_reply(int sd, u_int32_t *spip)
 {
 	struct sadb_msg hdr, *msg;
@@ -418,23 +436,13 @@ pfkey_reply(int sd, u_int32_t *spip)
 	struct sadb_sa *sa;
 	u_int8_t *data;
 	ssize_t len;
+	int rv;
 
-	for (;;) {
-		if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
-			log_warn("pfkey peek");
+	do {
+		rv = pfkey_read(sd, &hdr);
+		if (rv == -1)
 			return (-1);
-		}
-
-		if (hdr.sadb_msg_seq == sadb_msg_seq &&
-		    hdr.sadb_msg_pid == pid)
-			break;
-
-		/* not ours, discard */
-		if (read(sd, &hdr, sizeof(hdr)) == -1) {
-			log_warn("pfkey read");
-			return (-1);
-		}
-	}
+	} while (rv);
 
 	if (hdr.sadb_msg_errno != 0) {
 		errno = hdr.sadb_msg_errno;
@@ -730,11 +738,9 @@ pfkey_init(struct bgpd_sysdep *sysdep)
 		if (errno == EPROTONOSUPPORT) {
 			log_warnx("PF_KEY not available, disabling ipsec");
 			sysdep->no_pfkey = 1;
-			return (0);
-		} else {
-			log_warn("PF_KEY socket");
 			return (-1);
-		}
+		} else
+			fatal("pfkey setup failed");
 	}
-	return (0);
+	return (fd);
 }
diff --git a/bgpd/pftable.c b/bgpd/pftable.c
index 13b8c48..0a365df 100644
--- a/bgpd/pftable.c
+++ b/bgpd/pftable.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pftable.c,v 1.5 2005/07/01 09:19:24 claudio Exp $ */
+/*	$OpenBSD: pftable.c,v 1.6 2009/12/01 14:28:05 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Damien Miller <djm@openbsd.org>
@@ -214,7 +214,7 @@ pftable_add_work(const char *table, struct bgpd_addr *addr,
 
 	bzero(pfa, sizeof(*pfa));
 	memcpy(&pfa->pfra_u, &addr->ba, (len + 7U) / 8);
-	pfa->pfra_af = addr->af;
+	pfa->pfra_af = aid2af(addr->aid);
 	pfa->pfra_net = len;
 
 	pft->naddrs++;
diff --git a/bgpd/printconf.c b/bgpd/printconf.c
index c64d23d..3086b88 100644
--- a/bgpd/printconf.c
+++ b/bgpd/printconf.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: printconf.c,v 1.70 2009/06/06 01:10:29 claudio Exp $	*/
+/*	$OpenBSD: printconf.c,v 1.79 2010/03/05 15:25:00 claudio Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -16,9 +16,13 @@
  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#if defined(__FreeBSD__)	/* limits.h */
+#include <limits.h>
+#endif
 
 #include "bgpd.h"
 #include "mrt.h"
@@ -27,14 +31,19 @@
 
 void		 print_op(enum comp_ops);
 void		 print_community(int, int);
+void		 print_extcommunity(struct filter_extcommunity *);
+void		 print_origin(u_int8_t);
 void		 print_set(struct filter_set_head *);
 void		 print_mainconf(struct bgpd_config *);
+void		 print_rdomain_targets(struct filter_set_head *, const char *);
+void		 print_rdomain(struct rdomain *);
+const char	*print_af(u_int8_t);
 void		 print_network(struct network_config *);
 void		 print_peer(struct peer_config *, struct bgpd_config *,
 		    const char *);
 const char	*print_auth_alg(u_int8_t);
 const char	*print_enc_alg(u_int8_t);
-const char	*print_safi(u_int8_t);
+void		 print_announce(struct peer_config *, const char *);
 void		 print_rule(struct peer *, struct filter_rule *);
 const char *	 mrt_type(enum mrt_type);
 void		 print_mrt(u_int32_t, u_int32_t, const char *, const char *);
@@ -94,6 +103,45 @@ print_community(int as, int type)
 }
 
 void
+print_extcommunity(struct filter_extcommunity *c)
+{
+	switch (c->type & EXT_COMMUNITY_VALUE) {
+	case EXT_COMMUNITY_TWO_AS:
+		printf("%s %i:%i ", log_ext_subtype(c->subtype),
+		    c->data.ext_as.as, c->data.ext_as.val);
+		break;
+	case EXT_COMMUNITY_IPV4:
+		printf("%s %s:%i ", log_ext_subtype(c->subtype),
+		    inet_ntoa(c->data.ext_ip.addr), c->data.ext_ip.val);
+		break;
+	case EXT_COMMUNITY_FOUR_AS:
+		printf("%s %s:%i ", log_ext_subtype(c->subtype),
+		    log_as(c->data.ext_as4.as4), c->data.ext_as.val);
+		break;
+	case EXT_COMMUNITY_OPAQUE:
+		printf("%s 0x%llx ", log_ext_subtype(c->subtype),
+		    (long long unsigned int)c->data.ext_opaq);
+		break;
+	default:
+		printf("0x%x 0x%llx ", c->type, (long long unsigned int)c->data.ext_opaq);
+		break;
+	}
+}
+
+void
+print_origin(u_int8_t o)
+{
+	if (o == ORIGIN_IGP)
+		printf("igp ");
+	else if (o == ORIGIN_EGP)
+		printf("egp ");
+	else if (o == ORIGIN_INCOMPLETE)
+		printf("incomplete ");
+	else
+		printf("%u ", o);
+}
+
+void
 print_set(struct filter_set_head *set)
 {
 	struct filter_set	*s;
@@ -161,11 +209,23 @@ print_set(struct filter_set_head *set)
 		case ACTION_RTLABEL:
 			printf("rtlabel %s ", s->action.rtlabel);
 			break;
+		case ACTION_SET_ORIGIN:
+			printf("origin ");
+			print_origin(s->action.origin);
+			break;
 		case ACTION_RTLABEL_ID:
 		case ACTION_PFTABLE_ID:
 			/* not possible */
 			printf("king bula saiz: config broken");
 			break;
+		case ACTION_SET_EXT_COMMUNITY:
+			printf("ext-community ");
+			print_extcommunity(&s->action.ext_community);
+			break;
+		case ACTION_DEL_EXT_COMMUNITY:
+			printf("ext-community delete ");
+			print_extcommunity(&s->action.ext_community);
+			break;
 		}
 	}
 	printf("}");
@@ -182,6 +242,10 @@ print_mainconf(struct bgpd_config *conf)
 		printf(" %u", conf->short_as);
 	ina.s_addr = conf->bgpid;
 	printf("\nrouter-id %s\n", inet_ntoa(ina));
+
+	printf("socket \"%s\"\n", conf->csock);
+	if (conf->rcsock)
+		printf("socket \"%s\" restricted\n", conf->rcsock);
 	if (conf->holdtime)
 		printf("holdtime %u\n", conf->holdtime);
 	if (conf->min_holdtime)
@@ -189,11 +253,6 @@ print_mainconf(struct bgpd_config *conf)
 	if (conf->connectretry)
 		printf("connect-retry %u\n", conf->connectretry);
 
-	if (conf->flags & BGPD_FLAG_NO_FIB_UPDATE)
-		printf("fib-update no\n");
-	else
-		printf("fib-update yes\n");
-
 	if (conf->flags & BGPD_FLAG_NO_EVALUATE)
 		printf("route-collector yes\n");
 
@@ -214,43 +273,67 @@ print_mainconf(struct bgpd_config *conf)
 		printf("nexthop qualify via bgp\n");
 	if (conf->flags & BGPD_FLAG_NEXTHOP_DEFAULT)
 		printf("nexthop qualify via default\n");
+}
 
-	if (conf->flags & BGPD_FLAG_REDIST_CONNECTED) {
-		printf("network inet connected");
-		if (!TAILQ_EMPTY(&conf->connectset))
-			printf(" ");
-		print_set(&conf->connectset);
-		printf("\n");
-	}
-	if (conf->flags & BGPD_FLAG_REDIST_STATIC) {
-		printf("network inet static");
-		if (!TAILQ_EMPTY(&conf->staticset))
-			printf(" ");
-		print_set(&conf->staticset);
-		printf("\n");
-	}
-	if (conf->flags & BGPD_FLAG_REDIST6_CONNECTED) {
-		printf("network inet6 connected");
-		if (!TAILQ_EMPTY(&conf->connectset6))
-			printf(" ");
-		print_set(&conf->connectset6);
-		printf("\n");
-	}
-	if (conf->flags & BGPD_FLAG_REDIST_STATIC) {
-		printf("network inet6 static");
-		if (!TAILQ_EMPTY(&conf->staticset6))
-			printf(" ");
-		print_set(&conf->staticset6);
+void
+print_rdomain_targets(struct filter_set_head *set, const char *tgt)
+{
+	struct filter_set	*s;
+	TAILQ_FOREACH(s, set, entry) {
+		printf("\t%s ", tgt);
+		print_extcommunity(&s->action.ext_community);
 		printf("\n");
 	}
-	if (conf->rtableid)
-		printf("rtable %u\n", conf->rtableid);
+}
+
+void
+print_rdomain(struct rdomain *r)
+{
+	printf("rdomain %u {\n", r->rtableid);
+	printf("\tdescr \"%s\"\n", r->descr);
+	if (r->flags & F_RIB_NOFIBSYNC)
+		printf("\tfib-update no\n");
+	else
+		printf("\tfib-update yes\n");
+	printf("\tdepend on %s\n", r->ifmpe);
+
+	printf("\n\t%s\n", log_rd(r->rd));
+
+	print_rdomain_targets(&r->export, "export-target");
+	print_rdomain_targets(&r->import, "import-target");
+
+	printf("}\n");
+}
+
+const char *
+print_af(u_int8_t aid)
+{
+	/*
+	 * Hack around the fact that aid2str() will return "IPv4 unicast"
+	 * for AID_INET. AID_INET and AID_INET6 need special handling and
+	 * the other AID should never end up here (at least for now).
+	 */
+	if (aid == AID_INET)
+		return ("inet");
+	if (aid == AID_INET6)
+		return ("inet6");
+	return (aid2str(aid));
 }
 
 void
 print_network(struct network_config *n)
 {
-	printf("network %s/%u", log_addr(&n->prefix), n->prefixlen);
+	switch (n->type) {
+	case NETWORK_STATIC:
+		printf("network %s static", print_af(n->prefix.aid));
+		break;
+	case NETWORK_CONNECTED:
+		printf("network %s connected", print_af(n->prefix.aid));
+		break;
+	default:
+		printf("network %s/%u", log_addr(&n->prefix), n->prefixlen);
+		break;
+	}
 	if (!TAILQ_EMPTY(&n->attrset))
 		printf(" ");
 	print_set(&n->attrset);
@@ -263,8 +346,8 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
 	char		*method;
 	struct in_addr	 ina;
 
-	if ((p->remote_addr.af == AF_INET && p->remote_masklen != 32) ||
-	    (p->remote_addr.af == AF_INET6 && p->remote_masklen != 128))
+	if ((p->remote_addr.aid == AID_INET && p->remote_masklen != 32) ||
+	    (p->remote_addr.aid == AID_INET6 && p->remote_masklen != 128))
 		printf("%sneighbor %s/%u {\n", c, log_addr(&p->remote_addr),
 		    p->remote_masklen);
 	else
@@ -281,7 +364,7 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
 		printf("%s\tmultihop %u\n", c, p->distance);
 	if (p->passive)
 		printf("%s\tpassive\n", c);
-	if (p->local_addr.af)
+	if (p->local_addr.aid)
 		printf("%s\tlocal-address %s\n", c, log_addr(&p->local_addr));
 	if (p->max_prefix) {
 		printf("%s\tmax-prefix %u", c, p->max_prefix);
@@ -295,6 +378,12 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
 		printf("%s\tholdtime min %u\n", c, p->min_holdtime);
 	if (p->announce_capa == 0)
 		printf("%s\tannounce capabilities no\n", c);
+	if (p->capabilities.refresh == 0)
+		printf("%s\tannounce refresh no\n", c);
+	if (p->capabilities.restart == 1)
+		printf("%s\tannounce restart yes\n", c);
+	if (p->capabilities.as4byte == 0)
+		printf("%s\tannounce as4byte no\n", c);
 	if (p->announce_type == ANNOUNCE_SELF)
 		printf("%s\tannounce self\n", c);
 	else if (p->announce_type == ANNOUNCE_NONE)
@@ -324,6 +413,10 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
 		printf("%s\tdepend on \"%s\"\n", c, p->if_depend);
 	if (p->flags & PEERFLAG_TRANS_AS)
 		printf("%s\ttransparent-as yes\n", c);
+#if defined(IPV6_LINKLOCAL_PEER)
+	if (p->lliface[0])
+		printf("%s\tinterface %s\n", c, p->lliface);
+#endif
 
 	if (p->auth.method == AUTH_MD5SIG)
 		printf("%s\ttcp md5sig\n", c);
@@ -354,8 +447,7 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
 	if (p->ttlsec)
 		printf("%s\tttl-security yes\n", c);
 
-	printf("%s\tannounce IPv4 %s\n", c, print_safi(p->capabilities.mp_v4));
-	printf("%s\tannounce IPv6 %s\n", c, print_safi(p->capabilities.mp_v6));
+	print_announce(p, c);
 
 	if (p->softreconfig_in == 1)
 		printf("%s\tsoftreconfig in yes\n", c);
@@ -399,17 +491,14 @@ print_enc_alg(u_int8_t alg)
 	}
 }
 
-const char *
-print_safi(u_int8_t safi)
+void
+print_announce(struct peer_config *p, const char *c)
 {
-	switch (safi) {
-	case SAFI_NONE:
-		return ("none");
-	case SAFI_UNICAST:
-		return ("unicast");
-	default:
-		return ("?");
-	}
+	u_int8_t	aid;
+
+	for (aid = 0; aid < AID_MAX; aid++)
+		if (p->capabilities.mp[aid])
+			printf("%s\tannounce %s\n", c, aid2str(aid));
 }
 
 void
@@ -455,14 +544,14 @@ print_rule(struct peer *peer_l, struct filter_rule *r)
 	} else
 		printf("any ");
 
-	if (r->match.prefix.addr.af)
+	if (r->match.prefix.addr.aid)
 		printf("prefix %s/%u ", log_addr(&r->match.prefix.addr),
 		    r->match.prefix.len);
 
-	if (r->match.prefix.addr.af == 0 && r->match.prefixlen.af) {
-		if (r->match.prefixlen.af == AF_INET)
+	if (r->match.prefix.addr.aid == 0 && r->match.prefixlen.aid) {
+		if (r->match.prefixlen.aid == AID_INET)
 			printf("inet ");
-		if (r->match.prefixlen.af == AF_INET6)
+		if (r->match.prefixlen.aid == AID_INET6)
 			printf("inet6 ");
 	}
 
@@ -492,11 +581,20 @@ print_rule(struct peer *peer_l, struct filter_rule *r)
 			printf("unfluffy-as %s ", log_as(r->match.as.as));
 	}
 
+	if (r->match.aslen.type) {
+		printf("%s %u ", r->match.aslen.type == ASLEN_MAX ?
+		    "max-as-len" : "max-as-seq", r->match.aslen.aslen);
+	}
+
 	if (r->match.community.as != COMMUNITY_UNSET) {
 		printf("community ");
 		print_community(r->match.community.as,
 		    r->match.community.type);
 	}
+	if (r->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) {
+		printf("ext-community ");
+		print_extcommunity(&r->match.ext_community);
+	}
 
 	print_set(&r->set);
 
@@ -547,7 +645,7 @@ print_mrt(u_int32_t pid, u_int32_t gid, const char *prep, const char *prep2)
 			else
 				printf("%s %s %d\n", mrt_type(m->type),
 				    MRT2MC(m)->name,
-				    MRT2MC(m)->ReopenTimerInterval);
+				    (int)MRT2MC(m)->ReopenTimerInterval);
 		}
 }
 
@@ -612,26 +710,34 @@ peer_compare(const void *aa, const void *bb)
 void
 print_config(struct bgpd_config *conf, struct rib_names *rib_l,
     struct network_head *net_l, struct peer *peer_l,
-    struct filter_head *rules_l, struct mrt_head *mrt_l)
+    struct filter_head *rules_l, struct mrt_head *mrt_l,
+    struct rdomain_head *rdom_l)
 {
 	struct filter_rule	*r;
 	struct network		*n;
 	struct rde_rib		*rr;
+	struct rdomain		*rd;
 
 	xmrt_l = mrt_l;
-	printf("\n");
 	print_mainconf(conf);
 	printf("\n");
+	TAILQ_FOREACH(n, net_l, entry)
+		print_network(&n->net);
+	printf("\n");
+	SIMPLEQ_FOREACH(rd, rdom_l, entry)
+		print_rdomain(rd);
+	printf("\n");
 	SIMPLEQ_FOREACH(rr, rib_l, entry) {
 		if (rr->flags & F_RIB_NOEVALUATE)
 			printf("rde rib %s no evaluate\n", rr->name);
-		else
+		else if (rr->flags & F_RIB_NOFIB)
 			printf("rde rib %s\n", rr->name);
+		else
+			printf("rde rib %s rtable %u fib-update %s\n", rr->name,
+			    rr->rtableid, rr->flags & F_RIB_NOFIBSYNC ?
+			    "no" : "yes");
 	}
 	printf("\n");
-	TAILQ_FOREACH(n, net_l, entry)
-		print_network(&n->net);
-	printf("\n");
 	print_mrt(0, 0, "", "");
 	printf("\n");
 	print_groups(conf, peer_l);
diff --git a/bgpd/rde.c b/bgpd/rde.c
index 05b378e..74c07cd 100644
--- a/bgpd/rde.c
+++ b/bgpd/rde.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde.c,v 1.264 2009/06/29 12:22:16 claudio Exp $ */
+/*	$OpenBSD: rde.c,v 1.290 2010/03/30 15:43:30 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -18,6 +18,8 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 
 #include <errno.h>
 #include <ifaddrs.h>
@@ -51,12 +53,16 @@ void		 rde_update_withdraw(struct rde_peer *, struct bgpd_addr *,
 int		 rde_attr_parse(u_char *, u_int16_t, struct rde_peer *,
 		     struct rde_aspath *, struct mpattr *);
 u_int8_t	 rde_attr_missing(struct rde_aspath *, int, u_int16_t);
-int		 rde_get_mp_nexthop(u_char *, u_int16_t, u_int16_t,
-		     struct rde_aspath *);
+int		 rde_get_mp_nexthop(u_char *, u_int16_t, u_int8_t,
+		     struct rde_aspath *, struct rde_peer *);
+int		 rde_update_extract_prefix(u_char *, u_int16_t, void *,
+		     u_int8_t, u_int8_t);
 int		 rde_update_get_prefix(u_char *, u_int16_t, struct bgpd_addr *,
 		     u_int8_t *);
 int		 rde_update_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *,
 		     u_int8_t *);
+int		 rde_update_get_vpn4(u_char *, u_int16_t, struct bgpd_addr *,
+		     u_int8_t *);
 void		 rde_update_err(struct rde_peer *, u_int8_t , u_int8_t,
 		     void *, u_int16_t);
 void		 rde_update_log(const char *, u_int16_t,
@@ -78,11 +84,15 @@ void		 rde_dump_ctx_new(struct ctl_show_rib_request *, pid_t,
 void		 rde_dump_mrt_new(struct mrt *, pid_t, int);
 void		 rde_dump_done(void *);
 
+int		 rde_rdomain_import(struct rde_aspath *, struct rdomain *);
 void		 rde_up_dump_upcall(struct rib_entry *, void *);
 void		 rde_softreconfig_out(struct rib_entry *, void *);
 void		 rde_softreconfig_in(struct rib_entry *, void *);
+void		 rde_softreconfig_load(struct rib_entry *, void *);
+void		 rde_softreconfig_load_peer(struct rib_entry *, void *);
+void		 rde_softreconfig_unload_peer(struct rib_entry *, void *);
 void		 rde_update_queue_runner(void);
-void		 rde_update6_queue_runner(void);
+void		 rde_update6_queue_runner(u_int8_t);
 
 void		 peer_init(u_int32_t);
 void		 peer_shutdown(void);
@@ -91,10 +101,9 @@ struct rde_peer	*peer_add(u_int32_t, struct peer_config *);
 struct rde_peer	*peer_get(u_int32_t);
 void		 peer_up(u_int32_t, struct session_up *);
 void		 peer_down(u_int32_t);
-void		 peer_dump(u_int32_t, u_int16_t, u_int8_t);
-void		 peer_send_eor(struct rde_peer *, u_int16_t, u_int16_t);
+void		 peer_dump(u_int32_t, u_int8_t);
+void		 peer_send_eor(struct rde_peer *, u_int8_t);
 
-void		 network_init(struct network_head *);
 void		 network_add(struct network_config *, int);
 void		 network_delete(struct network_config *, int);
 void		 network_dump_upcall(struct rib_entry *, void *);
@@ -108,6 +117,7 @@ time_t			 reloadtime;
 struct rde_peer_head	 peerlist;
 struct rde_peer		*peerself;
 struct filter_head	*rules_l, *newrules;
+struct rdomain_head	*rdomains_l, *newdomains;
 struct imsgbuf		*ibuf_se;
 struct imsgbuf		*ibuf_se_ctl;
 struct imsgbuf		*ibuf_main;
@@ -120,11 +130,12 @@ struct rde_dump_ctx {
 };
 
 struct rde_mrt_ctx {
-	struct mrt		 mrt;
-	struct rib_context	 ribctx;
+	struct mrt		mrt;
+	struct rib_context	ribctx;
+	LIST_ENTRY(rde_mrt_ctx)	entry;
 };
 
-struct mrt_head rde_mrts = LIST_HEAD_INITIALIZER(rde_mrts);
+LIST_HEAD(, rde_mrt_ctx) rde_mrts = LIST_HEAD_INITIALIZER(rde_mrts);
 u_int rde_mrt_cnt;
 
 void
@@ -144,24 +155,18 @@ u_int32_t	attrhashsize = 512;
 u_int32_t	nexthophashsize = 64;
 
 pid_t
-rde_main(struct bgpd_config *config, struct peer *peer_l,
-    struct network_head *net_l, struct filter_head *rules,
-    struct mrt_head *mrt_l, struct rib_names *rib_n, int pipe_m2r[2],
-    int pipe_s2r[2], int pipe_m2s[2], int pipe_s2rctl[2], int debug)
+rde_main(int pipe_m2r[2], int pipe_s2r[2], int pipe_m2s[2], int pipe_s2rctl[2],
+    int debug)
 {
+	struct rlimit		 rl;
 	pid_t			 pid;
 	struct passwd		*pw;
-	struct peer		*p;
-	struct listen_addr	*la;
 	struct pollfd		*pfd = NULL;
-	struct filter_rule	*f;
-	struct filter_set	*set;
-	struct nexthop		*nh;
-	struct rde_rib		*rr;
-	struct mrt		*mrt, *xmrt;
+	struct rde_mrt_ctx	*mctx, *xmctx;
 	void			*newp;
 	u_int			 pfd_elms = 0, i, j;
 	int			 timeout;
+	u_int8_t		 aid;
 
 	switch (pid = fork()) {
 	case -1:
@@ -172,8 +177,6 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 		return (pid);
 	}
 
-	conf = config;
-
 	if ((pw = getpwnam(BGPD_USER)) == NULL)
 		fatal("getpwnam");
 
@@ -185,6 +188,12 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 	setproctitle("route decision engine");
 	bgpd_process = PROC_RDE;
 
+	if (getrlimit(RLIMIT_DATA, &rl) == -1)
+		fatal("getrlimit");
+	rl.rlim_cur = rl.rlim_max;
+	if (setrlimit(RLIMIT_DATA, &rl) == -1)
+		fatal("setrlimit");
+
 	if (setgroups(1, &pw->pw_gid) ||
 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
@@ -194,6 +203,8 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 	signal(SIGINT, rde_sighdlr);
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGHUP, SIG_IGN);
+	signal(SIGALRM, SIG_IGN);
+	signal(SIGUSR1, SIG_IGN);
 
 	close(pipe_s2r[0]);
 	close(pipe_s2rctl[0]);
@@ -210,50 +221,25 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 	imsg_init(ibuf_se_ctl, pipe_s2rctl[1]);
 	imsg_init(ibuf_main, pipe_m2r[1]);
 
-	/* peer list, mrt list and listener list are not used in the RDE */
-	while ((p = peer_l) != NULL) {
-		peer_l = p->next;
-		free(p);
-	}
-
-	while ((mrt = LIST_FIRST(mrt_l)) != NULL) {
-		LIST_REMOVE(mrt, entry);
-		free(mrt);
-	}
-
-	while ((la = TAILQ_FIRST(config->listen_addrs)) != NULL) {
-		TAILQ_REMOVE(config->listen_addrs, la, entry);
-		close(la->fd);
-		free(la);
-	}
-	free(config->listen_addrs);
-
 	pt_init();
-	while ((rr = SIMPLEQ_FIRST(&ribnames))) {
-		SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
-		rib_new(-1, rr->name, rr->flags);
-		free(rr);
-	}
 	path_init(pathhashsize);
 	aspath_init(pathhashsize);
 	attr_init(attrhashsize);
 	nexthop_init(nexthophashsize);
 	peer_init(peerhashsize);
-	rules_l = rules;
-	network_init(net_l);
 
+	rules_l = calloc(1, sizeof(struct filter_head));
+	if (rules_l == NULL)
+		fatal(NULL);
+	TAILQ_INIT(rules_l);
+	rdomains_l = calloc(1, sizeof(struct rdomain_head));
+	if (rdomains_l == NULL)
+		fatal(NULL);
+	SIMPLEQ_INIT(rdomains_l);
+	if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL)
+		fatal(NULL);
 	log_info("route decision engine ready");
 
-	TAILQ_FOREACH(f, rules, entry) {
-		f->peer.ribid = rib_find(f->rib);
-		TAILQ_FOREACH(set, &f->set, entry) {
-			if (set->type == ACTION_SET_NEXTHOP) {
-				nh = nexthop_get(&set->action.nexthop);
-				nh->refcnt++;
-			}
-		}
-	}
-
 	while (rde_quit == 0) {
 		if (pfd_elms < PFD_PIPE_COUNT + rde_mrt_cnt) {
 			if ((newp = realloc(pfd, sizeof(struct pollfd) *
@@ -287,11 +273,18 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 			timeout = 0;
 
 		i = PFD_PIPE_COUNT;
-		LIST_FOREACH(mrt, &rde_mrts, entry) {
-			if (mrt->wbuf.queued) {
-				pfd[i].fd = mrt->wbuf.fd;
+		for (mctx = LIST_FIRST(&rde_mrts); mctx != 0; mctx = xmctx) {
+			xmctx = LIST_NEXT(mctx, entry);
+			if (mctx->mrt.wbuf.queued) {
+				pfd[i].fd = mctx->mrt.wbuf.fd;
 				pfd[i].events = POLLOUT;
 				i++;
+			} else if (mctx->mrt.state == MRT_STATE_REMOVE) {
+				close(mctx->mrt.wbuf.fd);
+				LIST_REMOVE(&mctx->ribctx, entry);
+				LIST_REMOVE(mctx, entry);
+				free(mctx);
+				rde_mrt_cnt--;
 			}
 		}
 
@@ -325,24 +318,17 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 		if (pfd[PFD_PIPE_SESSION_CTL].revents & POLLIN)
 			rde_dispatch_imsg_session(ibuf_se_ctl);
 
-		for (j = PFD_PIPE_COUNT, mrt = LIST_FIRST(&rde_mrts);
-		    j < i && mrt != 0; j++) {
-			xmrt = LIST_NEXT(mrt, entry);
-			if (pfd[j].fd == mrt->wbuf.fd &&
+		for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts);
+		    j < i && mctx != 0; j++) {
+			if (pfd[j].fd == mctx->mrt.wbuf.fd &&
 			    pfd[j].revents & POLLOUT)
-				mrt_write(mrt);
-			if (mrt->wbuf.queued == 0 && 
-			    mrt->state == MRT_STATE_REMOVE) {
-				close(mrt->wbuf.fd);
-				LIST_REMOVE(mrt, entry);
-				free(mrt);
-				rde_mrt_cnt--;
-			}
-			mrt = xmrt;
+				mrt_write(&mctx->mrt);
+			mctx = LIST_NEXT(mctx, entry);
 		}
 
 		rde_update_queue_runner();
-		rde_update6_queue_runner();
+		for (aid = AID_INET6; aid < AID_MAX; aid++)
+			rde_update6_queue_runner(aid);
 		if (ibuf_se_ctl->w.queued <= 0)
 			rib_dump_runner();
 	}
@@ -351,11 +337,12 @@ rde_main(struct bgpd_config *config, struct peer *peer_l,
 	if (debug)
 		rde_shutdown();
 
-	while ((mrt = LIST_FIRST(&rde_mrts)) != NULL) {
-		msgbuf_clear(&mrt->wbuf);
-		close(mrt->wbuf.fd);
-		LIST_REMOVE(mrt, entry);
-		free(mrt);
+	while ((mctx = LIST_FIRST(&rde_mrts)) != NULL) {
+		msgbuf_clear(&mctx->mrt.wbuf);
+		close(mctx->mrt.wbuf.fd);
+		LIST_REMOVE(&mctx->ribctx, entry);
+		LIST_REMOVE(mctx, entry);
+		free(mctx);
 	}
 
 	msgbuf_clear(&ibuf_se->w);
@@ -378,13 +365,14 @@ rde_dispatch_imsg_session(struct imsgbuf *ibuf)
 	struct imsg		 imsg;
 	struct peer		 p;
 	struct peer_config	 pconf;
-	struct rrefresh		 r;
 	struct rde_peer		*peer;
 	struct session_up	 sup;
 	struct ctl_show_rib_request	req;
 	struct filter_set	*s;
 	struct nexthop		*nh;
-	int			 n;
+	ssize_t			 n;
+	int			 verbose;
+	u_int8_t		 aid;
 
 	if ((n = imsg_read(ibuf)) == -1)
 		fatal("rde_dispatch_imsg_session: imsg_read error");
@@ -423,12 +411,14 @@ rde_dispatch_imsg_session(struct imsgbuf *ibuf)
 			peer_down(imsg.hdr.peerid);
 			break;
 		case IMSG_REFRESH:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(r)) {
+			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) {
 				log_warnx("rde_dispatch: wrong imsg len");
 				break;
 			}
-			memcpy(&r, imsg.data, sizeof(r));
-			peer_dump(imsg.hdr.peerid, r.afi, r.safi);
+			memcpy(&aid, imsg.data, sizeof(aid));
+			if (aid >= AID_MAX)
+				fatalx("IMSG_REFRESH: bad AID");
+			peer_dump(imsg.hdr.peerid, aid);
 			break;
 		case IMSG_NETWORK_ADD:
 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
@@ -446,13 +436,13 @@ rde_dispatch_imsg_session(struct imsgbuf *ibuf)
 				break;
 			}
 			session_set = NULL;
-			switch (netconf_s.prefix.af) {
-			case AF_INET:
+			switch (netconf_s.prefix.aid) {
+			case AID_INET:
 				if (netconf_s.prefixlen > 32)
 					goto badnet;
 				network_add(&netconf_s, 0);
 				break;
-			case AF_INET6:
+			case AID_INET6:
 				if (netconf_s.prefixlen > 128)
 					goto badnet;
 				network_add(&netconf_s, 0);
@@ -544,6 +534,11 @@ badnet:
 			imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_MEM, 0,
 			    imsg.hdr.pid, -1, &rdemem, sizeof(rdemem));
 			break;
+		case IMSG_CTL_LOG_VERBOSE:
+			/* already checked by SE */
+			memcpy(&verbose, imsg.data, sizeof(verbose));
+			log_verbose(verbose);
+			break;
 		default:
 			break;
 		}
@@ -554,14 +549,17 @@ badnet:
 void
 rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 {
+	static struct rdomain	*rd;
 	struct imsg		 imsg;
 	struct mrt		 xmrt;
 	struct rde_rib		 rn;
 	struct rde_peer		*peer;
+	struct peer_config	*pconf;
 	struct filter_rule	*r;
 	struct filter_set	*s;
 	struct nexthop		*nh;
-	int			 n, fd, reconf_in = 0, reconf_out = 0;
+	int			 n, fd, reconf_in = 0, reconf_out = 0,
+				 reconf_rib = 0;
 	u_int16_t		 rid;
 
 	if ((n = imsg_read(ibuf)) == -1)
@@ -576,20 +574,12 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 			break;
 
 		switch (imsg.hdr.type) {
-		case IMSG_RECONF_CONF:
-			reloadtime = time(NULL);
-			newrules = calloc(1, sizeof(struct filter_head));
-			if (newrules == NULL)
-				fatal(NULL);
-			TAILQ_INIT(newrules);
-			if ((nconf = malloc(sizeof(struct bgpd_config))) ==
-			    NULL)
-				fatal(NULL);
-			memcpy(nconf, imsg.data, sizeof(struct bgpd_config));
-			for (rid = 0; rid < rib_size; rid++)
-				ribs[rid].state = RIB_DELETE;
-			break;
 		case IMSG_NETWORK_ADD:
+			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+			    sizeof(struct network_config)) {
+				log_warnx("rde_dispatch: wrong imsg len");
+				break;
+			}
 			memcpy(&netconf_p, imsg.data, sizeof(netconf_p));
 			TAILQ_INIT(&netconf_p.attrset);
 			parent_set = &netconf_p.attrset;
@@ -608,6 +598,26 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 			TAILQ_INIT(&netconf_p.attrset);
 			network_delete(&netconf_p, 1);
 			break;
+		case IMSG_RECONF_CONF:
+			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+			    sizeof(struct bgpd_config))
+				fatalx("IMSG_RECONF_CONF bad len");
+			reloadtime = time(NULL);
+			newrules = calloc(1, sizeof(struct filter_head));
+			if (newrules == NULL)
+				fatal(NULL);
+			TAILQ_INIT(newrules);
+			newdomains = calloc(1, sizeof(struct rdomain_head));
+			if (newdomains == NULL)
+				fatal(NULL);
+			SIMPLEQ_INIT(newdomains);
+			if ((nconf = malloc(sizeof(struct bgpd_config))) ==
+			    NULL)
+				fatal(NULL);
+			memcpy(nconf, imsg.data, sizeof(struct bgpd_config));
+			for (rid = 0; rid < rib_size; rid++)
+				ribs[rid].state = RECONF_DELETE;
+			break;
 		case IMSG_RECONF_RIB:
 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
 			    sizeof(struct rde_rib))
@@ -615,9 +625,26 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 			memcpy(&rn, imsg.data, sizeof(rn));
 			rid = rib_find(rn.name);
 			if (rid == RIB_FAILED)
-				rib_new(-1, rn.name, rn.flags);
-			else
-				ribs[rid].state = RIB_ACTIVE;
+				rib_new(rn.name, rn.rtableid, rn.flags);
+			else if (ribs[rid].rtableid != rn.rtableid ||
+			    (ribs[rid].flags & F_RIB_HASNOFIB) !=
+			    (rn.flags & F_RIB_HASNOFIB)) {
+				/* Big hammer in the F_RIB_NOFIB case but
+				 * not often enough used to optimise it more. */
+				rib_free(&ribs[rid]);
+				rib_new(rn.name, rn.rtableid, rn.flags);
+			} else
+				ribs[rid].state = RECONF_KEEP;
+			break;
+		case IMSG_RECONF_PEER:
+			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+			    sizeof(struct peer_config))
+				fatalx("IMSG_RECONF_PEER bad len");
+			if ((peer = peer_get(imsg.hdr.peerid)) == NULL)
+				break;
+			pconf = imsg.data;
+			strlcpy(peer->conf.rib, pconf->rib,
+			    sizeof(peer->conf.rib));
 			break;
 		case IMSG_RECONF_FILTER:
 			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
@@ -631,12 +658,42 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 			parent_set = &r->set;
 			TAILQ_INSERT_TAIL(newrules, r, entry);
 			break;
+		case IMSG_RECONF_RDOMAIN:
+			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+			    sizeof(struct rdomain))
+				fatalx("IMSG_RECONF_RDOMAIN bad len");
+			if ((rd = malloc(sizeof(struct rdomain))) == NULL)
+				fatal(NULL);
+			memcpy(rd, imsg.data, sizeof(struct rdomain));
+			TAILQ_INIT(&rd->import);
+			TAILQ_INIT(&rd->export);
+			SIMPLEQ_INSERT_TAIL(newdomains, rd, entry);
+			break;
+		case IMSG_RECONF_RDOMAIN_EXPORT:
+			if (rd == NULL) {
+				log_warnx("rde_dispatch_imsg_parent: "
+				    "IMSG_RECONF_RDOMAIN_EXPORT unexpected");
+				break;
+			}
+			parent_set = &rd->export;
+			break;
+		case IMSG_RECONF_RDOMAIN_IMPORT:
+			if (rd == NULL) {
+				log_warnx("rde_dispatch_imsg_parent: "
+				    "IMSG_RECONF_RDOMAIN_IMPORT unexpected");
+				break;
+			}
+			parent_set = &rd->import;
+			break;
+		case IMSG_RECONF_RDOMAIN_DONE:
+			parent_set = NULL;
+			break;
 		case IMSG_RECONF_DONE:
 			if (nconf == NULL)
 				fatalx("got IMSG_RECONF_DONE but no config");
 			if ((nconf->flags & BGPD_FLAG_NO_EVALUATE)
 			    != (conf->flags & BGPD_FLAG_NO_EVALUATE)) {
-				log_warnx( "change to/from route-collector "
+				log_warnx("change to/from route-collector "
 				    "mode ignored");
 				if (conf->flags & BGPD_FLAG_NO_EVALUATE)
 					nconf->flags |= BGPD_FLAG_NO_EVALUATE;
@@ -644,10 +701,27 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 					nconf->flags &= ~BGPD_FLAG_NO_EVALUATE;
 			}
 			memcpy(conf, nconf, sizeof(struct bgpd_config));
+			conf->listen_addrs = NULL;
+			conf->csock = NULL;
+			conf->rcsock = NULL;
 			free(nconf);
 			nconf = NULL;
 			parent_set = NULL;
-			prefix_network_clean(peerself, reloadtime, 0);
+			/* sync peerself with conf */
+			peerself->remote_bgpid = ntohl(conf->bgpid);
+			peerself->conf.local_as = conf->as;
+			peerself->conf.remote_as = conf->as;
+			peerself->short_as = conf->short_as;
+
+			/* apply new set of rdomain, sync will be done later */
+			while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) {
+				SIMPLEQ_REMOVE_HEAD(rdomains_l, entry);
+				filterset_free(&rd->import);
+				filterset_free(&rd->export);
+				free(rd);
+			}
+			free(rdomains_l);
+			rdomains_l = newdomains;
 
 			/* check if filter changed */
 			LIST_FOREACH(peer, &peerlist, peer_l) {
@@ -655,30 +729,59 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 					continue;
 				peer->reconf_out = 0;
 				peer->reconf_in = 0;
-				if (peer->conf.softreconfig_out &&
-				    !rde_filter_equal(rules_l, newrules, peer,
-				    DIR_OUT)) {
-					peer->reconf_out = 1;
-					reconf_out = 1;
-				}
+				peer->reconf_rib = 0;
 				if (peer->conf.softreconfig_in &&
 				    !rde_filter_equal(rules_l, newrules, peer,
 				    DIR_IN)) {
 					peer->reconf_in = 1;
 					reconf_in = 1;
 				}
+				if (peer->ribid != rib_find(peer->conf.rib)) {
+					rib_dump(&ribs[peer->ribid],
+					    rde_softreconfig_unload_peer, peer,
+					    AID_UNSPEC);
+					peer->ribid = rib_find(peer->conf.rib);
+					peer->reconf_rib = 1;
+					reconf_rib = 1;
+					continue;
+				}
+				if (peer->conf.softreconfig_out &&
+				    !rde_filter_equal(rules_l, newrules, peer,
+				    DIR_OUT)) {
+					peer->reconf_out = 1;
+					reconf_out = 1;
+				}
 			}
-			/* XXX this needs rework anyway */
-			/* sync local-RIB first */
+			/* bring ribs in sync before softreconfig dance */
+			for (rid = 0; rid < rib_size; rid++) {
+				if (ribs[rid].state == RECONF_DELETE)
+					rib_free(&ribs[rid]);
+				else if (ribs[rid].state == RECONF_REINIT)
+					rib_dump(&ribs[0],
+					    rde_softreconfig_load, &ribs[rid],
+					    AID_UNSPEC);
+			}
+			/* sync local-RIBs first */
 			if (reconf_in)
 				rib_dump(&ribs[0], rde_softreconfig_in, NULL,
-				    AF_UNSPEC);
+				    AID_UNSPEC);
 			/* then sync peers */
 			if (reconf_out) {
 				int i;
-				for (i = 1; i < rib_size; i++)
+				for (i = 1; i < rib_size; i++) {
+					if (ribs[i].state == RECONF_REINIT)
+						/* already synced by _load */
+						continue;
 					rib_dump(&ribs[i], rde_softreconfig_out,
-					    NULL, AF_UNSPEC);
+					    NULL, AID_UNSPEC);
+				}
+			}
+			if (reconf_rib) {
+				LIST_FOREACH(peer, &peerlist, peer_l) {
+					rib_dump(&ribs[peer->ribid],
+						rde_softreconfig_load_peer,
+						peer, AID_UNSPEC);
+				}
 			}
 
 			while ((r = TAILQ_FIRST(rules_l)) != NULL) {
@@ -688,16 +791,16 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 			}
 			free(rules_l);
 			rules_l = newrules;
-			for (rid = 0; rid < rib_size; rid++) {
-				if (ribs[rid].state == RIB_DELETE)
-					rib_free(&ribs[rid]);
-			}
+
 			log_info("RDE reconfigured");
 			break;
 		case IMSG_NEXTHOP_UPDATE:
 			nexthop_update(imsg.data);
 			break;
 		case IMSG_FILTER_SET:
+			if (imsg.hdr.len > IMSG_HEADER_SIZE +
+			    sizeof(struct filter_set))
+				fatalx("IMSG_RECONF_CONF bad len");
 			if (parent_set == NULL) {
 				log_warnx("rde_dispatch_imsg_parent: "
 				    "IMSG_FILTER_SET unexpected");
@@ -744,6 +847,8 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
 int
 rde_update_dispatch(struct imsg *imsg)
 {
+	struct bgpd_addr	 prefix;
+	struct mpattr		 mpa;
 	struct rde_peer		*peer;
 	struct rde_aspath	*asp = NULL;
 	u_char			*p, *mpp = NULL;
@@ -752,9 +857,8 @@ rde_update_dispatch(struct imsg *imsg)
 	u_int16_t		 withdrawn_len;
 	u_int16_t		 attrpath_len;
 	u_int16_t		 nlri_len;
-	u_int8_t		 prefixlen, safi, subtype;
-	struct bgpd_addr	 prefix;
-	struct mpattr		 mpa;
+	u_int8_t		 aid, prefixlen, safi, subtype;
+	u_int32_t		 fas;
 
 	peer = peer_get(imsg->hdr.peerid);
 	if (peer == NULL)	/* unknown peer, cannot happen */
@@ -810,26 +914,21 @@ rde_update_dispatch(struct imsg *imsg)
 			goto done;
 		}
 
-		/*
-		 * if either ATTR_AS4_AGGREGATOR or ATTR_AS4_PATH is present
-		 * try to fixup the attributes.
-		 * XXX do not fixup if F_ATTR_LOOP is set.
-		 */
-		if (asp->flags & F_ATTR_AS4BYTE_NEW &&
-		    !(asp->flags & F_ATTR_LOOP))
-			rde_as4byte_fixup(peer, asp);
+		rde_as4byte_fixup(peer, asp);
 
 		/* enforce remote AS if requested */
 		if (asp->flags & F_ATTR_ASPATH &&
-		    peer->conf.enforce_as == ENFORCE_AS_ON)
-			if (peer->conf.remote_as !=
-			    aspath_neighbor(asp->aspath)) {
-				log_peer_warnx(&peer->conf, "bad path, "
-				    "enforce remote-as enabled");
-				rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH,
+		    peer->conf.enforce_as == ENFORCE_AS_ON) {
+			fas = aspath_neighbor(asp->aspath);
+			if (peer->conf.remote_as != fas) {
+			    log_peer_warnx(&peer->conf, "bad path, "
+				"starting with %s, "
+				"enforce neighbor-as enabled", log_as(fas));
+			    rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH,
 				    NULL, 0);
-				goto done;
+			    goto done;
 			}
+		}
 
 		rde_reflector(peer, asp);
 	}
@@ -860,9 +959,9 @@ rde_update_dispatch(struct imsg *imsg)
 		p += pos;
 		len -= pos;
 
-		if (peer->capa_received.mp_v4 == SAFI_NONE &&
-		    peer->capa_received.mp_v6 != SAFI_NONE) {
-			log_peer_warnx(&peer->conf, "bad AFI, IPv4 disabled");
+		if (peer->capa.mp[AID_INET] == 0) {
+			log_peer_warnx(&peer->conf,
+			    "bad withdraw, %s disabled", aid2str(AID_INET));
 			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
 			    NULL, 0);
 			goto done;
@@ -892,15 +991,25 @@ rde_update_dispatch(struct imsg *imsg)
 		afi = ntohs(afi);
 		safi = *mpp++;
 		mplen--;
-		switch (afi) {
-		case AFI_IPv6:
-			if (peer->capa_received.mp_v6 == SAFI_NONE) {
-				log_peer_warnx(&peer->conf, "bad AFI, "
-				    "IPv6 disabled");
-				rde_update_err(peer, ERR_UPDATE,
-				    ERR_UPD_OPTATTR, NULL, 0);
-				goto done;
-			}
+
+		if (afi2aid(afi, safi, &aid) == -1) {
+			log_peer_warnx(&peer->conf,
+			    "bad AFI/SAFI pair in withdraw");
+			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
+			    NULL, 0);
+			goto done;
+		}
+
+		if (peer->capa.mp[aid] == 0) {
+			log_peer_warnx(&peer->conf,
+			    "bad withdraw, %s disabled", aid2str(aid));
+			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
+			    NULL, 0);
+			goto done;
+		}
+
+		switch (aid) {
+		case AID_INET6:
 			while (mplen > 0) {
 				if ((pos = rde_update_get_prefix6(mpp, mplen,
 				    &prefix, &prefixlen)) == -1) {
@@ -926,6 +1035,32 @@ rde_update_dispatch(struct imsg *imsg)
 				rde_update_withdraw(peer, &prefix, prefixlen);
 			}
 			break;
+		case AID_VPN_IPv4:
+			while (mplen > 0) {
+				if ((pos = rde_update_get_vpn4(mpp, mplen,
+				    &prefix, &prefixlen)) == -1) {
+					log_peer_warnx(&peer->conf,
+					    "bad VPNv4 withdraw prefix");
+					rde_update_err(peer, ERR_UPDATE,
+					    ERR_UPD_OPTATTR,
+					    mpa.unreach, mpa.unreach_len);
+					goto done;
+				}
+				if (prefixlen > 32) {
+					log_peer_warnx(&peer->conf,
+					    "bad VPNv4 withdraw prefix");
+					rde_update_err(peer, ERR_UPDATE,
+					    ERR_UPD_OPTATTR,
+					    mpa.unreach, mpa.unreach_len);
+					goto done;
+				}
+
+				mpp += pos;
+				mplen -= pos;
+
+				rde_update_withdraw(peer, &prefix, prefixlen);
+			}
+			break;
 		default:
 			/* silently ignore unsupported multiprotocol AF */
 			break;
@@ -963,9 +1098,9 @@ rde_update_dispatch(struct imsg *imsg)
 		p += pos;
 		nlri_len -= pos;
 
-		if (peer->capa_received.mp_v4 == SAFI_NONE &&
-		    peer->capa_received.mp_v6 != SAFI_NONE) {
-			log_peer_warnx(&peer->conf, "bad AFI, IPv4 disabled");
+		if (peer->capa.mp[AID_INET] == 0) {
+			log_peer_warnx(&peer->conf,
+			    "bad update, %s disabled", aid2str(AID_INET));
 			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
 			    NULL, 0);
 			goto done;
@@ -995,6 +1130,22 @@ rde_update_dispatch(struct imsg *imsg)
 		safi = *mpp++;
 		mplen--;
 
+		if (afi2aid(afi, safi, &aid) == -1) {
+			log_peer_warnx(&peer->conf,
+			    "bad AFI/SAFI pair in update");
+			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
+			    NULL, 0);
+			goto done;
+		}
+
+		if (peer->capa.mp[aid] == 0) {
+			log_peer_warnx(&peer->conf,
+			    "bad update, %s disabled", aid2str(aid));
+			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
+			    NULL, 0);
+			goto done;
+		}
+
 		/*
 		 * this works because asp is not linked.
 		 * But first unlock the previously locked nexthop.
@@ -1004,8 +1155,8 @@ rde_update_dispatch(struct imsg *imsg)
 			(void)nexthop_delete(asp->nexthop);
 			asp->nexthop = NULL;
 		}
-		if ((pos = rde_get_mp_nexthop(mpp, mplen, afi, asp)) == -1) {
-			log_peer_warnx(&peer->conf, "bad IPv6 nlri prefix");
+		if ((pos = rde_get_mp_nexthop(mpp, mplen, aid, asp, peer)) == -1) {
+			log_peer_warnx(&peer->conf, "bad nlri prefix");
 			rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR,
 			    mpa.reach, mpa.reach_len);
 			goto done;
@@ -1013,16 +1164,8 @@ rde_update_dispatch(struct imsg *imsg)
 		mpp += pos;
 		mplen -= pos;
 
-		switch (afi) {
-		case AFI_IPv6:
-			if (peer->capa_received.mp_v6 == SAFI_NONE) {
-				log_peer_warnx(&peer->conf, "bad AFI, "
-				    "IPv6 disabled");
-				rde_update_err(peer, ERR_UPDATE,
-				    ERR_UPD_OPTATTR, NULL, 0);
-				goto done;
-			}
-
+		switch (aid) {
+		case AID_INET6:
 			while (mplen > 0) {
 				if ((pos = rde_update_get_prefix6(mpp, mplen,
 				    &prefix, &prefixlen)) == -1) {
@@ -1058,6 +1201,42 @@ rde_update_dispatch(struct imsg *imsg)
 
 			}
 			break;
+		case AID_VPN_IPv4:
+			while (mplen > 0) {
+				if ((pos = rde_update_get_vpn4(mpp, mplen,
+				    &prefix, &prefixlen)) == -1) {
+					log_peer_warnx(&peer->conf,
+					    "bad VPNv4 nlri prefix");
+					rde_update_err(peer, ERR_UPDATE,
+					    ERR_UPD_OPTATTR,
+					    mpa.reach, mpa.reach_len);
+					goto done;
+				}
+				if (prefixlen > 32) {
+					rde_update_err(peer, ERR_UPDATE,
+					    ERR_UPD_OPTATTR,
+					    mpa.reach, mpa.reach_len);
+					goto done;
+				}
+
+				mpp += pos;
+				mplen -= pos;
+
+				rde_update_update(peer, asp, &prefix,
+				    prefixlen);
+
+				/* max prefix checker */
+				if (peer->conf.max_prefix &&
+				    peer->prefix_cnt >= peer->conf.max_prefix) {
+					log_peer_warnx(&peer->conf,
+					    "prefix limit reached");
+					rde_update_err(peer, ERR_CEASE,
+					    ERR_CEASE_MAX_PREFIX, NULL, 0);
+					goto done;
+				}
+
+			}
+			break;
 		default:
 			/* silently ignore unsupported multiprotocol AF */
 			break;
@@ -1085,7 +1264,8 @@ rde_update_update(struct rde_peer *peer, struct rde_aspath *asp,
     struct bgpd_addr *prefix, u_int8_t prefixlen)
 {
 	struct rde_aspath	*fasp;
-	int			 r = 0;
+	enum filter_actions	 action;
+	int			 r = 0, f = 0;
 	u_int16_t		 i;
 
 	peer->prefix_rcvd_update++;
@@ -1095,18 +1275,24 @@ rde_update_update(struct rde_peer *peer, struct rde_aspath *asp,
 
 	for (i = 1; i < rib_size; i++) {
 		/* input filter */
-		if (rde_filter(i, &fasp, rules_l, peer, asp, prefix, prefixlen,
-		    peer, DIR_IN) == ACTION_DENY)
-			goto done;
+		action = rde_filter(i, &fasp, rules_l, peer, asp, prefix,
+		    prefixlen, peer, DIR_IN);
 
 		if (fasp == NULL)
 			fasp = asp;
 
-		rde_update_log("update", i, peer, &fasp->nexthop->exit_nexthop,
-		    prefix, prefixlen);
-		r += path_update(&ribs[i], peer, fasp, prefix, prefixlen);
+		if (action == ACTION_ALLOW) {
+			rde_update_log("update", i, peer,
+			    &fasp->nexthop->exit_nexthop, prefix, prefixlen);
+			r += path_update(&ribs[i], peer, fasp, prefix,
+			    prefixlen);
+		} else if (prefix_remove(&ribs[i], peer, prefix, prefixlen,
+		    0)) {
+			rde_update_log("filtered withdraw", i, peer,
+			    NULL, prefix, prefixlen);
+			f++;
+		}
 
-done:
 		/* free modified aspath */
 		if (fasp != asp)
 			path_put(fasp);
@@ -1114,6 +1300,8 @@ done:
 
 	if (r)
 		peer->prefix_cnt++;
+	else if (f)
+		peer->prefix_cnt--;
 }
 
 void
@@ -1161,6 +1349,7 @@ rde_attr_parse(u_char *p, u_int16_t len, struct rde_peer *peer,
 	struct bgpd_addr nexthop;
 	u_char		*op = p, *npath;
 	u_int32_t	 tmp32;
+	int		 err;
 	u_int16_t	 attr_len, nlen;
 	u_int16_t	 plen = 0;
 	u_int8_t	 flags;
@@ -1195,6 +1384,7 @@ bad_len:
 	switch (type) {
 	case ATTR_UNDEF:
 		/* ignore and drop path attributes with a type code of 0 */
+		plen += attr_len;
 		break;
 	case ATTR_ORIGIN:
 		if (attr_len != 1)
@@ -1220,7 +1410,17 @@ bad_flags:
 	case ATTR_ASPATH:
 		if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0))
 			goto bad_flags;
-		if (aspath_verify(p, attr_len, rde_as4byte(peer)) != 0) {
+		err = aspath_verify(p, attr_len, rde_as4byte(peer));
+		if (err == AS_ERR_SOFT) {
+			/*
+			 * soft errors like unexpected segment types are
+			 * not considered fatal and the path is just
+			 * marked invalid.
+			 */
+			a->flags |= F_ATTR_PARSE_ERR;
+			log_peer_warnx(&peer->conf, "bad ASPATH, "
+			    "path invalidated and prefix withdrawn");
+		} else if (err != 0) {
 			rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH,
 			    NULL, 0);
 			return (-1);
@@ -1248,7 +1448,7 @@ bad_flags:
 		a->flags |= F_ATTR_NEXTHOP;
 
 		bzero(&nexthop, sizeof(nexthop));
-		nexthop.af = AF_INET;
+		nexthop.aid = AID_INET;
 		UPD_READ(&nexthop.v4.s_addr, p, plen, 4);
 		/*
 		 * Check if the nexthop is a valid IP address. We consider
@@ -1305,9 +1505,21 @@ bad_flags:
 		goto optattr;
 	case ATTR_AGGREGATOR:
 		if ((!rde_as4byte(peer) && attr_len != 6) ||
-		    (rde_as4byte(peer) && attr_len != 8))
-			goto bad_len;
-		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, 0))
+		    (rde_as4byte(peer) && attr_len != 8)) {
+			/*
+			 * ignore attribute in case of error as per
+			 * draft-ietf-idr-optional-transitive-00.txt
+			 * but only if partial bit is set
+			 */
+			if ((flags & ATTR_PARTIAL) == 0)
+				goto bad_len;
+			log_peer_warnx(&peer->conf, "bad AGGREGATOR, "
+			    "partial attribute ignored");
+			plen += attr_len;
+			break;
+		}
+		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
+		    ATTR_PARTIAL))
 			goto bad_flags;
 		if (!rde_as4byte(peer)) {
 			/* need to inflate aggregator AS to 4-byte */
@@ -1323,8 +1535,35 @@ bad_flags:
 		/* 4-byte ready server take the default route */
 		goto optattr;
 	case ATTR_COMMUNITIES:
-		if ((attr_len & 0x3) != 0)
-			goto bad_len;
+		if (attr_len % 4 != 0) {
+			/*
+			 * mark update as bad and withdraw all routes as per
+			 * draft-ietf-idr-optional-transitive-00.txt
+			 * but only if partial bit is set
+			 */
+			if ((flags & ATTR_PARTIAL) == 0)
+				goto bad_len;
+			a->flags |= F_ATTR_PARSE_ERR;
+			log_peer_warnx(&peer->conf, "bad COMMUNITIES, "
+			    "path invalidated and prefix withdrawn");
+		}
+		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
+		    ATTR_PARTIAL))
+			goto bad_flags;
+		goto optattr;
+	case ATTR_EXT_COMMUNITIES:
+		if (attr_len % 8 != 0) {
+			/*
+			 * mark update as bad and withdraw all routes as per
+			 * draft-ietf-idr-optional-transitive-00.txt
+			 * but only if partial bit is set
+			 */
+			if ((flags & ATTR_PARTIAL) == 0)
+				goto bad_len;
+			a->flags |= F_ATTR_PARSE_ERR;
+			log_peer_warnx(&peer->conf, "bad EXT_COMMUNITIES, "
+			    "path invalidated and prefix withdrawn");
+		}
 		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
 		    ATTR_PARTIAL))
 			goto bad_flags;
@@ -1336,7 +1575,7 @@ bad_flags:
 			goto bad_flags;
 		goto optattr;
 	case ATTR_CLUSTER_LIST:
-		if ((attr_len & 0x3) != 0)
+		if (attr_len % 4 != 0)
 			goto bad_len;
 		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0))
 			goto bad_flags;
@@ -1370,8 +1609,15 @@ bad_flags:
 		plen += attr_len;
 		break;
 	case ATTR_AS4_AGGREGATOR:
-		if (attr_len != 8)
-			goto bad_len;
+		if (attr_len != 8) {
+			/* see ATTR_AGGREGATOR ... */
+			if ((flags & ATTR_PARTIAL) == 0)
+				goto bad_len;
+			log_peer_warnx(&peer->conf, "bad AS4_AGGREGATOR, "
+			    "partial attribute ignored");
+			plen += attr_len;
+			break;
+		}
 		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
 		    ATTR_PARTIAL))
 			goto bad_flags;
@@ -1381,17 +1627,28 @@ bad_flags:
 		if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE,
 		    ATTR_PARTIAL))
 			goto bad_flags;
-		if (aspath_verify(p, attr_len, 1) != 0) {
+		if ((err = aspath_verify(p, attr_len, 1)) != 0) {
 			/*
 			 * XXX RFC does not specify how to handle errors.
 			 * XXX Instead of dropping the session because of a
-			 * XXX bad path just mark the full update as not
-			 * XXX loop-free the update is no longer eligible and
-			 * XXX will not be considered for routing or
-			 * XXX redistribution. Something better is needed.
+			 * XXX bad path just mark the full update as having
+			 * XXX a parse error which makes the update no longer
+			 * XXX eligible and will not be considered for routing
+			 * XXX or redistribution.
+			 * XXX We follow draft-ietf-idr-optional-transitive
+			 * XXX by looking at the partial bit.
+			 * XXX Consider soft errors similar to a partial attr.
 			 */
-			a->flags |= F_ATTR_LOOP;
-			goto optattr;
+			if (flags & ATTR_PARTIAL || err == AS_ERR_SOFT) {
+				a->flags |= F_ATTR_PARSE_ERR;
+				log_peer_warnx(&peer->conf, "bad AS4_PATH, "
+				    "path invalidated and prefix withdrawn");
+				goto optattr;
+			} else {
+				rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH,
+				    NULL, 0);
+				return (-1);
+			}
 		}
 		a->flags |= F_ATTR_AS4BYTE_NEW;
 		goto optattr;
@@ -1440,8 +1697,8 @@ rde_attr_missing(struct rde_aspath *a, int ebgp, u_int16_t nlrilen)
 }
 
 int
-rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int16_t afi,
-    struct rde_aspath *asp)
+rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int8_t aid,
+    struct rde_aspath *asp, struct rde_peer *peer)
 {
 	struct bgpd_addr	nexthop;
 	u_int8_t		totlen, nhlen;
@@ -1457,8 +1714,9 @@ rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int16_t afi,
 		return (-1);
 
 	bzero(&nexthop, sizeof(nexthop));
-	switch (afi) {
-	case AFI_IPv6:
+	nexthop.aid = aid;
+	switch (aid) {
+	case AID_INET6:
 		/*
 		 * RFC2545 describes that there may be a link-local
 		 * address carried in nexthop. Yikes!
@@ -1471,72 +1729,143 @@ rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int16_t afi,
 			log_warnx("bad multiprotocol nexthop, bad size");
 			return (-1);
 		}
-		nexthop.af = AF_INET6;
 		memcpy(&nexthop.v6.s6_addr, data, 16);
-		asp->nexthop = nexthop_get(&nexthop);
+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
+		if (IN6_IS_ADDR_LINKLOCAL(&nexthop.v6) &&
+		    peer->conf.lliface[0]) {
+			int ifindex;
+
+			ifindex = if_nametoindex(peer->conf.lliface);
+			if (ifindex != 0)
+				SET_IN6_LINKLOCAL_IFINDEX(nexthop.v6, ifindex);
+			else
+				log_warnx("bad interface: %s", peer->conf.lliface);
+		}
+#endif
+		break;
+	case AID_VPN_IPv4:
 		/*
-		 * lock the nexthop because it is not yet linked else
-		 * withdraws may remove this nexthop which in turn would
-		 * cause a use after free error.
+		 * Neither RFC4364 nor RFC3107 specify the format of the
+		 * nexthop in an explicit way. The quality of RFC went down
+		 * the toilet the larger the the number got.
+		 * RFC4364 is very confusing about VPN-IPv4 address and the
+		 * VPN-IPv4 prefix that carries also a MPLS label.
+		 * So the nexthop is a 12-byte address with a 64bit RD and
+		 * an IPv4 address following. In the nexthop case the RD can
+		 * be ignored.
+		 * Since the nexthop has to be in the main IPv4 table just
+		 * create an AID_INET nexthop. So we don't need to handle
+		 * AID_VPN_IPv4 in nexthop and kroute.
 		 */
-		asp->nexthop->refcnt++;
-
-		/* ignore reserved (old SNPA) field as per RFC 4760 */
-		totlen += nhlen + 1;
-		data += nhlen + 1;
-
-		return (totlen);
-	default:
-		log_warnx("bad multiprotocol nexthop, bad AF");
+		if (nhlen != 12) {
+			log_warnx("bad multiprotocol nexthop, bad size");
+			return (-1);
+		}
+		data += sizeof(u_int64_t);
+		nexthop.aid = AID_INET;
+		memcpy(&nexthop.v4, data, sizeof(nexthop.v4));
 		break;
+	default:
+		log_warnx("bad multiprotocol nexthop, bad AID");
+		return (-1);
 	}
 
-	return (-1);
+	asp->nexthop = nexthop_get(&nexthop);
+	/*
+	 * lock the nexthop because it is not yet linked else
+	 * withdraws may remove this nexthop which in turn would
+	 * cause a use after free error.
+	 */
+	asp->nexthop->refcnt++;
+
+	/* ignore reserved (old SNPA) field as per RFC4760 */
+	totlen += nhlen + 1;
+	data += nhlen + 1;
+
+	return (totlen);
+}
+
+int
+rde_update_extract_prefix(u_char *p, u_int16_t len, void *va,
+    u_int8_t pfxlen, u_int8_t max)
+{
+	static u_char addrmask[] = {
+	    0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+	u_char		*a = va;
+	int		 i;
+	u_int16_t	 plen = 0;
+
+	for (i = 0; pfxlen && i < max; i++) {
+		if (len <= plen)
+			return (-1);
+		if (pfxlen < 8) {
+			a[i] = *p++ & addrmask[pfxlen];
+			plen++;
+			break;
+		} else {
+			a[i] = *p++;
+			plen++;
+			pfxlen -= 8;
+		}
+	}
+	return (plen);
 }
 
 int
 rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
     u_int8_t *prefixlen)
 {
-	int		i;
-	u_int8_t	pfxlen;
-	u_int16_t	plen;
-	union {
-		struct in_addr	a32;
-		u_int8_t	a8[4];
-	}		addr;
+	u_int8_t	 pfxlen;
+	int		 plen;
 
 	if (len < 1)
 		return (-1);
 
-	memcpy(&pfxlen, p, 1);
-	p += 1;
-	plen = 1;
+	pfxlen = *p++;
+	len--;
 
 	bzero(prefix, sizeof(struct bgpd_addr));
-	addr.a32.s_addr = 0;
-	for (i = 0; i <= 3; i++) {
-		if (pfxlen > i * 8) {
-			if (len - plen < 1)
-				return (-1);
-			memcpy(&addr.a8[i], p++, 1);
-			plen++;
-		}
-	}
-	prefix->af = AF_INET;
-	prefix->v4.s_addr = addr.a32.s_addr;
+	prefix->aid = AID_INET;
 	*prefixlen = pfxlen;
 
-	return (plen);
+	if ((plen = rde_update_extract_prefix(p, len, &prefix->v4, pfxlen,
+	    sizeof(prefix->v4))) == -1)
+		return (-1);
+
+	return (plen + 1);	/* pfxlen needs to be added */
 }
 
 int
 rde_update_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
     u_int8_t *prefixlen)
 {
-	int		i;
+	int		plen;
 	u_int8_t	pfxlen;
-	u_int16_t	plen;
+
+	if (len < 1)
+		return (-1);
+
+	pfxlen = *p++;
+	len--;
+
+	bzero(prefix, sizeof(struct bgpd_addr));
+	prefix->aid = AID_INET6;
+	*prefixlen = pfxlen;
+
+	if ((plen = rde_update_extract_prefix(p, len, &prefix->v6, pfxlen,
+	    sizeof(prefix->v6))) == -1)
+		return (-1);
+
+	return (plen + 1);	/* pfxlen needs to be added */
+}
+
+int
+rde_update_get_vpn4(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
+    u_int8_t *prefixlen)
+{
+	int		 rv, done = 0;
+	u_int8_t	 pfxlen;
+	u_int16_t	 plen;
 
 	if (len < 1)
 		return (-1);
@@ -1546,25 +1875,50 @@ rde_update_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
 	plen = 1;
 
 	bzero(prefix, sizeof(struct bgpd_addr));
-	for (i = 0; i <= 15; i++) {
-		if (pfxlen > i * 8) {
-			if (len - plen < 1)
-				return (-1);
-			memcpy(&prefix->v6.s6_addr[i], p++, 1);
-			plen++;
-		}
-	}
-	prefix->af = AF_INET6;
+
+	/* label stack */
+	do {
+		if (len - plen < 3 || pfxlen < 3 * 8)
+			return (-1);
+		if (prefix->vpn4.labellen + 3U >
+		    sizeof(prefix->vpn4.labelstack))
+			return (-1);
+		prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++;
+		prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++;
+		prefix->vpn4.labelstack[prefix->vpn4.labellen] = *p++;
+		if (prefix->vpn4.labelstack[prefix->vpn4.labellen] &
+		    BGP_MPLS_BOS)
+			done = 1;
+		prefix->vpn4.labellen++;
+		plen += 3;
+		pfxlen -= 3 * 8;
+	} while (!done);
+
+	/* RD */
+	if (len - plen < (int)sizeof(u_int64_t) ||
+	    pfxlen < sizeof(u_int64_t) * 8)
+		return (-1);
+	memcpy(&prefix->vpn4.rd, p, sizeof(u_int64_t));
+	pfxlen -= sizeof(u_int64_t) * 8;
+	p += sizeof(u_int64_t);
+	plen += sizeof(u_int64_t);
+
+	/* prefix */
+	prefix->aid = AID_VPN_IPv4;
 	*prefixlen = pfxlen;
 
-	return (plen);
+	if ((rv = rde_update_extract_prefix(p, len, &prefix->vpn4.addr,
+	    pfxlen, sizeof(prefix->vpn4.addr))) == -1)
+		return (-1);
+
+	return (plen + rv);
 }
 
 void
 rde_update_err(struct rde_peer *peer, u_int8_t error, u_int8_t suberr,
     void *data, u_int16_t size)
 {
-	struct buf	*wbuf;
+	struct ibuf	*wbuf;
 
 	if ((wbuf = imsg_create(ibuf_se, IMSG_UPDATE_ERR, peer->conf.id, 0,
 	    size + sizeof(error) + sizeof(suberr))) == NULL)
@@ -1616,16 +1970,30 @@ rde_as4byte_fixup(struct rde_peer *peer, struct rde_aspath *a)
 	struct attr	*nasp, *naggr, *oaggr;
 	u_int32_t	 as;
 
+	/*
+	 * if either ATTR_AS4_AGGREGATOR or ATTR_AS4_PATH is present
+	 * try to fixup the attributes.
+	 * Do not fixup if F_ATTR_PARSE_ERR is set.
+	 */
+	if (!(a->flags & F_ATTR_AS4BYTE_NEW) || a->flags & F_ATTR_PARSE_ERR)
+		return;
+
 	/* first get the attributes */
 	nasp = attr_optget(a, ATTR_AS4_PATH);
 	naggr = attr_optget(a, ATTR_AS4_AGGREGATOR);
 
 	if (rde_as4byte(peer)) {
 		/* NEW session using 4-byte ASNs */
-		if (nasp)
+		if (nasp) {
+			log_peer_warnx(&peer->conf, "uses 4-byte ASN "
+			    "but sent AS4_PATH attribute.");
 			attr_free(a, nasp);
-		if (naggr)
+		}
+		if (naggr) {
+			log_peer_warnx(&peer->conf, "uses 4-byte ASN "
+			    "but sent AS4_AGGREGATOR attribute.");
 			attr_free(a, naggr);
+		}
 		return;
 	}
 	/* OLD session using 2-byte ASNs */
@@ -1669,6 +2037,10 @@ rde_reflector(struct rde_peer *peer, struct rde_aspath *asp)
 	u_int16_t	 len;
 	u_int32_t	 id;
 
+	/* do not consider updates with parse errors */
+	if (asp->flags & F_ATTR_PARSE_ERR)
+		return;
+
 	/* check for originator id if eq router_id drop */
 	if ((a = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) {
 		if (memcmp(&conf->bgpid, a->data, sizeof(conf->bgpid)) == 0) {
@@ -1724,7 +2096,7 @@ void
 rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags)
 {
 	struct ctl_show_rib	 rib;
-	struct buf		*wbuf;
+	struct ibuf		*wbuf;
 	struct attr		*a;
 	void			*bp;
 	u_int8_t		 l;
@@ -1748,23 +2120,23 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags)
 		/* announced network may have a NULL nexthop */
 		bzero(&rib.true_nexthop, sizeof(rib.true_nexthop));
 		bzero(&rib.exit_nexthop, sizeof(rib.exit_nexthop));
-		rib.true_nexthop.af = p->prefix->af;
-		rib.exit_nexthop.af = p->prefix->af;
+		rib.true_nexthop.aid = p->prefix->aid;
+		rib.exit_nexthop.aid = p->prefix->aid;
 	}
 	pt_getaddr(p->prefix, &rib.prefix);
 	rib.prefixlen = p->prefix->prefixlen;
 	rib.origin = asp->origin;
 	rib.flags = 0;
 	if (p->rib->active == p)
-		rib.flags |= F_RIB_ACTIVE;
+		rib.flags |= F_PREF_ACTIVE;
 	if (asp->peer->conf.ebgp == 0)
-		rib.flags |= F_RIB_INTERNAL;
+		rib.flags |= F_PREF_INTERNAL;
 	if (asp->flags & F_PREFIX_ANNOUNCED)
-		rib.flags |= F_RIB_ANNOUNCE;
+		rib.flags |= F_PREF_ANNOUNCE;
 	if (asp->nexthop == NULL || asp->nexthop->state == NEXTHOP_REACH)
-		rib.flags |= F_RIB_ELIGIBLE;
+		rib.flags |= F_PREF_ELIGIBLE;
 	if (asp->flags & F_ATTR_LOOP)
-		rib.flags &= ~F_RIB_ELIGIBLE;
+		rib.flags &= ~F_PREF_ELIGIBLE;
 	rib.aspath_len = aspath_length(asp->aspath);
 
 	if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid,
@@ -1784,13 +2156,13 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags)
 			    IMSG_CTL_SHOW_RIB_ATTR, 0, pid,
 			    attr_optlen(a))) == NULL)
 				return;
-			if ((bp = buf_reserve(wbuf, attr_optlen(a))) == NULL) {
-				buf_free(wbuf);
+			if ((bp = ibuf_reserve(wbuf, attr_optlen(a))) == NULL) {
+				ibuf_free(wbuf);
 				return;
 			}
 			if (attr_write(bp, attr_optlen(a), a->flags,
 			    a->type, a->data, a->len) == -1) {
-				buf_free(wbuf);
+				ibuf_free(wbuf);
 				return;
 			}
 			imsg_close(ibuf_se_ctl, wbuf);
@@ -1828,15 +2200,15 @@ rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req)
 {
 	struct rde_peer		*peer;
 
-	if (req->flags & F_CTL_ADJ_IN || 
+	if (req->flags & F_CTL_ADJ_IN ||
 	    !(req->flags & (F_CTL_ADJ_IN|F_CTL_ADJ_OUT))) {
 		if (req->peerid && req->peerid != p->aspath->peer->conf.id)
 			return;
-		if (req->type == IMSG_CTL_SHOW_RIB_AS && 
+		if (req->type == IMSG_CTL_SHOW_RIB_AS &&
 		    !aspath_match(p->aspath->aspath, req->as.type, req->as.as))
 			return;
 		if (req->type == IMSG_CTL_SHOW_RIB_COMMUNITY &&
-		    !rde_filter_community(p->aspath, req->community.as,
+		    !community_match(p->aspath, req->community.as,
 		    req->community.type))
 			return;
 		rde_dump_rib_as(p, p->aspath, req->pid, req->flags);
@@ -1872,7 +2244,7 @@ rde_dump_prefix_upcall(struct rib_entry *re, void *ptr)
 
 	pt = re->prefix;
 	pt_getaddr(pt, &addr);
-	if (addr.af != ctx->req.prefix.af)
+	if (addr.aid != ctx->req.prefix.aid)
 		return;
 	if (ctx->req.prefixlen > pt->prefixlen)
 		return;
@@ -1889,6 +2261,7 @@ rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid,
 	struct rib_entry	*re;
 	u_int			 error;
 	u_int16_t		 id;
+	u_int8_t		 hostplen = 0;
 
 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
 		log_warn("rde_dump_ctx_new");
@@ -1902,6 +2275,7 @@ rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid,
 		error = CTL_RES_NOSUCHPEER;
 		imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error,
 		    sizeof(error));
+		free(ctx);
 		return;
 	}
 
@@ -1924,7 +2298,18 @@ rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid,
 			ctx->ribctx.ctx_upcall = rde_dump_prefix_upcall;
 			break;
 		}
-		if (req->prefixlen == 32)
+		switch (req->prefix.aid) {
+		case AID_INET:
+		case AID_VPN_IPv4:
+			hostplen = 32;
+			break;
+		case AID_INET6:
+			hostplen = 128;
+			break;
+		default:
+			fatalx("rde_dump_ctx_new: unknown af");
+		}
+		if (req->prefixlen == hostplen)
 			re = rib_lookup(&ribs[id], &req->prefix);
 		else
 			re = rib_get(&ribs[id], &req->prefix, req->prefixlen);
@@ -1937,7 +2322,7 @@ rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid,
 	}
 	ctx->ribctx.ctx_done = rde_dump_done;
 	ctx->ribctx.ctx_arg = ctx;
-	ctx->ribctx.ctx_af = ctx->req.af;
+	ctx->ribctx.ctx_aid = ctx->req.aid;
 	rib_dump_r(&ctx->ribctx);
 }
 
@@ -1974,10 +2359,10 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t pid, int fd)
 	ctx->ribctx.ctx_count = RDE_RUNNER_ROUNDS;
 	ctx->ribctx.ctx_rib = &ribs[id];
 	ctx->ribctx.ctx_upcall = mrt_dump_upcall;
-	ctx->ribctx.ctx_done = mrt_dump_done;
+	ctx->ribctx.ctx_done = mrt_done;
 	ctx->ribctx.ctx_arg = &ctx->mrt;
-	ctx->ribctx.ctx_af = AF_UNSPEC;
-	LIST_INSERT_HEAD(&rde_mrts, &ctx->mrt, entry);
+	ctx->ribctx.ctx_aid = AID_UNSPEC;
+	LIST_INSERT_HEAD(&rde_mrts, ctx, entry);
 	rde_mrt_cnt++;
 	rib_dump_r(&ctx->ribctx);
 }
@@ -1985,13 +2370,25 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t pid, int fd)
 /*
  * kroute specific functions
  */
+int
+rde_rdomain_import(struct rde_aspath *asp, struct rdomain *rd)
+{
+	struct filter_set	*s;
+
+	TAILQ_FOREACH(s, &rd->import, entry) {
+		if (community_ext_match(asp, &s->action.ext_community, 0))
+			return (1);
+	}
+	return (0);
+}
+
 void
-rde_send_kroute(struct prefix *new, struct prefix *old)
+rde_send_kroute(struct prefix *new, struct prefix *old, u_int16_t ribid)
 {
-	struct kroute_label	 kl;
-	struct kroute6_label	 kl6;
+	struct kroute_full	 kr;
 	struct bgpd_addr	 addr;
 	struct prefix		*p;
+	struct rdomain		*rd;
 	enum imsg_type		 type;
 
 	/*
@@ -2011,43 +2408,45 @@ rde_send_kroute(struct prefix *new, struct prefix *old)
 	}
 
 	pt_getaddr(p->prefix, &addr);
-	switch (addr.af) {
-	case AF_INET:
-		bzero(&kl, sizeof(kl));
-		kl.kr.prefix.s_addr = addr.v4.s_addr;
-		kl.kr.prefixlen = p->prefix->prefixlen;
-		if (p->aspath->flags & F_NEXTHOP_REJECT)
-			kl.kr.flags |= F_REJECT;
-		if (p->aspath->flags & F_NEXTHOP_BLACKHOLE)
-			kl.kr.flags |= F_BLACKHOLE;
-		if (type == IMSG_KROUTE_CHANGE)
-			kl.kr.nexthop.s_addr =
-			    p->aspath->nexthop->true_nexthop.v4.s_addr;
-		strlcpy(kl.label, rtlabel_id2name(p->aspath->rtlabelid),
-		    sizeof(kl.label));
-		if (imsg_compose(ibuf_main, type, 0, 0, -1, &kl,
-		    sizeof(kl)) == -1)
-			fatal("imsg_compose error");
+	bzero(&kr, sizeof(kr));
+	memcpy(&kr.prefix, &addr, sizeof(kr.prefix));
+	kr.prefixlen = p->prefix->prefixlen;
+	if (p->aspath->flags & F_NEXTHOP_REJECT)
+		kr.flags |= F_REJECT;
+	if (p->aspath->flags & F_NEXTHOP_BLACKHOLE)
+		kr.flags |= F_BLACKHOLE;
+	if (type == IMSG_KROUTE_CHANGE)
+		memcpy(&kr.nexthop, &p->aspath->nexthop->true_nexthop,
+		    sizeof(kr.nexthop));
+	strlcpy(kr.label, rtlabel_id2name(p->aspath->rtlabelid),
+	    sizeof(kr.label));
+
+	switch (addr.aid) {
+	case AID_VPN_IPv4:
+		if (ribid != 1)
+			/* not Loc-RIB, no update for VPNs */
+			break;
+
+		SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
+			if (addr.vpn4.rd != rd->rd)
+				continue;
+			if (!rde_rdomain_import(p->aspath, rd))
+				continue;
+			/* must send exit_nexthop so that correct MPLS tunnel
+			 * is chosen
+			 */
+			if (type == IMSG_KROUTE_CHANGE)
+				memcpy(&kr.nexthop,
+				    &p->aspath->nexthop->exit_nexthop,
+				    sizeof(kr.nexthop));
+			if (imsg_compose(ibuf_main, type, rd->rtableid, 0, -1,
+			    &kr, sizeof(kr)) == -1)
+				fatal("imsg_compose error");
+		}
 		break;
-	case AF_INET6:
-		bzero(&kl6, sizeof(kl6));
-		memcpy(&kl6.kr.prefix, &addr.v6, sizeof(struct in6_addr));
-		kl6.kr.prefixlen = p->prefix->prefixlen;
-		if (p->aspath->flags & F_NEXTHOP_REJECT)
-			kl6.kr.flags |= F_REJECT;
-		if (p->aspath->flags & F_NEXTHOP_BLACKHOLE)
-			kl6.kr.flags |= F_BLACKHOLE;
-		if (type == IMSG_KROUTE_CHANGE) {
-			type = IMSG_KROUTE6_CHANGE;
-			memcpy(&kl6.kr.nexthop,
-			    &p->aspath->nexthop->true_nexthop.v6,
-			    sizeof(struct in6_addr));
-		} else
-			type = IMSG_KROUTE6_DELETE;
-		strlcpy(kl6.label, rtlabel_id2name(p->aspath->rtlabelid),
-		    sizeof(kl6.label));
-		if (imsg_compose(ibuf_main, type, 0, 0, -1, &kl6,
-		    sizeof(kl6)) == -1)
+	default:
+		if (imsg_compose(ibuf_main, type, ribs[ribid].rtableid, 0, -1,
+		    &kr, sizeof(kr)) == -1)
 			fatal("imsg_compose error");
 		break;
 	}
@@ -2098,7 +2497,6 @@ rde_send_pftable_commit(void)
 void
 rde_send_nexthop(struct bgpd_addr *next, int valid)
 {
-	size_t			 size;
 	int			 type;
 
 	if (valid)
@@ -2106,8 +2504,6 @@ rde_send_nexthop(struct bgpd_addr *next, int valid)
 	else
 		type = IMSG_NEXTHOP_REMOVE;
 
-	size = sizeof(struct bgpd_addr);
-
 	if (imsg_compose(ibuf_main, type, 0, 0, -1, next,
 	    sizeof(struct bgpd_addr)) == -1)
 		fatal("imsg_compose error");
@@ -2201,6 +2597,10 @@ rde_softreconfig_in(struct rib_entry *re, void *ptr)
 			continue;
 
 		for (i = 1; i < rib_size; i++) {
+			/* only active ribs need a softreconfig rerun */
+			if (ribs[i].state != RECONF_KEEP)
+				continue;
+
 			/* check if prefix changed */
 			oa = rde_filter(i, &oasp, rules_l, peer, asp, &addr,
 			    pt->prefixlen, peer, DIR_IN);
@@ -2228,7 +2628,7 @@ rde_softreconfig_in(struct rib_entry *re, void *ptr)
 				if (path_compare(nasp, oasp) == 0)
 					goto done;
 				/* send update */
-				path_update(&ribs[1], peer, nasp, &addr,
+				path_update(&ribs[i], peer, nasp, &addr,
 				    pt->prefixlen);
 			}
 
@@ -2241,6 +2641,104 @@ done:
 	}
 }
 
+void
+rde_softreconfig_load(struct rib_entry *re, void *ptr)
+{
+	struct rib		*rib = ptr;
+	struct prefix		*p, *np;
+	struct pt_entry		*pt;
+	struct rde_peer		*peer;
+	struct rde_aspath	*asp, *nasp;
+	enum filter_actions	 action;
+	struct bgpd_addr	 addr;
+
+	pt = re->prefix;
+	pt_getaddr(pt, &addr);
+	for (p = LIST_FIRST(&re->prefix_h); p != NULL; p = np) {
+		np = LIST_NEXT(p, rib_l);
+
+		/* store aspath as prefix may change till we're done */
+		asp = p->aspath;
+		peer = asp->peer;
+
+		action = rde_filter(rib->id, &nasp, newrules, peer, asp, &addr,
+		    pt->prefixlen, peer, DIR_IN);
+		nasp = nasp != NULL ? nasp : asp;
+
+		if (action == ACTION_ALLOW) {
+			/* update Local-RIB */
+			path_update(rib, peer, nasp, &addr, pt->prefixlen);
+		}
+
+		if (nasp != asp)
+			path_put(nasp);
+	}
+}
+
+void
+rde_softreconfig_load_peer(struct rib_entry *re, void *ptr)
+{
+	struct rde_peer		*peer = ptr;
+	struct prefix		*p = re->active;
+	struct pt_entry		*pt;
+	struct rde_aspath	*nasp;
+	enum filter_actions	 na;
+	struct bgpd_addr	 addr;
+
+	pt = re->prefix;
+	pt_getaddr(pt, &addr);
+
+	/* check if prefix was announced */
+	if (up_test_update(peer, p) != 1)
+		return;
+
+	na = rde_filter(re->ribid, &nasp, newrules, peer, p->aspath,
+	    &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
+	nasp = nasp != NULL ? nasp : p->aspath;
+
+	if (na == ACTION_DENY)
+		/* nothing todo */
+		goto done;
+
+	/* send update */
+	up_generate(peer, nasp, &addr, pt->prefixlen);
+done:
+	if (nasp != p->aspath)
+		path_put(nasp);
+}
+
+void
+rde_softreconfig_unload_peer(struct rib_entry *re, void *ptr)
+{
+	struct rde_peer		*peer = ptr;
+	struct prefix		*p = re->active;
+	struct pt_entry		*pt;
+	struct rde_aspath	*oasp;
+	enum filter_actions	 oa;
+	struct bgpd_addr	 addr;
+
+	pt = re->prefix;
+	pt_getaddr(pt, &addr);
+
+	/* check if prefix was announced */
+	if (up_test_update(peer, p) != 1)
+		return;
+
+	oa = rde_filter(re->ribid, &oasp, rules_l, peer, p->aspath,
+	    &addr, pt->prefixlen, p->aspath->peer, DIR_OUT);
+	oasp = oasp != NULL ? oasp : p->aspath;
+
+	if (oa == ACTION_DENY)
+		/* nothing todo */
+		goto done;
+
+	/* send withdraw */
+	up_generate(peer, NULL, &addr, pt->prefixlen);
+done:
+	if (oasp != p->aspath)
+		path_put(oasp);
+}
+
 /*
  * update specific functions
  */
@@ -2252,7 +2750,7 @@ rde_up_dump_upcall(struct rib_entry *re, void *ptr)
 	struct rde_peer		*peer = ptr;
 
 	if (re->ribid != peer->ribid)
-		fatalx("King Bula: monsterous evil horror.");
+		fatalx("King Bula: monstrous evil horror.");
 	if (re->active == NULL)
 		return;
 	up_generate_updates(rules_l, peer, re->active, NULL);
@@ -2265,7 +2763,7 @@ rde_generate_updates(u_int16_t ribid, struct prefix *new, struct prefix *old)
 
 	/*
 	 * If old is != NULL we know it was active and should be removed.
-	 * If new is != NULL we know it is reachable and then we should 
+	 * If new is != NULL we know it is reachable and then we should
 	 * generate an update.
 	 */
 	if (old == NULL && new == NULL)
@@ -2286,7 +2784,7 @@ void
 rde_update_queue_runner(void)
 {
 	struct rde_peer		*peer;
-	int			 r, sent, max = RDE_RUNNER_ROUNDS;
+	int			 r, sent, max = RDE_RUNNER_ROUNDS, eor = 0;
 	u_int16_t		 len, wd_len, wpos;
 
 	len = sizeof(queue_buf) - MSGSIZE_HEADER;
@@ -2300,7 +2798,7 @@ rde_update_queue_runner(void)
 			/* first withdraws */
 			wpos = 2; /* reserve space for the length field */
 			r = up_dump_prefix(queue_buf + wpos, len - wpos - 2,
-			    &peer->withdraws, peer);
+			    &peer->withdraws[AID_INET], peer);
 			wd_len = r;
 			/* write withdraws length filed */
 			wd_len = htons(wd_len);
@@ -2310,31 +2808,49 @@ rde_update_queue_runner(void)
 			/* now bgp path attributes */
 			r = up_dump_attrnlri(queue_buf + wpos, len - wpos,
 			    peer);
-			wpos += r;
-
-			if (wpos == 4)
-				/*
-				 * No packet to send. The 4 bytes are the
-				 * needed withdraw and path attribute length.
-				 */
-				continue;
+			switch (r) {
+			case -1:
+				eor = 1;
+				if (wd_len == 0) {
+					/* no withdraws queued just send EoR */
+					peer_send_eor(peer, AID_INET);
+					continue;
+				}
+				break;
+			case 2:
+				if (wd_len == 0) {
+					/*
+					 * No packet to send. No withdraws and
+					 * no path attributes. Skip.
+					 */
+					continue;
+				}
+				/* FALLTHROUGH */
+			default:
+				wpos += r;
+				break;
+			}
 
 			/* finally send message to SE */
 			if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id,
 			    0, -1, queue_buf, wpos) == -1)
 				fatal("imsg_compose error");
 			sent++;
+			if (eor) {
+				eor = 0;
+				peer_send_eor(peer, AID_INET);
+			}
 		}
 		max -= sent;
 	} while (sent != 0 && max > 0);
 }
 
 void
-rde_update6_queue_runner(void)
+rde_update6_queue_runner(u_int8_t aid)
 {
 	struct rde_peer		*peer;
 	u_char			*b;
-	int			 sent, max = RDE_RUNNER_ROUNDS / 2;
+	int			 r, sent, max = RDE_RUNNER_ROUNDS / 2;
 	u_int16_t		 len;
 
 	/* first withdraws ... */
@@ -2346,7 +2862,7 @@ rde_update6_queue_runner(void)
 			if (peer->state != PEER_UP)
 				continue;
 			len = sizeof(queue_buf) - MSGSIZE_HEADER;
-			b = up_dump_mp_unreach(queue_buf, &len, peer);
+			b = up_dump_mp_unreach(queue_buf, &len, peer, aid);
 
 			if (b == NULL)
 				continue;
@@ -2369,10 +2885,18 @@ rde_update6_queue_runner(void)
 			if (peer->state != PEER_UP)
 				continue;
 			len = sizeof(queue_buf) - MSGSIZE_HEADER;
-			b = up_dump_mp_reach(queue_buf, &len, peer);
-
-			if (b == NULL)
+			r = up_dump_mp_reach(queue_buf, &len, peer, aid);
+			switch (r) {
+			case -2:
+				continue;
+			case -1:
+				peer_send_eor(peer, aid);
 				continue;
+			default:
+				b = queue_buf + r;
+				break;
+			}
+
 			/* finally send message to SE */
 			if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id,
 			    0, -1, b, len) == -1)
@@ -2411,7 +2935,7 @@ rde_decisionflags(void)
 int
 rde_as4byte(struct rde_peer *peer)
 {
-	return (peer->capa_announced.as4byte && peer->capa_received.as4byte);
+	return (peer->capa.as4byte);
 }
 
 /*
@@ -2429,7 +2953,6 @@ void
 peer_init(u_int32_t hashsize)
 {
 	struct peer_config pc;
-	struct in_addr   id;
 	u_int32_t	 hs, i;
 
 	for (hs = 1; hs < hashsize; hs <<= 1)
@@ -2445,17 +2968,13 @@ peer_init(u_int32_t hashsize)
 	peertable.peer_hashmask = hs - 1;
 
 	bzero(&pc, sizeof(pc));
-	pc.remote_as = conf->as;
-	id.s_addr = conf->bgpid;
-	snprintf(pc.descr, sizeof(pc.descr), "LOCAL: ID %s", inet_ntoa(id));
+	snprintf(pc.descr, sizeof(pc.descr), "LOCAL");
 
 	peerself = peer_add(0, &pc);
 	if (peerself == NULL)
 		fatalx("peer_init add self");
 
 	peerself->state = PEER_UP;
-	peerself->remote_bgpid = ntohl(conf->bgpid);
-	peerself->short_as = conf->short_as;
 }
 
 void
@@ -2534,14 +3053,10 @@ peer_localaddrs(struct rde_peer *peer, struct bgpd_addr *laddr)
 			if (ifa->ifa_addr->sa_family ==
 			    match->ifa_addr->sa_family)
 				ifa = match;
-			peer->local_v4_addr.af = AF_INET;
-			peer->local_v4_addr.v4.s_addr =
-			    ((struct sockaddr_in *)ifa->ifa_addr)->
-			    sin_addr.s_addr;
+			sa2addr(ifa->ifa_addr, &peer->local_v4_addr);
 			break;
 		}
 	}
-
 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
 		if (ifa->ifa_addr->sa_family == AF_INET6 &&
 		    strcmp(ifa->ifa_name, match->ifa_name) == 0) {
@@ -2559,13 +3074,7 @@ peer_localaddrs(struct rde_peer *peer, struct bgpd_addr *laddr)
 			    &((struct sockaddr_in6 *)ifa->
 			    ifa_addr)->sin6_addr))
 				continue;
-			peer->local_v6_addr.af = AF_INET6;
-			memcpy(&peer->local_v6_addr.v6,
-			    &((struct sockaddr_in6 *)ifa->ifa_addr)->
-			    sin6_addr, sizeof(struct in6_addr));
-			peer->local_v6_addr.scope_id =
-			    ((struct sockaddr_in6 *)ifa->ifa_addr)->
-			    sin6_scope_id;
+			sa2addr(ifa->ifa_addr, &peer->local_v6_addr);
 			break;
 		}
 	}
@@ -2577,6 +3086,7 @@ void
 peer_up(u_int32_t id, struct session_up *sup)
 {
 	struct rde_peer	*peer;
+	u_int8_t	 i;
 
 	peer = peer_get(id);
 	if (peer == NULL) {
@@ -2590,10 +3100,7 @@ peer_up(u_int32_t id, struct session_up *sup)
 	peer->short_as = sup->short_as;
 	memcpy(&peer->remote_addr, &sup->remote_addr,
 	    sizeof(peer->remote_addr));
-	memcpy(&peer->capa_announced, &sup->capa_announced,
-	    sizeof(peer->capa_announced));
-	memcpy(&peer->capa_received, &sup->capa_received,
-	    sizeof(peer->capa_received));
+	memcpy(&peer->capa, &sup->capa, sizeof(peer->capa));
 
 	peer_localaddrs(peer, &sup->local_addr);
 
@@ -2607,7 +3114,10 @@ peer_up(u_int32_t id, struct session_up *sup)
 		 */
 		return;
 
-	peer_dump(id, AFI_ALL, SAFI_ALL);
+	for (i = 0; i < AID_MAX; i++) {
+		if (peer->capa.mp[i] == 1)
+			peer_dump(id, i);
+	}
 }
 
 void
@@ -2642,42 +3152,32 @@ peer_down(u_int32_t id)
 }
 
 void
-peer_dump(u_int32_t id, u_int16_t afi, u_int8_t safi)
+peer_dump(u_int32_t id, u_int8_t aid)
 {
 	struct rde_peer		*peer;
 
 	peer = peer_get(id);
 	if (peer == NULL) {
-		log_warnx("peer_down: unknown peer id %d", id);
+		log_warnx("peer_dump: unknown peer id %d", id);
 		return;
 	}
 
-	if (afi == AFI_ALL || afi == AFI_IPv4)
-		if (safi == SAFI_ALL || safi == SAFI_UNICAST) {
-			if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE)
-				up_generate_default(rules_l, peer, AF_INET);
-			else
-				rib_dump(&ribs[peer->ribid], rde_up_dump_upcall,
-				    peer, AF_INET);
-		}
-	if (afi == AFI_ALL || afi == AFI_IPv6)
-		if (safi == SAFI_ALL || safi == SAFI_UNICAST) {
-			if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE)
-				up_generate_default(rules_l, peer, AF_INET6);
-			else
-				rib_dump(&ribs[peer->ribid], rde_up_dump_upcall,
-				    peer, AF_INET6);
-		}
-
-	if (peer->capa_received.restart && peer->capa_announced.restart)
-		peer_send_eor(peer, afi, safi);
+	if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE)
+		up_generate_default(rules_l, peer, aid);
+	else
+		rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, peer, aid);
+	if (peer->capa.restart)
+		up_generate_marker(peer, aid);
 }
 
-/* End-of-RIB marker, draft-ietf-idr-restart-13.txt */
+/* End-of-RIB marker, RFC 4724 */
 void
-peer_send_eor(struct rde_peer *peer, u_int16_t afi, u_int16_t safi)
+peer_send_eor(struct rde_peer *peer, u_int8_t aid)
 {
-	if (afi == AFI_IPv4 && safi == SAFI_UNICAST) {
+	u_int16_t	afi;
+	u_int8_t	safi;
+
+	if (aid == AID_INET) {
 		u_char null[4];
 
 		bzero(&null, 4);
@@ -2688,6 +3188,9 @@ peer_send_eor(struct rde_peer *peer, u_int16_t afi, u_int16_t safi)
 		u_int16_t	i;
 		u_char		buf[10];
 
+		if (aid2afi(aid, &afi, &safi) == -1)
+			fatalx("peer_send_eor: bad AID");
+
 		i = 0;	/* v4 withdrawn len */
 		bcopy(&i, &buf[0], sizeof(i));
 		i = htons(6);	/* path attr len */
@@ -2709,25 +3212,43 @@ peer_send_eor(struct rde_peer *peer, u_int16_t afi, u_int16_t safi)
  * network announcement stuff
  */
 void
-network_init(struct network_head *net_l)
-{
-	struct network	*n;
-
-	reloadtime = time(NULL);
-
-	while ((n = TAILQ_FIRST(net_l)) != NULL) {
-		TAILQ_REMOVE(net_l, n, entry);
-		network_add(&n->net, 1);
-		free(n);
-	}
-}
-
-void
 network_add(struct network_config *nc, int flagstatic)
 {
+	struct rdomain		*rd;
 	struct rde_aspath	*asp;
+	struct filter_set_head	*vpnset = NULL;
+	in_addr_t		 prefix4;
 	u_int16_t		 i;
 
+	if (nc->rtableid) {
+		SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
+			if (rd->rtableid != nc->rtableid)
+				continue;
+			switch (nc->prefix.aid) {
+			case AID_INET:
+				prefix4 = nc->prefix.v4.s_addr;
+				bzero(&nc->prefix, sizeof(nc->prefix));
+				nc->prefix.aid = AID_VPN_IPv4;
+				nc->prefix.vpn4.rd = rd->rd;
+				nc->prefix.vpn4.addr.s_addr = prefix4;
+				nc->prefix.vpn4.labellen = 3;
+				nc->prefix.vpn4.labelstack[0] =
+				    (rd->label >> 12) & 0xff;
+				nc->prefix.vpn4.labelstack[1] =
+				    (rd->label >> 4) & 0xff;
+				nc->prefix.vpn4.labelstack[2] =
+				    (rd->label << 4) & 0xf0;
+				nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
+				vpnset = &rd->export;
+				break;
+			default:
+				log_warnx("unable to VPNize prefix");
+				filterset_free(&nc->attrset);
+				return;
+			}
+		}
+	}
+
 	asp = path_get();
 	asp->aspath = aspath_get(NULL, 0);
 	asp->origin = ORIGIN_IGP;
@@ -2737,7 +3258,9 @@ network_add(struct network_config *nc, int flagstatic)
 	if (!flagstatic)
 		asp->flags |= F_ANN_DYNAMIC;
 
-	rde_apply_set(asp, &nc->attrset, nc->prefix.af, peerself, peerself);
+	rde_apply_set(asp, &nc->attrset, nc->prefix.aid, peerself, peerself);
+	if (vpnset)
+		rde_apply_set(asp, vpnset, nc->prefix.aid, peerself, peerself);
 	for (i = 1; i < rib_size; i++)
 		path_update(&ribs[i], peerself, asp, &nc->prefix,
 		    nc->prefixlen);
@@ -2749,12 +3272,41 @@ network_add(struct network_config *nc, int flagstatic)
 void
 network_delete(struct network_config *nc, int flagstatic)
 {
-	u_int32_t	flags = F_PREFIX_ANNOUNCED;
-	u_int32_t	i;
+	struct rdomain	*rd;
+	in_addr_t	 prefix4;
+	u_int32_t	 flags = F_PREFIX_ANNOUNCED;
+	u_int32_t	 i;
 
 	if (!flagstatic)
 		flags |= F_ANN_DYNAMIC;
 
+	if (nc->rtableid) {
+		SIMPLEQ_FOREACH(rd, rdomains_l, entry) {
+			if (rd->rtableid != nc->rtableid)
+				continue;
+			switch (nc->prefix.aid) {
+			case AID_INET:
+				prefix4 = nc->prefix.v4.s_addr;
+				bzero(&nc->prefix, sizeof(nc->prefix));
+				nc->prefix.aid = AID_VPN_IPv4;
+				nc->prefix.vpn4.rd = rd->rd;
+				nc->prefix.vpn4.addr.s_addr = prefix4;
+				nc->prefix.vpn4.labellen = 3;
+				nc->prefix.vpn4.labelstack[0] =
+				    (rd->label >> 12) & 0xff;
+				nc->prefix.vpn4.labelstack[1] =
+				    (rd->label >> 4) & 0xff;
+				nc->prefix.vpn4.labelstack[2] =
+				    (rd->label << 4) & 0xf0;
+				nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS;
+				break;
+			default:
+				log_warnx("unable to VPNize prefix");
+				return;
+			}
+		}
+	}
+
 	for (i = rib_size - 1; i > 0; i--)
 		prefix_remove(&ribs[i], peerself, &nc->prefix, nc->prefixlen,
 		    flags);
@@ -2764,38 +3316,31 @@ void
 network_dump_upcall(struct rib_entry *re, void *ptr)
 {
 	struct prefix		*p;
-	struct kroute		 k;
-	struct kroute6		 k6;
+	struct kroute_full	 k;
 	struct bgpd_addr	 addr;
 	struct rde_dump_ctx	*ctx = ptr;
 
 	LIST_FOREACH(p, &re->prefix_h, rib_l) {
 		if (!(p->aspath->flags & F_PREFIX_ANNOUNCED))
 			continue;
-		if (p->prefix->af == AF_INET) {
-			bzero(&k, sizeof(k));
-			pt_getaddr(p->prefix, &addr);
-			k.prefix.s_addr = addr.v4.s_addr;
-			k.prefixlen = p->prefix->prefixlen;
-			if (p->aspath->peer == peerself)
-				k.flags = F_KERNEL;
-			if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK, 0,
-			    ctx->req.pid, -1, &k, sizeof(k)) == -1)
-				log_warnx("network_dump_upcall: "
-				    "imsg_compose error");
-		}
-		if (p->prefix->af == AF_INET6) {
-			bzero(&k6, sizeof(k6));
-			pt_getaddr(p->prefix, &addr);
-			memcpy(&k6.prefix, &addr.v6, sizeof(k6.prefix));
-			k6.prefixlen = p->prefix->prefixlen;
-			if (p->aspath->peer == peerself)
-				k6.flags = F_KERNEL;
-			if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK6, 0,
-			    ctx->req.pid, -1, &k6, sizeof(k6)) == -1)
-				log_warnx("network_dump_upcall: "
-				    "imsg_compose error");
-		}
+		pt_getaddr(p->prefix, &addr);
+
+		bzero(&k, sizeof(k));
+		memcpy(&k.prefix, &addr, sizeof(k.prefix));
+		if (p->aspath->nexthop == NULL ||
+		    p->aspath->nexthop->state != NEXTHOP_REACH)
+			k.nexthop.aid = k.prefix.aid;
+		else
+			memcpy(&k.nexthop, &p->aspath->nexthop->true_nexthop,
+			    sizeof(k.nexthop));
+		k.prefixlen = p->prefix->prefixlen;
+		k.flags = F_KERNEL;
+		if ((p->aspath->flags & F_ANN_DYNAMIC) == 0)
+			k.flags = F_STATIC;
+		if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK, 0,
+		    ctx->req.pid, -1, &k, sizeof(k)) == -1)
+			log_warnx("network_dump_upcall: "
+			    "imsg_compose error");
 	}
 }
 
@@ -2841,10 +3386,10 @@ sa_cmp(struct bgpd_addr *a, struct sockaddr *b)
 	struct sockaddr_in	*in_b;
 	struct sockaddr_in6	*in6_b;
 
-	if (a->af != b->sa_family)
+	if (aid2af(a->aid) != b->sa_family)
 		return (1);
 
-	switch (a->af) {
+	switch (b->sa_family) {
 	case AF_INET:
 		in_b = (struct sockaddr_in *)b;
 		if (a->v4.s_addr != in_b->sin_addr.s_addr)
diff --git a/bgpd/rde.h b/bgpd/rde.h
index 2664fd4..d2a594b 100644
--- a/bgpd/rde.h
+++ b/bgpd/rde.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde.h,v 1.120 2009/06/06 01:10:29 claudio Exp $ */
+/*	$OpenBSD: rde.h,v 1.138 2010/11/18 12:18:31 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
@@ -56,12 +56,9 @@ struct rde_peer {
 	struct bgpd_addr		 local_v6_addr;
 	struct uptree_prefix		 up_prefix;
 	struct uptree_attr		 up_attrs;
-	struct uplist_attr		 updates;
-	struct uplist_prefix		 withdraws;
-	struct uplist_attr		 updates6;
-	struct uplist_prefix		 withdraws6;
-	struct capabilities		 capa_announced;
-	struct capabilities		 capa_received;
+	struct uplist_attr		 updates[AID_MAX];
+	struct uplist_prefix		 withdraws[AID_MAX];
+	struct capabilities		 capa;
 	u_int64_t			 prefix_rcvd_update;
 	u_int64_t			 prefix_rcvd_withdraw;
 	u_int64_t			 prefix_sent_update;
@@ -77,10 +74,13 @@ struct rde_peer {
 	u_int16_t			 short_as;
 	u_int8_t			 reconf_in;	/* in filter changed */
 	u_int8_t			 reconf_out;	/* out filter changed */
+	u_int8_t			 reconf_rib;	/* rib changed */
 };
 
 #define AS_SET			1
 #define AS_SEQUENCE		2
+#define AS_CONFED_SEQUENCE	3
+#define AS_CONFED_SET		4
 #define ASPATH_HEADER_SIZE	(sizeof(struct aspath) - sizeof(u_char))
 
 LIST_HEAD(aspath_list, aspath);
@@ -163,6 +163,7 @@ LIST_HEAD(prefix_head, prefix);
 #define	F_NEXTHOP_REJECT	0x02000
 #define	F_NEXTHOP_BLACKHOLE	0x04000
 #define	F_NEXTHOP_NOMODIFY	0x08000
+#define	F_ATTR_PARSE_ERR	0x10000
 #define	F_ATTR_LINKED		0x20000
 
 
@@ -220,14 +221,14 @@ struct nexthop {
 /* generic entry without address specific part */
 struct pt_entry {
 	RB_ENTRY(pt_entry)		 pt_e;
-	sa_family_t			 af;
+	u_int8_t			 aid;
 	u_int8_t			 prefixlen;
 	u_int16_t			 refcnt;
 };
 
 struct pt_entry4 {
 	RB_ENTRY(pt_entry)		 pt_e;
-	sa_family_t			 af;
+	u_int8_t			 aid;
 	u_int8_t			 prefixlen;
 	u_int16_t			 refcnt;
 	struct in_addr			 prefix4;
@@ -235,12 +236,25 @@ struct pt_entry4 {
 
 struct pt_entry6 {
 	RB_ENTRY(pt_entry)		 pt_e;
-	sa_family_t			 af;
+	u_int8_t			 aid;
 	u_int8_t			 prefixlen;
 	u_int16_t			 refcnt;
 	struct in6_addr			 prefix6;
 };
 
+struct pt_entry_vpn4 {
+	RB_ENTRY(pt_entry)		 pt_e;
+	u_int8_t			 aid;
+	u_int8_t			 prefixlen;
+	u_int16_t			 refcnt;
+	struct in_addr			 prefix4;
+	u_int64_t			 rd;
+	u_int8_t			 labelstack[21];
+	u_int8_t			 labellen;
+	u_int8_t			 pad1;
+	u_int8_t			 pad2;
+};
+
 struct rib_context {
 	LIST_ENTRY(rib_context)		 entry;
 	struct rib_entry		*ctx_re;
@@ -250,7 +264,7 @@ struct rib_context {
 	void		(*ctx_wait)(void *);
 	void				*ctx_arg;
 	unsigned int			 ctx_count;
-	sa_family_t			 ctx_af;
+	u_int8_t			 ctx_aid;
 };
 
 struct rib_entry {
@@ -262,23 +276,15 @@ struct rib_entry {
 	u_int16_t		 flags;
 };
 
-enum rib_state {
-	RIB_NONE,
-	RIB_ACTIVE,
-	RIB_DELETE
-};
-
 struct rib {
 	char			name[PEER_DESCR_LEN];
 	struct rib_tree		rib;
-	enum rib_state		state;
+	u_int			rtableid;
 	u_int16_t		flags;
 	u_int16_t		id;
+	enum reconf_action 	state;
 };
 
-#define F_RIB_ENTRYLOCK		0x0001
-#define F_RIB_NOEVALUATE	0x0002
-#define F_RIB_NOFIB		0x0004
 #define RIB_FAILED		0xffff
 
 struct prefix {
@@ -293,7 +299,7 @@ extern struct rde_memstats rdemem;
 
 /* prototypes */
 /* rde.c */
-void		 rde_send_kroute(struct prefix *, struct prefix *);
+void		 rde_send_kroute(struct prefix *, struct prefix *, u_int16_t);
 void		 rde_send_nexthop(struct bgpd_addr *, int);
 void		 rde_send_pftable(u_int16_t, struct bgpd_addr *,
 		     u_int8_t, int);
@@ -309,7 +315,7 @@ int		 rde_as4byte(struct rde_peer *);
 /* rde_attr.c */
 int		 attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *,
 		     u_int16_t);
-int		 attr_writebuf(struct buf *, u_int8_t, u_int8_t, void *,
+int		 attr_writebuf(struct ibuf *, u_int8_t, u_int8_t, void *,
 		     u_int16_t);
 void		 attr_init(u_int32_t);
 void		 attr_shutdown(void);
@@ -327,6 +333,7 @@ int		 aspath_verify(void *, u_int16_t, int);
 #define		 AS_ERR_LEN	-1
 #define		 AS_ERR_TYPE	-2
 #define		 AS_ERR_BAD	-3
+#define		 AS_ERR_SOFT	-4
 void		 aspath_init(u_int32_t);
 void		 aspath_shutdown(void);
 struct aspath	*aspath_get(void *, u_int16_t);
@@ -342,21 +349,30 @@ int		 aspath_loopfree(struct aspath *, u_int32_t);
 int		 aspath_compare(struct aspath *, struct aspath *);
 u_char		*aspath_prepend(struct aspath *, u_int32_t, int, u_int16_t *);
 int		 aspath_match(struct aspath *, enum as_spec, u_int32_t);
-int		 community_match(void *, u_int16_t, int, int);
+int		 aspath_lenmatch(struct aspath *, enum aslen_spec, u_int);
+int		 community_match(struct rde_aspath *, int, int);
 int		 community_set(struct rde_aspath *, int, int);
 void		 community_delete(struct rde_aspath *, int, int);
+int		 community_ext_match(struct rde_aspath *,
+		    struct filter_extcommunity *, u_int16_t);
+int		 community_ext_set(struct rde_aspath *,
+		    struct filter_extcommunity *, u_int16_t);
+void		 community_ext_delete(struct rde_aspath *,
+		    struct filter_extcommunity *, u_int16_t);
+int		 community_ext_conv(struct filter_extcommunity *, u_int16_t,
+		    u_int64_t *);
 
 /* rde_rib.c */
 extern u_int16_t	 rib_size;
 extern struct rib	*ribs;
 
-u_int16_t	 rib_new(int, char *, u_int16_t);
+u_int16_t	 rib_new(char *, u_int, u_int16_t);
 u_int16_t	 rib_find(char *);
 void		 rib_free(struct rib *);
 struct rib_entry *rib_get(struct rib *, struct bgpd_addr *, int);
 struct rib_entry *rib_lookup(struct rib *, struct bgpd_addr *);
 void		 rib_dump(struct rib *, void (*)(struct rib_entry *, void *),
-		     void *, sa_family_t);
+		     void *, u_int8_t);
 void		 rib_dump_r(struct rib_context *);
 void		 rib_dump_runner(void);
 int		 rib_dump_pending(void);
@@ -395,7 +411,7 @@ void		 prefix_network_clean(struct rde_peer *, time_t, u_int32_t);
 void		 nexthop_init(u_int32_t);
 void		 nexthop_shutdown(void);
 void		 nexthop_modify(struct rde_aspath *, struct bgpd_addr *,
-		     enum action_types, sa_family_t);
+		     enum action_types, u_int8_t);
 void		 nexthop_link(struct rde_aspath *);
 void		 nexthop_unlink(struct rde_aspath *);
 int		 nexthop_delete(struct nexthop *);
@@ -415,12 +431,15 @@ int		 up_generate(struct rde_peer *, struct rde_aspath *,
 void		 up_generate_updates(struct filter_head *, struct rde_peer *,
 		     struct prefix *, struct prefix *);
 void		 up_generate_default(struct filter_head *, struct rde_peer *,
-		     sa_family_t);
+		     u_int8_t);
+int		 up_generate_marker(struct rde_peer *, u_int8_t);
 int		 up_dump_prefix(u_char *, int, struct uplist_prefix *,
 		     struct rde_peer *);
 int		 up_dump_attrnlri(u_char *, int, struct rde_peer *);
-u_char		*up_dump_mp_unreach(u_char *, u_int16_t *, struct rde_peer *);
-u_char		*up_dump_mp_reach(u_char *, u_int16_t *, struct rde_peer *);
+u_char		*up_dump_mp_unreach(u_char *, u_int16_t *, struct rde_peer *,
+		     u_int8_t);
+int		 up_dump_mp_reach(u_char *, u_int16_t *, struct rde_peer *,
+		     u_int8_t);
 
 /* rde_prefix.c */
 #define pt_empty(pt)	((pt)->refcnt == 0)
@@ -452,8 +471,7 @@ enum filter_actions rde_filter(u_int16_t, struct rde_aspath **,
 		     struct rde_aspath *, struct bgpd_addr *, u_int8_t,
 		     struct rde_peer *, enum directions);
 void		 rde_apply_set(struct rde_aspath *, struct filter_set_head *,
-		     sa_family_t, struct rde_peer *, struct rde_peer *);
-int		 rde_filter_community(struct rde_aspath *, int, int);
+		     u_int8_t, struct rde_peer *, struct rde_peer *);
 int		 rde_filter_equal(struct filter_head *, struct filter_head *,
 		     struct rde_peer *, enum directions);
 
diff --git a/bgpd/rde_attr.c b/bgpd/rde_attr.c
index 7b4591f..eab471f 100644
--- a/bgpd/rde_attr.c
+++ b/bgpd/rde_attr.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_attr.c,v 1.79 2009/03/19 06:52:59 claudio Exp $ */
+/*	$OpenBSD: rde_attr.c,v 1.83 2010/03/29 09:24:07 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -17,14 +17,22 @@
  */
 
 #include <sys/types.h>
+#if defined(__FreeBSD__)	/* sys/hash.h */
+#include "hash.h"
+#else
 #include <sys/hash.h>
+#endif /* defined(__FreeBSD__) */
 #include <sys/queue.h>
 
 #include <netinet/in.h>
 
+#include <limits.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#if defined(__FreeBSD__)	/* limits.h */
+#include <limits.h>
+#endif /* defined(__FreeBSD__) */
 
 #include "bgpd.h"
 #include "rde.h"
@@ -63,7 +71,7 @@ attr_write(void *p, u_int16_t p_len, u_int8_t flags, u_int8_t type,
 }
 
 int
-attr_writebuf(struct buf *buf, u_int8_t flags, u_int8_t type, void *data,
+attr_writebuf(struct ibuf *buf, u_int8_t flags, u_int8_t type, void *data,
     u_int16_t data_len)
 {
 	u_char	hdr[4];
@@ -80,9 +88,9 @@ attr_writebuf(struct buf *buf, u_int8_t flags, u_int8_t type, void *data,
 	hdr[0] = flags;
 	hdr[1] = type;
 
-	if (buf_add(buf, hdr, flags & ATTR_EXTLEN ? 4 : 3) == -1)
+	if (ibuf_add(buf, hdr, flags & ATTR_EXTLEN ? 4 : 3) == -1)
 		return (-1);
-	if (buf_add(buf, data, data_len) == -1)
+	if (ibuf_add(buf, data, data_len) == -1)
 		return (-1);
 	return (0);
 }
@@ -146,8 +154,11 @@ attr_optadd(struct rde_aspath *asp, u_int8_t flags, u_int8_t type,
 	for (l = 0; l < asp->others_len; l++) {
 		if (asp->others[l] == NULL)
 			break;
-		if (type == asp->others[l]->type)
+		if (type == asp->others[l]->type) {
+			if (a->refcnt == 0)
+				attr_put(a);
 			return (-1);
+		}
 	}
 
 	/* add attribute to the table but first bump refcnt */
@@ -405,6 +416,7 @@ aspath_verify(void *data, u_int16_t len, int as4byte)
 	u_int8_t	*seg = data;
 	u_int16_t	 seg_size, as_size = 2;
 	u_int8_t	 seg_len, seg_type;
+	int		 err = 0;
 
 	if (len & 1)
 		/* odd length aspath are invalid */
@@ -419,7 +431,15 @@ aspath_verify(void *data, u_int16_t len, int as4byte)
 		seg_type = seg[0];
 		seg_len = seg[1];
 
-		if (seg_type != AS_SET && seg_type != AS_SEQUENCE)
+		/*
+		 * BGP confederations should not show up but consider them
+		 * as a soft error which invalidates the path but keeps the
+		 * bgp session running.
+		 */
+		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
+			err = AS_ERR_SOFT;
+		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
+		    seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET)
 			return (AS_ERR_TYPE);
 
 		seg_size = 2 + as_size * seg_len;
@@ -431,7 +451,7 @@ aspath_verify(void *data, u_int16_t len, int as4byte)
 			/* empty aspath segments are not allowed */
 			return (AS_ERR_BAD);
 	}
-	return (0);	/* aspath is valid but probably not loop free */
+	return (err);	/* aspath is valid but probably not loop free */
 }
 
 void
@@ -972,14 +992,62 @@ aspath_match(struct aspath *a, enum as_spec type, u_int32_t as)
 }
 
 int
-community_match(void *data, u_int16_t len, int as, int type)
+aspath_lenmatch(struct aspath *a, enum aslen_spec type, u_int aslen)
 {
-	u_int8_t	*p = data;
-	u_int16_t	 eas, etype;
+	u_int8_t	*seg;
+	u_int32_t	 as, lastas = 0;
+	u_int		 count = 0;
+	u_int16_t	 len, seg_size;
+	u_int8_t	 i, seg_type, seg_len;
+
+	if (type == ASLEN_MAX) {
+		if (aslen < aspath_count(a->data, a->len))
+			return (1);
+		else
+			return (0);
+	}
 
-	len >>= 2; /* divide by four */
+	/* type == ASLEN_SEQ */
+	seg = a->data;
+	for (len = a->len; len > 0; len -= seg_size, seg += seg_size) {
+		seg_type = seg[0];
+		seg_len = seg[1];
+		seg_size = 2 + sizeof(u_int32_t) * seg_len;
 
-	for (; len > 0; len--) {
+		for (i = 0; i < seg_len; i++) {
+			/* what should we do with AS_SET? */
+			as = aspath_extract(seg, i);
+			if (as == lastas) {
+				if (aslen < ++count)
+					return (1);
+			} else
+				count = 1;
+			lastas = as;
+		}
+	}
+	return (0);
+}
+
+/*
+ * Functions handling communities and extended communities.
+ */
+
+int community_ext_matchone(struct filter_extcommunity *, u_int16_t, u_int64_t);
+
+int
+community_match(struct rde_aspath *asp, int as, int type)
+{
+	struct attr	*a;
+	u_int8_t	*p;
+	u_int16_t	 eas, etype, len;
+
+	a = attr_optget(asp, ATTR_COMMUNITIES);
+	if (a == NULL)
+		/* no communities, no match */
+		return (0);
+
+	p = a->data;
+	for (len = a->len / 4; len > 0; len--) {
 		eas = *p++;
 		eas <<= 8;
 		eas |= *p++;
@@ -1000,7 +1068,6 @@ community_set(struct rde_aspath *asp, int as, int type)
 	u_int8_t	*p = NULL;
 	unsigned int	 i, ncommunities = 0;
 	u_int8_t	 f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
-	u_int8_t	 t = ATTR_COMMUNITIES;
 
 	attr = attr_optget(asp, ATTR_COMMUNITIES);
 	if (attr != NULL) {
@@ -1017,7 +1084,7 @@ community_set(struct rde_aspath *asp, int as, int type)
 		p += 4;
 	}
 
-	if (ncommunities++ >= 0x3fff)
+	if (ncommunities++ >= USHRT_MAX / 4)
 		/* overflow */
 		return (0);
 
@@ -1032,11 +1099,10 @@ community_set(struct rde_aspath *asp, int as, int type)
 	if (attr != NULL) {
 		memcpy(p + 4, attr->data, attr->len);
 		f = attr->flags;
-		t = attr->type;
 		attr_free(asp, attr);
 	}
 
-	attr_optadd(asp, f, t, p, ncommunities << 2);
+	attr_optadd(asp, f, ATTR_COMMUNITIES, p, ncommunities << 2);
 
 	free(p);
 	return (1);
@@ -1049,7 +1115,7 @@ community_delete(struct rde_aspath *asp, int as, int type)
 	u_int8_t	*p, *n;
 	u_int16_t	 l, len = 0;
 	u_int16_t	 eas, etype;
-	u_int8_t	 f, t;
+	u_int8_t	 f;
 
 	attr = attr_optget(asp, ATTR_COMMUNITIES);
 	if (attr == NULL)
@@ -1100,10 +1166,250 @@ community_delete(struct rde_aspath *asp, int as, int type)
 	}
 
 	f = attr->flags;
-	t = attr->type;
 
 	attr_free(asp, attr);
-	attr_optadd(asp, f, t, n, len);
+	attr_optadd(asp, f, ATTR_COMMUNITIES, n, len);
 	free(n);
 }
 
+int
+community_ext_match(struct rde_aspath *asp, struct filter_extcommunity *c,
+    u_int16_t neighas)
+{
+	struct attr	*attr;
+	u_int8_t	*p;
+	u_int64_t	 ec;
+	u_int16_t	 len;
+
+	attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
+	if (attr == NULL)
+		/* no communities, no match */
+		return (0);
+
+	p = attr->data;
+	for (len = attr->len / sizeof(ec); len > 0; len--) {
+		memcpy(&ec, p, sizeof(ec));
+		if (community_ext_matchone(c, neighas, ec))
+			return (1);
+		p += sizeof(ec);
+	}
+
+	return (0);
+}
+
+int
+community_ext_set(struct rde_aspath *asp, struct filter_extcommunity *c,
+    u_int16_t neighas)
+{
+	struct attr	*attr;
+	u_int8_t	*p = NULL;
+	u_int64_t	 community;
+	unsigned int	 i, ncommunities = 0;
+	u_int8_t	 f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
+
+	if (community_ext_conv(c, neighas, &community))
+		return (0);
+
+	attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
+	if (attr != NULL) {
+		p = attr->data;
+		ncommunities = attr->len / sizeof(community);
+	}
+
+	/* first check if the community is not already set */
+	for (i = 0; i < ncommunities; i++) {
+		if (memcmp(&community, p, sizeof(community)) == 0)
+			/* already present, nothing todo */
+			return (1);
+		p += sizeof(community);
+	}
+
+	if (ncommunities++ >= USHRT_MAX / sizeof(community))
+		/* overflow */
+		return (0);
+
+	if ((p = malloc(ncommunities * sizeof(community))) == NULL)
+		fatal("community_ext_set");
+
+	memcpy(p, &community, sizeof(community));
+	if (attr != NULL) {
+		memcpy(p + sizeof(community), attr->data, attr->len);
+		f = attr->flags;
+		attr_free(asp, attr);
+	}
+
+	attr_optadd(asp, f, ATTR_EXT_COMMUNITIES, p,
+	    ncommunities * sizeof(community));
+
+	free(p);
+	return (1);
+}
+
+void
+community_ext_delete(struct rde_aspath *asp, struct filter_extcommunity *c,
+    u_int16_t neighas)
+{
+	struct attr	*attr;
+	u_int8_t	*p, *n;
+	u_int64_t	 community;
+	u_int16_t	 l, len = 0;
+	u_int8_t	 f;
+
+	if (community_ext_conv(c, neighas, &community))
+		return;
+
+	attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
+	if (attr == NULL)
+		/* no attr nothing to do */
+		return;
+
+	p = attr->data;
+	for (l = 0; l < attr->len; l += sizeof(community)) {
+		if (memcmp(&community, p + l, sizeof(community)) == 0)
+			/* match */
+			continue;
+		len += sizeof(community);
+	}
+
+	if (len == 0) {
+		attr_free(asp, attr);
+		return;
+	}
+
+	if ((n = malloc(len)) == NULL)
+		fatal("community_delete");
+
+	p = attr->data;
+	for (l = 0; l < len && p < attr->data + attr->len;
+	    p += sizeof(community)) {
+		if (memcmp(&community, p, sizeof(community)) == 0)
+			/* match */
+			continue;
+		memcpy(n + l, p, sizeof(community));
+		l += sizeof(community);
+	}
+
+	f = attr->flags;
+
+	attr_free(asp, attr);
+	attr_optadd(asp, f, ATTR_EXT_COMMUNITIES, n, len);
+	free(n);
+}
+
+int
+community_ext_conv(struct filter_extcommunity *c, u_int16_t neighas,
+    u_int64_t *community)
+{
+	u_int64_t	com;
+	u_int32_t	ip;
+
+	com = (u_int64_t)c->type << 56;
+	switch (c->type & EXT_COMMUNITY_VALUE) {
+	case EXT_COMMUNITY_TWO_AS:
+		com |= (u_int64_t)c->subtype << 48;
+		com |= (u_int64_t)c->data.ext_as.as << 32;
+		com |= c->data.ext_as.val;
+		break;
+	case EXT_COMMUNITY_IPV4:
+		com |= (u_int64_t)c->subtype << 48;
+		ip = ntohl(c->data.ext_ip.addr.s_addr);
+		com |= (u_int64_t)ip << 16;
+		com |= c->data.ext_ip.val;
+		break;
+	case EXT_COMMUNITY_FOUR_AS:
+		com |= (u_int64_t)c->subtype << 48;
+		com |= (u_int64_t)c->data.ext_as4.as4 << 16;
+		com |= c->data.ext_as4.val;
+		break;
+	case EXT_COMMUNITY_OPAQUE:
+		com |= (u_int64_t)c->subtype << 48;
+		com |= c->data.ext_opaq & EXT_COMMUNITY_OPAQUE_MAX;
+		break;
+	default:
+		com |= c->data.ext_opaq & 0xffffffffffffffULL;
+		break;
+	}
+
+	*community = htobe64(com);
+
+	return (0);
+}
+
+int
+community_ext_matchone(struct filter_extcommunity *c, u_int16_t neighas,
+    u_int64_t community)
+{
+	u_int64_t	com, mask;
+	u_int32_t	ip;
+
+	community = betoh64(community);
+
+	com = (u_int64_t)c->type << 56;
+	mask = 0xffULL << 56;
+	if ((com & mask) != (community & mask))
+		return (0);
+
+	switch (c->type & EXT_COMMUNITY_VALUE) {
+	case EXT_COMMUNITY_TWO_AS:
+	case EXT_COMMUNITY_IPV4:
+	case EXT_COMMUNITY_FOUR_AS:
+	case EXT_COMMUNITY_OPAQUE:
+		com = (u_int64_t)c->subtype << 48;
+		mask = 0xffULL << 48;
+		if ((com & mask) != (community & mask))
+			return (0);
+		break;
+	default:
+		com = c->data.ext_opaq & 0xffffffffffffffULL;
+		mask = 0xffffffffffffffULL;
+		if ((com & mask) == (community & mask))
+			return (1);
+		return (0);
+	}
+
+
+	switch (c->type & EXT_COMMUNITY_VALUE) {
+	case EXT_COMMUNITY_TWO_AS:
+		com = (u_int64_t)c->data.ext_as.as << 32;
+		mask = 0xffffULL << 32;
+		if ((com & mask) != (community & mask))
+			return (0);
+
+		com = c->data.ext_as.val;
+		mask = 0xffffffffULL;
+		if ((com & mask) == (community & mask))
+			return (1);
+		break;
+	case EXT_COMMUNITY_IPV4:
+		ip = ntohl(c->data.ext_ip.addr.s_addr);
+		com = (u_int64_t)ip << 16;
+		mask = 0xffffffff0000ULL;
+		if ((com & mask) != (community & mask))
+			return (0);
+
+		com = c->data.ext_ip.val;
+		mask = 0xffff;
+		if ((com & mask) == (community & mask))
+			return (1);
+		break;
+	case EXT_COMMUNITY_FOUR_AS:
+		com = (u_int64_t)c->data.ext_as4.as4 << 16;
+		mask = 0xffffffffULL << 16;
+		if ((com & mask) != (community & mask))
+			return (0);
+
+		com = c->data.ext_as4.val;
+		mask = 0xffff;
+		if ((com & mask) == (community & mask))
+			return (1);
+		break;
+	case EXT_COMMUNITY_OPAQUE:
+		com = c->data.ext_opaq & EXT_COMMUNITY_OPAQUE_MAX;
+		mask = EXT_COMMUNITY_OPAQUE_MAX;
+		if ((com & mask) == (community & mask))
+			return (1);
+		break;
+	}
+
+	return (0);
+}
diff --git a/bgpd/rde_decide.c b/bgpd/rde_decide.c
index 2a94aa9..d872527 100644
--- a/bgpd/rde_decide.c
+++ b/bgpd/rde_decide.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_decide.c,v 1.58 2009/06/29 14:10:13 claudio Exp $ */
+/*	$OpenBSD: rde_decide.c,v 1.59 2009/08/06 08:53:11 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -118,6 +118,12 @@ prefix_cmp(struct prefix *p1, struct prefix *p2)
 	asp1 = p1->aspath;
 	asp2 = p2->aspath;
 
+	/* pathes with errors are not eligible */
+	if (asp1->flags & F_ATTR_PARSE_ERR)
+		return (-1);
+	if (asp2->flags & F_ATTR_PARSE_ERR)
+		return (1);
+
 	/* only loop free pathes are eligible */
 	if (asp1->flags & F_ATTR_LOOP)
 		return (-1);
@@ -204,7 +210,7 @@ prefix_cmp(struct prefix *p1, struct prefix *p2)
 	}
 
 	fatalx("Uh, oh a politician in the decision process");
-	/* NOTREACHED */
+	return(0);	/* NOTREACHED */
 }
 
 /*
@@ -245,7 +251,7 @@ prefix_evaluate(struct prefix *p, struct rib_entry *re)
 	}
 
 	xp = LIST_FIRST(&re->prefix_h);
-	if (xp == NULL || xp->aspath->flags & F_ATTR_LOOP ||
+	if (xp == NULL || xp->aspath->flags & (F_ATTR_LOOP|F_ATTR_PARSE_ERR) ||
 	    (xp->aspath->nexthop != NULL &&
 	    xp->aspath->nexthop->state != NEXTHOP_REACH))
 		/* xp is ineligible */
@@ -263,7 +269,7 @@ prefix_evaluate(struct prefix *p, struct rib_entry *re)
 		 */
 		rde_generate_updates(re->ribid, xp, re->active);
 		if ((re->flags & F_RIB_NOFIB) == 0)
-			rde_send_kroute(xp, re->active);
+			rde_send_kroute(xp, re->active, re->ribid);
 
 		re->active = xp;
 		if (xp != NULL)
diff --git a/bgpd/rde_filter.c b/bgpd/rde_filter.c
index 001b796..e791fa1 100644
--- a/bgpd/rde_filter.c
+++ b/bgpd/rde_filter.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_filter.c,v 1.56 2009/06/06 01:10:29 claudio Exp $ */
+/*	$OpenBSD: rde_filter.c,v 1.62 2010/03/05 15:25:00 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -40,6 +40,13 @@ rde_filter(u_int16_t ribid, struct rde_aspath **new, struct filter_head *rules,
 	if (new != NULL)
 		*new = NULL;
 
+	if (asp->flags & F_ATTR_PARSE_ERR)
+		/*
+	 	 * don't try to filter bad updates just deny them
+		 * so they act as implicit withdraws
+		 */
+		return (ACTION_DENY);
+
 	TAILQ_FOREACH(f, rules, entry) {
 		if (dir != f->dir)
 			continue;
@@ -59,7 +66,7 @@ rde_filter(u_int16_t ribid, struct rde_aspath **new, struct filter_head *rules,
 					/* ... and use the copy from now on */
 					asp = *new;
 				}
-				rde_apply_set(asp, &f->set, prefix->af,
+				rde_apply_set(asp, &f->set, prefix->aid,
 				    from, peer);
 			}
 			if (f->action != ACTION_NONE)
@@ -73,7 +80,7 @@ rde_filter(u_int16_t ribid, struct rde_aspath **new, struct filter_head *rules,
 
 void
 rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh,
-    sa_family_t af, struct rde_peer *from, struct rde_peer *peer)
+    u_int8_t aid, struct rde_peer *from, struct rde_peer *peer)
 {
 	struct filter_set	*set;
 	u_char			*np;
@@ -167,7 +174,7 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh,
 		case ACTION_SET_NEXTHOP_NOMODIFY:
 		case ACTION_SET_NEXTHOP_SELF:
 			nexthop_modify(asp, &set->action.nexthop, set->type,
-			    af);
+			    aid);
 			break;
 		case ACTION_SET_COMMUNITY:
 			switch (set->action.community.as) {
@@ -243,6 +250,17 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh,
 			asp->rtlabelid = set->action.id;
 			rtlabel_ref(asp->rtlabelid);
 			break;
+		case ACTION_SET_ORIGIN:
+			asp->origin = set->action.origin;
+			break;
+		case ACTION_SET_EXT_COMMUNITY:
+			community_ext_set(asp, &set->action.ext_community,
+			    peer->conf.remote_as);
+			break;
+		case ACTION_DEL_EXT_COMMUNITY:
+			community_ext_delete(asp, &set->action.ext_community,
+			    peer->conf.remote_as);
+			break;
 		}
 	}
 }
@@ -251,11 +269,21 @@ int
 rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
     struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer)
 {
-	int	as, type;
+	u_int32_t	pas;
+	int		cas, type;
+
+	if (asp != NULL && f->match.as.type != AS_NONE) {
+		if (f->match.as.flags & AS_FLAG_NEIGHBORAS)
+			pas = peer->conf.remote_as;
+		else
+			pas = f->match.as.as;
+		if (aspath_match(asp->aspath, f->match.as.type, pas) == 0)
+			return (0);
+	}
 
-	if (asp != NULL && f->match.as.type != AS_NONE)
-		if (aspath_match(asp->aspath, f->match.as.type,
-		    f->match.as.as) == 0)
+	if (asp != NULL && f->match.aslen.type != ASLEN_NONE)
+		if (aspath_lenmatch(asp->aspath, f->match.aslen.type,
+		    f->match.aslen.aslen) == 0)
 			return (0);
 
 	if (asp != NULL && f->match.community.as != COMMUNITY_UNSET) {
@@ -263,10 +291,10 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
 		case COMMUNITY_ERROR:
 			fatalx("rde_apply_set bad community string");
 		case COMMUNITY_NEIGHBOR_AS:
-			as = peer->conf.remote_as;
+			cas = peer->conf.remote_as;
 			break;
 		default:
-			as = f->match.community.as;
+			cas = f->match.community.as;
 			break;
 		}
 
@@ -281,12 +309,17 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
 			break;
 		}
 
-		if (rde_filter_community(asp, as, type) == 0)
+		if (community_match(asp, cas, type) == 0)
 			return (0);
 	}
+	if (asp != NULL &&
+	    (f->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID))
+		if (community_ext_match(asp, &f->match.ext_community,
+		    peer->conf.remote_as) == 0)
+			return (0);
 
-	if (f->match.prefix.addr.af != 0) {
-		if (f->match.prefix.addr.af != prefix->af)
+	if (f->match.prefix.addr.aid != 0) {
+		if (f->match.prefix.addr.aid != prefix->aid)
 			/* don't use IPv4 rules for IPv6 and vice versa */
 			return (0);
 
@@ -322,7 +355,7 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
 	} else if (f->match.prefixlen.op != OP_NONE) {
 		/* only prefixlen without a prefix */
 
-		if (f->match.prefixlen.af != prefix->af)
+		if (f->match.prefixlen.aid != prefix->aid)
 			/* don't use IPv4 rules for IPv6 and vice versa */
 			return (0);
 
@@ -356,19 +389,6 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
 }
 
 int
-rde_filter_community(struct rde_aspath *asp, int as, int type)
-{
-	struct attr	*a;
-
-	a = attr_optget(asp, ATTR_COMMUNITIES);
-	if (a == NULL)
-		/* no communities, no match */
-		return (0);
-
-	return (community_match(a->data, a->len, as, type));
-}
-
-int
 rde_filter_equal(struct filter_head *a, struct filter_head *b,
     struct rde_peer *peer, enum directions dir)
 {
@@ -476,6 +496,12 @@ filterset_cmp(struct filter_set *a, struct filter_set *b)
 		return (a->action.community.type - b->action.community.type);
 	}
 
+	if (a->type == ACTION_SET_EXT_COMMUNITY ||
+	    a->type == ACTION_DEL_EXT_COMMUNITY) {	/* a->type == b->type */
+		return (memcmp(&a->action.ext_community,
+		    &b->action.ext_community, sizeof(a->action.ext_community)));
+	}
+
 	if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) {
 		/*
 		 * This is the only interesting case, all others are considered
@@ -483,13 +509,29 @@ filterset_cmp(struct filter_set *a, struct filter_set *b)
 		 * reject it at the same time. Allow one IPv4 and one IPv6
 		 * per filter set or only one of the other nexthop modifiers.
 		 */
-		return (a->action.nexthop.af - b->action.nexthop.af);
+		return (a->action.nexthop.aid - b->action.nexthop.aid);
 	}
 
 	/* equal */
 	return (0);
 }
 
+void
+filterset_move(struct filter_set_head *source, struct filter_set_head *dest)
+{
+	struct filter_set	*s;
+
+	TAILQ_INIT(dest);
+
+	if (source == NULL)
+		return;
+
+	while ((s = TAILQ_FIRST(source)) != NULL) {
+		TAILQ_REMOVE(source, s, entry);
+		TAILQ_INSERT_TAIL(dest, s, entry);
+	}
+}
+
 int
 filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh)
 {
@@ -574,6 +616,19 @@ filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh)
 			if (strcmp(as, bs) == 0)
 				continue;
 			break;
+		case ACTION_SET_ORIGIN:
+			if (a->type == b->type &&
+			    a->action.origin == b->action.origin)
+				continue;
+			break;
+		case ACTION_SET_EXT_COMMUNITY:
+		case ACTION_DEL_EXT_COMMUNITY:
+			if (a->type == b->type && memcmp(
+			    &a->action.ext_community,
+			    &b->action.ext_community,
+			    sizeof(a->action.ext_community)) == 0)
+				continue;
+			break;
 		}
 		/* compare failed */
 		return (0);
@@ -616,7 +671,14 @@ filterset_name(enum action_types type)
 	case ACTION_RTLABEL:
 	case ACTION_RTLABEL_ID:
 		return ("rtlabel");
+	case ACTION_SET_ORIGIN:
+		return ("origin");
+	case ACTION_SET_EXT_COMMUNITY:
+		return ("ext-community");
+	case ACTION_DEL_EXT_COMMUNITY:
+		return ("ext-community delete");
 	}
 
 	fatalx("filterset_name: got lost");
+	return (NULL); /* NOT REACHED */
 }
diff --git a/bgpd/rde_prefix.c b/bgpd/rde_prefix.c
index 0c8e211..eb97613 100644
--- a/bgpd/rde_prefix.c
+++ b/bgpd/rde_prefix.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_prefix.c,v 1.29 2009/05/30 18:27:17 claudio Exp $ */
+/*	$OpenBSD: rde_prefix.c,v 1.32 2010/03/26 15:41:04 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -38,15 +38,16 @@
  * pt_lookup: lookup a IP in the prefix table. Mainly for "show ip bgp".
  * pt_empty:  returns true if there is no bgp prefix linked to the pt_entry.
  * pt_init:   initialize prefix table.
- * pt_alloc?: allocate a AF specific pt_entry. Internal function.
+ * pt_alloc: allocate a AF specific pt_entry. Internal function.
  * pt_free:   free a pt_entry. Internal function.
  */
 
 /* internal prototypes */
-static struct pt_entry4	*pt_alloc4(void);
-static struct pt_entry6	*pt_alloc6(void);
+static struct pt_entry	*pt_alloc(struct pt_entry *);
 static void		 pt_free(struct pt_entry *);
 
+size_t	pt_sizes[AID_MAX] = AID_PTSIZE;
+
 RB_HEAD(pt_tree, pt_entry);
 RB_PROTOTYPE(pt_tree, pt_entry, pt_e, pt_prefix_cmp);
 RB_GENERATE(pt_tree, pt_entry, pt_e, pt_prefix_cmp);
@@ -70,17 +71,24 @@ void
 pt_getaddr(struct pt_entry *pte, struct bgpd_addr *addr)
 {
 	bzero(addr, sizeof(struct bgpd_addr));
-	switch (pte->af) {
-	case AF_INET:
-		addr->af = pte->af;
+	addr->aid = pte->aid;
+	switch (addr->aid) {
+	case AID_INET:
 		addr->v4 = ((struct pt_entry4 *)pte)->prefix4;
 		break;
-	case AF_INET6:
-		addr->af = pte->af;
+	case AID_INET6:
 		memcpy(&addr->v6, &((struct pt_entry6 *)pte)->prefix6,
 		    sizeof(addr->v6));
 		/* XXX scope_id ??? */
 		break;
+	case AID_VPN_IPv4:
+		addr->vpn4.addr = ((struct pt_entry_vpn4 *)pte)->prefix4;
+		addr->vpn4.rd = ((struct pt_entry_vpn4 *)pte)->rd;
+		addr->vpn4.labellen = ((struct pt_entry_vpn4 *)pte)->labellen;
+		memcpy(addr->vpn4.labelstack,
+		    ((struct pt_entry_vpn4 *)pte)->labelstack,
+		    addr->vpn4.labellen);
+		break;
 	default:
 		fatalx("pt_getaddr: unknown af");
 	}
@@ -89,33 +97,49 @@ pt_getaddr(struct pt_entry *pte, struct bgpd_addr *addr)
 struct pt_entry *
 pt_fill(struct bgpd_addr *prefix, int prefixlen)
 {
-	static struct pt_entry4	pte4;
-	static struct pt_entry6	pte6;
-	in_addr_t		addr_hbo;
+	static struct pt_entry4		pte4;
+	static struct pt_entry6		pte6;
+	static struct pt_entry_vpn4	pte_vpn4;
+	in_addr_t			addr_hbo;
 
-	switch (prefix->af) {
-	case AF_INET:
+	switch (prefix->aid) {
+	case AID_INET:
 		bzero(&pte4, sizeof(pte4));
+		pte4.aid = prefix->aid;
 		if (prefixlen > 32)
-			fatalx("pt_get: bad IPv4 prefixlen");
-		pte4.af = AF_INET;
+			fatalx("pt_fill: bad IPv4 prefixlen");
 		addr_hbo = ntohl(prefix->v4.s_addr);
 		pte4.prefix4.s_addr = htonl(addr_hbo &
 		    prefixlen2mask(prefixlen));
 		pte4.prefixlen = prefixlen;
 		return ((struct pt_entry *)&pte4);
-	case AF_INET6:
+	case AID_INET6:
 		bzero(&pte6, sizeof(pte6));
+		pte6.aid = prefix->aid;
 		if (prefixlen > 128)
 			fatalx("pt_get: bad IPv6 prefixlen");
-		pte6.af = AF_INET6;
 		pte6.prefixlen = prefixlen;
 		inet6applymask(&pte6.prefix6, &prefix->v6, prefixlen);
 		return ((struct pt_entry *)&pte6);
+	case AID_VPN_IPv4:
+		bzero(&pte_vpn4, sizeof(pte_vpn4));
+		pte_vpn4.aid = prefix->aid;
+		if (prefixlen > 32)
+			fatalx("pt_fill: bad IPv4 prefixlen");
+		addr_hbo = ntohl(prefix->vpn4.addr.s_addr);
+		pte_vpn4.prefix4.s_addr = htonl(addr_hbo &
+		    prefixlen2mask(prefixlen));
+		pte_vpn4.prefixlen = prefixlen;
+		pte_vpn4.rd = prefix->vpn4.rd;
+		pte_vpn4.labellen = prefix->vpn4.labellen;
+		memcpy(pte_vpn4.labelstack, prefix->vpn4.labelstack,
+		    prefix->vpn4.labellen);
+		return ((struct pt_entry *)&pte_vpn4);
 	default:
-		log_warnx("pt_get: unknown af");
-		return (NULL);
+		fatalx("pt_fill: unknown af");
 	}
+	/* NOT REACHED */
+	return (NULL);
 }
 
 struct pt_entry *
@@ -131,39 +155,12 @@ struct pt_entry *
 pt_add(struct bgpd_addr *prefix, int prefixlen)
 {
 	struct pt_entry		*p = NULL;
-	struct pt_entry4	*p4;
-	struct pt_entry6	*p6;
-	in_addr_t		 addr_hbo;
 
-	switch (prefix->af) {
-	case AF_INET:
-		p4 = pt_alloc4();
-		if (prefixlen > 32)
-			fatalx("pt_add: bad IPv4 prefixlen");
-		p4->af = AF_INET;
-		p4->prefixlen = prefixlen;
-		addr_hbo = ntohl(prefix->v4.s_addr);
-		p4->prefix4.s_addr = htonl(addr_hbo &
-		    prefixlen2mask(prefixlen));
-		p = (struct pt_entry *)p4;
-		break;
-	case AF_INET6:
-		p6 = pt_alloc6();
-		if (prefixlen > 128)
-			fatalx("pt_add: bad IPv6 prefixlen");
-		p6->af = AF_INET6;
-		p6->prefixlen = prefixlen;
-		inet6applymask(&p6->prefix6, &prefix->v6, prefixlen);
-		p = (struct pt_entry *)p6;
-		break;
-	default:
-		fatalx("pt_add: unknown af");
-	}
+	p = pt_fill(prefix, prefixlen);
+	p = pt_alloc(p);
 
-	if (RB_INSERT(pt_tree, &pttable, p) != NULL) {
-		log_warnx("pt_add: insert failed");
-		return (NULL);
-	}
+	if (RB_INSERT(pt_tree, &pttable, p) != NULL)
+		fatalx("pt_add: insert failed");
 
 	return (p);
 }
@@ -183,13 +180,14 @@ struct pt_entry *
 pt_lookup(struct bgpd_addr *addr)
 {
 	struct pt_entry	*p;
-	int		 i;
+	int		 i = 0;
 
-	switch (addr->af) {
-	case AF_INET:
+	switch (addr->aid) {
+	case AID_INET:
+	case AID_VPN_IPv4:
 		i = 32;
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		i = 128;
 		break;
 	default:
@@ -206,17 +204,18 @@ pt_lookup(struct bgpd_addr *addr)
 int
 pt_prefix_cmp(const struct pt_entry *a, const struct pt_entry *b)
 {
-	const struct pt_entry4	*a4, *b4;
-	const struct pt_entry6	*a6, *b6;
-	int			 i;
+	const struct pt_entry4		*a4, *b4;
+	const struct pt_entry6		*a6, *b6;
+	const struct pt_entry_vpn4	*va4, *vb4;
+	int				 i;
 
-	if (a->af > b->af)
+	if (a->aid > b->aid)
 		return (1);
-	if (a->af < b->af)
+	if (a->aid < b->aid)
 		return (-1);
 
-	switch (a->af) {
-	case AF_INET:
+	switch (a->aid) {
+	case AID_INET:
 		a4 = (const struct pt_entry4 *)a;
 		b4 = (const struct pt_entry4 *)b;
 		if (ntohl(a4->prefix4.s_addr) > ntohl(b4->prefix4.s_addr))
@@ -228,7 +227,7 @@ pt_prefix_cmp(const struct pt_entry *a, const struct pt_entry *b)
 		if (a4->prefixlen < b4->prefixlen)
 			return (-1);
 		return (0);
-	case AF_INET6:
+	case AID_INET6:
 		a6 = (const struct pt_entry6 *)a;
 		b6 = (const struct pt_entry6 *)b;
 
@@ -242,49 +241,49 @@ pt_prefix_cmp(const struct pt_entry *a, const struct pt_entry *b)
 		if (a6->prefixlen > b6->prefixlen)
 			return (1);
 		return (0);
+	case AID_VPN_IPv4:
+		va4 = (const struct pt_entry_vpn4 *)a;
+		vb4 = (const struct pt_entry_vpn4 *)b;
+		if (ntohl(va4->prefix4.s_addr) > ntohl(vb4->prefix4.s_addr))
+			return (1);
+		if (ntohl(va4->prefix4.s_addr) < ntohl(vb4->prefix4.s_addr))
+			return (-1);
+		if (va4->prefixlen > vb4->prefixlen)
+			return (1);
+		if (va4->prefixlen < vb4->prefixlen)
+			return (-1);
+		if (betoh64(va4->rd) > betoh64(vb4->rd))
+			return (1);
+		if (betoh64(va4->rd) < betoh64(vb4->rd))
+			return (-1);
+		return (0);
 	default:
 		fatalx("pt_prefix_cmp: unknown af");
 	}
 	return (-1);
 }
 
-/* returns a zeroed pt_entry function may not return on fail */
-static struct pt_entry4 *
-pt_alloc4(void)
+/*
+ * Returns a pt_entry cloned from the one passed in.
+ * Function may not return on failure.
+ */
+static struct pt_entry *
+pt_alloc(struct pt_entry *op)
 {
-	struct pt_entry4	*p;
+	struct pt_entry		*p;
 
-	p = calloc(1, sizeof(*p));
+	p = malloc(pt_sizes[op->aid]);
 	if (p == NULL)
 		fatal("pt_alloc");
-	rdemem.pt4_cnt++;
-	return (p);
-}
+	rdemem.pt_cnt[op->aid]++;
+	memcpy(p, op, pt_sizes[op->aid]);
 
-static struct pt_entry6 *
-pt_alloc6(void)
-{
-	struct pt_entry6	*p;
-
-	p = calloc(1, sizeof(*p));
-	if (p == NULL)
-		fatal("pt_alloc");
-	rdemem.pt6_cnt++;
 	return (p);
 }
 
 static void
 pt_free(struct pt_entry *pte)
 {
-	switch (pte->af) {
-	case AF_INET:
-		rdemem.pt4_cnt--;
-		break;
-	case AF_INET6:
-		rdemem.pt6_cnt--;
-		break;
-	default:
-		break;
-	}
+	rdemem.pt_cnt[pte->aid]--;
 	free(pte);
 }
diff --git a/bgpd/rde_rib.c b/bgpd/rde_rib.c
index 78dc203..afc01c9 100644
--- a/bgpd/rde_rib.c
+++ b/bgpd/rde_rib.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_rib.c,v 1.116 2009/06/29 14:13:48 claudio Exp $ */
+/*	$OpenBSD: rde_rib.c,v 1.125 2010/04/07 09:44:11 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
@@ -18,7 +18,11 @@
 
 #include <sys/types.h>
 #include <sys/queue.h>
+#if defined(__FreeBSD__)	/* sys/hash.h */
+#include "hash.h"
+#else
 #include <sys/hash.h>
+#endif /* defined(__FreeBSD__) */
 
 #include <stdlib.h>
 #include <string.h>
@@ -50,16 +54,15 @@ RB_GENERATE(rib_tree, rib_entry, rib_e, rib_compare);
 
 /* RIB specific functions */
 u_int16_t
-rib_new(int id, char *name, u_int16_t flags)
+rib_new(char *name, u_int rtableid, u_int16_t flags)
 {
 	struct rib	*xribs;
 	size_t		newsize;
+	u_int16_t	id;
 
-	if (id < 0) {
-		for (id = 0; id < rib_size; id++) {
-			if (*ribs[id].name == '\0')
-				break;
-		}
+	for (id = 0; id < rib_size; id++) {
+		if (*ribs[id].name == '\0')
+			break;
 	}
 
 	if (id == RIB_FAILED)
@@ -78,9 +81,10 @@ rib_new(int id, char *name, u_int16_t flags)
 	bzero(&ribs[id], sizeof(struct rib));
 	strlcpy(ribs[id].name, name, sizeof(ribs[id].name));
 	RB_INIT(&ribs[id].rib);
-	ribs[id].state = RIB_ACTIVE;
+	ribs[id].state = RECONF_REINIT;
 	ribs[id].id = id;
 	ribs[id].flags = flags;
+	ribs[id].rtableid = rtableid;
 
 	return (id);
 }
@@ -173,15 +177,16 @@ rib_lookup(struct rib *rib, struct bgpd_addr *addr)
 	struct rib_entry *re;
 	int		 i;
 
-	switch (addr->af) {
-	case AF_INET:
+	switch (addr->aid) {
+	case AID_INET:
+	case AID_VPN_IPv4:
 		for (i = 32; i >= 0; i--) {
 			re = rib_get(rib, addr, i);
 			if (re != NULL)
 				return (re);
 		}
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		for (i = 128; i >= 0; i--) {
 			re = rib_get(rib, addr, i);
 			if (re != NULL)
@@ -215,6 +220,7 @@ rib_add(struct rib *rib, struct bgpd_addr *prefix, int prefixlen)
 
         if (RB_INSERT(rib_tree, &rib->rib, re) != NULL) {
 		log_warnx("rib_add: insert failed");
+		free(re);
 		return (NULL);
 	}
 
@@ -254,7 +260,7 @@ rib_empty(struct rib_entry *re)
 
 void
 rib_dump(struct rib *rib, void (*upcall)(struct rib_entry *, void *),
-    void *arg, sa_family_t af)
+    void *arg, u_int8_t aid)
 {
 	struct rib_context	*ctx;
 
@@ -263,7 +269,7 @@ rib_dump(struct rib *rib, void (*upcall)(struct rib_entry *, void *),
 	ctx->ctx_rib = rib;
 	ctx->ctx_upcall = upcall;
 	ctx->ctx_arg = arg;
-	ctx->ctx_af = af;
+	ctx->ctx_aid = aid;
 	rib_dump_r(ctx);
 }
 
@@ -280,7 +286,8 @@ rib_dump_r(struct rib_context *ctx)
 		re = rib_restart(ctx);
 
 	for (i = 0; re != NULL; re = RB_NEXT(rib_tree, unused, re)) {
-		if (ctx->ctx_af != AF_UNSPEC && ctx->ctx_af != re->prefix->af)
+		if (ctx->ctx_aid != AID_UNSPEC &&
+		    ctx->ctx_aid != re->prefix->aid)
 			continue;
 		if (ctx->ctx_count && i++ >= ctx->ctx_count &&
 		    (re->flags & F_RIB_ENTRYLOCK) == 0) {
@@ -308,7 +315,7 @@ rib_restart(struct rib_context *ctx)
 	re->flags &= ~F_RIB_ENTRYLOCK;
 
 	/* find first non empty element */
-	while (rib_empty(re))
+	while (re && rib_empty(re))
 		re = RB_NEXT(rib_tree, unused, re);
 
 	/* free the previously locked rib element if empty */
@@ -632,11 +639,11 @@ prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
 	int		i;
 	u_int8_t	m;
 
-	if (a->af != b->af)
-		return (a->af - b->af);
+	if (a->aid != b->aid)
+		return (a->aid - b->aid);
 
-	switch (a->af) {
-	case AF_INET:
+	switch (a->aid) {
+	case AID_INET:
 		if (prefixlen > 32)
 			fatalx("prefix_cmp: bad IPv4 prefixlen");
 		mask = htonl(prefixlen2mask(prefixlen));
@@ -645,7 +652,7 @@ prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
 		if (aa != ba)
 			return (aa - ba);
 		return (0);
-	case AF_INET6:
+	case AID_INET6:
 		if (prefixlen > 128)
 			fatalx("prefix_cmp: bad IPv6 prefixlen");
 		for (i = 0; i < prefixlen / 8; i++)
@@ -660,6 +667,24 @@ prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
 				    (b->v6.s6_addr[prefixlen / 8] & m));
 		}
 		return (0);
+	case AID_VPN_IPv4:
+		if (prefixlen > 32)
+			fatalx("prefix_cmp: bad IPv4 VPN prefixlen");
+		if (betoh64(a->vpn4.rd) > betoh64(b->vpn4.rd))
+			return (1);
+		if (betoh64(a->vpn4.rd) < betoh64(b->vpn4.rd))
+			return (-1);
+		mask = htonl(prefixlen2mask(prefixlen));
+		aa = ntohl(a->vpn4.addr.s_addr & mask);
+		ba = ntohl(b->vpn4.addr.s_addr & mask);
+		if (aa != ba)
+			return (aa - ba);
+		if (a->vpn4.labellen > b->vpn4.labellen)
+			return (1);
+		if (a->vpn4.labellen < b->vpn4.labellen)
+			return (-1);
+		return (memcmp(a->vpn4.labelstack, b->vpn4.labelstack,
+		    a->vpn4.labellen));
 	default:
 		fatalx("prefix_cmp: unknown af");
 	}
@@ -806,16 +831,33 @@ prefix_write(u_char *buf, int len, struct bgpd_addr *prefix, u_int8_t plen)
 {
 	int	totlen;
 
-	if (prefix->af != AF_INET && prefix->af != AF_INET6)
-		return (-1);
-
-	totlen = PREFIX_SIZE(plen);
+	switch (prefix->aid) {
+	case AID_INET:
+	case AID_INET6:
+		totlen = PREFIX_SIZE(plen);
 
-	if (totlen > len)
+		if (totlen > len)
+			return (-1);
+		*buf++ = plen;
+		memcpy(buf, &prefix->ba, totlen - 1);
+		return (totlen);
+	case AID_VPN_IPv4:
+		totlen = PREFIX_SIZE(plen) + sizeof(prefix->vpn4.rd) +
+		    prefix->vpn4.labellen;
+		plen += (sizeof(prefix->vpn4.rd) + prefix->vpn4.labellen) * 8;
+
+		if (totlen > len)
+			return (-1);
+		*buf++ = plen;
+		memcpy(buf, &prefix->vpn4.labelstack, prefix->vpn4.labellen);
+		buf += prefix->vpn4.labellen;
+		memcpy(buf, &prefix->vpn4.rd, sizeof(prefix->vpn4.rd));
+		buf += sizeof(prefix->vpn4.rd);
+		memcpy(buf, &prefix->vpn4.addr, PREFIX_SIZE(plen) - 1);
+		return (totlen);
+	default:
 		return (-1);
-	*buf++ = plen;
-	memcpy(buf, &prefix->ba, totlen - 1);
-	return (totlen);
+	}
 }
 
 /*
@@ -861,7 +903,7 @@ prefix_updateall(struct rde_aspath *asp, enum nexthop_state state,
 			 */
 			if ((p->rib->flags & F_RIB_NOFIB) == 0 &&
 			    p == p->rib->active)
-				rde_send_kroute(p, NULL);
+				rde_send_kroute(p, NULL, p->rib->ribid);
 			continue;
 		}
 
@@ -885,16 +927,12 @@ prefix_updateall(struct rde_aspath *asp, enum nexthop_state state,
 void
 prefix_destroy(struct prefix *p)
 {
-	struct rib_entry	*re;
 	struct rde_aspath	*asp;
 
-	re = p->rib;
 	asp = p->aspath;
 	prefix_unlink(p);
 	prefix_free(p);
 
-	if (rib_empty(re))
-		rib_remove(re);
 	if (path_empty(asp))
 		path_destroy(asp);
 }
@@ -907,7 +945,6 @@ prefix_network_clean(struct rde_peer *peer, time_t reloadtime, u_int32_t flags)
 {
 	struct rde_aspath	*asp, *xasp;
 	struct prefix		*p, *xp;
-	struct pt_entry		*pte;
 
 	for (asp = LIST_FIRST(&peer->path_h); asp != NULL; asp = xasp) {
 		xasp = LIST_NEXT(asp, peer_l);
@@ -916,12 +953,8 @@ prefix_network_clean(struct rde_peer *peer, time_t reloadtime, u_int32_t flags)
 		for (p = LIST_FIRST(&asp->prefix_h); p != NULL; p = xp) {
 			xp = LIST_NEXT(p, path_l);
 			if (reloadtime > p->lastchange) {
-				pte = p->prefix;
 				prefix_unlink(p);
 				prefix_free(p);
-
-				if (pt_empty(pte))
-					pt_remove(pte);
 			}
 		}
 		if (path_empty(asp))
@@ -954,11 +987,11 @@ prefix_link(struct prefix *pref, struct rib_entry *re, struct rde_aspath *asp)
 static void
 prefix_unlink(struct prefix *pref)
 {
-	if (pref->rib) {
-		/* make route decision */
-		LIST_REMOVE(pref, rib_l);
-		prefix_evaluate(NULL, pref->rib);
-	}
+	struct rib_entry	*re = pref->rib;
+
+	/* make route decision */
+	LIST_REMOVE(pref, rib_l);
+	prefix_evaluate(NULL, re);
 
 	LIST_REMOVE(pref, path_l);
 	PREFIX_COUNT(pref->aspath, -1);
@@ -966,6 +999,8 @@ prefix_unlink(struct prefix *pref)
 	pt_unref(pref->prefix);
 	if (pt_empty(pref->prefix))
 		pt_remove(pref->prefix);
+	if (rib_empty(re))
+		rib_remove(re);
 
 	/* destroy all references to other objects */
 	pref->aspath = NULL;
@@ -973,8 +1008,8 @@ prefix_unlink(struct prefix *pref)
 	pref->rib = NULL;
 
 	/*
-	 * It's the caller's duty to remove empty aspath respectively pt_entry
-	 * structures. Also freeing the unlinked prefix is the caller's duty.
+	 * It's the caller's duty to remove empty aspath structures.
+	 * Also freeing the unlinked prefix is the caller's duty.
 	 */
 }
 
@@ -1070,10 +1105,6 @@ nexthop_update(struct kroute_nexthop *msg)
 		return;
 	}
 
-	if (nexthop_delete(nh))
-		/* nexthop no longer used */
-		return;
-
 	oldstate = nh->state;
 	if (msg->valid)
 		nh->state = NEXTHOP_REACH;
@@ -1088,21 +1119,13 @@ nexthop_update(struct kroute_nexthop *msg)
 		memcpy(&nh->true_nexthop, &msg->gateway,
 		    sizeof(nh->true_nexthop));
 
-	switch (msg->nexthop.af) {
-	case AF_INET:
-		nh->nexthop_netlen = msg->kr.kr4.prefixlen;
-		nh->nexthop_net.af = AF_INET;
-		nh->nexthop_net.v4.s_addr = msg->kr.kr4.prefix.s_addr;
-		break;
-	case AF_INET6:
-		nh->nexthop_netlen = msg->kr.kr6.prefixlen;
-		nh->nexthop_net.af = AF_INET6;
-		memcpy(&nh->nexthop_net.v6, &msg->kr.kr6.prefix,
-		    sizeof(struct in6_addr));
-		break;
-	default:
-		fatalx("nexthop_update: unknown af");
-	}
+	memcpy(&nh->nexthop_net, &msg->net,
+	    sizeof(nh->nexthop_net));
+	nh->nexthop_netlen = msg->netlen;
+
+	if (nexthop_delete(nh))
+		/* nexthop no longer used */
+		return;
 
 	if (rde_noevaluate())
 		/*
@@ -1118,7 +1141,7 @@ nexthop_update(struct kroute_nexthop *msg)
 
 void
 nexthop_modify(struct rde_aspath *asp, struct bgpd_addr *nexthop,
-    enum action_types type, sa_family_t af)
+    enum action_types type, u_int8_t aid)
 {
 	struct nexthop	*nh;
 
@@ -1138,7 +1161,7 @@ nexthop_modify(struct rde_aspath *asp, struct bgpd_addr *nexthop,
 		asp->flags |= F_NEXTHOP_SELF;
 		return;
 	}
-	if (af != nexthop->af)
+	if (aid != nexthop->aid)
 		return;
 
 	nh = nexthop_get(nexthop);
@@ -1233,17 +1256,17 @@ nexthop_compare(struct nexthop *na, struct nexthop *nb)
 	a = &na->exit_nexthop;
 	b = &nb->exit_nexthop;
 
-	if (a->af != b->af)
-		return (a->af - b->af);
+	if (a->aid != b->aid)
+		return (a->aid - b->aid);
 
-	switch (a->af) {
-	case AF_INET:
+	switch (a->aid) {
+	case AID_INET:
 		if (ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr))
 			return (1);
 		if (ntohl(a->v4.s_addr) < ntohl(b->v4.s_addr))
 			return (-1);
 		return (0);
-	case AF_INET6:
+	case AID_INET6:
 		return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr)));
 	default:
 		fatalx("nexthop_cmp: unknown af");
@@ -1269,14 +1292,14 @@ nexthop_hash(struct bgpd_addr *nexthop)
 {
 	u_int32_t	 h = 0;
 
-	switch (nexthop->af) {
-	case AF_INET:
+	switch (nexthop->aid) {
+	case AID_INET:
 		h = (AF_INET ^ ntohl(nexthop->v4.s_addr) ^
 		    ntohl(nexthop->v4.s_addr) >> 13) &
 		    nexthoptable.nexthop_hashmask;
 		break;
-	case AF_INET6:
-		h = hash32_buf(nexthop->v6.s6_addr, sizeof(struct in6_addr),
+	case AID_INET6:
+		h = hash32_buf(&nexthop->v6, sizeof(struct in6_addr),
 		    HASHINIT) & nexthoptable.nexthop_hashmask;
 		break;
 	default:
diff --git a/bgpd/rde_update.c b/bgpd/rde_update.c
index 209664f..f50223b 100644
--- a/bgpd/rde_update.c
+++ b/bgpd/rde_update.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: rde_update.c,v 1.68 2009/06/06 01:10:29 claudio Exp $ */
+/*	$OpenBSD: rde_update.c,v 1.77 2010/01/13 06:02:37 claudio Exp $ */
 
 /*
  * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
@@ -17,19 +17,27 @@
  */
 #include <sys/types.h>
 #include <sys/queue.h>
+#if defined(__FreeBSD__)	/* sys/hash.h */
+#include "hash.h"
+#else
 #include <sys/hash.h>
+#endif /* defined(__FreeBSD__) */
 
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#if defined(__FreeBSD__)	/* limits.h */
+#include <limits.h>
+#endif /* defined(__FreeBSD__) */
 
 #include "bgpd.h"
 #include "rde.h"
 
 in_addr_t	up_get_nexthop(struct rde_peer *, struct rde_aspath *);
 int		up_generate_mp_reach(struct rde_peer *, struct update_attr *,
-		    struct rde_aspath *, sa_family_t);
+		    struct rde_aspath *, u_int8_t);
 int		up_generate_attr(struct rde_peer *, struct update_attr *,
-		    struct rde_aspath *, sa_family_t);
+		    struct rde_aspath *, u_int8_t);
 
 /* update stuff. */
 struct update_prefix {
@@ -65,10 +73,12 @@ RB_GENERATE(uptree_attr, update_attr, entry, up_attr_cmp)
 void
 up_init(struct rde_peer *peer)
 {
-	TAILQ_INIT(&peer->updates);
-	TAILQ_INIT(&peer->withdraws);
-	TAILQ_INIT(&peer->updates6);
-	TAILQ_INIT(&peer->withdraws6);
+	u_int8_t	i;
+
+	for (i = 0; i < AID_MAX; i++) {
+		TAILQ_INIT(&peer->updates[i]);
+		TAILQ_INIT(&peer->withdraws[i]);
+	}
 	RB_INIT(&peer->up_prefix);
 	RB_INIT(&peer->up_attrs);
 	peer->up_pcnt = 0;
@@ -103,8 +113,10 @@ up_clear(struct uplist_attr *updates, struct uplist_prefix *withdraws)
 void
 up_down(struct rde_peer *peer)
 {
-	up_clear(&peer->updates, &peer->withdraws);
-	up_clear(&peer->updates6, &peer->withdraws6);
+	u_int8_t	i;
+
+	for (i = 0; i < AID_MAX; i++)
+		up_clear(&peer->updates[i], &peer->withdraws[i]);
 
 	RB_INIT(&peer->up_prefix);
 	RB_INIT(&peer->up_attrs);
@@ -120,19 +132,19 @@ up_prefix_cmp(struct update_prefix *a, struct update_prefix *b)
 {
 	int	i;
 
-	if (a->prefix.af < b->prefix.af)
+	if (a->prefix.aid < b->prefix.aid)
 		return (-1);
-	if (a->prefix.af > b->prefix.af)
+	if (a->prefix.aid > b->prefix.aid)
 		return (1);
 
-	switch (a->prefix.af) {
-	case AF_INET:
+	switch (a->prefix.aid) {
+	case AID_INET:
 		if (ntohl(a->prefix.v4.s_addr) < ntohl(b->prefix.v4.s_addr))
 			return (-1);
 		if (ntohl(a->prefix.v4.s_addr) > ntohl(b->prefix.v4.s_addr))
 			return (1);
 		break;
-	case AF_INET6:
+	case AID_INET6:
 		i = memcmp(&a->prefix.v6, &b->prefix.v6,
 		    sizeof(struct in6_addr));
 		if (i > 0)
@@ -140,6 +152,25 @@ up_prefix_cmp(struct update_prefix *a, struct update_prefix *b)
 		if (i < 0)
 			return (-1);
 		break;
+	case AID_VPN_IPv4:
+		if (betoh64(a->prefix.vpn4.rd) < betoh64(b->prefix.vpn4.rd))
+			return (-1);
+		if (betoh64(a->prefix.vpn4.rd) > betoh64(b->prefix.vpn4.rd))
+			return (1);
+		if (ntohl(a->prefix.v4.s_addr) < ntohl(b->prefix.v4.s_addr))
+			return (-1);
+		if (ntohl(a->prefix.v4.s_addr) > ntohl(b->prefix.v4.s_addr))
+			return (1);
+		if (a->prefixlen < b->prefixlen)
+			return (-1);
+		if (a->prefixlen > b->prefixlen)
+			return (1);
+		if (a->prefix.vpn4.labellen < b->prefix.vpn4.labellen)
+			return (-1);
+		if (a->prefix.vpn4.labellen > b->prefix.vpn4.labellen)
+			return (1);
+		return (memcmp(a->prefix.vpn4.labelstack,
+		    b->prefix.vpn4.labelstack, a->prefix.vpn4.labellen));
 	default:
 		fatalx("pt_prefix_cmp: unknown af");
 	}
@@ -174,18 +205,8 @@ up_add(struct rde_peer *peer, struct update_prefix *p, struct update_attr *a)
 	struct uplist_attr	*upl = NULL;
 	struct uplist_prefix	*wdl = NULL;
 
-	switch (p->prefix.af) {
-	case AF_INET:
-		upl = &peer->updates;
-		wdl = &peer->withdraws;
-		break;
-	case AF_INET6:
-		upl = &peer->updates6;
-		wdl = &peer->withdraws6;
-		break;
-	default:
-		fatalx("up_add: unknown AF");
-	}
+	upl = &peer->updates[p->prefix.aid];
+	wdl = &peer->withdraws[p->prefix.aid];
 
 	/* 1. search for attr */
 	if (a != NULL && (na = RB_FIND(uptree_attr, &peer->up_attrs, a)) ==
@@ -270,21 +291,14 @@ up_test_update(struct rde_peer *peer, struct prefix *p)
 		/* Do not send routes back to sender */
 		return (0);
 
+	if (p->aspath->flags & F_ATTR_PARSE_ERR)
+		fatalx("try to send out a botched path");
 	if (p->aspath->flags & F_ATTR_LOOP)
 		fatalx("try to send out a looped path");
 
 	pt_getaddr(p->prefix, &addr);
-	switch (addr.af) {
-	case AF_INET:
-		if (peer->capa_announced.mp_v4 == SAFI_NONE &&
-		    peer->capa_received.mp_v6 != SAFI_NONE)
-			return (-1);
-		break;
-	case AF_INET6:
-		if (peer->capa_announced.mp_v6 == SAFI_NONE)
-			return (-1);
-		break;
-	}
+	if (peer->capa.mp[addr.aid] == 0)
+		return (-1);
 
 	if (p->aspath->peer->conf.ebgp == 0 && peer->conf.ebgp == 0) {
 		/*
@@ -325,13 +339,13 @@ up_test_update(struct rde_peer *peer, struct prefix *p)
 	}
 
 	/* well known communities */
-	if (rde_filter_community(p->aspath,
+	if (community_match(p->aspath,
 	    COMMUNITY_WELLKNOWN, COMMUNITY_NO_ADVERTISE))
 		return (0);
-	if (peer->conf.ebgp && rde_filter_community(p->aspath,
+	if (peer->conf.ebgp && community_match(p->aspath,
 	    COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPORT))
 		return (0);
-	if (peer->conf.ebgp && rde_filter_community(p->aspath,
+	if (peer->conf.ebgp && community_match(p->aspath,
 	    COMMUNITY_WELLKNOWN, COMMUNITY_NO_EXPSUBCONFED))
 		return (0);
 
@@ -362,7 +376,7 @@ up_generate(struct rde_peer *peer, struct rde_aspath *asp,
 		if (ua == NULL)
 			fatal("up_generate");
 
-		if (up_generate_attr(peer, ua, asp, addr->af) == -1) {
+		if (up_generate_attr(peer, ua, asp, addr->aid) == -1) {
 			log_warnx("generation of bgp path attributes failed");
 			free(ua);
 			return (-1);
@@ -444,18 +458,12 @@ up_generate_updates(struct filter_head *rules, struct rde_peer *peer,
 /* send a default route to the specified peer */
 void
 up_generate_default(struct filter_head *rules, struct rde_peer *peer,
-    sa_family_t af)
+    u_int8_t aid)
 {
 	struct rde_aspath	*asp, *fasp;
 	struct bgpd_addr	 addr;
 
-	if (peer->capa_received.mp_v4 == SAFI_NONE &&
-	    peer->capa_received.mp_v6 != SAFI_NONE &&
-	    af == AF_INET)
-		return;
-
-	if (peer->capa_received.mp_v6 == SAFI_NONE &&
-	    af == AF_INET6)
+	if (peer->capa.mp[aid] == 0)
 		return;
 
 	asp = path_get();
@@ -471,7 +479,7 @@ up_generate_default(struct filter_head *rules, struct rde_peer *peer,
 
 	/* filter as usual */
 	bzero(&addr, sizeof(addr));
-	addr.af = af;
+	addr.aid = aid;
 
 	if (rde_filter(peer->ribid, &fasp, rules, peer, asp, &addr, 0, NULL,
 	    DIR_OUT) == ACTION_DENY) {
@@ -491,6 +499,43 @@ up_generate_default(struct filter_head *rules, struct rde_peer *peer,
 	path_put(asp);
 }
 
+/* generate a EoR marker in the update list. This is a horrible hack. */
+int
+up_generate_marker(struct rde_peer *peer, u_int8_t aid)
+{
+	struct update_attr	*ua;
+	struct update_attr	*na = NULL;
+	struct uplist_attr	*upl = NULL;
+
+	ua = calloc(1, sizeof(struct update_attr));
+	if (ua == NULL)
+		fatal("up_generate_marker");
+
+	upl = &peer->updates[aid];
+
+	/* 1. search for attr */
+	if ((na = RB_FIND(uptree_attr, &peer->up_attrs, ua)) == NULL) {
+		/* 1.1 if not found -> add */
+		TAILQ_INIT(&ua->prefix_h);
+		if (RB_INSERT(uptree_attr, &peer->up_attrs, ua) != NULL) {
+			log_warnx("uptree_attr insert failed");
+			/* cleanup */
+			free(ua);
+			return (-1);
+		}
+		TAILQ_INSERT_TAIL(upl, ua, attr_l);
+		peer->up_acnt++;
+	} else {
+		/* 1.2 if found -> use that, free ua */
+		free(ua);
+		ua = na;
+		/* move to end of update queue */
+		TAILQ_REMOVE(upl, ua, attr_l);
+		TAILQ_INSERT_TAIL(upl, ua, attr_l);
+	}
+	return (0);
+}
+
 u_char	up_attr_buf[4096];
 
 /* only for IPv4 */
@@ -551,28 +596,41 @@ up_get_nexthop(struct rde_peer *peer, struct rde_aspath *a)
 
 int
 up_generate_mp_reach(struct rde_peer *peer, struct update_attr *upa,
-    struct rde_aspath *a, sa_family_t af)
+    struct rde_aspath *a, u_int8_t aid)
 {
 	u_int16_t	tmp;
 
-	switch (af) {
-	case AF_INET6:
+	switch (aid) {
+	case AID_INET6:
 		upa->mpattr_len = 21; /* AFI + SAFI + NH LEN + NH + Reserved */
 		upa->mpattr = malloc(upa->mpattr_len);
 		if (upa->mpattr == NULL)
 			fatal("up_generate_mp_reach");
-		tmp = htons(AFI_IPv6);
+		if (aid2afi(aid, &tmp, &upa->mpattr[2]))
+			fatalx("up_generate_mp_reachi: bad AID");
+		tmp = htons(tmp);
 		memcpy(upa->mpattr, &tmp, sizeof(tmp));
-		upa->mpattr[2] = SAFI_UNICAST;
 		upa->mpattr[3] = sizeof(struct in6_addr);
 		upa->mpattr[20] = 0; /* Reserved must be 0 */
 
 		/* nexthop dance see also up_get_nexthop() */
-		if (peer->conf.ebgp == 0) {
+		if (a->flags & F_NEXTHOP_NOMODIFY) {
+			/* no modify flag set */
+			if (a->nexthop == NULL)
+				memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
+				    sizeof(struct in6_addr));
+			else
+				memcpy(&upa->mpattr[4],
+				    &a->nexthop->exit_nexthop.v6,
+				    sizeof(struct in6_addr));
+		} else if (a->flags & F_NEXTHOP_SELF)
+			memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
+			    sizeof(struct in6_addr));
+		else if (!peer->conf.ebgp) {
 			/* ibgp */
 			if (a->nexthop == NULL ||
-			    (a->nexthop->exit_nexthop.af == AF_INET6 &&
-			    memcmp(&a->nexthop->exit_nexthop.v6,
+			    (a->nexthop->exit_nexthop.aid == AID_INET6 &&
+			    !memcmp(&a->nexthop->exit_nexthop.v6,
 			    &peer->remote_addr.v6, sizeof(struct in6_addr))))
 				memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
 				    sizeof(struct in6_addr));
@@ -603,6 +661,68 @@ up_generate_mp_reach(struct rde_peer *peer, struct update_attr *upa,
 			memcpy(&upa->mpattr[4], &peer->local_v6_addr.v6,
 			    sizeof(struct in6_addr));
 		return (0);
+	case AID_VPN_IPv4:
+		upa->mpattr_len = 17; /* AFI + SAFI + NH LEN + NH + Reserved */
+		upa->mpattr = calloc(upa->mpattr_len, 1);
+		if (upa->mpattr == NULL)
+			fatal("up_generate_mp_reach");
+		if (aid2afi(aid, &tmp, &upa->mpattr[2]))
+			fatalx("up_generate_mp_reachi: bad AID");
+		tmp = htons(tmp);
+		memcpy(upa->mpattr, &tmp, sizeof(tmp));
+		upa->mpattr[3] = sizeof(u_int64_t) + sizeof(struct in_addr);
+
+		/* nexthop dance see also up_get_nexthop() */
+		if (a->flags & F_NEXTHOP_NOMODIFY) {
+			/* no modify flag set */
+			if (a->nexthop == NULL)
+				memcpy(&upa->mpattr[12],
+				    &peer->local_v4_addr.v4,
+				    sizeof(struct in_addr));
+			else
+				/* nexthops are stored as IPv4 addrs */
+				memcpy(&upa->mpattr[12],
+				    &a->nexthop->exit_nexthop.v4,
+				    sizeof(struct in_addr));
+		} else if (a->flags & F_NEXTHOP_SELF)
+			memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
+			    sizeof(struct in_addr));
+		else if (!peer->conf.ebgp) {
+			/* ibgp */
+			if (a->nexthop == NULL ||
+			    (a->nexthop->exit_nexthop.aid == AID_INET &&
+			    !memcmp(&a->nexthop->exit_nexthop.v4,
+			    &peer->remote_addr.v4, sizeof(struct in_addr))))
+				memcpy(&upa->mpattr[12],
+				    &peer->local_v4_addr.v4,
+				    sizeof(struct in_addr));
+			else
+				memcpy(&upa->mpattr[12],
+				    &a->nexthop->exit_nexthop.v4,
+				    sizeof(struct in_addr));
+		} else if (peer->conf.distance == 1) {
+			/* ebgp directly connected */
+			if (a->nexthop != NULL &&
+			    a->nexthop->flags & NEXTHOP_CONNECTED)
+				if (prefix_compare(&peer->remote_addr,
+				    &a->nexthop->nexthop_net,
+				    a->nexthop->nexthop_netlen) == 0) {
+					/*
+					 * nexthop and peer are in the same
+					 * subnet
+					 */
+					memcpy(&upa->mpattr[12],
+					    &a->nexthop->exit_nexthop.v4,
+					    sizeof(struct in_addr));
+					return (0);
+				}
+			memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
+			    sizeof(struct in_addr));
+		} else
+			/* ebgp multihop */
+			memcpy(&upa->mpattr[12], &peer->local_v4_addr.v4,
+			    sizeof(struct in_addr));
+		return (0);
 	default:
 		break;
 	}
@@ -611,7 +731,7 @@ up_generate_mp_reach(struct rde_peer *peer, struct update_attr *upa,
 
 int
 up_generate_attr(struct rde_peer *peer, struct update_attr *upa,
-    struct rde_aspath *a, sa_family_t af)
+    struct rde_aspath *a, u_int8_t aid)
 {
 	struct attr	*oa, *newaggr = NULL;
 	u_char		*pdata;
@@ -643,8 +763,8 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa,
 	wlen += r; len -= r;
 	free(pdata);
 
-	switch (af) {
-	case AF_INET:
+	switch (aid) {
+	case AID_INET:
 		nexthop = up_get_nexthop(peer, a);
 		if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN,
 		    ATTR_NEXTHOP, &nexthop, 4)) == -1)
@@ -659,9 +779,11 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa,
 	/*
 	 * The old MED from other peers MUST not be announced to others
 	 * unless the MED is originating from us or the peer is an IBGP one.
+	 * Only exception are routers with "transparent-as yes" set.
 	 */
 	if (a->flags & F_ATTR_MED && (peer->conf.ebgp == 0 ||
-	    a->flags & F_ATTR_MED_ANNOUNCE)) {
+	    a->flags & F_ATTR_MED_ANNOUNCE ||
+	    peer->conf.flags & PEERFLAG_TRANS_AS)) {
 		tmp32 = htonl(a->med);
 		if ((r = attr_write(up_attr_buf + wlen, len, ATTR_OPTIONAL,
 		    ATTR_MED, &tmp32, 4)) == -1)
@@ -791,7 +913,7 @@ up_generate_attr(struct rde_peer *peer, struct update_attr *upa,
 
 	/* write mp attribute to different buffer */
 	if (ismp)
-		if (up_generate_mp_reach(peer, upa, a, AF_INET6) == -1)
+		if (up_generate_mp_reach(peer, upa, a, aid) == -1)
 			return (-1);
 
 	/* the bgp path attributes are now stored in the global buf */
@@ -810,6 +932,7 @@ up_dump_prefix(u_char *buf, int len, struct uplist_prefix *prefix_head,
 {
 	struct update_prefix	*upp;
 	int			 r, wpos = 0;
+	u_int8_t		 i;
 
 	while ((upp = TAILQ_FIRST(prefix_head)) != NULL) {
 		if ((r = prefix_write(buf + wpos, len - wpos,
@@ -820,13 +943,14 @@ up_dump_prefix(u_char *buf, int len, struct uplist_prefix *prefix_head,
 			log_warnx("dequeuing update failed.");
 		TAILQ_REMOVE(upp->prefix_h, upp, prefix_l);
 		peer->up_pcnt--;
-		if (upp->prefix_h == &peer->withdraws ||
-		    upp->prefix_h == &peer->withdraws6) {
-			peer->up_wcnt--;
-			peer->prefix_sent_withdraw++;
-		} else {
-			peer->up_nlricnt--;
-			peer->prefix_sent_update++;
+		for (i = 0; i < AID_MAX; i++) {
+			if (upp->prefix_h == &peer->withdraws[i]) {
+				peer->up_wcnt--;
+				peer->prefix_sent_withdraw++;
+			} else {
+				peer->up_nlricnt--;
+				peer->prefix_sent_update++;
+			}
 		}
 		free(upp);
 	}
@@ -844,16 +968,21 @@ up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer)
 	 * It is possible that a queued path attribute has no nlri prefix.
 	 * Ignore and remove those path attributes.
 	 */
-	while ((upa = TAILQ_FIRST(&peer->updates)) != NULL)
+	while ((upa = TAILQ_FIRST(&peer->updates[AID_INET])) != NULL)
 		if (TAILQ_EMPTY(&upa->prefix_h)) {
+			attr_len = upa->attr_len;
 			if (RB_REMOVE(uptree_attr, &peer->up_attrs,
 			    upa) == NULL)
 				log_warnx("dequeuing update failed.");
-			TAILQ_REMOVE(&peer->updates, upa, attr_l);
+			TAILQ_REMOVE(&peer->updates[AID_INET], upa, attr_l);
 			free(upa->attr);
 			free(upa->mpattr);
 			free(upa);
 			peer->up_acnt--;
+			/* XXX horrible hack,
+			 * if attr_len is 0, it is a EoR marker */
+			if (attr_len == 0)
+				return (-1);
 		} else
 			break;
 
@@ -884,7 +1013,7 @@ up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer)
 	if (TAILQ_EMPTY(&upa->prefix_h)) {
 		if (RB_REMOVE(uptree_attr, &peer->up_attrs, upa) == NULL)
 			log_warnx("dequeuing update failed.");
-		TAILQ_REMOVE(&peer->updates, upa, attr_l);
+		TAILQ_REMOVE(&peer->updates[AID_INET], upa, attr_l);
 		free(upa->attr);
 		free(upa->mpattr);
 		free(upa);
@@ -895,12 +1024,13 @@ up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer)
 }
 
 u_char *
-up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
+up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer,
+    u_int8_t aid)
 {
 	int		wpos;
 	u_int16_t	datalen, tmp;
 	u_int16_t	attrlen = 2;	/* attribute header (without len) */
-	u_int8_t	flags = ATTR_OPTIONAL;
+	u_int8_t	flags = ATTR_OPTIONAL, safi;
 
 	/*
 	 * reserve space for withdraw len, attr len, the attribute header
@@ -912,7 +1042,7 @@ up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 		return (NULL);
 
 	datalen = up_dump_prefix(buf + wpos, *len - wpos,
-	    &peer->withdraws6, peer);
+	    &peer->withdraws[aid], peer);
 	if (datalen == 0)
 		return (NULL);
 
@@ -920,9 +1050,11 @@ up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 
 	/* prepend header, need to do it reverse */
 	/* safi & afi */
-	buf[--wpos] = SAFI_UNICAST;
+	if (aid2afi(aid, &tmp, &safi))
+		fatalx("up_dump_mp_unreach: bad AID");
+	buf[--wpos] = safi;
 	wpos -= sizeof(u_int16_t);
-	tmp = htons(AFI_IPv6);
+	tmp = htons(tmp);
 	memcpy(buf + wpos, &tmp, sizeof(u_int16_t));
 
 	/* attribute length */
@@ -959,33 +1091,39 @@ up_dump_mp_unreach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 	return (buf + wpos);
 }
 
-u_char *
-up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
+int
+up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer,
+    u_int8_t aid)
 {
 	struct update_attr	*upa;
 	int			wpos;
-	u_int16_t		datalen, tmp;
+	u_int16_t		attr_len, datalen, tmp;
 	u_int8_t		flags = ATTR_OPTIONAL;
 
 	/*
 	 * It is possible that a queued path attribute has no nlri prefix.
 	 * Ignore and remove those path attributes.
 	 */
-	while ((upa = TAILQ_FIRST(&peer->updates6)) != NULL)
+	while ((upa = TAILQ_FIRST(&peer->updates[aid])) != NULL)
 		if (TAILQ_EMPTY(&upa->prefix_h)) {
+			attr_len = upa->attr_len;
 			if (RB_REMOVE(uptree_attr, &peer->up_attrs,
 			    upa) == NULL)
 				log_warnx("dequeuing update failed.");
-			TAILQ_REMOVE(&peer->updates6, upa, attr_l);
+			TAILQ_REMOVE(&peer->updates[aid], upa, attr_l);
 			free(upa->attr);
 			free(upa->mpattr);
 			free(upa);
 			peer->up_acnt--;
+			/* XXX horrible hack,
+			 * if attr_len is 0, it is a EoR marker */
+			if (attr_len == 0)
+				return (-1);
 		} else
 			break;
 
 	if (upa == NULL)
-		return (NULL);
+		return (-2);
 
 	/*
 	 * reserve space for attr len, the attributes, the
@@ -993,12 +1131,12 @@ up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 	 */
 	wpos = 2 + 2 + upa->attr_len + 4 + upa->mpattr_len;
 	if (*len < wpos)
-		return (NULL);
+		return (-2);
 
 	datalen = up_dump_prefix(buf + wpos, *len - wpos,
 	    &upa->prefix_h, peer);
 	if (datalen == 0)
-		return (NULL);
+		return (-2);
 
 	if (upa->mpattr_len == 0 || upa->mpattr == NULL)
 		fatalx("mulitprotocol update without MP attrs");
@@ -1038,7 +1176,7 @@ up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 	if (TAILQ_EMPTY(&upa->prefix_h)) {
 		if (RB_REMOVE(uptree_attr, &peer->up_attrs, upa) == NULL)
 			log_warnx("dequeuing update failed.");
-		TAILQ_REMOVE(&peer->updates6, upa, attr_l);
+		TAILQ_REMOVE(&peer->updates[aid], upa, attr_l);
 		free(upa->attr);
 		free(upa->mpattr);
 		free(upa);
@@ -1046,6 +1184,5 @@ up_dump_mp_reach(u_char *buf, u_int16_t *len, struct rde_peer *peer)
 	}
 
 	*len = datalen + 4;
-	return (buf + wpos);
+	return (wpos);
 }
-
diff --git a/bgpd/session.c b/bgpd/session.c
index 4f7f787..3cdafc8 100644
--- a/bgpd/session.c
+++ b/bgpd/session.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: session.c,v 1.293 2009/06/07 05:56:24 eric Exp $ */
+/*	$OpenBSD: session.c,v 1.304 2010/01/05 08:49:57 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
@@ -21,6 +21,8 @@
 
 #include <sys/mman.h>
 #include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 #include <sys/un.h>
 #include <net/if_types.h>
 #include <netinet/in.h>
@@ -50,7 +52,12 @@
 #define PFD_PIPE_ROUTE_CTL	2
 #define PFD_SOCK_CTL		3
 #define PFD_SOCK_RCTL		4
-#define PFD_LISTENERS_START	5
+#define PFD_SOCK_PFKEY		5
+#define PFD_LISTENERS_START	6
+
+#if defined(__FreeBSD__) /* FreeBSD has no LINK_STATE_IS_UP macro. */
+#define LINK_STATE_IS_UP(_s)  ((_s) >= LINK_STATE_UP)
+#endif /* defined(__FreeBSD__) */ 
 
 void	session_sighdlr(int);
 int	setup_listeners(u_int *);
@@ -65,9 +72,8 @@ void	session_accept(int);
 int	session_connect(struct peer *);
 void	session_tcp_established(struct peer *);
 void	session_capa_ann_none(struct peer *);
-int	session_capa_add(struct peer *, struct buf *, u_int8_t, u_int8_t,
-	    u_int8_t *);
-int	session_capa_add_mp(struct buf *, u_int16_t, u_int8_t);
+int	session_capa_add(struct ibuf *, u_int8_t, u_int8_t);
+int	session_capa_add_mp(struct ibuf *, u_int8_t);
 struct bgp_msg	*session_newmsg(enum msg_type, u_int16_t);
 int	session_sendmsg(struct bgp_msg *, struct peer *);
 void	session_open(struct peer *);
@@ -75,7 +81,7 @@ void	session_keepalive(struct peer *);
 void	session_update(u_int32_t, void *, size_t);
 void	session_notification(struct peer *, u_int8_t, u_int8_t, void *,
 	    ssize_t);
-void	session_rrefresh(struct peer *, u_int16_t, u_int8_t);
+void	session_rrefresh(struct peer *, u_int8_t);
 int	session_dispatch_msg(struct pollfd *, struct peer *);
 int	parse_header(struct peer *, u_char *, u_int16_t *, u_int8_t *);
 int	parse_open(struct peer *);
@@ -83,22 +89,22 @@ int	parse_update(struct peer *);
 int	parse_refresh(struct peer *);
 int	parse_notification(struct peer *);
 int	parse_capabilities(struct peer *, u_char *, u_int16_t, u_int32_t *);
+int	capa_neg_calc(struct peer *);
 void	session_dispatch_imsg(struct imsgbuf *, int, u_int *);
 void	session_up(struct peer *);
 void	session_down(struct peer *);
 void	session_demote(struct peer *, int);
 
-int			 la_cmp(struct listen_addr *, struct listen_addr *);
-struct peer		*getpeerbyip(struct sockaddr *);
-int			 session_match_mask(struct peer *, struct sockaddr *);
-struct peer		*getpeerbyid(u_int32_t);
-static struct sockaddr	*addr2sa(struct bgpd_addr *, u_int16_t);
+int		 la_cmp(struct listen_addr *, struct listen_addr *);
+struct peer	*getpeerbyip(struct sockaddr *);
+int		 session_match_mask(struct peer *, struct bgpd_addr *);
+struct peer	*getpeerbyid(u_int32_t);
 
-struct bgpd_config	*conf, *nconf = NULL;
+struct bgpd_config	*conf, *nconf;
 struct bgpd_sysdep	 sysdep;
-struct peer		*npeers;
-volatile sig_atomic_t	 session_quit = 0;
-int			 pending_reconf = 0;
+struct peer		*peers, *npeers;
+volatile sig_atomic_t	 session_quit;
+int			 pending_reconf;
 int			 csock = -1, rcsock = -1;
 u_int			 peer_cnt;
 struct imsgbuf		*ibuf_rde;
@@ -175,12 +181,11 @@ setup_listeners(u_int *la_cnt)
 }
 
 pid_t
-session_main(struct bgpd_config *config, struct peer *cpeers,
-    struct network_head *net_l, struct filter_head *rules,
-    struct mrt_head *m_l, struct rib_names *rib_l, int pipe_m2s[2],
-    int pipe_s2r[2], int pipe_m2r[2], int pipe_s2rctl[2])
+session_main(int pipe_m2s[2], int pipe_s2r[2], int pipe_m2r[2],
+    int pipe_s2rctl[2])
 {
-	int			 nfds, timeout;
+	struct rlimit		 rl;
+	int			 nfds, timeout, pfkeysock;
 	unsigned int		 i, j, idx_peers, idx_listeners, idx_mrts;
 	pid_t			 pid;
 	u_int			 pfd_elms = 0, peer_l_elms = 0, mrt_l_elms = 0;
@@ -189,19 +194,13 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 	u_int32_t		 ctl_queued;
 	struct passwd		*pw;
 	struct peer		*p, **peer_l = NULL, *last, *next;
-	struct network		*net;
-	struct mrt		*m, **mrt_l = NULL;
-	struct filter_rule	*r;
+	struct mrt		*m, *xm, **mrt_l = NULL;
 	struct pollfd		*pfd = NULL;
 	struct ctl_conn		*ctl_conn;
 	struct listen_addr	*la;
-	struct rde_rib		*rr;
 	void			*newp;
 	short			 events;
 
-	conf = config;
-	peers = cpeers;
-
 	switch (pid = fork()) {
 	case -1:
 		fatal("cannot fork");
@@ -211,13 +210,6 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 		return (pid);
 	}
 
-	/* control socket is outside chroot */
-	if ((csock = control_init(0, conf->csock)) == -1)
-		fatalx("control socket setup failed");
-	if (conf->rcsock != NULL &&
-	    (rcsock = control_init(1, conf->rcsock)) == -1)
-		fatalx("control socket setup failed");
-
 	if ((pw = getpwnam(BGPD_USER)) == NULL)
 		fatal(NULL);
 
@@ -229,28 +221,31 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 	setproctitle("session engine");
 	bgpd_process = PROC_SE;
 
-	if (pfkey_init(&sysdep) == -1)
-		fatalx("pfkey setup failed");
+	if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
+		fatal("getrlimit");
+	rl.rlim_cur = rl.rlim_max;
+	if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+		fatal("setrlimit");
+
+	pfkeysock = pfkey_init(&sysdep);
 
 	if (setgroups(1, &pw->pw_gid) ||
 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
 		fatal("can't drop privileges");
 
-	listener_cnt = 0;
-	setup_listeners(&listener_cnt);
-
 	signal(SIGTERM, session_sighdlr);
 	signal(SIGINT, session_sighdlr);
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGHUP, SIG_IGN);
-	log_info("session engine ready");
+	signal(SIGALRM, SIG_IGN);
+	signal(SIGUSR1, SIG_IGN);
+
 	close(pipe_m2s[0]);
 	close(pipe_s2r[1]);
 	close(pipe_s2rctl[1]);
 	close(pipe_m2r[0]);
 	close(pipe_m2r[1]);
-	init_conf(conf);
 	if ((ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL ||
 	    (ibuf_rde_ctl = malloc(sizeof(struct imsgbuf))) == NULL ||
 	    (ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
@@ -258,37 +253,21 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 	imsg_init(ibuf_rde, pipe_s2r[0]);
 	imsg_init(ibuf_rde_ctl, pipe_s2rctl[0]);
 	imsg_init(ibuf_main, pipe_m2s[1]);
+
 	TAILQ_INIT(&ctl_conns);
-	control_listen(csock);
-	control_listen(rcsock);
 	LIST_INIT(&mrthead);
+	listener_cnt = 0;
 	peer_cnt = 0;
 	ctl_cnt = 0;
 
-	/* filter rules are not used in the SE */
-	while ((r = TAILQ_FIRST(rules)) != NULL) {
-		TAILQ_REMOVE(rules, r, entry);
-		free(r);
-	}
-	free(rules);
-
-	/* network list is not used in the SE */
-	while ((net = TAILQ_FIRST(net_l)) != NULL) {
-		TAILQ_REMOVE(net_l, net, entry);
-		filterset_free(&net->net.attrset);
-		free(net);
-	}
+	if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL)
+		fatal(NULL);
+	if ((conf->listen_addrs = calloc(1, sizeof(struct listen_addrs))) ==
+	    NULL)
+		fatal(NULL);
+	TAILQ_INIT(conf->listen_addrs);
 
-	/* main mrt list is not used in the SE */
-	while ((m = LIST_FIRST(m_l)) != NULL) {
-		LIST_REMOVE(m, entry);
-		free(m);
-	}
-	/* rib names not used in the SE */
-	while ((rr = SIMPLEQ_FIRST(&ribnames))) {
-		SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
-		free(rr);
-	}
+	log_info("session engine ready");
 
 	while (session_quit == 0) {
 		/* check for peers to be initialized or deleted */
@@ -308,8 +287,9 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 
 				/* reinit due? */
 				if (p->conf.reconf_action == RECONF_REINIT) {
-					bgp_fsm(p, EVNT_STOP);
-					timer_set(p, Timer_IdleHold, 0);
+					session_stop(p, ERR_CEASE_ADMIN_RESET);
+					if (!p->conf.down)
+						timer_set(p, Timer_IdleHold, 0);
 				}
 
 				/* deletion due? */
@@ -317,7 +297,7 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 					if (p->demoted)
 						session_demote(p, -1);
 					p->conf.demote_group[0] = 0;
-					bgp_fsm(p, EVNT_STOP);
+					session_stop(p, ERR_CEASE_PEER_UNCONF);
 					log_peer_warnx(&p->conf, "removed");
 					if (last != NULL)
 						last->next = next;
@@ -346,9 +326,17 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 		}
 
 		mrt_cnt = 0;
-		LIST_FOREACH(m, &mrthead, entry)
+		for (m = LIST_FIRST(&mrthead); m != NULL; m = xm) {
+			xm = LIST_NEXT(m, entry);
+			if (m->state == MRT_STATE_REMOVE) {
+				mrt_clean(m);
+				LIST_REMOVE(m, entry);
+				free(m);
+				continue;
+			}
 			if (m->wbuf.queued)
 				mrt_cnt++;
+		}
 
 		if (mrt_cnt > mrt_l_elms) {
 			if ((newp = realloc(mrt_l, sizeof(struct mrt *) *
@@ -394,14 +382,19 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 		if (ctl_queued < SESSION_CTL_QUEUE_MAX)
 			/*
 			 * Do not act as unlimited buffer. Don't read in more
-			 * messages if the ctl sockets are getting full. 
+			 * messages if the ctl sockets are getting full.
 			 */
 			pfd[PFD_PIPE_ROUTE_CTL].events = POLLIN;
 		pfd[PFD_SOCK_CTL].fd = csock;
 		pfd[PFD_SOCK_CTL].events = POLLIN;
 		pfd[PFD_SOCK_RCTL].fd = rcsock;
 		pfd[PFD_SOCK_RCTL].events = POLLIN;
-
+		pfd[PFD_SOCK_PFKEY].fd = pfkeysock;
+#if !defined(__FreeBSD__)
+		pfd[PFD_SOCK_PFKEY].events = POLLIN;
+#else
+		pfd[PFD_SOCK_PFKEY].events = 0;
+#endif
 		i = PFD_LISTENERS_START;
 		TAILQ_FOREACH(la, conf->listen_addrs, entry) {
 			pfd[i].fd = la->fd;
@@ -534,6 +527,14 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 			ctl_cnt += control_accept(rcsock, 1);
 		}
 
+		if (nfds > 0 && pfd[PFD_SOCK_PFKEY].revents & POLLIN) {
+			nfds--;
+			if (pfkey_read(pfkeysock, NULL) == -1) {
+				log_warnx("pfkey_read failed, exiting...");
+				session_quit = 1;
+			}
+		}
+
 		for (j = PFD_LISTENERS_START; nfds > 0 && j < idx_listeners;
 		    j++)
 			if (pfd[j].revents & POLLIN) {
@@ -557,7 +558,7 @@ session_main(struct bgpd_config *config, struct peer *cpeers,
 
 	while ((p = peers) != NULL) {
 		peers = p->next;
-		bgp_fsm(p, EVNT_STOP);
+		session_stop(p, ERR_CEASE_ADMIN_DOWN);
 		pfkey_remove(p);
 		free(p);
 	}
@@ -643,10 +644,9 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			timer_stop(peer, Timer_IdleHold);
 
 			/* allocate read buffer */
-			peer->rbuf = calloc(1, sizeof(struct buf_read));
+			peer->rbuf = calloc(1, sizeof(struct ibuf_read));
 			if (peer->rbuf == NULL)
 				fatal(NULL);
-			peer->rbuf->wpos = 0;
 
 			/* init write buffer */
 			msgbuf_init(&peer->wbuf);
@@ -746,7 +746,6 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			/* ignore */
 			break;
 		case EVNT_STOP:
-			session_notification(peer, ERR_CEASE, 0, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		case EVNT_CON_CLOSED:
@@ -780,7 +779,8 @@ bgp_fsm(struct peer *peer, enum session_events event)
 				change_state(peer, STATE_IDLE, event);
 			break;
 		default:
-			session_notification(peer, ERR_FSM, 0, NULL, 0);
+			session_notification(peer,
+			    ERR_FSM, ERR_FSM_UNEX_OPENSENT, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		}
@@ -791,7 +791,6 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			/* ignore */
 			break;
 		case EVNT_STOP:
-			session_notification(peer, ERR_CEASE, 0, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		case EVNT_CON_CLOSED:
@@ -815,7 +814,8 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			change_state(peer, STATE_IDLE, event);
 			break;
 		default:
-			session_notification(peer, ERR_FSM, 0, NULL, 0);
+			session_notification(peer,
+			    ERR_FSM, ERR_FSM_UNEX_OPENCONFIRM, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		}
@@ -826,7 +826,6 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			/* ignore */
 			break;
 		case EVNT_STOP:
-			session_notification(peer, ERR_CEASE, 0, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		case EVNT_CON_CLOSED:
@@ -856,7 +855,8 @@ bgp_fsm(struct peer *peer, enum session_events event)
 			change_state(peer, STATE_IDLE, event);
 			break;
 		default:
-			session_notification(peer, ERR_FSM, 0, NULL, 0);
+			session_notification(peer,
+			    ERR_FSM, ERR_FSM_UNEX_ESTABLISHED, NULL, 0);
 			change_state(peer, STATE_IDLE, event);
 			break;
 		}
@@ -923,6 +923,7 @@ change_state(struct peer *peer, enum session_state state,
 		timer_stop(peer, Timer_ConnectRetry);
 		timer_stop(peer, Timer_Keepalive);
 		timer_stop(peer, Timer_Hold);
+		timer_stop(peer, Timer_IdleHold);
 		timer_stop(peer, Timer_IdleHoldReset);
 		session_close_connection(peer);
 		msgbuf_clear(&peer->wbuf);
@@ -1069,7 +1070,7 @@ session_connect(struct peer *peer)
 	if (peer->fd != -1)
 		return (-1);
 
-	if ((peer->fd = socket(peer->conf.remote_addr.af, SOCK_STREAM,
+	if ((peer->fd = socket(aid2af(peer->conf.remote_addr.aid), SOCK_STREAM,
 	    IPPROTO_TCP)) == -1) {
 		log_peer_warn(&peer->conf, "session_connect socket");
 		bgp_fsm(peer, EVNT_CON_OPENFAIL);
@@ -1100,8 +1101,7 @@ session_connect(struct peer *peer)
 	peer->wbuf.fd = peer->fd;
 
 	/* if update source is set we need to bind() */
-	if (peer->conf.local_addr.af) {
-		sa = addr2sa(&peer->conf.local_addr, 0);
+	if ((sa = addr2sa(&peer->conf.local_addr, 0)) != NULL) {
 		if (bind(peer->fd, sa, sa->sa_len) == -1) {
 			log_peer_warn(&peer->conf, "session_connect bind");
 			bgp_fsm(peer, EVNT_CON_OPENFAIL);
@@ -1139,42 +1139,50 @@ session_setup_socket(struct peer *p)
 	int	nodelay = 1;
 	int	bsize;
 
-	if (p->conf.ebgp && p->conf.remote_addr.af == AF_INET) {
-		/* set TTL to foreign router's distance - 1=direct n=multihop
-		   with ttlsec, we always use 255 */
-		if (p->conf.ttlsec) {
-			ttl = 256 - p->conf.distance;
-			if (setsockopt(p->fd, IPPROTO_IP, IP_MINTTL, &ttl,
+	switch (p->conf.remote_addr.aid) {
+	case AID_INET:
+		/* set precedence, see RFC 1771 appendix 5 */
+		if (setsockopt(p->fd, IPPROTO_IP, IP_TOS, &pre, sizeof(pre)) ==
+		    -1) {
+			log_peer_warn(&p->conf,
+			    "session_setup_socket setsockopt TOS");
+			return (-1);
+		}
+
+		if (p->conf.ebgp) {
+			/* set TTL to foreign router's distance
+			   1=direct n=multihop with ttlsec, we always use 255 */
+			if (p->conf.ttlsec) {
+				ttl = 256 - p->conf.distance;
+				if (setsockopt(p->fd, IPPROTO_IP, IP_MINTTL,
+				    &ttl, sizeof(ttl)) == -1) {
+					log_peer_warn(&p->conf,
+					    "session_setup_socket: "
+					    "setsockopt MINTTL");
+					return (-1);
+				}
+				ttl = 255;
+			}
+
+			if (setsockopt(p->fd, IPPROTO_IP, IP_TTL, &ttl,
 			    sizeof(ttl)) == -1) {
 				log_peer_warn(&p->conf,
-				    "session_setup_socket setsockopt MINTTL");
+				    "session_setup_socket setsockopt TTL");
 				return (-1);
 			}
-			ttl = 255;
 		}
-
-		if (setsockopt(p->fd, IPPROTO_IP, IP_TTL, &ttl,
-		    sizeof(ttl)) == -1) {
-			log_peer_warn(&p->conf,
-			    "session_setup_socket setsockopt TTL");
-			return (-1);
-		}
-	}
-
-	if (p->conf.ebgp && p->conf.remote_addr.af == AF_INET6)
-		/* set hoplimit to foreign router's distance */
-		if (setsockopt(p->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl,
-		    sizeof(ttl)) == -1) {
-			log_peer_warn(&p->conf,
-			    "session_setup_socket setsockopt hoplimit");
-			return (-1);
+		break;
+	case AID_INET6:
+		if (p->conf.ebgp) {
+			/* set hoplimit to foreign router's distance */
+			if (setsockopt(p->fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+			    &ttl, sizeof(ttl)) == -1) {
+				log_peer_warn(&p->conf,
+				    "session_setup_socket setsockopt hoplimit");
+				return (-1);
+			}
 		}
-
-	/* if ttlsec is in use, set minttl */
-	if (p->conf.ttlsec) {
-		ttl = 256 - p->conf.distance;
-		setsockopt(p->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl));
-
+		break;
 	}
 
 	/* set TCP_NODELAY */
@@ -1185,14 +1193,6 @@ session_setup_socket(struct peer *p)
 		return (-1);
 	}
 
-	/* set precedence, see RFC 1771 appendix 5 */
-	if (p->conf.remote_addr.af == AF_INET &&
-	    setsockopt(p->fd, IPPROTO_IP, IP_TOS, &pre, sizeof(pre)) == -1) {
-		log_peer_warn(&p->conf,
-		    "session_setup_socket setsockopt TOS");
-		return (-1);
-	}
-
 	/* only increase bufsize (and thus window) if md5 or ipsec is in use */
 	if (p->conf.auth.method != AUTH_NONE) {
 		/* try to increase bufsize. no biggie if it fails */
@@ -1244,40 +1244,32 @@ session_tcp_established(struct peer *peer)
 void
 session_capa_ann_none(struct peer *peer)
 {
-	peer->capa.ann.mp_v4 = SAFI_NONE;
-	peer->capa.ann.mp_v4 = SAFI_NONE;
-	peer->capa.ann.refresh = 0;
-	peer->capa.ann.restart = 0;
-	peer->capa.ann.as4byte = 0;
+	bzero(&peer->capa.ann, sizeof(peer->capa.ann));
 }
 
 int
-session_capa_add(struct peer *p, struct buf *opb, u_int8_t capa_code,
-    u_int8_t capa_len, u_int8_t *optparamlen)
+session_capa_add(struct ibuf *opb, u_int8_t capa_code, u_int8_t capa_len)
 {
-	u_int8_t	op_type, op_len, tot_len, errs = 0;
-
-	op_type = OPT_PARAM_CAPABILITIES;
-	op_len = sizeof(capa_code) + sizeof(capa_len) + capa_len;
-	tot_len = sizeof(op_type) + sizeof(op_len) + op_len;
-	errs += buf_add(opb, &op_type, sizeof(op_type));
-	errs += buf_add(opb, &op_len, sizeof(op_len));
-	errs += buf_add(opb, &capa_code, sizeof(capa_code));
-	errs += buf_add(opb, &capa_len, sizeof(capa_len));
-	*optparamlen += tot_len;
+	int errs = 0;
+
+	errs += ibuf_add(opb, &capa_code, sizeof(capa_code));
+	errs += ibuf_add(opb, &capa_len, sizeof(capa_len));
 	return (errs);
 }
 
 int
-session_capa_add_mp(struct buf *buf, u_int16_t afi, u_int8_t safi)
+session_capa_add_mp(struct ibuf *buf, u_int8_t aid)
 {
-	u_int8_t		 pad = 0;
+	u_int8_t		 safi, pad = 0;
+	u_int16_t		 afi;
 	int			 errs = 0;
 
+	if (aid2afi(aid, &afi, &safi) == -1)
+		fatalx("session_capa_add_mp: bad afi/safi pair");
 	afi = htons(afi);
-	errs += buf_add(buf, &afi, sizeof(afi));
-	errs += buf_add(buf, &pad, sizeof(pad));
-	errs += buf_add(buf, &safi, sizeof(safi));
+	errs += ibuf_add(buf, &afi, sizeof(afi));
+	errs += ibuf_add(buf, &pad, sizeof(pad));
+	errs += ibuf_add(buf, &safi, sizeof(safi));
 
 	return (errs);
 }
@@ -1287,23 +1279,22 @@ session_newmsg(enum msg_type msgtype, u_int16_t len)
 {
 	struct bgp_msg		*msg;
 	struct msg_header	 hdr;
-	struct buf		*buf;
+	struct ibuf		*buf;
 	int			 errs = 0;
 
 	memset(&hdr.marker, 0xff, sizeof(hdr.marker));
 	hdr.len = htons(len);
 	hdr.type = msgtype;
 
-	if ((buf = buf_open(len)) == NULL)
+	if ((buf = ibuf_open(len)) == NULL)
 		return (NULL);
 
-	errs += buf_add(buf, &hdr.marker, sizeof(hdr.marker));
-	errs += buf_add(buf, &hdr.len, sizeof(hdr.len));
-	errs += buf_add(buf, &hdr.type, sizeof(hdr.type));
+	errs += ibuf_add(buf, &hdr.marker, sizeof(hdr.marker));
+	errs += ibuf_add(buf, &hdr.len, sizeof(hdr.len));
+	errs += ibuf_add(buf, &hdr.type, sizeof(hdr.type));
 
-	if (errs > 0 ||
-	    (msg = calloc(1, sizeof(*msg))) == NULL) {
-		buf_free(buf);
+	if (errs || (msg = calloc(1, sizeof(*msg))) == NULL) {
+		ibuf_free(buf);
 		return (NULL);
 	}
 
@@ -1329,7 +1320,7 @@ session_sendmsg(struct bgp_msg *msg, struct peer *p)
 			mrt_dump_bgp_msg(mrt, msg->buf->buf, msg->len, p);
 	}
 
-	buf_close(&p->wbuf, msg->buf);
+	ibuf_close(&p->wbuf, msg->buf);
 	free(msg);
 	return (0);
 }
@@ -1338,40 +1329,38 @@ void
 session_open(struct peer *p)
 {
 	struct bgp_msg		*buf;
-	struct buf		*opb;
+	struct ibuf		*opb;
 	struct msg_open		 msg;
 	u_int16_t		 len;
-	u_int8_t		 optparamlen = 0;
-	u_int			 errs = 0;
+	u_int8_t		 i, op_type, optparamlen = 0;
+	int			 errs = 0;
 
 
-	if ((opb = buf_dynamic(0, MAX_PKTSIZE - MSGSIZE_OPEN_MIN)) == NULL) {
+	if ((opb = ibuf_dynamic(0, UCHAR_MAX - sizeof(op_type) -
+	    sizeof(optparamlen))) == NULL) {
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
 	}
 
 	/* multiprotocol extensions, RFC 4760 */
-	if (p->capa.ann.mp_v4) {	/* 4 bytes data */
-		errs += session_capa_add(p, opb, CAPA_MP, 4, &optparamlen);
-		errs += session_capa_add_mp(opb, AFI_IPv4, p->capa.ann.mp_v4);
-	}
-	if (p->capa.ann.mp_v6) {	/* 4 bytes data */
-		errs += session_capa_add(p, opb, CAPA_MP, 4, &optparamlen);
-		errs += session_capa_add_mp(opb, AFI_IPv6, p->capa.ann.mp_v6);
-	}
+	for (i = 0; i < AID_MAX; i++)
+		if (p->capa.ann.mp[i]) {	/* 4 bytes data */
+			errs += session_capa_add(opb, CAPA_MP, 4);
+			errs += session_capa_add_mp(opb, i);
+		}
 
 	/* route refresh, RFC 2918 */
 	if (p->capa.ann.refresh)	/* no data */
-		errs += session_capa_add(p, opb, CAPA_REFRESH, 0, &optparamlen);
+		errs += session_capa_add(opb, CAPA_REFRESH, 0);
 
 	/* End-of-RIB marker, RFC 4724 */
 	if (p->capa.ann.restart) {	/* 2 bytes data */
 		u_char		c[2];
 
-		bzero(&c, 2);
 		c[0] = 0x80; /* we're always restarting */
-		errs += session_capa_add(p, opb, CAPA_RESTART, 2, &optparamlen);
-		errs += buf_add(opb, &c, 2);
+		c[1] = 0;
+		errs += session_capa_add(opb, CAPA_RESTART, 2);
+		errs += ibuf_add(opb, &c, 2);
 	}
 
 	/* 4-bytes AS numbers, draft-ietf-idr-as4bytes-13 */
@@ -1379,13 +1368,17 @@ session_open(struct peer *p)
 		u_int32_t	nas;
 
 		nas = htonl(conf->as);
-		errs += session_capa_add(p, opb, CAPA_AS4BYTE, 4, &optparamlen);
-		errs += buf_add(opb, &nas, 4);
+		errs += session_capa_add(opb, CAPA_AS4BYTE, sizeof(nas));
+		errs += ibuf_add(opb, &nas, sizeof(nas));
 	}
 
+	if (ibuf_size(opb))
+		optparamlen = ibuf_size(opb) + sizeof(op_type) +
+		    sizeof(optparamlen);
+
 	len = MSGSIZE_OPEN_MIN + optparamlen;
 	if (errs || (buf = session_newmsg(OPEN, len)) == NULL) {
-		buf_free(opb);
+		ibuf_free(opb);
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
 	}
@@ -1399,19 +1392,24 @@ session_open(struct peer *p)
 	msg.bgpid = conf->bgpid;	/* is already in network byte order */
 	msg.optparamlen = optparamlen;
 
-	errs += buf_add(buf->buf, &msg.version, sizeof(msg.version));
-	errs += buf_add(buf->buf, &msg.myas, sizeof(msg.myas));
-	errs += buf_add(buf->buf, &msg.holdtime, sizeof(msg.holdtime));
-	errs += buf_add(buf->buf, &msg.bgpid, sizeof(msg.bgpid));
-	errs += buf_add(buf->buf, &msg.optparamlen, sizeof(msg.optparamlen));
+	errs += ibuf_add(buf->buf, &msg.version, sizeof(msg.version));
+	errs += ibuf_add(buf->buf, &msg.myas, sizeof(msg.myas));
+	errs += ibuf_add(buf->buf, &msg.holdtime, sizeof(msg.holdtime));
+	errs += ibuf_add(buf->buf, &msg.bgpid, sizeof(msg.bgpid));
+	errs += ibuf_add(buf->buf, &msg.optparamlen, sizeof(msg.optparamlen));
 
-	if (optparamlen)
-		errs += buf_add(buf->buf, opb->buf, optparamlen);
+	if (optparamlen) {
+		op_type = OPT_PARAM_CAPABILITIES;
+		optparamlen = ibuf_size(opb);
+		errs += ibuf_add(buf->buf, &op_type, sizeof(op_type));
+		errs += ibuf_add(buf->buf, &optparamlen, sizeof(optparamlen));
+		errs += ibuf_add(buf->buf, opb->buf, ibuf_size(opb));
+	}
 
-	buf_free(opb);
+	ibuf_free(opb);
 
-	if (errs > 0) {
-		buf_free(buf->buf);
+	if (errs) {
+		ibuf_free(buf->buf);
 		free(buf);
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
@@ -1459,8 +1457,8 @@ session_update(u_int32_t peerid, void *data, size_t datalen)
 		return;
 	}
 
-	if (buf_add(buf->buf, data, datalen)) {
-		buf_free(buf->buf);
+	if (ibuf_add(buf->buf, data, datalen)) {
+		ibuf_free(buf->buf);
 		free(buf);
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
@@ -1480,29 +1478,27 @@ session_notification(struct peer *p, u_int8_t errcode, u_int8_t subcode,
     void *data, ssize_t datalen)
 {
 	struct bgp_msg		*buf;
-	u_int			 errs = 0;
-	u_int8_t		 null8 = 0;
+	int			 errs = 0;
 
 	if (p->stats.last_sent_errcode)	/* some notification already sent */
 		return;
 
+	log_notification(p, errcode, subcode, data, datalen, "sending");
+
 	if ((buf = session_newmsg(NOTIFICATION,
 	    MSGSIZE_NOTIFICATION_MIN + datalen)) == NULL) {
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
 	}
 
-	errs += buf_add(buf->buf, &errcode, sizeof(errcode));
-	if (errcode == ERR_CEASE)
-		errs += buf_add(buf->buf, &null8, sizeof(null8));
-	else
-		errs += buf_add(buf->buf, &subcode, sizeof(subcode));
+	errs += ibuf_add(buf->buf, &errcode, sizeof(errcode));
+	errs += ibuf_add(buf->buf, &subcode, sizeof(subcode));
 
 	if (datalen > 0)
-		errs += buf_add(buf->buf, data, datalen);
+		errs += ibuf_add(buf->buf, data, datalen);
 
-	if (errs > 0) {
-		buf_free(buf->buf);
+	if (errs) {
+		ibuf_free(buf->buf);
 		free(buf);
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
@@ -1521,23 +1517,29 @@ session_notification(struct peer *p, u_int8_t errcode, u_int8_t subcode,
 int
 session_neighbor_rrefresh(struct peer *p)
 {
+	u_int8_t	i;
+
 	if (!p->capa.peer.refresh)
 		return (-1);
 
-	if (p->capa.peer.mp_v4 != SAFI_NONE)
-		session_rrefresh(p, AFI_IPv4, p->capa.peer.mp_v4);
-	if (p->capa.peer.mp_v6 != SAFI_NONE)
-		session_rrefresh(p, AFI_IPv6, p->capa.peer.mp_v6);
+	for (i = 0; i < AID_MAX; i++) {
+		if (p->capa.peer.mp[i] != 0)
+			session_rrefresh(p, i);
+	}
 
 	return (0);
 }
 
 void
-session_rrefresh(struct peer *p, u_int16_t afi, u_int8_t safi)
+session_rrefresh(struct peer *p, u_int8_t aid)
 {
 	struct bgp_msg		*buf;
 	int			 errs = 0;
-	u_int8_t		 null8 = 0;
+	u_int16_t		 afi;
+	u_int8_t		 safi, null8 = 0;
+
+	if (aid2afi(aid, &afi, &safi) == -1)
+		fatalx("session_rrefresh: bad afi/safi pair");
 
 	if ((buf = session_newmsg(RREFRESH, MSGSIZE_RREFRESH)) == NULL) {
 		bgp_fsm(p, EVNT_CON_FATAL);
@@ -1545,12 +1547,12 @@ session_rrefresh(struct peer *p, u_int16_t afi, u_int8_t safi)
 	}
 
 	afi = htons(afi);
-	errs += buf_add(buf->buf, &afi, sizeof(afi));
-	errs += buf_add(buf->buf, &null8, sizeof(null8));
-	errs += buf_add(buf->buf, &safi, sizeof(safi));
+	errs += ibuf_add(buf->buf, &afi, sizeof(afi));
+	errs += ibuf_add(buf->buf, &null8, sizeof(null8));
+	errs += ibuf_add(buf->buf, &safi, sizeof(safi));
 
-	if (errs > 0) {
-		buf_free(buf->buf);
+	if (errs) {
+		ibuf_free(buf->buf);
 		free(buf);
 		bgp_fsm(p, EVNT_CON_FATAL);
 		return;
@@ -1853,12 +1855,6 @@ parse_open(struct peer *peer)
 	p += sizeof(short_as);
 	as = peer->short_as = ntohs(short_as);
 
-	/* if remote-as is zero and it's a cloned neighbor, accept any */
-	if (peer->conf.cloned && !peer->conf.remote_as && as != AS_TRANS) {
-		peer->conf.remote_as = as;
-		peer->conf.ebgp = (peer->conf.remote_as != conf->as);
-	}
-
 	memcpy(&oholdtime, p, sizeof(oholdtime));
 	p += sizeof(oholdtime);
 
@@ -1966,6 +1962,15 @@ parse_open(struct peer *peer)
 		}
 	}
 
+	/* if remote-as is zero and it's a cloned neighbor, accept any */
+	if (peer->conf.cloned && !peer->conf.remote_as && as != AS_TRANS) {
+		peer->conf.remote_as = as;
+		peer->conf.ebgp = (peer->conf.remote_as != conf->as);
+		if (!peer->conf.ebgp)
+			/* force enforce_as off for iBGP sessions */
+			peer->conf.enforce_as = ENFORCE_AS_OFF;
+	}
+
 	if (peer->conf.remote_as != as) {
 		log_peer_warnx(&peer->conf, "peer sent wrong AS %s",
 		    log_as(as));
@@ -1974,6 +1979,14 @@ parse_open(struct peer *peer)
 		return (-1);
 	}
 
+	if (capa_neg_calc(peer) == -1) {
+		log_peer_warnx(&peer->conf,
+		    "capabilitiy negotiation calculation failed");
+		session_notification(peer, ERR_OPEN, 0, NULL, 0);
+		change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
+		return (-1);
+	}
+
 	return (0);
 }
 
@@ -2008,24 +2021,35 @@ int
 parse_refresh(struct peer *peer)
 {
 	u_char		*p;
-	struct rrefresh	 r;
+	u_int16_t	 afi;
+	u_int8_t	 aid, safi;
 
 	p = peer->rbuf->rptr;
 	p += MSGSIZE_HEADER;	/* header is already checked */
 
+	/*
+	 * We could check if we actually announced the capability but
+	 * as long as the message is correctly encoded we don't care.
+	 */
+
 	/* afi, 2 byte */
-	memcpy(&r.afi, p, sizeof(r.afi));
-	r.afi = ntohs(r.afi);
+	memcpy(&afi, p, sizeof(afi));
+	afi = ntohs(afi);
 	p += 2;
 	/* reserved, 1 byte */
 	p += 1;
 	/* safi, 1 byte */
-	memcpy(&r.safi, p, sizeof(r.safi));
+	memcpy(&safi, p, sizeof(safi));
 
 	/* afi/safi unchecked -	unrecognized values will be ignored anyway */
+	if (afi2aid(afi, safi, &aid) == -1) {
+		log_peer_warnx(&peer->conf, "peer sent bad refresh, "
+		    "invalid afi/safi pair");
+		return (0);
+	}
 
-	if (imsg_compose(ibuf_rde, IMSG_REFRESH, peer->conf.id, 0, -1, &r,
-	    sizeof(r)) == -1)
+	if (imsg_compose(ibuf_rde, IMSG_REFRESH, peer->conf.id, 0, -1, &aid,
+	    sizeof(aid)) == -1)
 		return (-1);
 
 	return (0);
@@ -2035,11 +2059,12 @@ int
 parse_notification(struct peer *peer)
 {
 	u_char		*p;
+	u_int16_t	 datalen;
 	u_int8_t	 errcode;
 	u_int8_t	 subcode;
-	u_int16_t	 datalen;
 	u_int8_t	 capa_code;
 	u_int8_t	 capa_len;
+	u_int8_t	 i;
 
 	/* just log */
 	p = peer->rbuf->rptr;
@@ -2059,7 +2084,7 @@ parse_notification(struct peer *peer)
 	p += sizeof(subcode);
 	datalen -= sizeof(subcode);
 
-	log_notification(peer, errcode, subcode, p, datalen);
+	log_notification(peer, errcode, subcode, p, datalen, "received");
 	peer->errcnt++;
 
 	if (errcode == ERR_OPEN && subcode == ERR_OPEN_CAPA) {
@@ -2094,8 +2119,8 @@ parse_notification(struct peer *peer)
 			datalen -= capa_len;
 			switch (capa_code) {
 			case CAPA_MP:
-				peer->capa.ann.mp_v4 = SAFI_NONE;
-				peer->capa.ann.mp_v6 = SAFI_NONE;
+				for (i = 0; i < AID_MAX; i++)
+					peer->capa.ann.mp[i] = 0;
 				log_peer_warnx(&peer->conf,
 				    "disabling multiprotocol capability");
 				break;
@@ -2139,13 +2164,14 @@ parse_notification(struct peer *peer)
 int
 parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as)
 {
+	u_char		*capa_val;
+	u_int32_t	 remote_as;
 	u_int16_t	 len;
+	u_int16_t	 afi;
+	u_int8_t	 safi;
+	u_int8_t	 aid;
 	u_int8_t	 capa_code;
 	u_int8_t	 capa_len;
-	u_char		*capa_val;
-	u_int16_t	 mp_afi;
-	u_int8_t	 mp_safi;
-	u_int32_t	 remote_as;
 
 	len = dlen;
 	while (len > 0) {
@@ -2182,29 +2208,16 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as)
 				    "expect len 4, len is %u", capa_len);
 				return (-1);
 			}
-			memcpy(&mp_afi, capa_val, sizeof(mp_afi));
-			mp_afi = ntohs(mp_afi);
-			memcpy(&mp_safi, capa_val + 3, sizeof(mp_safi));
-			switch (mp_afi) {
-			case AFI_IPv4:
-				if (mp_safi < 1 || mp_safi > 3)
-					log_peer_warnx(&peer->conf,
-					    "parse_capabilities: AFI IPv4, "
-					    "mp_safi %u unknown", mp_safi);
-				else
-					peer->capa.peer.mp_v4 = mp_safi;
-				break;
-			case AFI_IPv6:
-				if (mp_safi < 1 || mp_safi > 3)
-					log_peer_warnx(&peer->conf,
-					    "parse_capabilities: AFI IPv6, "
-					    "mp_safi %u unknown", mp_safi);
-				else
-					peer->capa.peer.mp_v6 = mp_safi;
-				break;
-			default:			/* ignore */
+			memcpy(&afi, capa_val, sizeof(afi));
+			afi = ntohs(afi);
+			memcpy(&safi, capa_val + 3, sizeof(safi));
+			if (afi2aid(afi, safi, &aid) == -1) {
+				log_peer_warnx(&peer->conf,
+				    "parse_capabilities: AFI %u, "
+				    "safi %u unknown", afi, safi);
 				break;
 			}
+			peer->capa.peer.mp[aid] = 1;
 			break;
 		case CAPA_REFRESH:
 			peer->capa.peer.refresh = 1;
@@ -2232,6 +2245,37 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as)
 	return (0);
 }
 
+int
+capa_neg_calc(struct peer *p)
+{
+	u_int8_t	i, hasmp = 0;
+
+	/* refresh: does not realy matter here, use peer setting */
+	p->capa.neg.refresh = p->capa.peer.refresh;
+
+	/* as4byte: both side must announce capability */
+	if (p->capa.ann.as4byte && p->capa.peer.as4byte)
+		p->capa.neg.as4byte = 1;
+	else
+		p->capa.neg.as4byte = 0;
+
+	/* MP: both side must announce capability */
+	for (i = 0; i < AID_MAX; i++) {
+		if (p->capa.ann.mp[i] && p->capa.peer.mp[i]) {
+			p->capa.neg.mp[i] = 1;
+			hasmp = 1;
+		} else
+			p->capa.neg.mp[i] = 0;
+	}
+	/* if no MP capability present for default IPv4 unicast mode */
+	if (!hasmp)
+		p->capa.neg.mp[AID_INET] = 1;
+
+	p->capa.neg.restart = p->capa.peer.restart;
+
+	return (0);
+}
+
 void
 session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 {
@@ -2244,7 +2288,7 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 	struct kif		*kif;
 	u_char			*data;
 	enum reconf_action	 reconf;
-	int			 n, depend_ok;
+	int			 n, depend_ok, restricted;
 	u_int8_t		 errcode, subcode;
 
 	if ((n = imsg_read(ibuf)) == -1)
@@ -2332,15 +2376,42 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 			}
 
 			break;
+		case IMSG_RECONF_CTRL:
+			if (idx != PFD_PIPE_MAIN)
+				fatalx("reconf request not from parent");
+			if (imsg.hdr.len != IMSG_HEADER_SIZE +
+			    sizeof(restricted))
+				fatalx("IFINFO imsg with wrong len");
+			memcpy(&restricted, imsg.data, sizeof(restricted));
+			if (imsg.fd == -1) {
+				log_warnx("expected to receive fd for control "
+				    "socket but didn't receive any");
+				break;
+			}
+			if (restricted) {
+				control_shutdown(rcsock);
+				rcsock = imsg.fd;
+				control_listen(rcsock);
+			} else {
+				control_shutdown(csock);
+				csock = imsg.fd;
+				control_listen(csock);
+			}
+			break;
 		case IMSG_RECONF_DONE:
 			if (idx != PFD_PIPE_MAIN)
 				fatalx("reconf request not from parent");
 			if (nconf == NULL)
 				fatalx("got IMSG_RECONF_DONE but no config");
+			conf->flags = nconf->flags;
+			conf->log = nconf->log;
+			conf->bgpid = nconf->bgpid;
+			conf->clusterid = nconf->clusterid;
 			conf->as = nconf->as;
+			conf->short_as = nconf->short_as;
 			conf->holdtime = nconf->holdtime;
-			conf->bgpid = nconf->bgpid;
 			conf->min_holdtime = nconf->min_holdtime;
+			conf->connectretry = nconf->connectretry;
 
 			/* add new peers */
 			for (p = npeers; p != NULL; p = next) {
@@ -2408,7 +2479,8 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 						bgp_fsm(p, EVNT_START);
 					} else if (!depend_ok && p->depend_ok) {
 						p->depend_ok = depend_ok;
-						bgp_fsm(p, EVNT_STOP);
+						session_stop(p,
+						    ERR_CEASE_OTHER_CHANGE);
 					}
 				}
 			break;
@@ -2456,10 +2528,10 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 			}
 			break;
 		case IMSG_CTL_KROUTE:
-		case IMSG_CTL_KROUTE6:
 		case IMSG_CTL_KROUTE_ADDR:
 		case IMSG_CTL_SHOW_NEXTHOP:
 		case IMSG_CTL_SHOW_INTERFACE:
+		case IMSG_CTL_SHOW_FIB_TABLES:
 			if (idx != PFD_PIPE_MAIN)
 				fatalx("ctl kroute request not from parent");
 			control_imsg_relay(&imsg);
@@ -2469,7 +2541,6 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
 		case IMSG_CTL_SHOW_RIB_ATTR:
 		case IMSG_CTL_SHOW_RIB_MEM:
 		case IMSG_CTL_SHOW_NETWORK:
-		case IMSG_CTL_SHOW_NETWORK6:
 		case IMSG_CTL_SHOW_NEIGHBOR:
 			if (idx != PFD_PIPE_ROUTE_CTL)
 				fatalx("ctl rib request not from RDE");
@@ -2612,29 +2683,23 @@ getpeerbydesc(const char *descr)
 struct peer *
 getpeerbyip(struct sockaddr *ip)
 {
+	struct bgpd_addr addr;
 	struct peer	*p, *newpeer, *loose = NULL;
 	u_int32_t	 id;
 
+	sa2addr(ip, &addr);
+
 	/* we might want a more effective way to find peers by IP */
 	for (p = peers; p != NULL; p = p->next)
 		if (!p->conf.template &&
-		    p->conf.remote_addr.af == ip->sa_family) {
-			if (p->conf.remote_addr.af == AF_INET &&
-			    p->conf.remote_addr.v4.s_addr ==
-			    ((struct sockaddr_in *)ip)->sin_addr.s_addr)
-				return (p);
-			if (p->conf.remote_addr.af == AF_INET6 &&
-			    !bcmp(&p->conf.remote_addr.v6,
-			    &((struct sockaddr_in6 *)ip)->sin6_addr,
-			    sizeof(p->conf.remote_addr.v6)))
-				return (p);
-		}
+		    !memcmp(&addr, &p->conf.remote_addr, sizeof(addr)))
+			return (p);
 
 	/* try template matching */
 	for (p = peers; p != NULL; p = p->next)
 		if (p->conf.template &&
-		    p->conf.remote_addr.af == ip->sa_family &&
-		    session_match_mask(p, ip))
+		    p->conf.remote_addr.aid == addr.aid &&
+		    session_match_mask(p, &addr))
 			if (loose == NULL || loose->conf.remote_masklen <
 			    p->conf.remote_masklen)
 				loose = p;
@@ -2653,21 +2718,19 @@ getpeerbyip(struct sockaddr *ip)
 				break;
 			}
 		}
-		if (newpeer->conf.remote_addr.af == AF_INET) {
-			newpeer->conf.remote_addr.v4.s_addr =
-			    ((struct sockaddr_in *)ip)->sin_addr.s_addr;
+		sa2addr(ip, &newpeer->conf.remote_addr);
+		switch (ip->sa_family) {
+		case AF_INET:
 			newpeer->conf.remote_masklen = 32;
-		}
-		if (newpeer->conf.remote_addr.af == AF_INET6) {
-			memcpy(&p->conf.remote_addr.v6,
-			    &((struct sockaddr_in6 *)ip)->sin6_addr,
-			    sizeof(newpeer->conf.remote_addr.v6));
+			break;
+		case AF_INET6:
 			newpeer->conf.remote_masklen = 128;
+			break;
 		}
 		newpeer->conf.template = 0;
 		newpeer->conf.cloned = 1;
 		newpeer->state = newpeer->prev_state = STATE_NONE;
-		newpeer->conf.reconf_action = RECONF_REINIT;
+		newpeer->conf.reconf_action = RECONF_KEEP;
 		newpeer->rbuf = NULL;
 		init_peer(newpeer);
 		bgp_fsm(newpeer, EVNT_START);
@@ -2680,40 +2743,24 @@ getpeerbyip(struct sockaddr *ip)
 }
 
 int
-session_match_mask(struct peer *p, struct sockaddr *ip)
+session_match_mask(struct peer *p, struct bgpd_addr *a)
 {
-	int		 i;
 	in_addr_t	 v4mask;
-	struct in6_addr	*in;
-	struct in6_addr	 mask;
+	struct in6_addr	 masked;
 
-	if (p->conf.remote_addr.af == AF_INET) {
+	switch (p->conf.remote_addr.aid) {
+	case AID_INET:
 		v4mask = htonl(prefixlen2mask(p->conf.remote_masklen));
-		if (p->conf.remote_addr.v4.s_addr ==
-		    ((((struct sockaddr_in *)ip)->sin_addr.s_addr) & v4mask))
+		if (p->conf.remote_addr.v4.s_addr == (a->v4.s_addr & v4mask))
 			return (1);
-		else
-			return (0);
-	}
-
-	if (p->conf.remote_addr.af == AF_INET6) {
-		bzero(&mask, sizeof(mask));
-		for (i = 0; i < p->conf.remote_masklen / 8; i++)
-			mask.s6_addr[i] = 0xff;
-		i = p->conf.remote_masklen % 8;
-		if (i)
-			mask.s6_addr[p->conf.remote_masklen / 8] = 0xff00 >> i;
-
-		in = &((struct sockaddr_in6 *)ip)->sin6_addr;
-
-		for (i = 0; i < 16; i++)
-			if ((in->s6_addr[i] & mask.s6_addr[i]) !=
-			    p->conf.remote_addr.addr8[i])
-				return (0);
+		return (0);
+	case AID_INET6:
+		inet6applymask(&masked, &a->v6, p->conf.remote_masklen);
 
-		return (1);
+		if (!memcmp(&masked, &p->conf.remote_addr.v6, sizeof(masked)))
+			return (1);
+		return (0);
 	}
-
 	return (0);
 }
 
@@ -2733,6 +2780,7 @@ getpeerbyid(u_int32_t peerid)
 void
 session_down(struct peer *peer)
 {
+	bzero(&peer->capa.neg, sizeof(peer->capa.neg));
 	peer->stats.last_updown = time(NULL);
 	if (imsg_compose(ibuf_rde, IMSG_SESSION_DOWN, peer->conf.id, 0, -1,
 	    NULL, 0) == -1)
@@ -2748,35 +2796,12 @@ session_up(struct peer *p)
 	    &p->conf, sizeof(p->conf)) == -1)
 		fatalx("imsg_compose error");
 
-	switch (p->sa_local.ss_family) {
-	case AF_INET:
-		sup.local_addr.af = AF_INET;
-		memcpy(&sup.local_addr.v4,
-		    &((struct sockaddr_in *)&p->sa_local)->sin_addr,
-		    sizeof(sup.local_addr.v4));
-		sup.remote_addr.af = AF_INET;
-		memcpy(&sup.remote_addr.v4,
-		    &((struct sockaddr_in *)&p->sa_remote)->sin_addr,
-		    sizeof(sup.remote_addr.v4));
-		break;
-	case AF_INET6:
-		sup.local_addr.af = AF_INET6;
-		memcpy(&sup.local_addr.v6,
-		    &((struct sockaddr_in6 *)&p->sa_local)->sin6_addr,
-		    sizeof(sup.local_addr.v6));
-		sup.remote_addr.af = AF_INET6;
-		memcpy(&sup.remote_addr.v6,
-		    &((struct sockaddr_in6 *)&p->sa_remote)->sin6_addr,
-		    sizeof(sup.remote_addr.v6));
-		break;
-	default:
-		fatalx("session_up: unsupported address family");
-	}
+	sa2addr((struct sockaddr *)&p->sa_local, &sup.local_addr);
+	sa2addr((struct sockaddr *)&p->sa_remote, &sup.remote_addr);
 
 	sup.remote_bgpid = p->remote_bgpid;
 	sup.short_as = p->short_as;
-	memcpy(&sup.capa_announced, &p->capa.ann, sizeof(sup.capa_announced));
-	memcpy(&sup.capa_received, &p->capa.peer, sizeof(sup.capa_received));
+	memcpy(&sup.capa, &p->capa.neg, sizeof(sup.capa));
 	p->stats.last_updown = time(NULL);
 	if (imsg_compose(ibuf_rde, IMSG_SESSION_UP, p->conf.id, 0, -1,
 	    &sup, sizeof(sup)) == -1)
@@ -2784,9 +2809,10 @@ session_up(struct peer *p)
 }
 
 int
-imsg_compose_parent(int type, pid_t pid, void *data, u_int16_t datalen)
+imsg_compose_parent(int type, u_int32_t peerid, pid_t pid, void *data,
+    u_int16_t datalen)
 {
-	return (imsg_compose(ibuf_main, type, 0, pid, -1, data, datalen));
+	return (imsg_compose(ibuf_main, type, peerid, pid, -1, data, datalen));
 }
 
 int
@@ -2795,34 +2821,6 @@ imsg_compose_rde(int type, pid_t pid, void *data, u_int16_t datalen)
 	return (imsg_compose(ibuf_rde, type, 0, pid, -1, data, datalen));
 }
 
-static struct sockaddr *
-addr2sa(struct bgpd_addr *addr, u_int16_t port)
-{
-	static struct sockaddr_storage	 ss;
-	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
-	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
-
-	bzero(&ss, sizeof(ss));
-	switch (addr->af) {
-	case AF_INET:
-		sa_in->sin_family = AF_INET;
-		sa_in->sin_len = sizeof(struct sockaddr_in);
-		sa_in->sin_addr.s_addr = addr->v4.s_addr;
-		sa_in->sin_port = htons(port);
-		break;
-	case AF_INET6:
-		sa_in6->sin6_family = AF_INET6;
-		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
-		memcpy(&sa_in6->sin6_addr, &addr->v6,
-		    sizeof(sa_in6->sin6_addr));
-		sa_in6->sin6_port = htons(port);
-		sa_in6->sin6_scope_id = addr->scope_id;
-		break;
-	}
-
-	return ((struct sockaddr *)&ss);
-}
-
 void
 session_demote(struct peer *p, int level)
 {
@@ -2837,3 +2835,19 @@ session_demote(struct peer *p, int level)
 
 	p->demoted += level;
 }
+
+void
+session_stop(struct peer *peer, u_int8_t subcode)
+{
+	switch (peer->state) {
+	case STATE_OPENSENT:
+	case STATE_OPENCONFIRM:
+	case STATE_ESTABLISHED:
+		session_notification(peer, ERR_CEASE, subcode, NULL, 0);
+		break;
+	default:
+		/* session not open, no need to send notification */
+		break;
+	}
+	bgp_fsm(peer, EVNT_STOP);
+}
diff --git a/bgpd/session.h b/bgpd/session.h
index 9c33239..2bfa138 100644
--- a/bgpd/session.h
+++ b/bgpd/session.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: session.h,v 1.101 2009/06/05 20:26:38 claudio Exp $ */
+/*	$OpenBSD: session.h,v 1.111 2010/12/09 13:50:41 claudio Exp $ */
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -94,6 +94,13 @@ enum suberr_open {
 	ERR_OPEN_CAPA
 };
 
+enum suberr_fsm {
+	ERR_FSM_UNSPECIFIC = 0,
+	ERR_FSM_UNEX_OPENSENT,
+	ERR_FSM_UNEX_OPENCONFIRM,
+	ERR_FSM_UNEX_ESTABLISHED
+};
+
 enum opt_params {
 	OPT_PARAM_NONE,
 	OPT_PARAM_AUTH,
@@ -109,7 +116,7 @@ enum capa_codes {
 };
 
 struct bgp_msg {
-	struct buf	*buf;
+	struct ibuf	*buf;
 	enum msg_type	 type;
 	u_int16_t	 len;
 };
@@ -189,6 +196,7 @@ struct peer {
 	struct {
 		struct capabilities	ann;
 		struct capabilities	peer;
+		struct capabilities	neg;
 	}			 capa;
 	struct {
 		struct bgpd_addr	local_addr;
@@ -201,7 +209,7 @@ struct peer {
 	struct sockaddr_storage	 sa_remote;
 	struct peer_timer_head	 timers;
 	struct msgbuf		 wbuf;
-	struct buf_read		*rbuf;
+	struct ibuf_read	*rbuf;
 	struct peer		*next;
 	int			 fd;
 	int			 lasterr;
@@ -217,7 +225,7 @@ struct peer {
 	u_int8_t		 passive;
 };
 
-struct peer	*peers;
+extern struct peer	*peers;
 
 struct ctl_timer {
 	enum Timer	type;
@@ -226,38 +234,36 @@ struct ctl_timer {
 
 /* session.c */
 void		 session_socket_blockmode(int, enum blockmodes);
-pid_t		 session_main(struct bgpd_config *, struct peer *,
-		    struct network_head *, struct filter_head *,
-		    struct mrt_head *, struct rib_names *,
-		    int[2], int[2], int[2], int[2]);
+pid_t		 session_main(int[2], int[2], int[2], int[2]);
 void		 bgp_fsm(struct peer *, enum session_events);
 int		 session_neighbor_rrefresh(struct peer *p);
 struct peer	*getpeerbyaddr(struct bgpd_addr *);
 struct peer	*getpeerbydesc(const char *);
-int		 imsg_compose_parent(int, pid_t, void *, u_int16_t);
+int		 imsg_compose_parent(int, u_int32_t, pid_t, void *, u_int16_t);
 int		 imsg_compose_rde(int, pid_t, void *, u_int16_t);
+void	 	 session_stop(struct peer *, u_int8_t);
 
 /* log.c */
 char		*log_fmt_peer(const struct peer_config *);
 void		 log_statechange(struct peer *, enum session_state,
 		    enum session_events);
 void		 log_notification(const struct peer *, u_int8_t, u_int8_t,
-		    u_char *, u_int16_t);
+		    u_char *, u_int16_t, const char *);
 void		 log_conn_attempt(const struct peer *, struct sockaddr *);
 
 /* parse.y */
 int	 parse_config(char *, struct bgpd_config *, struct mrt_head *,
-	    struct peer **, struct network_head *, struct filter_head *);
+	    struct peer **, struct network_head *, struct filter_head *,
+	    struct rdomain_head *);
 
 /* config.c */
 int	 merge_config(struct bgpd_config *, struct bgpd_config *,
 	    struct peer *, struct listen_addrs *);
 void	 prepare_listeners(struct bgpd_config *);
+int	 get_mpe_label(struct rdomain *);
 
 /* rde.c */
-pid_t	 rde_main(struct bgpd_config *, struct peer *, struct network_head *,
-	    struct filter_head *, struct mrt_head *, struct rib_names *,
-	    int[2], int[2], int[2], int[2], int);
+pid_t	 rde_main(int[2], int[2], int[2], int[2], int);
 
 /* control.c */
 int	control_init(int, char *);
@@ -267,6 +273,7 @@ int	control_dispatch_msg(struct pollfd *, u_int *);
 unsigned int	control_accept(int, int);
 
 /* pfkey.c */
+int	pfkey_read(int, struct sadb_msg *);
 int	pfkey_establish(struct peer *);
 int	pfkey_remove(struct peer *);
 int	pfkey_init(struct bgpd_sysdep *);
@@ -274,7 +281,7 @@ int	pfkey_init(struct bgpd_sysdep *);
 /* printconf.c */
 void	print_config(struct bgpd_config *, struct rib_names *,
 	    struct network_head *, struct peer *, struct filter_head *,
-	    struct mrt_head *);
+	    struct mrt_head *, struct rdomain_head *);
 
 /* carp.c */
 int	 carp_demote_init(char *, int);
diff --git a/bgpd/timer.c b/bgpd/timer.c
index 41ccede..0aaba08 100644
--- a/bgpd/timer.c
+++ b/bgpd/timer.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: timer.c,v 1.13 2009/01/21 20:32:53 henning Exp $ */
+/*	$OpenBSD: timer.c,v 1.14 2010/10/24 17:20:08 deraadt Exp $ */
 
 /*
  * Copyright (c) 2003-2007 Henning Brauer <henning@openbsd.org>
@@ -43,7 +43,7 @@ timer_get(struct peer *p, enum Timer timer)
 
 	TAILQ_FOREACH(pt, &p->timers, entry)
 		if (pt->type == timer)
-				break;
+			break;
 
 	return (pt);
 }
diff --git a/bgpd/util.c b/bgpd/util.c
index b361b8c..7763458 100644
--- a/bgpd/util.c
+++ b/bgpd/util.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: util.c,v 1.6 2009/06/12 16:42:53 claudio Exp $ */
+/*	$OpenBSD: util.c,v 1.11 2010/03/29 09:04:43 claudio Exp $ */
 
 /*
  * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
@@ -18,6 +18,9 @@
  */
 #include <sys/types.h>
 #include <sys/socket.h>
+#if defined(__FreeBSD__)	/* sys/limits.h */
+#include <sys/limits.h>
+#endif /* defined(__FreeBSD__) */
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -28,15 +31,30 @@
 #include "bgpd.h"
 #include "rde.h"
 
+const char	*aspath_delim(u_int8_t, int);
+
 const char *
 log_addr(const struct bgpd_addr *addr)
 {
 	static char	buf[48];
+	char		tbuf[16];
 
-	if (inet_ntop(addr->af, &addr->ba, buf, sizeof(buf)) == NULL)
-		return ("?");
-	else
+	switch (addr->aid) {
+	case AID_INET:
+	case AID_INET6:
+		if (inet_ntop(aid2af(addr->aid), &addr->ba, buf,
+		    sizeof(buf)) == NULL)
+			return ("?");
+		return (buf);
+	case AID_VPN_IPv4:
+		if (inet_ntop(AF_INET, &addr->vpn4.addr, tbuf,
+		    sizeof(tbuf)) == NULL)
+			return ("?");
+		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn4.rd),
+		   tbuf);
 		return (buf);
+	}
+	return ("???");
 }
 
 const char *
@@ -90,6 +108,96 @@ log_as(u_int32_t as)
 	return (buf);
 }
 
+const char *
+log_rd(u_int64_t rd)
+{
+	static char	buf[32];
+	struct in_addr	addr;
+	u_int32_t	u32;
+	u_int16_t	u16;
+
+	rd = betoh64(rd);
+	switch (rd >> 48) {
+	case EXT_COMMUNITY_TWO_AS:
+		u32 = rd & 0xffffffff;
+		u16 = (rd >> 32) & 0xffff;
+		snprintf(buf, sizeof(buf), "rd %i:%i", u16, u32);
+		break;
+	case EXT_COMMUNITY_FOUR_AS:
+		u32 = (rd >> 16) & 0xffffffff;
+		u16 = rd & 0xffff;
+		snprintf(buf, sizeof(buf), "rd %s:%i", log_as(u32), u16);
+		break;
+	case EXT_COMMUNITY_IPV4:
+		u32 = (rd >> 16) & 0xffffffff;
+		u16 = rd & 0xffff;
+		addr.s_addr = htonl(u32);
+		snprintf(buf, sizeof(buf), "rd %s:%i", inet_ntoa(addr), u16);
+		break;
+	default:
+		return ("rd ?");
+	}
+	return (buf);
+}
+
+/* NOTE: this function does not check if the type/subtype combo is
+ * actually valid. */
+const char *
+log_ext_subtype(u_int8_t subtype)
+{
+	static char etype[6];
+
+	switch (subtype) {
+	case EXT_COMMUNITY_ROUTE_TGT:
+		return ("rt");	/* route target */
+	case EXT_CUMMUNITY_ROUTE_ORIG:
+		return ("soo");	/* source of origin */
+	case EXT_COMMUNITY_OSPF_DOM_ID:
+		return ("odi");	/* ospf domain id */
+	case EXT_COMMUNITY_OSPF_RTR_TYPE:
+		return ("ort");	/* ospf route type */
+	case EXT_COMMUNITY_OSPF_RTR_ID:
+		return ("ori");	/* ospf router id */
+	case EXT_COMMUNITY_BGP_COLLECT:
+		return ("bdc");	/* bgp data collection */
+	default:
+		snprintf(etype, sizeof(etype), "[%u]", subtype);
+		return (etype);
+	}
+}
+
+const char *
+aspath_delim(u_int8_t seg_type, int closing)
+{
+	static char db[8];
+
+	switch (seg_type) {
+	case AS_SET:
+		if (!closing)
+			return ("{ ");
+		else
+			return (" }");
+	case AS_SEQUENCE:
+		return ("");
+	case AS_CONFED_SEQUENCE:
+		if (!closing)
+			return ("( ");
+		else
+			return (" )");
+	case AS_CONFED_SET:
+		if (!closing)
+			return ("[ ");
+		else
+			return (" ]");
+	default:
+		if (!closing)
+			snprintf(db, sizeof(db), "!%u ", seg_type);
+		else
+			snprintf(db, sizeof(db), " !%u", seg_type);
+		return (db);
+	}
+}
+
 int
 aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
 {
@@ -118,16 +226,10 @@ aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
 		seg_len = seg[1];
 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
 
-		if (seg_type == AS_SET) {
-			if (total_size != 0)
-				r = snprintf(buf, size, " { ");
-			else
-				r = snprintf(buf, size, "{ ");
-			UPDATE();
-		} else if (total_size != 0) {
-			r = snprintf(buf, size, " ");
-			UPDATE();
-		}
+		r = snprintf(buf, size, "%s%s",
+		    total_size != 0 ? " " : "",
+		    aspath_delim(seg_type, 0));
+		UPDATE();
 
 		for (i = 0; i < seg_len; i++) {
 			r = snprintf(buf, size, "%s",
@@ -138,10 +240,8 @@ aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
 				UPDATE();
 			}
 		}
-		if (seg_type == AS_SET) {
-			r = snprintf(buf, size, " }");
-			UPDATE();
-		}
+		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
+		UPDATE();
 	}
 	/* ensure that we have a valid C-string especially for empty as path */
 	if (size > 0)
@@ -276,3 +376,115 @@ inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
 	for (i = 0; i < 16; i++)
 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
 }
+
+/* address family translation functions */
+const struct aid aid_vals[AID_MAX] = AID_VALS;
+
+const char *
+aid2str(u_int8_t aid)
+{
+	if (aid < AID_MAX)
+		return (aid_vals[aid].name);
+	return ("unknown AID");
+}
+
+int
+aid2afi(u_int8_t aid, u_int16_t *afi, u_int8_t *safi)
+{
+	if (aid < AID_MAX) {
+		*afi = aid_vals[aid].afi;
+		*safi = aid_vals[aid].safi;
+		return (0);
+	}
+	return (-1);
+}
+
+int
+afi2aid(u_int16_t afi, u_int8_t safi, u_int8_t *aid)
+{
+	u_int8_t i;
+
+	for (i = 0; i < AID_MAX; i++)
+		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
+			*aid = i;
+			return (0);
+		}
+
+	return (-1);
+}
+
+sa_family_t
+aid2af(u_int8_t aid)
+{
+	if (aid < AID_MAX)
+		return (aid_vals[aid].af);
+	return (AF_UNSPEC);
+}
+
+int
+af2aid(sa_family_t af, u_int8_t safi, u_int8_t *aid)
+{
+	u_int8_t i;
+
+	if (safi == 0) /* default to unicast subclass */
+		safi = SAFI_UNICAST;
+
+	for (i = 0; i < AID_MAX; i++)
+		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
+			*aid = i;
+			return (0);
+		}
+
+	return (-1);
+}
+
+struct sockaddr *
+addr2sa(struct bgpd_addr *addr, u_int16_t port)
+{
+	static struct sockaddr_storage	 ss;
+	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
+	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
+
+	if (addr->aid == AID_UNSPEC)
+		return (NULL);
+
+	bzero(&ss, sizeof(ss));
+	switch (addr->aid) {
+	case AID_INET:
+		sa_in->sin_family = AF_INET;
+		sa_in->sin_len = sizeof(struct sockaddr_in);
+		sa_in->sin_addr.s_addr = addr->v4.s_addr;
+		sa_in->sin_port = htons(port);
+		break;
+	case AID_INET6:
+		sa_in6->sin6_family = AF_INET6;
+		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+		memcpy(&sa_in6->sin6_addr, &addr->v6,
+		    sizeof(sa_in6->sin6_addr));
+		sa_in6->sin6_port = htons(port);
+		sa_in6->sin6_scope_id = addr->scope_id;
+		break;
+	}
+
+	return ((struct sockaddr *)&ss);
+}
+
+void
+sa2addr(struct sockaddr *sa, struct bgpd_addr *addr)
+{
+	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
+	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
+
+	bzero(addr, sizeof(*addr));
+	switch (sa->sa_family) {
+	case AF_INET:
+		addr->aid = AID_INET;
+		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
+		break;
+	case AF_INET6:
+		addr->aid = AID_INET6;
+		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
+		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
+		break;
+	}
+}
diff --git a/openbsd-compat/fmt_scaled.c b/openbsd-compat/fmt_scaled.c
new file mode 100644
index 0000000..134dbfb
--- /dev/null
+++ b/openbsd-compat/fmt_scaled.c
@@ -0,0 +1,268 @@
+/*	$OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003 Ian F. Darwin.  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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*
+ * fmt_scaled: Format numbers scaled for human comprehension
+ * scan_scaled: Scan numbers in this format.
+ *
+ * "Human-readable" output uses 4 digits max, and puts a unit suffix at
+ * the end.  Makes output compact and easy-to-read esp. on huge disks.
+ * Formatting code was originally in OpenBSD "df", converted to library routine.
+ * Scanning code written for OpenBSD libutil.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "util.h"
+
+typedef enum {
+	NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
+} unit_type;
+
+/* These three arrays MUST be in sync!  XXX make a struct */
+static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
+static char scale_chars[] = "BKMGTPE";
+static long long scale_factors[] = {
+	1LL,
+	1024LL,
+	1024LL*1024,
+	1024LL*1024*1024,
+	1024LL*1024*1024*1024,
+	1024LL*1024*1024*1024*1024,
+	1024LL*1024*1024*1024*1024*1024,
+};
+#define	SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
+
+#define MAX_DIGITS (SCALE_LENGTH * 3)	/* XXX strlen(sprintf("%lld", -1)? */
+
+/** Convert the given input string "scaled" into numeric in "result".
+ * Return 0 on success, -1 and errno set on error.
+ */
+int
+scan_scaled(char *scaled, long long *result)
+{
+	char *p = scaled;
+	int sign = 0;
+	unsigned int i, ndigits = 0, fract_digits = 0;
+	long long scale_fact = 1, whole = 0, fpart = 0;
+
+	/* Skip leading whitespace */
+	while (isascii(*p) && isspace(*p))
+		++p;
+
+	/* Then at most one leading + or - */
+	while (*p == '-' || *p == '+') {
+		if (*p == '-') {
+			if (sign) {
+				errno = EINVAL;
+				return -1;
+			}
+			sign = -1;
+			++p;
+		} else if (*p == '+') {
+			if (sign) {
+				errno = EINVAL;
+				return -1;
+			}
+			sign = +1;
+			++p;
+		}
+	}
+
+	/* Main loop: Scan digits, find decimal point, if present.
+	 * We don't allow exponentials, so no scientific notation
+	 * (but note that E for Exa might look like e to some!).
+	 * Advance 'p' to end, to get scale factor.
+	 */
+	for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) {
+		if (*p == '.') {
+			if (fract_digits > 0) {	/* oops, more than one '.' */
+				errno = EINVAL;
+				return -1;
+			}
+			fract_digits = 1;
+			continue;
+		}
+
+		i = (*p) - '0';			/* whew! finally a digit we can use */
+		if (fract_digits > 0) {
+			if (fract_digits >= MAX_DIGITS-1)
+				/* ignore extra fractional digits */
+				continue;
+			fract_digits++;		/* for later scaling */
+			fpart *= 10;
+			fpart += i;
+		} else {				/* normal digit */
+			if (++ndigits >= MAX_DIGITS) {
+				errno = ERANGE;
+				return -1;
+			}
+			whole *= 10;
+			whole += i;
+		}
+	}
+
+	if (sign) {
+		whole *= sign;
+		fpart *= sign;
+	}
+
+	/* If no scale factor given, we're done. fraction is discarded. */
+	if (!*p) {
+		*result = whole;
+		return 0;
+	}
+
+	/* Validate scale factor, and scale whole and fraction by it. */
+	for (i = 0; i < SCALE_LENGTH; i++) {
+
+		/** Are we there yet? */
+		if (*p == scale_chars[i] ||
+			*p == tolower(scale_chars[i])) {
+
+			/* If it ends with alphanumerics after the scale char, bad. */
+			if (isalnum(*(p+1))) {
+				errno = EINVAL;
+				return -1;
+			}
+			scale_fact = scale_factors[i];
+
+			/* scale whole part */
+			whole *= scale_fact;
+
+			/* truncate fpart so it does't overflow.
+			 * then scale fractional part.
+			 */
+			while (fpart >= LLONG_MAX / scale_fact) {
+				fpart /= 10;
+				fract_digits--;
+			}
+			fpart *= scale_fact;
+			if (fract_digits > 0) {
+				for (i = 0; i < fract_digits -1; i++)
+					fpart /= 10;
+			}
+			whole += fpart;
+			*result = whole;
+			return 0;
+		}
+	}
+	errno = ERANGE;
+	return -1;
+}
+
+/* Format the given "number" into human-readable form in "result".
+ * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
+ * Return 0 on success, -1 and errno set if error.
+ */
+int
+fmt_scaled(long long number, char *result)
+{
+	long long abval, fract = 0;
+	unsigned int i;
+	unit_type unit = NONE;
+
+	abval = (number < 0LL) ? -number : number;	/* no long long_abs yet */
+
+	/* Not every negative long long has a positive representation.
+	 * Also check for numbers that are just too darned big to format
+	 */
+	if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
+		errno = ERANGE;
+		return -1;
+	}
+
+	/* scale whole part; get unscaled fraction */
+	for (i = 0; i < SCALE_LENGTH; i++) {
+		if (abval/1024 < scale_factors[i]) {
+			unit = units[i];
+			fract = (i == 0) ? 0 : abval % scale_factors[i];
+			number /= scale_factors[i];
+			if (i > 0)
+				fract /= scale_factors[i - 1];
+			break;
+		}
+	}
+
+	fract = (10 * fract + 512) / 1024;
+	/* if the result would be >= 10, round main number */
+	if (fract == 10) {
+		if (number >= 0)
+			number++;
+		else
+			number--;
+		fract = 0;
+	}
+
+	if (number == 0)
+		strlcpy(result, "0B", FMT_SCALED_STRSIZE);
+	else if (unit == NONE || number >= 100 || number <= -100) {
+		if (fract >= 5) {
+			if (number >= 0)
+				number++;
+			else
+				number--;
+		}
+		(void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
+			number, scale_chars[unit]);
+	} else
+		(void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
+			number, fract, scale_chars[unit]);
+
+	return 0;
+}
+
+#ifdef	MAIN
+/*
+ * This is the original version of the program in the man page.
+ * Copy-and-paste whatever you need from it.
+ */
+int
+main(int argc, char **argv)
+{
+	char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE];
+	long long ninput = 10483892, result;
+
+	if (scan_scaled(cinput, &result) == 0)
+		printf("\"%s\" -> %lld\n", cinput, result);
+	else
+		perror(cinput);
+
+	if (fmt_scaled(ninput, buf) == 0)
+		printf("%lld -> \"%s\"\n", ninput, buf);
+	else
+		fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno));
+
+	return 0;
+}
+#endif
diff --git a/openbsd-compat/hash.h b/openbsd-compat/hash.h
new file mode 100644
index 0000000..2f0283f
--- /dev/null
+++ b/openbsd-compat/hash.h
@@ -0,0 +1,127 @@
+/*	$OpenBSD: hash.h,v 1.4 2004/05/25 18:37:23 jmc Exp $	*/
+
+/*
+ * Copyright (c) 2001 Tobias Weingartner
+ * 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 ``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 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.
+ */
+
+#ifndef _SYS_HASH_H_
+#define	_SYS_HASH_H_
+#include <sys/types.h>
+
+/*
+ * Note: SMALL_KERNEL might be used to shrink these, right now I
+ * do not see the point, as my kernel did not grow appreciably when
+ * I switched to these from other inline code.  This may have to be
+ * revisited when/if these functions become more prevalent in the
+ * kernel.
+ */
+
+/* Convenience */
+#ifndef	HASHINIT
+#define	HASHINIT	5381
+#define	HASHSTEP(x,c)	(((x << 5) + x) + (c))
+#endif
+
+/*
+ * Return a 32-bit hash of the given buffer.  The init
+ * value should be 0, or the previous hash value to extend
+ * the previous hash.
+ */
+static __inline uint32_t
+hash32_buf(const void *buf, size_t len, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (len--)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string.
+ */
+static __inline uint32_t
+hash32_str(const void *buf, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string, limited by N.
+ */
+static __inline uint32_t
+hash32_strn(const void *buf, size_t len, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && len--)
+		hash = HASHSTEP(hash, *p++);
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string terminated by C,
+ * (as well as 0).  This is mainly here as a helper for the
+ * namei() hashing of path name parts.
+ */
+static __inline uint32_t
+hash32_stre(const void *buf, int end, char **ep, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && (*p != end))
+		hash = HASHSTEP(hash, *p++);
+
+	if (ep)
+		*ep = (char *)p;
+
+	return hash;
+}
+
+/*
+ * Return a 32-bit hash of the given string, limited by N,
+ * and terminated by C (as well as 0).  This is mainly here
+ * as a helper for the namei() hashing of path name parts.
+ */
+static __inline uint32_t
+hash32_strne(const void *buf, size_t len, int end, char **ep, uint32_t hash)
+{
+	const unsigned char *p = buf;
+
+	while (*p && (*p != end) && len--)
+		hash = HASHSTEP(hash, *p++);
+
+	if (ep)
+		*ep = (char *)p;
+
+	return hash;
+}
+#endif /* !_SYS_HASH_H_ */
diff --git a/openbsd-compat/if_media.h b/openbsd-compat/if_media.h
new file mode 100644
index 0000000..8bc0d12
--- /dev/null
+++ b/openbsd-compat/if_media.h
@@ -0,0 +1,612 @@
+/*	$OpenBSD: if_media.h,v 1.17 2004/11/02 02:12:16 reyk Exp $	*/
+/*	$NetBSD: if_media.h,v 1.22 2000/02/17 21:53:16 sommerfeld Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the NetBSD
+ *	Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * Copyright (c) 1997
+ *	Jonathan Stone and Jason R. Thorpe.  All rights reserved.
+ *
+ * This software is derived from information provided by Matt Thomas.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Jonathan Stone
+ *	and Jason R. Thorpe for the NetBSD Project.
+ * 4. The names of the authors may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 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.
+ */
+
+#ifndef _NET_IF_MEDIA_H_
+#define _NET_IF_MEDIA_H_
+
+/*
+ * Prototypes and definitions for BSD/OS-compatible network interface
+ * media selection.
+ *
+ * Where it is safe to do so, this code strays slightly from the BSD/OS
+ * design.  Software which uses the API (device drivers, basically)
+ * shouldn't notice any difference.
+ *
+ * Many thanks to Matt Thomas for providing the information necessary
+ * to implement this interface.
+ */
+
+#ifdef _KERNEL
+
+#include <sys/queue.h>
+
+/*
+ * Driver callbacks for media status and change requests.
+ */
+typedef	int (*ifm_change_cb_t)(struct ifnet *ifp);
+typedef	void (*ifm_stat_cb_t)(struct ifnet *ifp, struct ifmediareq *req);
+
+/*
+ * In-kernel representation of a single supported media type.
+ */
+struct ifmedia_entry {
+	TAILQ_ENTRY(ifmedia_entry) ifm_list;
+	int	ifm_media;	/* description of this media attachment */
+	int	ifm_data;	/* for driver-specific use */
+	void	*ifm_aux;	/* for driver-specific use */
+};
+
+/*
+ * One of these goes into a network interface's softc structure.
+ * It is used to keep general media state.
+ */
+struct ifmedia {
+	int	ifm_mask;	/* mask of changes we don't care about */
+	int	ifm_media;	/* current user-set media word */
+	struct ifmedia_entry *ifm_cur;	/* currently selected media */
+	TAILQ_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */
+	ifm_change_cb_t	ifm_change;	/* media change driver callback */
+	ifm_stat_cb_t	ifm_status;	/* media status driver callback */
+};
+
+/* Initialize an interface's struct if_media field. */
+void	ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
+	    ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback);
+
+/* Add one supported medium to a struct ifmedia. */
+void	ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux);
+
+/* Add an array (of ifmedia_entry) media to a struct ifmedia. */
+void	ifmedia_list_add(struct ifmedia *mp, struct ifmedia_entry *lp,
+	    int count);
+
+/* Set default media type on initialization. */
+void	ifmedia_set(struct ifmedia *ifm, int mword);
+
+/* Common ioctl function for getting/setting media, called by driver. */
+int	ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr,
+	    struct ifmedia *ifm, u_long cmd);
+
+/* Locate a media entry */
+struct	ifmedia_entry *ifmedia_match(struct ifmedia *ifm,
+	     int flags, int mask);
+
+/* Delete all media for a given media instance */
+void	ifmedia_delete_instance(struct ifmedia *, int);
+
+/* Compute baudrate for a given media. */
+int	ifmedia_baudrate(int);
+#endif /*_KERNEL */
+
+/*
+ * if_media Options word:
+ *	Bits	Use
+ *	----	-------
+ *	0-4	Media subtype		MAX SUBTYPE == 31!
+ *	5-7	Media type
+ *	8-15	Type specific options
+ *	16-19	RFU
+ *	20-27	Shared (global) options
+ *	28-31	Instance
+ */
+
+/*
+ * Ethernet
+ */
+#define IFM_ETHER	0x00000020
+#define	IFM_10_T	3		/* 10BaseT - RJ45 */
+#define	IFM_10_2	4		/* 10Base2 - Thinnet */
+#define	IFM_10_5	5		/* 10Base5 - AUI */
+#define	IFM_100_TX	6		/* 100BaseTX - RJ45 */
+#define	IFM_100_FX	7		/* 100BaseFX - Fiber */
+#define	IFM_100_T4	8		/* 100BaseT4 - 4 pair cat 3 */
+#define	IFM_100_VG	9		/* 100VG-AnyLAN */
+#define	IFM_100_T2	10		/* 100BaseT2 */
+#define	IFM_1000_SX	11		/* 1000BaseSX - multi-mode fiber */
+#define	IFM_10_STP	12		/* 10BaseT over shielded TP */
+#define	IFM_10_FL	13		/* 10BaseFL - Fiber */
+#define	IFM_1000_LX	14		/* 1000baseLX - single-mode fiber */
+#define	IFM_1000_CX	15		/* 1000baseCX - 150ohm STP */
+#define	IFM_1000_T	16		/* 1000baseT - 4 pair cat 5 */
+#define	IFM_1000_TX	IFM_1000_T	/* for backwards compatibility */
+#define	IFM_HPNA_1	17		/* HomePNA 1.0 (1Mb/s) */
+
+#define	IFM_ETH_MASTER	0x00000100	/* master mode (1000baseT) */
+
+/*
+ * Token ring
+ */
+#define	IFM_TOKEN	0x00000040
+#define	IFM_TOK_STP4	3		/* Shielded twisted pair 4m - DB9 */
+#define	IFM_TOK_STP16	4		/* Shielded twisted pair 16m - DB9 */
+#define	IFM_TOK_UTP4	5		/* Unshielded twisted pair 4m - RJ45 */
+#define	IFM_TOK_UTP16	6		/* Unshielded twisted pair 16m - RJ45 */
+#define	IFM_TOK_ETR	0x00000200	/* Early token release */
+#define	IFM_TOK_SRCRT	0x00000400	/* Enable source routing features */
+#define	IFM_TOK_ALLR	0x00000800	/* All routes / Single route bcast */
+
+/*
+ * FDDI
+ */
+#define	IFM_FDDI	0x00000060
+#define	IFM_FDDI_SMF	3		/* Single-mode fiber */
+#define	IFM_FDDI_MMF	4		/* Multi-mode fiber */
+#define IFM_FDDI_UTP	5		/* CDDI / UTP */
+#define IFM_FDDI_DA	0x00000100	/* Dual attach / single attach */
+
+/*
+ * IEEE 802.11 Wireless
+ */
+#define	IFM_IEEE80211	0x00000080
+#define	IFM_IEEE80211_FH1	3	/* Frequency Hopping 1Mbps */
+#define	IFM_IEEE80211_FH2	4	/* Frequency Hopping 2Mbps */
+#define	IFM_IEEE80211_DS2	5	/* Direct Sequence 2Mbps */
+#define	IFM_IEEE80211_DS5	6	/* Direct Sequence 5Mbps*/
+#define	IFM_IEEE80211_DS11	7	/* Direct Sequence 11Mbps*/
+#define	IFM_IEEE80211_DS1	8	/* Direct Sequence  1Mbps*/
+#define IFM_IEEE80211_DS22      9	/* Direct Sequence 22Mbps */ 
+#define IFM_IEEE80211_OFDM6     10	/* OFDM 6Mbps */
+#define IFM_IEEE80211_OFDM9     11	/* OFDM 9Mbps */
+#define IFM_IEEE80211_OFDM12    12	/* OFDM 12Mbps */
+#define IFM_IEEE80211_OFDM18    13	/* OFDM 18Mbps */
+#define IFM_IEEE80211_OFDM24    14	/* OFDM 24Mbps */
+#define IFM_IEEE80211_OFDM36    15	/* OFDM 36Mbps */
+#define IFM_IEEE80211_OFDM48    16	/* OFDM 48Mbps */
+#define IFM_IEEE80211_OFDM54    17	/* OFDM 54Mbps */
+#define IFM_IEEE80211_OFDM72    18	/* OFDM 72Mbps */
+
+#define	IFM_IEEE80211_ADHOC	0x100	/* Operate in Adhoc mode */
+#define	IFM_IEEE80211_HOSTAP	0x200	/* Operate in Host AP mode */
+#define	IFM_IEEE80211_IBSS	0x400	/* Operate in IBSS mode */
+#define	IFM_IEEE80211_IBSSMASTER 0x800	/* Operate as an IBSS master */
+#define	IFM_IEEE80211_MONITOR	0x1000	/* Operate in Monitor mode */
+#define	IFM_IEEE80211_TURBO	0x2000	/* Operate in Turbo mode */
+
+/* operating mode for multi-mode devices */
+#define IFM_IEEE80211_11A	0x00010000	/* 5Ghz, OFDM mode */
+#define IFM_IEEE80211_11B	0x00020000	/* Direct Sequence mode */
+#define IFM_IEEE80211_11G	0x00030000	/* 2Ghz, CCK mode */
+#define IFM_IEEE80211_FH	0x00040000	/* 2Ghz, GFSK mode */
+
+/*
+ * Digitally multiplexed "Carrier" Serial Interfaces
+ */
+#define	IFM_TDM		0x000000a0
+#define IFM_TDM_T1		3	/* T1 B8ZS+ESF 24 ts  */
+#define IFM_TDM_T1_AMI		4	/* T1 AMI+SF 24 ts */
+#define IFM_TDM_E1		5	/* E1 HDB3+G.703 clearchannel 32 ts */
+#define IFM_TDM_E1_G704		6	/* E1 HDB3+G.703+G.704 channelized 31 ts */
+#define IFM_TDM_E1_AMI		7	/* E1 AMI+G.703 32 ts */
+#define IFM_TDM_E1_AMI_G704	8	/* E1 AMI+G.703+G.704 31 ts */
+#define IFM_TDM_T3		9	/* T3 B3ZS+C-bit 672 ts */
+#define IFM_TDM_T3_M13		10	/* T3 B3ZS+M13 672 ts */
+#define IFM_TDM_E3		11	/* E3 HDB3+G.751 512? ts */
+#define IFM_TDM_E3_G751		12	/* E3 G.751 512 ts */
+#define IFM_TDM_E3_G832		13	/* E3 G.832 512 ts */
+/*
+ * 6 major ways that networks talk: Drivers enforce independent selection,
+ * meaning, a driver will ensure that only one of these is set at a time.
+ */
+#define IFM_TDM_HDLC_CRC16	0x0100	/* Use 16-bit CRC for HDLC instead */
+#define IFM_TDM_PPP		0x0200	/* SPPP (dumb) */
+#define IFM_TDM_FR_ANSI		0x0400	/* Frame Relay + LMI ANSI "Annex D" */
+#define IFM_TDM_FR_CISCO	0x0800	/* Frame Relay + LMI Cisco */
+#define IFM_TDM_FR_ITU		0x1000	/* Frame Relay + LMI ITU "Q933A" */
+
+/*
+ * Common Access Redundancy Protocol
+ */
+#define	IFM_CARP		0x000000c0
+
+/*
+ * Shared media sub-types
+ */
+#define	IFM_AUTO	0		/* Autoselect best media */
+#define	IFM_MANUAL	1		/* Jumper/dipswitch selects media */
+#define	IFM_NONE	2		/* Deselect all media */
+
+/*
+ * Shared options
+ */
+#define IFM_FDX		0x00100000	/* Force full duplex */
+#define	IFM_HDX		0x00200000	/* Force half duplex */
+#define	IFM_FLOW	0x00400000	/* enable hardware flow control */
+#define IFM_FLAG0	0x01000000	/* Driver defined flag */
+#define IFM_FLAG1	0x02000000	/* Driver defined flag */
+#define IFM_FLAG2	0x04000000	/* Driver defined flag */
+#define	IFM_LOOP	0x08000000	/* Put hardware in loopback */
+
+/*
+ * Masks
+ */
+#define	IFM_NMASK	0x000000e0	/* Network type */
+#define	IFM_TMASK	0x0000001f	/* Media sub-type */
+#define	IFM_IMASK	0xf0000000	/* Instance */
+#define	IFM_ISHIFT	28		/* Instance shift */
+#define	IFM_OMASK	0x0000ff00	/* Type specific options */
+#define	IFM_MMASK	0x00070000	/* Mode */
+#define	IFM_MSHIFT	16		/* Mode shift */
+#define	IFM_GMASK	0x0ff00000	/* Global options */
+
+#define	IFM_NMIN	IFM_ETHER	/* lowest Network type */
+#define	IFM_NMAX	IFM_NMASK	/* highest Network type */
+
+/*
+ * Status bits
+ */
+#define	IFM_AVALID	0x00000001	/* Active bit valid */
+#define	IFM_ACTIVE	0x00000002	/* Interface attached to working net */
+
+/* Mask of "status valid" bits, for ifconfig(8). */
+#define	IFM_STATUS_VALID	IFM_AVALID
+
+/* List of "status valid" bits, for ifconfig(8). */
+#define	IFM_STATUS_VALID_LIST {						\
+	IFM_AVALID,							\
+	0								\
+}
+
+/*
+ * Macros to extract various bits of information from the media word.
+ */
+#define	IFM_TYPE(x)	((x) & IFM_NMASK)
+#define	IFM_SUBTYPE(x)	((x) & IFM_TMASK)
+#define	IFM_INST(x)	(((x) & IFM_IMASK) >> IFM_ISHIFT)
+#define	IFM_OPTIONS(x)	((x) & (IFM_OMASK|IFM_GMASK))
+#define	IFM_MODE(x)	((x) & IFM_MMASK)
+
+#define	IFM_INST_MAX	IFM_INST(IFM_IMASK)
+#define	IFM_INST_ANY	(-1)
+
+/*
+ * Macro to create a media word.
+ */
+#define	IFM_MAKEWORD(type, subtype, options, instance)			\
+    ((type) | (subtype) | (options) | ((instance) << IFM_ISHIFT))
+#define IFM_MAKEMODE(mode)                                              \
+           (((mode) << IFM_MSHIFT) & IFM_MMASK)
+/*
+ * NetBSD extension not defined in the BSDI API.  This is used in various
+ * places to get the canonical description for a given type/subtype.
+ *
+ * In the subtype and mediaopt descriptions, the valid TYPE bits are OR'd
+ * in to indicate which TYPE the subtype/option corresponds to.  If no
+ * TYPE is present, it is a shared media/mediaopt.
+ *
+ * Note that these are parsed case-insensitive.
+ *
+ * Order is important.  The first matching entry is the canonical name
+ * for a media type; subsequent matches are aliases.
+ */
+struct ifmedia_description {
+	int	ifmt_word;		/* word value; may be masked */
+	const char *ifmt_string;	/* description */
+};
+
+#define	IFM_TYPE_DESCRIPTIONS {						\
+	{ IFM_ETHER,			"Ethernet" },			\
+	{ IFM_ETHER,			"ether" },			\
+	{ IFM_TOKEN,			"TokenRing" },			\
+	{ IFM_TOKEN,			"token" },			\
+	{ IFM_FDDI,			"FDDI" },			\
+	{ IFM_IEEE80211,		"IEEE802.11" },			\
+	{ IFM_TDM,			"TDM" },			\
+	{ IFM_CARP,			"CARP" },			\
+	{ 0, NULL },							\
+}
+
+#define	IFM_TYPE_MATCH(dt, t)						\
+	(IFM_TYPE((dt)) == 0 || IFM_TYPE((dt)) == IFM_TYPE((t)))
+
+#define	IFM_SUBTYPE_DESCRIPTIONS {					\
+	{ IFM_AUTO,			"autoselect" },			\
+	{ IFM_AUTO,			"auto" },			\
+	{ IFM_MANUAL,			"manual" },			\
+	{ IFM_NONE,			"none" },			\
+									\
+	{ IFM_ETHER|IFM_10_T,		"10baseT" },			\
+	{ IFM_ETHER|IFM_10_T,		"10baseT/UTP" },		\
+	{ IFM_ETHER|IFM_10_T,		"UTP" },			\
+	{ IFM_ETHER|IFM_10_T,		"10UTP" },			\
+	{ IFM_ETHER|IFM_10_2,		"10base2" },			\
+	{ IFM_ETHER|IFM_10_2,		"10base2/BNC" },		\
+	{ IFM_ETHER|IFM_10_2,		"BNC" },			\
+	{ IFM_ETHER|IFM_10_2,		"10BNC" },			\
+	{ IFM_ETHER|IFM_10_5,		"10base5" },			\
+	{ IFM_ETHER|IFM_10_5,		"10base5/AUI" },		\
+	{ IFM_ETHER|IFM_10_5,		"AUI" },			\
+	{ IFM_ETHER|IFM_10_5,		"10AUI" },			\
+	{ IFM_ETHER|IFM_100_TX,		"100baseTX" },			\
+	{ IFM_ETHER|IFM_100_TX,		"100TX" },			\
+	{ IFM_ETHER|IFM_100_FX,		"100baseFX" },			\
+	{ IFM_ETHER|IFM_100_FX,		"100FX" },			\
+	{ IFM_ETHER|IFM_100_T4,		"100baseT4" },			\
+	{ IFM_ETHER|IFM_100_T4,		"100T4" },			\
+	{ IFM_ETHER|IFM_100_VG,		"100baseVG" },			\
+	{ IFM_ETHER|IFM_100_VG,		"100VG" },			\
+	{ IFM_ETHER|IFM_100_T2,		"100baseT2" },			\
+	{ IFM_ETHER|IFM_100_T2,		"100T2" },			\
+	{ IFM_ETHER|IFM_1000_SX,	"1000baseSX" },			\
+	{ IFM_ETHER|IFM_1000_SX,	"1000SX" },			\
+	{ IFM_ETHER|IFM_10_STP,		"10baseSTP" },			\
+	{ IFM_ETHER|IFM_10_STP,		"STP" },			\
+	{ IFM_ETHER|IFM_10_STP,		"10STP" },			\
+	{ IFM_ETHER|IFM_10_FL,		"10baseFL" },			\
+	{ IFM_ETHER|IFM_10_FL,		"FL" },				\
+	{ IFM_ETHER|IFM_10_FL,		"10FL" },			\
+	{ IFM_ETHER|IFM_1000_LX,	"1000baseLX" },			\
+	{ IFM_ETHER|IFM_1000_LX,	"1000LX" },			\
+	{ IFM_ETHER|IFM_1000_CX,	"1000baseCX" },			\
+	{ IFM_ETHER|IFM_1000_CX,	"1000CX" },			\
+	{ IFM_ETHER|IFM_1000_T,		"1000baseT" },			\
+	{ IFM_ETHER|IFM_1000_T,		"1000T" },			\
+	{ IFM_ETHER|IFM_1000_T,		"1000baseTX" },			\
+	{ IFM_ETHER|IFM_1000_T,		"1000TX" },			\
+	{ IFM_ETHER|IFM_HPNA_1,		"HomePNA1" },			\
+	{ IFM_ETHER|IFM_HPNA_1,		"HPNA1" },			\
+									\
+	{ IFM_TOKEN|IFM_TOK_STP4,	"DB9/4Mbit" },			\
+	{ IFM_TOKEN|IFM_TOK_STP4,	"4STP" },			\
+	{ IFM_TOKEN|IFM_TOK_STP16,	"DB9/16Mbit" },			\
+	{ IFM_TOKEN|IFM_TOK_STP16,	"16STP" },			\
+	{ IFM_TOKEN|IFM_TOK_UTP4,	"UTP/4Mbit" },			\
+	{ IFM_TOKEN|IFM_TOK_UTP4,	"4UTP" },			\
+	{ IFM_TOKEN|IFM_TOK_UTP16,	"UTP/16Mbit" },			\
+	{ IFM_TOKEN|IFM_TOK_UTP16,	"16UTP" },			\
+									\
+	{ IFM_FDDI|IFM_FDDI_SMF,	"Single-mode" },		\
+	{ IFM_FDDI|IFM_FDDI_SMF,	"SMF" },			\
+	{ IFM_FDDI|IFM_FDDI_MMF,	"Multi-mode" },			\
+	{ IFM_FDDI|IFM_FDDI_MMF,	"MMF" },			\
+	{ IFM_FDDI|IFM_FDDI_UTP,	"UTP" },			\
+	{ IFM_FDDI|IFM_FDDI_UTP,	"CDDI" },			\
+									\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH1,	"FH1" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH2,	"FH2" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS2,	"DS2" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS5,	"DS5" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS11,	"DS11" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS1,	"DS1" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS22,	"DS22" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM6,	"OFDM6" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM9,	"OFDM9" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM12,	"OFDM12" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM18,	"OFDM18" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM24,	"OFDM24" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM36,	"OFDM36" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM48,	"OFDM48" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM54,	"OFDM54" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM72,	"OFDM72" },		\
+									\
+	{ IFM_TDM|IFM_TDM_T1,		"t1" },				\
+	{ IFM_TDM|IFM_TDM_T1_AMI,	"t1-ami" },			\
+	{ IFM_TDM|IFM_TDM_E1,		"e1" },				\
+	{ IFM_TDM|IFM_TDM_E1_G704,	"e1-g.704" },			\
+	{ IFM_TDM|IFM_TDM_E1_AMI,	"e1-ami" },			\
+	{ IFM_TDM|IFM_TDM_E1_AMI_G704,	"e1-ami-g.704" },		\
+	{ IFM_TDM|IFM_TDM_T3,		"t3" },				\
+	{ IFM_TDM|IFM_TDM_T3_M13,	"t3-m13" },			\
+	{ IFM_TDM|IFM_TDM_E3,		"e3" },				\
+	{ IFM_TDM|IFM_TDM_E3_G751,	"e3-g.751" },			\
+	{ IFM_TDM|IFM_TDM_E3_G832,	"e3-g.832" },			\
+									\
+	{ 0, NULL },							\
+}
+
+#define IFM_MODE_DESCRIPTIONS {                                         \
+        { IFM_AUTO,                             "autoselect" },         \
+        { IFM_AUTO,                             "auto" },               \
+        { IFM_IEEE80211|IFM_IEEE80211_11A,      "11a" },                \
+        { IFM_IEEE80211|IFM_IEEE80211_11B,      "11b" },                \
+        { IFM_IEEE80211|IFM_IEEE80211_11G,      "11g" },                \
+        { IFM_IEEE80211|IFM_IEEE80211_FH,       "fh" },                 \
+        { 0, NULL },                                                    \
+}
+
+#define	IFM_OPTION_DESCRIPTIONS {					\
+	{ IFM_FDX,			"full-duplex" },		\
+	{ IFM_FDX,			"fdx" },			\
+	{ IFM_HDX,			"half-duplex" },		\
+	{ IFM_HDX,			"hdx" },			\
+	{ IFM_FLAG0,			"flag0" },			\
+	{ IFM_FLAG1,			"flag1" },			\
+	{ IFM_FLAG2,			"flag2" },			\
+	{ IFM_LOOP,			"loopback" },			\
+	{ IFM_LOOP,			"hw-loopback"},			\
+	{ IFM_LOOP,			"loop" },			\
+									\
+	{ IFM_ETHER|IFM_ETH_MASTER,	"master" },			\
+									\
+	{ IFM_TOKEN|IFM_TOK_ETR,	"EarlyTokenRelease" },		\
+	{ IFM_TOKEN|IFM_TOK_ETR,	"ETR" },			\
+	{ IFM_TOKEN|IFM_TOK_SRCRT,	"SourceRouting" },		\
+	{ IFM_TOKEN|IFM_TOK_SRCRT,	"SRCRT" },			\
+	{ IFM_TOKEN|IFM_TOK_ALLR,	"AllRoutes" },			\
+	{ IFM_TOKEN|IFM_TOK_ALLR,	"ALLR" },			\
+									\
+	{ IFM_FDDI|IFM_FDDI_DA,		"dual-attach" },		\
+	{ IFM_FDDI|IFM_FDDI_DA,		"das" },			\
+									\
+	{ IFM_IEEE80211|IFM_IEEE80211_ADHOC,	"adhoc" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_HOSTAP,	"hostap" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_IBSS,	"ibss" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_IBSSMASTER, "ibss-master" },	\
+	{ IFM_IEEE80211|IFM_IEEE80211_MONITOR,	"monitor" },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_TURBO,	"turbo" },		\
+									\
+	{ IFM_TDM|IFM_TDM_HDLC_CRC16,	"hdlc-crc16" },			\
+	{ IFM_TDM|IFM_TDM_PPP,		"ppp" },			\
+	{ IFM_TDM|IFM_TDM_FR_ANSI,	"framerelay-ansi" },		\
+	{ IFM_TDM|IFM_TDM_FR_CISCO,	"framerelay-cisco" },		\
+	{ IFM_TDM|IFM_TDM_FR_ANSI,	"framerelay-itu" },		\
+									\
+	{ 0, NULL },							\
+}
+
+/*
+ * Baudrate descriptions for the various media types.
+ */
+struct ifmedia_baudrate {
+	int	ifmb_word;		/* media word */
+	int	ifmb_baudrate;		/* corresponding baudrate */
+};
+
+#define	IFM_BAUDRATE_DESCRIPTIONS {					\
+	{ IFM_ETHER|IFM_10_T,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_2,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_5,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_100_TX,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_FX,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_T4,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_VG,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_100_T2,		IF_Mbps(100) },			\
+	{ IFM_ETHER|IFM_1000_SX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_10_STP,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_10_FL,		IF_Mbps(10) },			\
+	{ IFM_ETHER|IFM_1000_LX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_1000_CX,	IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_1000_T,		IF_Mbps(1000) },		\
+	{ IFM_ETHER|IFM_HPNA_1,		IF_Mbps(1) },			\
+									\
+	{ IFM_TOKEN|IFM_TOK_STP4,	IF_Mbps(4) },			\
+	{ IFM_TOKEN|IFM_TOK_STP16,	IF_Mbps(16) },			\
+	{ IFM_TOKEN|IFM_TOK_UTP4,	IF_Mbps(4) },			\
+	{ IFM_TOKEN|IFM_TOK_UTP16,	IF_Mbps(16) },			\
+									\
+	{ IFM_FDDI|IFM_FDDI_SMF,	IF_Mbps(100) },			\
+	{ IFM_FDDI|IFM_FDDI_MMF,	IF_Mbps(100) },			\
+	{ IFM_FDDI|IFM_FDDI_UTP,	IF_Mbps(100) },			\
+									\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH1, IF_Mbps(1) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_FH2, IF_Mbps(2) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS1, IF_Mbps(1) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS2, IF_Mbps(2) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS5, IF_Mbps(5) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS11, IF_Mbps(11) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_DS22, IF_Mbps(22) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM6, IF_Mbps(6) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM9, IF_Mbps(9) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM12, IF_Mbps(12) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM18, IF_Mbps(18) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM24, IF_Mbps(24) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM36, IF_Mbps(36) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM48, IF_Mbps(48) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM54, IF_Mbps(54) },		\
+	{ IFM_IEEE80211|IFM_IEEE80211_OFDM72, IF_Mbps(72) },		\
+									\
+	{ IFM_TDM|IFM_TDM_T1,		IF_Kbps(1536) },		\
+	{ IFM_TDM|IFM_TDM_T1_AMI,	IF_Kbps(1536) },		\
+	{ IFM_TDM|IFM_TDM_E1,		IF_Kbps(2048) },		\
+	{ IFM_TDM|IFM_TDM_E1_G704,	IF_Kbps(2048) },		\
+	{ IFM_TDM|IFM_TDM_E1_AMI,	IF_Kbps(2048) },		\
+	{ IFM_TDM|IFM_TDM_E1_AMI_G704,	IF_Kbps(2048) },		\
+	{ IFM_TDM|IFM_TDM_T3,		IF_Kbps(44736) },		\
+	{ IFM_TDM|IFM_TDM_T3_M13,	IF_Kbps(44736) },		\
+	{ IFM_TDM|IFM_TDM_E3,		IF_Kbps(34368) },		\
+	{ IFM_TDM|IFM_TDM_E3_G751,	IF_Kbps(34368) },		\
+	{ IFM_TDM|IFM_TDM_E3_G832,	IF_Kbps(34368) },		\
+									\
+	{ 0, 0 },							\
+}
+
+/*
+ * Status bit descriptions for the various media types.
+ */
+struct ifmedia_status_description {
+	int	ifms_type;
+	int	ifms_valid;
+	int	ifms_bit;
+	const char *ifms_string[2];
+};
+
+#define	IFM_STATUS_DESC(ifms, bit)					\
+	(ifms)->ifms_string[((ifms)->ifms_bit & (bit)) ? 1 : 0]
+
+#define	IFM_STATUS_DESCRIPTIONS {					\
+	{ IFM_ETHER,		IFM_AVALID,	IFM_ACTIVE,		\
+	    { "no carrier", "active" } },				\
+	{ IFM_FDDI,		IFM_AVALID,	IFM_ACTIVE,		\
+	    { "no ring", "inserted" } },				\
+	{ IFM_TOKEN,		IFM_AVALID,	IFM_ACTIVE,		\
+	    { "no ring", "inserted" } },				\
+	{ IFM_IEEE80211,	IFM_AVALID,	IFM_ACTIVE,		\
+	    { "no network", "active" } },				\
+	{ IFM_TDM,		IFM_AVALID,	IFM_ACTIVE,		\
+	    { "no carrier", "active" } },				\
+	{ IFM_CARP,		IFM_AVALID,	IFM_ACTIVE,		\
+	    { "backup", "master" } },					\
+	{ 0,			0,		0,			\
+	    { NULL, NULL } }						\
+}
+#endif	/* _NET_IF_MEDIA_H_ */
diff --git a/openbsd-compat/imsg-buffer.c b/openbsd-compat/imsg-buffer.c
new file mode 100644
index 0000000..dec27ff
--- /dev/null
+++ b/openbsd-compat/imsg-buffer.c
@@ -0,0 +1,303 @@
+/*	$OpenBSD: imsg-buffer.c,v 1.1 2010/05/26 16:44:32 nicm Exp $	*/
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int	ibuf_realloc(struct ibuf *, size_t);
+void	ibuf_enqueue(struct msgbuf *, struct ibuf *);
+void	ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+	struct ibuf	*buf;
+
+	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+		return (NULL);
+	if ((buf->buf = malloc(len)) == NULL) {
+		free(buf);
+		return (NULL);
+	}
+	buf->size = buf->max = len;
+	buf->fd = -1;
+
+	return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+	struct ibuf	*buf;
+
+	if (max < len)
+		return (NULL);
+
+	if ((buf = ibuf_open(len)) == NULL)
+		return (NULL);
+
+	if (max > 0)
+		buf->max = max;
+
+	return (buf);
+}
+
+int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+	u_char	*b;
+
+	/* on static buffers max is eq size and so the following fails */
+	if (buf->wpos + len > buf->max) {
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	b = realloc(buf->buf, buf->wpos + len);
+	if (b == NULL)
+		return (-1);
+	buf->buf = b;
+	buf->size = buf->wpos + len;
+
+	return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+	if (buf->wpos + len > buf->size)
+		if (ibuf_realloc(buf, len) == -1)
+			return (-1);
+
+	memcpy(buf->buf + buf->wpos, data, len);
+	buf->wpos += len;
+	return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+	void	*b;
+
+	if (buf->wpos + len > buf->size)
+		if (ibuf_realloc(buf, len) == -1)
+			return (NULL);
+
+	b = buf->buf + buf->wpos;
+	buf->wpos += len;
+	return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+	/* only allowed to seek in already written parts */
+	if (pos + len > buf->wpos)
+		return (NULL);
+
+	return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+	return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+	return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+	struct iovec	 iov[IOV_MAX];
+	struct ibuf	*buf;
+	unsigned int	 i = 0;
+	ssize_t	n;
+
+	bzero(&iov, sizeof(iov));
+	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+		if (i >= IOV_MAX)
+			break;
+		iov[i].iov_base = buf->buf + buf->rpos;
+		iov[i].iov_len = buf->wpos - buf->rpos;
+		i++;
+	}
+
+	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+		if (errno == EAGAIN || errno == ENOBUFS ||
+		    errno == EINTR)	/* try later */
+			return (0);
+		else
+			return (-1);
+	}
+
+	if (n == 0) {			/* connection closed */
+		errno = 0;
+		return (-2);
+	}
+
+	msgbuf_drain(msgbuf, n);
+
+	return (0);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+	free(buf->buf);
+	free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+	msgbuf->queued = 0;
+	msgbuf->fd = -1;
+	TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+	struct ibuf	*buf, *next;
+
+	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+	    buf = next) {
+		next = TAILQ_NEXT(buf, entry);
+		if (buf->rpos + n >= buf->wpos) {
+			n -= buf->wpos - buf->rpos;
+			ibuf_dequeue(msgbuf, buf);
+		} else {
+			buf->rpos += n;
+			n = 0;
+		}
+	}
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+	struct ibuf	*buf;
+
+	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+		ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+	struct iovec	 iov[IOV_MAX];
+	struct ibuf	*buf;
+	unsigned int	 i = 0;
+	ssize_t		 n;
+	struct msghdr	 msg;
+	struct cmsghdr	*cmsg;
+	union {
+		struct cmsghdr	hdr;
+		char		buf[CMSG_SPACE(sizeof(int))];
+	} cmsgbuf;
+
+	bzero(&iov, sizeof(iov));
+	bzero(&msg, sizeof(msg));
+	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+		if (i >= IOV_MAX)
+			break;
+		iov[i].iov_base = buf->buf + buf->rpos;
+		iov[i].iov_len = buf->wpos - buf->rpos;
+		i++;
+		if (buf->fd != -1)
+			break;
+	}
+
+	msg.msg_iov = iov;
+	msg.msg_iovlen = i;
+
+	if (buf != NULL && buf->fd != -1) {
+		msg.msg_control = (caddr_t)&cmsgbuf.buf;
+		msg.msg_controllen = sizeof(cmsgbuf.buf);
+		cmsg = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		*(int *)CMSG_DATA(cmsg) = buf->fd;
+	}
+
+	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+		if (errno == EAGAIN || errno == ENOBUFS ||
+		    errno == EINTR)	/* try later */
+			return (0);
+		else
+			return (-1);
+	}
+
+	if (n == 0) {			/* connection closed */
+		errno = 0;
+		return (-2);
+	}
+
+	/*
+	 * assumption: fd got sent if sendmsg sent anything
+	 * this works because fds are passed one at a time
+	 */
+	if (buf != NULL && buf->fd != -1) {
+		close(buf->fd);
+		buf->fd = -1;
+	}
+
+	msgbuf_drain(msgbuf, n);
+
+	return (0);
+}
+
+void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+	msgbuf->queued++;
+}
+
+void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+	if (buf->fd != -1)
+		close(buf->fd);
+
+	msgbuf->queued--;
+	ibuf_free(buf);
+}
diff --git a/openbsd-compat/imsg.c b/openbsd-compat/imsg.c
new file mode 100644
index 0000000..a0be894
--- /dev/null
+++ b/openbsd-compat/imsg.c
@@ -0,0 +1,271 @@
+/*	$OpenBSD: imsg.c,v 1.1 2010/05/26 16:44:32 nicm Exp $	*/
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int	 imsg_get_fd(struct imsgbuf *);
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+	msgbuf_init(&ibuf->w);
+	bzero(&ibuf->r, sizeof(ibuf->r));
+	ibuf->fd = fd;
+	ibuf->w.fd = fd;
+	ibuf->pid = getpid();
+	TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+	struct msghdr		 msg;
+	struct cmsghdr		*cmsg;
+	union {
+		struct cmsghdr hdr;
+		char	buf[CMSG_SPACE(sizeof(int) * 16)];
+	} cmsgbuf;
+	struct iovec		 iov;
+	ssize_t			 n;
+	int			 fd;
+	struct imsg_fd		*ifd;
+
+	bzero(&msg, sizeof(msg));
+
+	iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+	iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = &cmsgbuf.buf;
+	msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+	if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+		if (errno != EINTR && errno != EAGAIN) {
+			return (-1);
+		}
+		return (-2);
+	}
+
+	ibuf->r.wpos += n;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_RIGHTS) {
+			fd = (*(int *)CMSG_DATA(cmsg));
+			if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) {
+				close(fd);
+				return (-1);
+			}
+			ifd->fd = fd;
+			TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
+		}
+		/* we do not handle other ctl data level */
+	}
+
+	return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+	size_t			 av, left, datalen;
+
+	av = ibuf->r.wpos;
+
+	if (IMSG_HEADER_SIZE > av)
+		return (0);
+
+	memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+	if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+	    imsg->hdr.len > MAX_IMSGSIZE) {
+		errno = ERANGE;
+		return (-1);
+	}
+	if (imsg->hdr.len > av)
+		return (0);
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+	ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+	if ((imsg->data = malloc(datalen)) == NULL)
+		return (-1);
+
+	if (imsg->hdr.flags & IMSGF_HASFD)
+		imsg->fd = imsg_get_fd(ibuf);
+	else
+		imsg->fd = -1;
+
+	memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+	if (imsg->hdr.len < av) {
+		left = av - imsg->hdr.len;
+		memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+		ibuf->r.wpos = left;
+	} else
+		ibuf->r.wpos = 0;
+
+	return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+	struct ibuf	*wbuf;
+
+	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+		return (-1);
+
+	if (imsg_add(wbuf, data, datalen) == -1)
+		return (-1);
+
+	wbuf->fd = fd;
+
+	imsg_close(ibuf, wbuf);
+
+	return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+	struct ibuf	*wbuf;
+	int		 i, datalen = 0;
+
+	for (i = 0; i < iovcnt; i++)
+		datalen += iov[i].iov_len;
+
+	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+		return (-1);
+
+	for (i = 0; i < iovcnt; i++)
+		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+			return (-1);
+
+	wbuf->fd = fd;
+
+	imsg_close(ibuf, wbuf);
+
+	return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+    pid_t pid, u_int16_t datalen)
+{
+	struct ibuf	*wbuf;
+	struct imsg_hdr	 hdr;
+
+	datalen += IMSG_HEADER_SIZE;
+	if (datalen > MAX_IMSGSIZE) {
+		errno = ERANGE;
+		return (NULL);
+	}
+
+	hdr.type = type;
+	hdr.flags = 0;
+	hdr.peerid = peerid;
+	if ((hdr.pid = pid) == 0)
+		hdr.pid = ibuf->pid;
+	if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+		return (NULL);
+	}
+	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+		return (NULL);
+
+	return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, void *data, u_int16_t datalen)
+{
+	if (datalen)
+		if (ibuf_add(msg, data, datalen) == -1) {
+			ibuf_free(msg);
+			return (-1);
+		}
+	return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+	struct imsg_hdr	*hdr;
+
+	hdr = (struct imsg_hdr *)msg->buf;
+
+	hdr->flags &= ~IMSGF_HASFD;
+	if (msg->fd != -1)
+		hdr->flags |= IMSGF_HASFD;
+
+	hdr->len = (u_int16_t)msg->wpos;
+
+	ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+	free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+	int		 fd;
+	struct imsg_fd	*ifd;
+
+	if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+		return (-1);
+
+	fd = ifd->fd;
+	TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+	free(ifd);
+
+	return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+	while (ibuf->w.queued)
+		if (msgbuf_write(&ibuf->w) < 0)
+			return (-1);
+	return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+	int	fd;
+
+	msgbuf_clear(&ibuf->w);
+	while ((fd = imsg_get_fd(ibuf)) != -1)
+		close(fd);
+}
diff --git a/openbsd-compat/imsg.h b/openbsd-compat/imsg.h
new file mode 100644
index 0000000..d691f7d
--- /dev/null
+++ b/openbsd-compat/imsg.h
@@ -0,0 +1,112 @@
+/*	$OpenBSD: imsg.h,v 1.2 2010/06/23 07:53:55 nicm Exp $	*/
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IMSG_H_
+#define _IMSG_H_
+
+#define IBUF_READ_SIZE		65535
+#define IMSG_HEADER_SIZE	sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE		16384
+
+struct ibuf {
+	TAILQ_ENTRY(ibuf)	 entry;
+	u_char			*buf;
+	size_t			 size;
+	size_t			 max;
+	size_t			 wpos;
+	size_t			 rpos;
+	int			 fd;
+};
+
+struct msgbuf {
+	TAILQ_HEAD(, ibuf)	 bufs;
+	u_int32_t		 queued;
+	int			 fd;
+};
+
+struct ibuf_read {
+	u_char			 buf[IBUF_READ_SIZE];
+	u_char			*rptr;
+	size_t			 wpos;
+};
+
+struct imsg_fd {
+	TAILQ_ENTRY(imsg_fd)	entry;
+	int			fd;
+};
+
+struct imsgbuf {
+	TAILQ_HEAD(, imsg_fd)	 fds;
+	struct ibuf_read	 r;
+	struct msgbuf		 w;
+	int			 fd;
+	pid_t			 pid;
+};
+
+#define IMSGF_HASFD	1
+
+struct imsg_hdr {
+	u_int32_t	 type;
+	u_int16_t	 len;
+	u_int16_t	 flags;
+	u_int32_t	 peerid;
+	u_int32_t	 pid;
+};
+
+struct imsg {
+	struct imsg_hdr	 hdr;
+	int		 fd;
+	void		*data;
+};
+
+
+/* buffer.c */
+struct ibuf	*ibuf_open(size_t);
+struct ibuf	*ibuf_dynamic(size_t, size_t);
+int		 ibuf_add(struct ibuf *, const void *, size_t);
+void		*ibuf_reserve(struct ibuf *, size_t);
+void		*ibuf_seek(struct ibuf *, size_t, size_t);
+size_t		 ibuf_size(struct ibuf *);
+size_t		 ibuf_left(struct ibuf *);
+void		 ibuf_close(struct msgbuf *, struct ibuf *);
+int		 ibuf_write(struct msgbuf *);
+void		 ibuf_free(struct ibuf *);
+void		 msgbuf_init(struct msgbuf *);
+void		 msgbuf_clear(struct msgbuf *);
+int		 msgbuf_write(struct msgbuf *);
+void		 msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+void	 imsg_init(struct imsgbuf *, int);
+ssize_t	 imsg_read(struct imsgbuf *);
+ssize_t	 imsg_get(struct imsgbuf *, struct imsg *);
+int	 imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+	    int, void *, u_int16_t);
+int	 imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t,  pid_t,
+	    int, const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+	    u_int16_t);
+int	 imsg_add(struct ibuf *, void *, u_int16_t);
+void	 imsg_close(struct imsgbuf *, struct ibuf *);
+void	 imsg_free(struct imsg *);
+int	 imsg_flush(struct imsgbuf *);
+void	 imsg_clear(struct imsgbuf *);
+
+#endif
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
new file mode 100644
index 0000000..79322d5
--- /dev/null
+++ b/openbsd-compat/openbsd-compat.h
@@ -0,0 +1,87 @@
+/*
+ * $hrs: openbgpd/openbsd-compat/openbsd-compat.h,v 1.6 2011/07/03 04:46:38 hrs Exp $
+ */
+
+#ifndef _OPENBSD_COMPAT_H
+#define _OPENBSD_COMPAT_H
+
+#define	__dead
+
+/* bgpctl/bgpctl.c */
+#include <sys/endian.h>
+#define betoh64(x)	(be64toh(x))
+
+/* bgpd/irrfilter.c */
+typedef unsigned long	ulong;
+
+/* bgpd/bgpd.c */
+#ifndef	RTLABEL_LEN	/* defined in net/pfvar.h */
+#define RTLABEL_LEN	32
+#endif
+#define RTA_LABEL	0
+
+#define SIMPLEQ_FOREACH         STAILQ_FOREACH
+#define SIMPLEQ_FIRST           STAILQ_FIRST
+#define SIMPLEQ_REMOVE_HEAD     STAILQ_REMOVE_HEAD
+#define SIMPLEQ_INSERT_TAIL     STAILQ_INSERT_TAIL
+#define SIMPLEQ_ENTRY           STAILQ_ENTRY
+#define SIMPLEQ_HEAD            STAILQ_HEAD
+#define SIMPLEQ_INIT            STAILQ_INIT
+#define SIMPLEQ_HEAD_INITIALIZER	STAILQ_HEAD_INITIALIZER
+
+/* Routing priorities used by the different routing protocols */
+#define RTP_NONE        0       /* unset priority use sane default */
+#define RTP_CONNECTED   4       /* directly connected routes */
+#define RTP_STATIC      8       /* static routes base priority */
+#define RTP_OSPF        32      /* OSPF routes */
+#define RTP_ISIS        36      /* IS-IS routes */
+#define RTP_RIP         40      /* RIP routes */
+#define RTP_BGP         48      /* BGP routes */
+#define RTP_DEFAULT     56      /* routes that have nothing set */
+#define RTP_MAX         63      /* maximum priority */
+#define RTP_ANY         64      /* any of the above */
+#define RTP_MASK        0x7f
+#define RTP_DOWN        0x80    /* route/link is down */
+
+/* missing LINK_STATE_* macros in net/if.h */
+#define LINK_STATE_INVALID	LINK_STATE_UNKNOWN	/* link invalid */
+#define LINK_STATE_KALIVE_DOWN	7	/* keepalive reports down */
+#define LINK_STATE_HALF_DUPLEX	5	/* link is up and half duplex */
+#define LINK_STATE_FULL_DUPLEX	6	/* link is up and full duplex */
+
+/*
+ * Status bit descriptions for the various interface types.
+ */
+struct if_status_description {
+	unsigned char	ifs_type;
+	unsigned char	ifs_state;
+	const char *ifs_string;
+};
+
+#define LINK_STATE_DESC_MATCH(_ifs, _t, _s)				\
+	(((_ifs)->ifs_type == (_t) || (_ifs)->ifs_type == 0) &&		\
+	    (_ifs)->ifs_state == (_s))
+
+#define LINK_STATE_DESCRIPTIONS {					\
+	{ IFT_ETHER, LINK_STATE_DOWN, "no carrier" },			\
+									\
+	{ IFT_IEEE80211, LINK_STATE_DOWN, "no network" },		\
+									\
+	{ IFT_PPP, LINK_STATE_DOWN, "no carrier" },			\
+									\
+	{ IFT_CARP, LINK_STATE_DOWN, "backup" },			\
+	{ IFT_CARP, LINK_STATE_UP, "master" },				\
+	{ IFT_CARP, LINK_STATE_HALF_DUPLEX, "master" },			\
+	{ IFT_CARP, LINK_STATE_FULL_DUPLEX, "master" },			\
+									\
+	{ 0, LINK_STATE_UP, "active" },					\
+	{ 0, LINK_STATE_HALF_DUPLEX, "active" },			\
+	{ 0, LINK_STATE_FULL_DUPLEX, "active" },			\
+									\
+/*	{ 0, LINK_STATE_UNKNOWN, "unknown" },	*/			\
+	{ 0, LINK_STATE_INVALID, "invalid" },				\
+	{ 0, LINK_STATE_DOWN, "down" },					\
+	{ 0, LINK_STATE_KALIVE_DOWN, "keepalive down" },		\
+	{ 0, 0, NULL }							\
+}
+#endif /* _OPENBSD_COMPAT_H */
diff --git a/openbsd-compat/pfkey_compat.c b/openbsd-compat/pfkey_compat.c
new file mode 100644
index 0000000..b0d5907
--- /dev/null
+++ b/openbsd-compat/pfkey_compat.c
@@ -0,0 +1,32 @@
+#include "bgpd.h"
+#include "session.h"
+
+int
+pfkey_establish(struct peer *p)
+{
+	if (p->conf.auth.method)
+		return (-1);
+	return (0);
+}
+
+int
+pfkey_remove(struct peer *p)
+{
+        if (p->conf.auth.method)
+                return (-1);
+        return (0);
+}
+
+int
+pfkey_init(struct bgpd_sysdep *sysdep)
+{
+	log_warnx("no kernel support for PF_KEY");
+	sysdep->no_pfkey = 1;
+	return (-1);
+}
+
+int
+pfkey_read(int sd, struct sadb_msg *h)
+{
+	return (1);
+}
diff --git a/openbsd-compat/util.h b/openbsd-compat/util.h
new file mode 100644
index 0000000..aac35cf
--- /dev/null
+++ b/openbsd-compat/util.h
@@ -0,0 +1,119 @@
+/*	$OpenBSD: util.h,v 1.27 2006/06/14 02:14:25 krw Exp $	*/
+/*	$NetBSD: util.h,v 1.2 1996/05/16 07:00:22 thorpej Exp $	*/
+
+/*-
+ * Copyright (c) 1995
+ *	The Regents of the University of California.  All rights reserved.
+ * Portions Copyright (c) 1996, Jason Downs.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+/*
+ * fparseln() specific operation flags.
+ */
+#define FPARSELN_UNESCESC	0x01
+#define FPARSELN_UNESCCONT	0x02
+#define FPARSELN_UNESCCOMM	0x04
+#define FPARSELN_UNESCREST	0x08
+#define FPARSELN_UNESCALL	0x0f
+
+/*
+ * opendev() specific operation flags.
+ */
+#define OPENDEV_PART	0x01		/* Try to open the raw partition. */
+#define OPENDEV_BLCK	0x04		/* Open block, not character device. */
+
+/*
+ * uucplock(3) specific flags.
+ */
+#define UU_LOCK_INUSE (1)
+#define UU_LOCK_OK (0)
+#define UU_LOCK_OPEN_ERR (-1)
+#define UU_LOCK_READ_ERR (-2)
+#define UU_LOCK_CREAT_ERR (-3)
+#define UU_LOCK_WRITE_ERR (-4)
+#define UU_LOCK_LINK_ERR (-5)
+#define UU_LOCK_TRY_ERR (-6)
+#define UU_LOCK_OWNER_ERR (-7)
+
+/*
+ * fmt_scaled(3) specific flags.
+ */
+#define	FMT_SCALED_STRSIZE	7	/* minus sign, 4 digits, suffix, null byte */
+
+/*
+ * stub struct definitions.
+ */
+struct __sFILE;
+struct login_cap;
+struct passwd;
+struct termios;
+struct utmp;
+struct winsize;
+
+__BEGIN_DECLS
+char   *fparseln(struct __sFILE *, size_t *, size_t *, const char[3], int);
+void	login(struct utmp *);
+int	login_tty(int);
+int	logout(const char *);
+void	logwtmp(const char *, const char *, const char *);
+int	opendev(char *, int, int, char **);
+int	pidfile(const char *);
+void	pw_setdir(const char *);
+char   *pw_file(const char *);
+int	pw_lock(int retries);
+int	pw_mkdb(char *, int);
+int	pw_abort(void);
+void	pw_init(void);
+void	pw_edit(int, const char *);
+void	pw_prompt(void);
+void	pw_copy(int, int, const struct passwd *, const struct passwd *);
+int	pw_scan(char *, struct passwd *, int *);
+void	pw_error(const char *, int, int);
+int	openpty(int *, int *, char *, struct termios *, struct winsize *);
+int	opendisk(const char *path, int flags, char *buf, size_t buflen,
+	    int iscooked);
+pid_t	forkpty(int *, char *, struct termios *, struct winsize *);
+int	getmaxpartitions(void);
+int	getrawpartition(void);
+void	login_fbtab(const char *, uid_t, gid_t);
+int	login_check_expire(struct __sFILE *, struct passwd *, char *, int);
+char   *readlabelfs(char *, int);
+const char *uu_lockerr(int _uu_lockresult);
+int     uu_lock(const char *_ttyname);
+int	uu_lock_txfr(const char *_ttyname, pid_t _pid);
+int     uu_unlock(const char *_ttyname);
+int	fmt_scaled(long long number, char *result);
+int	scan_scaled(char *scaled, long long *result);
+__END_DECLS
+
+#endif /* !_UTIL_H_ */
--