summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/bgpd/Makefile4
-rw-r--r--usr.sbin/bgpd/bgpd.812
-rw-r--r--usr.sbin/bgpd/bgpd.c202
-rw-r--r--usr.sbin/bgpd/bgpd.conf.537
-rw-r--r--usr.sbin/bgpd/bgpd.h67
-rw-r--r--usr.sbin/bgpd/config.c22
-rw-r--r--usr.sbin/bgpd/control.c3
-rw-r--r--usr.sbin/bgpd/parse.y121
-rw-r--r--usr.sbin/bgpd/printconf.c19
-rw-r--r--usr.sbin/bgpd/rde.c224
-rw-r--r--usr.sbin/bgpd/rtr.c334
-rw-r--r--usr.sbin/bgpd/rtr_proto.c1157
-rw-r--r--usr.sbin/bgpd/session.c4
-rw-r--r--usr.sbin/bgpd/session.h33
-rw-r--r--usr.sbin/bgpd/util.c34
15 files changed, 2167 insertions, 106 deletions
diff --git a/usr.sbin/bgpd/Makefile b/usr.sbin/bgpd/Makefile
index 55d6f47157e..61c73d5069e 100644
--- a/usr.sbin/bgpd/Makefile
+++ b/usr.sbin/bgpd/Makefile
@@ -1,11 +1,11 @@
-# $OpenBSD: Makefile,v 1.36 2020/01/01 07:25:04 claudio Exp $
+# $OpenBSD: Makefile,v 1.37 2021/02/16 08:29:16 claudio Exp $
PROG= bgpd
SRCS= bgpd.c session.c log.c logmsg.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 rde_community.c printconf.c \
rde_filter.c rde_sets.c rde_trie.c pftable.c name2id.c \
- util.c carp.c timer.c rde_peer.c
+ util.c carp.c timer.c rde_peer.c rtr.c rtr_proto.c
CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
diff --git a/usr.sbin/bgpd/bgpd.8 b/usr.sbin/bgpd/bgpd.8
index 3c2cb0aab46..26439ce8eaa 100644
--- a/usr.sbin/bgpd/bgpd.8
+++ b/usr.sbin/bgpd/bgpd.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.8,v 1.63 2021/02/01 07:39:59 jmc Exp $
+.\" $OpenBSD: bgpd.8,v 1.64 2021/02/16 08:29:16 claudio Exp $
.\"
.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: February 1 2021 $
+.Dd $Mdocdate: February 16 2021 $
.Dt BGPD 8
.Os
.Sh NAME
@@ -413,6 +413,14 @@ has been started.
.Re
.Pp
.Rs
+.%A R. Bush
+.%A R. Austein
+.%D September 2017
+.%R RFC 8210
+.%T The Resource Public Key Infrastructure (RPKI) to Router Protocol, Version 1
+.Re
+.Pp
+.Rs
.%A J. Mauch
.%A J. Snijders
.%A G. Hankins
diff --git a/usr.sbin/bgpd/bgpd.c b/usr.sbin/bgpd/bgpd.c
index 63e47689f85..e6bf4c88d49 100644
--- a/usr.sbin/bgpd/bgpd.c
+++ b/usr.sbin/bgpd/bgpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.c,v 1.233 2021/01/04 17:44:14 claudio Exp $ */
+/* $OpenBSD: bgpd.c,v 1.234 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -47,7 +47,9 @@ int send_config(struct bgpd_config *);
int dispatch_imsg(struct imsgbuf *, int, struct bgpd_config *);
int control_setup(struct bgpd_config *);
static void getsockpair(int [2]);
-int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *);
+int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *,
+ struct imsgbuf *);
+void bgpd_rtr_connect(struct rtr_config *);
int cflags;
volatile sig_atomic_t mrtdump;
@@ -57,6 +59,7 @@ pid_t reconfpid;
int reconfpending;
struct imsgbuf *ibuf_se;
struct imsgbuf *ibuf_rde;
+struct imsgbuf *ibuf_rtr;
struct rib_names ribnames = SIMPLEQ_HEAD_INITIALIZER(ribnames);
char *cname;
char *rcname;
@@ -91,9 +94,10 @@ usage(void)
#define PFD_PIPE_SESSION 0
#define PFD_PIPE_RDE 1
-#define PFD_SOCK_ROUTE 2
-#define PFD_SOCK_PFKEY 3
-#define POLL_MAX 4
+#define PFD_PIPE_RTR 2
+#define PFD_SOCK_ROUTE 3
+#define PFD_SOCK_PFKEY 4
+#define POLL_MAX 5
#define MAX_TIMEOUT 3600
int cmd_opts;
@@ -107,7 +111,7 @@ main(int argc, char *argv[])
struct peer *p;
struct pollfd pfd[POLL_MAX];
time_t timeout;
- pid_t se_pid = 0, rde_pid = 0, pid;
+ pid_t se_pid = 0, rde_pid = 0, rtr_pid = 0, pid;
char *conffile;
char *saved_argv0;
int debug = 0;
@@ -115,6 +119,7 @@ main(int argc, char *argv[])
int ch, status;
int pipe_m2s[2];
int pipe_m2r[2];
+ int pipe_m2roa[2];
conffile = CONFFILE;
@@ -126,7 +131,7 @@ main(int argc, char *argv[])
if (saved_argv0 == NULL)
saved_argv0 = "bgpd";
- while ((ch = getopt(argc, argv, "cdD:f:nRSv")) != -1) {
+ while ((ch = getopt(argc, argv, "cdD:f:nRSTv")) != -1) {
switch (ch) {
case 'c':
cmd_opts |= BGPD_OPT_FORCE_DEMOTE;
@@ -156,6 +161,9 @@ main(int argc, char *argv[])
case 'S':
proc = PROC_SE;
break;
+ case 'T':
+ proc = PROC_RTR;
+ break;
default:
usage();
/* NOTREACHED */
@@ -168,7 +176,7 @@ main(int argc, char *argv[])
usage();
if (cmd_opts & BGPD_OPT_NOACTION) {
- if ((conf = parse_config(conffile, NULL)) == NULL)
+ if ((conf = parse_config(conffile, NULL, NULL)) == NULL)
exit(1);
if (cmd_opts & BGPD_OPT_VERBOSE)
@@ -193,6 +201,9 @@ main(int argc, char *argv[])
case PROC_SE:
session_main(debug, cmd_opts & BGPD_OPT_VERBOSE);
/* NOTREACHED */
+ case PROC_RTR:
+ rtr_main(debug, cmd_opts & BGPD_OPT_VERBOSE);
+ /* NOTREACHED */
}
if (geteuid())
@@ -201,7 +212,7 @@ main(int argc, char *argv[])
if (getpwnam(BGPD_USER) == NULL)
errx(1, "unknown user %s", BGPD_USER);
- if ((conf = parse_config(conffile, NULL)) == NULL) {
+ if ((conf = parse_config(conffile, NULL, NULL)) == NULL) {
log_warnx("config file %s has errors", conffile);
exit(1);
}
@@ -219,12 +230,15 @@ main(int argc, char *argv[])
getsockpair(pipe_m2s);
getsockpair(pipe_m2r);
+ getsockpair(pipe_m2roa);
/* fork children */
rde_pid = start_child(PROC_RDE, saved_argv0, pipe_m2r[1], debug,
cmd_opts & BGPD_OPT_VERBOSE);
se_pid = start_child(PROC_SE, saved_argv0, pipe_m2s[1], debug,
cmd_opts & BGPD_OPT_VERBOSE);
+ rtr_pid = start_child(PROC_RTR, saved_argv0, pipe_m2roa[1], debug,
+ cmd_opts & BGPD_OPT_VERBOSE);
signal(SIGTERM, sighdlr);
signal(SIGINT, sighdlr);
@@ -234,10 +248,12 @@ main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL ||
- (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_rtr = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf_se, pipe_m2s[0]);
imsg_init(ibuf_rde, pipe_m2r[0]);
+ imsg_init(ibuf_rtr, pipe_m2roa[0]);
mrt_init(ibuf_rde, ibuf_se);
if (kr_init(&rfd) == -1)
quit = 1;
@@ -262,7 +278,7 @@ BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd",
fatal("pledge");
#endif
- if (imsg_send_sockets(ibuf_se, ibuf_rde))
+ if (imsg_send_sockets(ibuf_se, ibuf_rde, ibuf_rtr))
fatal("could not establish imsg links");
/* control setup needs to happen late since it sends imsgs */
if (control_setup(conf) == -1)
@@ -285,6 +301,7 @@ BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd",
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se);
set_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde);
+ set_pollfd(&pfd[PFD_PIPE_RTR], ibuf_rtr);
if (timeout < 0 || timeout > MAX_TIMEOUT)
timeout = MAX_TIMEOUT;
@@ -313,8 +330,18 @@ BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd",
ibuf_rde = NULL;
quit = 1;
} else {
- if (dispatch_imsg(ibuf_rde, PFD_PIPE_RDE, conf) ==
- -1)
+ if (dispatch_imsg(ibuf_rde, PFD_PIPE_RDE, conf) == -1)
+ quit = 1;
+ }
+
+ if (handle_pollfd(&pfd[PFD_PIPE_RTR], ibuf_rtr) == -1) {
+ log_warnx("main: Lost connection to RTR");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ quit = 1;
+ } else {
+ if (dispatch_imsg(ibuf_rtr, PFD_PIPE_RTR, conf) == -1)
quit = 1;
}
@@ -377,6 +404,12 @@ BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd",
free(ibuf_rde);
ibuf_rde = NULL;
}
+ if (ibuf_rtr) {
+ msgbuf_clear(&ibuf_rtr->w);
+ close(ibuf_rtr->fd);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ }
/* cleanup kernel data structures */
carp_demote_shutdown();
@@ -404,6 +437,8 @@ BROKEN if (pledge("stdio rpath wpath cpath fattr unix route recvfd sendfd",
name = "route decision engine";
else if (pid == se_pid)
name = "session engine";
+ else if (pid == rtr_pid)
+ name = "rtr engine";
log_warnx("%s terminated; signal %d", name,
WTERMSIG(status));
}
@@ -449,6 +484,9 @@ start_child(enum bgpd_process p, char *argv0, int fd, int debug, int verbose)
case PROC_SE:
argv[argc++] = "-S";
break;
+ case PROC_RTR:
+ argv[argc++] = "-T";
+ break;
}
if (debug)
argv[argc++] = "-d";
@@ -481,7 +519,8 @@ reconfigure(char *conffile, struct bgpd_config *conf)
return (2);
log_info("rereading config");
- if ((new_conf = parse_config(conffile, &conf->peers)) == NULL)
+ if ((new_conf = parse_config(conffile, &conf->peers,
+ &conf->rtrs)) == NULL)
return (1);
merge_config(conf, new_conf);
@@ -509,8 +548,9 @@ send_config(struct bgpd_config *conf)
struct prefixset *ps;
struct prefixset_item *psi, *npsi;
struct roa *roa, *nroa;
+ struct rtr_config *rtr;
- reconfpending = 2; /* one per child */
+ reconfpending = 3; /* one per child */
expand_networks(conf);
@@ -523,6 +563,9 @@ send_config(struct bgpd_config *conf)
if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1,
conf, sizeof(*conf)) == -1)
return (-1);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_CONF, 0, 0, -1,
+ conf, sizeof(*conf)) == -1)
+ return (-1);
TAILQ_FOREACH(la, conf->listen_addrs, entry) {
if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd,
@@ -595,17 +638,18 @@ send_config(struct bgpd_config *conf)
free(ps);
}
- if (!RB_EMPTY(&conf->roa)) {
- if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1,
- NULL, 0) == -1)
+ /* roa table and rtr config are sent to the RTR engine */
+ RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nroa) {
+ RB_REMOVE(roa_tree, &conf->roa, roa);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_ROA_ITEM, 0, 0,
+ -1, roa, sizeof(*roa)) == -1)
+ return (-1);
+ free(roa);
+ }
+ SIMPLEQ_FOREACH(rtr, &conf->rtrs, entry) {
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_RTR_CONFIG, rtr->id,
+ 0, -1, rtr->descr, sizeof(rtr->descr)) == -1)
return (-1);
- RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nroa) {
- RB_REMOVE(roa_tree, &conf->roa, roa);
- if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0,
- -1, roa, sizeof(*roa)) == -1)
- return (-1);
- free(roa);
- }
}
/* as-sets for filters in the RDE */
@@ -695,6 +739,8 @@ send_config(struct bgpd_config *conf)
return (-1);
if (imsg_compose(ibuf_rde, IMSG_RECONF_DRAIN, 0, 0, -1, NULL, 0) == -1)
return (-1);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_DRAIN, 0, 0, -1, NULL, 0) == -1)
+ return (-1);
/* mrt changes can be sent out of bound */
mrt_reconfigure(conf->mrt);
@@ -706,6 +752,7 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx, struct bgpd_config *conf)
{
struct imsg imsg;
struct peer *p;
+ struct rtr_config *r;
ssize_t n;
int rv, verbose;
@@ -874,6 +921,9 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx, struct bgpd_config *conf)
break;
}
if (idx == PFD_PIPE_SESSION) {
+ imsg_compose(ibuf_rtr, IMSG_RECONF_DONE, 0,
+ 0, -1, NULL, 0);
+ } else if (idx == PFD_PIPE_RTR) {
imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0,
0, -1, NULL, 0);
@@ -898,9 +948,66 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx, struct bgpd_config *conf)
*/
imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0,
0, -1, NULL, 0);
- reconfpending = 2; /* expecting 2 DONE msg */
+ reconfpending = 3; /* expecting 2 DONE msg */
+ }
+ break;
+ case IMSG_SOCKET_CONN:
+ if (idx != PFD_PIPE_RTR) {
+ log_warnx("connect request not from RTR");
+ } else {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ if (imsg.hdr.peerid == r->id)
+ break;
+ }
+ if (r == NULL)
+ log_warnx("unknown rtr id %d",
+ imsg.hdr.peerid);
+ else
+ bgpd_rtr_connect(r);
}
break;
+ case IMSG_CTL_SHOW_RTR:
+ if (idx == PFD_PIPE_SESSION) {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ imsg_compose(ibuf_rtr, imsg.hdr.type,
+ r->id, imsg.hdr.pid, -1, NULL, 0);
+ }
+ imsg_compose(ibuf_rtr, IMSG_CTL_END,
+ 0, imsg.hdr.pid, -1, NULL, 0);
+ } else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ctl_show_rtr)) {
+ log_warnx("IMSG_CTL_SHOW_RTR with wrong len");
+ } else if (idx == PFD_PIPE_RTR) {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ if (imsg.hdr.peerid == r->id)
+ break;
+ }
+ if (r != NULL) {
+ struct ctl_show_rtr *msg;
+ msg = imsg.data;
+ strlcpy(msg->descr, r->descr,
+ sizeof(msg->descr));
+ msg->local_addr = r->local_addr;
+ msg->remote_addr = r->remote_addr;
+ msg->remote_port = r->remote_port;
+
+ imsg_compose(ibuf_se, imsg.hdr.type,
+ imsg.hdr.peerid, imsg.hdr.pid,
+ -1, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ }
+ }
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_TIMER:
+ if (idx != PFD_PIPE_RTR) {
+ log_warnx("connect request not from RTR");
+ break;
+ }
+ imsg_compose(ibuf_se, imsg.hdr.type, imsg.hdr.peerid,
+ imsg.hdr.pid, -1, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ break;
default:
break;
}
@@ -1117,13 +1224,15 @@ getsockpair(int pipe[2])
}
int
-imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde)
+imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde, struct imsgbuf *roa)
{
int pipe_s2r[2];
int pipe_s2r_ctl[2];
+ int pipe_r2r[2];
getsockpair(pipe_s2r);
getsockpair(pipe_s2r_ctl);
+ getsockpair(pipe_r2r);
if (imsg_compose(se, IMSG_SOCKET_CONN, 0, 0, pipe_s2r[0],
NULL, 0) == -1)
@@ -1139,5 +1248,44 @@ imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde)
NULL, 0) == -1)
return (-1);
+ if (imsg_compose(roa, IMSG_SOCKET_CONN_RTR, 0, 0, pipe_r2r[0],
+ NULL, 0) == -1)
+ return (-1);
+ if (imsg_compose(rde, IMSG_SOCKET_CONN_RTR, 0, 0, pipe_r2r[1],
+ NULL, 0) == -1)
+ return (-1);
+
return (0);
}
+
+void
+bgpd_rtr_connect(struct rtr_config *r)
+{
+ socklen_t len;
+ int fd;
+
+ /* XXX should be non-blocking */
+ fd = socket(aid2af(r->remote_addr.aid), SOCK_STREAM, 0);
+ if (fd == -1) {
+ log_warn("rtr %s", r->descr);
+ return;
+ }
+ if (r->local_addr.aid != AID_UNSPEC) {
+ if (bind(fd, addr2sa(&r->local_addr, 0, &len), len) == -1) {
+ log_warn("rtr %s: bind to %s", r->descr,
+ log_addr(&r->local_addr));
+ close(fd);
+ return;
+ }
+ }
+
+ if (connect(fd, addr2sa(&r->remote_addr, r->remote_port, &len), len) ==
+ -1) {
+ log_warn("rtr %s: connect to %s:%u", r->descr,
+ log_addr(&r->remote_addr), r->remote_port);
+ close(fd);
+ return;
+ }
+
+ imsg_compose(ibuf_rtr, IMSG_SOCKET_CONN, r->id, 0, fd, NULL, 0);
+}
diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5
index eb937417734..a1e21a7d760 100644
--- a/usr.sbin/bgpd/bgpd.conf.5
+++ b/usr.sbin/bgpd/bgpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: bgpd.conf.5,v 1.207 2021/01/27 14:57:29 jmc Exp $
+.\" $OpenBSD: bgpd.conf.5,v 1.208 2021/02/16 08:29:16 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: January 27 2021 $
+.Dd $Mdocdate: February 16 2021 $
.Dt BGPD.CONF 5
.Os
.Sh NAME
@@ -494,6 +494,39 @@ and the Origin Validation State (OVS) is set.
roa-set { 192.0.2.0/24 maxlen 24 source-as 64511
203.0.113.0/24 source-as 64496 }
.Ed
+.Pp
+.It Xo
+.Ic rtr Ar address
+.Ic { Ar ... Ic }
+.Xc
+The
+.Ic rtr
+block specifies a
+.Em RPKI to Router Protocol
+session.
+The rtr session properties are as follows:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic descr Ar description
+Add a description.
+The description is used in logging and status reports, but has no further
+meaning for
+.Xr bgpd 8 .
+.Pp
+.It Ic local-address Ar address
+Bind to the specific IP address before opening the TCP connection to the
+.Em rtr
+server.
+.Pp
+.It Ic port Ar number
+Specify the TCP destination port for the
+.Em rtr
+session.
+If not specified the default
+.Ic port
+is
+.Em 323 .
+.El
.El
.Sh NETWORK ANNOUNCEMENTS
.Ic network
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h
index 97f20cde479..03578e98493 100644
--- a/usr.sbin/bgpd/bgpd.h
+++ b/usr.sbin/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.h,v 1.411 2021/01/25 09:15:23 claudio Exp $ */
+/* $OpenBSD: bgpd.h,v 1.412 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -120,7 +120,8 @@
enum bgpd_process {
PROC_MAIN,
PROC_SE,
- PROC_RDE
+ PROC_RDE,
+ PROC_RTR,
};
enum reconf_action {
@@ -266,6 +267,9 @@ SIMPLEQ_HEAD(as_set_head, as_set);
struct filter_rule;
TAILQ_HEAD(filter_head, filter_rule);
+struct rtr_config;
+SIMPLEQ_HEAD(rtr_config_head, rtr_config);
+
struct bgpd_config {
struct peer_head peers;
struct l3vpn_head l3vpns;
@@ -278,8 +282,8 @@ struct bgpd_config {
struct roa_tree roa;
struct rde_prefixset_head rde_prefixsets;
struct rde_prefixset_head rde_originsets;
- struct rde_prefixset rde_roa;
struct as_set_head as_sets;
+ struct rtr_config_head rtrs;
char *csock;
char *rcsock;
int flags;
@@ -433,6 +437,44 @@ struct network {
TAILQ_ENTRY(network) entry;
};
+enum rtr_error {
+ NO_ERROR = -1,
+ CORRUPT_DATA = 0,
+ INTERNAL_ERROR,
+ NO_DATA_AVAILABLE,
+ INVALID_REQUEST,
+ UNSUPP_PROTOCOL_VERS,
+ UNSUPP_PDU_TYPE,
+ UNK_REC_WDRAWL,
+ DUP_REC_RECV,
+ UNEXP_PROTOCOL_VERS,
+};
+
+struct rtr_config {
+ SIMPLEQ_ENTRY(rtr_config) entry;
+ char descr[PEER_DESCR_LEN];
+ struct bgpd_addr remote_addr;
+ struct bgpd_addr local_addr;
+ u_int32_t id;
+ in_addr_t remote_port;
+};
+
+struct ctl_show_rtr {
+ char descr[PEER_DESCR_LEN];
+ struct bgpd_addr remote_addr;
+ struct bgpd_addr local_addr;
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ int session_id;
+ in_addr_t remote_port;
+ enum rtr_error last_sent_error;
+ enum rtr_error last_recv_error;
+ char last_sent_msg[REASON_LEN];
+ char last_recv_msg[REASON_LEN];
+};
+
enum imsg_type {
IMSG_NONE,
IMSG_CTL_END,
@@ -462,6 +504,7 @@ enum imsg_type {
IMSG_CTL_LOG_VERBOSE,
IMSG_CTL_SHOW_FIB_TABLES,
IMSG_CTL_SHOW_SET,
+ IMSG_CTL_SHOW_RTR,
IMSG_CTL_TERMINATE,
IMSG_NETWORK_ADD,
IMSG_NETWORK_ASPATH,
@@ -472,6 +515,7 @@ enum imsg_type {
IMSG_FILTER_SET,
IMSG_SOCKET_CONN,
IMSG_SOCKET_CONN_CTL,
+ IMSG_SOCKET_CONN_RTR,
IMSG_RECONF_CONF,
IMSG_RECONF_RIB,
IMSG_RECONF_PEER,
@@ -490,6 +534,7 @@ enum imsg_type {
IMSG_RECONF_ORIGIN_SET,
IMSG_RECONF_ROA_SET,
IMSG_RECONF_ROA_ITEM,
+ IMSG_RECONF_RTR_CONFIG,
IMSG_RECONF_DRAIN,
IMSG_RECONF_DONE,
IMSG_UPDATE,
@@ -1194,11 +1239,13 @@ void free_prefixsets(struct prefixset_head *);
void free_rde_prefixsets(struct rde_prefixset_head *);
void free_prefixtree(struct prefixset_tree *);
void free_roatree(struct roa_tree *);
+void free_rtrs(struct rtr_config_head *);
void filterlist_free(struct filter_head *);
int host(const char *, struct bgpd_addr *, u_int8_t *);
u_int32_t get_bgpid(void);
void expand_networks(struct bgpd_config *);
RB_PROTOTYPE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
+int roa_cmp(struct roa *, struct roa *);
RB_PROTOTYPE(roa_tree, roa, entry, roa_cmp);
/* kroute.c */
@@ -1262,8 +1309,10 @@ void pftable_unref(u_int16_t);
u_int16_t pftable_ref(u_int16_t);
/* parse.y */
-int cmdline_symset(char *);
-struct prefixset *find_prefixset(char *, struct prefixset_head *);
+int cmdline_symset(char *);
+struct prefixset *find_prefixset(char *, struct prefixset_head *);
+struct bgpd_config *parse_config(char *, struct peer_head *,
+ struct rtr_config_head *);
/* pftable.c */
int pftable_exists(const char *);
@@ -1320,6 +1369,7 @@ const char *log_as(u_int32_t);
const char *log_rd(u_int64_t);
const char *log_ext_subtype(short, u_int8_t);
const char *log_reason(const char *);
+const char *log_rtr_error(enum rtr_error);
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);
@@ -1356,7 +1406,8 @@ const char * get_baudrate(unsigned long long, char *);
static const char * const log_procnames[] = {
"parent",
"SE",
- "RDE"
+ "RDE",
+ "RTR"
};
/* logmsg.c and needed by bgpctl */
@@ -1485,6 +1536,10 @@ static const char * const timernames[] = {
"IdleHoldTimer",
"IdleHoldResetTimer",
"CarpUndemoteTimer",
+ "RestartTimer",
+ "RTR RefreshTimer",
+ "RTR RetryTimer",
+ "RTR ExpireTimer",
""
};
diff --git a/usr.sbin/bgpd/config.c b/usr.sbin/bgpd/config.c
index 1ccb45e459d..d5b326e9af6 100644
--- a/usr.sbin/bgpd/config.c
+++ b/usr.sbin/bgpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.98 2021/01/04 13:42:11 claudio Exp $ */
+/* $OpenBSD: config.c,v 1.99 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
@@ -60,6 +60,7 @@ new_config(void)
SIMPLEQ_INIT(&conf->rde_originsets);
RB_INIT(&conf->roa);
SIMPLEQ_INIT(&conf->as_sets);
+ SIMPLEQ_INIT(&conf->rtrs);
TAILQ_INIT(conf->filters);
TAILQ_INIT(conf->listen_addrs);
@@ -163,6 +164,18 @@ free_roatree(struct roa_tree *r)
}
void
+free_rtrs(struct rtr_config_head *rh)
+{
+ struct rtr_config *r;
+
+ while (!SIMPLEQ_EMPTY(rh)) {
+ r = SIMPLEQ_FIRST(rh);
+ SIMPLEQ_REMOVE_HEAD(rh, entry);
+ free(r);
+ }
+}
+
+void
free_config(struct bgpd_config *conf)
{
struct peer *p, *next;
@@ -178,6 +191,7 @@ free_config(struct bgpd_config *conf)
free_rde_prefixsets(&conf->rde_originsets);
as_sets_free(&conf->as_sets);
free_roatree(&conf->roa);
+ free_rtrs(&conf->rtrs);
while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
TAILQ_REMOVE(conf->listen_addrs, la, entry);
@@ -246,6 +260,10 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf)
RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa);
RB_ROOT(&conf->roa) = NULL;
+ /* switch the rtr_configs, first remove the old ones */
+ free_rtrs(&xconf->rtrs);
+ SIMPLEQ_CONCAT(&xconf->rtrs, &conf->rtrs);
+
/* switch the prefixsets, first remove the old ones */
free_prefixsets(&xconf->prefixsets);
SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets);
@@ -582,7 +600,7 @@ prefixset_cmp(struct prefixset_item *a, struct prefixset_item *b)
RB_GENERATE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
-static inline int
+int
roa_cmp(struct roa *a, struct roa *b)
{
int i;
diff --git a/usr.sbin/bgpd/control.c b/usr.sbin/bgpd/control.c
index 4430362547c..1e7bbda3b9d 100644
--- a/usr.sbin/bgpd/control.c
+++ b/usr.sbin/bgpd/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.103 2020/12/30 07:29:56 claudio Exp $ */
+/* $OpenBSD: control.c,v 1.104 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -448,6 +448,7 @@ control_dispatch_msg(struct pollfd *pfd, struct peer_head *peers)
case IMSG_CTL_RELOAD:
case IMSG_CTL_SHOW_INTERFACE:
case IMSG_CTL_SHOW_FIB_TABLES:
+ case IMSG_CTL_SHOW_RTR:
c->ibuf.pid = imsg.hdr.pid;
imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y
index f2802403053..7dd7f7c2b4f 100644
--- a/usr.sbin/bgpd/parse.y
+++ b/usr.sbin/bgpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.412 2021/01/25 09:15:23 claudio Exp $ */
+/* $OpenBSD: parse.y,v 1.413 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -89,12 +89,14 @@ char *symget(const char *);
static struct bgpd_config *conf;
static struct network_head *netconf;
static struct peer_head *new_peers, *cur_peers;
+static struct rtr_config_head *cur_rtrs;
static struct peer *curpeer;
static struct peer *curgroup;
static struct rde_rib *currib;
static struct l3vpn *curvpn;
static struct prefixset *curpset, *curoset;
static struct roa_tree *curroatree;
+static struct rtr_config *currtr;
static struct filter_head *filter_l;
static struct filter_head *peerfilter_l;
static struct filter_head *groupfilter_l;
@@ -162,6 +164,8 @@ static void add_as_set(u_int32_t);
static void done_as_set(void);
static struct prefixset *new_prefix_set(char *, int);
static void add_roa_set(struct prefixset_item *, u_int32_t, u_int8_t);
+static struct rtr_config *get_rtr(struct bgpd_addr *);
+static int insert_rtr(struct rtr_config *);
typedef struct {
union {
@@ -196,7 +200,7 @@ typedef struct {
%token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE FIBPRIORITY RTABLE
%token NONE UNICAST VPN RD EXPORT EXPORTTRGT IMPORTTRGT DEFAULTROUTE
-%token RDE RIB EVALUATE IGNORE COMPARE
+%token RDE RIB EVALUATE IGNORE COMPARE RTR PORT
%token GROUP NEIGHBOR NETWORK
%token EBGP IBGP
%token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
@@ -252,6 +256,7 @@ grammar : /* empty */
| grammar prefixset '\n'
| grammar roa_set '\n'
| grammar origin_set '\n'
+ | grammar rtr '\n'
| grammar rib '\n'
| grammar conf_main '\n'
| grammar l3vpn '\n'
@@ -551,6 +556,59 @@ roa_set_l : prefixset_item SOURCEAS as4number_any {
}
;
+rtr : RTR address {
+ currtr = get_rtr(&$2);
+ currtr->remote_port = 323;
+ if (insert_rtr(currtr) == -1) {
+ free(currtr);
+ YYERROR;
+ }
+ currtr = NULL;
+ }
+ | RTR address {
+ currtr = get_rtr(&$2);
+ currtr->remote_port = 323;
+ } '{' optnl rtropt_l optnl '}' {
+ if (insert_rtr(currtr) == -1) {
+ free(currtr);
+ YYERROR;
+ }
+ currtr = NULL;
+ }
+ ;
+
+rtropt_l : rtropt
+ | rtropt_l optnl rtropt
+
+rtropt : DESCR STRING {
+ if (strlcpy(currtr->descr, $2,
+ sizeof(currtr->descr)) >=
+ sizeof(currtr->descr)) {
+ yyerror("descr \"%s\" too long: max %zu",
+ $2, sizeof(currtr->descr) - 1);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | LOCALADDR address {
+ if ($2.aid != currtr->remote_addr.aid) {
+ yyerror("Bad address family %s for "
+ "local-addr", aid2str($2.aid));
+ YYERROR;
+ }
+ currtr->local_addr = $2;
+ }
+ | PORT NUMBER {
+ if ($2 < 1 || $2 > USHRT_MAX) {
+ yyerror("local-port must be between %u and %u",
+ 1, USHRT_MAX);
+ YYERROR;
+ }
+ currtr->remote_port = $2;
+ }
+ ;
+
conf_main : AS as4number {
conf->as = $2;
if ($2 > USHRT_MAX)
@@ -2866,6 +2924,7 @@ lookup(char *s)
{ "password", PASSWORD},
{ "peer-as", PEERAS},
{ "pftable", PFTABLE},
+ { "port", PORT},
{ "prefix", PREFIX},
{ "prefix-set", PREFIXSET},
{ "prefixlen", PREFIXLEN},
@@ -2887,6 +2946,7 @@ lookup(char *s)
{ "router-id", ROUTERID},
{ "rtable", RTABLE},
{ "rtlabel", RTLABEL},
+ { "rtr", RTR},
{ "self", SELF},
{ "set", SET},
{ "socket", SOCKET },
@@ -3285,7 +3345,7 @@ init_config(struct bgpd_config *c)
}
struct bgpd_config *
-parse_config(char *filename, struct peer_head *ph)
+parse_config(char *filename, struct peer_head *ph, struct rtr_config_head *rh)
{
struct sym *sym, *next;
struct rde_rib *rr;
@@ -3309,6 +3369,7 @@ parse_config(char *filename, struct peer_head *ph)
curgroup = NULL;
cur_peers = ph;
+ cur_rtrs = rh;
new_peers = &conf->peers;
netconf = &conf->networks;
@@ -4568,3 +4629,57 @@ add_roa_set(struct prefixset_item *npsi, u_int32_t as, u_int8_t max)
/* just ignore duplicates */
free(roa);
}
+
+static struct rtr_config *
+get_rtr(struct bgpd_addr *addr)
+{
+ struct rtr_config *n;
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL) {
+ yyerror("out of memory");
+ return NULL;
+ }
+
+ n->remote_addr = *addr;
+ strlcpy(n->descr, log_addr(addr), sizeof(currtr->descr));
+
+ return n;
+}
+
+static int
+insert_rtr(struct rtr_config *new)
+{
+ static uint32_t id;
+ struct rtr_config *r;
+
+ if (id == UINT32_MAX) {
+ yyerror("out of rtr session IDs");
+ return -1;
+ }
+
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry)
+ if (memcmp(&r->remote_addr, &new->remote_addr,
+ sizeof(r->remote_addr)) == 0 &&
+ r->remote_port == new->remote_port) {
+ yyerror("duplicate rtr session to %s:%u",
+ log_addr(&new->remote_addr), new->remote_port);
+ return -1;
+ }
+
+ if (cur_rtrs)
+ SIMPLEQ_FOREACH(r, cur_rtrs, entry)
+ if (memcmp(&r->remote_addr, &new->remote_addr,
+ sizeof(r->remote_addr)) == 0 &&
+ r->remote_port == new->remote_port) {
+ new->id = r->id;
+ break;
+ }
+
+ if (new->id == 0)
+ new->id = ++id;
+
+ SIMPLEQ_INSERT_TAIL(&conf->rtrs, currtr, entry);
+
+ return 0;
+}
diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c
index 58cff608d80..122ae266689 100644
--- a/usr.sbin/bgpd/printconf.c
+++ b/usr.sbin/bgpd/printconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: printconf.c,v 1.145 2021/01/25 09:15:23 claudio Exp $ */
+/* $OpenBSD: printconf.c,v 1.146 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -42,6 +42,7 @@ void print_as_sets(struct as_set_head *);
void print_prefixsets(struct prefixset_head *);
void print_originsets(struct prefixset_head *);
void print_roa(struct roa_tree *);
+void print_rtrs(struct rtr_config_head *);
void print_peer(struct peer_config *, struct bgpd_config *,
const char *);
const char *print_auth_alg(u_int8_t);
@@ -579,6 +580,21 @@ print_roa(struct roa_tree *r)
}
void
+print_rtrs(struct rtr_config_head *rh)
+{
+ struct rtr_config *r;
+
+ SIMPLEQ_FOREACH(r, rh, entry) {
+ printf("rtr %s {\n", log_addr(&r->remote_addr));
+ printf("\tdescr \"%s\"\n", r->descr);
+ printf("\tport %u\n", r->remote_port);
+ if (r->local_addr.aid != AID_UNSPEC)
+ printf("local-addr %s\n", log_addr(&r->local_addr));
+ printf("}\n\n");
+ }
+}
+
+void
print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
{
char *method;
@@ -1010,6 +1026,7 @@ print_config(struct bgpd_config *conf, struct rib_names *rib_l)
struct l3vpn *vpn;
print_mainconf(conf);
+ print_rtrs(&conf->rtrs);
print_roa(&conf->roa);
print_as_sets(&conf->as_sets);
print_prefixsets(&conf->prefixsets);
diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c
index f720b84b444..ca6a515b412 100644
--- a/usr.sbin/bgpd/rde.c
+++ b/usr.sbin/bgpd/rde.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.c,v 1.514 2021/01/25 09:15:24 claudio Exp $ */
+/* $OpenBSD: rde.c,v 1.515 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -32,7 +32,6 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
-#include <err.h>
#include "bgpd.h"
#include "rde.h"
@@ -42,11 +41,13 @@
#define PFD_PIPE_MAIN 0
#define PFD_PIPE_SESSION 1
#define PFD_PIPE_SESSION_CTL 2
-#define PFD_PIPE_COUNT 3
+#define PFD_PIPE_ROA 3
+#define PFD_PIPE_COUNT 4
void rde_sighdlr(int);
void rde_dispatch_imsg_session(struct imsgbuf *);
void rde_dispatch_imsg_parent(struct imsgbuf *);
+void rde_dispatch_imsg_rtr(struct imsgbuf *);
void rde_dispatch_imsg_peer(struct rde_peer *, void *);
void rde_update_dispatch(struct rde_peer *, struct imsg *);
int rde_update_update(struct rde_peer *, struct filterstate *,
@@ -79,6 +80,7 @@ static void rde_softreconfig_in(struct rib_entry *, void *);
static void rde_softreconfig_sync_reeval(struct rib_entry *, void *);
static void rde_softreconfig_sync_fib(struct rib_entry *, void *);
static void rde_softreconfig_sync_done(void *, u_int8_t);
+static void rde_roa_reload(void);
static int rde_no_as_set(struct rde_peer *);
int rde_update_queue_pending(void);
void rde_update_queue_runner(void);
@@ -102,8 +104,10 @@ int ovs_match(struct prefix *, u_int32_t);
static struct imsgbuf *ibuf_se;
static struct imsgbuf *ibuf_se_ctl;
+static struct imsgbuf *ibuf_rtr;
static struct imsgbuf *ibuf_main;
static struct bgpd_config *conf, *nconf;
+static struct rde_prefixset rde_roa, roa_new;
volatile sig_atomic_t rde_quit = 0;
struct filter_head *out_rules, *out_rules_tmp;
@@ -231,6 +235,7 @@ rde_main(int debug, int verbose)
set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main);
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se);
set_pollfd(&pfd[PFD_PIPE_SESSION_CTL], ibuf_se_ctl);
+ set_pollfd(&pfd[PFD_PIPE_ROA], ibuf_rtr);
i = PFD_PIPE_COUNT;
for (mctx = LIST_FIRST(&rde_mrts); mctx != 0; mctx = xmctx) {
@@ -282,6 +287,14 @@ rde_main(int debug, int verbose)
} else
rde_dispatch_imsg_session(ibuf_se_ctl);
+ if (handle_pollfd(&pfd[PFD_PIPE_ROA], ibuf_rtr) == -1) {
+ log_warnx("RDE: Lost connection to ROA");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ } else
+ rde_dispatch_imsg_rtr(ibuf_rtr);
+
for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts);
j < i && mctx != 0; j++) {
if (pfd[j].fd == mctx->mrt.wbuf.fd &&
@@ -578,7 +591,7 @@ badnetdel:
break;
case IMSG_CTL_SHOW_SET:
/* first roa set */
- pset = &conf->rde_roa;
+ pset = &rde_roa;
memset(&cset, 0, sizeof(cset));
cset.type = ROA_SET;
strlcpy(cset.name, "RPKI ROA", sizeof(cset.name));
@@ -668,7 +681,6 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
static struct l3vpn *vpn;
struct imsg imsg;
struct mrt xmrt;
- struct roa roa;
struct rde_rib rr;
struct filterstate state;
struct imsgbuf *i;
@@ -693,15 +705,17 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
switch (imsg.hdr.type) {
case IMSG_SOCKET_CONN:
case IMSG_SOCKET_CONN_CTL:
+ case IMSG_SOCKET_CONN_RTR:
if ((fd = imsg.fd) == -1) {
- log_warnx("expected to receive imsg fd to "
- "SE but didn't receive any");
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
break;
}
if ((i = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(i, fd);
- if (imsg.hdr.type == IMSG_SOCKET_CONN) {
+ switch (imsg.hdr.type) {
+ case IMSG_SOCKET_CONN:
if (ibuf_se) {
log_warnx("Unexpected imsg connection "
"to SE received");
@@ -709,7 +723,8 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
free(ibuf_se);
}
ibuf_se = i;
- } else {
+ break;
+ case IMSG_SOCKET_CONN_CTL:
if (ibuf_se_ctl) {
log_warnx("Unexpected imsg ctl "
"connection to SE received");
@@ -717,6 +732,16 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
free(ibuf_se_ctl);
}
ibuf_se_ctl = i;
+ break;
+ case IMSG_SOCKET_CONN_RTR:
+ if (ibuf_rtr) {
+ log_warnx("Unexpected imsg ctl "
+ "connection to ROA received");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ }
+ ibuf_rtr = i;
+ break;
}
break;
case IMSG_NETWORK_ADD:
@@ -865,17 +890,6 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
}
last_prefixset = ps;
break;
- case IMSG_RECONF_ROA_SET:
- strlcpy(nconf->rde_roa.name, "RPKI ROA",
- sizeof(nconf->rde_roa.name));
- last_prefixset = &nconf->rde_roa;
- break;
- case IMSG_RECONF_ROA_ITEM:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(roa))
- fatalx("IMSG_RECONF_ROA_ITEM bad len");
- memcpy(&roa, imsg.data, sizeof(roa));
- rv = trie_roa_add(&last_prefixset->th, &roa);
- break;
case IMSG_RECONF_PREFIX_SET_ITEM:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(psi))
fatalx("IMSG_RECONF_PREFIX_SET_ITEM bad len");
@@ -886,7 +900,7 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
&psi.p.addr, psi.p.len,
psi.p.len_min, psi.p.len_max);
if (rv == -1)
- log_warnx("trie_add(%s) %s/%u) failed",
+ log_warnx("trie_add(%s) %s/%u failed",
last_prefixset->name, log_addr(&psi.p.addr),
psi.p.len);
break;
@@ -991,6 +1005,46 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf)
/* ignore end message because a dump is atomic */
break;
default:
+ fatalx("unhandled IMSG %u", imsg.hdr.type);
+ }
+ imsg_free(&imsg);
+ }
+}
+
+void
+rde_dispatch_imsg_rtr(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ struct roa roa;
+ int n;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("rde_dispatch_imsg_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_RECONF_ROA_SET:
+ /* start of update */
+ break;
+ case IMSG_RECONF_ROA_ITEM:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(roa))
+ fatalx("IMSG_RECONF_ROA_ITEM bad len");
+ memcpy(&roa, imsg.data, sizeof(roa));
+ if (trie_roa_add(&roa_new.th, &roa) != 0) {
+ struct bgpd_addr p = {
+ .aid = roa.aid,
+ .v6 = roa.prefix.inet6
+ };
+ log_warnx("trie_roa_add %s/%u failed",
+ log_addr(&p), roa.prefixlen);
+ }
+ break;
+ case IMSG_RECONF_DONE:
+ /* end of update */
+ rde_roa_reload();
break;
}
imsg_free(&imsg);
@@ -1439,7 +1493,7 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in,
const char *wmsg = "filtered, withdraw";
peer->prefix_rcvd_update++;
- vstate = rde_roa_validity(&conf->rde_roa, prefix, prefixlen,
+ vstate = rde_roa_validity(&rde_roa, prefix, prefixlen,
aspath_origin(in->aspath.aspath));
/* add original path to the Adj-RIB-In */
@@ -3153,7 +3207,6 @@ rde_reload_done(void)
struct filter_head *fh;
struct rde_prefixset_head prefixsets_old;
struct rde_prefixset_head originsets_old;
- struct rde_prefixset roa_old;
struct as_set_head as_sets_old;
u_int16_t rid;
int reload = 0;
@@ -3166,7 +3219,6 @@ rde_reload_done(void)
SIMPLEQ_CONCAT(&prefixsets_old, &conf->rde_prefixsets);
SIMPLEQ_CONCAT(&originsets_old, &conf->rde_originsets);
SIMPLEQ_CONCAT(&as_sets_old, &conf->as_sets);
- roa_old = conf->rde_roa;
/* merge the main config */
copy_config(conf, nconf);
@@ -3176,10 +3228,6 @@ rde_reload_done(void)
SIMPLEQ_CONCAT(&conf->rde_originsets, &nconf->rde_originsets);
SIMPLEQ_CONCAT(&conf->as_sets, &nconf->as_sets);
- conf->rde_roa = nconf->rde_roa;
- conf->rde_roa.lastchange = roa_old.lastchange;
- memset(&nconf->rde_roa, 0, sizeof(nconf->rde_roa));
-
/* apply new set of l3vpn, sync will be done later */
free_l3vpns(&conf->l3vpns);
SIMPLEQ_CONCAT(&conf->l3vpns, &nconf->l3vpns);
@@ -3197,16 +3245,6 @@ rde_reload_done(void)
peerself->conf.remote_masklen = 32;
peerself->short_as = conf->short_as;
- /* check if roa changed */
- if (trie_equal(&conf->rde_roa.th, &roa_old.th) == 0) {
- log_debug("roa change: reloading Adj-RIB-In");
- conf->rde_roa.dirty = 1;
- conf->rde_roa.lastchange = getmonotime();
- reload++; /* run softreconf in */
- }
-
- trie_free(&roa_old.th); /* old roa no longer needed */
-
rde_mark_prefixsets_dirty(&prefixsets_old, &conf->rde_prefixsets);
rde_mark_prefixsets_dirty(&originsets_old, &conf->rde_originsets);
as_sets_mark_dirty(&as_sets_old, &conf->as_sets);
@@ -3443,8 +3481,6 @@ rde_softreconfig_in(struct rib_entry *re, void *bula)
struct rde_aspath *asp;
enum filter_actions action;
struct bgpd_addr prefix;
- int force_eval;
- u_int8_t vstate;
u_int16_t i;
pt = re->prefix;
@@ -3452,17 +3488,6 @@ rde_softreconfig_in(struct rib_entry *re, void *bula)
LIST_FOREACH(p, &re->prefix_h, entry.list.rib) {
asp = prefix_aspath(p);
peer = prefix_peer(p);
- force_eval = 0;
-
- if (conf->rde_roa.dirty) {
- /* ROA validation state update */
- vstate = rde_roa_validity(&conf->rde_roa,
- &prefix, pt->prefixlen, aspath_origin(asp->aspath));
- if (vstate != p->validation_state) {
- force_eval = 1;
- p->validation_state = vstate;
- }
- }
/* skip announced networks, they are never filtered */
if (asp->flags & F_PREFIX_ANNOUNCED)
@@ -3473,7 +3498,7 @@ rde_softreconfig_in(struct rib_entry *re, void *bula)
if (rib == NULL)
continue;
- if (rib->state != RECONF_RELOAD && !force_eval)
+ if (rib->state != RECONF_RELOAD)
continue;
rde_filterstate_prep(&state, asp, prefix_communities(p),
@@ -3575,6 +3600,99 @@ rde_softreconfig_sync_done(void *arg, u_int8_t aid)
}
/*
+ * ROA specific functions. The roa set is updated independent of the config
+ * so this runs outside of the softreconfig handlers.
+ */
+static void
+rde_roa_softreload(struct rib_entry *re, void *bula)
+{
+ struct filterstate state;
+ struct rib *rib;
+ struct prefix *p;
+ struct pt_entry *pt;
+ struct rde_peer *peer;
+ struct rde_aspath *asp;
+ enum filter_actions action;
+ struct bgpd_addr prefix;
+ u_int8_t vstate;
+ u_int16_t i;
+
+ pt = re->prefix;
+ pt_getaddr(pt, &prefix);
+ LIST_FOREACH(p, &re->prefix_h, entry.list.rib) {
+ asp = prefix_aspath(p);
+ peer = prefix_peer(p);
+
+ /* ROA validation state update */
+ vstate = rde_roa_validity(&rde_roa,
+ &prefix, pt->prefixlen, aspath_origin(asp->aspath));
+ if (vstate == p->validation_state)
+ continue;
+ p->validation_state = vstate;
+
+ /* skip announced networks, they are never filtered */
+ if (asp->flags & F_PREFIX_ANNOUNCED)
+ continue;
+
+ for (i = RIB_LOC_START; i < rib_size; i++) {
+ rib = rib_byid(i);
+ if (rib == NULL)
+ continue;
+
+ rde_filterstate_prep(&state, asp, prefix_communities(p),
+ prefix_nexthop(p), prefix_nhflags(p));
+ action = rde_filter(rib->in_rules, peer, peer, &prefix,
+ pt->prefixlen, p->validation_state, &state);
+
+ if (action == ACTION_ALLOW) {
+ /* update Local-RIB */
+ prefix_update(rib, peer, &state, &prefix,
+ pt->prefixlen, p->validation_state);
+ } else if (action == ACTION_DENY) {
+ /* remove from Local-RIB */
+ prefix_withdraw(rib, peer, &prefix,
+ pt->prefixlen);
+ }
+
+ rde_filterstate_clean(&state);
+ }
+ }
+}
+
+static void
+rde_roa_softreload_done(void *arg, u_int8_t aid)
+{
+ /* the roa update is done */
+ log_info("ROA softreload done");
+}
+
+static void
+rde_roa_reload(void)
+{
+ struct rde_prefixset roa_old;
+
+ roa_old = rde_roa;
+ rde_roa = roa_new;
+ memset(&roa_new, 0, sizeof(roa_new));
+
+ /* check if roa changed */
+ if (trie_equal(&rde_roa.th, &roa_old.th)) {
+ rde_roa.lastchange = roa_old.lastchange;
+ trie_free(&roa_old.th); /* old roa no longer needed */
+ return;
+ }
+
+ rde_roa.lastchange = getmonotime();
+ trie_free(&roa_old.th); /* old roa no longer needed */
+
+ log_debug("ROA change: reloading Adj-RIB-In");
+ if (rib_dump_new(RIB_ADJ_IN, AID_UNSPEC, RDE_RUNNER_ROUNDS,
+ rib_byid(RIB_ADJ_IN), rde_roa_softreload,
+ rde_roa_softreload_done, NULL) == -1)
+ fatal("%s: rib_dump_new", __func__);
+}
+
+/*
* generic helper function
*/
u_int32_t
@@ -3737,7 +3855,7 @@ network_add(struct network_config *nc, struct filterstate *state)
rde_apply_set(vpnset, peerself, peerself, state,
nc->prefix.aid);
- vstate = rde_roa_validity(&conf->rde_roa, &nc->prefix,
+ vstate = rde_roa_validity(&rde_roa, &nc->prefix,
nc->prefixlen, aspath_origin(state->aspath.aspath));
if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, state, &nc->prefix,
nc->prefixlen, vstate) == 1)
diff --git a/usr.sbin/bgpd/rtr.c b/usr.sbin/bgpd/rtr.c
new file mode 100644
index 00000000000..dc4d9ca1522
--- /dev/null
+++ b/usr.sbin/bgpd/rtr.c
@@ -0,0 +1,334 @@
+/* $OpenBSD: rtr.c,v 1.1 2021/02/16 08:29:16 claudio Exp $ */
+
+/*
+ * Copyright (c) 2020 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/tree.h>
+#include <errno.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "log.h"
+
+static void rtr_dispatch_imsg_parent(struct imsgbuf *);
+static void rtr_dispatch_imsg_rde(struct imsgbuf *);
+
+volatile sig_atomic_t rtr_quit;
+static struct imsgbuf *ibuf_main;
+static struct imsgbuf *ibuf_rde;
+static struct bgpd_config *conf, *nconf;
+
+static void
+rtr_sighdlr(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ rtr_quit = 1;
+ break;
+ }
+}
+
+#define PFD_PIPE_MAIN 0
+#define PFD_PIPE_RDE 1
+#define PFD_PIPE_COUNT 2
+
+void
+roa_insert(struct roa_tree *rt, struct roa *in)
+{
+ struct roa *roa;
+
+ if ((roa = malloc(sizeof(*roa))) == NULL)
+ fatal("roa alloc");
+ memcpy(roa, in, sizeof(*roa));
+ if (RB_INSERT(roa_tree, rt, roa) != NULL)
+ /* just ignore duplicates */
+ free(roa);
+}
+
+void
+rtr_main(int debug, int verbose)
+{
+ struct passwd *pw;
+ struct pollfd *pfd = NULL;
+ void *newp;
+ size_t pfd_elms = 0, i;
+ time_t now, timeout;
+
+ log_init(debug, LOG_DAEMON);
+ log_setverbose(verbose);
+
+ log_procinit(log_procnames[PROC_RTR]);
+
+ if ((pw = getpwnam(BGPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("rtr engine");
+
+ 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");
+
+ if (pledge("stdio recvfd", NULL) == -1)
+ fatal("pledge");
+
+ signal(SIGTERM, rtr_sighdlr);
+ signal(SIGINT, rtr_sighdlr);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+
+ if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_main, 3);
+
+ conf = new_config();
+ log_info("rtr engine ready");
+
+ while (rtr_quit == 0) {
+ i = rtr_count();
+ if (pfd_elms < PFD_PIPE_COUNT + i) {
+ if ((newp = reallocarray(pfd,
+ PFD_PIPE_COUNT + i,
+ sizeof(struct pollfd))) == NULL)
+ fatal("realloc pollfd");
+ pfd = newp;
+ pfd_elms = PFD_PIPE_COUNT + i;
+ }
+ timeout = 240; /* loop every 240s at least */
+ bzero(pfd, sizeof(struct pollfd) * pfd_elms);
+
+ set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main);
+ set_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde);
+
+ now = getmonotime();
+ i = PFD_PIPE_COUNT;
+ i += rtr_poll_events(pfd + i, pfd_elms - i, &timeout);
+
+ if (poll(pfd, i, timeout * 1000) == -1) {
+ if (errno != EINTR)
+ fatal("poll error");
+ continue;
+ }
+
+ if (handle_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main) == -1)
+ fatalx("Lost connection to parent");
+ else
+ rtr_dispatch_imsg_parent(ibuf_main);
+
+ if (handle_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde) == -1) {
+ log_warnx("RTR: Lost connection to RDE");
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ ibuf_rde = NULL;
+ } else
+ rtr_dispatch_imsg_rde(ibuf_rde);
+
+ now = getmonotime();
+ i = PFD_PIPE_COUNT;
+ rtr_check_events(pfd + i, pfd_elms - i);
+ }
+
+ rtr_shutdown();
+
+ free_config(conf);
+ free(pfd);
+
+ /* close pipes */
+ if (ibuf_rde) {
+ msgbuf_clear(&ibuf_rde->w);
+ close(ibuf_rde->fd);
+ free(ibuf_rde);
+ }
+ msgbuf_clear(&ibuf_main->w);
+ close(ibuf_main->fd);
+ free(ibuf_main);
+
+ log_info("rtr engine exiting");
+ exit(0);
+}
+
+static void
+rtr_dispatch_imsg_parent(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ struct roa *roa;
+ struct rtr_session *rs;
+ int n, fd;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_SOCKET_CONN_RTR:
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
+ break;
+ }
+ if (ibuf_rde) {
+ log_warnx("Unexpected imsg ctl "
+ "connection to RDE received");
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ }
+ if ((ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_rde, fd);
+ break;
+ case IMSG_SOCKET_CONN:
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
+ break;
+ }
+ if ((rs = rtr_get(imsg.hdr.peerid)) == NULL) {
+ log_warnx("IMSG_SOCKET_CONN: unknown rtr id %d",
+ imsg.hdr.peerid);
+ break;
+ }
+ rtr_open(rs, fd);
+ break;
+ case IMSG_RECONF_CONF:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct bgpd_config))
+ fatalx("IMSG_RECONF_CONF bad len");
+ nconf = new_config();
+ copy_config(nconf, imsg.data);
+ rtr_config_prep();
+ break;
+ case IMSG_RECONF_ROA_ITEM:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(*roa))
+ fatalx("IMSG_RECONF_ROA_ITEM bad len");
+ roa_insert(&nconf->roa, imsg.data);
+ break;
+ case IMSG_RECONF_RTR_CONFIG:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != PEER_DESCR_LEN)
+ fatalx("IMSG_RECONF_RTR_CONFIG bad len");
+ rs = rtr_get(imsg.hdr.peerid);
+ if (rs == NULL)
+ rtr_new(imsg.hdr.peerid, imsg.data);
+ else
+ rtr_config_keep(rs);
+ break;
+ case IMSG_RECONF_DRAIN:
+ imsg_compose(ibuf_main, IMSG_RECONF_DRAIN, 0, 0,
+ -1, NULL, 0);
+ break;
+ case IMSG_RECONF_DONE:
+ if (nconf == NULL)
+ fatalx("got IMSG_RECONF_DONE but no config");
+ copy_config(conf, nconf);
+ /* switch the roa, first remove the old one */
+ free_roatree(&conf->roa);
+ /* then move the RB tree root */
+ RB_ROOT(&conf->roa) = RB_ROOT(&nconf->roa);
+ RB_ROOT(&nconf->roa) = NULL;
+ /* finally merge the rtr session */
+ rtr_config_merge();
+ rtr_recalc();
+ log_info("RTR engine reconfigured");
+ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
+ -1, NULL, 0);
+ free_config(nconf);
+ nconf = NULL;
+ break;
+ case IMSG_CTL_SHOW_RTR:
+ if ((rs = rtr_get(imsg.hdr.peerid)) == NULL) {
+ log_warnx("IMSG_CTL_SHOW_RTR: "
+ "unknown rtr id %d", imsg.hdr.peerid);
+ break;
+ }
+ rtr_show(rs, imsg.hdr.pid);
+ break;
+ case IMSG_CTL_END:
+ imsg_compose(ibuf_main, IMSG_CTL_END, 0, imsg.hdr.pid,
+ -1, NULL, 0);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+}
+
+static void
+rtr_dispatch_imsg_rde(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ int n;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0)
+ break;
+
+ /* NOTHING */
+
+ imsg_free(&imsg);
+ }
+}
+
+void
+rtr_imsg_compose(int type, uint32_t id, pid_t pid, void *data, size_t datalen)
+{
+ imsg_compose(ibuf_main, type, id, pid, -1, data, datalen);
+}
+
+/*
+ * Merge all RPKI ROA trees into one as one big union.
+ * Simply try to add all roa entries into a new RB tree.
+ * This could be made a fair bit faster but for now this is good enough.
+ */
+void
+rtr_recalc(void)
+{
+ struct roa_tree rt;
+ struct roa *roa, *nr;
+
+ RB_INIT(&rt);
+
+ RB_FOREACH(roa, roa_tree, &conf->roa)
+ roa_insert(&rt, roa);
+ rtr_roa_merge(&rt);
+
+ imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, NULL, 0);
+ RB_FOREACH_SAFE(roa, roa_tree, &rt, nr) {
+ RB_REMOVE(roa_tree, &rt, roa);
+ imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0, -1,
+ roa, sizeof(*roa));
+ free(roa);
+ }
+ imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0);
+}
diff --git a/usr.sbin/bgpd/rtr_proto.c b/usr.sbin/bgpd/rtr_proto.c
new file mode 100644
index 00000000000..4354a580f19
--- /dev/null
+++ b/usr.sbin/bgpd/rtr_proto.c
@@ -0,0 +1,1157 @@
+/* $OpenBSD: rtr_proto.c,v 1.1 2021/02/16 08:29:16 claudio Exp $ */
+
+/*
+ * Copyright (c) 2020 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/tree.h>
+#include <errno.h>
+#include <stdint.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "log.h"
+
+struct rtr_header {
+ uint8_t version;
+ uint8_t type;
+ uint16_t session_id; /* or error code */
+ uint32_t length;
+};
+
+#define RTR_MAX_LEN 2048
+#define RTR_DEFAULT_REFRESH 3600
+#define RTR_DEFAULT_RETRY 600
+#define RTR_DEFAULT_EXPIRE 7200
+
+enum rtr_pdu_type {
+ SERIAL_NOTIFY = 0,
+ SERIAL_QUERY,
+ RESET_QUERY,
+ CACHE_RESPONSE,
+ IPV4_PREFIX,
+ IPV6_PREFIX = 6,
+ END_OF_DATA = 7,
+ CACHE_RESET = 8,
+ ROUTER_KEY = 9,
+ ERROR_REPORT = 10,
+};
+
+#define FLAG_ANNOUNCE 0x1
+#define FLAG_MASK FLAG_ANNOUNCE
+struct rtr_ipv4 {
+ uint8_t flags;
+ uint8_t prefixlen;
+ uint8_t maxlen;
+ uint8_t zero;
+ uint32_t prefix;
+ uint32_t asnum;
+};
+
+struct rtr_ipv6 {
+ uint8_t flags;
+ uint8_t prefixlen;
+ uint8_t maxlen;
+ uint8_t zero;
+ uint32_t prefix[4];
+ uint32_t asnum;
+};
+
+struct rtr_endofdata {
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+};
+
+enum rtr_event {
+ RTR_EVNT_START,
+ RTR_EVNT_CON_OPEN,
+ RTR_EVNT_CON_CLOSED,
+ RTR_EVNT_TIMER_REFRESH,
+ RTR_EVNT_TIMER_RETRY,
+ RTR_EVNT_TIMER_EXPIRE,
+ RTR_EVNT_SEND_ERROR,
+ RTR_EVNT_SERIAL_NOTIFY,
+ RTR_EVNT_CACHE_RESPONSE,
+ RTR_EVNT_END_OF_DATA,
+ RTR_EVNT_CACHE_RESET,
+ RTR_EVNT_NO_DATA,
+};
+
+static const char *rtr_eventnames[] = {
+ "start",
+ "connection open",
+ "connection closed",
+ "refresh timer expired",
+ "retry timer expired",
+ "expire timer expired",
+ "sent error",
+ "serial notify received",
+ "cache response received",
+ "end of data received",
+ "cache reset received",
+ "no data"
+};
+
+enum rtr_state {
+ RTR_STATE_CLOSED,
+ RTR_STATE_ERROR,
+ RTR_STATE_IDLE,
+ RTR_STATE_ACTIVE,
+};
+
+static const char *rtr_statenames[] = {
+ "closed",
+ "error",
+ "idle",
+ "active"
+};
+
+struct rtr_session {
+ TAILQ_ENTRY(rtr_session) entry;
+ char descr[PEER_DESCR_LEN];
+ struct roa_tree roa_set;
+ struct ibuf_read r;
+ struct msgbuf w;
+ struct timer_head timers;
+ uint32_t id; /* rtr_config id */
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ int session_id;
+ int fd;
+ enum rtr_state state;
+ enum reconf_action reconf_action;
+ enum rtr_error last_sent_error;
+ enum rtr_error last_recv_error;
+ char last_sent_msg[REASON_LEN];
+ char last_recv_msg[REASON_LEN];
+};
+
+TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs);
+
+static void rtr_fsm(struct rtr_session *, enum rtr_event);
+
+static const char *
+log_rtr(struct rtr_session *rs)
+{
+ return rs->descr;
+}
+
+static const char *
+log_rtr_type(enum rtr_pdu_type type)
+{
+ static char buf[20];
+
+ switch (type) {
+ case SERIAL_NOTIFY:
+ return "serial notify";
+ case SERIAL_QUERY:
+ return "serial query";
+ case RESET_QUERY:
+ return "reset query";
+ case CACHE_RESPONSE:
+ return "cache response";
+ case IPV4_PREFIX:
+ return "IPv4 prefix";
+ case IPV6_PREFIX:
+ return "IPv6 prefix";
+ case END_OF_DATA:
+ return "end of data";
+ case CACHE_RESET:
+ return "cache reset";
+ case ROUTER_KEY:
+ return "router key";
+ case ERROR_REPORT:
+ return "error report";
+ default:
+ snprintf(buf, sizeof(buf), "unknown %u", type);
+ return buf;
+ }
+};
+
+static struct ibuf *
+rtr_newmsg(enum rtr_pdu_type type, uint32_t len, u_int16_t session_id)
+{
+ struct ibuf *buf;
+ struct rtr_header rh;
+
+ if (len > RTR_MAX_LEN) {
+ errno = ERANGE;
+ return NULL;
+ }
+ len += sizeof(rh);
+ if ((buf = ibuf_open(len)) == NULL)
+ return NULL;
+
+ memset(&rh, 0, sizeof(rh));
+ rh.version = 1;
+ rh.type = type;
+ rh.session_id = htons(session_id);
+ rh.length = htonl(len);
+
+ /* can not fail with fixed buffers */
+ ibuf_add(buf, &rh, sizeof(rh));
+ return buf;
+}
+
+/*
+ * Try to send an error PDU to cache, put connection into error
+ * state.
+ */
+static void
+rtr_send_error(struct rtr_session *rs, enum rtr_error err, char *msg,
+ void *pdu, size_t len)
+{
+ struct ibuf *buf;
+ size_t mlen = 0;
+ uint32_t hdrlen;
+
+ rs->last_sent_error = err;
+ if (msg) {
+ mlen = strlen(msg);
+ strlcpy(rs->last_sent_msg, msg, sizeof(rs->last_sent_msg));
+ } else
+ memset(rs->last_sent_msg, 0, sizeof(rs->last_sent_msg));
+
+ rtr_fsm(rs, RTR_EVNT_SEND_ERROR);
+
+ buf = rtr_newmsg(ERROR_REPORT, 2 * sizeof(hdrlen) + len + mlen, err);
+ if (buf == NULL) {
+ log_warn("rtr %s: send error report", log_rtr(rs));
+ return;
+ }
+
+ /* can not fail with fixed buffers */
+ hdrlen = ntohl(len);
+ ibuf_add(buf, &hdrlen, sizeof(hdrlen));
+ ibuf_add(buf, pdu, len);
+ hdrlen = ntohl(mlen);
+ ibuf_add(buf, &hdrlen, sizeof(hdrlen));
+ ibuf_add(buf, msg, mlen);
+ ibuf_close(&rs->w, buf);
+
+ log_warnx("%s: sending error report[%u] %s", log_rtr(rs), err,
+ msg ? msg : "");
+}
+
+static void
+rtr_reset_query(struct rtr_session *rs)
+{
+ struct ibuf *buf;
+
+ buf = rtr_newmsg(RESET_QUERY, 0, 0);
+ if (buf == NULL) {
+ log_warn("rtr %s: send reset query", log_rtr(rs));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return;
+ }
+ ibuf_close(&rs->w, buf);
+}
+
+static void
+rtr_serial_query(struct rtr_session *rs)
+{
+ struct ibuf *buf;
+ uint32_t s;
+
+ buf = rtr_newmsg(SERIAL_QUERY, sizeof(s), rs->session_id);
+ if (buf == NULL) {
+ log_warn("rtr %s: send serial query", log_rtr(rs));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return;
+ }
+
+ /* can not fail with fixed buffers */
+ s = htonl(rs->serial);
+ ibuf_add(buf, &s, sizeof(s));
+ ibuf_close(&rs->w, buf);
+}
+
+/*
+ * Validate the common rtr header (first 8 bytes) including the
+ * included length field.
+ * Returns -1 on failure. On success msgtype and msglen are set
+ * and the function return 0.
+ */
+static int
+rtr_parse_header(struct rtr_session *rs, void *buf,
+ size_t *msglen, enum rtr_pdu_type *msgtype)
+{
+ struct rtr_header rh;
+ uint32_t len = 16; /* default for ERROR_REPORT */
+ int session_id;
+
+ memcpy(&rh, buf, sizeof(rh));
+
+ if (rh.version != 1) {
+ log_warnx("rtr %s: received message with unsupported version",
+ log_rtr(rs));
+ rtr_send_error(rs, UNEXP_PROTOCOL_VERS, NULL, &rh, sizeof(rh));
+ return -1;
+ }
+
+ *msgtype = rh.type;
+ *msglen = ntohl(rh.length);
+
+ switch (*msgtype) {
+ case SERIAL_NOTIFY:
+ session_id = rs->session_id;
+ len = 12;
+ break;
+ case CACHE_RESPONSE:
+ /* set session_id if not yet happened */
+ if (rs->session_id == -1)
+ rs->session_id = ntohs(rh.session_id);
+ session_id = rs->session_id;
+ len = 8;
+ break;
+ case IPV4_PREFIX:
+ session_id = 0;
+ len = 20;
+ break;
+ case IPV6_PREFIX:
+ session_id = 0;
+ len = 32;
+ break;
+ case END_OF_DATA:
+ session_id = rs->session_id;
+ len = 24;
+ break;
+ case CACHE_RESET:
+ session_id = 0;
+ len = 8;
+ break;
+ case ROUTER_KEY:
+ len = 36; /* XXX probably too small, but we ignore it */
+ /* FALLTHROUGH */
+ case ERROR_REPORT:
+ if (*msglen > RTR_MAX_LEN) {
+ log_warnx("rtr %s: received %s: msg too big: %zu byte",
+ log_rtr(rs), log_rtr_type(*msgtype), *msglen);
+ rtr_send_error(rs, CORRUPT_DATA, "too big",
+ &rh, sizeof(rh));
+ return -1;
+ }
+ if (*msglen < len) {
+ log_warnx("rtr %s: received %s: msg too small: "
+ "%zu byte", log_rtr(rs), log_rtr_type(*msgtype),
+ *msglen);
+ rtr_send_error(rs, CORRUPT_DATA, "too small",
+ &rh, sizeof(rh));
+ return -1;
+ }
+ /*
+ * session_id check ommitted since ROUTER_KEY and ERROR_REPORT
+ * use the field for different things.
+ */
+ return 0;
+ default:
+ log_warnx("rtr %s: received unknown message: type %u",
+ log_rtr(rs), *msgtype);
+ rtr_send_error(rs, UNSUPP_PDU_TYPE, NULL, &rh, sizeof(rh));
+ return -1;
+ }
+
+ if (len != *msglen) {
+ log_warnx("rtr %s: received %s: illegal len: %zu byte not %u",
+ log_rtr(rs), log_rtr_type(*msgtype), *msglen, len);
+ rtr_send_error(rs, CORRUPT_DATA, "bad length",
+ &rh, sizeof(rh));
+ return -1;
+ }
+
+ if (session_id != ntohs(rh.session_id)) {
+ /* ignore SERIAL_NOTIFY during startup */
+ if (rs->session_id == -1 && *msgtype == SERIAL_NOTIFY)
+ return 0;
+
+ log_warnx("rtr %s: received %s: bad session_id: %d != %d",
+ log_rtr(rs), log_rtr_type(*msgtype), ntohs(rh.session_id),
+ session_id);
+ rtr_send_error(rs, CORRUPT_DATA, "bad session_id",
+ &rh, sizeof(rh));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+rtr_parse_notify(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state == RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: while active (ignored)",
+ log_rtr(rs), log_rtr_type(SERIAL_NOTIFY));
+ return 0;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_SERIAL_NOTIFY);
+ return 0;
+}
+
+static int
+rtr_parse_cache_response(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state != RTR_STATE_IDLE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(CACHE_RESPONSE));
+ return -1;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_CACHE_RESPONSE);
+ return 0;
+}
+
+static int
+rtr_parse_ipv4_prefix(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_ipv4 ip4;
+ struct roa *roa;
+
+ if (len != sizeof(struct rtr_header) + sizeof(ip4)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, "bad len", buf, len);
+ return -1;
+ }
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, NULL, buf, len);
+ return -1;
+ }
+
+ memcpy(&ip4, buf + sizeof(struct rtr_header), sizeof(ip4));
+
+ if ((roa = calloc(1, sizeof(*roa))) == NULL) {
+ log_warn("rtr %s: received %s",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return -1;
+ }
+ roa->aid = AID_INET;
+ roa->prefixlen = ip4.prefixlen;
+ roa->maxlen = ip4.maxlen;
+ roa->asnum = ntohl(ip4.asnum);
+ roa->prefix.inet.s_addr = ip4.prefix;
+
+ if (ip4.flags & FLAG_ANNOUNCE) {
+ if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
+ log_warnx("rtr %s: received %s: duplicate announcement",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, DUP_REC_RECV, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ } else {
+ struct roa *r;
+
+ r = RB_FIND(roa_tree, &rs->roa_set, roa);
+ if (r == NULL) {
+ log_warnx("rtr %s: received %s: unknown withdrawl",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, UNK_REC_WDRAWL, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ RB_REMOVE(roa_tree, &rs->roa_set, r);
+ free(r);
+ free(roa);
+ }
+
+ return 0;
+}
+
+static int
+rtr_parse_ipv6_prefix(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_ipv6 ip6;
+ struct roa *roa;
+
+ if (len != sizeof(struct rtr_header) + sizeof(ip6)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, "bad len", buf, len);
+ return -1;
+ }
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, NULL, buf, len);
+ return -1;
+ }
+
+ memcpy(&ip6, buf + sizeof(struct rtr_header), sizeof(ip6));
+
+ if ((roa = calloc(1, sizeof(*roa))) == NULL) {
+ log_warn("rtr %s: received %s",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return -1;
+ }
+ roa->aid = AID_INET6;
+ roa->prefixlen = ip6.prefixlen;
+ roa->maxlen = ip6.maxlen;
+ roa->asnum = ntohl(ip6.asnum);
+ memcpy(&roa->prefix.inet6, ip6.prefix, sizeof(roa->prefix.inet6));
+
+ if (ip6.flags & FLAG_ANNOUNCE) {
+ if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
+ log_warnx("rtr %s: received %s: duplicate announcement",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, DUP_REC_RECV, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ } else {
+ struct roa *r;
+
+ r = RB_FIND(roa_tree, &rs->roa_set, roa);
+ if (r == NULL) {
+ log_warnx("rtr %s: received %s: unknown withdrawl",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, UNK_REC_WDRAWL, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ RB_REMOVE(roa_tree, &rs->roa_set, r);
+ free(r);
+ free(roa);
+ }
+ return 0;
+}
+
+static int
+rtr_parse_end_of_data(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_endofdata eod;
+ uint32_t t;
+
+ buf += sizeof(struct rtr_header);
+ len -= sizeof(struct rtr_header);
+
+ if (len != sizeof(eod)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+ }
+
+ memcpy(&eod, buf, sizeof(eod));
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+ }
+
+ rs->serial = ntohl(eod.serial);
+ /* validate timer values to be in the right range */
+ t = ntohl(eod.refresh);
+ if (t < 1 || t > 86400)
+ goto bad;
+ rs->refresh = t;
+ t = ntohl(eod.retry);
+ if (t < 1 || t > 7200)
+ goto bad;
+ rs->retry = t;
+ t = ntohl(eod.expire);
+ if (t < 600 || t > 172800)
+ goto bad;
+ if (t <= rs->retry || t <= rs->refresh)
+ goto bad;
+ rs->expire = t;
+
+ rtr_fsm(rs, RTR_EVNT_END_OF_DATA);
+ return 0;
+
+bad:
+ log_warnx("rtr %s: received %s: bad timeout values",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+}
+
+static int
+rtr_parse_cache_reset(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state != RTR_STATE_IDLE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(CACHE_RESET));
+ return -1;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_CACHE_RESET);
+ return 0;
+}
+
+/*
+ * Parse an Error Response message. This function behaves a bit different
+ * from other parse functions since on error the connection needs to be
+ * dropped without sending an error response back.
+ */
+static int
+rtr_parse_error(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_header rh;
+ uint32_t pdu_len, msg_len;
+ uint8_t *msg;
+ char *str = NULL;
+ uint16_t errcode;
+
+ memcpy(&rh, buf, sizeof(rh));
+ buf += sizeof(struct rtr_header);
+ len -= sizeof(struct rtr_header);
+ errcode = ntohs(rh.session_id);
+
+ memcpy(&pdu_len, buf, sizeof(pdu_len));
+ pdu_len = ntohs(pdu_len);
+
+ if (len < pdu_len + sizeof(pdu_len)) {
+ log_warnx("rtr %s: received %s: bad pdu len: %u byte",
+ log_rtr(rs), log_rtr_type(ERROR_REPORT), pdu_len);
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return -1;
+ }
+
+ /* for now just ignore the embedded pdu */
+ buf += pdu_len + sizeof(pdu_len);
+ len -= pdu_len + sizeof(pdu_len);
+
+ memcpy(&msg_len, buf, sizeof(msg_len));
+ msg_len = ntohs(msg_len);
+
+ if (len < msg_len + sizeof(msg_len)) {
+ log_warnx("rtr %s: received %s: bad msg len: %u byte",
+ log_rtr(rs), log_rtr_type(ERROR_REPORT), msg_len);
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return -1;
+ }
+
+ msg = buf + sizeof(msg_len);
+ if (msg_len != 0)
+ /* optional error msg, no need to check for failure */
+ str = strndup(msg, msg_len);
+
+ log_warnx("rtr %s: received error: %s%s%s", log_rtr(rs),
+ log_rtr_error(errcode), str ? ": " : "", str ? str : "");
+
+ if (errcode == NO_DATA_AVAILABLE) {
+ rtr_fsm(rs, RTR_EVNT_NO_DATA);
+ } else {
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ rs->last_recv_error = errcode;
+ if (str)
+ strlcpy(rs->last_recv_msg, str,
+ sizeof(rs->last_recv_msg));
+ else
+ memset(rs->last_recv_msg, 0,
+ sizeof(rs->last_recv_msg));
+
+ free(str);
+ return -1;
+ }
+ free(str);
+
+ return 0;
+}
+
+/*
+ * Try to process received rtr message, it is possible that not a full
+ * message is in the buffer. In that case stop, once new data is available
+ * a retry will be done.
+ */
+static void
+rtr_process_msg(struct rtr_session *rs)
+{
+ size_t rpos, av, left;
+ void *rptr;
+ size_t msglen;
+ enum rtr_pdu_type msgtype;
+
+ rpos = 0;
+ av = rs->r.wpos;
+
+ for (;;) {
+ if (rpos + sizeof(struct rtr_header) > av)
+ break;
+ rptr = rs->r.buf + rpos;
+ if (rtr_parse_header(rs, rptr, &msglen, &msgtype) == -1)
+ return;
+
+ /* missing data */
+ if (rpos + msglen > av)
+ break;
+
+ switch (msgtype) {
+ case SERIAL_NOTIFY:
+ if (rtr_parse_notify(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case CACHE_RESPONSE:
+ if (rtr_parse_cache_response(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case IPV4_PREFIX:
+ if (rtr_parse_ipv4_prefix(rs, rptr, msglen) == -1) {
+ return;
+ }
+ break;
+ case IPV6_PREFIX:
+ if (rtr_parse_ipv6_prefix(rs, rptr, msglen) == -1) {
+ return;
+ }
+ break;
+ case END_OF_DATA:
+ if (rtr_parse_end_of_data(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case CACHE_RESET:
+ if (rtr_parse_cache_reset(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case ROUTER_KEY:
+ /* silently ignore router key */
+ break;
+ case ERROR_REPORT:
+ if (rtr_parse_error(rs, rptr, msglen) == -1)
+ /* no need to send back an error */
+ return;
+ break;
+ default:
+ log_warnx("rtr %s: received %s: unexpected pdu type",
+ log_rtr(rs), log_rtr_type(msgtype));
+ rtr_send_error(rs, INVALID_REQUEST, NULL, rptr, msglen);
+ return;
+ }
+ rpos += msglen;
+ }
+
+ left = av - rpos;
+ memmove(&rs->r.buf, rs->r.buf + rpos, left);
+ rs->r.wpos = left;
+}
+
+/*
+ * Simple FSM for RTR sessions
+ */
+static void
+rtr_fsm(struct rtr_session *rs, enum rtr_event event)
+{
+ enum rtr_state prev_state = rs->state;
+
+ switch (event) {
+ case RTR_EVNT_CON_CLOSED:
+ if (rs->state != RTR_STATE_CLOSED) {
+ /* flush buffers */
+ msgbuf_clear(&rs->w);
+ rs->r.wpos = 0;
+ close(rs->fd);
+ rs->fd = -1;
+ }
+ rs->state = RTR_STATE_CLOSED;
+ /* try to reopen session */
+ timer_set(&rs->timers, Timer_Rtr_Retry,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_START:
+ case RTR_EVNT_TIMER_RETRY:
+ switch (rs->state) {
+ case RTR_STATE_ERROR:
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ case RTR_STATE_CLOSED:
+ timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
+ rtr_imsg_compose(IMSG_SOCKET_CONN, rs->id, 0, NULL, 0);
+ return;
+ default:
+ break;
+ }
+ /* FALLTHROUGH */
+ case RTR_EVNT_CON_OPEN:
+ timer_stop(&rs->timers, Timer_Rtr_Retry);
+ if (rs->session_id == -1)
+ rtr_reset_query(rs);
+ else
+ rtr_serial_query(rs);
+ break;
+ case RTR_EVNT_SERIAL_NOTIFY:
+ /* schedule a refresh after a quick wait */
+ timer_set(&rs->timers, Timer_Rtr_Refresh,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_TIMER_REFRESH:
+ /* send serial query */
+ rtr_serial_query(rs);
+ break;
+ case RTR_EVNT_TIMER_EXPIRE:
+ free_roatree(&rs->roa_set);
+ rtr_recalc();
+ break;
+ case RTR_EVNT_CACHE_RESPONSE:
+ rs->state = RTR_STATE_ACTIVE;
+ timer_stop(&rs->timers, Timer_Rtr_Refresh);
+ timer_stop(&rs->timers, Timer_Rtr_Retry);
+ break;
+ case RTR_EVNT_END_OF_DATA:
+ /* start refresh and expire timers */
+ timer_set(&rs->timers, Timer_Rtr_Refresh, rs->refresh);
+ timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
+ rs->state = RTR_STATE_IDLE;
+ rtr_recalc();
+ break;
+ case RTR_EVNT_CACHE_RESET:
+ /* reset session and retry after a quick wait */
+ rs->session_id = -1;
+ free_roatree(&rs->roa_set);
+ timer_set(&rs->timers, Timer_Rtr_Retry,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_NO_DATA:
+ /* start retry timer */
+ timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
+ /* stop refresh timer just to be sure */
+ timer_stop(&rs->timers, Timer_Rtr_Refresh);
+ break;
+ case RTR_EVNT_SEND_ERROR:
+ rs->state = RTR_STATE_ERROR;
+ /* flush receive buffer */
+ rs->r.wpos = 0;
+ break;
+ }
+
+ log_info("rtr %s: state change %s -> %s, reason: %s",
+ log_rtr(rs), rtr_statenames[prev_state], rtr_statenames[rs->state],
+ rtr_eventnames[event]);
+}
+
+/*
+ * IO handler for RTR sessions
+ */
+static void
+rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs)
+{
+ ssize_t n;
+ int error;
+
+ if (pfd->revents & POLLHUP) {
+ log_warnx("rtr %s: Connection closed, hangup", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (pfd->revents & (POLLERR|POLLNVAL)) {
+ log_warnx("rtr %s: Connection closed, error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (pfd->revents & POLLOUT && rs->w.queued) {
+ if ((error = ibuf_write(&rs->w)) <= 0 && errno != EAGAIN) {
+ if (error == 0)
+ log_warnx("rtr %s: Connection closed",
+ log_rtr(rs));
+ else if (error == -1)
+ log_warn("rtr %s: write error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (rs->w.queued == 0 && rs->state == RTR_STATE_ERROR)
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+ if (pfd->revents & POLLIN) {
+ if ((n = read(rs->fd, rs->r.buf + rs->r.wpos,
+ sizeof(rs->r.buf) - rs->r.wpos)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("rtr %s: read error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+ return;
+ }
+ if (n == 0) {
+ log_warnx("rtr %s: Connection closed", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ rs->r.wpos += n;
+
+ /* new data arrived, try to process it */
+ rtr_process_msg(rs);
+ }
+
+}
+
+void
+rtr_check_events(struct pollfd *pfds, size_t npfds)
+{
+ struct rtr_session *rs;
+ struct timer *t;
+ time_t now;
+ size_t i = 0;
+
+ for (i = 0; i < npfds; i++) {
+ if (pfds[i].revents == 0)
+ continue;
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if (rs->fd == pfds[i].fd) {
+ rtr_dispatch_msg(&pfds[i], rs);
+ break;
+ }
+ if (rs == NULL)
+ log_warnx("%s: unknown fd in pollfds", __func__);
+ }
+
+ /* run all timers */
+ now = getmonotime();
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if ((t = timer_nextisdue(&rs->timers, now)) != NULL) {
+ log_debug("rtr %s: %s triggered", log_rtr(rs),
+ timernames[t->type]);
+ /* stop timer so it does not trigger again */
+ timer_stop(&rs->timers, t->type);
+ switch (t->type) {
+ case Timer_Rtr_Refresh:
+ rtr_fsm(rs, RTR_EVNT_TIMER_REFRESH);
+ break;
+ case Timer_Rtr_Retry:
+ rtr_fsm(rs, RTR_EVNT_TIMER_RETRY);
+ break;
+ case Timer_Rtr_Expire:
+ rtr_fsm(rs, RTR_EVNT_TIMER_EXPIRE);
+ break;
+ default:
+ fatalx("King Bula lost in time");
+ }
+ }
+}
+
+size_t
+rtr_count(void)
+{
+ struct rtr_session *rs;
+ size_t count = 0;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ count++;
+ return count;
+}
+
+size_t
+rtr_poll_events(struct pollfd *pfds, size_t npfds, time_t *timeout)
+{
+ struct rtr_session *rs;
+ time_t now = getmonotime();
+ size_t i = 0;
+
+ TAILQ_FOREACH(rs, &rtrs, entry) {
+ time_t nextaction;
+ struct pollfd *pfd = pfds + i++;
+
+ if (i > npfds)
+ fatalx("%s: too many sessions for pollfd", __func__);
+
+ if ((nextaction = timer_nextduein(&rs->timers, now)) != -1 &&
+ nextaction < *timeout)
+ *timeout = nextaction;
+
+ if (rs->state == RTR_STATE_CLOSED) {
+ pfd->fd = -1;
+ continue;
+ }
+
+ pfd->fd = rs->fd;
+ pfd->events = 0;
+
+ if (rs->w.queued)
+ pfd->events |= POLLOUT;
+ if (rs->state >= RTR_STATE_IDLE)
+ pfd->events |= POLLIN;
+ }
+
+ return i;
+}
+
+struct rtr_session *
+rtr_new(uint32_t id, char *descr)
+{
+ struct rtr_session *rs;
+
+ if ((rs = calloc(1, sizeof(*rs))) == NULL)
+ fatal("RTR session %s", descr);
+
+ RB_INIT(&rs->roa_set);
+ TAILQ_INIT(&rs->timers);
+ msgbuf_init(&rs->w);
+
+ strlcpy(rs->descr, descr, sizeof(rs->descr));
+ rs->id = id;
+ rs->session_id = -1;
+ rs->refresh = RTR_DEFAULT_REFRESH;
+ rs->retry = RTR_DEFAULT_RETRY;
+ rs->expire = RTR_DEFAULT_EXPIRE;
+ rs->state = RTR_STATE_CLOSED;
+ rs->reconf_action = RECONF_REINIT;
+ rs->last_recv_error = NO_ERROR;
+ rs->last_sent_error = NO_ERROR;
+
+ /* make sure that some timer is running to abort bad sessions */
+ timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
+
+ log_debug("rtr %s: new session, start", log_rtr(rs));
+ TAILQ_INSERT_TAIL(&rtrs, rs, entry);
+ rtr_fsm(rs, RTR_EVNT_START);
+
+ return rs;
+}
+
+struct rtr_session *
+rtr_get(uint32_t id)
+{
+ struct rtr_session *rs;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if (rs->id == id)
+ return rs;
+ return NULL;
+}
+
+void
+rtr_free(struct rtr_session *rs)
+{
+ if (rs == NULL)
+ return;
+
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ timer_remove_all(&rs->timers);
+ free_roatree(&rs->roa_set);
+ free(rs);
+}
+
+void
+rtr_open(struct rtr_session *rs, int fd)
+{
+ if (rs->state != RTR_STATE_CLOSED) {
+ log_warnx("rtr %s: bad session state", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+
+ log_debug("rtr %s: connection opened", log_rtr(rs));
+
+ rs->fd = rs->w.fd = fd;
+ rs->state = RTR_STATE_IDLE;
+ rtr_fsm(rs, RTR_EVNT_CON_OPEN);
+}
+
+void
+rtr_config_prep(void)
+{
+ struct rtr_session *rs;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ rs->reconf_action = RECONF_DELETE;
+}
+
+void
+rtr_config_merge(void)
+{
+ struct rtr_session *rs, *nrs;
+
+ TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
+ if (rs->reconf_action == RECONF_DELETE) {
+ TAILQ_REMOVE(&rtrs, rs, entry);
+ rtr_free(rs);
+ }
+}
+
+void
+rtr_config_keep(struct rtr_session *rs)
+{
+ rs->reconf_action = RECONF_KEEP;
+}
+
+void
+rtr_roa_merge(struct roa_tree *rt)
+{
+ struct rtr_session *rs;
+ struct roa *roa;
+
+ TAILQ_FOREACH(rs, &rtrs, entry) {
+ RB_FOREACH(roa, roa_tree, &rs->roa_set)
+ roa_insert(rt, roa);
+ }
+}
+
+void
+rtr_shutdown(void)
+{
+ struct rtr_session *rs, *nrs;
+
+ TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
+ rtr_free(rs);
+}
+
+void
+rtr_show(struct rtr_session *rs, pid_t pid)
+{
+ struct ctl_show_rtr msg;
+ struct ctl_timer ct;
+ u_int i;
+ time_t d;
+
+ memset(&msg, 0, sizeof(msg));
+
+ /* descr, remote_addr, local_addr and remote_port set by parent */
+ msg.serial = rs->serial;
+ msg.refresh = rs->refresh;
+ msg.retry = rs->retry;
+ msg.expire = rs->expire;
+ msg.session_id = rs->session_id;
+ msg.last_sent_error = rs->last_sent_error;
+ msg.last_recv_error = rs->last_recv_error;
+ strlcpy(msg.last_sent_msg, rs->last_sent_msg,
+ sizeof(msg.last_sent_msg));
+ strlcpy(msg.last_recv_msg, rs->last_recv_msg,
+ sizeof(msg.last_recv_msg));
+
+ /* send back imsg */
+ rtr_imsg_compose(IMSG_CTL_SHOW_RTR, rs->id, pid, &msg, sizeof(msg));
+
+ /* send back timer imsgs */
+ for (i = 1; i < Timer_Max; i++) {
+ if (!timer_running(&rs->timers, i, &d))
+ continue;
+ ct.type = i;
+ ct.val = d;
+ rtr_imsg_compose(IMSG_CTL_SHOW_TIMER, rs->id, pid,
+ &ct, sizeof(ct));
+ }
+}
diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c
index 93db42d13b2..6539101ede7 100644
--- a/usr.sbin/bgpd/session.c
+++ b/usr.sbin/bgpd/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.410 2021/01/05 10:02:44 claudio Exp $ */
+/* $OpenBSD: session.c,v 1.411 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
@@ -2829,6 +2829,8 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt)
case IMSG_CTL_SHOW_NEXTHOP:
case IMSG_CTL_SHOW_INTERFACE:
case IMSG_CTL_SHOW_FIB_TABLES:
+ case IMSG_CTL_SHOW_RTR:
+ case IMSG_CTL_SHOW_TIMER:
if (idx != PFD_PIPE_MAIN)
fatalx("ctl kroute request not from parent");
control_imsg_relay(&imsg);
diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h
index c3c85307ce6..ddf1caf4ab4 100644
--- a/usr.sbin/bgpd/session.h
+++ b/usr.sbin/bgpd/session.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.h,v 1.149 2020/12/23 13:20:48 claudio Exp $ */
+/* $OpenBSD: session.h,v 1.150 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -186,6 +186,9 @@ enum Timer {
Timer_IdleHoldReset,
Timer_CarpUndemote,
Timer_RestartTimeout,
+ Timer_Rtr_Refresh,
+ Timer_Rtr_Retry,
+ Timer_Rtr_Expire,
Timer_Max
};
@@ -281,9 +284,6 @@ void mrt_dump_state(struct mrt *, u_int16_t, u_int16_t,
struct peer *);
void mrt_done(struct mrt *);
-/* parse.y */
-struct bgpd_config *parse_config(char *, struct peer_head *);
-
/* pfkey.c */
int pfkey_read(int, struct sadb_msg *);
int pfkey_establish(struct peer *);
@@ -299,7 +299,30 @@ void tcp_md5_del_listener(struct bgpd_config *, struct peer *);
void print_config(struct bgpd_config *, struct rib_names *);
/* rde.c */
-void rde_main(int, int);
+void rde_main(int, int);
+
+/* rtr_proto.c */
+struct rtr_session;
+size_t rtr_count(void);
+void rtr_check_events(struct pollfd *, size_t);
+size_t rtr_poll_events(struct pollfd *, size_t, time_t *);
+struct rtr_session *rtr_new(uint32_t, char *);
+struct rtr_session *rtr_get(uint32_t);
+void rtr_free(struct rtr_session *);
+void rtr_open(struct rtr_session *, int);
+struct roa_tree *rtr_get_roa(struct rtr_session *);
+void rtr_config_prep(void);
+void rtr_config_merge(void);
+void rtr_config_keep(struct rtr_session *);
+void rtr_roa_merge(struct roa_tree *);
+void rtr_shutdown(void);
+void rtr_show(struct rtr_session *, pid_t);
+
+/* rtr.c */
+void roa_insert(struct roa_tree *, struct roa *);
+void rtr_main(int, int);
+void rtr_imsg_compose(int, uint32_t, pid_t, void *, size_t);
+void rtr_recalc(void);
/* session.c */
RB_PROTOTYPE(peer_head, peer, entry, peer_compare);
diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c
index 5d677b0c605..09d82e402e9 100644
--- a/usr.sbin/bgpd/util.c
+++ b/usr.sbin/bgpd/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.60 2021/01/25 09:15:24 claudio Exp $ */
+/* $OpenBSD: util.c,v 1.61 2021/02/16 08:29:16 claudio Exp $ */
/*
* Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
@@ -162,6 +162,38 @@ log_reason(const char *communication) {
}
const char *
+log_rtr_error(enum rtr_error err)
+{
+ static char buf[20];
+
+ switch (err) {
+ case NO_ERROR:
+ return "No Error";
+ case CORRUPT_DATA:
+ return "Corrupt Data";
+ case INTERNAL_ERROR:
+ return "Internal Error";
+ case NO_DATA_AVAILABLE:
+ return "No Data Available";
+ case INVALID_REQUEST:
+ return "Invalid Request";
+ case UNSUPP_PROTOCOL_VERS:
+ return "Unsupported Protocol Version";
+ case UNSUPP_PDU_TYPE:
+ return "Unsupported PDU Type";
+ case UNK_REC_WDRAWL:
+ return "Withdrawl of Unknown Record";
+ case DUP_REC_RECV:
+ return "Duplicate Announcement Received";
+ case UNEXP_PROTOCOL_VERS:
+ return "Unexpected Protocol Version";
+ default:
+ snprintf(buf, sizeof(buf), "unknown %u", err);
+ return buf;
+ }
+}
+
+const char *
aspath_delim(u_int8_t seg_type, int closing)
{
static char db[8];