diff options
-rw-r--r-- | usr.sbin/bgpd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.8 | 12 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.c | 202 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 37 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 67 | ||||
-rw-r--r-- | usr.sbin/bgpd/config.c | 22 | ||||
-rw-r--r-- | usr.sbin/bgpd/control.c | 3 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 121 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 19 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 224 | ||||
-rw-r--r-- | usr.sbin/bgpd/rtr.c | 334 | ||||
-rw-r--r-- | usr.sbin/bgpd/rtr_proto.c | 1157 | ||||
-rw-r--r-- | usr.sbin/bgpd/session.c | 4 | ||||
-rw-r--r-- | usr.sbin/bgpd/session.h | 33 | ||||
-rw-r--r-- | usr.sbin/bgpd/util.c | 34 |
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]; |