summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/openbgpd/Makefile30
-rw-r--r--net/openbgpd/distinfo4
-rw-r--r--net/openbgpd/files/patch-Makefile12
-rw-r--r--net/openbgpd/files/patch-Makefile.am20
-rw-r--r--net/openbgpd/files/patch-Makefile.inc12
-rw-r--r--net/openbgpd/files/patch-bgpctl_Makefile31
-rw-r--r--net/openbgpd/files/patch-bgpctl_bgpctl.8287
-rw-r--r--net/openbgpd/files/patch-bgpctl_bgpctl.c1529
-rw-r--r--net/openbgpd/files/patch-bgpctl_irr_asset.c14
-rw-r--r--net/openbgpd/files/patch-bgpctl_irr_output.c14
-rw-r--r--net/openbgpd/files/patch-bgpctl_irr_parser.c48
-rw-r--r--net/openbgpd/files/patch-bgpctl_irr_prefix.c157
-rw-r--r--net/openbgpd/files/patch-bgpctl_irrfilter.c24
-rw-r--r--net/openbgpd/files/patch-bgpctl_irrfilter.h59
-rw-r--r--net/openbgpd/files/patch-bgpctl_mrtparser.c977
-rw-r--r--net/openbgpd/files/patch-bgpctl_mrtparser.h122
-rw-r--r--net/openbgpd/files/patch-bgpctl_parser.c400
-rw-r--r--net/openbgpd/files/patch-bgpctl_parser.h55
-rw-r--r--net/openbgpd/files/patch-bgpctl_whois.c18
-rw-r--r--net/openbgpd/files/patch-bgpd_Makefile30
-rw-r--r--net/openbgpd/files/patch-bgpd_bgpd.8348
-rw-r--r--net/openbgpd/files/patch-bgpd_bgpd.c693
-rw-r--r--net/openbgpd/files/patch-bgpd_bgpd.conf.5746
-rw-r--r--net/openbgpd/files/patch-bgpd_bgpd.h872
-rw-r--r--net/openbgpd/files/patch-bgpd_buffer.c104
-rw-r--r--net/openbgpd/files/patch-bgpd_carp.c54
-rw-r--r--net/openbgpd/files/patch-bgpd_config.c109
-rw-r--r--net/openbgpd/files/patch-bgpd_control.c171
-rw-r--r--net/openbgpd/files/patch-bgpd_imsg.c275
-rw-r--r--net/openbgpd/files/patch-bgpd_imsg.h115
-rw-r--r--net/openbgpd/files/patch-bgpd_kroute.c3140
-rw-r--r--net/openbgpd/files/patch-bgpd_log.c117
-rw-r--r--net/openbgpd/files/patch-bgpd_log.h39
-rw-r--r--net/openbgpd/files/patch-bgpd_mrt.c864
-rw-r--r--net/openbgpd/files/patch-bgpd_mrt.h287
-rw-r--r--net/openbgpd/files/patch-bgpd_name2id.c14
-rw-r--r--net/openbgpd/files/patch-bgpd_parse.y1626
-rw-r--r--net/openbgpd/files/patch-bgpd_pfkey.c471
-rw-r--r--net/openbgpd/files/patch-bgpd_pftable.c17
-rw-r--r--net/openbgpd/files/patch-bgpd_printconf.c439
-rw-r--r--net/openbgpd/files/patch-bgpd_rde.c2614
-rw-r--r--net/openbgpd/files/patch-bgpd_rde.h361
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_attr.c562
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_decide.c133
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_filter.c297
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_prefix.c301
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_rib.c513
-rw-r--r--net/openbgpd/files/patch-bgpd_rde_update.c644
-rw-r--r--net/openbgpd/files/patch-bgpd_session.c2075
-rw-r--r--net/openbgpd/files/patch-bgpd_session.h188
-rw-r--r--net/openbgpd/files/patch-bgpd_timer.c32
-rw-r--r--net/openbgpd/files/patch-bgpd_util.c440
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_fmt_scaled.c275
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_hash.h134
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_if_media.h619
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_imsg-buffer.c312
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_imsg.c312
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_imsg.h119
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_openbsd-compat.h98
-rw-r--r--net/openbgpd/files/patch-openbsd-compat_util.h126
-rw-r--r--net/openbgpd/pkg-plist6
61 files changed, 24467 insertions, 38 deletions
diff --git a/net/openbgpd/Makefile b/net/openbgpd/Makefile
index 346d4be10e53..3262c1935d70 100644
--- a/net/openbgpd/Makefile
+++ b/net/openbgpd/Makefile
@@ -1,9 +1,13 @@
# $FreeBSD$
PORTNAME= openbgpd
-PORTVERSION= 6.5p0
+PORTVERSION= 5.2.20121209
+PORTREVISION= 3
+PORTEPOCH= 1
CATEGORIES= net
MASTER_SITES= OPENBSD/OpenBGPD
+DISTNAME= ${PORTNAME}-4.6
+DIST_SUBDIR= ${PORTNAME}
MAINTAINER= hrs@FreeBSD.org
COMMENT= Free implementation of the Border Gateway Protocol, Version 4
@@ -11,21 +15,27 @@ COMMENT= Free implementation of the Border Gateway Protocol, Version 4
LICENSE= ISCL
LICENSE_FILE= ${FILESDIR}/COPYING
-USES= autoreconf libtool uidfix
-USE_RC_SUBR= ${PORTNAME}
+CONFLICTS= zebra-[0-9]* quagga-[0-9]*
-GNU_CONFIGURE= yes
+USES= tar:tgz uidfix
+USE_RC_SUBR= ${PORTNAME}
+NO_WRKSUBDIR= yes
+PLIST_FILES= sbin/bgpctl sbin/bgpd man/man5/bgpd.conf.5.gz \
+ man/man8/bgpctl.8.gz man/man8/bgpd.8.gz
SUB_FILES= pkg-message
-
USERS= _bgpd
GROUPS= _bgpd
-CONFLICTS= zebra-[0-9]* quagga-[0-9]*
-
-OPTIONS_DEFINE= IPV6LLPEER
-OPTIONS_DEFAULT= IPV6LLPEER
-IPV6LLPEER_DESC= Support nexthop using IPv6 link-local address
+OPTIONS_DEFINE= IPV6LLPEER
+OPTIONS_DEFAULT=IPV6LLPEER
+IPV6LLPEER_DESC=Support nexthop using IPv6 link-local address
IPV6LLPEER_MAKE_ARGS= -DIPV6_LINKLOCAL_PEER
+post-patch:
+ @${REINPLACE_CMD} -e "s|%%PREFIX%%|${PREFIX}|g" \
+ ${WRKSRC}/bgpd/bgpd.8 \
+ ${WRKSRC}/bgpd/bgpd.conf.5 \
+ ${WRKSRC}/bgpctl/bgpctl.8
+
.include <bsd.port.mk>
diff --git a/net/openbgpd/distinfo b/net/openbgpd/distinfo
index 106b1935ec8b..a4c5403642fe 100644
--- a/net/openbgpd/distinfo
+++ b/net/openbgpd/distinfo
@@ -1,3 +1,3 @@
TIMESTAMP = 1556692508
-SHA256 (openbgpd-6.5p0.tar.gz) = 20c1a40bafcbbea60c4ecc6dd2e87fcba6847bfad62739b705a3806b6b442a56
-SIZE (openbgpd-6.5p0.tar.gz) = 677691
+SHA256 (openbgpd/openbgpd-4.6.tgz) = d9a0a3542e5ec744889ca12871f01aa1d86f12844e093010f37d0601796e15cf
+SIZE (openbgpd/openbgpd-4.6.tgz) = 168197
diff --git a/net/openbgpd/files/patch-Makefile b/net/openbgpd/files/patch-Makefile
new file mode 100644
index 000000000000..77ae22e026e6
--- /dev/null
+++ b/net/openbgpd/files/patch-Makefile
@@ -0,0 +1,12 @@
+Index: Makefile
+===================================================================
+RCS file: Makefile
+diff -N Makefile
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ Makefile 30 Jun 2009 07:07:55 -0000 1.2
+@@ -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/net/openbgpd/files/patch-Makefile.am b/net/openbgpd/files/patch-Makefile.am
deleted file mode 100644
index da85a8ad6103..000000000000
--- a/net/openbgpd/files/patch-Makefile.am
+++ /dev/null
@@ -1,20 +0,0 @@
---- Makefile.am.orig 2019-05-01 11:22:14 UTC
-+++ Makefile.am
-@@ -19,13 +19,14 @@
- EXTRA_DIST = README.md VERSION bgpd.conf
-
- install-data-hook:
-- @if [ ! -d "$(DESTDIR)$(localstatedir)/run" ]; then \
-+ if [ ! -d "$(DESTDIR)$(localstatedir)/run" ]; then \
- $(INSTALL) -m 755 -d "$(DESTDIR)$(localstatedir)/run"; \
- fi
-- @if [ ! -d "$(DESTDIR)$(sysconfdir)" ]; then \
-+ if [ ! -d "$(DESTDIR)$(sysconfdir)" ]; then \
- $(INSTALL) -m 755 -d "$(DESTDIR)$(sysconfdir)"; \
- fi
-- @if [ ! -f "$(DESTDIR)$(sysconfdir)/bgpd.conf" ]; then \
-+ $(INSTALL) -m 644 "$(srcdir)/bgpd.conf" "$(DESTDIR)$(sysconfdir)/bgpd.conf.sample"; \
-+ if [ ! -f "$(DESTDIR)$(sysconfdir)/bgpd.conf" ]; then \
- $(INSTALL) -m 644 "$(srcdir)/bgpd.conf" "$(DESTDIR)$(sysconfdir)/bgpd.conf"; \
- else \
- echo; \
diff --git a/net/openbgpd/files/patch-Makefile.inc b/net/openbgpd/files/patch-Makefile.inc
new file mode 100644
index 000000000000..7a2bef96c0d8
--- /dev/null
+++ b/net/openbgpd/files/patch-Makefile.inc
@@ -0,0 +1,12 @@
+Index: Makefile.inc
+===================================================================
+RCS file: Makefile.inc
+diff -N Makefile.inc
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ Makefile.inc 16 May 2014 01:06:14 -0000 1.5
+@@ -0,0 +1,5 @@
++# $hrs: openbgpd/Makefile.inc,v 1.5 2014/05/16 01:06:14 hrs Exp $
++
++PREFIX?= /usr/local
++BINDIR?= ${PREFIX}/sbin
++MANDIR?= ${PREFIX}/man/man
diff --git a/net/openbgpd/files/patch-bgpctl_Makefile b/net/openbgpd/files/patch-bgpctl_Makefile
new file mode 100644
index 000000000000..572c0fcb8362
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_Makefile
@@ -0,0 +1,31 @@
+Index: bgpctl/Makefile
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/Makefile,v
+retrieving revision 1.1.1.1
+retrieving revision 1.4
+diff -u -p -r1.1.1.1 -r1.4
+--- bgpctl/Makefile 30 Jun 2009 05:46:15 -0000 1.1.1.1
++++ bgpctl/Makefile 13 Oct 2012 18:35:56 -0000 1.4
+@@ -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+= irr_parser.c mrtparser.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/net/openbgpd/files/patch-bgpctl_bgpctl.8 b/net/openbgpd/files/patch-bgpctl_bgpctl.8
new file mode 100644
index 000000000000..2e08c575981e
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_bgpctl.8
@@ -0,0 +1,287 @@
+Index: bgpctl/bgpctl.8
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/bgpctl.8,v
+retrieving revision 1.1.1.6
+retrieving revision 1.6
+diff -u -p -r1.1.1.6 -r1.6
+--- bgpctl/bgpctl.8 14 Feb 2010 20:20:13 -0000 1.1.1.6
++++ bgpctl/bgpctl.8 13 Oct 2012 18:35:56 -0000 1.6
+@@ -1,4 +1,4 @@
+-.\" $OpenBSD: bgpctl.8,v 1.49 2009/06/06 06:11:17 claudio Exp $
++.\" $OpenBSD: bgpctl.8,v 1.59 2012/05/27 20:49:42 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 27 2012 $
+ .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,12 +113,21 @@ Note that the neighbor is not obliged to
+ 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
+ Remove all dynamically added prefixes from the list of announced networks.
++.It Cm network mrt file Ar file filter
++Import networks from an MRT table dump for debugging purposes.
++.Ar filter
++can be specified similarly to the
++.Ar show mrt
++command.
++Only networks matching the filter will be imported.
+ .It Cm network show Ar family
+ Show all announced networks.
+ .Ar family ,
+@@ -122,7 +146,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,9 +157,81 @@ 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.
++.It Xo
++.Cm show mrt
++.Op Ar options
++.Ar filter
++.Xc
++Show routes from an MRT table dump file.
++.Ar filter
++can be an IP address, a CIDR prefix, an AS filter, a combination or nothing:
++.Pp
++.Bl -tag -width "address/len all" -compact
++.It Ar address
++Show best matching route for address.
++.It Ar address Ns Li / Ns Ar len
++Show RIB entry for this CIDR prefix.
++.It Xo
++.Ar address Ns Li / Ns Ar len
++.Cm all
++.Xc
++Show all entries in the specified range.
++.\".It Ar address/len Cm longer-prefixes
++.It Cm as Ar as
++Show all entries with
++.Ar as
++anywhere in the AS path.
++.It Cm empty-as
++Show all entries that are internal routes with no AS's in the AS path.
++.It Cm neighbor Ar ip
++Show only entries from the specified peer.
++.It Cm peer-as Ar as
++Show all entries with
++.Ar as
++as leftmost AS.
++.It Cm source-as Ar as
++Show all entries with
++.Ar as
++as rightmost AS.
++.It Cm transit-as Ar as
++Show all entries with
++.Ar as
++anywhere but rightmost.
++.El
++.Pp
++Additionally, the following
++.Ar options
++are defined:
++.Pp
++.Bl -tag -width "file name" -compact
++.It Cm detail
++Show more detailed output for matching routes.
++.It Ar family
++Limit the output to the given address family.
++.It Cm file Ar name
++Read the MRT dump from file
++.Ar name
++instead of using stdin.
++.El
++.Pp
++Multiple options and filters can be used at the same time.
++.It Cm show summary
++Show a list of all neighbors, including information about the session state
++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 neighbor Ar peer modifier
+ Show detailed information about the neighbor identified by
+ .Ar peer ,
+@@ -183,33 +279,33 @@ Show all entries in the specified range.
+ Show all entries with
+ .Ar as
+ anywhere in the AS path.
+-.It Cm source-as Ar as
+-Show all entries with
+-.Ar as
+-as rightmost AS.
+-.It Cm transit-as Ar as
+-Show all entries with
+-.Ar as
+-anywhere but rightmost.
+-.It Cm peer-as Ar as
+-Show all entries with
+-.Ar as
+-as leftmost AS.
+-.It Cm empty-as
+-Show all entries that are internal routes with no AS's in the AS path.
+ .It Cm community Ar community
+ Show all entries with community
+ .Ar community .
++.It Cm empty-as
++Show all entries that are internal routes with no AS's in the AS path.
++.It Cm memory
++Show RIB memory statistics.
+ .It Cm neighbor Ar peer
+ Show only entries from the specified peer.
+-.It Cm table Ar rib
+-Show only entries from the specified RIB table.
++.It Cm peer-as Ar as
++Show all entries with
++.Ar as
++as leftmost AS.
++.It Cm source-as Ar as
++Show all entries with
++.Ar as
++as rightmost AS.
+ .It Cm summary
+ This is the same as the
+ .Ic show summary
+ command.
+-.It Cm memory
+-Show RIB memory statistics.
++.It Cm table Ar rib
++Show only entries from the specified RIB table.
++.It Cm transit-as Ar as
++Show all entries with
++.Ar as
++anywhere but rightmost.
+ .El
+ .Pp
+ Additionally, the following
+@@ -217,8 +313,10 @@ Additionally, the following
+ are defined:
+ .Pp
+ .Bl -tag -width "detail" -compact
++.It Cm selected
++Show only selected routes.
+ .It Cm detail
+-Show more detailed output for matched routes.
++Show more detailed output for matching routes.
+ .It Ar family
+ Limit the output to the given address family.
+ .It Cm in
+@@ -243,10 +341,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
+@@ -260,10 +360,19 @@ control socket
+ .Xr bgpd 8 ,
+ .Xr bgplg 8 ,
+ .Xr bgplgsh 8
++.Sh STANDARDS
+ .Rs
+-.%R RFC 2622
+-.%T "Routing Policy Specification Language (RPSL)"
++.%A C. Alaettinoglu
++.%A C. Villamizar
++.%A E. Gerich
++.%A D. Kessens
++.%A D. Meyer
++.%A T. Bates
++.%A D. Karrenberg
++.%A M. Terpstra
+ .%D June 1999
++.%R RFC 2622
++.%T Routing Policy Specification Language (RPSL)
+ .Re
+ .Sh HISTORY
+ The
diff --git a/net/openbgpd/files/patch-bgpctl_bgpctl.c b/net/openbgpd/files/patch-bgpctl_bgpctl.c
new file mode 100644
index 000000000000..1553efc83281
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_bgpctl.c
@@ -0,0 +1,1529 @@
+Index: bgpctl/bgpctl.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/bgpctl.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.10
+diff -u -p -r1.1.1.7 -r1.10
+--- bgpctl/bgpctl.c 14 Feb 2010 20:20:14 -0000 1.1.1.7
++++ bgpctl/bgpctl.c 8 Dec 2012 20:17:55 -0000 1.10
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: bgpctl.c,v 1.142 2009/06/06 06:33:15 eric Exp $ */
++/* $OpenBSD: bgpctl.c,v 1.167 2012/11/15 19:55:08 sthen 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"
+@@ -37,6 +49,11 @@
+ #include "log.h"
+ #include "parser.h"
+ #include "irrfilter.h"
++#include "mrtparser.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,
+@@ -50,12 +67,14 @@ int show_summary_msg(struct imsg *, in
+ 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_capa_restart(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 +84,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);
+@@ -73,16 +92,25 @@ const char * print_origin(u_int8_t, int
+ void print_flags(u_int8_t, int);
+ int show_rib_summary_msg(struct imsg *);
+ int show_rib_detail_msg(struct imsg *, int);
++void show_rib_brief(struct ctl_show_rib *, u_char *);
++void show_rib_detail(struct ctl_show_rib *, u_char *, int);
++void show_attr(void *, u_int16_t);
+ 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 *);
+ void send_filterset(struct imsgbuf *, struct filter_set_head *);
+ static const char *get_errstr(u_int8_t, u_int8_t);
+ int show_result(struct imsg *);
++void show_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *);
++void network_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *);
++void show_mrt_state(struct mrt_bgp_state *, void *);
++void show_mrt_msg(struct mrt_bgp_msg *, void *);
++void mrt_to_bgpd_addr(union mrt_addr *, struct bgpd_addr *);
+
+ struct imsgbuf *ibuf;
++struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg };
++struct mrt_parser net_mrt = { network_mrt_dump, NULL, NULL };
+
+ __dead void
+ usage(void)
+@@ -98,7 +126,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 +156,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));
+@@ -154,7 +185,7 @@ main(int argc, char *argv[])
+ case NONE:
+ case IRRFILTER:
+ usage();
+- /* not reached */
++ /* NOTREACHED */
+ case SHOW:
+ case SHOW_SUMMARY:
+ imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0);
+@@ -164,24 +195,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 +231,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 +245,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;
+@@ -217,15 +256,35 @@ main(int argc, char *argv[])
+ sizeof(res->community));
+ type = IMSG_CTL_SHOW_RIB_COMMUNITY;
+ }
+- memcpy(&ribreq.neighbor, &neighbor,
+- sizeof(ribreq.neighbor));
++ 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))
+ show_rib_summary_head();
+ break;
++ case SHOW_MRT:
++ close(fd);
++ bzero(&ribreq, sizeof(ribreq));
++ if (res->as.type != AS_NONE)
++ memcpy(&ribreq.as, &res->as, sizeof(res->as));
++ if (res->addr.aid) {
++ memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr));
++ ribreq.prefixlen = res->prefixlen;
++ }
++ if (res->community.as != COMMUNITY_UNSET &&
++ res->community.type != COMMUNITY_UNSET)
++ memcpy(&ribreq.community, &res->community,
++ sizeof(res->community));
++ memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor));
++ ribreq.aid = res->aid;
++ ribreq.flags = res->flags;
++ show_mrt.arg = &ribreq;
++ if (!(res->flags & F_CTL_DETAIL))
++ show_rib_summary_head();
++ mrt_parse(res->mrtfd, &show_mrt, 1);
++ exit(0);
+ case SHOW_RIB_MEM:
+ imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0);
+ break;
+@@ -237,12 +296,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 +351,40 @@ 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 NETWORK_MRT:
++ bzero(&ribreq, sizeof(ribreq));
++ if (res->as.type != AS_NONE)
++ memcpy(&ribreq.as, &res->as, sizeof(res->as));
++ if (res->addr.aid) {
++ memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr));
++ ribreq.prefixlen = res->prefixlen;
++ }
++ if (res->community.as != COMMUNITY_UNSET &&
++ res->community.type != COMMUNITY_UNSET)
++ memcpy(&ribreq.community, &res->community,
++ sizeof(res->community));
++ memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor));
++ ribreq.aid = res->aid;
++ ribreq.flags = res->flags;
++ net_mrt.arg = &ribreq;
++ mrt_parse(res->mrtfd, &net_mrt, 1);
++ done = 1;
++ 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 +393,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 +418,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 +447,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 +461,10 @@ main(int argc, char *argv[])
+ case NETWORK_REMOVE:
+ case NETWORK_FLUSH:
+ case IRRFILTER:
++ case LOG_VERBOSE:
++ case LOG_BRIEF:
++ case SHOW_MRT:
++ case NETWORK_MRT:
+ break;
+ }
+ imsg_free(&imsg);
+@@ -398,8 +490,8 @@ fmt_peer(const char *descr, const struct
+ }
+
+ 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 {
+@@ -430,7 +522,7 @@ show_summary_msg(struct imsg *imsg, int
+ p->conf.remote_masklen, nodescr);
+ if (strlen(s) >= 20)
+ s[20] = 0;
+- printf("%-20s %8s %10llu %10llu %5u %-8s ",
++ printf("%-20s %8s %10" PRIu64 " %10" PRIu64 " %5u %-8s ",
+ s, log_as(p->conf.remote_as),
+ p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
+ p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
+@@ -492,8 +584,8 @@ show_neighbor_terse(struct imsg *imsg)
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_NEIGHBOR:
+ p = imsg->data;
+- printf("%llu %llu %llu %llu %llu %llu %llu "
+- "%llu %llu %llu %u %u %llu %llu %llu %llu\n",
++ printf("%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " "
++ "%" PRIu64 " %" PRIu64 " %" PRIu64 " %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ p->stats.msg_sent_open, p->stats.msg_rcvd_open,
+ p->stats.msg_sent_notification,
+ p->stats.msg_rcvd_notification,
+@@ -521,13 +613,15 @@ show_neighbor_msg(struct imsg *imsg, enu
+ 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,9 +643,20 @@ show_neighbor_msg(struct imsg *imsg, enu
+ 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);
++ if (p->conf.max_prefix) {
++ printf(" Max-prefix: %u", p->conf.max_prefix);
++ if (p->conf.max_prefix_restart)
++ printf(" (restart %u)",
++ p->conf.max_prefix_restart);
++ printf("\n");
++ }
+ printf(" BGP version 4, remote router-id %s\n",
+ inet_ntoa(ina));
+ printf(" BGP state = %s", statenames[p->state]);
+@@ -563,22 +668,24 @@ show_neighbor_msg(struct imsg *imsg, enu
+ 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.grestart.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");
+- if (p->capa.peer.restart)
+- printf(" Graceful Restart\n");
++ if (p->capa.peer.grestart.restart) {
++ printf(" Graceful Restart");
++ print_neighbor_capa_restart(p);
++ printf("\n");
++ }
+ if (p->capa.peer.as4byte)
+ printf(" 4-byte AS numbers\n");
+ }
+@@ -633,20 +740,38 @@ show_neighbor_msg(struct imsg *imsg, enu
+ }
+
+ 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
++print_neighbor_capa_restart(struct peer *p)
++{
++ int comma;
++ u_int8_t i;
++
++ if (p->capa.peer.grestart.timeout)
++ printf(": Timeout: %d, ", p->capa.peer.grestart.timeout);
++ for (i = 0, comma = 0; i < AID_MAX; i++)
++ if (p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT) {
++ if (!comma &&
++ p->capa.peer.grestart.flags[i] & CAPA_GR_RESTART)
++ printf("restarted, ");
++ if (comma)
++ printf(", ");
++ printf("%s", aid2str(i));
++ if (p->capa.peer.grestart.flags[i] & CAPA_GR_FORWARD)
++ printf(" (preserved)");
++ comma = 1;
++ }
+ }
+
+ void
+@@ -654,17 +779,17 @@ print_neighbor_msgstats(struct peer *p)
+ {
+ printf(" Message statistics:\n");
+ printf(" %-15s %-10s %-10s\n", "", "Sent", "Received");
+- printf(" %-15s %10llu %10llu\n", "Opens",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Opens",
+ p->stats.msg_sent_open, p->stats.msg_rcvd_open);
+- printf(" %-15s %10llu %10llu\n", "Notifications",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Notifications",
+ p->stats.msg_sent_notification, p->stats.msg_rcvd_notification);
+- printf(" %-15s %10llu %10llu\n", "Updates",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Updates",
+ p->stats.msg_sent_update, p->stats.msg_rcvd_update);
+- printf(" %-15s %10llu %10llu\n", "Keepalives",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Keepalives",
+ p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive);
+- printf(" %-15s %10llu %10llu\n", "Route Refresh",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Route Refresh",
+ p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh);
+- printf(" %-15s %10llu %10llu\n\n", "Total",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n\n", "Total",
+ p->stats.msg_sent_open + p->stats.msg_sent_notification +
+ p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
+ p->stats.msg_sent_rrefresh,
+@@ -673,14 +798,16 @@ print_neighbor_msgstats(struct peer *p)
+ p->stats.msg_rcvd_rrefresh);
+ printf(" Update statistics:\n");
+ printf(" %-15s %-10s %-10s\n", "", "Sent", "Received");
+- printf(" %-15s %10llu %10llu\n", "Updates",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Updates",
+ p->stats.prefix_sent_update, p->stats.prefix_rcvd_update);
+- printf(" %-15s %10llu %10llu\n", "Withdraws",
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "Withdraws",
+ p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw);
++ printf(" %-15s %10" PRIu64 " %10" PRIu64 "\n", "End-of-Rib",
++ p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor);
+ }
+
+ void
+-print_timer(const char *name, timer_t d)
++print_timer(const char *name, time_t d)
+ {
+ printf(" %-20s ", name);
+
+@@ -745,6 +872,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 +921,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;
+-
+- show_fib_flags(k6->flags);
++ kt = imsg->data;
+
+- 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 +969,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 +1054,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 +1091,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), "%" PRIu64 " 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), "%" PRIu64 " 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), "%" PRIu64 " K%s",
++ baudrate / IF_Kbps(1), unit);
+ else
+- printf("%llu Bit/s", baudrate);
++ snprintf(bbuf, sizeof(bbuf), "%" PRIu64 " %s",
++ baudrate, unit);
++
++ return (bbuf);
+ }
+
+ int
+@@ -982,17 +1137,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:
+@@ -1008,10 +1158,10 @@ show_interface_msg(struct imsg *imsg)
+ void
+ show_rib_summary_head(void)
+ {
+- printf(
+- "flags: * = Valid, > = Selected, I = via IBGP, A = Announced\n");
++ printf("flags: * = Valid, > = Selected, I = via IBGP, A = Announced, "
++ "S = Stale\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 +1199,30 @@ 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_STALE)
++ *p++ = 'S';
++ 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_STALE)
++ printf(", stale");
++ 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");
+ }
+ }
+@@ -1077,27 +1231,14 @@ int
+ show_rib_summary_msg(struct imsg *imsg)
+ {
+ struct ctl_show_rib rib;
+- char *aspath;
+ u_char *asdata;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_RIB:
+ memcpy(&rib, imsg->data, sizeof(rib));
+-
+- print_prefix(&rib.prefix, rib.prefixlen, rib.flags);
+- printf("%-15s ", log_addr(&rib.exit_nexthop));
+-
+- printf(" %5u %5u ", rib.local_pref, rib.med);
+-
+ asdata = imsg->data;
+ asdata += sizeof(struct ctl_show_rib);
+- if (aspath_asprint(&aspath, asdata, rib.aspath_len) == -1)
+- err(1, NULL);
+- if (strlen(aspath) > 0)
+- printf("%s ", aspath);
+- free(aspath);
+-
+- printf("%s\n", print_origin(rib.origin, 1));
++ show_rib_brief(&rib, asdata);
+ break;
+ case IMSG_CTL_END:
+ return (1);
+@@ -1112,108 +1253,21 @@ int
+ show_rib_detail_msg(struct imsg *imsg, int nodescr)
+ {
+ struct ctl_show_rib rib;
+- struct in_addr id;
+- char *aspath, *s;
+- u_char *data;
+- u_int32_t as;
+- u_int16_t ilen, alen, ioff;
+- u_int8_t flags, type;
+- time_t now;
++ u_char *asdata;
++ u_int16_t ilen;
+
+ switch (imsg->hdr.type) {
+ case IMSG_CTL_SHOW_RIB:
+ memcpy(&rib, imsg->data, sizeof(rib));
+-
+- printf("\nBGP routing table entry for %s/%u\n",
+- log_addr(&rib.prefix), rib.prefixlen);
+-
+- data = imsg->data;
+- data += sizeof(struct ctl_show_rib);
+- if (aspath_asprint(&aspath, data, rib.aspath_len) == -1)
+- err(1, NULL);
+- if (strlen(aspath) > 0)
+- printf(" %s\n", aspath);
+- free(aspath);
+-
+- s = fmt_peer(rib.descr, &rib.remote_addr, -1, nodescr);
+- printf(" Nexthop %s ", log_addr(&rib.exit_nexthop));
+- printf("(via %s) from %s (", log_addr(&rib.true_nexthop), s);
+- free(s);
+- id.s_addr = htonl(rib.remote_id);
+- printf("%s)\n", inet_ntoa(id));
+-
+- printf(" Origin %s, metric %u, localpref %u, ",
+- print_origin(rib.origin, 0), rib.med, rib.local_pref);
+- print_flags(rib.flags, 0);
+-
+- now = time(NULL);
+- if (now > rib.lastchange)
+- now -= rib.lastchange;
+- else
+- now = 0;
+-
+- printf("\n Last update: %s ago\n",
+- fmt_timeframe_core(now));
++ asdata = imsg->data;
++ asdata += sizeof(struct ctl_show_rib);
++ show_rib_detail(&rib, asdata, nodescr);
+ break;
+ case IMSG_CTL_SHOW_RIB_ATTR:
+ ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ if (ilen < 3)
+ errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
+- data = imsg->data;
+- flags = data[0];
+- type = data[1];
+-
+- /* get the attribute length */
+- if (flags & ATTR_EXTLEN) {
+- if (ilen < 4)
+- errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
+- memcpy(&alen, data+2, sizeof(u_int16_t));
+- alen = ntohs(alen);
+- data += 4;
+- ilen -= 4;
+- } else {
+- alen = data[2];
+- data += 3;
+- ilen -= 3;
+- }
+- /* bad imsg len how can that happen!? */
+- if (alen != ilen)
+- errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
+-
+- switch (type) {
+- case ATTR_COMMUNITIES:
+- printf(" Communities: ");
+- show_community(data, alen);
+- printf("\n");
+- break;
+- 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));
+- break;
+- case ATTR_ORIGINATOR_ID:
+- memcpy(&id, data, sizeof(id));
+- printf(" Originator Id: %s\n", inet_ntoa(id));
+- break;
+- case ATTR_CLUSTER_LIST:
+- printf(" Cluster ID List:");
+- for (ioff = 0; ioff + sizeof(id) <= ilen;
+- ioff += sizeof(id)) {
+- memcpy(&id, data + ioff, sizeof(id));
+- printf(" %s", inet_ntoa(id));
+- }
+- printf("\n");
+- break;
+- case ATTR_EXT_COMMUNITIES:
+- printf(" Ext. communities: ");
+- show_ext_community(data, alen);
+- printf("\n");
+- break;
+- default:
+- /* ignore unknown attributes */
+- break;
+- }
++ show_attr(imsg->data, ilen);
+ break;
+ case IMSG_CTL_END:
+ printf("\n");
+@@ -1225,67 +1279,128 @@ show_rib_detail_msg(struct imsg *imsg, i
+ return (0);
+ }
+
+-char *
+-fmt_mem(int64_t num)
++void
++show_rib_brief(struct ctl_show_rib *r, u_char *asdata)
+ {
+- static char buf[16];
++ char *aspath;
+
+- if (fmt_scaled(num, buf) == -1)
+- snprintf(buf, sizeof(buf), "%lldB", (long long)num);
++ print_prefix(&r->prefix, r->prefixlen, r->flags);
++ printf(" %-15s ", log_addr(&r->exit_nexthop));
++ printf(" %5u %5u ", r->local_pref, r->med);
+
+- return (buf);
++ if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1)
++ err(1, NULL);
++ if (strlen(aspath) > 0)
++ printf("%s ", aspath);
++ free(aspath);
++
++ printf("%s\n", print_origin(r->origin, 1));
+ }
+
+-int
+-show_rib_memory_msg(struct imsg *imsg)
++void
++show_rib_detail(struct ctl_show_rib *r, u_char *asdata, int nodescr)
+ {
+- struct rde_memstats stats;
++ struct in_addr id;
++ char *aspath, *s;
++ time_t now;
+
+- 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)));
+- printf("%10lld rib entries using %s of memory\n",
+- (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt *
+- sizeof(struct rib_entry)));
+- printf("%10lld prefix entries using %s of memory\n",
+- (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt *
+- sizeof(struct prefix)));
+- printf("%10lld BGP path attribute entries using %s of memory\n",
+- (long long)stats.path_cnt, fmt_mem(stats.path_cnt *
+- sizeof(struct rde_aspath)));
+- printf("%10lld BGP AS-PATH attribute entries using "
+- "%s of memory,\n\t and holding %lld references\n",
+- (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size),
+- (long long)stats.aspath_refs);
+- printf("%10lld BGP attributes entries using %s of memory\n",
+- (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt *
+- sizeof(struct attr)));
+- printf("\t and holding %lld references\n",
+- (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) +
+- stats.prefix_cnt * sizeof(struct prefix) +
+- stats.rib_cnt * sizeof(struct rib_entry) +
+- stats.path_cnt * sizeof(struct rde_aspath) +
+- stats.aspath_size + stats.attr_cnt * sizeof(struct attr) +
+- stats.attr_data));
++ printf("\nBGP routing table entry for %s/%u\n",
++ log_addr(&r->prefix), r->prefixlen);
++
++ if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1)
++ err(1, NULL);
++ if (strlen(aspath) > 0)
++ printf(" %s\n", aspath);
++ free(aspath);
++
++ s = fmt_peer(r->descr, &r->remote_addr, -1, nodescr);
++ printf(" Nexthop %s ", log_addr(&r->exit_nexthop));
++ printf("(via %s) from %s (", log_addr(&r->true_nexthop), s);
++ free(s);
++ id.s_addr = htonl(r->remote_id);
++ printf("%s)\n", inet_ntoa(id));
++
++ printf(" Origin %s, metric %u, localpref %u, weight %u, ",
++ print_origin(r->origin, 0), r->med, r->local_pref, r->weight);
++ print_flags(r->flags, 0);
++
++ now = time(NULL);
++ if (now > r->lastchange)
++ now -= r->lastchange;
++ else
++ now = 0;
++
++ printf("\n Last update: %s ago\n", fmt_timeframe_core(now));
++}
++
++void
++show_attr(void *b, u_int16_t len)
++{
++ char *data = b;
++ struct in_addr id;
++ u_int32_t as;
++ u_int16_t alen, ioff;
++ u_int8_t flags, type;
++
++ data = b;
++ if (len < 3)
++ errx(1, "show_attr: too short bgp attr");
++
++ flags = data[0];
++ type = data[1];
++
++ /* get the attribute length */
++ if (flags & ATTR_EXTLEN) {
++ if (len < 4)
++ errx(1, "show_attr: too short bgp attr");
++ memcpy(&alen, data+2, sizeof(u_int16_t));
++ alen = ntohs(alen);
++ data += 4;
++ len -= 4;
++ } else {
++ alen = data[2];
++ data += 3;
++ len -= 3;
++ }
++
++ /* bad imsg len how can that happen!? */
++ if (alen > len)
++ errx(1, "show_attr: bad length");
++
++ switch (type) {
++ case ATTR_COMMUNITIES:
++ printf(" Communities: ");
++ show_community(data, alen);
++ printf("\n");
++ break;
++ case ATTR_AGGREGATOR:
++ memcpy(&as, data, sizeof(as));
++ memcpy(&id, data + sizeof(as), sizeof(id));
++ printf(" Aggregator: %s [%s]\n",
++ log_as(ntohl(as)), inet_ntoa(id));
++ break;
++ case ATTR_ORIGINATOR_ID:
++ memcpy(&id, data, sizeof(id));
++ printf(" Originator Id: %s\n", inet_ntoa(id));
++ break;
++ case ATTR_CLUSTER_LIST:
++ printf(" Cluster ID List:");
++ for (ioff = 0; ioff + sizeof(id) <= alen;
++ ioff += sizeof(id)) {
++ memcpy(&id, data + ioff, sizeof(id));
++ printf(" %s", inet_ntoa(id));
++ }
++ printf("\n");
++ break;
++ case ATTR_EXT_COMMUNITIES:
++ printf(" Ext. communities: ");
++ show_ext_community(data, alen);
++ printf("\n");
+ break;
+ default:
++ /* ignore unknown attributes */
+ break;
+ }
+-
+- return (1);
+ }
+
+ void
+@@ -1328,30 +1443,6 @@ show_community(u_char *data, u_int16_t l
+ }
+ }
+
+-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,34 +1463,101 @@ show_ext_community(u_char *data, u_int16
+ 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%" PRIx64, log_ext_subtype(subtype), ext);
+ break;
+ default:
+ memcpy(&ext, data + i, sizeof(ext));
+- printf("0x%llx", betoh64(ext));
++ printf("0x%" PRIx64, betoh64(ext));
+ }
+ if (i + 8 < len)
+ printf(", ");
+ }
+ }
+
++char *
++fmt_mem(int64_t num)
++{
++ static char buf[16];
++
++ if (fmt_scaled(num, buf) == -1)
++ snprintf(buf, sizeof(buf), "%lldB", (long long)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");
++ 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)));
++ printf("%10lld prefix entries using %s of memory\n",
++ (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt *
++ sizeof(struct prefix)));
++ printf("%10lld BGP path attribute entries using %s of memory\n",
++ (long long)stats.path_cnt, fmt_mem(stats.path_cnt *
++ sizeof(struct rde_aspath)));
++ printf("%10lld BGP AS-PATH attribute entries using "
++ "%s of memory,\n\t and holding %lld references\n",
++ (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size),
++ (long long)stats.aspath_refs);
++ printf("%10lld BGP attributes entries using %s of memory\n",
++ (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt *
++ sizeof(struct attr)));
++ printf("\t and holding %lld references\n",
++ (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(pts +
++ stats.prefix_cnt * sizeof(struct prefix) +
++ stats.rib_cnt * sizeof(struct rib_entry) +
++ stats.path_cnt * sizeof(struct rde_aspath) +
++ stats.aspath_size + stats.attr_cnt * sizeof(struct attr) +
++ stats.attr_data));
++ break;
++ default:
++ break;
++ }
++
++ return (1);
++}
++
+ void
+ send_filterset(struct imsgbuf *i, struct filter_set_head *set)
+ {
+@@ -1469,6 +1627,183 @@ show_result(struct imsg *imsg)
+ return (1);
+ }
+
++void
++show_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg)
++{
++ struct ctl_show_rib ctl;
++ struct ctl_show_rib_request *req = arg;
++ struct mrt_rib_entry *mre;
++ u_int16_t i, j;
++
++ for (i = 0; i < mr->nentries; i++) {
++ mre = &mr->entries[i];
++ bzero(&ctl, sizeof(ctl));
++ mrt_to_bgpd_addr(&mr->prefix, &ctl.prefix);
++ ctl.prefixlen = mr->prefixlen;
++ ctl.lastchange = mre->originated;
++ mrt_to_bgpd_addr(&mre->nexthop, &ctl.true_nexthop);
++ mrt_to_bgpd_addr(&mre->nexthop, &ctl.exit_nexthop);
++ ctl.origin = mre->origin;
++ ctl.local_pref = mre->local_pref;
++ ctl.med = mre->med;
++ /* weight is not part of the mrt dump so it can't be set */
++ ctl.aspath_len = mre->aspath_len;
++
++ if (mre->peer_idx < mp->npeers) {
++ mrt_to_bgpd_addr(&mp->peers[mre->peer_idx].addr,
++ &ctl.remote_addr);
++ ctl.remote_id = mp->peers[mre->peer_idx].bgp_id;
++ }
++
++ /* filter by neighbor */
++ if (req->neighbor.addr.aid != AID_UNSPEC &&
++ memcmp(&req->neighbor.addr, &ctl.remote_addr,
++ sizeof(ctl.remote_addr)) != 0)
++ continue;
++ /* filter by AF */
++ if (req->aid && req->aid != ctl.prefix.aid)
++ return;
++ /* filter by prefix */
++ if (req->prefix.aid != AID_UNSPEC) {
++ if (!prefix_compare(&req->prefix, &ctl.prefix,
++ req->prefixlen)) {
++ if (req->flags & F_LONGER) {
++ if (req->prefixlen > ctl.prefixlen)
++ return;
++ } else if (req->prefixlen != ctl.prefixlen)
++ return;
++ } else
++ return;
++ }
++ /* filter by AS */
++ if (req->as.type != AS_NONE &&
++ !aspath_match(mre->aspath, mre->aspath_len,
++ req->as.type, req->as.as))
++ continue;
++
++ if (req->flags & F_CTL_DETAIL) {
++ show_rib_detail(&ctl, mre->aspath, 1);
++ for (j = 0; j < mre->nattrs; j++)
++ show_attr(mre->attrs[j].attr,
++ mre->attrs[j].attr_len);
++ } else
++ show_rib_brief(&ctl, mre->aspath);
++ }
++}
++
++void
++network_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg)
++{
++ struct ctl_show_rib ctl;
++ struct network_config net;
++ struct ctl_show_rib_request *req = arg;
++ struct mrt_rib_entry *mre;
++ struct ibuf *msg;
++ u_int16_t i, j;
++
++ for (i = 0; i < mr->nentries; i++) {
++ mre = &mr->entries[i];
++ bzero(&ctl, sizeof(ctl));
++ mrt_to_bgpd_addr(&mr->prefix, &ctl.prefix);
++ ctl.prefixlen = mr->prefixlen;
++ ctl.lastchange = mre->originated;
++ mrt_to_bgpd_addr(&mre->nexthop, &ctl.true_nexthop);
++ mrt_to_bgpd_addr(&mre->nexthop, &ctl.exit_nexthop);
++ ctl.origin = mre->origin;
++ ctl.local_pref = mre->local_pref;
++ ctl.med = mre->med;
++ ctl.aspath_len = mre->aspath_len;
++
++ if (mre->peer_idx < mp->npeers) {
++ mrt_to_bgpd_addr(&mp->peers[mre->peer_idx].addr,
++ &ctl.remote_addr);
++ ctl.remote_id = mp->peers[mre->peer_idx].bgp_id;
++ }
++
++ /* filter by neighbor */
++ if (req->neighbor.addr.aid != AID_UNSPEC &&
++ memcmp(&req->neighbor.addr, &ctl.remote_addr,
++ sizeof(ctl.remote_addr)) != 0)
++ continue;
++ /* filter by AF */
++ if (req->aid && req->aid != ctl.prefix.aid)
++ return;
++ /* filter by prefix */
++ if (req->prefix.aid != AID_UNSPEC) {
++ if (!prefix_compare(&req->prefix, &ctl.prefix,
++ req->prefixlen)) {
++ if (req->flags & F_LONGER) {
++ if (req->prefixlen > ctl.prefixlen)
++ return;
++ } else if (req->prefixlen != ctl.prefixlen)
++ return;
++ } else
++ return;
++ }
++ /* filter by AS */
++ if (req->as.type != AS_NONE &&
++ !aspath_match(mre->aspath, mre->aspath_len,
++ req->as.type, req->as.as))
++ continue;
++
++ bzero(&net, sizeof(net));
++ memcpy(&net.prefix, &ctl.prefix, sizeof(net.prefix));
++ net.prefixlen = ctl.prefixlen;
++ net.type = NETWORK_MRTCLONE;
++ /* XXX rtableid */
++
++ imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
++ &net, sizeof(net));
++ if ((msg = imsg_create(ibuf, IMSG_NETWORK_ASPATH,
++ 0, 0, sizeof(ctl) + mre->aspath_len)) == NULL)
++ errx(1, "imsg_create failure");
++ if (imsg_add(msg, &ctl, sizeof(ctl)) == -1 ||
++ imsg_add(msg, mre->aspath, mre->aspath_len) == -1)
++ errx(1, "imsg_add failure");
++ imsg_close(ibuf, msg);
++ for (j = 0; j < mre->nattrs; j++)
++ imsg_compose(ibuf, IMSG_NETWORK_ATTR, 0, 0, -1,
++ mre->attrs[j].attr, mre->attrs[j].attr_len);
++ imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0);
++
++ while (ibuf->w.queued) {
++ if (msgbuf_write(&ibuf->w) < 0)
++ err(1, "write error");
++ }
++ }
++}
++
++void
++show_mrt_state(struct mrt_bgp_state *ms, void *arg)
++{
++ printf("show_mrt_state\n");
++}
++
++void
++show_mrt_msg(struct mrt_bgp_msg *mm, void *arg)
++{
++ printf("show_mrt_msg\n");
++}
++
++void
++mrt_to_bgpd_addr(union mrt_addr *ma, struct bgpd_addr *ba)
++{
++ switch (ma->sa.sa_family) {
++ case AF_INET:
++ case AF_INET6:
++ sa2addr(&ma->sa, ba);
++ break;
++ case AF_VPNv4:
++ bzero(ba, sizeof(*ba));
++ ba->aid = AID_VPN_IPv4;
++ ba->vpn4.rd = ma->svpn4.sv_rd;
++ ba->vpn4.addr.s_addr = ma->svpn4.sv_addr.s_addr;
++ memcpy(ba->vpn4.labelstack, ma->svpn4.sv_label,
++ sizeof(ba->vpn4.labelstack));
++ break;
++ }
++}
++
+ /* following functions are necessary for imsg framework */
+ void
+ log_warnx(const char *emsg, ...)
+@@ -1495,3 +1830,9 @@ fatal(const char *emsg)
+ {
+ err(1, emsg);
+ }
++
++void
++fatalx(const char *emsg)
++{
++ errx(1, emsg);
++}
diff --git a/net/openbgpd/files/patch-bgpctl_irr_asset.c b/net/openbgpd/files/patch-bgpctl_irr_asset.c
new file mode 100644
index 000000000000..cf0e4a449c66
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irr_asset.c
@@ -0,0 +1,14 @@
+Index: bgpctl/irr_asset.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irr_asset.c,v
+retrieving revision 1.1.1.2
+retrieving revision 1.1.1.3
+diff -u -p -r1.1.1.2 -r1.1.1.3
+--- bgpctl/irr_asset.c 9 Jul 2009 16:49:55 -0000 1.1.1.2
++++ bgpctl/irr_asset.c 13 Oct 2012 18:22:52 -0000 1.1.1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: irr_asset.c,v 1.8 2009/04/14 21:10:54 jj Exp $ */
++/* $OpenBSD: irr_asset.c,v 1.7 2007/03/31 12:46:55 henning Exp $ */
+
+ /*
+ * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
diff --git a/net/openbgpd/files/patch-bgpctl_irr_output.c b/net/openbgpd/files/patch-bgpctl_irr_output.c
new file mode 100644
index 000000000000..b611216c1928
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irr_output.c
@@ -0,0 +1,14 @@
+Index: bgpctl/irr_output.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irr_output.c,v
+retrieving revision 1.1.1.1
+retrieving revision 1.1.1.2
+diff -u -p -r1.1.1.1 -r1.1.1.2
+--- bgpctl/irr_output.c 30 Jun 2009 05:46:15 -0000 1.1.1.1
++++ bgpctl/irr_output.c 13 Oct 2012 18:22:52 -0000 1.1.1.2
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: irr_output.c,v 1.13 2007/03/05 17:28:21 henning Exp $ */
++/* $OpenBSD: irr_output.c,v 1.12 2007/03/05 15:02:05 henning Exp $ */
+
+ /*
+ * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
diff --git a/net/openbgpd/files/patch-bgpctl_irr_parser.c b/net/openbgpd/files/patch-bgpctl_irr_parser.c
new file mode 100644
index 000000000000..2e445ea37809
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irr_parser.c
@@ -0,0 +1,48 @@
+Index: bgpctl/irr_parser.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irr_parser.c,v
+retrieving revision 1.1.1.5
+retrieving revision 1.5
+diff -u -p -r1.1.1.5 -r1.5
+--- bgpctl/irr_parser.c 14 Feb 2010 20:20:14 -0000 1.1.1.5
++++ bgpctl/irr_parser.c 13 Oct 2012 18:35:56 -0000 1.5
+@@ -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,11 +408,13 @@ 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 */
+- if (strlen(val) > 0 && val[strlen(val) - 1] == ',')
++ /* route is single-value, but seen trailing , and \r in the wild */
++ if (strlen(val) > 0 && (val[strlen(val) - 1] == ',' ||
++ val[strlen(val) - 1] == '\r'))
+ val[strlen(val) - 1] = '\0';
+
+ return (prefixset_addmember(val));
diff --git a/net/openbgpd/files/patch-bgpctl_irr_prefix.c b/net/openbgpd/files/patch-bgpctl_irr_prefix.c
new file mode 100644
index 000000000000..4c97de2a4c7d
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irr_prefix.c
@@ -0,0 +1,157 @@
+Index: bgpctl/irr_prefix.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irr_prefix.c,v
+retrieving revision 1.1.1.5
+retrieving revision 1.1.1.8
+diff -u -p -r1.1.1.5 -r1.1.1.8
+--- bgpctl/irr_prefix.c 14 Feb 2010 20:20:14 -0000 1.1.1.5
++++ bgpctl/irr_prefix.c 13 Oct 2012 18:22:52 -0000 1.1.1.8
+@@ -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.17 2009/09/08 16:11:36 sthen 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 *p
+ 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
+ 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/net/openbgpd/files/patch-bgpctl_irrfilter.c b/net/openbgpd/files/patch-bgpctl_irrfilter.c
new file mode 100644
index 000000000000..79c31fc722aa
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irrfilter.c
@@ -0,0 +1,24 @@
+Index: bgpctl/irrfilter.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irrfilter.c,v
+retrieving revision 1.1.1.1
+retrieving revision 1.3
+diff -u -p -r1.1.1.1 -r1.3
+--- bgpctl/irrfilter.c 30 Jun 2009 05:46:15 -0000 1.1.1.1
++++ bgpctl/irrfilter.c 13 Oct 2012 18:35:56 -0000 1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: irrfilter.c,v 1.4 2007/05/28 23:31:53 henning Exp $ */
++/* $OpenBSD: irrfilter.c,v 1.3 2007/03/06 16:45:34 henning Exp $ */
+
+ /*
+ * Copyright (c) 2007 Henning Brauer <henning@openbsd.org>
+@@ -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/net/openbgpd/files/patch-bgpctl_irrfilter.h b/net/openbgpd/files/patch-bgpctl_irrfilter.h
new file mode 100644
index 000000000000..485b97320e58
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_irrfilter.h
@@ -0,0 +1,59 @@
+Index: bgpctl/irrfilter.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/irrfilter.h,v
+retrieving revision 1.1.1.5
+retrieving revision 1.4
+diff -u -p -r1.1.1.5 -r1.4
+--- bgpctl/irrfilter.h 14 Feb 2010 20:20:14 -0000 1.1.1.5
++++ bgpctl/irrfilter.h 13 Oct 2012 18:35:56 -0000 1.4
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: irrfilter.h,v 1.7 2007/03/06 16:45:34 henning Exp $ */
++/* $OpenBSD: irrfilter.h,v 1.8 2009/09/08 15:40:25 claudio 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/net/openbgpd/files/patch-bgpctl_mrtparser.c b/net/openbgpd/files/patch-bgpctl_mrtparser.c
new file mode 100644
index 000000000000..0d463c0064ef
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_mrtparser.c
@@ -0,0 +1,977 @@
+Index: bgpctl/mrtparser.c
+===================================================================
+RCS file: bgpctl/mrtparser.c
+diff -N bgpctl/mrtparser.c
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ bgpctl/mrtparser.c 13 Oct 2012 18:22:53 -0000 1.1.1.1
+@@ -0,0 +1,970 @@
++/* $OpenBSD: mrtparser.c,v 1.2 2012/03/06 07:52:32 claudio Exp $ */
++/*
++ * Copyright (c) 2011 Claudio Jeker <claudio@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/types.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <err.h>
++#include <errno.h>
++#include <limits.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "mrt.h"
++#include "mrtparser.h"
++
++void *mrt_read_msg(int, struct mrt_hdr *);
++size_t mrt_read_buf(int, void *, size_t);
++
++struct mrt_peer *mrt_parse_v2_peer(struct mrt_hdr *, void *);
++struct mrt_rib *mrt_parse_v2_rib(struct mrt_hdr *, void *);
++int mrt_parse_dump(struct mrt_hdr *, void *, struct mrt_peer **,
++ struct mrt_rib **);
++int mrt_parse_dump_mp(struct mrt_hdr *, void *, struct mrt_peer **,
++ struct mrt_rib **);
++int mrt_extract_attr(struct mrt_rib_entry *, u_char *, int, sa_family_t,
++ int);
++
++void mrt_free_peers(struct mrt_peer *);
++void mrt_free_rib(struct mrt_rib *);
++void mrt_free_bgp_state(struct mrt_bgp_state *);
++void mrt_free_bgp_msg(struct mrt_bgp_msg *);
++
++u_char *mrt_aspath_inflate(void *, u_int16_t, u_int16_t *);
++int mrt_extract_addr(void *, u_int, union mrt_addr *, sa_family_t);
++
++void *
++mrt_read_msg(int fd, struct mrt_hdr *hdr)
++{
++ void *buf;
++
++ bzero(hdr, sizeof(*hdr));
++ if (mrt_read_buf(fd, hdr, sizeof(*hdr)) != sizeof(*hdr))
++ return (NULL);
++
++ if ((buf = malloc(ntohl(hdr->length))) == NULL)
++ err(1, "malloc(%d)", hdr->length);
++
++ if (mrt_read_buf(fd, buf, ntohl(hdr->length)) != ntohl(hdr->length)) {
++ free(buf);
++ return (NULL);
++ }
++ return (buf);
++}
++
++size_t
++mrt_read_buf(int fd, void *buf, size_t len)
++{
++ char *b = buf;
++ ssize_t n;
++
++ while (len > 0) {
++ if ((n = read(fd, b, len)) == -1) {
++ if (errno == EINTR)
++ continue;
++ err(1, "read");
++ }
++ if (n == 0)
++ break;
++ b += n;
++ len -= n;
++ }
++
++ return (b - (char *)buf);
++}
++
++void
++mrt_parse(int fd, struct mrt_parser *p, int verbose)
++{
++ struct mrt_hdr h;
++ struct mrt_peer *pctx = NULL;
++ struct mrt_rib *r;
++ void *msg;
++
++ while ((msg = mrt_read_msg(fd, &h))) {
++ switch (ntohs(h.type)) {
++ case MSG_NULL:
++ case MSG_START:
++ case MSG_DIE:
++ case MSG_I_AM_DEAD:
++ case MSG_PEER_DOWN:
++ case MSG_PROTOCOL_BGP:
++ case MSG_PROTOCOL_IDRP:
++ case MSG_PROTOCOL_BGP4PLUS:
++ case MSG_PROTOCOL_BGP4PLUS1:
++ if (verbose)
++ printf("deprecated MRT type %d\n",
++ ntohs(h.type));
++ break;
++ case MSG_PROTOCOL_RIP:
++ case MSG_PROTOCOL_RIPNG:
++ case MSG_PROTOCOL_OSPF:
++ case MSG_PROTOCOL_ISIS_ET:
++ case MSG_PROTOCOL_ISIS:
++ case MSG_PROTOCOL_OSPFV3_ET:
++ case MSG_PROTOCOL_OSPFV3:
++ if (verbose)
++ printf("unsuported MRT type %d\n",
++ ntohs(h.type));
++ break;
++ case MSG_TABLE_DUMP:
++ switch (ntohs(h.subtype)) {
++ case MRT_DUMP_AFI_IP:
++ case MRT_DUMP_AFI_IPv6:
++ if (p->dump == NULL)
++ break;
++ if (mrt_parse_dump(&h, msg, &pctx, &r) == 0) {
++ p->dump(r, pctx, p->arg);
++ mrt_free_rib(r);
++ }
++ break;
++ default:
++ if (verbose)
++ printf("unknown AFI %d in table dump\n",
++ ntohs(h.subtype));
++ break;
++ }
++ break;
++ case MSG_TABLE_DUMP_V2:
++ switch (ntohs(h.subtype)) {
++ case MRT_DUMP_V2_PEER_INDEX_TABLE:
++ if (p->dump == NULL)
++ break;
++ if (pctx)
++ mrt_free_peers(pctx);
++ pctx = mrt_parse_v2_peer(&h, msg);
++ break;
++ case MRT_DUMP_V2_RIB_IPV4_UNICAST:
++ case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
++ case MRT_DUMP_V2_RIB_IPV6_UNICAST:
++ case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
++ case MRT_DUMP_V2_RIB_GENERIC:
++ if (p->dump == NULL)
++ break;
++ r = mrt_parse_v2_rib(&h, msg);
++ if (r) {
++ p->dump(r, pctx, p->arg);
++ mrt_free_rib(r);
++ }
++ break;
++ default:
++ if (verbose)
++ printf("unhandled BGP4MP subtype %d\n",
++ ntohs(h.subtype));
++ break;
++ }
++ break;
++ case MSG_PROTOCOL_BGP4MP_ET:
++ /* currently just ignore the microsec field */
++ msg = (char *)msg + sizeof(u_int32_t);
++ h.length -= sizeof(u_int32_t);
++ /* FALLTHROUGH */
++ case MSG_PROTOCOL_BGP4MP:
++ switch (ntohs(h.subtype)) {
++ case BGP4MP_STATE_CHANGE:
++ case BGP4MP_STATE_CHANGE_AS4:
++ /* XXX p->state(s, p->arg); */
++ errx(1, "BGP4MP subtype not yet implemented");
++ break;
++ case BGP4MP_MESSAGE:
++ case BGP4MP_MESSAGE_AS4:
++ case BGP4MP_MESSAGE_LOCAL:
++ case BGP4MP_MESSAGE_AS4_LOCAL:
++ /* XXX p->message(m, p->arg); */
++ errx(1, "BGP4MP subtype not yet implemented");
++ break;
++ case BGP4MP_ENTRY:
++ if (p->dump == NULL)
++ break;
++ if (mrt_parse_dump_mp(&h, msg, &pctx, &r) ==
++ 0) {
++ p->dump(r, pctx, p->arg);
++ mrt_free_rib(r);
++ }
++ break;
++ default:
++ if (verbose)
++ printf("unhandled BGP4MP subtype %d\n",
++ ntohs(h.subtype));
++ break;
++ }
++ break;
++ default:
++ if (verbose)
++ printf("unknown MRT type %d\n", ntohs(h.type));
++ break;
++ }
++ free(msg);
++ }
++ if (pctx)
++ mrt_free_peers(pctx);
++}
++
++struct mrt_peer *
++mrt_parse_v2_peer(struct mrt_hdr *hdr, void *msg)
++{
++ struct mrt_peer_entry *peers;
++ struct mrt_peer *p;
++ u_int8_t *b = msg;
++ u_int32_t bid, as4;
++ u_int16_t cnt, i, as2;
++ u_int len = ntohl(hdr->length);
++
++ if (len < 8) /* min msg size */
++ return NULL;
++
++ p = calloc(1, sizeof(struct mrt_peer));
++ if (p == NULL)
++ err(1, "calloc");
++
++ /* collector bgp id */
++ memcpy(&bid, b, sizeof(bid));
++ b += sizeof(bid);
++ len -= sizeof(bid);
++ p->bgp_id = ntohl(bid);
++
++ /* view name length */
++ memcpy(&cnt, b, sizeof(cnt));
++ b += sizeof(cnt);
++ len -= sizeof(cnt);
++ cnt = ntohs(cnt);
++
++ /* view name */
++ if (cnt > len)
++ goto fail;
++ if (cnt != 0) {
++ if ((p->view = malloc(cnt + 1)) == NULL)
++ err(1, "malloc");
++ memcpy(p->view, b, cnt);
++ p->view[cnt] = 0;
++ } else
++ if ((p->view = strdup("")) == NULL)
++ err(1, "strdup");
++ b += cnt;
++ len -= cnt;
++
++ /* peer_count */
++ if (len < sizeof(cnt))
++ goto fail;
++ memcpy(&cnt, b, sizeof(cnt));
++ b += sizeof(cnt);
++ len -= sizeof(cnt);
++ cnt = ntohs(cnt);
++
++ /* peer entries */
++ if ((peers = calloc(cnt, sizeof(struct mrt_peer_entry))) == NULL)
++ err(1, "calloc");
++ for (i = 0; i < cnt; i++) {
++ u_int8_t type;
++
++ if (len < sizeof(u_int8_t) + sizeof(u_int32_t))
++ goto fail;
++ type = *b++;
++ len -= 1;
++ memcpy(&bid, b, sizeof(bid));
++ b += sizeof(bid);
++ len -= sizeof(bid);
++ peers[i].bgp_id = ntohl(bid);
++
++ if (type & MRT_DUMP_V2_PEER_BIT_I) {
++ if (mrt_extract_addr(b, len, &peers[i].addr,
++ AF_INET6) == -1)
++ goto fail;
++ b += sizeof(struct in6_addr);
++ len -= sizeof(struct in6_addr);
++ } else {
++ if (mrt_extract_addr(b, len, &peers[i].addr,
++ AF_INET) == -1)
++ goto fail;
++ b += sizeof(struct in_addr);
++ len -= sizeof(struct in_addr);
++ }
++
++ if (type & MRT_DUMP_V2_PEER_BIT_A) {
++ memcpy(&as4, b, sizeof(as4));
++ b += sizeof(as4);
++ len -= sizeof(as4);
++ as4 = ntohl(as4);
++ } else {
++ memcpy(&as2, b, sizeof(as2));
++ b += sizeof(as2);
++ len -= sizeof(as2);
++ as4 = ntohs(as2);
++ }
++ peers[i].asnum = as4;
++ }
++ p->peers = peers;
++ p->npeers = cnt;
++ return (p);
++fail:
++ mrt_free_peers(p);
++ return (NULL);
++}
++
++struct mrt_rib *
++mrt_parse_v2_rib(struct mrt_hdr *hdr, void *msg)
++{
++ struct mrt_rib_entry *entries;
++ struct mrt_rib *r;
++ u_int8_t *b = msg;
++ u_int len = ntohl(hdr->length);
++ u_int32_t snum;
++ u_int16_t cnt, i;
++ u_int8_t plen;
++
++ if (len < sizeof(snum) + 1)
++ return NULL;
++
++ r = calloc(1, sizeof(struct mrt_rib));
++ if (r == NULL)
++ err(1, "calloc");
++
++ /* seq_num */
++ memcpy(&snum, b, sizeof(snum));
++ b += sizeof(snum);
++ len -= sizeof(snum);
++ r->seqnum = ntohl(snum);
++
++ switch (ntohs(hdr->subtype)) {
++ case MRT_DUMP_V2_RIB_IPV4_UNICAST:
++ case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
++ plen = *b++;
++ len -= 1;
++ if (len < MRT_PREFIX_LEN(plen))
++ goto fail;
++ r->prefix.sin.sin_family = AF_INET;
++ r->prefix.sin.sin_len = sizeof(struct sockaddr_in);
++ memcpy(&r->prefix.sin.sin_addr, b, MRT_PREFIX_LEN(plen));
++ b += MRT_PREFIX_LEN(plen);
++ len -= MRT_PREFIX_LEN(plen);
++ r->prefixlen = plen;
++ break;
++ case MRT_DUMP_V2_RIB_IPV6_UNICAST:
++ case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
++ plen = *b++;
++ len -= 1;
++ if (len < MRT_PREFIX_LEN(plen))
++ goto fail;
++ r->prefix.sin6.sin6_family = AF_INET6;
++ r->prefix.sin6.sin6_len = sizeof(struct sockaddr_in6);
++ memcpy(&r->prefix.sin6.sin6_addr, b, MRT_PREFIX_LEN(plen));
++ b += MRT_PREFIX_LEN(plen);
++ len -= MRT_PREFIX_LEN(plen);
++ r->prefixlen = plen;
++ break;
++ case MRT_DUMP_V2_RIB_GENERIC:
++ /* XXX unhandled */
++ errx(1, "MRT_DUMP_V2_RIB_GENERIC subtype not yet implemented");
++ goto fail;
++ }
++
++ /* entries count */
++ if (len < sizeof(cnt))
++ goto fail;
++ memcpy(&cnt, b, sizeof(cnt));
++ b += sizeof(cnt);
++ len -= sizeof(cnt);
++ cnt = ntohs(cnt);
++ r->nentries = cnt;
++
++ /* entries */
++ if ((entries = calloc(cnt, sizeof(struct mrt_rib_entry))) == NULL)
++ err(1, "calloc");
++ for (i = 0; i < cnt; i++) {
++ u_int32_t otm;
++ u_int16_t pix, alen;
++ if (len < 2 * sizeof(u_int16_t) + sizeof(u_int32_t))
++ goto fail;
++ /* peer index */
++ memcpy(&pix, b, sizeof(pix));
++ b += sizeof(pix);
++ len -= sizeof(pix);
++ entries[i].peer_idx = ntohs(pix);
++
++ /* originated */
++ memcpy(&otm, b, sizeof(otm));
++ b += sizeof(otm);
++ len -= sizeof(otm);
++ entries[i].originated = ntohl(otm);
++
++ /* attr_len */
++ memcpy(&alen, b, sizeof(alen));
++ b += sizeof(alen);
++ len -= sizeof(alen);
++ alen = ntohs(alen);
++
++ /* attr */
++ if (len < alen)
++ goto fail;
++ if (mrt_extract_attr(&entries[i], b, alen,
++ r->prefix.sa.sa_family, 1) == -1)
++ goto fail;
++ b += alen;
++ len -= alen;
++ }
++ r->entries = entries;
++ return (r);
++fail:
++ mrt_free_rib(r);
++ return (NULL);
++}
++
++int
++mrt_parse_dump(struct mrt_hdr *hdr, void *msg, struct mrt_peer **pp,
++ struct mrt_rib **rp)
++{
++ struct mrt_peer *p;
++ struct mrt_rib *r;
++ struct mrt_rib_entry *re;
++ u_int8_t *b = msg;
++ u_int len = ntohl(hdr->length);
++ u_int16_t asnum, alen;
++
++ if (*pp == NULL) {
++ *pp = calloc(1, sizeof(struct mrt_peer));
++ if (*pp == NULL)
++ err(1, "calloc");
++ (*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry));
++ if ((*pp)->peers == NULL)
++ err(1, "calloc");
++ (*pp)->npeers = 1;
++ }
++ p = *pp;
++
++ *rp = r = calloc(1, sizeof(struct mrt_rib));
++ if (r == NULL)
++ err(1, "calloc");
++ re = calloc(1, sizeof(struct mrt_rib_entry));
++ if (re == NULL)
++ err(1, "calloc");
++ r->nentries = 1;
++ r->entries = re;
++
++ if (len < 2 * sizeof(u_int16_t))
++ goto fail;
++ /* view */
++ b += sizeof(u_int16_t);
++ len -= sizeof(u_int16_t);
++ /* seqnum */
++ memcpy(&r->seqnum, b, sizeof(u_int16_t));
++ b += sizeof(u_int16_t);
++ len -= sizeof(u_int16_t);
++ r->seqnum = ntohs(r->seqnum);
++
++ switch (ntohs(hdr->subtype)) {
++ case MRT_DUMP_AFI_IP:
++ if (mrt_extract_addr(b, len, &r->prefix, AF_INET) == -1)
++ goto fail;
++ b += sizeof(struct in_addr);
++ len -= sizeof(struct in_addr);
++ break;
++ case MRT_DUMP_AFI_IPv6:
++ if (mrt_extract_addr(b, len, &r->prefix, AF_INET6) == -1)
++ goto fail;
++ b += sizeof(struct in6_addr);
++ len -= sizeof(struct in6_addr);
++ break;
++ }
++ if (len < 2 * sizeof(u_int32_t) + 2 * sizeof(u_int16_t) + 2)
++ goto fail;
++ r->prefixlen = *b++;
++ len -= 1;
++ /* status */
++ b += 1;
++ len -= 1;
++ /* originated */
++ memcpy(&re->originated, b, sizeof(u_int32_t));
++ b += sizeof(u_int32_t);
++ len -= sizeof(u_int32_t);
++ re->originated = ntohl(re->originated);
++ /* peer ip */
++ switch (ntohs(hdr->subtype)) {
++ case MRT_DUMP_AFI_IP:
++ if (mrt_extract_addr(b, len, &p->peers->addr, AF_INET) == -1)
++ goto fail;
++ b += sizeof(struct in_addr);
++ len -= sizeof(struct in_addr);
++ break;
++ case MRT_DUMP_AFI_IPv6:
++ if (mrt_extract_addr(b, len, &p->peers->addr, AF_INET6) == -1)
++ goto fail;
++ b += sizeof(struct in6_addr);
++ len -= sizeof(struct in6_addr);
++ break;
++ }
++ memcpy(&asnum, b, sizeof(asnum));
++ b += sizeof(asnum);
++ len -= sizeof(asnum);
++ p->peers->asnum = ntohs(asnum);
++
++ memcpy(&alen, b, sizeof(alen));
++ b += sizeof(alen);
++ len -= sizeof(alen);
++ alen = ntohs(alen);
++
++ /* attr */
++ if (len < alen)
++ goto fail;
++ if (mrt_extract_attr(re, b, alen, r->prefix.sa.sa_family, 0) == -1)
++ goto fail;
++ b += alen;
++ len -= alen;
++
++ return (0);
++fail:
++ mrt_free_rib(r);
++ return (-1);
++}
++
++int
++mrt_parse_dump_mp(struct mrt_hdr *hdr, void *msg, struct mrt_peer **pp,
++ struct mrt_rib **rp)
++{
++ struct mrt_peer *p;
++ struct mrt_rib *r;
++ struct mrt_rib_entry *re;
++ u_int8_t *b = msg;
++ u_int len = ntohl(hdr->length);
++ u_int16_t asnum, alen, afi;
++ u_int8_t safi, nhlen;
++ sa_family_t af;
++
++ if (*pp == NULL) {
++ *pp = calloc(1, sizeof(struct mrt_peer));
++ if (*pp == NULL)
++ err(1, "calloc");
++ (*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry));
++ if ((*pp)->peers == NULL)
++ err(1, "calloc");
++ (*pp)->npeers = 1;
++ }
++ p = *pp;
++
++ *rp = r = calloc(1, sizeof(struct mrt_rib));
++ if (r == NULL)
++ err(1, "calloc");
++ re = calloc(1, sizeof(struct mrt_rib_entry));
++ if (re == NULL)
++ err(1, "calloc");
++ r->nentries = 1;
++ r->entries = re;
++
++ if (len < 4 * sizeof(u_int16_t))
++ goto fail;
++ /* source AS */
++ b += sizeof(u_int16_t);
++ len -= sizeof(u_int16_t);
++ /* dest AS */
++ memcpy(&asnum, b, sizeof(asnum));
++ b += sizeof(asnum);
++ len -= sizeof(asnum);
++ p->peers->asnum = ntohs(asnum);
++ /* iface index */
++ b += sizeof(u_int16_t);
++ len -= sizeof(u_int16_t);
++ /* afi */
++ memcpy(&afi, b, sizeof(afi));
++ b += sizeof(afi);
++ len -= sizeof(afi);
++ afi = ntohs(afi);
++
++ /* source + dest ip */
++ switch (afi) {
++ case MRT_DUMP_AFI_IP:
++ if (len < 2 * sizeof(struct in_addr))
++ goto fail;
++ /* source IP */
++ b += sizeof(struct in_addr);
++ len -= sizeof(struct in_addr);
++ /* dest IP */
++ if (mrt_extract_addr(b, len, &p->peers->addr, AF_INET) == -1)
++ goto fail;
++ b += sizeof(struct in_addr);
++ len -= sizeof(struct in_addr);
++ break;
++ case MRT_DUMP_AFI_IPv6:
++ if (len < 2 * sizeof(struct in6_addr))
++ goto fail;
++ /* source IP */
++ b += sizeof(struct in6_addr);
++ len -= sizeof(struct in6_addr);
++ /* dest IP */
++ if (mrt_extract_addr(b, len, &p->peers->addr, AF_INET6) == -1)
++ goto fail;
++ b += sizeof(struct in6_addr);
++ len -= sizeof(struct in6_addr);
++ break;
++ }
++
++ if (len < 2 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t))
++ goto fail;
++ /* view + status */
++ b += 2 * sizeof(u_int16_t);
++ len -= 2 * sizeof(u_int16_t);
++ /* originated */
++ memcpy(&re->originated, b, sizeof(u_int32_t));
++ b += sizeof(u_int32_t);
++ len -= sizeof(u_int32_t);
++ re->originated = ntohl(re->originated);
++
++ /* afi */
++ memcpy(&afi, b, sizeof(afi));
++ b += sizeof(afi);
++ len -= sizeof(afi);
++ afi = ntohs(afi);
++
++ /* safi */
++ safi = *b++;
++ len -= 1;
++
++ switch (afi) {
++ case MRT_DUMP_AFI_IP:
++ if (safi == 1 || safi == 2) {
++ af = AF_INET;
++ break;
++ } else if (safi == 128) {
++ af = AF_VPNv4;
++ break;
++ }
++ goto fail;
++ case MRT_DUMP_AFI_IPv6:
++ if (safi != 1 && safi != 2)
++ goto fail;
++ af = AF_INET6;
++ break;
++ default:
++ goto fail;
++ }
++
++ /* nhlen */
++ nhlen = *b++;
++ len -= 1;
++
++ /* nexthop */
++ if (mrt_extract_addr(b, len, &re->nexthop, af) == -1)
++ goto fail;
++ if (len < nhlen)
++ goto fail;
++ b += nhlen;
++ len -= nhlen;
++
++ if (len < 1)
++ goto fail;
++ r->prefixlen = *b++;
++ len -= 1;
++
++ /* prefix */
++ switch (af) {
++ case AF_INET:
++ if (len < MRT_PREFIX_LEN(r->prefixlen))
++ goto fail;
++ r->prefix.sin.sin_family = AF_INET;
++ r->prefix.sin.sin_len = sizeof(struct sockaddr_in);
++ memcpy(&r->prefix.sin.sin_addr, b,
++ MRT_PREFIX_LEN(r->prefixlen));
++ b += MRT_PREFIX_LEN(r->prefixlen);
++ len -= MRT_PREFIX_LEN(r->prefixlen);
++ break;
++ case AF_INET6:
++ if (len < MRT_PREFIX_LEN(r->prefixlen))
++ goto fail;
++ r->prefix.sin6.sin6_family = AF_INET6;
++ r->prefix.sin6.sin6_len = sizeof(struct sockaddr_in6);
++ memcpy(&r->prefix.sin6.sin6_addr, b,
++ MRT_PREFIX_LEN(r->prefixlen));
++ b += MRT_PREFIX_LEN(r->prefixlen);
++ len -= MRT_PREFIX_LEN(r->prefixlen);
++ break;
++ case AF_VPNv4:
++ if (len < MRT_PREFIX_LEN(r->prefixlen))
++ goto fail;
++ errx(1, "AF_VPNv4 handling not yet implemented");
++ goto fail;
++ }
++
++ memcpy(&alen, b, sizeof(alen));
++ b += sizeof(alen);
++ len -= sizeof(alen);
++ alen = ntohs(alen);
++
++ /* attr */
++ if (len < alen)
++ goto fail;
++ if (mrt_extract_attr(re, b, alen, r->prefix.sa.sa_family, 0) == -1)
++ goto fail;
++ b += alen;
++ len -= alen;
++
++ return (0);
++fail:
++ mrt_free_rib(r);
++ return (-1);
++}
++
++int
++mrt_extract_attr(struct mrt_rib_entry *re, u_char *a, int alen, sa_family_t af,
++ int as4)
++{
++ struct mrt_attr *ap;
++ u_int32_t tmp;
++ u_int16_t attr_len;
++ u_int8_t type, flags, *attr;
++
++ do {
++ if (alen < 3)
++ return (-1);
++ attr = a;
++ flags = *a++;
++ alen -= 1;
++ type = *a++;
++ alen -= 1;
++
++ if (flags & MRT_ATTR_EXTLEN) {
++ if (alen < 2)
++ return (-1);
++ memcpy(&attr_len, a, sizeof(attr_len));
++ attr_len = ntohs(attr_len);
++ a += sizeof(attr_len);
++ alen -= sizeof(attr_len);
++ } else {
++ attr_len = *a++;
++ alen -= 1;
++ }
++ switch (type) {
++ case MRT_ATTR_ORIGIN:
++ if (attr_len != 1)
++ return (-1);
++ re->origin = *a;
++ break;
++ case MRT_ATTR_ASPATH:
++ if (as4) {
++ re->aspath_len = attr_len;
++ if ((re->aspath = malloc(attr_len)) == NULL)
++ err(1, "malloc");
++ memcpy(re->aspath, a, attr_len);
++ } else {
++ re->aspath = mrt_aspath_inflate(a, attr_len,
++ &re->aspath_len);
++ if (re->aspath == NULL)
++ return (-1);
++ }
++ break;
++ case MRT_ATTR_NEXTHOP:
++ if (attr_len != 4)
++ return (-1);
++ if (af != AF_INET)
++ break;
++ memcpy(&tmp, a, sizeof(tmp));
++ re->nexthop.sin.sin_len = sizeof(struct sockaddr_in);
++ re->nexthop.sin.sin_family = AF_INET;
++ re->nexthop.sin.sin_addr.s_addr = tmp;
++ break;
++ case MRT_ATTR_MED:
++ if (attr_len != 4)
++ return (-1);
++ memcpy(&tmp, a, sizeof(tmp));
++ re->med = ntohl(tmp);
++ break;
++ case MRT_ATTR_LOCALPREF:
++ if (attr_len != 4)
++ return (-1);
++ memcpy(&tmp, a, sizeof(tmp));
++ re->local_pref = ntohl(tmp);
++ break;
++ case MRT_ATTR_MP_REACH_NLRI:
++ /*
++ * XXX horrible hack:
++ * Once again IETF and the real world differ in the
++ * implementation. In short the abbreviated MP_NLRI
++ * hack in the standard is not used in real life.
++ * Detect the two cases by looking at the first byte
++ * of the payload (either the nexthop addr length (RFC)
++ * or the high byte of the AFI (old form)). If the
++ * first byte matches the expected nexthop length it
++ * is expected to be the RFC 6396 encoding.
++ */
++ if (*a != attr_len - 1) {
++ a += 3;
++ alen -= 3;
++ attr_len -= 3;
++ }
++ switch (af) {
++ case AF_INET6:
++ if (attr_len < sizeof(struct in6_addr) + 1)
++ return (-1);
++ re->nexthop.sin6.sin6_len =
++ sizeof(struct sockaddr_in6);
++ re->nexthop.sin6.sin6_family = AF_INET6;
++ memcpy(&re->nexthop.sin6.sin6_addr, a + 1,
++ sizeof(struct in6_addr));
++ break;
++ case AF_VPNv4:
++ if (attr_len < sizeof(u_int64_t) +
++ sizeof(struct in_addr))
++ return (-1);
++ re->nexthop.svpn4.sv_len =
++ sizeof(struct sockaddr_vpn4);
++ re->nexthop.svpn4.sv_family = AF_VPNv4;
++ memcpy(&tmp, a + 1 + sizeof(u_int64_t),
++ sizeof(tmp));
++ re->nexthop.svpn4.sv_addr.s_addr = tmp;
++ break;
++ }
++ break;
++ case MRT_ATTR_AS4PATH:
++ if (!as4) {
++ if (re->aspath)
++ free(re->aspath);
++ re->aspath_len = attr_len;
++ if ((re->aspath = malloc(attr_len)) == NULL)
++ err(1, "malloc");
++ memcpy(re->aspath, a, attr_len);
++ break;
++ }
++ /* FALLTHROUGH */
++ default:
++ re->nattrs++;
++ if (re->nattrs >= UCHAR_MAX)
++ err(1, "too many attributes");
++ ap = realloc(re->attrs,
++ re->nattrs * sizeof(struct mrt_attr));
++ if (ap == NULL)
++ err(1, "realloc");
++ re->attrs = ap;
++ ap = re->attrs + re->nattrs - 1;
++ ap->attr_len = a + attr_len - attr;
++ if ((ap->attr = malloc(ap->attr_len)) == NULL)
++ err(1, "malloc");
++ memcpy(ap->attr, attr, ap->attr_len);
++ break;
++ }
++ a += attr_len;
++ alen -= attr_len;
++ } while (alen > 0);
++
++ return (0);
++}
++
++void
++mrt_free_peers(struct mrt_peer *p)
++{
++ free(p->peers);
++ free(p->view);
++ free(p);
++}
++
++void
++mrt_free_rib(struct mrt_rib *r)
++{
++ u_int16_t i, j;
++
++ for (i = 0; i < r->nentries && r->entries; i++) {
++ for (j = 0; j < r->entries[i].nattrs; j++)
++ free(r->entries[i].attrs[j].attr);
++ free(r->entries[i].attrs);
++ free(r->entries[i].aspath);
++ }
++
++ free(r->entries);
++ free(r);
++}
++
++void
++mrt_free_bgp_state(struct mrt_bgp_state *s)
++{
++ free(s);
++}
++
++void
++mrt_free_bgp_msg(struct mrt_bgp_msg *m)
++{
++ free(m->msg);
++ free(m);
++}
++
++u_char *
++mrt_aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen)
++{
++ u_int8_t *seg, *nseg, *ndata;
++ u_int16_t seg_size, olen, nlen;
++ u_int8_t seg_len;
++
++ /* first calculate the length of the aspath */
++ seg = data;
++ nlen = 0;
++ for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) {
++ seg_len = seg[1];
++ seg_size = 2 + sizeof(u_int16_t) * seg_len;
++ nlen += 2 + sizeof(u_int32_t) * seg_len;
++
++ if (seg_size > olen)
++ return NULL;
++ }
++
++ *newlen = nlen;
++ if ((ndata = malloc(nlen)) == NULL)
++ err(1, "malloc");
++
++ /* then copy the aspath */
++ seg = data;
++ for (nseg = ndata; nseg < ndata + nlen; ) {
++ *nseg++ = *seg++;
++ *nseg++ = seg_len = *seg++;
++ for (; seg_len > 0; seg_len--) {
++ *nseg++ = 0;
++ *nseg++ = 0;
++ *nseg++ = *seg++;
++ *nseg++ = *seg++;
++ }
++ }
++
++ return (ndata);
++}
++
++int
++mrt_extract_addr(void *msg, u_int len, union mrt_addr *addr, sa_family_t af)
++{
++ u_int8_t *b = msg;
++
++ switch (af) {
++ case AF_INET:
++ if (len < sizeof(struct in_addr))
++ return (-1);
++ addr->sin.sin_family = AF_INET;
++ addr->sin.sin_len = sizeof(struct sockaddr_in);
++ memcpy(&addr->sin.sin_addr, b, sizeof(struct in_addr));
++ return sizeof(struct in_addr);
++ case AF_INET6:
++ if (len < sizeof(struct in6_addr))
++ return (-1);
++ addr->sin6.sin6_family = AF_INET6;
++ addr->sin6.sin6_len = sizeof(struct sockaddr_in6);
++ memcpy(&addr->sin6.sin6_addr, b, sizeof(struct in6_addr));
++ return sizeof(struct in6_addr);
++ case AF_VPNv4:
++ if (len < sizeof(u_int64_t) + sizeof(struct in_addr))
++ return (-1);
++ addr->svpn4.sv_len = sizeof(struct sockaddr_vpn4);
++ addr->svpn4.sv_family = AF_VPNv4;
++ memcpy(&addr->svpn4.sv_addr, b + sizeof(u_int64_t),
++ sizeof(struct in_addr));
++ return (sizeof(u_int64_t) + sizeof(struct in_addr));
++ default:
++ return (-1);
++ }
++}
diff --git a/net/openbgpd/files/patch-bgpctl_mrtparser.h b/net/openbgpd/files/patch-bgpctl_mrtparser.h
new file mode 100644
index 000000000000..2f11567e54f1
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_mrtparser.h
@@ -0,0 +1,122 @@
+Index: bgpctl/mrtparser.h
+===================================================================
+RCS file: bgpctl/mrtparser.h
+diff -N bgpctl/mrtparser.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ bgpctl/mrtparser.h 13 Oct 2012 18:22:53 -0000 1.1.1.1
+@@ -0,0 +1,115 @@
++/* $OpenBSD$ */
++/*
++ * Copyright (c) 2011 Claudio Jeker <claudio@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.
++ */
++
++struct sockaddr_vpn4 {
++ u_int8_t sv_len;
++ sa_family_t sv_family;
++ u_int8_t sv_labellen;
++ u_int8_t sv_pad;
++ struct in_addr sv_addr;
++ u_int64_t sv_rd;
++ u_int8_t sv_label[21];
++ u_int8_t sv_pad2[3];
++};
++
++#define AF_VPNv4 250 /* XXX high enough to not cause issues */
++
++union mrt_addr {
++ struct sockaddr_in6 sin6;
++ struct sockaddr_in sin;
++ struct sockaddr_vpn4 svpn4;
++ struct sockaddr sa;
++};
++
++/* data structures for the MSG_TABLE_DUMP_V2 format */
++struct mrt_peer_entry {
++ union mrt_addr addr;
++ u_int32_t bgp_id;
++ u_int32_t asnum;
++};
++
++struct mrt_peer {
++ char *view;
++ struct mrt_peer_entry *peers;
++ u_int32_t bgp_id;
++ u_int16_t npeers;
++};
++
++struct mrt_attr {
++ void *attr;
++ size_t attr_len;
++};
++
++struct mrt_rib_entry {
++ void *aspath;
++ struct mrt_attr *attrs;
++ union mrt_addr nexthop;
++ time_t originated;
++ u_int32_t local_pref;
++ u_int32_t med;
++ u_int16_t peer_idx;
++ u_int16_t aspath_len;
++ u_int16_t nattrs;
++ u_int8_t origin;
++};
++
++struct mrt_rib {
++ struct mrt_rib_entry *entries;
++ union mrt_addr prefix;
++ u_int32_t seqnum;
++ u_int16_t nentries;
++ u_int8_t prefixlen;
++};
++
++/* data structures for the BGP4MP MESSAGE and STATE types */
++struct mrt_bgp_state {
++ union mrt_addr src;
++ union mrt_addr dst;
++ u_int32_t src_as;
++ u_int32_t dst_as;
++ u_int16_t old_state;
++ u_int16_t new_state;
++};
++
++struct mrt_bgp_msg {
++ union mrt_addr src;
++ union mrt_addr dst;
++ u_int32_t src_as;
++ u_int32_t dst_as;
++ u_int16_t msg_len;
++ void *msg;
++};
++
++#define MRT_ATTR_ORIGIN 1
++#define MRT_ATTR_ASPATH 2
++#define MRT_ATTR_NEXTHOP 3
++#define MRT_ATTR_MED 4
++#define MRT_ATTR_LOCALPREF 5
++#define MRT_ATTR_MP_REACH_NLRI 14
++#define MRT_ATTR_AS4PATH 17
++#define MRT_ATTR_EXTLEN 0x10
++
++#define MRT_PREFIX_LEN(x) ((((u_int)x) + 7) / 8)
++
++struct mrt_parser {
++ void (*dump)(struct mrt_rib *, struct mrt_peer *, void *);
++ void (*state)(struct mrt_bgp_state *, void *);
++ void (*message)(struct mrt_bgp_msg *, void *);
++ void *arg;
++};
++
++void mrt_parse(int, struct mrt_parser *, int);
diff --git a/net/openbgpd/files/patch-bgpctl_parser.c b/net/openbgpd/files/patch-bgpctl_parser.c
new file mode 100644
index 000000000000..0ce85818b1eb
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_parser.c
@@ -0,0 +1,400 @@
+Index: bgpctl/parser.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/parser.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.7
+diff -u -p -r1.1.1.6 -r1.7
+--- bgpctl/parser.c 14 Feb 2010 20:20:14 -0000 1.1.1.6
++++ bgpctl/parser.c 13 Oct 2012 18:35:56 -0000 1.7
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: parser.c,v 1.54 2009/06/12 16:44:02 claudio Exp $ */
++/* $OpenBSD: parser.c,v 1.64 2012/03/27 18:24:11 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -16,11 +16,16 @@
+ * 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 <err.h>
+ #include <errno.h>
++#include <fcntl.h>
+ #include <limits.h>
+ #include <netdb.h>
+ #include <stdio.h>
+@@ -52,7 +57,9 @@ enum token_type {
+ PREPSELF,
+ WEIGHT,
+ FAMILY,
+- GETOPT
++ GETOPT,
++ RTABLE,
++ FILENAME
+ };
+
+ enum getopts {
+@@ -72,14 +79,18 @@ static const struct token t_show[];
+ static const struct token t_show_summary[];
+ static const struct token t_show_fib[];
+ static const struct token t_show_rib[];
++static const struct token t_show_mrt[];
++static const struct token t_show_mrt_file[];
+ static const struct token t_show_rib_neigh[];
++static const struct token t_show_mrt_neigh[];
+ static const struct token t_show_rib_rib[];
+ static const struct token t_show_neighbor[];
+ static const struct token t_show_neighbor_modifiers[];
+ static const struct token t_fib[];
+ static const struct token t_neighbor[];
+ static const struct token t_neighbor_modifiers[];
+-static const struct token t_show_as[];
++static const struct token t_show_rib_as[];
++static const struct token t_show_mrt_as[];
+ static const struct token t_show_prefix[];
+ static const struct token t_show_ip[];
+ static const struct token t_show_community[];
+@@ -97,6 +108,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 +119,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,8 +131,10 @@ 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},
++ { KEYWORD, "mrt", SHOW_MRT, t_show_mrt},
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
+@@ -128,24 +145,26 @@ 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[] = {
+ { NOTOKEN, "", NONE, NULL},
+- { ASTYPE, "as", AS_ALL, t_show_as},
+- { ASTYPE, "source-as", AS_SOURCE, t_show_as},
+- { ASTYPE, "transit-as", AS_TRANSIT, t_show_as},
+- { ASTYPE, "peer-as", AS_PEER, t_show_as},
++ { ASTYPE, "as", AS_ALL, t_show_rib_as},
++ { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as},
++ { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as},
++ { ASTYPE, "peer-as", AS_PEER, t_show_rib_as},
+ { ASTYPE, "empty-as", AS_EMPTY, t_show_rib},
+ { KEYWORD, "community", NONE, t_show_community},
++ { FLAG, "selected", F_CTL_ACTIVE, t_show_rib},
+ { FLAG, "detail", F_CTL_DETAIL, t_show_rib},
+ { FLAG, "in", F_CTL_ADJ_IN, t_show_rib},
+ { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib},
+@@ -158,12 +177,38 @@ static const struct token t_show_rib[] =
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
++
++static const struct token t_show_mrt[] = {
++ { NOTOKEN, "", NONE, NULL},
++ { ASTYPE, "as", AS_ALL, t_show_mrt_as},
++ { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as},
++ { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as},
++ { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as},
++ { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt},
++ { FLAG, "detail", F_CTL_DETAIL, t_show_mrt},
++ { KEYWORD, "neighbor", NONE, t_show_mrt_neigh},
++ { KEYWORD, "file", NONE, t_show_mrt_file},
++ { FAMILY, "", NONE, t_show_mrt},
++ { PREFIX, "", NONE, t_show_prefix},
++ { ENDTOKEN, "", NONE, NULL}
++};
++
++static const struct token t_show_mrt_file[] = {
++ { FILENAME, "", NONE, t_show_mrt},
++ { ENDTOKEN, "", NONE, NULL}
++};
++
+ static const struct token t_show_rib_neigh[] = {
+ { PEERADDRESS, "", NONE, t_show_rib},
+ { PEERDESC, "", NONE, t_show_rib},
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
++static const struct token t_show_mrt_neigh[] = {
++ { PEERADDRESS, "", NONE, t_show_mrt},
++ { ENDTOKEN, "", NONE, NULL}
++};
++
+ static const struct token t_show_rib_rib[] = {
+ { RIBNAME, "", NONE, t_show_rib},
+ { ENDTOKEN, "", NONE, NULL}
+@@ -187,6 +232,7 @@ static const struct token t_show_neighbo
+ 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}
+ };
+
+@@ -204,11 +250,16 @@ static const struct token t_neighbor_mod
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
+-static const struct token t_show_as[] = {
++static const struct token t_show_rib_as[] = {
+ { ASNUM, "", NONE, t_show_rib},
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
++static const struct token t_show_mrt_as[] = {
++ { ASNUM, "", NONE, t_show_mrt},
++ { ENDTOKEN, "", NONE, NULL}
++};
++
+ static const struct token t_show_prefix[] = {
+ { NOTOKEN, "", NONE, NULL},
+ { FLAG, "all", F_LONGER, NULL},
+@@ -231,6 +282,7 @@ static const struct token t_network[] =
+ { KEYWORD, "delete", NETWORK_REMOVE, t_prefix},
+ { KEYWORD, "flush", NETWORK_FLUSH, NULL},
+ { KEYWORD, "show", NETWORK_SHOW, t_network_show},
++ { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt},
+ { ENDTOKEN, "", NONE, NULL}
+ };
+
+@@ -311,6 +363,22 @@ static const struct token t_irrfilter_op
+ { 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 +472,22 @@ match_token(int *argc, char **argv[], co
+ 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.af = AF_INET;
++ res.aid = AID_INET;
+ }
+- if (!strcmp(word, "inet6") || !strcmp(word, "IPv6")) {
++ if (!strcmp(word, "inet6") ||
++ !strcasecmp(word, "IPv6")) {
+ match++;
+ t = &table[i];
+- res.af = AF_INET6;
++ res.aid = AID_INET6;
++ }
++ if (!strcasecmp(word, "VPNv4")) {
++ match++;
++ t = &table[i];
++ res.aid = AID_VPN_IPv4;
+ }
+ break;
+ case ADDRESS:
+@@ -485,6 +560,7 @@ match_token(int *argc, char **argv[], co
+ case PREPNBR:
+ case PREPSELF:
+ case WEIGHT:
++ case RTABLE:
+ if (word != NULL && strlen(word) > 0 &&
+ parse_number(word, &res, table[i].type)) {
+ match++;
+@@ -518,6 +594,23 @@ match_token(int *argc, char **argv[], co
+ t = &table[i];
+ }
+ break;
++ case FILENAME:
++ if (word != NULL && strlen(word) > 0) {
++ if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
++ /*
++ * ignore error if path has no / and
++ * does not exist. In hope to print
++ * usage.
++ */
++ if (errno == ENOENT &&
++ !strchr(word, '/'))
++ break;
++ err(1, "mrt open(%s)", word);
++ }
++ match++;
++ t = &table[i];
++ }
++ break;
+ case ENDTOKEN:
+ break;
+ }
+@@ -577,6 +670,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,11 +680,14 @@ 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");
+ break;
++ case FILENAME:
++ fprintf(stderr, " <filename>\n");
++ break;
+ case ENDTOKEN:
+ break;
+ }
+@@ -608,7 +707,7 @@ parse_addr(const char *word, struct bgpd
+ 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 +717,7 @@ parse_addr(const char *word, struct bgpd
+ 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);
+ }
+@@ -647,7 +740,7 @@ parse_prefix(const char *word, struct bg
+ if ((p = strrchr(word, '/')) != NULL) {
+ mask = strtonum(p + 1, 0, 128, &errstr);
+ if (errstr)
+- errx(1, "invalid netmask: %s", errstr);
++ errx(1, "netmask %s", errstr);
+
+ if ((ps = malloc(strlen(word) - strlen(p) + 1)) == NULL)
+ err(1, "parse_prefix: malloc");
+@@ -663,15 +756,15 @@ parse_prefix(const char *word, struct bg
+ 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 +799,7 @@ parse_asnum(const char *word, u_int32_t
+ 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 +823,11 @@ parse_number(const char *word, struct pa
+ 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 +980,14 @@ bgpctl_getopt(int *argc, char **argv[],
+ 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/net/openbgpd/files/patch-bgpctl_parser.h b/net/openbgpd/files/patch-bgpctl_parser.h
new file mode 100644
index 000000000000..1d570c747a7c
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_parser.h
@@ -0,0 +1,55 @@
+Index: bgpctl/parser.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/parser.h,v
+retrieving revision 1.1.1.6
+retrieving revision 1.1.1.9
+diff -u -p -r1.1.1.6 -r1.1.1.9
+--- bgpctl/parser.h 14 Feb 2010 20:20:14 -0000 1.1.1.6
++++ bgpctl/parser.h 13 Oct 2012 18:22:53 -0000 1.1.1.9
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: parser.h,v 1.19 2009/06/06 06:05:41 claudio Exp $ */
++/* $OpenBSD: parser.h,v 1.23 2011/09/21 10:37:51 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -29,7 +29,9 @@ enum actions {
+ SHOW_NEIGHBOR_TIMERS,
+ SHOW_NEIGHBOR_TERSE,
+ SHOW_FIB,
++ SHOW_FIB_TABLES,
+ SHOW_RIB,
++ SHOW_MRT,
+ SHOW_RIB_MEM,
+ SHOW_NEXTHOP,
+ SHOW_INTERFACE,
+@@ -37,6 +39,8 @@ enum actions {
+ FIB,
+ FIB_COUPLE,
+ FIB_DECOUPLE,
++ LOG_VERBOSE,
++ LOG_BRIEF,
+ NEIGHBOR,
+ NEIGHBOR_UP,
+ NEIGHBOR_DOWN,
+@@ -46,6 +50,7 @@ enum actions {
+ NETWORK_REMOVE,
+ NETWORK_FLUSH,
+ NETWORK_SHOW,
++ NETWORK_MRT,
+ IRRFILTER
+ };
+
+@@ -59,9 +64,11 @@ 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;
++ int mrtfd;
+ };
+
+ __dead void usage(void);
diff --git a/net/openbgpd/files/patch-bgpctl_whois.c b/net/openbgpd/files/patch-bgpctl_whois.c
new file mode 100644
index 000000000000..0e4b86c84b6e
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpctl_whois.c
@@ -0,0 +1,18 @@
+Index: bgpctl/whois.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpctl/whois.c,v
+retrieving revision 1.1.1.5
+retrieving revision 1.1.1.7
+diff -u -p -r1.1.1.5 -r1.1.1.7
+--- bgpctl/whois.c 14 Feb 2010 20:20:14 -0000 1.1.1.5
++++ bgpctl/whois.c 13 Oct 2012 18:22:54 -0000 1.1.1.7
+@@ -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/net/openbgpd/files/patch-bgpd_Makefile b/net/openbgpd/files/patch-bgpd_Makefile
new file mode 100644
index 000000000000..fc2701497ef1
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_Makefile
@@ -0,0 +1,30 @@
+--- bgpd/Makefile.orig 2013-02-21 19:20:05.000000000 +0000
++++ bgpd/Makefile 2013-02-21 19:20:54.000000000 +0000
+@@ -1,15 +1,25 @@
+ # $OpenBSD: Makefile,v 1.28 2009/06/25 14:14:54 deraadt 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
++ 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/net/openbgpd/files/patch-bgpd_bgpd.8 b/net/openbgpd/files/patch-bgpd_bgpd.8
new file mode 100644
index 000000000000..f59cd5269f2c
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_bgpd.8
@@ -0,0 +1,348 @@
+Index: bgpd/bgpd.8
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/bgpd.8,v
+retrieving revision 1.1.1.8
+retrieving revision 1.10
+diff -u -p -r1.1.1.8 -r1.10
+--- bgpd/bgpd.8 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/bgpd.8 13 Oct 2012 18:36:00 -0000 1.10
+@@ -1,4 +1,4 @@
+-.\" $OpenBSD: bgpd.8,v 1.28 2009/01/13 23:01:36 sthen Exp $
++.\" $OpenBSD: bgpd.8,v 1.45 2012/08/24 20:13:03 jmc 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: August 24 2012 $
+ .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,106 @@ concerning
+ with other BGP systems.
+ .Nm
+ uses the Border Gateway Protocol, Version 4,
+-as described in RFC 1771.
+-Please refer to that document for more information about BGP.
++as described in RFC 4271.
++.Pp
++BGP is an exterior gateway protocol using a multiple step decision process
++to find the best path.
++Advanced filtering can be used to influence the route
++decision for traffic engineering.
++The session engine of
++.Nm
++is responsible for maintaining the TCP session with each neighbor.
++Updates are passed to the Route Decision Engine (RDE) where the paths
++are filtered and used to compute a Routing Information Base (RIB).
++The parent process is responsible for keeping the RIB in sync with
++the kernel routing table.
++.Pp
++The route decision process selects the best path by evaluating all paths to
++the same destination.
++The decision process continues to the next step if paths have equal attributes.
++Paths that are less preferred are taken out of consideration until there is
++only one path left.
++.Bl -enum -width 42 -offset bula
++.It
++All paths with errors or loops are not eligible.
++.It
++Paths with an unreachable nexthop are not eligible.
++After this step all remaining paths are valid.
++.It
++The path with the highest
++.Em LOCAL_PREF
++is selected.
++.It
++The path with the shortest
++.Em AS path
++attribute is selected.
++.It
++The
++.Em ORIGIN
++attribute is compared.
++The order is IGP before EGP before incomplete origins.
++.It
++The path with the lowest
++.Em MULTI_EXIT_DISC
++metric is selected.
++Normally, this value is only considered when choosing between multiple
++routes sent by the same neighbouring AS.
++However, if
++.Dq Li rde med compare always
++is set in the configuration, the metric is compared for routes sent by any AS.
++.It
++Comparison of the BGP session type.
++Paths learned over an external (EBGP) session are preferred over those
++learned via an internal (IBGP) session.
++.It
++The path with the lowest local
++.Em weight
++is selected.
++.It
++If
++.Dq Li rde route-age evaluate
++is set then the oldest path is selected.
++.It
++The path coming from the neighbor with the lowest
++.Em BGP ID
++wins.
++If the
++.Em ORIGINATOR_ID
++attribute is present that value will be used in the comparison instead.
++.It
++The path with the shortest
++.Em CLUSTER_LIST
++attribute is selected.
++If it is not present then a length of 0 is used in the comparison.
++.It
++The path coming from the peer with the lowest IP address is selected.
++IPv4 sessions will be preferred over IPv6 ones.
++.It
++In case of locally announced prefixes
++.Nm
++will prefer statically set prefixes over dynamically inserted ones.
++.El
++.Pp
++Attributes set by filters can be used to tip the decision process to prefer
++particular paths over others.
++This can be achieved by changing the
++.Em localpref ,
++.Em med ,
++or
++.Em weight
++attributes.
++AS path prepending or changing the
++.Em med
++or
++.Em origin
++attribute can be used to influencing the routing behaviour on remote systems.
+ .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 +204,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
+@@ -149,55 +227,144 @@ control socket
+ .Xr bgpctl 8 ,
+ .Xr bgplg 8 ,
+ .Xr bgplgsh 8
++.Sh STANDARDS
+ .Rs
+-.%R RFC 1771
+-.%T "A Border Gateway Protocol 4 (BGP-4)"
+-.%D March 1995
+-.Re
+-.Rs
+-.%R RFC 1997
+-.%T "BGP Communities Attribute"
++.%A R. Chandra
++.%A P. Traina
++.%A "T. Li"
+ .%D August 1996
++.%R RFC 1997
++.%T BGP Communities Attribute
+ .Re
++.Pp
+ .Rs
+-.%R RFC 2385
+-.%T "Protection of BGP Sessions via the TCP MD5 Signature Option"
++.%A A. Heffernan
+ .%D August 1998
++.%R RFC 2385
++.%T Protection of BGP Sessions via the TCP MD5 Signature Option
+ .Re
++.Pp
+ .Rs
+-.%R RFC 2796
+-.%T "BGP Route Reflection - An Alternative to Full Mesh IBGP"
+-.%D April 2000
++.%A P. Marques
++.%A F. Dupont
++.%D March 1999
++.%R RFC 2545
++.%T Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing
+ .Re
++.Pp
+ .Rs
+-.%R RFC 2918
+-.%T "Route Refresh Capability for BGP-4"
++.%A E. Chen
+ .%D September 2000
++.%R RFC 2918
++.%T Route Refresh Capability for BGP-4
+ .Re
++.Pp
+ .Rs
+-.%R RFC 3392
+-.%T "Capabilities Advertisement with BGP-4"
+-.%D January 1999
++.%A G. Huston
++.%D April 2004
++.%R RFC 3765
++.%T NOPEER Community for Border Gateway Protocol (BGP) Route Scope Control
+ .Re
++.Pp
+ .Rs
+-.%R RFC 3682
+-.%T "The Generalized TTL Security Mechanism (GTSM)"
+-.%D February 2004
++.%A Y. Rekhter
++.%A "T. Li"
++.%A S. Hares
++.%D January 2006
++.%R RFC 4271
++.%T A Border Gateway Protocol 4 (BGP-4)
+ .Re
++.Pp
+ .Rs
+-.%R RFC 3765
+-.%T "NOPEER Community for Border Gateway Protocol"
+-.%D April 2004
++.%A S. Sangli
++.%A D. Tappan
++.%A Y. Rekhter
++.%D February 2006
++.%R RFC 4360
++.%T BGP Extended Communities Attribute
+ .Re
++.Pp
+ .Rs
+-.%R RFC 4760
+-.%T "Multiprotocol Extensions for BGP-4"
++.%A E. Rosen
++.%A Y. Rekhter
++.%D February 2006
++.%R RFC 4364
++.%T BGP/MPLS IP Virtual Private Networks (VPNs)
++.Re
++.Pp
++.Rs
++.%A T. Bates
++.%A E. Chen
++.%A R. Chandra
++.%D April 2006
++.%R RFC 4456
++.%T "BGP Route Reflection: An Alternative to Full Mesh Internal BGP (IBGP)"
++.Re
++.Pp
++.Rs
++.%A E. Chen
++.%A V. Gillet
++.%D April 2006
++.%R RFC 4486
++.%T Subcodes for BGP Cease Notification Message
++.Re
++.Pp
++.Rs
++.%A T. Bates
++.%A R. Chandra
++.%A D. Katz
++.%A Y. Rekhter
+ .%D January 2007
++.%R RFC 4760
++.%T Multiprotocol Extensions for BGP-4
+ .Re
++.Pp
+ .Rs
+-.%R RFC 4893
+-.%T "BGP Support for Four-octet AS Number Space"
++.%A Q. Vohra
++.%A E. Chen
+ .%D May 2007
++.%R RFC 4893
++.%T BGP Support for Four-octet AS Number Space
++.Re
++.Pp
++.Rs
++.%A V. Gill
++.%A J. Heasley
++.%A D. Meyer
++.%A P. Savola
++.%A C. Pignatoro
++.%D October 2007
++.%R RFC 5082
++.%T The Generalized TTL Security Mechanism (GTSM)
++.Re
++.Pp
++.Rs
++.%A J. Scudder
++.%A R. Chandra
++.%D February 2009
++.%R RFC 5492
++.%T Capabilities Advertisement with BGP-4
++.Re
++.Pp
++.Rs
++.%D April 2009
++.%R draft-ietf-idr-optional-transitive-00
++.%T Error Handling for Optional Transitive BGP Attributes
++.Re
++.Pp
++.Rs
++.%D August 2011
++.%R draft-ietf-grow-mrt-17
++.%T MRT routing information export format
++.Re
++.Pp
++.Rs
++.%A J. Dong
++.%A M. Chen
++.%A A. Suryanarayana
++.%D May 2012
++.%R RFC 6608
++.%T Subcodes for BGP Finite State Machine Error
+ .Re
+ .Sh HISTORY
+ The
diff --git a/net/openbgpd/files/patch-bgpd_bgpd.c b/net/openbgpd/files/patch-bgpd_bgpd.c
new file mode 100644
index 000000000000..322a8088a075
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_bgpd.c
@@ -0,0 +1,693 @@
+Index: bgpd/bgpd.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/bgpd.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.1.1.12
+diff -u -p -r1.1.1.7 -r1.1.1.12
+--- bgpd/bgpd.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/bgpd.c 8 Dec 2012 10:37:08 -0000 1.1.1.12
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: bgpd.c,v 1.148 2009/06/07 00:30:23 claudio Exp $ */
++/* $OpenBSD: bgpd.c,v 1.169 2012/09/18 09:45:51 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,23 @@ 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;
++int reconfpending;
+ 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 +86,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 +101,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;
+@@ -124,18 +119,13 @@ main(int argc, char *argv[])
+ bgpd_process = PROC_MAIN;
+
+ log_init(1); /* log to stderr until daemonized */
+-
+- if ((rules_l = calloc(1, sizeof(struct filter_head))) == NULL)
+- err(1, NULL);
++ log_verbose(1);
+
+ 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 +148,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 +161,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");
+@@ -202,6 +185,7 @@ main(int argc, char *argv[])
+ errx(1, "unknown user %s", BGPD_USER);
+
+ log_init(debug);
++ log_verbose(conf.opts & BGPD_OPT_VERBOSE);
+
+ if (!debug)
+ daemon(1, 0);
+@@ -225,13 +209,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 +234,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;
+@@ -335,15 +294,16 @@ main(int argc, char *argv[])
+ u_int error;
+
+ 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;
+ case 0: /* all OK */
+ error = 0;
+ break;
++ case 2:
++ error = CTL_RES_PENDING;
++ break;
+ default: /* parse error */
+ error = CTL_RES_PARSE_ERROR;
+ break;
+@@ -389,13 +349,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 +373,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 +414,33 @@ send_filterset(struct imsgbuf *i, struct
+
+ 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 (reconfpending) {
++ log_info("previous reload still running");
++ return (2);
++ }
++ reconfpending = 2; /* one per child */
++
++ log_info("rereading config");
++ 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);
++ reconfpending = 0;
+ return (1);
+ }
+
+ cflags = conf->flags;
+- connectset = &conf->connectset;
+- staticset = &conf->staticset;
+- connectset6 = &conf->connectset6;
+- staticset6 = &conf->staticset6;
+-
+ prepare_listeners(conf);
+
+ /* start reconfiguration */
+@@ -483,12 +451,6 @@ reconfigure(char *conffile, struct bgpd_
+ 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 +458,104 @@ reconfigure(char *conffile, struct bgpd_
+ 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)
+- return (-1);
+- if (imsg_compose(ibuf_rde, IMSG_NETWORK_DONE, 0, 0, -1,
+- NULL, 0) == -1)
+- return (-1);
+- TAILQ_REMOVE(&net_l, n, entry);
+- filterset_free(&n->net.attrset);
+- free(n);
++ /* 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_RECONF_PEER, p->conf.id, 0, -1,
++ &p->conf, sizeof(struct peer_config)) == -1)
++ return (-1);
+ }
+
+- /* 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 +565,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 +588,39 @@ dispatch_imsg(struct imsgbuf *ibuf, int
+ 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)
+@@ -646,26 +654,28 @@ dispatch_imsg(struct imsgbuf *ibuf, int
+ case IMSG_CTL_RELOAD:
+ if (idx != PFD_PIPE_SESSION)
+ log_warnx("reload request not from SE");
+- else
++ else {
+ reconfig = 1;
+ reconfpid = imsg.hdr.pid;
++ }
+ break;
+ case IMSG_CTL_FIB_COUPLE:
+ 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 +702,16 @@ dispatch_imsg(struct imsgbuf *ibuf, int
+ 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;
++ case IMSG_RECONF_DONE:
++ if (reconfpending == 0)
++ log_warnx("unexpected RECONF_DONE received");
++ reconfpending--;
++ break;
+ default:
+ break;
+ }
+@@ -707,7 +727,7 @@ send_nexthop_update(struct kroute_nextho
+ {
+ 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 +737,7 @@ send_nexthop_update(struct kroute_nextho
+ 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 +753,20 @@ send_imsg_session(int type, pid_t pid, v
+ }
+
+ 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 +794,45 @@ bgpd_filternexthop(struct kroute *kr, st
+
+ 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/net/openbgpd/files/patch-bgpd_bgpd.conf.5 b/net/openbgpd/files/patch-bgpd_bgpd.conf.5
new file mode 100644
index 000000000000..32f4439fc0ab
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_bgpd.conf.5
@@ -0,0 +1,746 @@
+Index: bgpd/bgpd.conf.5
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/bgpd.conf.5,v
+retrieving revision 1.1.1.7
+retrieving revision 1.10
+diff -u -p -r1.1.1.7 -r1.10
+--- bgpd/bgpd.conf.5 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/bgpd.conf.5 8 Dec 2012 20:17:59 -0000 1.10
+@@ -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.122 2012/11/13 09:47:20 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: November 13 2012 $
+ .Dt BGPD.CONF 5
+ .Os
+ .Sh NAME
+@@ -26,11 +26,11 @@
+ The
+ .Xr bgpd 8
+ daemon implements the Border Gateway Protocol version 4 as described
+-in RFC 1771.
++in RFC 4271.
+ .Sh SECTIONS
+ The
+ .Nm
+-config file is divided into four main sections.
++config file is divided into five main sections.
+ .Bl -tag -width xxxx
+ .It Sy Macros
+ User-defined variables may be defined and used later, simplifying the
+@@ -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
+@@ -54,9 +56,16 @@ the sections should be grouped and appea
+ .Nm
+ in the order shown above.
+ .Pp
++The current line can be extended over multiple lines using a backslash
++.Pq Sq \e .
+ Comments can be put anywhere in the file using a hash mark
+ .Pq Sq # ,
+ and extend to the end of the current line.
++Care should be taken when commenting out multi-line text:
++the comment is effective until the end of the entire block.
++.Pp
++Argument names not beginning with a letter, digit, or underscore
++must be quoted.
+ .Pp
+ Additional configuration files can be included with the
+ .Ic include
+@@ -66,8 +75,8 @@ include "/etc/bgpd/bgpd-10.0.0.1.filter"
+ .Ed
+ .Sh MACROS
+ Macros can be defined that will later be expanded in context.
+-Macro names must start with a letter, and may contain letters, digits
+-and underscores.
++Macro names must start with a letter, digit, or underscore,
++and may contain any of those characters.
+ Macro names may not be reserved words (for example,
+ .Ic AS ,
+ .Ic neighbor ,
+@@ -93,7 +102,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,29 +152,33 @@ 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 Ns | Ns Ic table-v2
+ .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
+ .Em routing information base ,
+ and all BGP messages in Multi-threaded Routing Toolkit (MRT) format.
+-Dumping the RIB is normally an expensive operation,
+-but it should not influence the session handling.
+ It is possible to dump alternate RIB with the use of
+ .Ar name .
+ .Pp
+ For example, the following will dump the entire table to the
+ .Xr strftime 3 Ns -expanded
+ filename.
+-The
++Only the
++.Ic table-v2
++format is able to dump a multi-protocol RIB correctly.
++Both
++.Ic table
++and
+ .Ic table-mp
+-format is multi-protocol capable but often not supported by 3rd-party tools.
++formats are more or less limited when handling multi-protocol entries and
++are only left around to support 3rd party tools not handling the new format.
+ The timeout is optional:
+ .Bd -literal -offset indent
+ dump table "/tmp/rib-dump-%H%M" 300
+@@ -195,7 +208,7 @@ dump updates out "/tmp/updates-out-%H%M"
+ .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 +255,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 +291,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,38 +308,47 @@ 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 ,
+ the
+-.Em MED
++.Em MULTI_EXIT_DISC
+ attributes will always be compared.
+ The default is
+ .Ic strict ,
+-where the
+-.Em MED
+-is only compared between peers belonging to the same AS.
++where the metric is only compared between peers belonging to the same AS.
+ .Pp
+ .It Xo
+ .Ic rde
+ .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 +361,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 +383,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 +409,110 @@ to EBGP neighbors are not prepended with
+ 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 sole purpose of the Route Distinguisher
++.Ic rd
++is to ensure that possible common prefixes are destinct between VPNs.
++The
++.Ic rd
++is neither used to identify the origin of the prefix nor to control into
++which VPNs the prefix is distributed to.
++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.
++.El
+ .Sh NEIGHBORS AND GROUPS
+ .Xr bgpd 8
+ establishes TCP connections to other BGP speakers called
+@@ -470,21 +607,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 +644,29 @@ This can be helpful to connect to old or
+ 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 +678,7 @@ The demotion counter will be increased a
+ .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 +722,8 @@ Do not start the session when bgpd comes
+ .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 +738,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 +763,16 @@ Inherited from the global configuration
+ 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 +807,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 +819,11 @@ is responsible for managing the session
+ 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,11 +878,11 @@ Do not attempt to actively open a TCP co
+ .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
+-Act as an RFC 2796
++Act as an RFC 4456
+ .Em route-reflector
+ for this neighbor.
+ An optional cluster ID can be specified; otherwise the BGP ID will be used.
+@@ -732,8 +912,8 @@ These sets are rewritten into filter rul
+ .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 +940,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 +952,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 +1029,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 +1101,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 +1154,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 +1162,37 @@ 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 Ic nexthop Ar address
++This rule applies only to
++.Em UPDATES
++where the nexthop is equal to
++.Ar address .
++The
++.Ar address
++can be set to
++.Em neighbor
++in which case the nexthop is compared against the address of the neighbor.
++Nexthop filtering is not supported on locally announced networks and one must
++take into consideration previous rules overwriting nexthops.
++.Pp
+ .It Xo
+ .Ic prefix
+ .Ar address Ns Li / Ns Ar len
+@@ -1028,6 +1268,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 +1325,48 @@ Alternately, well-known communities may
+ 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 +1396,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 +1459,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 +1482,8 @@ For prefixes with equally long paths, th
+ 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/net/openbgpd/files/patch-bgpd_bgpd.h b/net/openbgpd/files/patch-bgpd_bgpd.h
new file mode 100644
index 000000000000..2ce5fe58102f
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_bgpd.h
@@ -0,0 +1,872 @@
+Index: bgpd/bgpd.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/bgpd.h,v
+retrieving revision 1.1.1.8
+retrieving revision 1.15
+diff -u -p -r1.1.1.8 -r1.15
+--- bgpd/bgpd.h 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/bgpd.h 16 May 2014 00:36:26 -0000 1.15
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: bgpd.h,v 1.241 2009/06/12 16:42:53 claudio Exp $ */
++/* $OpenBSD: bgpd.h,v 1.273 2012/09/18 10:10:00 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,9 +82,12 @@
+ #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
++#define F_CTL_ACTIVE 0x8000
+
+ /*
+ * Limit the number of control messages generated by the RDE and queued in
+@@ -109,18 +111,75 @@ 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_MIN 1 /* skip AID_UNSPEC since that is a dummy */
++
++#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 +200,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,12 +259,24 @@ 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 */
+-};
++ struct {
++ int16_t timeout; /* graceful restart timeout */
++ int8_t flags[AID_MAX]; /* graceful restart per AID flags */
++ int8_t restart; /* graceful restart, RFC 4724 */
++ } grestart;
++ int8_t mp[AID_MAX]; /* multiprotocol extensions, RFC 4760 */
++ int8_t refresh; /* route refresh, RFC 2918 */
++ int8_t as4byte; /* 4-byte ASnum, RFC 4893 */
++};
++
++#define CAPA_GR_PRESENT 0x01
++#define CAPA_GR_RESTART 0x02
++#define CAPA_GR_FORWARD 0x04
++#define CAPA_GR_RESTARTING 0x08
++
++#define CAPA_GR_TIMEMASK 0x0fff
++#define CAPA_GR_R_FLAG 0x8000
++#define CAPA_GR_F_FLAG 0x80
+
+ struct peer_config {
+ struct bgpd_addr remote_addr;
+@@ -237,7 +303,7 @@ struct peer_config {
+ u_int8_t template;
+ u_int8_t remote_masklen;
+ u_int8_t cloned;
+- u_int8_t ebgp; /* 1 = ebgp, 0 = ibgp */
++ u_int8_t ebgp; /* 0 = ibgp else ebgp */
+ u_int8_t distance; /* 1 = direct, >1 = multihop */
+ u_int8_t passive;
+ u_int8_t down;
+@@ -248,21 +314,33 @@ 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,
++ NETWORK_MRTCLONE
++};
++
+ struct network_config {
+- struct bgpd_addr prefix;
+- struct filter_set_head attrset;
+- u_int8_t prefixlen;
++ struct bgpd_addr prefix;
++ struct filter_set_head attrset;
++ struct rde_aspath *asp;
++ 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 +354,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,11 +365,14 @@ 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_ASPATH,
++ IMSG_NETWORK_ATTR,
+ IMSG_NETWORK_REMOVE,
+ IMSG_NETWORK_FLUSH,
+ IMSG_NETWORK_DONE,
+@@ -302,19 +382,25 @@ 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,
+ IMSG_SESSION_ADD,
+ IMSG_SESSION_UP,
+ IMSG_SESSION_DOWN,
++ IMSG_SESSION_STALE,
++ IMSG_SESSION_FLUSH,
++ IMSG_SESSION_RESTARTED,
+ IMSG_MRT_OPEN,
+ IMSG_MRT_REOPEN,
+ IMSG_MRT_CLOSE,
+ IMSG_KROUTE_CHANGE,
+ IMSG_KROUTE_DELETE,
+- IMSG_KROUTE6_CHANGE,
+- IMSG_KROUTE6_DELETE,
+ IMSG_NEXTHOP_ADD,
+ IMSG_NEXTHOP_REMOVE,
+ IMSG_NEXTHOP_UPDATE,
+@@ -337,6 +423,7 @@ enum ctl_results {
+ CTL_RES_DENIED,
+ CTL_RES_NOCAP,
+ CTL_RES_PARSE_ERROR,
++ CTL_RES_PENDING,
+ CTL_RES_NOMEM
+ };
+
+@@ -379,9 +466,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 +521,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 +542,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 +555,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 +570,11 @@ 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
++#define F_PREF_STALE 0x10
+
+ struct ctl_show_rib {
+ struct bgpd_addr true_nexthop;
+@@ -472,9 +586,7 @@ struct ctl_show_rib {
+ u_int32_t remote_id;
+ u_int32_t local_pref;
+ u_int32_t med;
+- u_int32_t prefix_cnt;
+- u_int32_t active_cnt;
+- u_int32_t rib_cnt;
++ u_int32_t weight;
+ u_int16_t aspath_len;
+ u_int16_t flags;
+ u_int8_t prefixlen;
+@@ -482,13 +594,6 @@ struct ctl_show_rib {
+ /* plus a aspath_len bytes long aspath */
+ };
+
+-struct ctl_show_rib_prefix {
+- struct bgpd_addr prefix;
+- time_t lastchange;
+- u_int16_t flags;
+- u_int8_t prefixlen;
+-};
+-
+ enum as_spec {
+ AS_NONE,
+ AS_ALL,
+@@ -498,16 +603,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 +659,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 +726,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 {
+@@ -592,18 +755,28 @@ struct filter_prefix {
+ u_int8_t len;
+ };
+
++struct filter_nexthop {
++ struct bgpd_addr addr;
++ u_int8_t flags;
++#define FILTER_NEXTHOP_ADDR 1
++#define FILTER_NEXTHOP_NEIGHBOR 2
++};
++
+ 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_nexthop nexthop;
++ 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 +808,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 +826,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,82 +883,117 @@ 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;
++/* macros for IPv6 link-local address */
++#ifdef __KAME__
++#define IN6_LINKLOCAL_IFINDEX(addr) \
++ ((addr).s6_addr[2] << 8 | (addr).s6_addr[3])
++
++#define SET_IN6_LINKLOCAL_IFINDEX(addr, index) \
++ do { \
++ (addr).s6_addr[2] = ((index) >> 8) & 0xff; \
++ (addr).s6_addr[3] = (index) & 0xff; \
++ } while (0)
++#endif
++
++#define MRT_FILE_LEN 512
++#define MRT2MC(x) ((struct mrt_config *)(x))
++#define MRT_MAX_TIMEOUT 7200
++
++enum mrt_type {
++ MRT_NONE,
++ MRT_TABLE_DUMP,
++ MRT_TABLE_DUMP_MP,
++ MRT_TABLE_DUMP_V2,
++ MRT_ALL_IN,
++ MRT_ALL_OUT,
++ MRT_UPDATE_IN,
++ MRT_UPDATE_OUT
++};
++
++enum mrt_state {
++ MRT_STATE_RUNNING,
++ MRT_STATE_OPEN,
++ MRT_STATE_REOPEN,
++ MRT_STATE_REMOVE
+ };
+-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
+-
+-/* Subsequent Address Family Identifier as per RFC 4760 */
+-#define SAFI_NONE 0x00
+-#define SAFI_UNICAST 0x01
+-#define SAFI_MULTICAST 0x02
+-#define SAFI_ALL 0xff
++struct mrt {
++ char rib[PEER_DESCR_LEN];
++ struct msgbuf wbuf;
++ LIST_ENTRY(mrt) entry;
++ u_int32_t peer_id;
++ u_int32_t group_id;
++ enum mrt_type type;
++ enum mrt_state state;
++ u_int16_t seqnum;
++};
+
+-/* 4-byte magic AS number */
+-#define AS_TRANS 23456
++struct mrt_config {
++ struct mrt conf;
++ char name[MRT_FILE_LEN]; /* base file name */
++ char file[MRT_FILE_LEN]; /* actual file name */
++ time_t ReopenTimer;
++ time_t ReopenTimerInterval;
++};
+
+ /* 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 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 *, ...);
+-void log_warn(const char *, ...);
+-void log_warnx(const char *, ...);
+-void log_info(const char *, ...);
+-void log_debug(const char *, ...);
+-void fatal(const char *) __dead;
+-void fatalx(const char *) __dead;
+-
+-/* parse.y */
+-int cmdline_symset(char *);
++/* control.c */
++void control_cleanup(const char *);
++int control_imsg_relay(struct imsg *);
+
+ /* config.c */
+ 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);
+
+-/* control.c */
+-void control_cleanup(const char *);
+-int control_imsg_relay(struct imsg *);
++/* 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 *, ...);
++void log_warn(const char *, ...);
++void log_warnx(const char *, ...);
++void log_info(const char *, ...);
++void log_debug(const char *, ...);
++void fatal(const char *) __dead;
++void fatalx(const char *) __dead;
+
+-/* pftable.c */
+-int pftable_exists(const char *);
+-int pftable_add(const char *);
+-int pftable_clear_all(void);
+-int pftable_addr_add(struct pftable_msg *);
+-int pftable_addr_remove(struct pftable_msg *);
+-int pftable_commit(void);
++/* mrt.c */
++void mrt_clear_seq(void);
++void mrt_write(struct mrt *);
++void mrt_clean(struct mrt *);
++void mrt_init(struct imsgbuf *, struct imsgbuf *);
++int mrt_timeout(struct mrt_head *);
++void mrt_reconfigure(struct mrt_head *);
++void mrt_handler(struct mrt_head *);
++struct mrt *mrt_get(struct mrt_head *, struct mrt *);
++int mrt_mergeconfig(struct mrt_head *, struct mrt_head *);
+
+ /* name2id.c */
+ u_int16_t rib_name2id(const char *);
+@@ -768,10 +1009,22 @@ const char *pftable_id2name(u_int16_t);
+ void pftable_unref(u_int16_t);
+ void pftable_ref(u_int16_t);
+
++/* parse.y */
++int cmdline_symset(char *);
++
++/* pftable.c */
++int pftable_exists(const char *);
++int pftable_add(const char *);
++int pftable_clear_all(void);
++int pftable_addr_add(struct pftable_msg *);
++int pftable_addr_remove(struct pftable_msg *);
++int pftable_commit(void);
+
+ /* 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 +1032,24 @@ const char *log_addr(const struct bgpd_a
+ 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);
++int aspath_match(void *, u_int16_t, enum as_spec, u_int32_t);
++u_int32_t aspath_extract(const void *, int);
++int prefix_compare(const struct bgpd_addr *,
++ const struct bgpd_addr *, int);
+ 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/net/openbgpd/files/patch-bgpd_buffer.c b/net/openbgpd/files/patch-bgpd_buffer.c
new file mode 100644
index 000000000000..692fe85c1687
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_buffer.c
@@ -0,0 +1,104 @@
+Index: bgpd/buffer.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/buffer.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.3
+diff -u -p -r1.1.1.7 -r1.3
+--- bgpd/buffer.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/buffer.c 8 Dec 2012 20:17:59 -0000 1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: buffer.c,v 1.43 2009/06/06 06:33:15 eric Exp $ */
++/* $OpenBSD: buffer.c,v 1.44 2009/07/23 18:58:42 eric Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -144,7 +144,7 @@ int
+ buf_write(struct msgbuf *msgbuf)
+ {
+ struct iovec iov[IOV_MAX];
+- struct buf *buf, *next;
++ struct buf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+
+@@ -153,7 +153,7 @@ buf_write(struct msgbuf *msgbuf)
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+- iov[i].iov_len = buf->size - buf->rpos;
++ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ }
+
+@@ -170,17 +170,7 @@ buf_write(struct msgbuf *msgbuf)
+ 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;
+- }
+- }
++ msgbuf_drain(msgbuf, n);
+
+ return (0);
+ }
+@@ -201,6 +191,24 @@ msgbuf_init(struct msgbuf *msgbuf)
+ }
+
+ void
++msgbuf_drain(struct msgbuf *msgbuf, size_t n)
++{
++ struct buf *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;
++ buf_dequeue(msgbuf, buf);
++ } else {
++ buf->rpos += n;
++ n = 0;
++ }
++ }
++}
++
++void
+ msgbuf_clear(struct msgbuf *msgbuf)
+ {
+ struct buf *buf;
+@@ -213,7 +221,7 @@ int
+ msgbuf_write(struct msgbuf *msgbuf)
+ {
+ struct iovec iov[IOV_MAX];
+- struct buf *buf, *next;
++ struct buf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+@@ -270,17 +278,7 @@ msgbuf_write(struct msgbuf *msgbuf)
+ 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;
+- }
+- }
++ msgbuf_drain(msgbuf, n);
+
+ return (0);
+ }
diff --git a/net/openbgpd/files/patch-bgpd_carp.c b/net/openbgpd/files/patch-bgpd_carp.c
new file mode 100644
index 000000000000..7ed50075cd5c
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_carp.c
@@ -0,0 +1,54 @@
+Index: bgpd/carp.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/carp.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.4
+diff -u -p -r1.1.1.6 -r1.4
+--- bgpd/carp.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/carp.c 22 Oct 2009 15:10:02 -0000 1.4
+@@ -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 demot
+
+ close(s);
+ return (res);
++#endif /* defined(__FreeBSD__) */
+ }
diff --git a/net/openbgpd/files/patch-bgpd_config.c b/net/openbgpd/files/patch-bgpd_config.c
new file mode 100644
index 000000000000..fbd02e102483
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_config.c
@@ -0,0 +1,109 @@
+Index: bgpd/config.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/config.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.3
+diff -u -p -r1.1.1.6 -r1.3
+--- bgpd/config.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/config.c 13 Oct 2012 18:36:00 -0000 1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: config.c,v 1.51 2009/01/26 23:10:02 claudio Exp $ */
++/* $OpenBSD: config.c,v 1.55 2010/09/02 14:03:21 sobrado 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,
+
+ /* 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,
+ 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,
+ 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
+ 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
+ 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 *co
+ }
+ }
+ }
++
++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/net/openbgpd/files/patch-bgpd_control.c b/net/openbgpd/files/patch-bgpd_control.c
new file mode 100644
index 000000000000..5d6bade713cc
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_control.c
@@ -0,0 +1,171 @@
+Index: bgpd/control.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/control.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.1.1.10
+diff -u -p -r1.1.1.7 -r1.1.1.10
+--- bgpd/control.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/control.c 13 Oct 2012 18:22:41 -0000 1.1.1.10
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: control.c,v 1.61 2009/05/05 20:09:19 sthen Exp $ */
++/* $OpenBSD: control.c,v 1.71 2012/04/12 17:26:09 claudio 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);
+ }
+@@ -122,15 +122,18 @@ control_accept(int listenfd, int restric
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+- if (errno != EWOULDBLOCK && errno != EINTR)
+- log_warn("session_control_accept");
++ if (errno == ENFILE || errno == EMFILE) {
++ pauseaccept = getmonotime();
++ return (0);
++ } else if (errno != EWOULDBLOCK && errno != EINTR)
++ 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);
+ }
+@@ -182,7 +185,7 @@ control_close(int fd)
+
+ close(c->ibuf.fd);
+ free(c);
+-
++ pauseaccept = 0;
+ return (1);
+ }
+
+@@ -191,7 +194,8 @@ control_dispatch_msg(struct pollfd *pfd,
+ {
+ 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 +309,8 @@ control_dispatch_msg(struct pollfd *pfd,
+ 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 +333,19 @@ control_dispatch_msg(struct pollfd *pfd,
+ 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 +363,19 @@ control_dispatch_msg(struct pollfd *pfd,
+ "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 +387,7 @@ control_dispatch_msg(struct pollfd *pfd,
+ 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 +414,7 @@ control_dispatch_msg(struct pollfd *pfd,
+ 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;
+@@ -418,6 +434,8 @@ control_dispatch_msg(struct pollfd *pfd,
+ imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
+ case IMSG_NETWORK_ADD:
++ case IMSG_NETWORK_ASPATH:
++ case IMSG_NETWORK_ATTR:
+ case IMSG_NETWORK_REMOVE:
+ case IMSG_NETWORK_FLUSH:
+ case IMSG_NETWORK_DONE:
+@@ -425,6 +443,20 @@ control_dispatch_msg(struct pollfd *pfd,
+ 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/net/openbgpd/files/patch-bgpd_imsg.c b/net/openbgpd/files/patch-bgpd_imsg.c
new file mode 100644
index 000000000000..0ae6c8a603e7
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_imsg.c
@@ -0,0 +1,275 @@
+Index: bgpd/imsg.c
+===================================================================
+RCS file: bgpd/imsg.c
+diff -N bgpd/imsg.c
+--- bgpd/imsg.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ /dev/null 1 Jan 1970 00:00:00 -0000
+@@ -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/net/openbgpd/files/patch-bgpd_imsg.h b/net/openbgpd/files/patch-bgpd_imsg.h
new file mode 100644
index 000000000000..09fe037f51eb
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_imsg.h
@@ -0,0 +1,115 @@
+Index: bgpd/imsg.h
+===================================================================
+RCS file: bgpd/imsg.h
+diff -N bgpd/imsg.h
+--- bgpd/imsg.h 14 Feb 2010 20:19:57 -0000 1.1.1.5
++++ /dev/null 1 Jan 1970 00:00:00 -0000
+@@ -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/net/openbgpd/files/patch-bgpd_kroute.c b/net/openbgpd/files/patch-bgpd_kroute.c
new file mode 100644
index 000000000000..0b7f3943ac4a
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_kroute.c
@@ -0,0 +1,3140 @@
+Index: bgpd/kroute.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/kroute.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.15
+diff -u -p -r1.1.1.7 -r1.15
+--- bgpd/kroute.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/kroute.c 16 May 2014 00:36:26 -0000 1.15
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: kroute.c,v 1.169 2009/06/25 15:54:22 claudio Exp $ */
++/* $OpenBSD: kroute.c,v 1.190 2012/07/13 16:57:35 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);
+-
+-struct knexthop_node *knexthop_find(struct bgpd_addr *);
+-int knexthop_insert(struct knexthop_node *);
+-int knexthop_remove(struct knexthop_node *);
+-void knexthop_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 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,16 @@ int kif_kr6_remove(struct kroute6_nod
+ 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 *);
++void knexthop_validate(struct ktable *,
++ struct knexthop_node *);
++void knexthop_track(struct ktable *, void *);
++void knexthop_send_update(struct knexthop_node *);
++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(void);
++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 +165,20 @@ void get_rtaddrs(int, 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 +186,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 +224,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
++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(struct kroute_label *kl)
++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));
+
+- rtlabel_unref(kl->kr.labelid);
++ 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;
+
+- if (kroute_remove(kr) == -1)
++ 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 (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 +759,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,41 +825,18 @@ 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;
+- struct kroute6_node *k6;
+-
+- bzero(&nh, sizeof(nh));
+- memcpy(&nh.nexthop, addr, sizeof(nh.nexthop));
+- nh.valid = 1;
+- if (h->kroute != NULL && addr->af == AF_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.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) {
+- 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;
+- memcpy(&nh.gateway.v6, &k6->r.nexthop,
+- sizeof(struct in6_addr));
+- }
+- memcpy(&nh.kr.kr6, &k6->r, sizeof(nh.kr.kr6));
+- }
+-
+- send_nexthop_update(&nh);
++ knexthop_send_update(h);
+ } else {
+ if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) {
+ log_warn("kr_nexthop_add");
+@@ -492,7 +844,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 +852,26 @@ 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_delete: 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 +880,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 +888,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,41 +994,190 @@ 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;
+ }
+-
+- send_imsg_session(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
++
++ send_imsg_session(IMSG_CTL_END, imsg->hdr.pid, NULL, 0);
++}
++
++void
++kr_ifinfo(char *ifname)
++{
++ struct kif_node *kif;
++
++ RB_FOREACH(kif, kif_tree, &kit)
++ if (!strcmp(ifname, kif->k.ifname)) {
++ send_imsg_session(IMSG_IFINFO, 0,
++ &kif->k, sizeof(kif->k));
++ return;
++ }
++}
++
++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;
++ case NETWORK_MRTCLONE:
++ /* can not happen */
++ break;
++ }
++ }
++ return (NULL);
+ }
+
+-void
+-kr_ifinfo(char *ifname)
++struct network *
++kr_net_match6(struct ktable *kt, struct kroute6 *kr6)
+ {
+- struct kif_node *kif;
++ struct network *xn;
+
+- RB_FOREACH(kif, kif_tree, &kit)
+- if (!strcmp(ifname, kif->k.ifname)) {
+- send_imsg_session(IMSG_IFINFO, 0,
+- &kif->k, sizeof(kif->k));
+- return;
++ 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;
++ case NETWORK_MRTCLONE:
++ /* can not happen */
++ break;
+ }
++ }
++ return (NULL);
+ }
+
+-struct redist_node {
+- LIST_ENTRY(redist_node) entry;
+- struct kroute *kr;
+- struct kroute6 *kr6;
+-};
++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);
++ }
++ }
+
+-LIST_HEAD(, redist_node) redistlist;
++ 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 +1205,40 @@ kr_redistribute(int type, struct kroute
+ 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;
+-
+- 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);
+- }
+-
+- return (bgpd_redistribute(type, kr, NULL));
++ 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;
++
++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 (send_network(type, &net, match ? &match->net.attrset : NULL));
+ }
+
+ int
+-kr_redistribute6(int type, struct kroute6 *kr6)
+-{
+- struct redist_node *rn;
++kr_redistribute6(int type, struct ktable *kt, struct kroute6 *kr6)
++{
++ 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 +1270,107 @@ kr_redistribute6(int type, struct kroute
+ * 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);
+- }
++ 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 (bgpd_redistribute(type, NULL, kr6));
++ 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 +1427,28 @@ kroute6_compare(struct kroute6_node *a,
+ 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 +1466,8 @@ kif_compare(struct kif_node *a, struct k
+ */
+
+ 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 +1476,15 @@ kroute_find(in_addr_t prefix, u_int8_t p
+ 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 +1511,13 @@ kroute_matchgw(struct kroute_node *kr, s
+ }
+
+ 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;
+@@ -941,13 +1525,14 @@ kroute_insert(struct kroute_node *kr)
+ kr->next = NULL; /* to be sure */
+ }
+
++ /* XXX this is wrong for nexthop validated via BGP */
+ 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 +1540,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 +1560,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);
+@@ -1001,14 +1587,14 @@ 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)
++ if (kr->r.flags & F_NEXTHOP)
++ 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 +1607,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 +1626,15 @@ kroute6_find(const struct in6_addr *pref
+ 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);
+@@ -1056,17 +1643,29 @@ kroute6_find(const struct in6_addr *pref
+ struct kroute6_node *
+ kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6)
+ {
+- struct in6_addr nexthop;
++ struct sockaddr_in6 nexthop;
+
+ if (sa_in6 == NULL) {
+ log_warnx("kroute6_matchgw: no nexthop defined");
+ return (NULL);
+ }
+- memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop));
++ memcpy(&nexthop, sa_in6, sizeof(nexthop));
++#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
++ if (IN6_IS_ADDR_LINKLOCAL(&nexthop.sin6_addr)) {
++ /* Embed scope id and set sin6_scope_id. */
++ if (nexthop.sin6_scope_id == 0)
++ nexthop.sin6_scope_id =
++ IN6_LINKLOCAL_IFINDEX(nexthop.sin6_addr);
++ else
++ SET_IN6_LINKLOCAL_IFINDEX(nexthop.sin6_addr,
++ nexthop.sin6_scope_id);
++ }
++#endif
+
+ while (kr) {
+- if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == NULL)
+- return (kr);
++ if (memcmp(&kr->r.nexthop, &nexthop.sin6_addr,
++ sizeof(nexthop.sin6_addr)) == 0)
++ return (kr);
+ kr = kr->next;
+ }
+
+@@ -1074,13 +1673,13 @@ kroute6_matchgw(struct kroute6_node *kr,
+ }
+
+ 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;
+@@ -1088,14 +1687,15 @@ kroute6_insert(struct kroute6_node *kr)
+ kr->next = NULL; /* to be sure */
+ }
+
++ /* XXX this is wrong for nexthop validated via BGP */
+ 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 +1704,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 +1724,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);
+@@ -1151,14 +1752,14 @@ 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)
++ if (kr->r.flags & F_NEXTHOP)
++ 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 +1772,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 +1822,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 +1859,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 +1868,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,113 +2079,109 @@ kroute6_validate(struct kroute6 *kr)
+ }
+
+ void
+-knexthop_validate(struct knexthop_node *kn)
++knexthop_validate(struct ktable *kt, struct knexthop_node *kn)
+ {
++ void *oldk;
+ 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)
+- was_valid = kroute_validate(&kr->r);
+- if (kn->nexthop.af == AF_INET6 && (kr6 = kn->kroute) != NULL)
+- was_valid = kroute6_validate(&kr6->r);
++ oldk = kn->kroute;
++ kroute_detach_nexthop(kt, kn);
+
+- bzero(&n, sizeof(n));
+- memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
+- kroute_detach_nexthop(kn);
+-
+- switch (kn->nexthop.af) {
+- case AF_INET:
+- if ((kr = kroute_match(kn->nexthop.v4.s_addr, 0)) == NULL) {
+- if (was_valid)
+- send_nexthop_update(&n);
+- } else { /* match */
+- if (kroute_validate(&kr->r)) { /* valid */
+- 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));
+- send_nexthop_update(&n);
+- } else /* down */
+- if (was_valid)
+- send_nexthop_update(&n);
++ switch (kn->nexthop.aid) {
++ case AID_INET:
++ kr = kroute_match(kt, kn->nexthop.v4.s_addr, 0);
+
++ if (kr) {
+ kn->kroute = kr;
+ kr->r.flags |= F_NEXTHOP;
+ }
++
++ /*
++ * Send update if nexthop route changed under us if
++ * the route remains the same then the NH state has not
++ * changed. State changes are tracked by knexthop_track().
++ */
++ if (kr != oldk)
++ knexthop_send_update(kn);
+ break;
+- case AF_INET6:
+- if ((kr6 = kroute6_match(&kn->nexthop.v6, 0)) == NULL) {
+- if (was_valid)
+- send_nexthop_update(&n);
+- } else { /* match */
+- if (kroute6_validate(&kr6->r)) { /* valid */
+- 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;
+- memcpy(&n.gateway.v6, &kr6->r.nexthop,
+- sizeof(struct in6_addr));
+- }
+- memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6));
+- send_nexthop_update(&n);
+- } else /* down */
+- if (was_valid)
+- send_nexthop_update(&n);
++ case AID_INET6:
++ kr6 = kroute6_match(kt, &kn->nexthop.v6, 0);
+
++ if (kr6) {
+ kn->kroute = kr6;
+ kr6->r.flags |= F_NEXTHOP;
+ }
++
++ if (kr6 != oldk)
++ knexthop_send_update(kn);
+ break;
+ }
+ }
+
+ void
+-knexthop_track(void *krn)
++knexthop_track(struct ktable *kt, void *krp)
+ {
+ struct knexthop_node *kn;
++
++ RB_FOREACH(kn, knexthop_tree, KT2KNT(kt))
++ if (kn->kroute == krp)
++ knexthop_send_update(kn);
++}
++
++void
++knexthop_send_update(struct knexthop_node *kn)
++{
++ struct kroute_nexthop n;
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
+- struct kroute_nexthop n;
+
+- RB_FOREACH(kn, knexthop_tree, &knt)
+- if (kn->kroute == krn) {
+- bzero(&n, sizeof(n));
+- memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
++ bzero(&n, sizeof(n));
++ memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
+
+- switch (kn->nexthop.af) {
+- case AF_INET:
+- kr = krn;
+- 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));
+- break;
+- case AF_INET6:
+- kr6 = krn;
+- 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;
+- memcpy(&n.gateway.v6, &kr6->r.nexthop,
+- sizeof(struct in6_addr));
+- }
+- memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6));
+- break;
+- }
+- send_nexthop_update(&n);
++ if (kn->kroute == NULL) {
++ n.valid = 0; /* NH is not valid */
++ send_nexthop_update(&n);
++ return;
++ }
++
++ switch (kn->nexthop.aid) {
++ case AID_INET:
++ kr = kn->kroute;
++ n.valid = kroute_validate(&kr->r);
++ n.connected = kr->r.flags & F_CONNECTED;
++ if ((n.gateway.v4.s_addr =
++ kr->r.nexthop.s_addr) != 0)
++ n.gateway.aid = AID_INET;
++ if (n.connected) {
++ n.net.aid = AID_INET;
++ n.net.v4.s_addr = kr->r.prefix.s_addr;
++ n.netlen = kr->r.prefixlen;
+ }
++ break;
++ case AID_INET6:
++ kr6 = kn->kroute;
++ n.valid = kroute6_validate(&kr6->r);
++ n.connected = kr6->r.flags & F_CONNECTED;
++ if (memcmp(&kr6->r.nexthop, &in6addr_any,
++ sizeof(struct in6_addr)) != 0) {
++ n.gateway.aid = AID_INET6;
++ memcpy(&n.gateway.v6, &kr6->r.nexthop,
++ sizeof(struct in6_addr));
++ }
++ if (n.connected) {
++ 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);
+ }
+
+ 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 +2191,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 +2205,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 +2214,13 @@ kroute6_match(struct in6_addr *key, int
+ /* 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,31 +2228,30 @@ kroute6_match(struct in6_addr *key, int
+ }
+
+ 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;
+ struct kroute6_node *k6;
+
++ if (kn->kroute == NULL)
++ return;
++
+ /*
+ * check whether there's another nexthop depending on this kroute
+ * if not remove the flag
+ */
+-
+- 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))
+- ; /* nothing */
++ RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
++ if (s->kroute == kn->kroute && s != kn)
++ break;
+
+ 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 +2266,7 @@ kroute_detach_nexthop(struct knexthop_no
+ */
+
+ int
+-protect_lo(void)
++protect_lo(struct ktable *kt)
+ {
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
+@@ -1675,11 +2276,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 +2290,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 +2327,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 +2365,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_
+ case 0x00:
+ return (l);
+ default:
+- fatalx("non continguous inet6 netmask");
++ fatalx("non contiguous inet6 netmask");
+ }
+ }
+
+@@ -1788,7 +2389,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,11 +2409,10 @@ get_rtaddrs(int addrs, struct sockaddr *
+ 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;
+- struct kroute_nexthop nh;
+- struct knexthop_node *n;
+ u_int8_t reachable;
+
+ if ((kif = kif_find(ifindex)) == NULL) {
+@@ -1833,28 +2433,18 @@ if_change(u_short ifindex, int flags, st
+
+ 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 (n->kroute == kkr->kr) {
+- bzero(&nh, sizeof(nh));
+- memcpy(&nh.nexthop, &n->nexthop,
+- sizeof(nh.nexthop));
+- if (kroute_validate(&kkr->kr->r)) {
+- nh.valid = 1;
+- nh.connected = 1;
+- if ((nh.gateway.v4.s_addr =
+- kkr->kr->r.nexthop.s_addr) != 0)
+- nh.gateway.af = AF_INET;
+- }
+- memcpy(&nh.kr.kr4, &kkr->kr->r,
+- sizeof(nh.kr.kr4));
+- send_nexthop_update(&nh);
+- }
++ if (kt == NULL)
++ continue;
++
++ knexthop_track(kt, kkr->kr);
+ }
+ LIST_FOREACH(kkr6, &kif->kroute6_l, entry) {
+ if (reachable)
+@@ -1862,27 +2452,10 @@ if_change(u_short ifindex, int flags, st
+ else
+ kkr6->kr->r.flags |= F_DOWN;
+
+- RB_FOREACH(n, knexthop_tree, &knt)
+- if (n->kroute == kkr6->kr) {
+- bzero(&nh, sizeof(nh));
+- memcpy(&nh.nexthop, &n->nexthop,
+- sizeof(nh.nexthop));
+- if (kroute6_validate(&kkr6->kr->r)) {
+- nh.valid = 1;
+- nh.connected = 1;
+- if (memcmp(&kkr6->kr->r.nexthop,
+- &in6addr_any, sizeof(struct
+- in6_addr))) {
+- nh.gateway.af = AF_INET6;
+- memcpy(&nh.gateway.v6,
+- &kkr6->kr->r.nexthop,
+- sizeof(struct in6_addr));
+- }
+- }
+- memcpy(&nh.kr.kr6, &kkr6->kr->r,
+- sizeof(nh.kr.kr6));
+- send_nexthop_update(&nh);
+- }
++ if (kt == NULL)
++ continue;
++
++ knexthop_track(kt, kkr6->kr);
+ }
+ }
+
+@@ -1917,25 +2490,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 +2570,37 @@ send_rtmsg(int fd, int action, struct kr
+ 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 +2613,11 @@ send_rtmsg(int fd, int action, struct kr
+ 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 +2626,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 +2645,23 @@ send_rt6msg(int fd, int action, struct k
+ 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 +2714,7 @@ send_rt6msg(int fd, int action, struct k
+ 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 +2727,11 @@ send_rt6msg(int fd, int action, struct k
+ 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 +2740,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 +2773,35 @@ fetchtable(u_int rtableid, int connected
+ 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 +2809,11 @@ fetchtable(u_int rtableid, int connected
+ 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 +2832,11 @@ fetchtable(u_int rtableid, int connected
+ }
+
+ 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 +2854,12 @@ fetchtable(u_int rtableid, int connected
+ 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,11 +2873,25 @@ fetchtable(u_int rtableid, int connected
+ }
+
+ 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,
+ sizeof(kr6->r.prefix));
++#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
++ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.prefix)) {
++ if (((struct sockaddr_in6 *)sa)->sin6_scope_id !=0)
++ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.prefix,
++ ((struct sockaddr_in6 *)sa)->sin6_scope_id);
++ else
++ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.prefix,
++ rtm->rtm_index);
++ }
++#endif
+
+ sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK];
+ if (rtm->rtm_flags & RTF_STATIC)
+@@ -2257,8 +2906,12 @@ fetchtable(u_int rtableid, int connected
+ 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;
+@@ -2280,6 +2933,13 @@ fetchtable(u_int rtableid, int connected
+ memcpy(&kr6->r.nexthop,
+ &((struct sockaddr_in6 *)gw)->sin6_addr,
+ sizeof(kr6->r.nexthop));
++#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
++ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.nexthop) &&
++ ((struct sockaddr_in6 *)gw)->sin6_scope_id != 0) {
++ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.nexthop,
++ ((struct sockaddr_in6 *)gw)->sin6_scope_id);
++ }
++#endif
+ break;
+ case AF_LINK:
+ if (sa->sa_family == AF_INET)
+@@ -2290,23 +2950,28 @@ fetchtable(u_int rtableid, int connected
+ }
+
+ 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 +2992,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 +3061,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 +3083,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 +3099,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 +3127,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;
+@@ -2468,7 +3135,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
+ struct bgpd_addr prefix;
+- int flags, oflags, mpath = 0;
++ int flags, oflags, mpath = 0, changed = 0;
+ u_int16_t ifindex;
+ u_int8_t prefixlen;
+ u_int8_t prio;
+@@ -2494,31 +3161,54 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ 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));
++#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
++ if (IN6_IS_ADDR_LINKLOCAL(&prefix.v6) != 0) {
++ if (((struct sockaddr_in6 *)sa)->sin6_scope_id !=0)
++ SET_IN6_LINKLOCAL_IFINDEX(prefix.v6,
++ ((struct sockaddr_in6 *)sa)->sin6_scope_id);
++ else
++ SET_IN6_LINKLOCAL_IFINDEX(prefix.v6,
++ rtm->rtm_index);
++ }
++#endif
+ 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 +3227,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ }
+
+ 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 +3244,12 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ 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 +3264,23 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ 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 */
+@@ -2605,30 +3292,38 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ } else if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add4;
+
+- if (sa_in != NULL)
++ if (sa_in != NULL) {
++ if (kr->r.nexthop.s_addr !=
++ sa_in->sin_addr.s_addr)
++ changed = 1;
+ kr->r.nexthop.s_addr =
+ sa_in->sin_addr.s_addr;
+- else
++ } else {
++ if (kr->r.nexthop.s_addr != 0)
++ changed = 1;
+ kr->r.nexthop.s_addr = 0;
++ }
+
+ if (kr->r.flags & F_NEXTHOP)
+ flags |= F_NEXTHOP;
+ oflags = kr->r.flags;
++ if (flags != oflags)
++ changed = 1;
+ kr->r.flags = flags;
+ if ((oflags & F_CONNECTED) &&
+ !(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);
++ if (kr->r.flags & F_NEXTHOP && changed)
++ knexthop_track(kt, kr);
+ }
+ } else if (rtm->rtm_type == RTM_CHANGE) {
+ log_warnx("change req for %s/%u: not in table",
+@@ -2651,50 +3346,62 @@ 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 &&
+ (kr6 = kroute6_matchgw(kr6, sa_in6)) ==
+ NULL) {
+ log_warnx("dispatch_rtmsg[change] "
+- "mpath route not found");
++ "IPv6 mpath route not found");
+ return (-1);
+ } else if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add6;
+
+- if (sa_in6 != NULL)
++ if (sa_in6 != NULL) {
++ if (memcmp(&kr6->r.nexthop,
++ &sa_in6->sin6_addr,
++ sizeof(struct in6_addr)))
++ changed = 1;
+ memcpy(&kr6->r.nexthop,
+ &sa_in6->sin6_addr,
+ sizeof(struct in6_addr));
+- else
++ } else {
++ if (memcmp(&kr6->r.nexthop,
++ &in6addr_any,
++ sizeof(struct in6_addr)))
++ changed = 1;
+ memcpy(&kr6->r.nexthop,
+ &in6addr_any,
+ sizeof(struct in6_addr));
++ }
+
+ if (kr6->r.flags & F_NEXTHOP)
+ flags |= F_NEXTHOP;
+ oflags = kr6->r.flags;
++ if (flags != oflags)
++ changed = 1;
+ kr6->r.flags = flags;
+ if ((oflags & F_CONNECTED) &&
+ !(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);
++ if (kr6->r.flags & F_NEXTHOP && changed)
++ knexthop_track(kt, kr6);
+ }
+ } else if (rtm->rtm_type == RTM_CHANGE) {
+ log_warnx("change req for %s/%u: not in table",
+@@ -2719,8 +3426,12 @@ add6:
+ kr6->r.flags = flags;
+ kr6->r.ifindex = ifindex;
+ kr6->r.priority = prio;
+-
+- kroute6_insert(kr6);
++#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER)
++ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.nexthop))
++ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.nexthop,
++ ifindex);
++#endif
++ kroute6_insert(kt, kr6);
+ }
+ break;
+ }
diff --git a/net/openbgpd/files/patch-bgpd_log.c b/net/openbgpd/files/patch-bgpd_log.c
new file mode 100644
index 000000000000..6860c0f7f940
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_log.c
@@ -0,0 +1,117 @@
+Index: bgpd/log.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/log.c,v
+retrieving revision 1.1.1.5
+retrieving revision 1.1.1.8
+diff -u -p -r1.1.1.5 -r1.1.1.8
+--- bgpd/log.c 14 Feb 2010 20:19:57 -0000 1.1.1.5
++++ bgpd/log.c 13 Oct 2012 18:22:43 -0000 1.1.1.8
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: log.c,v 1.50 2007/04/23 13:04:24 claudio Exp $ */
++/* $OpenBSD: log.c,v 1.55 2011/08/20 19:02:28 sthen 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 *p
+ 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 {
+@@ -77,6 +79,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 +201,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 +258,7 @@ log_statechange(struct peer *peer, enum
+
+ 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;
+@@ -283,27 +291,31 @@ log_notification(const struct peer *peer
+ suberrname = suberr_cease_names[subcode];
+ break;
+ case ERR_HOLDTIMEREXPIRED:
+- case ERR_FSM:
+ uk = 1;
+ break;
++ case ERR_FSM:
++ if (subcode >= sizeof(suberr_fsm_names)/sizeof(char *))
++ uk = 1;
++ else
++ suberrname = suberr_fsm_names[subcode];
++ 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 +330,9 @@ log_conn_attempt(const struct peer *peer
+ 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/net/openbgpd/files/patch-bgpd_log.h b/net/openbgpd/files/patch-bgpd_log.h
new file mode 100644
index 000000000000..3d0d94de2a2e
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_log.h
@@ -0,0 +1,39 @@
+Index: bgpd/log.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/log.h,v
+retrieving revision 1.1.1.2
+retrieving revision 1.1.1.3
+diff -u -p -r1.1.1.2 -r1.1.1.3
+--- bgpd/log.h 9 Jul 2009 16:49:54 -0000 1.1.1.2
++++ bgpd/log.h 13 Oct 2012 18:22:43 -0000 1.1.1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: log.h,v 1.11 2008/09/11 14:49:58 henning Exp $ */
++/* $OpenBSD: log.h,v 1.13 2012/06/10 11:16:08 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -71,6 +71,13 @@ static const char * const suberr_open_na
+ "unsupported capability"
+ };
+
++static const char * const suberr_fsm_names[] = {
++ "unspecified error",
++ "received unexpected message in OpenSent",
++ "received unexpected message in OpenConfirm",
++ "received unexpected message in Established"
++};
++
+ static const char * const suberr_update_names[] = {
+ "none",
+ "attribute list error",
+@@ -109,7 +116,9 @@ static const char * const ctl_res_strerr
+ "no such neighbor",
+ "permission denied",
+ "neighbor does not have this capability",
+- "config file has errors, reload failed"
++ "config file has errors, reload failed",
++ "previous reload still running",
++ "out of memory"
+ };
+
+ static const char * const timernames[] = {
diff --git a/net/openbgpd/files/patch-bgpd_mrt.c b/net/openbgpd/files/patch-bgpd_mrt.c
new file mode 100644
index 000000000000..31546fcf96d0
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_mrt.c
@@ -0,0 +1,864 @@
+Index: bgpd/mrt.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/mrt.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.1.1.11
+diff -u -p -r1.1.1.7 -r1.1.1.11
+--- bgpd/mrt.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/mrt.c 8 Dec 2012 10:37:09 -0000 1.1.1.11
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: mrt.c,v 1.63 2009/06/29 12:22:16 claudio Exp $ */
++/* $OpenBSD: mrt.c,v 1.72 2011/11/06 10:29:05 guenther Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
+@@ -21,6 +21,7 @@
+
+ #include <errno.h>
+ #include <fcntl.h>
++#include <limits.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <time.h>
+@@ -32,20 +33,22 @@
+
+ #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);
+ 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_entry_v2(struct mrt *, struct rib_entry *, u_int32_t);
++int mrt_dump_peer(struct ibuf *, struct rde_peer *);
++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_warn("mrt_dump1: ibuf_add error"); \
+ goto fail; \
+ } \
+ } while (0)
+@@ -54,8 +57,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_warn("mrt_dump2: ibuf_add error"); \
+ goto fail; \
+ } \
+ } while (0)
+@@ -64,8 +67,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_warn("mrt_dump3: ibuf_add error"); \
+ goto fail; \
+ } \
+ } while (0)
+@@ -73,8 +76,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_warn("mrt_dump4: ibuf_add error"); \
+ goto fail; \
+ } \
+ } while (0)
+@@ -83,55 +86,64 @@ 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) {
+- log_warnx("mrt_dump_bgp_msg: buf_add error");
+- buf_free(buf);
++ if (ibuf_add(buf, pkg, pkglen) == -1) {
++ log_warn("mrt_dump_bgp_msg: ibuf_add error");
++ 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,
++ int v2)
+ {
+ 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, safi;
+
+ /* origin */
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN,
+@@ -140,12 +152,16 @@ mrt_attr_dump(struct buf *buf, struct rd
+
+ /* 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 (!v2)
++ pdata = aspath_deflate(pdata, &plen, &neednewpath);
++ 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)
+@@ -159,7 +175,7 @@ mrt_attr_dump(struct buf *buf, struct rd
+ return (-1);
+ }
+
+- /* local preference, only valid for ibgp */
++ /* local preference */
+ tmp = htonl(a->lpref);
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp, 4) == -1)
+ return (-1);
+@@ -173,12 +189,51 @@ mrt_attr_dump(struct buf *buf, struct rd
+ return (-1);
+ }
+
++ if (nexthop && nexthop->aid != AID_INET) {
++ struct ibuf *nhbuf;
++
++ if ((nhbuf = ibuf_dynamic(0, UCHAR_MAX)) == NULL)
++ return (-1);
++ if (!v2) {
++ if (aid2afi(nexthop->aid, &afi, &safi))
++ return (-1);
++ DUMP_SHORT(nhbuf, afi);
++ DUMP_BYTE(nhbuf, safi);
++ }
++ switch (nexthop->aid) {
++ case AID_INET6:
++ DUMP_BYTE(nhbuf, sizeof(struct in6_addr));
++ if (ibuf_add(nhbuf, &nexthop->v6,
++ sizeof(struct in6_addr)) == -1) {
++ }
++ break;
++ case AID_VPN_IPv4:
++ DUMP_BYTE(nhbuf, sizeof(u_int64_t) +
++ sizeof(struct in_addr));
++ DUMP_NLONG(nhbuf, 0); /* set RD to 0 */
++ DUMP_NLONG(nhbuf, 0);
++ DUMP_NLONG(nhbuf, nexthop->v4.s_addr);
++ break;
++ }
++ if (!v2)
++ DUMP_BYTE(nhbuf, 0);
++ if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI,
++ nhbuf->buf, ibuf_size(nhbuf)) == -1) {
++fail:
++ ibuf_free(nhbuf);
++ return (-1);
++ }
++ ibuf_free(nhbuf);
++ }
++
+ 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,28 +244,26 @@ 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;
+- void *bptr;
++ struct ibuf *buf, *hbuf = NULL, *h2buf = NULL;
+ 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) {
+- log_warn("mrt_dump_entry_mp: buf_dynamic");
++ if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
++ log_warn("mrt_dump_entry_mp: ibuf_dynamic");
+ return (-1);
+ }
+
+- if (mrt_attr_dump(buf, p->aspath, NULL) == -1) {
++ if (mrt_attr_dump(buf, p->aspath, NULL, 0) == -1) {
+ 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");
++ log_warn("mrt_dump_entry_mp: ibuf_dynamic");
+ goto fail;
+ }
+
+@@ -219,25 +272,26 @@ mrt_dump_entry_mp(struct mrt *mrt, struc
+ 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");
++ log_warn("mrt_dump_entry_mp: ibuf_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,25 +301,25 @@ mrt_dump_entry_mp(struct mrt *mrt, struc
+
+ 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) {
+- log_warnx("mrt_dump_entry_mp: buf_add error");
++ if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
++ log_warn("mrt_dump_entry_mp: ibuf_add error");
+ goto fail;
+ }
+ break;
+@@ -274,35 +328,30 @@ mrt_dump_entry_mp(struct mrt *mrt, struc
+ goto fail;
+ }
+
+- p_len = PREFIX_SIZE(p->prefix->prefixlen);
+- if ((bptr = buf_reserve(h2buf, p_len)) == NULL) {
+- log_warnx("mrt_dump_entry_mp: buf_reserve error");
+- goto fail;
+- }
+- if (prefix_write(bptr, p_len, &addr, p->prefix->prefixlen) == -1) {
+- log_warnx("mrt_dump_entry_mp: prefix_write error");
++ if (prefix_writebuf(h2buf, &addr, p->prefix->prefixlen) == -1) {
++ log_warn("mrt_dump_entry_mp: prefix_writebuf error");
+ goto fail;
+ }
+
+ 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 +359,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) {
+- log_warnx("mrt_dump_entry: buf_dynamic");
++ if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
++ log_warn("mrt_dump_entry: ibuf_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) {
++ if (mrt_attr_dump(buf, p->aspath, nh, 0) == -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 +397,241 @@ mrt_dump_entry(struct mrt *mrt, struct p
+ 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_warn("mrt_dump_entry: ibuf_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_warn("mrt_dump_entry: ibuf_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);
++}
++
++int
++mrt_dump_entry_v2(struct mrt *mrt, struct rib_entry *re, u_int32_t snum)
++{
++ struct ibuf *buf, *hbuf = NULL;
++ struct prefix *p;
++ struct bgpd_addr addr;
++ size_t len, off;
++ u_int16_t subtype, nump;
++
++ switch (re->prefix->aid) {
++ case AID_INET:
++ subtype = MRT_DUMP_V2_RIB_IPV4_UNICAST;
++ break;
++ case AID_INET6:
++ subtype = MRT_DUMP_V2_RIB_IPV6_UNICAST;
++ break;
++ default:
++ subtype = MRT_DUMP_V2_RIB_GENERIC;
++ break;
++ }
++
++ if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
++ log_warn("mrt_dump_entry: ibuf_dynamic");
++ return (-1);
++ }
++
++ DUMP_LONG(buf, snum);
++ pt_getaddr(re->prefix, &addr);
++ if (subtype == MRT_DUMP_V2_RIB_GENERIC) {
++ u_int16_t afi;
++ u_int8_t safi;
++
++ aid2afi(re->prefix->aid, &afi, &safi);
++ DUMP_SHORT(buf, afi);
++ DUMP_BYTE(buf, safi);
++ }
++ if (prefix_writebuf(buf, &addr, re->prefix->prefixlen) == -1) {
++ log_warn("mrt_dump_entry_mp: prefix_writebuf error");
++ goto fail;
++ }
++
++ off = ibuf_size(buf);
++ if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
++ log_warn("mrt_dump_v2_hdr: ibuf_reserve error");
++ goto fail;
++ }
++ nump = 0;
++ LIST_FOREACH(p, &re->prefix_h, rib_l) {
++ struct bgpd_addr *nh;
++ struct ibuf *tbuf;
++
++ if (p->aspath->nexthop == NULL) {
++ bzero(&addr, sizeof(struct bgpd_addr));
++ addr.aid = p->prefix->aid;
++ nh = &addr;
++ } else
++ nh = &p->aspath->nexthop->exit_nexthop;
++
++ DUMP_SHORT(buf, p->aspath->peer->mrt_idx);
++ DUMP_LONG(buf, p->lastchange); /* originated */
++
++ if ((tbuf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
++ log_warn("mrt_dump_entry_v2: ibuf_dynamic");
++ return (-1);
++ }
++ if (mrt_attr_dump(tbuf, p->aspath, nh, 1) == -1) {
++ log_warnx("mrt_dump_entry_v2: mrt_attr_dump error");
++ ibuf_free(buf);
++ return (-1);
++ }
++ len = ibuf_size(tbuf);
++ DUMP_SHORT(buf, (u_int16_t)len);
++ if (ibuf_add(buf, tbuf->buf, ibuf_size(tbuf)) == -1) {
++ log_warn("mrt_dump_entry_v2: ibuf_add error");
++ ibuf_free(tbuf);
++ return (-1);
++ }
++ ibuf_free(tbuf);
++ nump++;
++ }
++ nump = htons(nump);
++ memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
++
++ len = ibuf_size(buf);
++ if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, subtype, len) == -1) {
++ ibuf_free(buf);
++ return (-1);
++ }
++
++ ibuf_close(&mrt->wbuf, hbuf);
++ ibuf_close(&mrt->wbuf, buf);
++
++ return (0);
++fail:
++ if (hbuf)
++ ibuf_free(hbuf);
++ ibuf_free(buf);
++ return (-1);
++}
++
++int
++mrt_dump_v2_hdr(struct mrt *mrt, struct bgpd_config *conf,
++ struct rde_peer_head *ph)
++{
++ struct rde_peer *peer;
++ struct ibuf *buf, *hbuf = NULL;
++ size_t len, off;
++ u_int16_t nlen, nump;
++
++ if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
++ log_warn("mrt_dump_v2_hdr: ibuf_dynamic");
++ return (-1);
++ }
++
++ DUMP_NLONG(buf, conf->bgpid);
++ nlen = strlen(mrt->rib);
++ if (nlen > 0)
++ nlen += 1;
++ DUMP_SHORT(buf, nlen);
++ if (ibuf_add(buf, mrt->rib, nlen) == -1) {
++ log_warn("mrt_dump_v2_hdr: ibuf_add error");
++ goto fail;
++ }
++
++ off = ibuf_size(buf);
++ if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
++ log_warn("mrt_dump_v2_hdr: ibuf_reserve error");
++ goto fail;
++ }
++ nump = 0;
++ LIST_FOREACH(peer, ph, peer_l) {
++ peer->mrt_idx = nump;
++ if (mrt_dump_peer(buf, peer) == -1)
++ goto fail;
++ nump++;
++ }
++ nump = htons(nump);
++ memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
++
++ len = ibuf_size(buf);
++ if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2,
++ MRT_DUMP_V2_PEER_INDEX_TABLE, len) == -1)
++ goto fail;
++
++ ibuf_close(&mrt->wbuf, hbuf);
++ ibuf_close(&mrt->wbuf, buf);
++
++ return (0);
++fail:
++ if (hbuf)
++ ibuf_free(hbuf);
++ ibuf_free(buf);
++ return (-1);
++}
++
++int
++mrt_dump_peer(struct ibuf *buf, struct rde_peer *peer)
++{
++ u_int8_t type = 0;
++
++ if (peer->capa.as4byte)
++ type |= MRT_DUMP_V2_PEER_BIT_A;
++ if (peer->remote_addr.aid == AID_INET6)
++ type |= MRT_DUMP_V2_PEER_BIT_I;
++
++ DUMP_BYTE(buf, type);
++ DUMP_LONG(buf, peer->remote_bgpid);
++
++ switch (peer->remote_addr.aid) {
++ case AID_INET:
++ DUMP_NLONG(buf, peer->remote_addr.v4.s_addr);
++ break;
++ case AID_INET6:
++ if (ibuf_add(buf, &peer->remote_addr.v6,
++ sizeof(struct in6_addr)) == -1) {
++ log_warn("mrt_dump_peer: ibuf_add error");
++ goto fail;
++ }
++ break;
++ case AID_UNSPEC: /* XXX special handling for peer_self? */
++ DUMP_NLONG(buf, 0);
++ break;
++ default:
++ log_warnx("king bula found new AF in mrt_dump_entry_mp");
++ goto fail;
++ }
++
++ if (peer->capa.as4byte)
++ DUMP_LONG(buf, peer->conf.remote_as);
++ else
++ DUMP_SHORT(buf, peer->short_as);
++
++ return (0);
++fail:
+ return (-1);
+ }
+
+@@ -371,6 +641,11 @@ mrt_dump_upcall(struct rib_entry *re, vo
+ struct mrt *mrtbuf = ptr;
+ struct prefix *p;
+
++ if (mrtbuf->type == MRT_TABLE_DUMP_V2) {
++ mrt_dump_entry_v2(mrtbuf, re, mrtbuf->seqnum++);
++ return;
++ }
++
+ /*
+ * dump all prefixes even the inactive ones. That is the way zebra
+ * dumps the table so we do the same. If only the active route should
+@@ -387,7 +662,7 @@ mrt_dump_upcall(struct rib_entry *re, vo
+ }
+
+ void
+-mrt_dump_done(void *ptr)
++mrt_done(void *ptr)
+ {
+ struct mrt *mrtbuf = ptr;
+
+@@ -395,14 +670,14 @@ 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");
++ log_warn("mrt_dump_hdr_se: ibuf_dynamic error");
+ return (-1);
+ }
+
+@@ -468,23 +743,23 @@ mrt_dump_hdr_se(struct buf ** bp, struct
+ 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");
++ log_warn("mrt_dump_hdr_se: ibuf_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");
++ log_warn("mrt_dump_hdr_se: ibuf_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");
++ log_warn("mrt_dump_hdr_se: ibuf_add error");
+ goto fail;
+ }
+ break;
+@@ -493,20 +768,20 @@ mrt_dump_hdr_se(struct buf ** bp, struct
+ 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");
++ log_warn("mrt_dump_hdr_rde: ibuf_dynamic error");
+ return (-1);
+ }
+
+@@ -517,19 +792,28 @@ mrt_dump_hdr_rde(struct buf **bp, u_int1
+
+ 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:
++ case MSG_TABLE_DUMP_V2:
+ DUMP_LONG(*bp, len);
+ break;
+ default:
+ log_warnx("mrt_dump_hdr_rde: unsupported type");
+ goto fail;
+- }
++ }
+ return (0);
+
+ fail:
+- buf_free(*bp);
++ ibuf_free(*bp);
+ return (-1);
+ }
+
+@@ -538,21 +822,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;
+ }
+@@ -590,7 +875,8 @@ mrt_open(struct mrt *mrt, time_t now)
+ else
+ type = IMSG_MRT_REOPEN;
+
+- if (mrt->type == MRT_TABLE_DUMP || mrt->type == MRT_TABLE_DUMP_MP)
++ if (mrt->type == MRT_TABLE_DUMP || mrt->type == MRT_TABLE_DUMP_MP ||
++ mrt->type == MRT_TABLE_DUMP_V2)
+ i = 0;
+
+ if (imsg_compose(mrt_imsgbuf[i], type, 0, 0, fd,
+@@ -659,7 +945,9 @@ mrt_handler(struct mrt_head *mrt)
+ LIST_FOREACH(m, mrt, entry) {
+ if (m->state == MRT_STATE_RUNNING &&
+ (MRT2MC(m)->ReopenTimerInterval != 0 ||
+- m->type == MRT_TABLE_DUMP)) {
++ m->type == MRT_TABLE_DUMP ||
++ m->type == MRT_TABLE_DUMP_MP ||
++ m->type == MRT_TABLE_DUMP_V2)) {
+ if (mrt_open(m, now) == -1)
+ continue;
+ MRT2MC(m)->ReopenTimer =
diff --git a/net/openbgpd/files/patch-bgpd_mrt.h b/net/openbgpd/files/patch-bgpd_mrt.h
new file mode 100644
index 000000000000..0c9d835f5b78
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_mrt.h
@@ -0,0 +1,287 @@
+Index: bgpd/mrt.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/mrt.h,v
+retrieving revision 1.1.1.6
+retrieving revision 1.1.1.9
+diff -u -p -r1.1.1.6 -r1.1.1.9
+--- bgpd/mrt.h 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/mrt.h 13 Oct 2012 18:22:43 -0000 1.1.1.9
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: mrt.h,v 1.23 2009/06/29 12:22:16 claudio Exp $ */
++/* $OpenBSD: mrt.h,v 1.30 2011/09/18 09:31:25 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
+ */
+
+@@ -37,11 +35,18 @@
+ * | length | length of packet excluding this header
+ * +--------+--------+--------+--------+
+ *
+- * ET types include an additional 32bit microsecond field comming after the
+- * length field.
++ * ET types include an additional 32bit microsecond field coming after the
++ * length field. Which is accounted in the length field.
+ */
+ #define MRT_HEADER_SIZE 12
+
++struct mrt_hdr {
++ u_int32_t timestamp;
++ u_int16_t type;
++ u_int16_t subtype;
++ u_int32_t length;
++} __packed;
++
+ enum MRT_MSG_TYPES {
+ MSG_NULL, /* 0 empty msg (deprecated) */
+ MSG_START, /* 1 sender is starting up */
+@@ -70,13 +75,15 @@ enum MRT_MSG_TYPES {
+ * that are normaly saved as MSG_TABLE_DUMP.
+ * In most cases this is the format to choose to dump updates et al.
+ */
+-enum MRT_BGP4MP_TYPES {
++enum MRT_BGP4MP_SUBTYPES {
+ BGP4MP_STATE_CHANGE, /* state change */
+ 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 */
+@@ -104,6 +111,7 @@ enum MRT_BGP4MP_TYPES {
+ *
+ * The source_ip and dest_ip are dependant of the afi type. For IPv6 source_ip
+ * and dest_ip are both 16 bytes long.
++ * For the AS4 types the source_as and dest_as numbers are both 4 bytes long.
+ *
+ * Payload of a BGP4MP_STATE_CHANGE packet:
+ *
+@@ -155,6 +163,98 @@ enum MRT_BGP4MP_TYPES {
+ */
+
+ /*
++ * New MRT dump format MSG_TABLE_DUMP_V2, the dump is implemented with
++ * sub-tables for peers and NLRI entries just use the index into the peer
++ * table.
++ */
++enum MRT_DUMP_V2_SUBTYPES {
++ MRT_DUMP_V2_PEER_INDEX_TABLE=1,
++ MRT_DUMP_V2_RIB_IPV4_UNICAST=2,
++ MRT_DUMP_V2_RIB_IPV4_MULTICAST=3,
++ MRT_DUMP_V2_RIB_IPV6_UNICAST=4,
++ MRT_DUMP_V2_RIB_IPV6_MULTICAST=5,
++ MRT_DUMP_V2_RIB_GENERIC=6
++};
++
++/*
++ * Format of the MRT_DUMP_V2_PEER_INDEX_TABLE:
++ * If there is no view_name, view_name_len must be set to 0
++ *
++ * +--------+--------+--------+--------+
++ * | collector_bgp_id |
++ * +--------+--------+--------+--------+
++ * | view_name_len | view_name
++ * +--------+--------+--------+--------+
++ * view_name (variable) ... |
++ * +--------+--------+--------+--------+
++ * | peer_count | peer_entries
++ * +--------+--------+--------+--------+
++ * peer_entries (variable) ...
++ * +--------+--------+--------+--------+
++ *
++ * The format of a peer_entry is the following:
++ *
++ * +--------+
++ * | type |
++ * +--------+--------+--------+--------+
++ * | peer_bgp_id |
++ * +--------+--------+--------+--------+
++ * | peer_ip_addr (variable) |
++ * +--------+--------+--------+--------+
++ * | peer_as (variable) |
++ * +--------+--------+--------+--------+
++ *
++ * The message is packed a bit strangely. The type byte defines what size
++ * the peer addr and peer AS have.
++ * The position of a peer in the PEER_INDEX_TABLE is used as the index for
++ * the other messages.
++ */
++#define MRT_DUMP_V2_PEER_BIT_I 0x1 /* set for IPv6 addrs */
++#define MRT_DUMP_V2_PEER_BIT_A 0x2 /* set for 32 bits AS number */
++
++/*
++ * AFI/SAFI specific RIB Subtypes are special to save a few bytes.
++ *
++ * +--------+--------+--------+--------+
++ * | seq_num |
++ * +--------+--------+--------+--------+
++ * | plen | prefix (variable)
++ * +--------+--------+--------+--------+
++ * | #entry | rib entries (variable)
++ * +--------+--------+--------+--------+
++ *
++ * The RIB_GENERIC subtype is needed for the less common AFI/SAFI pairs
++ *
++ * +--------+--------+--------+--------+
++ * | seq_num |
++ * +--------+--------+--------+--------+
++ * | AFI | SAFI | NLRI
++ * +--------+--------+--------+--------+
++ * NLRI (variable) ...
++ * +--------+--------+--------+--------+
++ * | #entry | rib entries (variable)
++ * +--------+--------+--------+--------+
++ */
++
++/*
++ * The RIB entries have the following form.
++ *
++ * +--------+--------+
++ * | peer index |
++ * +--------+--------+--------+--------+
++ * | originated_time |
++ * +--------+--------+--------+--------+
++ * | attr_len | bgp_attrs
++ * +--------+--------+--------+--------+
++ * bgp_attrs (variable) ...
++ * +--------+--------+--------+--------+
++ *
++ * Some BGP path attributes need special encoding:
++ * - the AS_PATH attribute MUST be encoded as 4-Byte AS
++ * - the MP_REACH_NLRI only consists of the nexthop len and nexthop address
++ */
++
++/*
+ * Format for routing table dumps in "old" mrt format.
+ * Type MSG_TABLE_DUMP and subtype is AFI_IPv4 (1) for IPv4 and AFI_IPv6 (2)
+ * for IPv6. In the IPv6 case prefix and peer_ip are both 16 bytes long.
+@@ -182,8 +282,14 @@ enum MRT_BGP4MP_TYPES {
+ * The status field is unused and should be set to 1.
+ */
+
++enum MRT_DUMP_SUBTYPES {
++ MRT_DUMP_AFI_IP=1,
++ MRT_DUMP_AFI_IPv6=2
++};
++
+ /* 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
+@@ -192,7 +298,7 @@ enum MRT_BGP4MP_TYPES {
+ * Only for bgp messages (type 5, 9 and 10)
+ * Nota bene for bgp dumps MSG_PROTOCOL_BGP4MP should be used.
+ */
+-enum MRT_BGP_TYPES {
++enum MRT_BGP_SUBTYPES {
+ MSG_BGP_NULL,
+ MSG_BGP_UPDATE, /* raw update packet (contains both withdraws
+ and announcements) */
+@@ -221,10 +327,8 @@ enum MRT_BGP_TYPES {
+ *
+ * For IPv6 the type is MSG_PROTOCOL_BGP4PLUS and the subtype remains
+ * MSG_BGP_UPDATE. The source_ip and dest_ip are again extended to 16 bytes.
+- */
+-
+-/*
+- * For subtype MSG_BGP_STATECHANGE (for all BGP types or just for the
++ *
++ * For subtype MSG_BGP_STATE_CHANGE (for all BGP types or just for the
+ * MSG_PROTOCOL_BGP4PLUS case? Unclear.)
+ *
+ * +--------+--------+--------+--------+
+@@ -235,7 +339,7 @@ enum MRT_BGP_TYPES {
+ * | new_state |
+ * +--------+--------+
+ *
+- * State are defined in RFC 1771.
++ * States are defined in RFC 1771/4271.
+ */
+
+ /*
+@@ -251,66 +355,4 @@ enum MRT_BGP_TYPES {
+ * terminated ... | 0 |
+ * +--------+--------+--------+
+ */
+-
+-#define MRT_FILE_LEN 512
+-enum mrt_type {
+- MRT_NONE,
+- MRT_TABLE_DUMP,
+- MRT_TABLE_DUMP_MP,
+- MRT_ALL_IN,
+- MRT_ALL_OUT,
+- MRT_UPDATE_IN,
+- MRT_UPDATE_OUT
+-};
+-
+-enum mrt_state {
+- MRT_STATE_RUNNING,
+- MRT_STATE_OPEN,
+- MRT_STATE_REOPEN,
+- MRT_STATE_REMOVE
+-};
+-
+-struct mrt {
+- char rib[PEER_DESCR_LEN];
+- struct msgbuf wbuf;
+- LIST_ENTRY(mrt) entry;
+- u_int32_t peer_id;
+- u_int32_t group_id;
+- enum mrt_type type;
+- enum mrt_state state;
+- u_int16_t seqnum;
+-};
+-
+-struct mrt_config {
+- struct mrt conf;
+- char name[MRT_FILE_LEN]; /* base file name */
+- char file[MRT_FILE_LEN]; /* actual file name */
+- time_t ReopenTimer;
+- time_t ReopenTimerInterval;
+-};
+-
+-#define MRT2MC(x) ((struct mrt_config *)(x))
+-#define MRT_MAX_TIMEOUT 7200
+-
+-struct peer;
+-struct prefix;
+-struct rib_entry;
+-
+-/* prototypes */
+-void mrt_dump_bgp_msg(struct mrt *, void *, u_int16_t,
+- struct peer *);
+-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_write(struct mrt *);
+-void mrt_clean(struct mrt *);
+-void mrt_init(struct imsgbuf *, struct imsgbuf *);
+-int mrt_timeout(struct mrt_head *);
+-void mrt_reconfigure(struct mrt_head *);
+-void mrt_handler(struct mrt_head *);
+-struct mrt *mrt_get(struct mrt_head *, struct mrt *);
+-int mrt_mergeconfig(struct mrt_head *, struct mrt_head *);
+-
+ #endif
diff --git a/net/openbgpd/files/patch-bgpd_name2id.c b/net/openbgpd/files/patch-bgpd_name2id.c
new file mode 100644
index 000000000000..bf6cfbd067a8
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_name2id.c
@@ -0,0 +1,14 @@
+Index: bgpd/name2id.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/name2id.c,v
+retrieving revision 1.1.1.2
+retrieving revision 1.1.1.3
+diff -u -p -r1.1.1.2 -r1.1.1.3
+--- bgpd/name2id.c 9 Jul 2009 16:49:54 -0000 1.1.1.2
++++ bgpd/name2id.c 13 Oct 2012 18:22:43 -0000 1.1.1.3
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: name2id.c,v 1.9 2009/06/04 04:46:42 claudio Exp $ */
++/* $OpenBSD: name2id.c,v 1.8 2009/05/17 12:25:15 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2004, 2005 Henning Brauer <henning@openbsd.org>
diff --git a/net/openbgpd/files/patch-bgpd_parse.y b/net/openbgpd/files/patch-bgpd_parse.y
new file mode 100644
index 000000000000..0f9160187aac
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_parse.y
@@ -0,0 +1,1626 @@
+Index: bgpd/parse.y
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/parse.y,v
+retrieving revision 1.1.1.8
+retrieving revision 1.12
+diff -u -p -r1.1.1.8 -r1.12
+--- bgpd/parse.y 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/parse.y 8 Dec 2012 20:17:59 -0000 1.12
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: parse.y,v 1.231 2009/06/06 01:10:29 claudio Exp $ */
++/* $OpenBSD: parse.y,v 1.264 2012/09/23 09:39:17 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>
+@@ -33,6 +36,9 @@
+ #include <limits.h>
+ #include <stdarg.h>
+ #include <stdio.h>
++#if defined(__FreeBSD__)
++#include <stdlib.h>
++#endif
+ #include <string.h>
+ #include <syslog.h>
+
+@@ -74,10 +80,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 +113,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 +121,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 +131,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 +169,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 +218,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 +226,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 +293,8 @@ yesno : STRING {
+ else if (!strcmp($1, "no"))
+ $$ = 0;
+ else {
++ yyerror("syntax error, "
++ "either yes or no expected");
+ free($1);
+ YYERROR;
+ }
+@@ -318,7 +339,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 +363,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 +390,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 +399,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 +440,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;
+
+@@ -484,6 +454,8 @@ conf_main : AS as4number {
+ action = MRT_TABLE_DUMP;
+ else if (!strcmp($2, "table-mp"))
+ action = MRT_TABLE_DUMP_MP;
++ else if (!strcmp($2, "table-v2"))
++ action = MRT_TABLE_DUMP_V2;
+ else {
+ yyerror("unknown mrt dump type");
+ free($2);
+@@ -511,6 +483,8 @@ conf_main : AS as4number {
+ action = MRT_TABLE_DUMP;
+ else if (!strcmp($4, "table-mp"))
+ action = MRT_TABLE_DUMP_MP;
++ else if (!strcmp($4, "table-v2"))
++ action = MRT_TABLE_DUMP_V2;
+ else {
+ yyerror("unknown mrt dump type");
+ free($3);
+@@ -575,11 +549,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 +571,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 +612,47 @@ mrtdump : DUMP STRING inout STRING optn
+ }
+ ;
+
++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 +664,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 +682,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 +701,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 +715,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 +734,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 +967,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 +1028,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 +1046,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.grestart.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 +1269,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 +1343,10 @@ family : IPV4 { $$ = AFI_IPv4; }
+ | IPV6 { $$ = AFI_IPv6; }
+ ;
+
++nettype : STATIC { $$ = 1; },
++ | CONNECTED { $$ = 0; }
++ ;
++
+ espah : ESP { $$ = 1; }
+ | AH { $$ = 0; }
+ ;
+@@ -1336,12 +1526,12 @@ filter_prefix_l : filter_prefix { $$
+ ;
+
+ 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 +1600,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 +1633,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 +1653,93 @@ 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;
++ }
++ | NEXTHOP address {
++ if (fmopts.m.nexthop.flags) {
++ yyerror("nexthop already specified");
++ YYERROR;
++ }
++ if (fmopts.aid && fmopts.aid != $2.aid) {
++ yyerror("nexthop address family doesn't match "
++ "rule address family");
++ YYERROR;
++ }
++ fmopts.m.nexthop.addr = $2;
++ fmopts.m.nexthop.flags = FILTER_NEXTHOP_ADDR;
++ }
++ | NEXTHOP NEIGHBOR {
++ if (fmopts.m.nexthop.flags) {
++ yyerror("nexthop already specified");
++ YYERROR;
++ }
++ fmopts.m.nexthop.flags = FILTER_NEXTHOP_NEIGHBOR;
+ }
+ ;
+
+@@ -1588,7 +1845,7 @@ filter_set_opt : LOCALPREF NUMBER {
+ }
+ if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+ fatal(NULL);
+- if ($2 > 0) {
++ if ($2 >= 0) {
+ $$->type = ACTION_SET_MED;
+ $$->action.metric = $2;
+ } else {
+@@ -1623,7 +1880,7 @@ filter_set_opt : LOCALPREF NUMBER {
+ }
+ if (($$ = calloc(1, sizeof(struct filter_set))) == NULL)
+ fatal(NULL);
+- if ($2 > 0) {
++ if ($2 >= 0) {
+ $$->type = ACTION_SET_MED;
+ $$->action.metric = $2;
+ } else {
+@@ -1782,8 +2039,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 +2052,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 +2151,7 @@ lookup(char *s)
+ { "allow", ALLOW},
+ { "announce", ANNOUNCE},
+ { "any", ANY},
++ { "as-4byte", AS4BYTE },
+ { "blackhole", BLACKHOLE},
+ { "capabilities", CAPABILITIES},
+ { "community", COMMUNITY},
+@@ -1889,16 +2168,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 +2191,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 +2205,7 @@ lookup(char *s)
+ { "nexthop", NEXTHOP},
+ { "no-modify", NOMODIFY},
+ { "on", ON},
++ { "origin", ORIGIN},
+ { "out", OUT},
+ { "passive", PASSIVE},
+ { "password", PASSWORD},
+@@ -1929,10 +2217,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 +2233,7 @@ lookup(char *s)
+ { "rtlabel", RTLABEL},
+ { "self", SELF},
+ { "set", SET},
++ { "socket", SOCKET },
+ { "softreconfig", SOFTRECONFIG},
+ { "source-as", SOURCEAS},
+ { "spi", SPI},
+@@ -2117,9 +2410,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 +2429,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 +2588,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 +2633,15 @@ parse_config(char *filename, struct bgpd
+ 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 +2663,9 @@ parse_config(char *filename, struct bgpd
+
+ 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 +2679,44 @@ parse_config(char *filename, struct bgpd
+
+ 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 +2848,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 +2880,176 @@ parsecommunity(char *s, int *as, int *ty
+
+ 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 +3060,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.grestart.restart = 1;
++ 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 +3088,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 +3173,52 @@ add_mrtconfig(enum mrt_type type, char *
+ }
+
+ 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 +3227,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 +3368,11 @@ str2key(char *s, char *dest, size_t max_
+ 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 +3383,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,18 +3403,14 @@ 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)
+- p->conf.announce_type = p->conf.ebgp == 0 ?
+- ANNOUNCE_ALL : ANNOUNCE_SELF;
++ p->conf.announce_type = p->conf.ebgp ?
++ ANNOUNCE_SELF : ANNOUNCE_ALL;
+ if (p->conf.enforce_as == ENFORCE_AS_UNDEF)
+- p->conf.enforce_as = p->conf.ebgp == 0 ?
+- ENFORCE_AS_OFF : ENFORCE_AS_ON;
++ p->conf.enforce_as = p->conf.ebgp ?
++ ENFORCE_AS_ON : ENFORCE_AS_OFF;
+
+ /* EBGP neighbors are not allowed in route reflector clusters */
+ if (p->conf.reflector_client && p->conf.ebgp) {
+@@ -2909,6 +3419,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 +3442,11 @@ merge_filterset(struct filter_set_head *
+ 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 +3473,18 @@ merge_filterset(struct filter_set_head *
+ 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 +3514,6 @@ copy_filterset(struct filter_set_head *s
+ }
+ }
+
+-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/net/openbgpd/files/patch-bgpd_pfkey.c b/net/openbgpd/files/patch-bgpd_pfkey.c
new file mode 100644
index 000000000000..224298fadf0d
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_pfkey.c
@@ -0,0 +1,471 @@
+diff -ur bgpd.orig/pfkey.c bgpd/pfkey.c
+--- bgpd.orig/pfkey.c 2013-03-15 12:07:16.000000000 +0000
++++ bgpd/pfkey.c 2013-03-15 12:07:47.000000000 +0000
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: pfkey.c,v 1.37 2009/04/21 15:25:52 henning Exp $ */
++/* $OpenBSD: pfkey.c,v 1.40 2009/12/14 17:38:18 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -21,7 +21,7 @@
+ #include <sys/socket.h>
+ #include <sys/uio.h>
+ #include <net/pfkeyv2.h>
+-#include <netinet/ip_ipsp.h>
++//#include <netinet/ip_ipsp.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <limits.h>
+@@ -65,15 +65,15 @@
+ {
+ struct sadb_msg smsg;
+ struct sadb_sa sa;
+- struct sadb_address sa_src, sa_dst, sa_peer, sa_smask, sa_dmask;
++ struct sadb_address sa_src, sa_dst;
+ struct sadb_key sa_akey, sa_ekey;
+ struct sadb_spirange sa_spirange;
+- struct sadb_protocol sa_flowtype, sa_protocol;
+ struct iovec iov[IOV_CNT];
+ ssize_t n;
+ int len = 0;
+ int iov_cnt;
+- struct sockaddr_storage ssrc, sdst, speer, smask, dmask;
++ struct sockaddr_storage ssrc, sdst, smask, dmask;
++ struct sockaddr *saptr;
+
+ if (!pid)
+ pid = getpid();
+@@ -81,22 +81,17 @@
+ /* 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 +102,17 @@
+
+ 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:
+@@ -135,7 +125,7 @@
+ smsg.sadb_msg_version = PF_KEY_V2;
+ smsg.sadb_msg_seq = ++sadb_msg_seq;
+ smsg.sadb_msg_pid = pid;
+- smsg.sadb_msg_len = sizeof(smsg) / 8;
++ smsg.sadb_msg_len = PFKEY_UNIT64(sizeof(smsg));
+ smsg.sadb_msg_type = mtype;
+ smsg.sadb_msg_satype = satype;
+
+@@ -143,7 +133,7 @@
+ case SADB_GETSPI:
+ bzero(&sa_spirange, sizeof(sa_spirange));
+ sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+- sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
++ sa_spirange.sadb_spirange_len = PFKEY_UNIT64(sizeof(sa_spirange));
+ sa_spirange.sadb_spirange_min = 0x100;
+ sa_spirange.sadb_spirange_max = 0xffffffff;
+ sa_spirange.sadb_spirange_reserved = 0;
+@@ -153,11 +143,12 @@
+ case SADB_DELETE:
+ bzero(&sa, sizeof(sa));
+ sa.sadb_sa_exttype = SADB_EXT_SA;
+- sa.sadb_sa_len = sizeof(sa) / 8;
++ sa.sadb_sa_len = PFKEY_UNIT64(sizeof(sa));
+ sa.sadb_sa_replay = 0;
+ sa.sadb_sa_spi = spi;
+ sa.sadb_sa_state = SADB_SASTATE_MATURE;
+ break;
++#if 0
+ case SADB_X_ADDFLOW:
+ case SADB_X_DELFLOW:
+ bzero(&sa_flowtype, sizeof(sa_flowtype));
+@@ -172,35 +163,37 @@
+ sa_protocol.sadb_protocol_direction = 0;
+ sa_protocol.sadb_protocol_proto = 6;
+ break;
++#endif
+ }
+
+ bzero(&sa_src, sizeof(sa_src));
+ sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+- sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
++ sa_src.sadb_address_len = PFKEY_UNIT64(sizeof(sa_src) + ROUNDUP(ssrc.ss_len));
+
+ bzero(&sa_dst, sizeof(sa_dst));
+ sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+- sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
++ sa_dst.sadb_address_len = PFKEY_UNIT64(sizeof(sa_dst) + ROUNDUP(sdst.ss_len));
+
+ sa.sadb_sa_auth = aalg;
+- sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */
++ sa.sadb_sa_encrypt = ealg; /* XXX */
+
+ switch (mtype) {
+ case SADB_ADD:
+ case SADB_UPDATE:
+ bzero(&sa_akey, sizeof(sa_akey));
+ sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+- sa_akey.sadb_key_len = (sizeof(sa_akey) +
+- ((alen + 7) / 8) * 8) / 8;
++ sa_akey.sadb_key_len = PFKEY_UNIT64(sizeof(sa_akey) +
++ (PFKEY_ALIGN8(alen)));
+ sa_akey.sadb_key_bits = 8 * alen;
+
+ bzero(&sa_ekey, sizeof(sa_ekey));
+ sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+- sa_ekey.sadb_key_len = (sizeof(sa_ekey) +
+- ((elen + 7) / 8) * 8) / 8;
++ sa_ekey.sadb_key_len = PFKEY_UNIT64(sizeof(sa_ekey) +
++ (PFKEY_ALIGN8(elen)));
+ sa_ekey.sadb_key_bits = 8 * elen;
+
+ break;
++#if 0
+ case SADB_X_ADDFLOW:
+ case SADB_X_DELFLOW:
+ /* sa_peer always points to the remote machine */
+@@ -220,8 +213,8 @@
+ 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 +226,7 @@
+ 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 +240,8 @@
+ 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 +253,7 @@
+ 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,
+@@ -284,6 +277,7 @@
+ sa_dmask.sadb_address_len =
+ (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
+ break;
++#endif
+ }
+
+ iov_cnt = 0;
+@@ -310,6 +304,7 @@
+ smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
+ iov_cnt++;
+ break;
++#if 0
+ case SADB_X_ADDFLOW:
+ /* sa_peer always points to the remote machine */
+ iov[iov_cnt].iov_base = &sa_peer;
+@@ -351,6 +346,7 @@
+ smsg.sadb_msg_len += sa_dmask.sadb_address_len;
+ iov_cnt++;
+ break;
++#endif
+ }
+
+ /* dest addr */
+@@ -380,7 +376,7 @@
+ iov[iov_cnt].iov_len = sizeof(sa_akey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = akey;
+- iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8;
++ iov[iov_cnt].iov_len = PFKEY_ALIGN8(alen);
+ smsg.sadb_msg_len += sa_akey.sadb_key_len;
+ iov_cnt++;
+ }
+@@ -390,14 +386,14 @@
+ iov[iov_cnt].iov_len = sizeof(sa_ekey);
+ iov_cnt++;
+ iov[iov_cnt].iov_base = ekey;
+- iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8;
++ iov[iov_cnt].iov_len = PFKEY_ALIGN8(elen);
+ smsg.sadb_msg_len += sa_ekey.sadb_key_len;
+ iov_cnt++;
+ }
+ break;
+ }
+
+- len = smsg.sadb_msg_len * 8;
++ len = PFKEY_UNUNIT64(smsg.sadb_msg_len);
+ do {
+ n = writev(sd, iov, iov_cnt);
+ } while (n == -1 && (errno == EAGAIN || errno == EINTR));
+@@ -411,6 +407,33 @@
+ }
+
+ 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,27 +441,17 @@
+ 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");
+- 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");
++ do {
++ rv = pfkey_read(sd, &hdr);
++ if (rv == -1)
+ return (-1);
+- }
+- }
++ } while (rv);
+
+ if (hdr.sadb_msg_errno != 0) {
+ errno = hdr.sadb_msg_errno;
+- if (errno == ESRCH)
++ if (errno == ESRCH || errno == EEXIST)
+ return (0);
+ else {
+ log_warn("pfkey");
+@@ -486,13 +499,8 @@
+ pfkey_sa_add(struct bgpd_addr *src, struct bgpd_addr *dst, u_int8_t keylen,
+ char *key, u_int32_t *spi)
+ {
+- if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_GETSPI, 0,
+- src, dst, 0, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
+- return (-1);
+- if (pfkey_reply(fd, spi) < 0)
+- return (-1);
+- if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_UPDATE, 0,
+- src, dst, *spi, 0, keylen, key, 0, 0, NULL, 0, 0) < 0)
++ if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_ADD, 0,
++ src, dst, *spi, SADB_X_AALG_TCP_MD5, keylen, key, SADB_EALG_NONE, 0, NULL, 0, 0) < 0)
+ return (-1);
+ if (pfkey_reply(fd, NULL) < 0)
+ return (-1);
+@@ -503,7 +511,7 @@
+ pfkey_sa_remove(struct bgpd_addr *src, struct bgpd_addr *dst, u_int32_t *spi)
+ {
+ if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_DELETE, 0,
+- src, dst, *spi, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
++ src, dst, *spi, SADB_X_AALG_TCP_MD5, 0, NULL, 0, 0, NULL, 0, 0) < 0)
+ return (-1);
+ if (pfkey_reply(fd, NULL) < 0)
+ return (-1);
+@@ -511,37 +519,37 @@
+ return (0);
+ }
+
++#define TCP_SIG_SPI 0x1000
+ int
+ pfkey_md5sig_establish(struct peer *p)
+ {
+ sleep(1);
+
+- if (!p->auth.spi_out)
+- if (pfkey_sa_add(&p->auth.local_addr, &p->conf.remote_addr,
+- p->conf.auth.md5key_len, p->conf.auth.md5key,
+- &p->auth.spi_out) == -1)
+- return (-1);
+- if (!p->auth.spi_in)
+- if (pfkey_sa_add(&p->conf.remote_addr, &p->auth.local_addr,
+- p->conf.auth.md5key_len, p->conf.auth.md5key,
+- &p->auth.spi_in) == -1)
+- return (-1);
++ p->auth.spi_out = htonl(TCP_SIG_SPI);
++ if (pfkey_sa_add(&p->auth.local_addr, &p->conf.remote_addr,
++ p->conf.auth.md5key_len, p->conf.auth.md5key,
++ &p->auth.spi_out) == -1)
++ return (-1);
++ p->auth.spi_in = htonl(TCP_SIG_SPI);
++ if (pfkey_sa_add(&p->conf.remote_addr, &p->auth.local_addr,
++ p->conf.auth.md5key_len, p->conf.auth.md5key,
++ &p->auth.spi_out) == -1)
++ return (-1);
+
+ p->auth.established = 1;
+ return (0);
+ }
++#undef TCP_SIG_SPI
+
+ int
+ pfkey_md5sig_remove(struct peer *p)
+ {
+- if (p->auth.spi_out)
+- if (pfkey_sa_remove(&p->auth.local_addr, &p->conf.remote_addr,
+- &p->auth.spi_out) == -1)
+- return (-1);
+- if (p->auth.spi_in)
+- if (pfkey_sa_remove(&p->conf.remote_addr, &p->auth.local_addr,
+- &p->auth.spi_in) == -1)
+- return (-1);
++ if (pfkey_sa_remove(&p->auth.local_addr, &p->conf.remote_addr,
++ &p->auth.spi_out) == -1)
++ return (-1);
++ if (pfkey_sa_remove(&p->conf.remote_addr, &p->auth.local_addr,
++ &p->auth.spi_in) == -1)
++ return (-1);
+
+ p->auth.established = 0;
+ return (0);
+@@ -550,6 +558,7 @@
+ int
+ pfkey_ipsec_establish(struct peer *p)
+ {
++#if 0
+ uint8_t satype = SADB_SATYPE_ESP;
+
+ switch (p->auth.method) {
+@@ -621,6 +630,9 @@
+
+ p->auth.established = 1;
+ return (0);
++#else
++ return (-1);
++#endif
+ }
+
+ int
+@@ -660,6 +672,7 @@
+ break;
+ }
+
++#if 0
+ if (pfkey_flow(fd, satype, SADB_X_DELFLOW, IPSP_DIRECTION_OUT,
+ &p->auth.local_addr, &p->conf.remote_addr, 0, BGP_PORT) < 0)
+ return (-1);
+@@ -681,6 +694,7 @@
+ if (pfkey_flow(fd, satype, SADB_X_DELFLOW, IPSP_DIRECTION_IN,
+ &p->conf.remote_addr, &p->auth.local_addr, BGP_PORT, 0) < 0)
+ return (-1);
++#endif
+ if (pfkey_reply(fd, NULL) < 0)
+ return (-1);
+
+@@ -715,9 +729,7 @@
+ int
+ pfkey_remove(struct peer *p)
+ {
+- if (!p->auth.established)
+- return (0);
+- else if (p->auth.method == AUTH_MD5SIG)
++ if (p->auth.method == AUTH_MD5SIG)
+ return (pfkey_md5sig_remove(p));
+ else
+ return (pfkey_ipsec_remove(p));
+@@ -730,11 +742,9 @@
+ 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/net/openbgpd/files/patch-bgpd_pftable.c b/net/openbgpd/files/patch-bgpd_pftable.c
new file mode 100644
index 000000000000..ee6a1ea2750c
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_pftable.c
@@ -0,0 +1,17 @@
+Index: bgpd/pftable.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/pftable.c,v
+retrieving revision 1.1.1.5
+retrieving revision 1.1.1.7
+diff -u -p -r1.1.1.5 -r1.1.1.7
+--- bgpd/pftable.c 14 Feb 2010 20:19:57 -0000 1.1.1.5
++++ bgpd/pftable.c 13 Oct 2012 18:22:44 -0000 1.1.1.7
+@@ -214,7 +214,7 @@ pftable_add_work(const char *table, stru
+
+ 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/net/openbgpd/files/patch-bgpd_printconf.c b/net/openbgpd/files/patch-bgpd_printconf.c
new file mode 100644
index 000000000000..9b00e1efa386
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_printconf.c
@@ -0,0 +1,439 @@
+Index: bgpd/printconf.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/printconf.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.11
+diff -u -p -r1.1.1.7 -r1.11
+--- bgpd/printconf.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/printconf.c 16 May 2014 00:36:26 -0000 1.11
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: printconf.c,v 1.70 2009/06/06 01:10:29 claudio Exp $ */
++/* $OpenBSD: printconf.c,v 1.88 2012/09/23 09:39:18 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
+ 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
+ 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
+ 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.grestart.restart == 0)
++ printf("%s\tannounce restart no\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
+ 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
+ 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 f
+ } 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 ");
+ }
+
+@@ -479,6 +568,13 @@ print_rule(struct peer *peer_l, struct f
+ }
+ }
+
++ if (r->match.nexthop.flags) {
++ if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
++ printf("nexthop neighbor ");
++ else
++ printf("nexthop %s ", log_addr(&r->match.nexthop.addr));
++ }
++
+ if (r->match.as.type) {
+ if (r->match.as.type == AS_ALL)
+ printf("AS %s ", log_as(r->match.as.as));
+@@ -492,11 +588,20 @@ print_rule(struct peer *peer_l, struct f
+ 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);
+
+@@ -513,6 +618,8 @@ mrt_type(enum mrt_type t)
+ return "table";
+ case MRT_TABLE_DUMP_MP:
+ return "table-mp";
++ case MRT_TABLE_DUMP_V2:
++ return "table-v2";
+ case MRT_ALL_IN:
+ return "all in";
+ case MRT_ALL_OUT:
+@@ -541,12 +648,12 @@ print_mrt(u_int32_t pid, u_int32_t gid,
+ printf("%s%sdump ", prep, prep2);
+ if (m->rib[0])
+ printf("rib %s ", m->rib);
++ printf("%s \"%s\"", mrt_type(m->type),
++ MRT2MC(m)->name);
+ if (MRT2MC(m)->ReopenTimerInterval == 0)
+- printf("%s %s\n", mrt_type(m->type),
+- MRT2MC(m)->name);
++ printf("\n");
+ else
+- printf("%s %s %d\n", mrt_type(m->type),
+- MRT2MC(m)->name,
++ printf(" %ld\n",
+ MRT2MC(m)->ReopenTimerInterval);
+ }
+ }
+@@ -612,26 +719,34 @@ peer_compare(const void *aa, const void
+ 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/net/openbgpd/files/patch-bgpd_rde.c b/net/openbgpd/files/patch-bgpd_rde.c
new file mode 100644
index 000000000000..76761d859f1d
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde.c
@@ -0,0 +1,2614 @@
+Index: bgpd/rde.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde.c,v
+retrieving revision 1.1.1.8
+retrieving revision 1.12
+diff -u -p -r1.1.1.8 -r1.12
+--- bgpd/rde.c 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/rde.c 16 May 2014 00:36:26 -0000 1.12
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: rde.c,v 1.264 2009/06/29 12:22:16 claudio Exp $ */
++/* $OpenBSD: rde.c,v 1.320 2012/09/18 09:45:51 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+@@ -18,10 +18,11 @@
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
++#include <sys/time.h>
++#include <sys/resource.h>
+
+ #include <errno.h>
+ #include <ifaddrs.h>
+-#include <limits.h>
+ #include <pwd.h>
+ #include <poll.h>
+ #include <signal.h>
+@@ -50,13 +51,18 @@ void rde_update_withdraw(struct rde_pe
+ u_int8_t);
+ int rde_attr_parse(u_char *, u_int16_t, struct rde_peer *,
+ struct rde_aspath *, struct mpattr *);
++int rde_attr_add(struct rde_aspath *, u_char *, u_int16_t);
+ 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_
+ 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,12 @@ struct rde_peer *peer_add(u_int32_t, str
+ 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_flush(struct rde_peer *, u_int8_t);
++void peer_stale(u_int32_t, u_int8_t);
++void peer_recv_eor(struct rde_peer *, u_int8_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 +120,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 +133,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 +158,17 @@ 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)
+ {
+ 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 +179,6 @@ rde_main(struct bgpd_config *config, str
+ return (pid);
+ }
+
+- conf = config;
+-
+ if ((pw = getpwnam(BGPD_USER)) == NULL)
+ fatal("getpwnam");
+
+@@ -194,6 +199,8 @@ rde_main(struct bgpd_config *config, str
+ 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 +217,25 @@ rde_main(struct bgpd_config *config, str
+ 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 +269,18 @@ rde_main(struct bgpd_config *config, str
+ 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 +314,17 @@ rde_main(struct bgpd_config *config, str
+ 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 +333,12 @@ rde_main(struct bgpd_config *config, str
+ 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 +361,18 @@ rde_dispatch_imsg_session(struct imsgbuf
+ 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 csr;
+ struct ctl_show_rib_request req;
++ struct rde_peer *peer;
++ struct rde_aspath *asp;
+ struct filter_set *s;
+ struct nexthop *nh;
+- int n;
++ u_int8_t *asdata;
++ ssize_t n;
++ int verbose;
++ u_int16_t len;
++ u_int8_t aid;
+
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("rde_dispatch_imsg_session: imsg_read error");
+@@ -422,13 +410,56 @@ rde_dispatch_imsg_session(struct imsgbuf
+ case IMSG_SESSION_DOWN:
+ peer_down(imsg.hdr.peerid);
+ break;
++ case IMSG_SESSION_STALE:
++ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) {
++ log_warnx("rde_dispatch: wrong imsg len");
++ break;
++ }
++ memcpy(&aid, imsg.data, sizeof(aid));
++ if (aid >= AID_MAX)
++ fatalx("IMSG_SESSION_STALE: bad AID");
++ peer_stale(imsg.hdr.peerid, aid);
++ break;
++ case IMSG_SESSION_FLUSH:
++ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) {
++ log_warnx("rde_dispatch: wrong imsg len");
++ break;
++ }
++ memcpy(&aid, imsg.data, sizeof(aid));
++ if (aid >= AID_MAX)
++ fatalx("IMSG_SESSION_FLUSH: bad AID");
++ if ((peer = peer_get(imsg.hdr.peerid)) == NULL) {
++ log_warnx("rde_dispatch: unknown peer id %d",
++ imsg.hdr.peerid);
++ break;
++ }
++ peer_flush(peer, aid);
++ break;
++ case IMSG_SESSION_RESTARTED:
++ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) {
++ log_warnx("rde_dispatch: wrong imsg len");
++ break;
++ }
++ memcpy(&aid, imsg.data, sizeof(aid));
++ if (aid >= AID_MAX)
++ fatalx("IMSG_SESSION_RESTARTED: bad AID");
++ if ((peer = peer_get(imsg.hdr.peerid)) == NULL) {
++ log_warnx("rde_dispatch: unknown peer id %d",
++ imsg.hdr.peerid);
++ break;
++ }
++ if (peer->staletime[aid])
++ peer_flush(peer, aid);
++ 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 !=
+@@ -440,23 +471,68 @@ rde_dispatch_imsg_session(struct imsgbuf
+ TAILQ_INIT(&netconf_s.attrset);
+ session_set = &netconf_s.attrset;
+ break;
++ case IMSG_NETWORK_ASPATH:
++ if (imsg.hdr.len - IMSG_HEADER_SIZE <
++ sizeof(struct ctl_show_rib)) {
++ log_warnx("rde_dispatch: wrong imsg len");
++ bzero(&netconf_s, sizeof(netconf_s));
++ break;
++ }
++ asdata = imsg.data;
++ asdata += sizeof(struct ctl_show_rib);
++ memcpy(&csr, imsg.data, sizeof(csr));
++ if (csr.aspath_len + sizeof(csr) > imsg.hdr.len -
++ IMSG_HEADER_SIZE) {
++ log_warnx("rde_dispatch: wrong aspath len");
++ bzero(&netconf_s, sizeof(netconf_s));
++ break;
++ }
++ asp = path_get();
++ asp->lpref = csr.local_pref;
++ asp->med = csr.med;
++ asp->weight = csr.weight;
++ asp->flags = csr.flags;
++ asp->origin = csr.origin;
++ asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC;
++ asp->aspath = aspath_get(asdata, csr.aspath_len);
++ netconf_s.asp = asp;
++ break;
++ case IMSG_NETWORK_ATTR:
++ if (imsg.hdr.len <= IMSG_HEADER_SIZE) {
++ log_warnx("rde_dispatch: wrong imsg len");
++ break;
++ }
++ /* parse path attributes */
++ len = imsg.hdr.len - IMSG_HEADER_SIZE;
++ asp = netconf_s.asp;
++ if (rde_attr_add(asp, imsg.data, len) == -1) {
++ log_warnx("rde_dispatch: bad network "
++ "attribute");
++ path_put(asp);
++ bzero(&netconf_s, sizeof(netconf_s));
++ break;
++ }
++ break;
+ case IMSG_NETWORK_DONE:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE) {
+ log_warnx("rde_dispatch: wrong imsg len");
+ 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);
+ break;
++ case 0:
++ /* something failed beforehands */
++ break;
+ default:
+ badnet:
+ log_warnx("rde_dispatch: bad network");
+@@ -528,10 +604,14 @@ badnet:
+ peer->prefix_rcvd_update;
+ p.stats.prefix_rcvd_withdraw =
+ peer->prefix_rcvd_withdraw;
++ p.stats.prefix_rcvd_eor =
++ peer->prefix_rcvd_eor;
+ p.stats.prefix_sent_update =
+ peer->prefix_sent_update;
+ p.stats.prefix_sent_withdraw =
+ peer->prefix_sent_withdraw;
++ p.stats.prefix_sent_eor =
++ peer->prefix_sent_eor;
+ }
+ imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NEIGHBOR, 0,
+ imsg.hdr.pid, -1, &p, sizeof(struct peer));
+@@ -544,6 +624,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 +639,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 +664,12 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +688,26 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +715,26 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +748,42 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +791,27 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +819,59 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +881,18 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ }
+ 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");
++ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
++ -1, NULL, 0);
+ 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");
+@@ -725,7 +920,8 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ log_warnx("expected to receive fd for mrt dump "
+ "but didn't receive any");
+ else if (xmrt.type == MRT_TABLE_DUMP ||
+- xmrt.type == MRT_TABLE_DUMP_MP) {
++ xmrt.type == MRT_TABLE_DUMP_MP ||
++ xmrt.type == MRT_TABLE_DUMP_V2) {
+ rde_dump_mrt_new(&xmrt, imsg.hdr.pid, fd);
+ } else
+ close(fd);
+@@ -744,6 +940,8 @@ rde_dispatch_imsg_parent(struct imsgbuf
+ 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 +950,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 +1007,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 +1052,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;
+@@ -879,6 +1071,10 @@ rde_update_dispatch(struct imsg *imsg)
+ ERR_UPD_ATTRLIST, NULL, 0);
+ return (-1);
+ }
++ if (withdrawn_len == 0) {
++ /* EoR marker */
++ peer_recv_eor(peer, AID_INET);
++ }
+ return (0);
+ }
+
+@@ -892,15 +1088,30 @@ 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;
++ }
++
++ if ((asp->flags & ~F_ATTR_MP_UNREACH) == 0 && mplen == 0) {
++ /* EoR marker */
++ peer_recv_eor(peer, aid);
++ }
++
++ switch (aid) {
++ case AID_INET6:
+ while (mplen > 0) {
+ if ((pos = rde_update_get_prefix6(mpp, mplen,
+ &prefix, &prefixlen)) == -1) {
+@@ -926,6 +1137,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 +1200,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 +1232,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 +1257,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 +1266,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 +1303,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 +1366,8 @@ rde_update_update(struct rde_peer *peer,
+ 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 +1377,24 @@ rde_update_update(struct rde_peer *peer,
+
+ 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 +1402,8 @@ done:
+
+ if (r)
+ peer->prefix_cnt++;
++ else if (f)
++ peer->prefix_cnt--;
+ }
+
+ void
+@@ -1152,7 +1442,7 @@ rde_update_withdraw(struct rde_peer *pee
+ } while (0)
+
+ #define CHECK_FLAGS(s, t, m) \
+- (((s) & ~(ATTR_EXTLEN | (m))) == (t))
++ (((s) & ~(ATTR_DEFMASK | (m))) == (t))
+
+ int
+ rde_attr_parse(u_char *p, u_int16_t len, struct rde_peer *peer,
+@@ -1161,6 +1451,7 @@ rde_attr_parse(u_char *p, u_int16_t len,
+ 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 +1486,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 +1512,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 +1550,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 +1607,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 +1637,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 +1677,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 +1711,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,20 +1729,31 @@ 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;
+- }
+- a->flags |= F_ATTR_AS4BYTE_NEW;
+- 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;
+ default:
+ if ((flags & ATTR_OPTIONAL) == 0) {
+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_UNKNWN_WK_ATTR,
+@@ -1415,6 +1774,42 @@ bad_list:
+
+ return (plen);
+ }
++
++int
++rde_attr_add(struct rde_aspath *a, u_char *p, u_int16_t len)
++{
++ u_int16_t attr_len;
++ u_int16_t plen = 0;
++ u_int8_t flags;
++ u_int8_t type;
++ u_int8_t tmp8;
++
++ if (a == NULL) /* no aspath, nothing to do */
++ return (0);
++ if (len < 3)
++ return (-1);
++
++ UPD_READ(&flags, p, plen, 1);
++ UPD_READ(&type, p, plen, 1);
++
++ if (flags & ATTR_EXTLEN) {
++ if (len - plen < 2)
++ return (-1);
++ UPD_READ(&attr_len, p, plen, 2);
++ attr_len = ntohs(attr_len);
++ } else {
++ UPD_READ(&tmp8, p, plen, 1);
++ attr_len = tmp8;
++ }
++
++ if (len - plen < attr_len)
++ return (-1);
++
++ if (attr_optadd(a, flags, type, p, attr_len) == -1)
++ return (-1);
++ return (0);
++}
++
+ #undef UPD_READ
+ #undef CHECK_FLAGS
+
+@@ -1440,8 +1835,8 @@ rde_attr_missing(struct rde_aspath *a, i
+ }
+
+ 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 +1852,9 @@ rde_get_mp_nexthop(u_char *data, u_int16
+ 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 +1867,144 @@ rde_get_mp_nexthop(u_char *data, u_int16
+ 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);
++ nexthop.scope_id = 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 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 +2014,50 @@ rde_update_get_prefix6(u_char *p, u_int1
+ 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 +2109,30 @@ rde_as4byte_fixup(struct rde_peer *peer,
+ 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 +2176,10 @@ rde_reflector(struct rde_peer *peer, str
+ 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) {
+@@ -1677,10 +2188,10 @@ rde_reflector(struct rde_peer *peer, str
+ return;
+ }
+ } else if (conf->flags & BGPD_FLAG_REFLECTOR) {
+- if (peer->conf.ebgp == 0)
+- id = htonl(peer->remote_bgpid);
+- else
++ if (peer->conf.ebgp)
+ id = conf->bgpid;
++ else
++ id = htonl(peer->remote_bgpid);
+ if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID,
+ &id, sizeof(u_int32_t)) == -1)
+ fatalx("attr_optadd failed but impossible");
+@@ -1724,17 +2235,17 @@ 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;
++ time_t staletime;
+ u_int8_t l;
+
+ bzero(&rib, sizeof(rib));
+ rib.lastchange = p->lastchange;
+ rib.local_pref = asp->lpref;
+ rib.med = asp->med;
+- rib.prefix_cnt = asp->prefix_cnt;
+- rib.active_cnt = asp->active_cnt;
++ rib.weight = asp->weight;
+ strlcpy(rib.descr, asp->peer->conf.descr, sizeof(rib.descr));
+ memcpy(&rib.remote_addr, &asp->peer->remote_addr,
+ sizeof(rib.remote_addr));
+@@ -1748,23 +2259,26 @@ rde_dump_rib_as(struct prefix *p, struct
+ /* 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;
+- if (asp->peer->conf.ebgp == 0)
+- rib.flags |= F_RIB_INTERNAL;
++ rib.flags |= F_PREF_ACTIVE;
++ if (!asp->peer->conf.ebgp)
++ 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;
++ staletime = asp->peer->staletime[p->prefix->aid];
++ if (staletime && p->lastchange <= staletime)
++ rib.flags |= F_PREF_STALE;
+ rib.aspath_len = aspath_length(asp->aspath);
+
+ if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid,
+@@ -1784,13 +2298,13 @@ rde_dump_rib_as(struct prefix *p, struct
+ 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,17 +2342,20 @@ rde_dump_filter(struct prefix *p, struct
+ {
+ 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 &&
+- !aspath_match(p->aspath->aspath, req->as.type, req->as.as))
++ if (req->type == IMSG_CTL_SHOW_RIB_AS &&
++ !aspath_match(p->aspath->aspath->data,
++ p->aspath->aspath->len, 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;
++ if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p)
++ return;
+ rde_dump_rib_as(p, p->aspath, req->pid, req->flags);
+ } else if (req->flags & F_CTL_ADJ_OUT) {
+ if (p->rib->active != p)
+@@ -1872,7 +2389,7 @@ rde_dump_prefix_upcall(struct rib_entry
+
+ 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 +2406,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
+ 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 +2420,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
+ error = CTL_RES_NOSUCHPEER;
+ imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error,
+ sizeof(error));
++ free(ctx);
+ return;
+ }
+
+@@ -1924,7 +2443,18 @@ rde_dump_ctx_new(struct ctl_show_rib_req
+ 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 +2467,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
+ }
+ 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);
+ }
+
+@@ -1971,13 +2501,17 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t
+ free(ctx);
+ return;
+ }
++
++ if (ctx->mrt.type == MRT_TABLE_DUMP_V2)
++ mrt_dump_v2_hdr(&ctx->mrt, conf, &peerlist);
++
+ 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 +2519,25 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t
+ /*
+ * 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 +2557,43 @@ rde_send_kroute(struct prefix *new, stru
+ }
+
+ 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 (!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 +2644,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 +2651,6 @@ rde_send_nexthop(struct bgpd_addr *next,
+ 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 +2744,10 @@ rde_softreconfig_in(struct rib_entry *re
+ 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 +2775,7 @@ rde_softreconfig_in(struct rib_entry *re
+ 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 +2788,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 +2897,7 @@ rde_up_dump_upcall(struct rib_entry *re,
+ 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 +2910,7 @@ rde_generate_updates(u_int16_t ribid, st
+
+ /*
+ * 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 +2931,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 +2945,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 +2955,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 +3009,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 +3032,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 +3082,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 +3100,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 +3115,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 +3200,10 @@ peer_localaddrs(struct rde_peer *peer, s
+ 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 +3221,7 @@ peer_localaddrs(struct rde_peer *peer, s
+ &((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,23 +3233,22 @@ 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) {
+- log_warnx("peer_up: peer id %d already exists", id);
++ log_warnx("peer_up: unknown peer id %d", id);
+ return;
+ }
+
+- if (peer->state != PEER_DOWN && peer->state != PEER_NONE)
++ if (peer->state != PEER_DOWN && peer->state != PEER_NONE &&
++ peer->state != PEER_UP)
+ fatalx("peer_up: bad state");
+ peer->remote_bgpid = ntohl(sup->remote_bgpid);
+ 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 +3262,10 @@ peer_up(u_int32_t id, struct session_up
+ */
+ 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
+@@ -2641,43 +3299,90 @@ peer_down(u_int32_t id)
+ free(peer);
+ }
+
++/*
++ * Flush all routes older then staletime. If staletime is 0 all routes will
++ * be flushed.
++ */
++void
++peer_flush(struct rde_peer *peer, u_int8_t aid)
++{
++ struct rde_aspath *asp, *nasp;
++
++ /* walk through per peer RIB list and remove all stale prefixes. */
++ for (asp = LIST_FIRST(&peer->path_h); asp != NULL; asp = nasp) {
++ nasp = LIST_NEXT(asp, peer_l);
++ path_remove_stale(asp, aid);
++ }
++
++ /* Deletions are performed in path_remove() */
++ rde_send_pftable_commit();
++
++ /* flushed no need to keep staletime */
++ peer->staletime[aid] = 0;
++}
++
+ void
+-peer_dump(u_int32_t id, u_int16_t afi, u_int8_t safi)
++peer_stale(u_int32_t id, u_int8_t aid)
+ {
+ struct rde_peer *peer;
++ time_t now;
+
+ peer = peer_get(id);
+ if (peer == NULL) {
+- log_warnx("peer_down: unknown peer id %d", id);
++ log_warnx("peer_stale: 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->staletime[aid])
++ peer_flush(peer, aid);
++ peer->staletime[aid] = now = time(NULL);
+
+- if (peer->capa_received.restart && peer->capa_announced.restart)
+- peer_send_eor(peer, afi, safi);
++ /* make sure new prefixes start on a higher timestamp */
++ do {
++ sleep(1);
++ } while (now >= time(NULL));
+ }
+
+-/* End-of-RIB marker, draft-ietf-idr-restart-13.txt */
+ void
+-peer_send_eor(struct rde_peer *peer, u_int16_t afi, u_int16_t safi)
++peer_dump(u_int32_t id, u_int8_t aid)
+ {
+- if (afi == AFI_IPv4 && safi == SAFI_UNICAST) {
++ struct rde_peer *peer;
++
++ peer = peer_get(id);
++ if (peer == NULL) {
++ log_warnx("peer_dump: unknown peer id %d", id);
++ return;
++ }
++
++ 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.grestart.restart)
++ up_generate_marker(peer, aid);
++}
++
++/* End-of-RIB marker, RFC 4724 */
++void
++peer_recv_eor(struct rde_peer *peer, u_int8_t aid)
++{
++ peer->prefix_rcvd_eor++;
++
++ /* First notify SE to remove possible race with the timeout. */
++ if (imsg_compose(ibuf_se, IMSG_SESSION_RESTARTED, peer->conf.id,
++ 0, -1, &aid, sizeof(aid)) == -1)
++ fatal("imsg_compose error");
++}
++
++void
++peer_send_eor(struct rde_peer *peer, u_int8_t aid)
++{
++ u_int16_t afi;
++ u_int8_t safi;
++
++ peer->prefix_sent_eor++;
++
++ if (aid == AID_INET) {
+ u_char null[4];
+
+ bzero(&null, 4);
+@@ -2688,6 +3393,9 @@ peer_send_eor(struct rde_peer *peer, u_i
+ 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,39 +3417,61 @@ peer_send_eor(struct rde_peer *peer, u_i
+ * 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;
+
+- asp = path_get();
+- asp->aspath = aspath_get(NULL, 0);
+- asp->origin = ORIGIN_IGP;
+- asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH |
+- F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED;
+- /* the nexthop is unset unless a default set overrides it */
++ 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;
++ }
++ }
++ }
++
++ if (nc->type == NETWORK_MRTCLONE) {
++ asp = nc->asp;
++ } else {
++ asp = path_get();
++ asp->aspath = aspath_get(NULL, 0);
++ asp->origin = ORIGIN_IGP;
++ asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH |
++ F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED;
++ /* the nexthop is unset unless a default set overrides it */
++ }
+ 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);
+-
+ path_put(asp);
+ filterset_free(&nc->attrset);
+ }
+@@ -2749,12 +3479,41 @@ network_add(struct network_config *nc, i
+ 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 +3523,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 +3593,10 @@ sa_cmp(struct bgpd_addr *a, struct socka
+ 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)
+@@ -2855,10 +3607,11 @@ sa_cmp(struct bgpd_addr *a, struct socka
+ #ifdef __KAME__
+ /* directly stolen from sbin/ifconfig/ifconfig.c */
+ if (IN6_IS_ADDR_LINKLOCAL(&in6_b->sin6_addr)) {
+- in6_b->sin6_scope_id =
+- ntohs(*(u_int16_t *)&in6_b->sin6_addr.s6_addr[2]);
+- in6_b->sin6_addr.s6_addr[2] =
+- in6_b->sin6_addr.s6_addr[3] = 0;
++ if (in6_b->sin6_scope_id == 0) {
++ in6_b->sin6_scope_id =
++ IN6_LINKLOCAL_IFINDEX(in6_b->sin6_addr);
++ }
++ SET_IN6_LINKLOCAL_IFINDEX(in6_b->sin6_addr, 0);
+ }
+ #endif
+ if (bcmp(&a->v6, &in6_b->sin6_addr,
diff --git a/net/openbgpd/files/patch-bgpd_rde.h b/net/openbgpd/files/patch-bgpd_rde.h
new file mode 100644
index 000000000000..ba7d2d095891
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde.h
@@ -0,0 +1,361 @@
+Index: bgpd/rde.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde.h,v
+retrieving revision 1.1.1.8
+retrieving revision 1.1.1.13
+diff -u -p -r1.1.1.8 -r1.1.1.13
+--- bgpd/rde.h 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/rde.h 8 Dec 2012 10:37:09 -0000 1.1.1.13
+@@ -1,8 +1,8 @@
+-/* $OpenBSD: rde.h,v 1.120 2009/06/06 01:10:29 claudio Exp $ */
++/* $OpenBSD: rde.h,v 1.144 2012/09/12 05:56:22 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
+- * Andre Oppermann <oppermann@pipeline.ch>
++ * Andre Oppermann <oppermann@networx.ch>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+@@ -56,16 +56,16 @@ 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;
++ time_t staletime[AID_MAX];
+ u_int64_t prefix_rcvd_update;
+ u_int64_t prefix_rcvd_withdraw;
++ u_int64_t prefix_rcvd_eor;
+ u_int64_t prefix_sent_update;
+ u_int64_t prefix_sent_withdraw;
++ u_int64_t prefix_sent_eor;
+ u_int32_t prefix_cnt; /* # of prefixes */
+ u_int32_t remote_bgpid; /* host byte order! */
+ u_int32_t up_pcnt;
+@@ -75,12 +75,16 @@ struct rde_peer {
+ enum peer_state state;
+ u_int16_t ribid;
+ u_int16_t short_as;
++ u_int16_t mrt_idx;
+ 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);
+@@ -117,6 +121,9 @@ enum attrtypes {
+ #define ATTR_PARTIAL 0x20
+ #define ATTR_TRANSITIVE 0x40
+ #define ATTR_OPTIONAL 0x80
++#define ATTR_RESERVED 0x0f
++/* by default mask the reserved bits and the ext len bit */
++#define ATTR_DEFMASK (ATTR_RESERVED | ATTR_EXTLEN)
+
+ /* default attribute flags for well known attributes */
+ #define ATTR_WELL_KNOWN ATTR_TRANSITIVE
+@@ -163,6 +170,8 @@ LIST_HEAD(prefix_head, prefix);
+ #define F_NEXTHOP_REJECT 0x02000
+ #define F_NEXTHOP_BLACKHOLE 0x04000
+ #define F_NEXTHOP_NOMODIFY 0x08000
++#define F_NEXTHOP_MASK 0x0f000
++#define F_ATTR_PARSE_ERR 0x10000
+ #define F_ATTR_LINKED 0x20000
+
+
+@@ -220,14 +229,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 +244,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 +272,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 +284,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 {
+@@ -292,8 +306,14 @@ struct prefix {
+ extern struct rde_memstats rdemem;
+
+ /* prototypes */
++/* mrt.c */
++int mrt_dump_v2_hdr(struct mrt *, struct bgpd_config *,
++ struct rde_peer_head *);
++void mrt_dump_upcall(struct rib_entry *, void *);
++void mrt_done(void *);
++
+ /* 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 +329,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 +347,7 @@ int aspath_verify(void *, u_int16_t, i
+ #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);
+@@ -341,22 +362,66 @@ u_int32_t aspath_neighbor(struct aspath
+ 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_decide.c */
++void prefix_evaluate(struct prefix *, struct rib_entry *);
++
++/* rde_filter.c */
++enum filter_actions rde_filter(u_int16_t, struct rde_aspath **,
++ struct filter_head *, struct rde_peer *,
++ 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 *,
++ u_int8_t, struct rde_peer *, struct rde_peer *);
++int rde_filter_equal(struct filter_head *, struct filter_head *,
++ struct rde_peer *, enum directions);
++
++/* rde_prefix.c */
++#define pt_empty(pt) ((pt)->refcnt == 0)
++#define pt_ref(pt) do { \
++ ++(pt)->refcnt; \
++ if ((pt)->refcnt == 0) \
++ fatalx("pt_ref: overflow"); \
++} while(0)
++#define pt_unref(pt) do { \
++ if ((pt)->refcnt == 0) \
++ fatalx("pt_unref: underflow"); \
++ --(pt)->refcnt; \
++} while(0)
++
++void pt_init(void);
++void pt_shutdown(void);
++void pt_getaddr(struct pt_entry *, struct bgpd_addr *);
++struct pt_entry *pt_fill(struct bgpd_addr *, int);
++struct pt_entry *pt_get(struct bgpd_addr *, int);
++struct pt_entry *pt_add(struct bgpd_addr *, int);
++void pt_remove(struct pt_entry *);
++struct pt_entry *pt_lookup(struct bgpd_addr *);
++int pt_prefix_cmp(const struct pt_entry *, const struct pt_entry *);
+
+ /* 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);
+@@ -368,6 +433,7 @@ int path_update(struct rib *, struct r
+ int path_compare(struct rde_aspath *, struct rde_aspath *);
+ struct rde_aspath *path_lookup(struct rde_aspath *, struct rde_peer *);
+ void path_remove(struct rde_aspath *);
++void path_remove_stale(struct rde_aspath *, u_int8_t);
+ void path_destroy(struct rde_aspath *);
+ int path_empty(struct rde_aspath *);
+ struct rde_aspath *path_copy(struct rde_aspath *);
+@@ -375,8 +441,6 @@ struct rde_aspath *path_get(void);
+ void path_put(struct rde_aspath *);
+
+ #define PREFIX_SIZE(x) (((x) + 7) / 8 + 1)
+-int prefix_compare(const struct bgpd_addr *,
+- const struct bgpd_addr *, int);
+ struct prefix *prefix_get(struct rib *, struct rde_peer *,
+ struct bgpd_addr *, int, u_int32_t);
+ int prefix_add(struct rib *, struct rde_aspath *,
+@@ -385,6 +449,7 @@ void prefix_move(struct rde_aspath *,
+ int prefix_remove(struct rib *, struct rde_peer *,
+ struct bgpd_addr *, int, u_int32_t);
+ int prefix_write(u_char *, int, struct bgpd_addr *, u_int8_t);
++int prefix_writebuf(struct ibuf *, struct bgpd_addr *, u_int8_t);
+ struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *,
+ u_int32_t);
+ void prefix_updateall(struct rde_aspath *, enum nexthop_state,
+@@ -395,7 +460,7 @@ void prefix_network_clean(struct rde_p
+ 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 *);
+@@ -403,9 +468,6 @@ void nexthop_update(struct kroute_next
+ struct nexthop *nexthop_get(struct bgpd_addr *);
+ int nexthop_compare(struct nexthop *, struct nexthop *);
+
+-/* rde_decide.c */
+-void prefix_evaluate(struct prefix *, struct rib_entry *);
+-
+ /* rde_update.c */
+ void up_init(struct rde_peer *);
+ void up_down(struct rde_peer *);
+@@ -415,49 +477,14 @@ int up_generate(struct rde_peer *, str
+ 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 *);
+-
+-/* rde_prefix.c */
+-#define pt_empty(pt) ((pt)->refcnt == 0)
+-#define pt_ref(pt) do { \
+- ++(pt)->refcnt; \
+- if ((pt)->refcnt == 0) \
+- fatalx("pt_ref: overflow"); \
+-} while(0)
+-#define pt_unref(pt) do { \
+- if ((pt)->refcnt == 0) \
+- fatalx("pt_unref: underflow"); \
+- --(pt)->refcnt; \
+-} while(0)
+-
+-void pt_init(void);
+-void pt_shutdown(void);
+-void pt_getaddr(struct pt_entry *, struct bgpd_addr *);
+-struct pt_entry *pt_fill(struct bgpd_addr *, int);
+-struct pt_entry *pt_get(struct bgpd_addr *, int);
+-struct pt_entry *pt_add(struct bgpd_addr *, int);
+-void pt_remove(struct pt_entry *);
+-struct pt_entry *pt_lookup(struct bgpd_addr *);
+-int pt_prefix_cmp(const struct pt_entry *, const struct pt_entry *);
+-
+-
+-/* rde_filter.c */
+-enum filter_actions rde_filter(u_int16_t, struct rde_aspath **,
+- struct filter_head *, struct rde_peer *,
+- 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);
+-int rde_filter_equal(struct filter_head *, struct filter_head *,
+- struct rde_peer *, enum directions);
+-
+-/* util.c */
+-u_int32_t aspath_extract(const void *, int);
++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);
+
+ #endif /* __RDE_H__ */
diff --git a/net/openbgpd/files/patch-bgpd_rde_attr.c b/net/openbgpd/files/patch-bgpd_rde_attr.c
new file mode 100644
index 000000000000..2c0192abe5da
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_attr.c
@@ -0,0 +1,562 @@
+Index: bgpd/rde_attr.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_attr.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.7
+diff -u -p -r1.1.1.6 -r1.7
+--- bgpd/rde_attr.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/rde_attr.c 13 Oct 2012 18:36:00 -0000 1.7
+@@ -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.90 2012/04/12 17:27:20 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"
+@@ -36,12 +44,12 @@ attr_write(void *p, u_int16_t p_len, u_i
+ u_char *b = p;
+ u_int16_t tmp, tot_len = 2; /* attribute header (without len) */
+
++ flags &= ~ATTR_DEFMASK;
+ if (data_len > 255) {
+ tot_len += 2 + data_len;
+ flags |= ATTR_EXTLEN;
+ } else {
+ tot_len += 1 + data_len;
+- flags &= ~ATTR_EXTLEN;
+ }
+
+ if (tot_len > p_len)
+@@ -63,26 +71,26 @@ attr_write(void *p, u_int16_t p_len, u_i
+ }
+
+ 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];
+
++ flags &= ~ATTR_DEFMASK;
+ if (data_len > 255) {
+ flags |= ATTR_EXTLEN;
+ hdr[2] = (data_len >> 8) & 0xff;
+ hdr[3] = data_len & 0xff;
+ } else {
+- flags &= ~ATTR_EXTLEN;
+ hdr[2] = data_len & 0xff;
+ }
+
+ 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_in
+ 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 */
+@@ -318,6 +329,7 @@ attr_alloc(u_int8_t flags, u_int8_t type
+ fatal("attr_optadd");
+ rdemem.attr_cnt++;
+
++ flags &= ~ATTR_DEFMASK; /* normalize mask */
+ a->flags = flags;
+ a->hash = hash32_buf(&flags, sizeof(flags), HASHINIT);
+ a->type = type;
+@@ -347,6 +359,7 @@ attr_lookup(u_int8_t flags, u_int8_t typ
+ struct attr *a;
+ u_int32_t hash;
+
++ flags &= ~ATTR_DEFMASK; /* normalize mask */
+ hash = hash32_buf(&flags, sizeof(flags), HASHINIT);
+ hash = hash32_buf(&type, sizeof(type), hash);
+ hash = hash32_buf(&len, sizeof(len), hash);
+@@ -405,6 +418,7 @@ aspath_verify(void *data, u_int16_t len,
+ 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 +433,15 @@ aspath_verify(void *data, u_int16_t len,
+ 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 +453,7 @@ aspath_verify(void *data, u_int16_t len,
+ /* 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
+@@ -762,15 +784,9 @@ aspath_countcopy(struct aspath *aspath,
+ u_int32_t
+ aspath_neighbor(struct aspath *aspath)
+ {
+- /*
+- * Empty aspath is OK -- internal as route.
+- * But what is the neighbor? For now let's return 0.
+- * That should not break anything.
+- */
+-
++ /* Empty aspath is OK -- internal AS route. */
+ if (aspath->len == 0)
+- return (0);
+-
++ return (rde_local_as());
+ return (aspath_extract(aspath->data, 0));
+ }
+
+@@ -910,76 +926,63 @@ aspath_prepend(struct aspath *asp, u_int
+ return (p);
+ }
+
+-/* we need to be able to search more than one as */
+ int
+-aspath_match(struct aspath *a, enum as_spec type, u_int32_t as)
++aspath_lenmatch(struct aspath *a, enum aslen_spec type, u_int aslen)
+ {
+ u_int8_t *seg;
+- int final;
++ 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 == AS_EMPTY) {
+- if (a->len == 0)
++ if (type == ASLEN_MAX) {
++ if (aslen < aspath_count(a->data, a->len))
+ return (1);
+ else
+ return (0);
+ }
+
+- final = 0;
++ /* 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;
+
+- final = (len == seg_size);
+-
+- /* just check the first (leftmost) AS */
+- if (type == AS_PEER) {
+- if (as == aspath_extract(seg, 0))
+- return (1);
+- else
+- return (0);
+- }
+- /* just check the final (rightmost) AS */
+- if (type == AS_SOURCE) {
+- /* not yet in the final segment */
+- if (!final)
+- continue;
+-
+- if (as == aspath_extract(seg, seg_len - 1))
+- return (1);
+- else
+- return (0);
+- }
+-
+- /* AS_TRANSIT or AS_ALL */
+ for (i = 0; i < seg_len; i++) {
+- if (as == aspath_extract(seg, i)) {
+- /*
+- * the source (rightmost) AS is excluded from
+- * AS_TRANSIT matches.
+- */
+- if (final && i == seg_len - 1 &&
+- type == AS_TRANSIT)
+- return (0);
+- return (1);
+- }
++ /* 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(void *data, u_int16_t len, int as, int type)
++community_match(struct rde_aspath *asp, int as, int type)
+ {
+- u_int8_t *p = data;
+- u_int16_t eas, etype;
++ struct attr *a;
++ u_int8_t *p;
++ u_int16_t eas, etype, len;
+
+- len >>= 2; /* divide by four */
++ a = attr_optget(asp, ATTR_COMMUNITIES);
++ if (a == NULL)
++ /* no communities, no match */
++ return (0);
+
+- for (; len > 0; len--) {
++ p = a->data;
++ for (len = a->len / 4; len > 0; len--) {
+ eas = *p++;
+ eas <<= 8;
+ eas |= *p++;
+@@ -1000,7 +1003,6 @@ community_set(struct rde_aspath *asp, in
+ 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 +1019,7 @@ community_set(struct rde_aspath *asp, in
+ p += 4;
+ }
+
+- if (ncommunities++ >= 0x3fff)
++ if (ncommunities++ >= USHRT_MAX / 4)
+ /* overflow */
+ return (0);
+
+@@ -1032,11 +1034,10 @@ community_set(struct rde_aspath *asp, in
+ 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 +1050,7 @@ community_delete(struct rde_aspath *asp,
+ 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 +1101,250 @@ community_delete(struct rde_aspath *asp,
+ }
+
+ 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/net/openbgpd/files/patch-bgpd_rde_decide.c b/net/openbgpd/files/patch-bgpd_rde_decide.c
new file mode 100644
index 000000000000..c9d27af5dbef
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_decide.c
@@ -0,0 +1,133 @@
+Index: bgpd/rde_decide.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_decide.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.4
+diff -u -p -r1.1.1.6 -r1.4
+--- bgpd/rde_decide.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/rde_decide.c 13 Oct 2012 18:36:00 -0000 1.4
+@@ -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.61 2012/04/12 17:31:05 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
+@@ -109,6 +109,9 @@ int
+ prefix_cmp(struct prefix *p1, struct prefix *p2)
+ {
+ struct rde_aspath *asp1, *asp2;
++ struct attr *a;
++ u_int32_t p1id, p2id;
++ int p1cnt, p2cnt;
+
+ if (p1 == NULL)
+ return (-1);
+@@ -118,6 +121,12 @@ prefix_cmp(struct prefix *p1, struct pre
+ 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);
+@@ -130,7 +139,7 @@ prefix_cmp(struct prefix *p1, struct pre
+ if (asp1->nexthop != NULL && asp1->nexthop->state != NEXTHOP_REACH)
+ return (-1);
+
+- /* 2. preference of prefix, bigger is better */
++ /* 2. local preference of prefix, bigger is better */
+ if ((asp1->lpref - asp2->lpref) != 0)
+ return (asp1->lpref - asp2->lpref);
+
+@@ -154,10 +163,10 @@ prefix_cmp(struct prefix *p1, struct pre
+ * It is absolutely important that the ebgp value in peer_config.ebgp
+ * is bigger than all other ones (IBGP, confederations)
+ */
+- if ((asp1->peer->conf.ebgp - asp2->peer->conf.ebgp) != 0) {
+- if (asp1->peer->conf.ebgp == 1) /* p1 is EBGP other is lower */
++ if (asp1->peer->conf.ebgp != asp2->peer->conf.ebgp) {
++ if (asp1->peer->conf.ebgp) /* p1 is EBGP other is lower */
+ return 1;
+- else if (asp2->peer->conf.ebgp == 1) /* p2 is EBGP */
++ else if (asp2->peer->conf.ebgp) /* p2 is EBGP */
+ return -1;
+ }
+
+@@ -181,13 +190,30 @@ prefix_cmp(struct prefix *p1, struct pre
+ if ((p2->lastchange - p1->lastchange) != 0)
+ return (p2->lastchange - p1->lastchange);
+
+- /* 10. lowest BGP Id wins */
+- if ((p2->aspath->peer->remote_bgpid -
+- p1->aspath->peer->remote_bgpid) != 0)
+- return (p2->aspath->peer->remote_bgpid -
+- p1->aspath->peer->remote_bgpid);
++ /* 10. lowest BGP Id wins, use ORIGINATOR_ID if present */
++ if ((a = attr_optget(asp1, ATTR_ORIGINATOR_ID)) != NULL) {
++ memcpy(&p1id, a->data, sizeof(p1id));
++ p1id = ntohl(p1id);
++ } else
++ p1id = asp1->peer->remote_bgpid;
++ if ((a = attr_optget(asp2, ATTR_ORIGINATOR_ID)) != NULL) {
++ memcpy(&p2id, a->data, sizeof(p2id));
++ p2id = ntohl(p2id);
++ } else
++ p2id = asp2->peer->remote_bgpid;
++ if ((p2id - p1id) != 0)
++ return (p2id - p1id);
++
++ /* 11. compare CLUSTER_LIST length, shorter is better */
++ p1cnt = p2cnt = 0;
++ if ((a = attr_optget(asp1, ATTR_CLUSTER_LIST)) != NULL)
++ p1cnt = a->len / sizeof(u_int32_t);
++ if ((a = attr_optget(asp2, ATTR_CLUSTER_LIST)) != NULL)
++ p2cnt = a->len / sizeof(u_int32_t);
++ if ((p2cnt - p1cnt) != 0)
++ return (p2cnt - p1cnt);
+
+- /* 11. lowest peer address wins (IPv4 is better than IPv6) */
++ /* 12. lowest peer address wins (IPv4 is better than IPv6) */
+ if (memcmp(&p1->aspath->peer->remote_addr,
+ &p2->aspath->peer->remote_addr,
+ sizeof(p1->aspath->peer->remote_addr)) != 0)
+@@ -195,7 +221,7 @@ prefix_cmp(struct prefix *p1, struct pre
+ &p2->aspath->peer->remote_addr,
+ sizeof(p1->aspath->peer->remote_addr)));
+
+- /* 12. for announced prefixes prefer dynamic routes */
++ /* 13. for announced prefixes prefer dynamic routes */
+ if ((asp1->flags & F_ANN_DYNAMIC) != (asp2->flags & F_ANN_DYNAMIC)) {
+ if (asp1->flags & F_ANN_DYNAMIC)
+ return (1);
+@@ -204,7 +230,7 @@ prefix_cmp(struct prefix *p1, struct pre
+ }
+
+ fatalx("Uh, oh a politician in the decision process");
+- /* NOTREACHED */
++ return(0); /* NOTREACHED */
+ }
+
+ /*
+@@ -245,7 +271,7 @@ prefix_evaluate(struct prefix *p, struct
+ }
+
+ 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 +289,7 @@ prefix_evaluate(struct prefix *p, struct
+ */
+ 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/net/openbgpd/files/patch-bgpd_rde_filter.c b/net/openbgpd/files/patch-bgpd_rde_filter.c
new file mode 100644
index 000000000000..c17d9fc5fdbf
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_filter.c
@@ -0,0 +1,297 @@
+Index: bgpd/rde_filter.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_filter.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.9
+diff -u -p -r1.1.1.7 -r1.9
+--- bgpd/rde_filter.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/rde_filter.c 8 Dec 2012 20:17:59 -0000 1.9
+@@ -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.67 2011/09/20 21:19:06 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
+@@ -26,7 +26,7 @@
+ #include "rde.h"
+
+ int rde_filter_match(struct filter_rule *, struct rde_aspath *,
+- struct bgpd_addr *, u_int8_t, struct rde_peer *);
++ struct bgpd_addr *, u_int8_t, struct rde_peer *, struct rde_peer *);
+ int filterset_equal(struct filter_set_head *, struct filter_set_head *);
+
+ enum filter_actions
+@@ -40,6 +40,13 @@ rde_filter(u_int16_t ribid, struct rde_a
+ 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;
+@@ -51,7 +58,7 @@ rde_filter(u_int16_t ribid, struct rde_a
+ if (f->peer.peerid != 0 &&
+ f->peer.peerid != peer->conf.id)
+ continue;
+- if (rde_filter_match(f, asp, prefix, prefixlen, peer)) {
++ if (rde_filter_match(f, asp, prefix, prefixlen, peer, from)) {
+ if (asp != NULL && new != NULL) {
+ /* asp may get modified so create a copy */
+ if (*new == NULL) {
+@@ -59,7 +66,7 @@ rde_filter(u_int16_t ribid, struct rde_a
+ /* ... 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_a
+
+ 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, st
+ 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,19 +250,42 @@ rde_apply_set(struct rde_aspath *asp, st
+ 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;
+ }
+ }
+ }
+
+ int
+ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp,
+- struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer)
++ struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer,
++ struct rde_peer *from)
+ {
+- int as, type;
++ u_int32_t pas;
++ int cas, type;
+
+- 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.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->data, asp->aspath->len,
++ f->match.as.type, pas) == 0)
++ return (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 +293,10 @@ rde_filter_match(struct filter_rule *f,
+ 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 +311,17 @@ rde_filter_match(struct filter_rule *f,
+ 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 +357,7 @@ rde_filter_match(struct filter_rule *f,
+ } 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);
+
+@@ -350,25 +385,40 @@ rde_filter_match(struct filter_rule *f,
+ }
+ /* NOTREACHED */
+ }
++ if (f->match.nexthop.flags != 0) {
++ struct bgpd_addr *nexthop, *cmpaddr;
++ if (asp->nexthop == NULL)
++ /* no nexthop, skip */
++ return (0);
++ nexthop = &asp->nexthop->exit_nexthop;
++ if (f->match.nexthop.flags == FILTER_NEXTHOP_ADDR)
++ cmpaddr = &f->match.nexthop.addr;
++ else
++ cmpaddr = &from->remote_addr;
++ if (cmpaddr->aid != nexthop->aid)
++ /* don't use IPv4 rules for IPv6 and vice versa */
++ return (0);
++
++ switch (cmpaddr->aid) {
++ case AID_INET:
++ if (cmpaddr->v4.s_addr != nexthop->v4.s_addr)
++ return (0);
++ break;
++ case AID_INET6:
++ if (memcmp(&cmpaddr->v6, &nexthop->v6,
++ sizeof(struct in6_addr)))
++ return (0);
++ break;
++ default:
++ fatalx("King Bula lost in address space");
++ }
++ }
+
+ /* matched somewhen or is anymatch rule */
+ return (1);
+ }
+
+ 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 +526,12 @@ filterset_cmp(struct filter_set *a, stru
+ 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 +539,29 @@ filterset_cmp(struct filter_set *a, stru
+ * 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 +646,19 @@ filterset_equal(struct filter_set_head *
+ 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 +701,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/net/openbgpd/files/patch-bgpd_rde_prefix.c b/net/openbgpd/files/patch-bgpd_rde_prefix.c
new file mode 100644
index 000000000000..daea5cf62272
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_prefix.c
@@ -0,0 +1,301 @@
+Index: bgpd/rde_prefix.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_prefix.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.6
+diff -u -p -r1.1.1.6 -r1.6
+--- bgpd/rde_prefix.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/rde_prefix.c 13 Oct 2012 18:36:00 -0000 1.6
+@@ -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.31 2010/01/13 06:02:37 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
+ 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");
+- }
+
+- if (RB_INSERT(pt_tree, &pttable, p) != NULL) {
+- log_warnx("pt_add: insert failed");
+- return (NULL);
+- }
++ p = pt_fill(prefix, prefixlen);
++ p = pt_alloc(p);
++
++ 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,
+ 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,
+ 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/net/openbgpd/files/patch-bgpd_rde_rib.c b/net/openbgpd/files/patch-bgpd_rde_rib.c
new file mode 100644
index 000000000000..2fe781c69c4b
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_rib.c
@@ -0,0 +1,513 @@
+Index: bgpd/rde_rib.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_rib.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.8
+diff -u -p -r1.1.1.7 -r1.8
+--- bgpd/rde_rib.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/rde_rib.c 13 Oct 2012 18:36:00 -0000 1.8
+@@ -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.133 2012/07/01 11:55:13 sthen 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 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 fl
+ 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_
+ 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_add
+
+ 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)
+ 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 */
+@@ -502,6 +509,36 @@ path_remove(struct rde_aspath *asp)
+ }
+ }
+
++/* remove all stale routes or if staletime is 0 remove all routes for
++ a specified AID. */
++void
++path_remove_stale(struct rde_aspath *asp, u_int8_t aid)
++{
++ struct prefix *p, *np;
++ time_t staletime;
++
++ staletime = asp->peer->staletime[aid];
++ for (p = LIST_FIRST(&asp->prefix_h); p != NULL; p = np) {
++ np = LIST_NEXT(p, path_l);
++ if (p->prefix->aid != aid)
++ continue;
++
++ if (staletime && p->lastchange > staletime)
++ continue;
++
++ if (asp->pftableid) {
++ struct bgpd_addr addr;
++
++ pt_getaddr(p->prefix, &addr);
++ /* Commit is done in peer_flush() */
++ rde_send_pftable(p->aspath->pftableid, &addr,
++ p->prefix->prefixlen, 1);
++ }
++ prefix_destroy(p);
++ }
++}
++
++
+ /* this function is only called by prefix_remove and path_remove */
+ void
+ path_destroy(struct rde_aspath *asp)
+@@ -624,48 +661,6 @@ static void prefix_link(struct prefix
+ struct rde_aspath *);
+ static void prefix_unlink(struct prefix *);
+
+-int
+-prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
+- int prefixlen)
+-{
+- in_addr_t mask, aa, ba;
+- int i;
+- u_int8_t m;
+-
+- if (a->af != b->af)
+- return (a->af - b->af);
+-
+- switch (a->af) {
+- case AF_INET:
+- if (prefixlen > 32)
+- fatalx("prefix_cmp: bad IPv4 prefixlen");
+- mask = htonl(prefixlen2mask(prefixlen));
+- aa = ntohl(a->v4.s_addr & mask);
+- ba = ntohl(b->v4.s_addr & mask);
+- if (aa != ba)
+- return (aa - ba);
+- return (0);
+- case AF_INET6:
+- if (prefixlen > 128)
+- fatalx("prefix_cmp: bad IPv6 prefixlen");
+- for (i = 0; i < prefixlen / 8; i++)
+- if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
+- return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
+- i = prefixlen % 8;
+- if (i) {
+- m = 0xff00 >> i;
+- if ((a->v6.s6_addr[prefixlen / 8] & m) !=
+- (b->v6.s6_addr[prefixlen / 8] & m))
+- return ((a->v6.s6_addr[prefixlen / 8] & m) -
+- (b->v6.s6_addr[prefixlen / 8] & m));
+- }
+- return (0);
+- default:
+- fatalx("prefix_cmp: unknown af");
+- }
+- return (-1);
+-}
+-
+ /*
+ * search for specified prefix of a peer. Returns NULL if not found.
+ */
+@@ -806,16 +801,58 @@ prefix_write(u_char *buf, int len, struc
+ {
+ int totlen;
+
+- if (prefix->af != AF_INET && prefix->af != AF_INET6)
++ switch (prefix->aid) {
++ case AID_INET:
++ case AID_INET6:
++ totlen = PREFIX_SIZE(plen);
++
++ 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);
++ }
++}
+
+- totlen = PREFIX_SIZE(plen);
++int
++prefix_writebuf(struct ibuf *buf, struct bgpd_addr *prefix, u_int8_t plen)
++{
++ int totlen;
++ void *bptr;
+
+- if (totlen > len)
++ switch (prefix->aid) {
++ case AID_INET:
++ case AID_INET6:
++ totlen = PREFIX_SIZE(plen);
++ break;
++ case AID_VPN_IPv4:
++ totlen = PREFIX_SIZE(plen) + sizeof(prefix->vpn4.rd) +
++ prefix->vpn4.labellen;
++ default:
+ return (-1);
+- *buf++ = plen;
+- memcpy(buf, &prefix->ba, totlen - 1);
+- return (totlen);
++ }
++
++ if ((bptr = ibuf_reserve(buf, totlen)) == NULL)
++ return (-1);
++ if (prefix_write(bptr, totlen, prefix, plen) == -1)
++ return (-1);
++ return (0);
+ }
+
+ /*
+@@ -861,7 +898,7 @@ prefix_updateall(struct rde_aspath *asp,
+ */
+ 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;
+ }
+
+@@ -871,7 +908,7 @@ prefix_updateall(struct rde_aspath *asp,
+ * If the prefix is the active one remove it first,
+ * this has to be done because we can not detect when
+ * the active prefix changes its state. In this case
+- * we know that this is a withdrawl and so the second
++ * we know that this is a withdrawal and so the second
+ * prefix_evaluate() will generate no update because
+ * the nexthop is unreachable or ineligible.
+ */
+@@ -885,16 +922,12 @@ prefix_updateall(struct rde_aspath *asp,
+ 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,21 +940,16 @@ prefix_network_clean(struct rde_peer *pe
+ {
+ 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);
+- if ((asp->flags & F_ANN_DYNAMIC) == flags)
++ if ((asp->flags & F_ANN_DYNAMIC) != flags)
+ continue;
+ 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 +982,11 @@ prefix_link(struct prefix *pref, struct
+ 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 +994,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 +1003,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 +1100,6 @@ nexthop_update(struct kroute_nexthop *ms
+ return;
+ }
+
+- if (nexthop_delete(nh))
+- /* nexthop no longer used */
+- return;
+-
+ oldstate = nh->state;
+ if (msg->valid)
+ nh->state = NEXTHOP_REACH;
+@@ -1088,21 +1114,13 @@ nexthop_update(struct kroute_nexthop *ms
+ 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,35 +1136,38 @@ nexthop_update(struct kroute_nexthop *ms
+
+ 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;
+
+- if (type == ACTION_SET_NEXTHOP_REJECT) {
+- asp->flags |= F_NEXTHOP_REJECT;
++ if (type == ACTION_SET_NEXTHOP && aid != nexthop->aid)
+ return;
+- }
+- if (type == ACTION_SET_NEXTHOP_BLACKHOLE) {
++
++ asp->flags &= ~F_NEXTHOP_MASK;
++ switch (type) {
++ case ACTION_SET_NEXTHOP_REJECT:
++ asp->flags |= F_NEXTHOP_REJECT;
++ break;
++ case ACTION_SET_NEXTHOP_BLACKHOLE:
+ asp->flags |= F_NEXTHOP_BLACKHOLE;
+- return;
+- }
+- if (type == ACTION_SET_NEXTHOP_NOMODIFY) {
++ break;
++ case ACTION_SET_NEXTHOP_NOMODIFY:
+ asp->flags |= F_NEXTHOP_NOMODIFY;
+- return;
+- }
+- if (type == ACTION_SET_NEXTHOP_SELF) {
++ break;
++ case ACTION_SET_NEXTHOP_SELF:
+ asp->flags |= F_NEXTHOP_SELF;
+- return;
++ break;
++ case ACTION_SET_NEXTHOP:
++ nh = nexthop_get(nexthop);
++ if (asp->flags & F_ATTR_LINKED)
++ nexthop_unlink(asp);
++ asp->nexthop = nh;
++ if (asp->flags & F_ATTR_LINKED)
++ nexthop_link(asp);
++ break;
++ default:
++ break;
+ }
+- if (af != nexthop->af)
+- return;
+-
+- nh = nexthop_get(nexthop);
+- if (asp->flags & F_ATTR_LINKED)
+- nexthop_unlink(asp);
+- asp->nexthop = nh;
+- if (asp->flags & F_ATTR_LINKED)
+- nexthop_link(asp);
+ }
+
+ void
+@@ -1233,17 +1254,17 @@ nexthop_compare(struct nexthop *na, stru
+ 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 +1290,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/net/openbgpd/files/patch-bgpd_rde_update.c b/net/openbgpd/files/patch-bgpd_rde_update.c
new file mode 100644
index 000000000000..ccd9601dcd3d
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_rde_update.c
@@ -0,0 +1,644 @@
+Index: bgpd/rde_update.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde_update.c,v
+retrieving revision 1.1.1.7
+retrieving revision 1.8
+diff -u -p -r1.1.1.7 -r1.8
+--- bgpd/rde_update.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/rde_update.c 13 Oct 2012 18:36:00 -0000 1.8
+@@ -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, en
+ 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, st
+ 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, s
+ {
+ 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, s
+ 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 upd
+ 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,23 +291,16 @@ up_test_update(struct rde_peer *peer, st
+ /* 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) {
++ if (!p->aspath->peer->conf.ebgp && !peer->conf.ebgp) {
+ /*
+ * route reflector redistribution rules:
+ * 1. if announce is set -> announce
+@@ -325,13 +339,13 @@ up_test_update(struct rde_peer *peer, st
+ }
+
+ /* 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, struc
+ 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 *
+ /* 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 *
+
+ /* 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 *
+ 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, st
+
+ 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 *pe
+ 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 *pe
+
+ 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,
+ 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,
+ /*
+ * 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)) {
++ if (a->flags & F_ATTR_MED && (!peer->conf.ebgp ||
++ 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)
+@@ -669,7 +791,7 @@ up_generate_attr(struct rde_peer *peer,
+ wlen += r; len -= r;
+ }
+
+- if (peer->conf.ebgp == 0) {
++ if (!peer->conf.ebgp) {
+ /* local preference, only valid for ibgp */
+ tmp32 = htonl(a->lpref);
+ if ((r = attr_write(up_attr_buf + wlen, len, ATTR_WELL_KNOWN,
+@@ -704,7 +826,7 @@ up_generate_attr(struct rde_peer *peer,
+ u_int16_t tas;
+
+ if ((!(oa->flags & ATTR_TRANSITIVE)) &&
+- peer->conf.ebgp != 0) {
++ peer->conf.ebgp) {
+ r = 0;
+ break;
+ }
+@@ -730,7 +852,7 @@ up_generate_attr(struct rde_peer *peer,
+ case ATTR_ORIGINATOR_ID:
+ case ATTR_CLUSTER_LIST:
+ if ((!(oa->flags & ATTR_TRANSITIVE)) &&
+- peer->conf.ebgp != 0) {
++ peer->conf.ebgp) {
+ r = 0;
+ break;
+ }
+@@ -791,7 +913,7 @@ up_generate_attr(struct rde_peer *peer,
+
+ /* 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, str
+ {
+ 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, str
+ 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, s
+ * 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, s
+ 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, s
+ }
+
+ 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_
+ 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_
+
+ /* 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_
+ 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
+ */
+ 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
+ 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 = datalen + 4;
+- return (buf + wpos);
++ return (wpos);
+ }
+-
diff --git a/net/openbgpd/files/patch-bgpd_session.c b/net/openbgpd/files/patch-bgpd_session.c
new file mode 100644
index 000000000000..66c05a92aec0
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_session.c
@@ -0,0 +1,2075 @@
+Index: bgpd/session.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/session.c,v
+retrieving revision 1.1.1.8
+retrieving revision 1.13
+diff -u -p -r1.1.1.8 -r1.13
+--- bgpd/session.c 14 Feb 2010 20:19:57 -0000 1.1.1.8
++++ bgpd/session.c 8 Dec 2012 20:17:59 -0000 1.13
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: session.c,v 1.293 2009/06/07 05:56:24 eric Exp $ */
++/* $OpenBSD: session.c,v 1.325 2012/09/18 09:45:50 claudio Exp $ */
+
+ /*
+ * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
+@@ -21,18 +21,21 @@
+
+ #include <sys/mman.h>
+ #include <sys/socket.h>
++#include <sys/time.h>
++#include <sys/resource.h>
+ #include <sys/un.h>
++#include <sys/queue.h>
+ #include <net/if_types.h>
+ #include <netinet/in.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ #include <netinet/tcp.h>
++#include <netinet/tcp_var.h>
+ #include <arpa/inet.h>
+
+ #include <err.h>
+ #include <errno.h>
+ #include <fcntl.h>
+-#include <limits.h>
+ #include <poll.h>
+ #include <pwd.h>
+ #include <signal.h>
+@@ -50,7 +53,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 +73,9 @@ 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);
++int session_capa_add_gr(struct peer *, 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,30 +83,34 @@ 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_graceful_restart(struct peer *);
++int session_graceful_is_restarting(struct peer *);
++int session_graceful_stop(struct peer *);
+ int session_dispatch_msg(struct pollfd *, struct peer *);
++int session_process_msg(struct peer *);
+ int parse_header(struct peer *, u_char *, u_int16_t *, u_int8_t *);
+ int parse_open(struct peer *);
+ 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;
+@@ -106,6 +118,7 @@ struct imsgbuf *ibuf_rde_ctl;
+ struct imsgbuf *ibuf_main;
+
+ struct mrt_head mrthead;
++time_t pauseaccept;
+
+ void
+ session_sighdlr(int sig)
+@@ -125,6 +138,22 @@ setup_listeners(u_int *la_cnt)
+ int opt;
+ struct listen_addr *la;
+ u_int cnt = 0;
++#if defined(__FreeBSD__)
++ int s;
++
++ /* Check if TCP_MD5SIG is supported. */
++ s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
++ if (s < 0)
++ fatal("socket open for TCP_MD5SIG check");
++ opt = TF_SIGNATURE;
++ if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, &opt, sizeof(opt)) == -1) {
++ if (errno == ENOPROTOOPT || errno == EINVAL)
++ sysdep.no_md5sig = 1;
++ else
++ fatal("setsockopt TCP_MD5SIG");
++ }
++ close(s);
++#endif /* defined(__FreeBSD__) */
+
+ TAILQ_FOREACH(la, conf->listen_addrs, entry) {
+ la->reconf = RECONF_NONE;
+@@ -140,6 +169,7 @@ setup_listeners(u_int *la_cnt)
+ }
+
+ opt = 1;
++#if !defined(__FreeBSD__)
+ if (setsockopt(la->fd, IPPROTO_TCP, TCP_MD5SIG,
+ &opt, sizeof(opt)) == -1) {
+ if (errno == ENOPROTOOPT) { /* system w/o md5sig */
+@@ -148,6 +178,7 @@ setup_listeners(u_int *la_cnt)
+ } else
+ fatal("setsockopt TCP_MD5SIG");
+ }
++#endif /* !defined(__FreeBSD__) */
+
+ /* set ttl to 255 so that ttl-security works */
+ if (la->sa.ss_family == AF_INET && setsockopt(la->fd,
+@@ -175,12 +206,10 @@ 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;
++ 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 +218,13 @@ session_main(struct bgpd_config *config,
+ 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 +234,6 @@ session_main(struct bgpd_config *config,
+ 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);
+
+@@ -228,29 +244,25 @@ session_main(struct bgpd_config *config,
+
+ setproctitle("session engine");
+ bgpd_process = PROC_SE;
+-
+- if (pfkey_init(&sysdep) == -1)
+- fatalx("pfkey setup failed");
++ 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 +270,21 @@ session_main(struct bgpd_config *config,
+ 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 +304,9 @@ session_main(struct bgpd_config *config,
+
+ /* 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 +314,7 @@ session_main(struct bgpd_config *config,
+ 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 +343,17 @@ session_main(struct bgpd_config *config,
+ }
+
+ 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,18 +399,31 @@ session_main(struct bgpd_config *config,
+ 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;
+-
++ if (pauseaccept == 0) {
++ pfd[PFD_SOCK_CTL].fd = csock;
++ pfd[PFD_SOCK_CTL].events = POLLIN;
++ pfd[PFD_SOCK_RCTL].fd = rcsock;
++ pfd[PFD_SOCK_RCTL].events = POLLIN;
++ } else {
++ pfd[PFD_SOCK_CTL].fd = -1;
++ pfd[PFD_SOCK_RCTL].fd = -1;
++ }
++ 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;
+- pfd[i].events = POLLIN;
++ if (pauseaccept == 0) {
++ pfd[i].fd = la->fd;
++ pfd[i].events = POLLIN;
++ } else
++ pfd[i].fd = -1;
+ i++;
+ }
+ idx_listeners = i;
+@@ -450,6 +468,10 @@ session_main(struct bgpd_config *config,
+ p->state == STATE_ESTABLISHED)
+ session_demote(p, -1);
+ break;
++ case Timer_RestartTimeout:
++ timer_stop(p, Timer_RestartTimeout);
++ session_graceful_stop(p);
++ break;
+ default:
+ fatalx("King Bula lost in time");
+ }
+@@ -462,6 +484,9 @@ session_main(struct bgpd_config *config,
+ events = POLLIN;
+ if (p->wbuf.queued > 0 || p->state == STATE_CONNECT)
+ events |= POLLOUT;
++ /* is there still work to do? */
++ if (p->rbuf && p->rbuf->wpos)
++ timeout = 0;
+
+ /* poll events */
+ if (p->fd != -1 && events != 0) {
+@@ -492,12 +517,21 @@ session_main(struct bgpd_config *config,
+ i++;
+ }
+
++ if (pauseaccept && timeout > 1)
++ timeout = 1;
+ if (timeout < 0)
+ timeout = 0;
+ if ((nfds = poll(pfd, i, timeout * 1000)) == -1)
+ if (errno != EINTR)
+ fatal("poll error");
+
++ /*
++ * If we previously saw fd exhaustion, we stop accept()
++ * for 1 second to throttle the accept() loop.
++ */
++ if (pauseaccept && getmonotime() > pauseaccept + 1)
++ pauseaccept = 0;
++
+ if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & POLLOUT)
+ if (msgbuf_write(&ibuf_main->w) < 0)
+ fatal("pipe write error");
+@@ -534,6 +568,14 @@ session_main(struct bgpd_config *config,
+ 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) {
+@@ -545,6 +587,10 @@ session_main(struct bgpd_config *config,
+ nfds -= session_dispatch_msg(&pfd[j],
+ peer_l[j - idx_listeners]);
+
++ for (p = peers; p != NULL; p = p->next)
++ if (p->rbuf && p->rbuf->wpos)
++ session_process_msg(p);
++
+ for (; nfds > 0 && j < idx_mrts; j++)
+ if (pfd[j].revents & POLLOUT) {
+ nfds--;
+@@ -557,7 +603,7 @@ session_main(struct bgpd_config *config,
+
+ 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 +689,9 @@ bgp_fsm(struct peer *peer, enum session_
+ 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 +791,6 @@ bgp_fsm(struct peer *peer, enum session_
+ /* 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 +824,8 @@ bgp_fsm(struct peer *peer, enum session_
+ 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 +836,6 @@ bgp_fsm(struct peer *peer, enum session_
+ /* 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 +859,8 @@ bgp_fsm(struct peer *peer, enum session_
+ 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 +871,6 @@ bgp_fsm(struct peer *peer, enum session_
+ /* 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 +900,8 @@ bgp_fsm(struct peer *peer, enum session_
+ 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;
+ }
+@@ -885,9 +930,10 @@ start_timer_keepalive(struct peer *peer)
+ void
+ session_close_connection(struct peer *peer)
+ {
+- if (peer->fd != -1)
++ if (peer->fd != -1) {
+ close(peer->fd);
+-
++ pauseaccept = 0;
++ }
+ peer->fd = peer->wbuf.fd = -1;
+ }
+
+@@ -923,20 +969,31 @@ change_state(struct peer *peer, enum ses
+ 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);
+ free(peer->rbuf);
+ peer->rbuf = NULL;
+ bzero(&peer->capa.peer, sizeof(peer->capa.peer));
+- if (peer->state == STATE_ESTABLISHED)
+- session_down(peer);
++
+ if (event != EVNT_STOP) {
+ timer_set(peer, Timer_IdleHold, peer->IdleHoldTime);
+ if (event != EVNT_NONE &&
+ peer->IdleHoldTime < MAX_IDLE_HOLD/2)
+ peer->IdleHoldTime *= 2;
+ }
++ if (peer->state == STATE_ESTABLISHED) {
++ if (peer->capa.neg.grestart.restart == 2 &&
++ (event == EVNT_CON_CLOSED ||
++ event == EVNT_CON_FATAL)) {
++ /* don't punish graceful restart */
++ timer_set(peer, Timer_IdleHold, 0);
++ peer->IdleHoldTime /= 2;
++ session_graceful_restart(peer);
++ } else
++ session_down(peer);
++ }
+ if (peer->state == STATE_NONE ||
+ peer->state == STATE_ESTABLISHED) {
+ /* initialize capability negotiation structures */
+@@ -947,6 +1004,20 @@ change_state(struct peer *peer, enum ses
+ }
+ break;
+ case STATE_CONNECT:
++ if (peer->state == STATE_ESTABLISHED &&
++ peer->capa.neg.grestart.restart == 2) {
++ /* do the graceful restart dance */
++ session_graceful_restart(peer);
++ peer->holdtime = INTERVAL_HOLD_INITIAL;
++ 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);
++ bzero(&peer->capa.peer, sizeof(peer->capa.peer));
++ }
+ break;
+ case STATE_ACTIVE:
+ break;
+@@ -990,7 +1061,10 @@ session_accept(int listenfd)
+ len = sizeof(cliaddr);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&cliaddr, &len)) == -1) {
+- if (errno == EWOULDBLOCK || errno == EINTR)
++ if (errno == ENFILE || errno == EMFILE) {
++ pauseaccept = getmonotime();
++ return;
++ } else if (errno == EWOULDBLOCK || errno == EINTR)
+ return;
+ else
+ log_warn("accept");
+@@ -1017,6 +1091,7 @@ session_accept(int listenfd)
+ }
+ }
+
++open:
+ if (p->conf.auth.method != AUTH_NONE && sysdep.no_pfkey) {
+ log_peer_warnx(&p->conf,
+ "ipsec or md5sig configured but not available");
+@@ -1049,6 +1124,13 @@ session_accept(int listenfd)
+ }
+ session_socket_blockmode(connfd, BM_NONBLOCK);
+ bgp_fsm(p, EVNT_CON_OPEN);
++ return;
++ } else if (p != NULL && p->state == STATE_ESTABLISHED &&
++ p->capa.neg.grestart.restart == 2) {
++ /* first do the graceful restart dance */
++ change_state(p, STATE_CONNECT, EVNT_CON_CLOSED);
++ /* then do part of the open dance */
++ goto open;
+ } else {
+ log_conn_attempt(p, (struct sockaddr *)&cliaddr);
+ close(connfd);
+@@ -1069,7 +1151,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 +1182,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 +1220,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,24 +1274,18 @@ 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 */
+ bsize = 65535;
+- while (setsockopt(p->fd, SOL_SOCKET, SO_RCVBUF, &bsize,
+- sizeof(bsize)) == -1)
++ while (bsize > 8192 &&
++ setsockopt(p->fd, SOL_SOCKET, SO_RCVBUF, &bsize,
++ sizeof(bsize)) == -1 && errno != EINVAL)
+ bsize /= 2;
+ bsize = 65535;
+- while (setsockopt(p->fd, SOL_SOCKET, SO_SNDBUF, &bsize,
+- sizeof(bsize)) == -1)
++ while (bsize > 8192 &&
++ setsockopt(p->fd, SOL_SOCKET, SO_SNDBUF, &bsize,
++ sizeof(bsize)) == -1 && errno != EINVAL)
+ bsize /= 2;
+ }
+
+@@ -1244,40 +1327,56 @@ session_tcp_established(struct peer *pee
+ 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)
+-{
+- 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;
++session_capa_add(struct ibuf *opb, u_int8_t capa_code, u_int8_t capa_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 += ibuf_add(buf, &afi, sizeof(afi));
++ errs += ibuf_add(buf, &pad, sizeof(pad));
++ errs += ibuf_add(buf, &safi, sizeof(safi));
++
++ return (errs);
++}
++
++int
++session_capa_add_gr(struct peer *p, struct ibuf *b, u_int8_t aid)
++{
++ u_int errs = 0;
++ u_int16_t afi;
++ u_int8_t flags, safi;
++
++ if (aid2afi(aid, &afi, &safi)) {
++ log_warn("session_capa_add_gr: bad AID");
++ return (1);
++ }
++ if (p->capa.neg.grestart.flags[aid] & CAPA_GR_RESTARTING)
++ flags = CAPA_GR_F_FLAG;
++ else
++ flags = 0;
++
+ 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(b, &afi, sizeof(afi));
++ errs += ibuf_add(b, &safi, sizeof(safi));
++ errs += ibuf_add(b, &flags, sizeof(flags));
+
+ return (errs);
+ }
+@@ -1287,23 +1386,22 @@ session_newmsg(enum msg_type msgtype, u_
+ {
+ 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));
+-
+- if (errs > 0 ||
+- (msg = calloc(1, sizeof(*msg))) == NULL) {
+- buf_free(buf);
++ 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 || (msg = calloc(1, sizeof(*msg))) == NULL) {
++ ibuf_free(buf);
+ return (NULL);
+ }
+
+@@ -1329,7 +1427,7 @@ session_sendmsg(struct bgp_msg *msg, str
+ 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 +1436,70 @@ 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;
++ int mpcapa = 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);
++ mpcapa++;
++ }
+
+ /* 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);
++ /* graceful restart and End-of-RIB marker, RFC 4724 */
++ if (p->capa.ann.grestart.restart) {
++ int rst = 0;
++ u_int16_t hdr;
++ u_int8_t grlen;
++
++ if (mpcapa) {
++ grlen = 2 + 4 * mpcapa;
++ for (i = 0; i < AID_MAX; i++) {
++ if (p->capa.neg.grestart.flags[i] &
++ CAPA_GR_RESTARTING)
++ rst++;
++ }
++ } else { /* AID_INET */
++ grlen = 2 + 4;
++ if (p->capa.neg.grestart.flags[AID_INET] &
++ CAPA_GR_RESTARTING)
++ rst++;
++ }
++
++ hdr = conf->holdtime; /* default timeout */
++ /* if client does graceful restart don't set R flag */
++ if (!rst)
++ hdr |= CAPA_GR_R_FLAG;
++ hdr = htons(hdr);
++
++ errs += session_capa_add(opb, CAPA_RESTART, grlen);
++ errs += ibuf_add(opb, &hdr, sizeof(hdr));
++
++ if (mpcapa) {
++ for (i = 0; i < AID_MAX; i++) {
++ if (p->capa.ann.mp[i]) {
++ errs += session_capa_add_gr(p, opb, i);
++ }
++ }
++ } else { /* AID_INET */
++ errs += session_capa_add_gr(p, opb, AID_INET);
++ }
+ }
+
+ /* 4-bytes AS numbers, draft-ietf-idr-as4bytes-13 */
+@@ -1379,13 +1507,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 +1531,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));
+-
+- if (optparamlen)
+- errs += buf_add(buf->buf, opb->buf, 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) {
++ 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 +1596,8 @@ session_update(u_int32_t peerid, void *d
+ 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 +1617,27 @@ session_notification(struct peer *p, u_i
+ 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 +1656,29 @@ session_notification(struct peer *p, u_i
+ 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 +1686,12 @@ session_rrefresh(struct peer *p, u_int16
+ }
+
+ 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;
+@@ -1565,13 +1706,74 @@ session_rrefresh(struct peer *p, u_int16
+ }
+
+ int
++session_graceful_restart(struct peer *p)
++{
++ u_int8_t i;
++
++ timer_set(p, Timer_RestartTimeout, p->capa.neg.grestart.timeout);
++
++ for (i = 0; i < AID_MAX; i++) {
++ if (p->capa.neg.grestart.flags[i] & CAPA_GR_PRESENT) {
++ if (imsg_compose(ibuf_rde, IMSG_SESSION_STALE,
++ p->conf.id, 0, -1, &i, sizeof(i)) == -1)
++ return (-1);
++ log_peer_warnx(&p->conf,
++ "graceful restart of %s, keeping routes",
++ aid2str(i));
++ p->capa.neg.grestart.flags[i] |= CAPA_GR_RESTARTING;
++ } else if (p->capa.neg.mp[i]) {
++ if (imsg_compose(ibuf_rde, IMSG_SESSION_FLUSH,
++ p->conf.id, 0, -1, &i, sizeof(i)) == -1)
++ return (-1);
++ log_peer_warnx(&p->conf,
++ "graceful restart of %s, flushing routes",
++ aid2str(i));
++ }
++ }
++ return (0);
++}
++
++int
++session_graceful_is_restarting(struct peer *p)
++{
++ u_int8_t i;
++
++ for (i = 0; i < AID_MAX; i++)
++ if (p->capa.neg.grestart.flags[i] & CAPA_GR_RESTARTING)
++ return (1);
++ return (0);
++}
++
++int
++session_graceful_stop(struct peer *p)
++{
++ u_int8_t i;
++
++ for (i = 0; i < AID_MAX; i++) {
++ /*
++ * Only flush if the peer is restarting and the peer indicated
++ * it hold the forwarding state. In all other cases the
++ * session was already flushed when the session came up.
++ */
++ if (p->capa.neg.grestart.flags[i] & CAPA_GR_RESTARTING &&
++ p->capa.neg.grestart.flags[i] & CAPA_GR_FORWARD) {
++ log_peer_warnx(&p->conf, "graceful restart of %s, "
++ "time-out, flushing", aid2str(i));
++ if (imsg_compose(ibuf_rde, IMSG_SESSION_FLUSH,
++ p->conf.id, 0, -1, &i, sizeof(i)) == -1)
++ return (-1);
++ }
++ p->capa.neg.grestart.flags[i] &= ~CAPA_GR_RESTARTING;
++ }
++ return (0);
++}
++
++int
+ session_dispatch_msg(struct pollfd *pfd, struct peer *p)
+ {
+- ssize_t n, rpos, av, left;
++ ssize_t n;
+ socklen_t len;
+- int error, processed = 0;
+- u_int16_t msglen;
+- u_int8_t msgtype;
++ int error;
+
+ if (p->state == STATE_CONNECT) {
+ if (pfd->revents & POLLOUT) {
+@@ -1641,71 +1843,83 @@ session_dispatch_msg(struct pollfd *pfd,
+ return (1);
+ }
+
+- rpos = 0;
+- av = p->rbuf->wpos + n;
++ p->rbuf->wpos += n;
+ p->stats.last_read = time(NULL);
++ return (1);
++ }
++ return (0);
++}
+
+- /*
+- * session might drop to IDLE -> buffers deallocated
+- * we MUST check rbuf != NULL before use
+- */
+- for (;;) {
+- if (rpos + MSGSIZE_HEADER > av)
+- break;
+- if (p->rbuf == NULL)
+- break;
+- if (parse_header(p, p->rbuf->buf + rpos, &msglen,
+- &msgtype) == -1)
+- return (0);
+- if (rpos + msglen > av)
+- break;
+- p->rbuf->rptr = p->rbuf->buf + rpos;
+-
+- switch (msgtype) {
+- case OPEN:
+- bgp_fsm(p, EVNT_RCVD_OPEN);
+- p->stats.msg_rcvd_open++;
+- break;
+- case UPDATE:
+- bgp_fsm(p, EVNT_RCVD_UPDATE);
+- p->stats.msg_rcvd_update++;
+- break;
+- case NOTIFICATION:
+- bgp_fsm(p, EVNT_RCVD_NOTIFICATION);
+- p->stats.msg_rcvd_notification++;
+- break;
+- case KEEPALIVE:
+- bgp_fsm(p, EVNT_RCVD_KEEPALIVE);
+- p->stats.msg_rcvd_keepalive++;
+- break;
+- case RREFRESH:
+- parse_refresh(p);
+- p->stats.msg_rcvd_rrefresh++;
+- break;
+- default: /* cannot happen */
+- session_notification(p, ERR_HEADER,
+- ERR_HDR_TYPE, &msgtype, 1);
+- log_warnx("received message with "
+- "unknown type %u", msgtype);
+- bgp_fsm(p, EVNT_CON_FATAL);
+- }
+- rpos += msglen;
+- if (++processed > MSG_PROCESS_LIMIT)
+- break;
+- }
+- if (p->rbuf == NULL)
+- return (1);
++int
++session_process_msg(struct peer *p)
++{
++ ssize_t rpos, av, left;
++ int processed = 0;
++ u_int16_t msglen;
++ u_int8_t msgtype;
+
+- if (rpos < av) {
+- left = av - rpos;
+- memcpy(&p->rbuf->buf, p->rbuf->buf + rpos, left);
+- p->rbuf->wpos = left;
+- } else
+- p->rbuf->wpos = 0;
++ rpos = 0;
++ av = p->rbuf->wpos;
+
+- return (1);
++ /*
++ * session might drop to IDLE -> buffers deallocated
++ * we MUST check rbuf != NULL before use
++ */
++ for (;;) {
++ if (rpos + MSGSIZE_HEADER > av)
++ break;
++ if (p->rbuf == NULL)
++ break;
++ if (parse_header(p, p->rbuf->buf + rpos, &msglen,
++ &msgtype) == -1)
++ return (0);
++ if (rpos + msglen > av)
++ break;
++ p->rbuf->rptr = p->rbuf->buf + rpos;
++
++ switch (msgtype) {
++ case OPEN:
++ bgp_fsm(p, EVNT_RCVD_OPEN);
++ p->stats.msg_rcvd_open++;
++ break;
++ case UPDATE:
++ bgp_fsm(p, EVNT_RCVD_UPDATE);
++ p->stats.msg_rcvd_update++;
++ break;
++ case NOTIFICATION:
++ bgp_fsm(p, EVNT_RCVD_NOTIFICATION);
++ p->stats.msg_rcvd_notification++;
++ break;
++ case KEEPALIVE:
++ bgp_fsm(p, EVNT_RCVD_KEEPALIVE);
++ p->stats.msg_rcvd_keepalive++;
++ break;
++ case RREFRESH:
++ parse_refresh(p);
++ p->stats.msg_rcvd_rrefresh++;
++ break;
++ default: /* cannot happen */
++ session_notification(p, ERR_HEADER, ERR_HDR_TYPE,
++ &msgtype, 1);
++ log_warnx("received message with unknown type %u",
++ msgtype);
++ bgp_fsm(p, EVNT_CON_FATAL);
++ }
++ rpos += msglen;
++ if (++processed > MSG_PROCESS_LIMIT)
++ break;
+ }
+- return (0);
++ if (p->rbuf == NULL)
++ return (1);
++
++ if (rpos < av) {
++ left = av - rpos;
++ memcpy(&p->rbuf->buf, p->rbuf->buf + rpos, left);
++ p->rbuf->wpos = left;
++ } else
++ p->rbuf->wpos = 0;
++
++ return (1);
+ }
+
+ int
+@@ -1853,12 +2067,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 +2174,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 +2191,14 @@ parse_open(struct peer *peer)
+ return (-1);
+ }
+
++ if (capa_neg_calc(peer) == -1) {
++ log_peer_warnx(&peer->conf,
++ "capability 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 +2233,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 +2271,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 +2296,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 +2331,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;
+@@ -2105,7 +2342,7 @@ parse_notification(struct peer *peer)
+ "disabling route refresh capability");
+ break;
+ case CAPA_RESTART:
+- peer->capa.ann.restart = 0;
++ peer->capa.ann.grestart.restart = 0;
+ log_peer_warnx(&peer->conf,
+ "disabling restart capability");
+ break;
+@@ -2139,19 +2376,23 @@ 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_int16_t gr_header;
++ u_int8_t safi;
++ u_int8_t aid;
++ u_int8_t gr_flags;
+ 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;
++ u_int8_t i;
+
+ len = dlen;
+ while (len > 0) {
+ if (len < 2) {
+- log_peer_warnx(&peer->conf, "parse_capabilities: "
+- "expect len >= 2, len is %u", len);
++ log_peer_warnx(&peer->conf, "Bad capabilities attr "
++ "length: %u, too short", len);
+ return (-1);
+ }
+ memcpy(&capa_code, d, sizeof(capa_code));
+@@ -2163,7 +2404,7 @@ parse_capabilities(struct peer *peer, u_
+ if (capa_len > 0) {
+ if (len < capa_len) {
+ log_peer_warnx(&peer->conf,
+- "parse_capabilities: "
++ "Bad capabilities attr length: "
+ "len %u smaller than capa_len %u",
+ len, capa_len);
+ return (-1);
+@@ -2178,47 +2419,82 @@ parse_capabilities(struct peer *peer, u_
+ case CAPA_MP: /* RFC 4760 */
+ if (capa_len != 4) {
+ log_peer_warnx(&peer->conf,
+- "parse_capabilities: "
+- "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;
++ "Bad multi protocol capability length: "
++ "%u", capa_len);
+ 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,
++ "Received multi protocol capability: "
++ " unknown AFI %u, safi %u pair",
++ afi, safi);
+ break;
+ }
++ peer->capa.peer.mp[aid] = 1;
+ break;
+ case CAPA_REFRESH:
+ peer->capa.peer.refresh = 1;
+ break;
+ case CAPA_RESTART:
+- peer->capa.peer.restart = 1;
+- /* we don't care about the further restart capas yet */
++ if (capa_len == 2) {
++ /* peer only supports EoR marker */
++ peer->capa.peer.grestart.restart = 1;
++ peer->capa.peer.grestart.timeout = 0;
++ break;
++ } else if (capa_len % 4 != 2) {
++ log_peer_warnx(&peer->conf,
++ "Bad graceful restart capability length: "
++ "%u", capa_len);
++ peer->capa.peer.grestart.restart = 0;
++ peer->capa.peer.grestart.timeout = 0;
++ break;
++ }
++
++ memcpy(&gr_header, capa_val, sizeof(gr_header));
++ gr_header = ntohs(gr_header);
++ peer->capa.peer.grestart.timeout =
++ gr_header & CAPA_GR_TIMEMASK;
++ if (peer->capa.peer.grestart.timeout == 0) {
++ log_peer_warnx(&peer->conf, "Received "
++ "graceful restart timeout is zero");
++ peer->capa.peer.grestart.restart = 0;
++ break;
++ }
++
++ for (i = 2; i <= capa_len - 4; i += 4) {
++ memcpy(&afi, capa_val + i, sizeof(afi));
++ afi = ntohs(afi);
++ memcpy(&safi, capa_val + i + 2, sizeof(safi));
++ if (afi2aid(afi, safi, &aid) == -1) {
++ log_peer_warnx(&peer->conf,
++ "Received graceful restart capa: "
++ " unknown AFI %u, safi %u pair",
++ afi, safi);
++ continue;
++ }
++ memcpy(&gr_flags, capa_val + i + 3,
++ sizeof(gr_flags));
++ peer->capa.peer.grestart.flags[aid] |=
++ CAPA_GR_PRESENT;
++ if (gr_flags & CAPA_GR_F_FLAG)
++ peer->capa.peer.grestart.flags[aid] |=
++ CAPA_GR_FORWARD;
++ if (gr_header & CAPA_GR_R_FLAG)
++ peer->capa.peer.grestart.flags[aid] |=
++ CAPA_GR_RESTART;
++ peer->capa.peer.grestart.restart = 2;
++ }
+ break;
+ case CAPA_AS4BYTE:
+ if (capa_len != 4) {
+ log_peer_warnx(&peer->conf,
+- "parse_capabilities: "
+- "expect len 4, len is %u", capa_len);
+- return (-1);
++ "Bad AS4BYTE capability length: "
++ "%u", capa_len);
++ peer->capa.peer.as4byte = 0;
++ break;
+ }
+ memcpy(&remote_as, capa_val, sizeof(remote_as));
+ *as = ntohl(remote_as);
+@@ -2232,6 +2508,66 @@ parse_capabilities(struct peer *peer, u_
+ 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 default to IPv4 unicast mode */
++ if (!hasmp)
++ p->capa.neg.mp[AID_INET] = 1;
++
++ /*
++ * graceful restart: only the peer capabilities are of interest here.
++ * It is necessary to compare the new values with the previous ones
++ * and act acordingly. AFI/SAFI that are not part in the MP capability
++ * are treated as not being present.
++ */
++
++ for (i = 0; i < AID_MAX; i++) {
++ /* disable GR if the AFI/SAFI is not present */
++ if (p->capa.peer.grestart.flags[i] & CAPA_GR_PRESENT &&
++ p->capa.neg.mp[i] == 0)
++ p->capa.peer.grestart.flags[i] = 0; /* disable */
++ /* look at current GR state and decide what to do */
++ if (p->capa.neg.grestart.flags[i] & CAPA_GR_RESTARTING) {
++ if (!(p->capa.peer.grestart.flags[i] &
++ CAPA_GR_FORWARD)) {
++ if (imsg_compose(ibuf_rde, IMSG_SESSION_FLUSH,
++ p->conf.id, 0, -1, &i, sizeof(i)) == -1)
++ return (-1);
++ log_peer_warnx(&p->conf, "graceful restart of "
++ "%s, not restarted, flushing", aid2str(i));
++ }
++ p->capa.neg.grestart.flags[i] =
++ p->capa.peer.grestart.flags[i] | CAPA_GR_RESTARTING;
++ } else
++ p->capa.neg.grestart.flags[i] =
++ p->capa.peer.grestart.flags[i];
++ }
++ p->capa.neg.grestart.timeout = p->capa.peer.grestart.timeout;
++ p->capa.neg.grestart.restart = p->capa.peer.grestart.restart;
++
++ return (0);
++}
++
+ void
+ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
+ {
+@@ -2244,8 +2580,8 @@ session_dispatch_imsg(struct imsgbuf *ib
+ struct kif *kif;
+ u_char *data;
+ enum reconf_action reconf;
+- int n, depend_ok;
+- u_int8_t errcode, subcode;
++ int n, depend_ok, restricted;
++ u_int8_t aid, errcode, subcode;
+
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("session_dispatch_imsg: imsg_read error");
+@@ -2332,15 +2668,42 @@ session_dispatch_imsg(struct imsgbuf *ib
+ }
+
+ 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) {
+@@ -2388,6 +2751,8 @@ session_dispatch_imsg(struct imsgbuf *ib
+ nconf = NULL;
+ pending_reconf = 0;
+ log_info("SE reconfigured");
++ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
++ -1, NULL, 0);
+ break;
+ case IMSG_IFINFO:
+ if (idx != PFD_PIPE_MAIN)
+@@ -2397,9 +2762,7 @@ session_dispatch_imsg(struct imsgbuf *ib
+ fatalx("IFINFO imsg with wrong len");
+ kif = imsg.data;
+ depend_ok = (kif->flags & IFF_UP) &&
+- (LINK_STATE_IS_UP(kif->link_state) ||
+- (kif->link_state == LINK_STATE_UNKNOWN &&
+- kif->media_type != IFT_CARP));
++ LINK_STATE_IS_UP(kif->link_state);
+
+ for (p = peers; p != NULL; p = p->next)
+ if (!strcmp(p->conf.if_depend, kif->ifname)) {
+@@ -2408,7 +2771,8 @@ session_dispatch_imsg(struct imsgbuf *ib
+ 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 +2820,10 @@ session_dispatch_imsg(struct imsgbuf *ib
+ }
+ 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 +2833,6 @@ session_dispatch_imsg(struct imsgbuf *ib
+ 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");
+@@ -2531,6 +2894,40 @@ session_dispatch_imsg(struct imsgbuf *ib
+ break;
+ }
+ break;
++ case IMSG_SESSION_RESTARTED:
++ if (idx != PFD_PIPE_ROUTE)
++ fatalx("update request not from RDE");
++ if (imsg.hdr.len < IMSG_HEADER_SIZE + sizeof(aid)) {
++ log_warnx("RDE sent invalid restart msg");
++ break;
++ }
++ if ((p = getpeerbyid(imsg.hdr.peerid)) == NULL) {
++ log_warnx("no such peer: id=%u",
++ imsg.hdr.peerid);
++ break;
++ }
++ memcpy(&aid, imsg.data, sizeof(aid));
++ if (aid >= AID_MAX)
++ fatalx("IMSG_SESSION_RESTARTED: bad AID");
++ if (p->capa.neg.grestart.flags[aid] &
++ CAPA_GR_RESTARTING &&
++ p->capa.neg.grestart.flags[aid] &
++ CAPA_GR_FORWARD) {
++ log_peer_warnx(&p->conf,
++ "graceful restart of %s finished",
++ aid2str(aid));
++ p->capa.neg.grestart.flags[aid] &=
++ ~CAPA_GR_RESTARTING;
++ timer_stop(p, Timer_RestartTimeout);
++
++ /* signal back to RDE to cleanup stale routes */
++ if (imsg_compose(ibuf_rde,
++ IMSG_SESSION_RESTARTED, imsg.hdr.peerid, 0,
++ -1, &aid, sizeof(aid)) == -1)
++ fatal("imsg_compose: "
++ "IMSG_SESSION_RESTARTED");
++ }
++ break;
+ default:
+ break;
+ }
+@@ -2612,29 +3009,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 +3044,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 +3069,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 +3106,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)
+@@ -2744,39 +3118,17 @@ session_up(struct peer *p)
+ {
+ struct session_up sup;
+
+- if (imsg_compose(ibuf_rde, IMSG_SESSION_ADD, p->conf.id, 0, -1,
+- &p->conf, sizeof(p->conf)) == -1)
+- fatalx("imsg_compose error");
++ if (!session_graceful_is_restarting(p))
++ if (imsg_compose(ibuf_rde, IMSG_SESSION_ADD, p->conf.id, 0, -1,
++ &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 +3136,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 +3148,6 @@ imsg_compose_rde(int type, pid_t pid, vo
+ 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 +3162,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/net/openbgpd/files/patch-bgpd_session.h b/net/openbgpd/files/patch-bgpd_session.h
new file mode 100644
index 000000000000..6ffbd79abd1f
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_session.h
@@ -0,0 +1,188 @@
+Index: bgpd/session.h
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/session.h,v
+retrieving revision 1.1.1.7
+retrieving revision 1.1.1.10
+diff -u -p -r1.1.1.7 -r1.1.1.10
+--- bgpd/session.h 14 Feb 2010 20:19:57 -0000 1.1.1.7
++++ bgpd/session.h 13 Oct 2012 18:22:50 -0000 1.1.1.10
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: session.h,v 1.101 2009/06/05 20:26:38 claudio Exp $ */
++/* $OpenBSD: session.h,v 1.113 2012/04/12 17:26:09 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;
+ };
+@@ -155,8 +162,10 @@ struct peer_stats {
+ u_int64_t msg_sent_rrefresh;
+ u_int64_t prefix_rcvd_update;
+ u_int64_t prefix_rcvd_withdraw;
++ u_int64_t prefix_rcvd_eor;
+ u_int64_t prefix_sent_update;
+ u_int64_t prefix_sent_withdraw;
++ u_int64_t prefix_sent_eor;
+ time_t last_updown;
+ time_t last_read;
+ u_int32_t prefix_cnt;
+@@ -172,6 +181,7 @@ enum Timer {
+ Timer_IdleHold,
+ Timer_IdleHoldReset,
+ Timer_CarpUndemote,
++ Timer_RestartTimeout,
+ Timer_Max
+ };
+
+@@ -189,6 +199,7 @@ struct peer {
+ struct {
+ struct capabilities ann;
+ struct capabilities peer;
++ struct capabilities neg;
+ } capa;
+ struct {
+ struct bgpd_addr local_addr;
+@@ -201,7 +212,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,47 +228,25 @@ struct peer {
+ u_int8_t passive;
+ };
+
+-struct peer *peers;
++extern struct peer *peers;
++extern time_t pauseaccept;
+
+ struct ctl_timer {
+ enum Timer type;
+ time_t val;
+ };
+
+-/* 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]);
+-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_rde(int, pid_t, void *, u_int16_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);
+-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 *);
++/* carp.c */
++int carp_demote_init(char *, int);
++void carp_demote_shutdown(void);
++int carp_demote_get(char *);
++int carp_demote_set(char *, int);
+
+ /* config.c */
+ int merge_config(struct bgpd_config *, struct bgpd_config *,
+ struct peer *, struct listen_addrs *);
+ void prepare_listeners(struct bgpd_config *);
+-
+-/* 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);
++int get_mpe_label(struct rdomain *);
+
+ /* control.c */
+ int control_init(int, char *);
+@@ -266,7 +255,27 @@ void control_shutdown(int);
+ int control_dispatch_msg(struct pollfd *, u_int *);
+ unsigned int control_accept(int, int);
+
++/* 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, const char *);
++void log_conn_attempt(const struct peer *, struct sockaddr *);
++
++/* mrt.c */
++void mrt_dump_bgp_msg(struct mrt *, void *, u_int16_t,
++ struct peer *);
++void mrt_dump_state(struct mrt *, u_int16_t, u_int16_t,
++ struct peer *);
++
++/* parse.y */
++int parse_config(char *, struct bgpd_config *, struct mrt_head *,
++ struct peer **, struct network_head *, struct filter_head *,
++ struct rdomain_head *);
++
+ /* 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,15 +283,24 @@ 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);
+-void carp_demote_shutdown(void);
+-int carp_demote_get(char *);
+-int carp_demote_set(char *, int);
++/* rde.c */
++pid_t rde_main(int[2], int[2], int[2], int[2], int);
++
++/* session.c */
++void session_socket_blockmode(int, enum blockmodes);
++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, 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);
+
+ /* timer.c */
++time_t getmonotime(void);
+ struct peer_timer *timer_get(struct peer *, enum Timer);
+ struct peer_timer *timer_nextisdue(struct peer *);
+ time_t timer_nextduein(struct peer *);
diff --git a/net/openbgpd/files/patch-bgpd_timer.c b/net/openbgpd/files/patch-bgpd_timer.c
new file mode 100644
index 000000000000..1238897b94fa
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_timer.c
@@ -0,0 +1,32 @@
+Index: bgpd/timer.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/timer.c,v
+retrieving revision 1.1.1.2
+retrieving revision 1.1.1.4
+diff -u -p -r1.1.1.2 -r1.1.1.4
+--- bgpd/timer.c 9 Jul 2009 16:49:54 -0000 1.1.1.2
++++ bgpd/timer.c 13 Oct 2012 18:22:50 -0000 1.1.1.4
+@@ -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>
+@@ -23,8 +23,6 @@
+ #include "bgpd.h"
+ #include "session.h"
+
+-time_t getmonotime(void);
+-
+ time_t
+ getmonotime(void)
+ {
+@@ -43,7 +41,7 @@ timer_get(struct peer *p, enum Timer tim
+
+ TAILQ_FOREACH(pt, &p->timers, entry)
+ if (pt->type == timer)
+- break;
++ break;
+
+ return (pt);
+ }
diff --git a/net/openbgpd/files/patch-bgpd_util.c b/net/openbgpd/files/patch-bgpd_util.c
new file mode 100644
index 000000000000..54e74ec9805f
--- /dev/null
+++ b/net/openbgpd/files/patch-bgpd_util.c
@@ -0,0 +1,440 @@
+Index: bgpd/util.c
+===================================================================
+RCS file: /home/cvs/private/hrs/openbgpd/bgpd/util.c,v
+retrieving revision 1.1.1.6
+retrieving revision 1.7
+diff -u -p -r1.1.1.6 -r1.7
+--- bgpd/util.c 14 Feb 2010 20:19:57 -0000 1.1.1.6
++++ bgpd/util.c 13 Oct 2012 18:36:00 -0000 1.7
+@@ -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, v
+ 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, v
+ 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)
+@@ -235,6 +335,67 @@ aspath_strlen(void *data, u_int16_t len)
+ return (total_size);
+ }
+
++/* we need to be able to search more than one as */
++int
++aspath_match(void *data, u_int16_t len, enum as_spec type, u_int32_t as)
++{
++ u_int8_t *seg;
++ int final;
++ u_int16_t seg_size;
++ u_int8_t i, seg_type, seg_len;
++
++ if (type == AS_EMPTY) {
++ if (len == 0)
++ return (1);
++ else
++ return (0);
++ }
++
++ final = 0;
++ seg = data;
++ for (; 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;
++
++ final = (len == seg_size);
++
++ /* just check the first (leftmost) AS */
++ if (type == AS_PEER) {
++ if (as == aspath_extract(seg, 0))
++ return (1);
++ else
++ return (0);
++ }
++ /* just check the final (rightmost) AS */
++ if (type == AS_SOURCE) {
++ /* not yet in the final segment */
++ if (!final)
++ continue;
++
++ if (as == aspath_extract(seg, seg_len - 1))
++ return (1);
++ else
++ return (0);
++ }
++
++ /* AS_TRANSIT or AS_ALL */
++ for (i = 0; i < seg_len; i++) {
++ if (as == aspath_extract(seg, i)) {
++ /*
++ * the source (rightmost) AS is excluded from
++ * AS_TRANSIT matches.
++ */
++ if (final && i == seg_len - 1 &&
++ type == AS_TRANSIT)
++ return (0);
++ return (1);
++ }
++ }
++ }
++ return (0);
++}
++
+ /*
+ * Extract the asnum out of the as segment at the specified position.
+ * Direct access is not possible because of non-aligned reads.
+@@ -251,6 +412,66 @@ aspath_extract(const void *seg, int pos)
+ return (ntohl(as));
+ }
+
++int
++prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
++ int prefixlen)
++{
++ in_addr_t mask, aa, ba;
++ int i;
++ u_int8_t m;
++
++ if (a->aid != b->aid)
++ return (a->aid - b->aid);
++
++ switch (a->aid) {
++ case AID_INET:
++ if (prefixlen > 32)
++ fatalx("prefix_cmp: bad IPv4 prefixlen");
++ mask = htonl(prefixlen2mask(prefixlen));
++ aa = ntohl(a->v4.s_addr & mask);
++ ba = ntohl(b->v4.s_addr & mask);
++ if (aa != ba)
++ return (aa - ba);
++ return (0);
++ case AID_INET6:
++ if (prefixlen > 128)
++ fatalx("prefix_cmp: bad IPv6 prefixlen");
++ for (i = 0; i < prefixlen / 8; i++)
++ if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
++ return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
++ i = prefixlen % 8;
++ if (i) {
++ m = 0xff00 >> i;
++ if ((a->v6.s6_addr[prefixlen / 8] & m) !=
++ (b->v6.s6_addr[prefixlen / 8] & m))
++ return ((a->v6.s6_addr[prefixlen / 8] & m) -
++ (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");
++ }
++ return (-1);
++}
++
+ in_addr_t
+ prefixlen2mask(u_int8_t prefixlen)
+ {
+@@ -276,3 +497,115 @@ inet6applymask(struct in6_addr *dest, co
+ 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/net/openbgpd/files/patch-openbsd-compat_fmt_scaled.c b/net/openbgpd/files/patch-openbsd-compat_fmt_scaled.c
new file mode 100644
index 000000000000..65407211cbc8
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_fmt_scaled.c
@@ -0,0 +1,275 @@
+Index: openbsd-compat/fmt_scaled.c
+===================================================================
+RCS file: openbsd-compat/fmt_scaled.c
+diff -N openbsd-compat/fmt_scaled.c
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/fmt_scaled.c 30 Jun 2009 06:40:07 -0000 1.1
+@@ -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/net/openbgpd/files/patch-openbsd-compat_hash.h b/net/openbgpd/files/patch-openbsd-compat_hash.h
new file mode 100644
index 000000000000..0ca34733d041
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_hash.h
@@ -0,0 +1,134 @@
+Index: openbsd-compat/hash.h
+===================================================================
+RCS file: openbsd-compat/hash.h
+diff -N openbsd-compat/hash.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/hash.h 30 Jun 2009 05:48:11 -0000 1.1
+@@ -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/net/openbgpd/files/patch-openbsd-compat_if_media.h b/net/openbgpd/files/patch-openbsd-compat_if_media.h
new file mode 100644
index 000000000000..ffd56e0cd429
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_if_media.h
@@ -0,0 +1,619 @@
+Index: openbsd-compat/if_media.h
+===================================================================
+RCS file: openbsd-compat/if_media.h
+diff -N openbsd-compat/if_media.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/if_media.h 30 Jun 2009 05:48:11 -0000 1.1
+@@ -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/net/openbgpd/files/patch-openbsd-compat_imsg-buffer.c b/net/openbgpd/files/patch-openbsd-compat_imsg-buffer.c
new file mode 100644
index 000000000000..3db0ec22abe8
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_imsg-buffer.c
@@ -0,0 +1,312 @@
+Index: openbsd-compat/imsg-buffer.c
+===================================================================
+RCS file: openbsd-compat/imsg-buffer.c
+diff -N openbsd-compat/imsg-buffer.c
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/imsg-buffer.c 8 Dec 2012 20:17:59 -0000 1.2
+@@ -0,0 +1,305 @@
++/* $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++;
++ }
++
++again:
++ if ((n = writev(msgbuf->fd, iov, i)) == -1) {
++ if (errno == EAGAIN || errno == EINTR)
++ goto again;
++ if (errno == ENOBUFS)
++ errno = EAGAIN;
++ return (-1);
++ }
++
++ if (n == 0) { /* connection closed */
++ errno = 0;
++ return (0);
++ }
++
++ msgbuf_drain(msgbuf, n);
++
++ return (1);
++}
++
++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;
++ }
++
++again:
++ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
++ if (errno == EAGAIN || errno == EINTR)
++ goto again;
++ if (errno == ENOBUFS)
++ errno = EAGAIN;
++ return (-1);
++ }
++
++ if (n == 0) { /* connection closed */
++ errno = 0;
++ return (0);
++ }
++
++ /*
++ * 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 (1);
++}
++
++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/net/openbgpd/files/patch-openbsd-compat_imsg.c b/net/openbgpd/files/patch-openbsd-compat_imsg.c
new file mode 100644
index 000000000000..c23d5aa22060
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_imsg.c
@@ -0,0 +1,312 @@
+Index: openbsd-compat/imsg.c
+===================================================================
+RCS file: openbsd-compat/imsg.c
+diff -N openbsd-compat/imsg.c
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/imsg.c 8 Dec 2012 20:17:59 -0000 1.2
+@@ -0,0 +1,305 @@
++/* $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_fd_overhead = 0;
++
++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) * 1)];
++ } cmsgbuf;
++ struct iovec iov;
++ ssize_t n = -1;
++ 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 ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
++ return (-1);
++
++again:
++#if defined(__FreeBSD__)
++ if (imsg_fd_overhead +
++#else
++ if (getdtablecount() + imsg_fd_overhead +
++#endif
++ (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)
++ >= getdtablesize()) {
++ errno = EAGAIN;
++ return (-1);
++ }
++
++ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
++ if (errno == EMSGSIZE)
++ goto fail;
++ if (errno != EINTR && errno != EAGAIN)
++ goto fail;
++ goto again;
++ }
++
++ 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) {
++ int i;
++ int j;
++
++ /*
++ * We only accept one file descriptor. Due to C
++ * padding rules, our control buffer might contain
++ * more than one fd, and we must close them.
++ */
++ j = ((char *)cmsg + cmsg->cmsg_len -
++ (char *)CMSG_DATA(cmsg)) / sizeof(int);
++ for (i = 0; i < j; i++) {
++ fd = ((int *)CMSG_DATA(cmsg))[i];
++ if (i == 0) {
++ ifd->fd = fd;
++ TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
++ entry);
++ ifd = NULL;
++ } else
++ close(fd);
++ }
++ }
++ /* we do not handle other ctl data level */
++ }
++
++fail:
++ if (ifd)
++ free(ifd);
++ 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/net/openbgpd/files/patch-openbsd-compat_imsg.h b/net/openbgpd/files/patch-openbsd-compat_imsg.h
new file mode 100644
index 000000000000..91cf15ea1afd
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_imsg.h
@@ -0,0 +1,119 @@
+Index: openbsd-compat/imsg.h
+===================================================================
+RCS file: openbsd-compat/imsg.h
+diff -N openbsd-compat/imsg.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/imsg.h 8 Dec 2012 20:17:59 -0000 1.2
+@@ -0,0 +1,112 @@
++/* $OpenBSD: imsg.h,v 1.1 2010/05/26 16:44:32 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/net/openbgpd/files/patch-openbsd-compat_openbsd-compat.h b/net/openbgpd/files/patch-openbsd-compat_openbsd-compat.h
new file mode 100644
index 000000000000..3855a8dd3f95
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_openbsd-compat.h
@@ -0,0 +1,98 @@
+Index: openbsd-compat/openbsd-compat.h
+===================================================================
+RCS file: openbsd-compat/openbsd-compat.h
+diff -N openbsd-compat/openbsd-compat.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/openbsd-compat.h 13 Oct 2012 18:50:10 -0000 1.8
+@@ -0,0 +1,91 @@
++/*
++ * $hrs: openbgpd/openbsd-compat/openbsd-compat.h,v 1.8 2012/10/13 18:50:10 hrs Exp $
++ */
++
++#ifndef _OPENBSD_COMPAT_H
++#define _OPENBSD_COMPAT_H
++
++#define __dead
++
++/* bgpctl/bgpctl.c */
++#include <sys/endian.h>
++#include <inttypes.h>
++#define betoh64(x) (be64toh(x))
++#ifndef IFT_CARP
++#define IFT_CARP 0xf8
++#endif
++
++/* 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/net/openbgpd/files/patch-openbsd-compat_util.h b/net/openbgpd/files/patch-openbsd-compat_util.h
new file mode 100644
index 000000000000..c2de585e338a
--- /dev/null
+++ b/net/openbgpd/files/patch-openbsd-compat_util.h
@@ -0,0 +1,126 @@
+Index: openbsd-compat/util.h
+===================================================================
+RCS file: openbsd-compat/util.h
+diff -N openbsd-compat/util.h
+--- /dev/null 1 Jan 1970 00:00:00 -0000
++++ openbsd-compat/util.h 30 Jun 2009 06:40:07 -0000 1.1
+@@ -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_ */
diff --git a/net/openbgpd/pkg-plist b/net/openbgpd/pkg-plist
deleted file mode 100644
index 76795a6b9942..000000000000
--- a/net/openbgpd/pkg-plist
+++ /dev/null
@@ -1,6 +0,0 @@
-sbin/bgpctl
-sbin/bgpd
-man/man5/bgpd.conf.5.gz
-man/man8/bgpctl.8.gz
-man/man8/bgpd.8.gz
-@sample etc/bgpd.conf.sample