diff options
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 |