Hi, Here is a patch to add RPKI/ROA support to bgpd(8) (based on RFC6810). (bgpctl(8) patch in the next mail)
Can someone have a look and tell me what is wrong and what must be and how to improved ? * What needs to be done (in no specific order) : - Do some benchmark - Improve error handling - Add support for multiple validators and preference setting - Add transports (TLS/TCP-MD5/SSH) - Load cache from a file (job@'s request) * How does it work : The patch creates a new thread (ROA engine, roa.{c,h}) when bgpd(8) starts. If no 'validator' directive is found in bgpd.conf(5), the ROA engine does basically nothing. If such a directive is found, ROA engine (ROAE) will connect to the RPKI/ROA cache validator and get a list of validated prefix is everything is fine. ROAE will inform RDE that it can send prefixes to be validated. Upon prefix reception, ROAE will return a status (VALID, INVALID, NOT FOUND) to RDE which in turn will update the RIB according to filter rules. If ROAE loses its validated prefix list, it will tell RDE to reset prefix validation status to NOT FOUND. * Here is a sample bgpd.conf(5) : --- AS 65531 router-id 192.168.10.20 network 198.168.55.0/24 neighbor "192.168.10.21" { remote-as 60983 } neighbor "2a00:6060:1::10:21" { remote-as 60983 } validator 2a02:cdc5:9715:0:185:5:200:241 port 8282 allow from any allow to any match from any roa-state invalid set localpref 50 match from any roa-state valid set localpref 110 --- * Patch : Index: Makefile =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v retrieving revision 1.32 diff -u -p -r1.32 Makefile --- Makefile 21 Aug 2017 14:43:33 -0000 1.32 +++ Makefile 26 Aug 2017 19:48:24 -0000 @@ -4,7 +4,8 @@ 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 printconf.c \ - rde_filter.c pftable.c name2id.c util.c carp.c timer.c + rde_filter.c pftable.c name2id.c util.c carp.c timer.c \ + roa.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations Index: bgpd.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v retrieving revision 1.191 diff -u -p -r1.191 bgpd.c --- bgpd.c 12 Aug 2017 16:31:09 -0000 1.191 +++ bgpd.c 26 Aug 2017 19:48:24 -0000 @@ -35,6 +35,7 @@ #include "bgpd.h" #include "mrt.h" +#include "roa.h" #include "session.h" #include "log.h" @@ -43,10 +44,12 @@ __dead void usage(void); int main(int, char *[]); pid_t start_child(enum bgpd_process, char *, int, int, int); int send_filterset(struct imsgbuf *, struct filter_set_head *); -int reconfigure(char *, struct bgpd_config *, struct peer **); +int reconfigure(char *, struct bgpd_config *, struct peer **, + struct validator **); int dispatch_imsg(struct imsgbuf *, int, struct bgpd_config *); int control_setup(struct bgpd_config *); -int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *); +int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *, + struct imsgbuf *); int cflags; volatile sig_atomic_t mrtdump; @@ -56,6 +59,7 @@ pid_t reconfpid; int reconfpending; struct imsgbuf *ibuf_se; struct imsgbuf *ibuf_rde; +struct imsgbuf *ibuf_roa; struct rib_names ribnames = SIMPLEQ_HEAD_INITIALIZER(ribnames); char *cname; char *rcname; @@ -90,8 +94,9 @@ usage(void) #define PFD_PIPE_SESSION 0 #define PFD_PIPE_ROUTE 1 -#define PFD_SOCK_ROUTE 2 -#define POLL_MAX 3 +#define PFD_PIPE_ROA 2 +#define PFD_SOCK_ROUTE 3 +#define POLL_MAX 4 #define MAX_TIMEOUT 3600 int cmd_opts; @@ -101,16 +106,18 @@ main(int argc, char *argv[]) { struct bgpd_config *conf; struct peer *peer_l, *p; + struct validator *validator_l, *v; struct pollfd pfd[POLL_MAX]; - pid_t io_pid = 0, rde_pid = 0, pid; + pid_t io_pid = 0, rde_pid = 0, roa_pid = 0, pid; char *conffile; char *saved_argv0; int debug = 0; - int rflag = 0, sflag = 0; + int rflag = 0, sflag = 0, aflag = 0; int rfd = -1; int ch, timeout, status; int pipe_m2s[2]; int pipe_m2r[2]; + int pipe_m2a[2]; conffile = CONFFILE; bgpd_process = PROC_MAIN; @@ -125,8 +132,9 @@ main(int argc, char *argv[]) conf = new_config(); peer_l = NULL; + validator_l = NULL; - while ((ch = getopt(argc, argv, "cdD:f:nRSv")) != -1) { + while ((ch = getopt(argc, argv, "AcdD:f:nRSv")) != -1) { switch (ch) { case 'c': cmd_opts |= BGPD_OPT_FORCE_DEMOTE; @@ -150,6 +158,9 @@ main(int argc, char *argv[]) cmd_opts |= BGPD_OPT_VERBOSE2; cmd_opts |= BGPD_OPT_VERBOSE; break; + case 'A': + aflag = 1; + break; case 'R': rflag = 1; break; @@ -164,16 +175,17 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - if (argc > 0 || (sflag && rflag)) + if (argc > 0 || (sflag && rflag && aflag)) usage(); if (cmd_opts & BGPD_OPT_NOACTION) { - if (parse_config(conffile, conf, &peer_l)) + if (parse_config(conffile, conf, &peer_l, &validator_l)) exit(1); if (cmd_opts & BGPD_OPT_VERBOSE) print_config(conf, &ribnames, &conf->networks, peer_l, - conf->filters, conf->mrt, &conf->rdomains); + validator_l, conf->filters, conf->mrt, + &conf->rdomains); else fprintf(stderr, "configuration OK\n"); exit(0); @@ -183,6 +195,8 @@ main(int argc, char *argv[]) rde_main(debug, cmd_opts & BGPD_OPT_VERBOSE); else if (sflag) session_main(debug, cmd_opts & BGPD_OPT_VERBOSE); + else if (aflag) + roa_main(debug, cmd_opts & BGPD_OPT_VERBOSE); if (geteuid()) errx(1, "need root privileges"); @@ -204,12 +218,17 @@ main(int argc, char *argv[]) if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, PF_UNSPEC, pipe_m2r) == -1) fatal("socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_m2a) == -1) + fatal("socketpair"); /* fork children */ rde_pid = start_child(PROC_RDE, saved_argv0, pipe_m2r[1], debug, cmd_opts & BGPD_OPT_VERBOSE); io_pid = start_child(PROC_SE, saved_argv0, pipe_m2s[1], debug, cmd_opts & BGPD_OPT_VERBOSE); + roa_pid = start_child(PROC_ROA, saved_argv0, pipe_m2a[1], debug, + cmd_opts & BGPD_OPT_VERBOSE); signal(SIGTERM, sighdlr); signal(SIGINT, sighdlr); @@ -219,10 +238,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_roa = malloc(sizeof(struct imsgbuf))) == NULL) fatal(NULL); imsg_init(ibuf_se, pipe_m2s[0]); imsg_init(ibuf_rde, pipe_m2r[0]); + imsg_init(ibuf_roa, pipe_m2a[0]); mrt_init(ibuf_rde, ibuf_se); if ((rfd = kr_init()) == -1) quit = 1; @@ -246,9 +267,9 @@ BROKEN if (pledge("stdio rpath wpath cpa fatal("pledge"); #endif - if (imsg_send_sockets(ibuf_se, ibuf_rde)) + if (imsg_send_sockets(ibuf_se, ibuf_rde, ibuf_roa)) fatal("could not establish imsg links"); - quit = reconfigure(conffile, conf, &peer_l); + quit = reconfigure(conffile, conf, &peer_l, &validator_l); if (pftable_clear_all() != 0) quit = 1; @@ -257,6 +278,7 @@ BROKEN if (pledge("stdio rpath wpath cpa set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se); set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde); + set_pollfd(&pfd[PFD_PIPE_ROA], ibuf_roa); pfd[PFD_SOCK_ROUTE].fd = rfd; pfd[PFD_SOCK_ROUTE].events = POLLIN; @@ -295,6 +317,18 @@ BROKEN if (pledge("stdio rpath wpath cpa quit = 1; } + if (handle_pollfd(&pfd[PFD_PIPE_ROA], ibuf_roa) == -1) { + log_warnx("main: Lost connection to ROA"); + msgbuf_clear(&ibuf_roa->w); + free(ibuf_roa); + ibuf_roa = NULL; + quit = 1; + } else { + if (dispatch_imsg(ibuf_roa, PFD_PIPE_ROA, conf) == + -1) + quit = 1; + } + if (pfd[PFD_SOCK_ROUTE].revents & POLLIN) { if (kr_dispatch_msg() == -1) quit = 1; @@ -304,7 +338,8 @@ BROKEN if (pledge("stdio rpath wpath cpa u_int error; reconfig = 0; - switch (reconfigure(conffile, conf, &peer_l)) { + switch (reconfigure(conffile, conf, &peer_l, + &validator_l)) { case -1: /* fatal error */ quit = 1; break; @@ -342,11 +377,20 @@ BROKEN if (pledge("stdio rpath wpath cpa close(ibuf_rde->fd); free(ibuf_rde); } + if (ibuf_roa) { + msgbuf_clear(&ibuf_roa->w); + close(ibuf_roa->fd); + free(ibuf_roa); + } while ((p = peer_l) != NULL) { peer_l = p->next; free(p); } + while ((v = validator_l) != NULL) { + validator_l = v->next; + free(v); + } control_cleanup(conf->csock); control_cleanup(conf->rcsock); @@ -405,6 +449,9 @@ start_child(enum bgpd_process p, char *a case PROC_SE: argv[argc++] = "-S"; break; + case PROC_ROA: + argv[argc++] = "-A"; + break; } if (debug) argv[argc++] = "-d"; @@ -429,9 +476,11 @@ send_filterset(struct imsgbuf *i, struct } int -reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) +reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l, + struct validator **validator_l) { struct peer *p; + struct validator *v; struct filter_rule *r; struct listen_addr *la; struct rde_rib *rr; @@ -444,7 +493,7 @@ reconfigure(char *conffile, struct bgpd_ reconfpending = 2; /* one per child */ log_info("rereading config"); - if (parse_config(conffile, conf, peer_l)) { + if (parse_config(conffile, conf, peer_l, validator_l)) { log_warnx("config file %s has errors, not reloading", conffile); reconfpending = 0; @@ -461,6 +510,9 @@ reconfigure(char *conffile, struct bgpd_ if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1, conf, sizeof(struct bgpd_config)) == -1) return (-1); + if (imsg_compose(ibuf_roa, IMSG_RECONF_CONF, 0, 0, -1, + conf, sizeof(struct bgpd_config)) == -1) + return (-1); TAILQ_FOREACH(la, conf->listen_addrs, entry) { if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd, @@ -497,6 +549,13 @@ reconfigure(char *conffile, struct bgpd_ return (-1); } + /* send validator list to ROA engine */ + for (v = *validator_l; v != NULL; v = v->next) { + if (imsg_compose(ibuf_roa, IMSG_RECONF_VALIDATOR, v->id, + 0, -1, v, sizeof(struct validator)) == -1) + return (-1); + } + /* networks go via kroute to the RDE */ if (kr_net_reload(0, &conf->networks)) return (-1); @@ -555,6 +614,8 @@ reconfigure(char *conffile, struct bgpd_ /* signal the SE first then the RDE to activate the new config */ if (imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1) return (-1); + if (imsg_compose(ibuf_roa, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0) == -1) + return (-1); /* mrt changes can be sent out of bound */ mrt_reconfigure(conf->mrt); @@ -671,6 +732,7 @@ dispatch_imsg(struct imsgbuf *ibuf, int case IMSG_CTL_KROUTE: case IMSG_CTL_KROUTE_ADDR: case IMSG_CTL_SHOW_NEXTHOP: + case IMSG_CTL_SHOW_VALIDATOR: case IMSG_CTL_SHOW_INTERFACE: case IMSG_CTL_SHOW_FIB_TABLES: if (idx != PFD_PIPE_SESSION) @@ -895,10 +957,13 @@ handle_pollfd(struct pollfd *pfd, struct } 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_r2a[2]; int pipe_s2r_ctl[2]; + int pipe_s2a_ctl[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, PF_UNSPEC, pipe_s2r) == -1) @@ -906,6 +971,12 @@ imsg_send_sockets(struct imsgbuf *se, st if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, PF_UNSPEC, pipe_s2r_ctl) == -1) return (-1); + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_r2a) == -1) + return (-1); + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_s2a_ctl) == -1) + return (-1); if (imsg_compose(se, IMSG_SOCKET_CONN, 0, 0, pipe_s2r[0], NULL, 0) == -1) @@ -918,6 +989,20 @@ imsg_send_sockets(struct imsgbuf *se, st NULL, 0) == -1) return (-1); if (imsg_compose(rde, IMSG_SOCKET_CONN_CTL, 0, 0, pipe_s2r_ctl[1], + NULL, 0) == -1) + return (-1); + + if (imsg_compose(roa, IMSG_SOCKET_ROA, 0, 0, pipe_r2a[0], + NULL, 0) == -1) + return (-1); + if (imsg_compose(rde, IMSG_SOCKET_ROA, 0, 0, pipe_r2a[1], + NULL, 0) == -1) + return (-1); + + if (imsg_compose(se, IMSG_SOCKET_CONN_CTL_ROA, 0, 0, pipe_s2a_ctl[0], + NULL, 0) == -1) + return (-1); + if (imsg_compose(roa, IMSG_SOCKET_CONN_CTL_ROA, 0, 0, pipe_s2a_ctl[1], NULL, 0) == -1) return (-1); Index: bgpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v retrieving revision 1.163 diff -u -p -r1.163 bgpd.conf.5 --- bgpd.conf.5 12 Aug 2017 17:39:51 -0000 1.163 +++ bgpd.conf.5 26 Aug 2017 19:48:24 -0000 @@ -433,6 +433,23 @@ If set to to EBGP neighbors are not prepended with the local AS. The default is .Ic no . +.Pp +.It Xo +.Ic validator Ar address Op Ic port Ar port +.Op Ic poll-interval Ar time +.Op Ic preference Ar value +.Op Ic local-address Ar address +.Xc +Configure a RPKI-ROA cache validator. +.Ar address +can be an IPv4 or IPv6 address. Default +.Ar port +is 323. Default +.Ar time +is 3600 seconds. Default +.Ar value +is 128 and must be comprise between 1 and 255. +Currently only one validator is supported. .El .Sh MPLS VPN CONFIGURATION .Xr bgpd 8 @@ -1388,6 +1405,12 @@ Apply rule only to the specified RIB. This only applies for received updates, so not for rules using the .Ar to peer parameter. +.Pp +.It Ic roa-state Pq Ic valid | not-found | invalid +This rule applies only to route with specified RPKI-ROA status. +.Bd -literal -offset indent +deny from any roa-state invalid +.Ed .Pp .It Ic set Ar attribute ... All matching rules can set the Index: bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.314 diff -u -p -r1.314 bgpd.h --- bgpd.h 12 Aug 2017 16:47:50 -0000 1.314 +++ bgpd.h 26 Aug 2017 19:48:24 -0000 @@ -85,6 +85,9 @@ #define F_CTL_ADJ_OUT 0x4000 #define F_CTL_ACTIVE 0x8000 #define F_RTLABEL 0x10000 +#define F_ROA_VALID 0x20000 +#define F_ROA_NOTFOUND 0x40000 +#define F_ROA_INVALID 0x80000 /* * Limit the number of messages queued in the session engine. @@ -102,7 +105,8 @@ enum bgpd_process { PROC_MAIN, PROC_SE, - PROC_RDE + PROC_RDE, + PROC_ROA } bgpd_process; enum reconf_action { @@ -389,6 +393,7 @@ enum imsg_type { IMSG_CTL_SHOW_TIMER, IMSG_CTL_LOG_VERBOSE, IMSG_CTL_SHOW_FIB_TABLES, + IMSG_CTL_SHOW_VALIDATOR, IMSG_NETWORK_ADD, IMSG_NETWORK_ASPATH, IMSG_NETWORK_ATTR, @@ -398,6 +403,8 @@ enum imsg_type { IMSG_FILTER_SET, IMSG_SOCKET_CONN, IMSG_SOCKET_CONN_CTL, + IMSG_SOCKET_ROA, + IMSG_SOCKET_CONN_CTL_ROA, IMSG_RECONF_CONF, IMSG_RECONF_RIB, IMSG_RECONF_PEER, @@ -408,6 +415,7 @@ enum imsg_type { IMSG_RECONF_RDOMAIN_EXPORT, IMSG_RECONF_RDOMAIN_IMPORT, IMSG_RECONF_RDOMAIN_DONE, + IMSG_RECONF_VALIDATOR, IMSG_RECONF_DONE, IMSG_UPDATE, IMSG_UPDATE_ERR, @@ -432,7 +440,11 @@ enum imsg_type { IMSG_IFINFO, IMSG_DEMOTE, IMSG_XON, - IMSG_XOFF + IMSG_XOFF, + IMSG_VPL_UPDATE, + IMSG_VALIDATE_PREFIX, + IMSG_STATUS, + IMSG_ROA_NOTREADY }; struct demote_msg { @@ -597,11 +609,13 @@ struct ctl_neighbor { int show_timers; }; -#define F_PREF_ELIGIBLE 0x01 -#define F_PREF_ACTIVE 0x02 -#define F_PREF_INTERNAL 0x04 -#define F_PREF_ANNOUNCE 0x08 -#define F_PREF_STALE 0x10 +#define F_PREF_ELIGIBLE 0x01 +#define F_PREF_ACTIVE 0x02 +#define F_PREF_INTERNAL 0x04 +#define F_PREF_ANNOUNCE 0x08 +#define F_PREF_STALE 0x10 +#define F_PREF_ROAVALID 0x20 +#define F_PREF_ROAINVALID 0x40 struct ctl_show_rib { struct bgpd_addr true_nexthop; @@ -700,7 +714,7 @@ struct ctl_show_rib_request { struct filter_largecommunity large_community; u_int32_t peerid; pid_t pid; - u_int16_t flags; + u_int32_t flags; enum imsg_type type; u_int8_t prefixlen; u_int8_t aid; @@ -848,6 +862,7 @@ struct filter_match { struct filter_community community; struct filter_largecommunity large_community; struct filter_extcommunity ext_community; + u_int32_t roaflag; }; union filter_rule_ptr { @@ -1128,11 +1143,13 @@ sa_family_t aid2af(u_int8_t); int af2aid(sa_family_t, u_int8_t, u_int8_t *); struct sockaddr *addr2sa(struct bgpd_addr *, u_int16_t); void sa2addr(struct sockaddr *, struct bgpd_addr *); +u_int32_t aspath_getsourceas(struct rde_aspath *); static const char * const log_procnames[] = { "parent", "SE", - "RDE" + "RDE", + "ROA" }; /* logmsg.c and needed by bgpctl */ Index: carp.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/carp.c,v retrieving revision 1.9 diff -u -p -r1.9 carp.c --- carp.c 24 Jan 2017 04:22:42 -0000 1.9 +++ carp.c 26 Aug 2017 19:48:24 -0000 @@ -27,6 +27,7 @@ #include <unistd.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" Index: config.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/config.c,v retrieving revision 1.67 diff -u -p -r1.67 config.c --- config.c 29 May 2017 09:56:33 -0000 1.67 +++ config.c 26 Aug 2017 19:48:24 -0000 @@ -33,6 +33,7 @@ #include <unistd.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" Index: control.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/control.c,v retrieving revision 1.90 diff -u -p -r1.90 control.c --- control.c 11 Aug 2017 16:02:53 -0000 1.90 +++ control.c 26 Aug 2017 19:48:24 -0000 @@ -26,6 +26,7 @@ #include <unistd.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" @@ -257,6 +258,7 @@ control_dispatch_msg(struct pollfd *pfd, case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_TERSE: case IMSG_CTL_SHOW_TIMER: + case IMSG_CTL_SHOW_VALIDATOR: break; default: /* clear imsg type to prevent processing */ @@ -488,6 +490,10 @@ control_dispatch_msg(struct pollfd *pfd, case IMSG_FILTER_SET: imsg_ctl_rde(imsg.hdr.type, 0, imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE); + break; + case IMSG_CTL_SHOW_VALIDATOR: + imsg_ctl_roa(imsg.hdr.type, 0, imsg.data, + imsg.hdr.len - IMSG_HEADER_SIZE); break; case IMSG_CTL_LOG_VERBOSE: if (imsg.hdr.len != IMSG_HEADER_SIZE + Index: logmsg.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/logmsg.c,v retrieving revision 1.3 diff -u -p -r1.3 logmsg.c --- logmsg.c 28 May 2017 20:14:15 -0000 1.3 +++ logmsg.c 26 Aug 2017 19:48:24 -0000 @@ -25,6 +25,7 @@ #include <syslog.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" Index: mrt.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/mrt.c,v retrieving revision 1.83 diff -u -p -r1.83 mrt.c --- mrt.c 27 May 2017 10:55:45 -0000 1.83 +++ mrt.c 26 Aug 2017 19:48:24 -0000 @@ -29,6 +29,7 @@ #include "bgpd.h" #include "rde.h" +#include "roa.h" #include "session.h" #include "mrt.h" Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.315 diff -u -p -r1.315 parse.y --- parse.y 21 Aug 2017 14:41:22 -0000 1.315 +++ parse.y 26 Aug 2017 19:48:24 -0000 @@ -43,6 +43,7 @@ #include "bgpd.h" #include "mrt.h" +#include "roa.h" #include "session.h" #include "rde.h" #include "log.h" @@ -85,13 +86,15 @@ static struct network_head *netconf; static struct peer *peer_l, *peer_l_old; static struct peer *curpeer; static struct peer *curgroup; +static struct validator *validator_l, *validator_l_old; +static struct validator *curvalidator; static struct rdomain *currdom; static struct filter_head *filter_l; static struct filter_head *peerfilter_l; static struct filter_head *groupfilter_l; static struct filter_rule *curpeer_filter[2]; static struct filter_rule *curgroup_filter[2]; -static u_int32_t id; +static u_int32_t peerid, validatorid; struct filter_rib_l { struct filter_rib_l *next; @@ -127,12 +130,14 @@ struct filter_match_l { struct peer *alloc_peer(void); struct peer *new_peer(void); +struct validator *new_validator(void); struct peer *new_group(void); int add_mrtconfig(enum mrt_type, char *, int, struct peer *, char *); int add_rib(char *, u_int, u_int16_t); struct rde_rib *find_rib(char *); -int get_id(struct peer *); +int get_peerid(struct peer *); +int get_validatorid(struct validator *); int merge_prefixspec(struct filter_prefix_l *, struct filter_prefixlen *); int expand_rule(struct filter_rule *, struct filter_rib_l *, @@ -140,6 +145,7 @@ int expand_rule(struct filter_rule *, struct filter_set_head *); int str2key(char *, char *, size_t); int neighbor_consistent(struct peer *); +int validator_consistent(struct validator *); int merge_filterset(struct filter_set_head *, struct filter_set *); void copy_filterset(struct filter_set_head *, struct filter_set_head *); @@ -208,13 +214,16 @@ typedef struct { %token IPV4 IPV6 %token QUALIFY VIA %token NE LE GE XRANGE LONGER +%token VALIDATOR PORT POLLINTERVAL INVALID NOTFOUND VALID +%token VALIDATORPREF ROASTATE %token <v.string> STRING %token <v.number> NUMBER %type <v.number> asnumber as4number as4number_any optnumber %type <v.number> espah family restart origincode nettype %type <v.number> yesno inout restricted +%type <v.number> port pollinterval preference roa_status %type <v.string> string -%type <v.addr> address +%type <v.addr> address localaddress %type <v.prefix> prefix addrspec %type <v.u8> action quick direction delete %type <v.filter_rib> filter_rib_h filter_rib_l filter_rib @@ -240,6 +249,7 @@ grammar : /* empty */ | grammar neighbor '\n' | grammar group '\n' | grammar filterrule '\n' + | grammar validator '\n' | grammar error '\n' { file->errors++; } ; @@ -958,7 +968,7 @@ rdomainopts : RD STRING { } ; -neighbor : { curpeer = new_peer(); } +neighbor : { curpeer = new_peer(); } NEIGHBOR addrspec { memcpy(&curpeer->conf.remote_addr, &$3.prefix, sizeof(curpeer->conf.remote_addr)); @@ -970,8 +980,8 @@ neighbor : { curpeer = new_peer(); } curpeer->conf.remote_addr.aid] == -1) curpeer->conf.capabilities.mp[ curpeer->conf.remote_addr.aid] = 1; - if (get_id(curpeer)) { - yyerror("get_id failed"); + if (get_peerid(curpeer)) { + yyerror("get_peerid failed"); YYERROR; } } @@ -1004,8 +1014,8 @@ group : GROUP string optnl '{' optnl { YYERROR; } free($2); - if (get_id(curgroup)) { - yyerror("get_id failed"); + if (get_peerid(curgroup)) { + yyerror("get_peerid failed"); YYERROR; } } @@ -1806,11 +1816,13 @@ filter_match_h : /* empty */ { bzero(&$$, sizeof($$)); $$.m.community.as = COMMUNITY_UNSET; $$.m.large_community.as = COMMUNITY_UNSET; + $$.m.roaflag = ROA_F_DUMMY; } | { bzero(&fmopts, sizeof(fmopts)); fmopts.m.community.as = COMMUNITY_UNSET; fmopts.m.large_community.as = COMMUNITY_UNSET; + fmopts.m.roaflag = ROA_F_DUMMY; } filter_match { memcpy(&$$, &fmopts, sizeof($$)); @@ -1916,6 +1928,9 @@ filter_elm : filter_prefix_h { } fmopts.m.nexthop.flags = FILTER_NEXTHOP_NEIGHBOR; } + | ROASTATE roa_status { + fmopts.m.roaflag = $2; + } ; prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); } @@ -2289,6 +2304,72 @@ filter_set_opt : LOCALPREF NUMBER { } ; +validator : VALIDATOR address port pollinterval preference localaddress { + if (curvalidator) { + yyerror("Cannot have multiple validators"); + YYERROR; + } + curvalidator = new_validator(); + memcpy(&curvalidator->remote_addr, &$2, + sizeof(curvalidator->remote_addr)); + curvalidator->port = $3; + curvalidator->pollinterval = $4; + curvalidator->preference = $5; + + if ($6.aid) + memcpy(&curvalidator->local_addr, &$6, + sizeof(curvalidator->local_addr)); + + + if (validator_consistent(curvalidator) == -1) + YYERROR; + + curvalidator->next = validator_l; + validator_l = curvalidator; + } + ; + +port : PORT NUMBER { + if ($2 < 1 || $2 > 65535) { + yyerror("invalid port number %lld", $2); + YYERROR; + } + $$ = $2; + } + | { $$ = ROA_PORT; } /* empty */ + ; + +pollinterval : { $$ = ROA_POLLING_INTERVAL; } /* empty */ + | POLLINTERVAL NUMBER { + if ($2 < 60 || $2 > 3600) { + yyerror("invalid polling interval %lld," + " must be between 60 and 3600", $2); + YYERROR; + } + $$ = $2; + } + ; + +preference : { $$ = ROA_PREFERENCE; } /* empty */ + | VALIDATORPREF NUMBER { + if ($2 < 1 || $2 > 255) { + yyerror("invalid validator preference " + "%lld must be between 1 and 255", $2); + YYERROR; + } + $$ = $2; + } + ; + +localaddress : LOCALADDR address { $$ = $2; } + | { $$.aid = 0; } /* empty */ + ; + +roa_status : NOTFOUND { $$ = ROA_F_NOTFOUND; } + | INVALID { $$ = ROA_F_INVALID; } + | VALID { $$ = ROA_F_VALID; } + ; + origincode : string { if (!strcmp($1, "egp")) $$ = ORIGIN_EGP; @@ -2399,6 +2480,7 @@ lookup(char *s) { "inet", IPV4}, { "inet6", IPV6}, { "ipsec", IPSEC}, + { "invalid", INVALID}, { "key", KEY}, { "large-community", LARGECOMMUNITY}, { "listen", LISTEN}, @@ -2420,6 +2502,7 @@ lookup(char *s) { "network", NETWORK}, { "nexthop", NEXTHOP}, { "no-modify", NOMODIFY}, + { "not-found", NOTFOUND}, { "on", ON}, { "or-longer", LONGER}, { "origin", ORIGIN}, @@ -2428,6 +2511,9 @@ lookup(char *s) { "password", PASSWORD}, { "peer-as", PEERAS}, { "pftable", PFTABLE}, + { "poll-interval", POLLINTERVAL}, + { "port", PORT}, + { "pref", VALIDATORPREF}, { "prefix", PREFIX}, { "prefixlen", PREFIXLEN}, { "prepend-neighbor", PREPEND_PEER}, @@ -2443,6 +2529,7 @@ lookup(char *s) { "restart", RESTART}, { "restricted", RESTRICTED}, { "rib", RIB}, + { "roa-state", ROASTATE}, { "route-collector", ROUTECOLL}, { "route-reflector", REFLECTOR}, { "router-id", ROUTERID}, @@ -2459,6 +2546,8 @@ lookup(char *s) { "transit-as", TRANSITAS}, { "transparent-as", TRANSPARENT}, { "ttl-security", TTLSECURITY}, + { "valid", VALID}, + { "validator", VALIDATOR}, { "via", VIA}, { "weight", WEIGHT} }; @@ -2797,10 +2886,12 @@ popfile(void) } int -parse_config(char *filename, struct bgpd_config *xconf, struct peer **xpeers) +parse_config(char *filename, struct bgpd_config *xconf, struct peer **xpeers, + struct validator **xvalidators) { struct sym *sym, *next; struct peer *p, *pnext; + struct validator *v, *vnext; struct rde_rib *rr; int errors = 0; @@ -2820,7 +2911,11 @@ parse_config(char *filename, struct bgpd peer_l_old = *xpeers; curpeer = NULL; curgroup = NULL; - id = 1; + peerid = 1; + validator_l = NULL; + validator_l_old = *xvalidators; + curvalidator = NULL; + validatorid = 1; netconf = &conf->networks; @@ -2857,6 +2952,11 @@ parse_config(char *filename, struct bgpd free(p); } + for (v = validator_l; v != NULL; v = vnext) { + vnext = v->next; + free(v); + } + while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) { SIMPLEQ_REMOVE_HEAD(&ribnames, entry); free(rr); @@ -2880,12 +2980,18 @@ parse_config(char *filename, struct bgpd errors += mrt_mergeconfig(xconf->mrt, conf->mrt); errors += merge_config(xconf, conf, peer_l); *xpeers = peer_l; + *xvalidators = validator_l; for (p = peer_l_old; p != NULL; p = pnext) { pnext = p->next; free(p); } + for (v = validator_l_old; v != NULL; v = vnext) { + vnext = v->next; + free(v); + } + free(filter_l); free(peerfilter_l); free(groupfilter_l); @@ -3310,6 +3416,24 @@ new_peer(void) return (p); } +struct validator * +new_validator(void) +{ + struct validator *v; + + if ((v = calloc(1, sizeof(struct validator))) == NULL) + fatal("new_validator"); + + /* some sane defaults */ + v->id = 0; + v->state = ROA_STATE_NONE; + v->next = NULL; + v->port = ROA_PORT; + v->pollinterval = ROA_POLLING_INTERVAL; + v->preference = ROA_PREFERENCE; + return (v); +} + struct peer * new_group(void) { @@ -3434,7 +3558,7 @@ find_rib(char *name) } int -get_id(struct peer *newpeer) +get_peerid(struct peer *newpeer) { struct peer *p; @@ -3454,12 +3578,13 @@ get_id(struct peer *newpeer) } /* new one */ - for (; id < UINT_MAX / 2; id++) { + for (; peerid < UINT_MAX / 2; peerid++) { for (p = peer_l_old; p != NULL && - p->conf.id != id && p->conf.groupid != id; p = p->next) + p->conf.id != peerid && p->conf.groupid != peerid; + p = p->next) ; /* nothing */ if (p == NULL) { /* we found a free id */ - newpeer->conf.id = id++; + newpeer->conf.id = peerid++; return (0); } } @@ -3468,6 +3593,34 @@ get_id(struct peer *newpeer) } int +get_validatorid(struct validator *newvalidator) +{ + struct validator *v; + + for (v = validator_l_old; v != NULL; v = v->next) + if (newvalidator->remote_addr.aid) { + if (!memcmp(&v->remote_addr, + &newvalidator->remote_addr, + sizeof(v->remote_addr))) { + newvalidator->id = v->id; + return (0); + } + } + + for (; validatorid < UINT_MAX / 2; validatorid++) { + for (v = validator_l_old; v != NULL && v->id != validatorid; + v = v->next) + ; + + if (v == NULL) { + newvalidator->id = validatorid++; + return (0); + } + } + return (-1); +} + +int merge_prefixspec(struct filter_prefix_l *p, struct filter_prefixlen *pl) { u_int8_t max_len = 0; @@ -3668,6 +3821,20 @@ str2key(char *s, char *dest, size_t max_ } int +validator_consistent(struct validator *v) +{ + /* local-address and validator's address: same address family */ + if (v->local_addr.aid && + v->local_addr.aid != v->remote_addr.aid) { + yyerror("local-address and validator's address " + "must be of the same address family"); + return (-1); + } + + return (0); +} + +int neighbor_consistent(struct peer *p) { u_int8_t i; @@ -3875,6 +4042,7 @@ get_rule(enum action_types type) r->action = ACTION_NONE; r->match.community.as = COMMUNITY_UNSET; r->match.large_community.as = COMMUNITY_UNSET; + r->match.roaflag = ROA_F_DUMMY; TAILQ_INIT(&r->set); if (curpeer == curgroup) { /* group */ Index: pfkey.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/pfkey.c,v retrieving revision 1.51 diff -u -p -r1.51 pfkey.c --- pfkey.c 21 Aug 2017 14:43:33 -0000 1.51 +++ pfkey.c 26 Aug 2017 19:48:24 -0000 @@ -30,6 +30,7 @@ #include <unistd.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" Index: printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.106 diff -u -p -r1.106 printconf.c --- printconf.c 12 Aug 2017 16:47:50 -0000 1.106 +++ printconf.c 26 Aug 2017 19:48:24 -0000 @@ -25,6 +25,7 @@ #include "bgpd.h" #include "mrt.h" +#include "roa.h" #include "session.h" #include "rde.h" #include "log.h" @@ -42,6 +43,7 @@ const char *print_af(u_int8_t); void print_network(struct network_config *, const char *); void print_peer(struct peer_config *, struct bgpd_config *, const char *); +void print_validators(struct validator *); const char *print_auth_alg(u_int8_t); const char *print_enc_alg(u_int8_t); void print_announce(struct peer_config *, const char *); @@ -707,6 +709,13 @@ print_rule(struct peer *peer_l, struct f r->match.large_community.ld2); } + if (r->match.roaflag == ROA_F_NOTFOUND) + printf("roa-state not-found "); + else if (r->match.roaflag == ROA_F_INVALID) + printf("roa-state invalid "); + else if (r->match.roaflag == ROA_F_VALID) + printf("roa-state valid "); + print_set(&r->set); printf("\n"); @@ -821,8 +830,8 @@ peer_compare(const void *aa, const void void print_config(struct bgpd_config *conf, struct rib_names *rib_l, struct network_head *net_l, struct peer *peer_l, - struct filter_head *rules_l, struct mrt_head *mrt_l, - struct rdomain_head *rdom_l) + struct validator *validator_l, struct filter_head *rules_l, + struct mrt_head *mrt_l, struct rdomain_head *rdom_l) { struct filter_rule *r; struct network *n; @@ -850,8 +859,26 @@ print_config(struct bgpd_config *conf, s printf("\n"); print_mrt(conf, 0, 0, "", ""); printf("\n"); + print_validators(validator_l); + printf("\n"); print_groups(conf, peer_l); printf("\n"); TAILQ_FOREACH(r, rules_l, entry) print_rule(peer_l, r); +} + +void +print_validators(struct validator *validator_l) +{ + struct validator *v; + + for (v = validator_l; v != NULL; v = v->next) { + printf("validator %s ", log_addr(&v->remote_addr)); + printf("port %d ", v->port); + printf("poll-interval %d ", v->pollinterval); + printf("pref %d ", v->preference); + if (v->local_addr.aid) + printf("local-address %s ", log_addr(&v->local_addr)); + printf("\n"); + } } Index: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.371 diff -u -p -r1.371 rde.c --- rde.c 11 Aug 2017 16:02:53 -0000 1.371 +++ rde.c 26 Aug 2017 19:48:24 -0000 @@ -38,13 +38,15 @@ #include "bgpd.h" #include "mrt.h" #include "rde.h" +#include "roa.h" #include "session.h" #include "log.h" #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 *); @@ -133,8 +135,9 @@ struct filter_head *out_rules, *out_rule struct rdomain_head *rdomains_l, *newdomains; struct imsgbuf *ibuf_se; struct imsgbuf *ibuf_se_ctl; -struct imsgbuf *ibuf_main; +struct imsgbuf *ibuf_main, *ibuf_roa; struct rde_memstats rdemem; +int roaready = 0; struct rde_dump_ctx { LIST_ENTRY(rde_dump_ctx) entry; @@ -257,6 +260,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_roa); if (rde_dump_pending() && ibuf_se_ctl && ibuf_se_ctl->w.queued == 0) @@ -305,6 +309,15 @@ rde_main(int debug, int verbose) } else rde_dispatch_imsg_session(ibuf_se_ctl); + if (handle_pollfd(&pfd[PFD_PIPE_ROA], ibuf_roa) == + -1) { + log_warnx("RDE: Lost connection to ROA"); + msgbuf_clear(&ibuf_roa->w); + free(ibuf_roa); + ibuf_roa = NULL; + } else + rde_dispatch_imsg_session(ibuf_roa); + for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts); j < i && mctx != 0; j++) { if (pfd[j].fd == mctx->mrt.wbuf.fd && @@ -373,6 +386,7 @@ rde_dispatch_imsg_session(struct imsgbuf int verbose; u_int16_t len; u_int8_t aid; + struct p_validate pv; while (ibuf) { if ((n = imsg_get(ibuf, &imsg)) == -1) @@ -639,6 +653,26 @@ badnet: rde_dump_ctx_throttle(imsg.hdr.pid, 1); } break; + case IMSG_VPL_UPDATE: + /* VPL has been updated, send all prefixes to ROA + * engine for validation + */ + roaready = 1; + send_vpl_update(imsg.hdr.pid, ibuf_roa); + break; + case IMSG_STATUS: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct p_validate)) { + log_warnx("rde_dispatch: wrong imsg len"); + break; + } + memcpy(&pv, imsg.data, sizeof(struct p_validate)); + update_prefix_status(pv); + break; + case IMSG_ROA_NOTREADY: + roaready = 0; + reset_prefix_status(); + break; default: break; } @@ -670,6 +704,7 @@ rde_dispatch_imsg_parent(struct imsgbuf switch (imsg.hdr.type) { case IMSG_SOCKET_CONN: + case IMSG_SOCKET_ROA: case IMSG_SOCKET_CONN_CTL: if ((fd = imsg.fd) == -1) { log_warnx("expected to receive imsg fd to " @@ -687,7 +722,7 @@ rde_dispatch_imsg_parent(struct imsgbuf free(ibuf_se); } ibuf_se = i; - } else { + } else if (imsg.hdr.type == IMSG_SOCKET_CONN_CTL) { if (ibuf_se_ctl) { log_warnx("Unexpected imsg ctl " "connection to SE received"); @@ -695,6 +730,14 @@ rde_dispatch_imsg_parent(struct imsgbuf free(ibuf_se_ctl); } ibuf_se_ctl = i; + } else { + if (ibuf_roa) { + log_warnx("Unexpected imsg ctl " + "connection to ROA received"); + msgbuf_clear(&ibuf_roa->w); + free(ibuf_roa); + } + ibuf_roa = i; } break; case IMSG_NETWORK_ADD: @@ -1330,7 +1373,7 @@ rde_update_update(struct rde_peer *peer, peer->prefix_rcvd_update++; /* add original path to the Adj-RIB-In */ - path_update(&ribs[0].rib, peer, asp, prefix, prefixlen); + path_update(&ribs[0].rib, peer, asp, prefix, prefixlen, ROA_F_DUMMY); peer->prefix_cnt++; for (i = 1; i < rib_size; i++) { @@ -1338,7 +1381,7 @@ rde_update_update(struct rde_peer *peer, break; /* input filter */ action = rde_filter(ribs[i].in_rules, &fasp, peer, asp, prefix, - prefixlen, peer); + prefixlen, peer, ROA_F_DUMMY); if (fasp == NULL) fasp = asp; @@ -1347,7 +1390,7 @@ rde_update_update(struct rde_peer *peer, rde_update_log("update", i, peer, &fasp->nexthop->exit_nexthop, prefix, prefixlen); path_update(&ribs[i].rib, peer, fasp, prefix, - prefixlen); + prefixlen, ROA_F_DUMMY); } else if (prefix_remove(&ribs[i].rib, peer, prefix, prefixlen, 0)) { rde_update_log("filtered withdraw", i, peer, @@ -2227,6 +2270,17 @@ rde_dump_rib_as(struct prefix *p, struct rib.flags |= F_PREF_STALE; rib.aspath_len = aspath_length(asp->aspath); + switch (p->validity) { + case ROA_F_VALID: + rib.flags |= F_PREF_ROAVALID; + break; + case ROA_F_INVALID: + rib.flags |= F_PREF_ROAINVALID; + break; + default: + break; + } + if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, sizeof(rib) + rib.aspath_len)) == NULL) return; @@ -2270,7 +2324,7 @@ rde_dump_filterout(struct rde_peer *peer pt_getaddr(p->prefix, &addr); a = rde_filter(out_rules, &asp, peer, p->aspath, &addr, - p->prefix->prefixlen, p->aspath->peer); + p->prefix->prefixlen, p->aspath->peer, p->validity); if (asp) asp->peer = p->aspath->peer; else @@ -2309,6 +2363,11 @@ rde_dump_filter(struct prefix *p, struct return; if ((req->flags & F_CTL_ACTIVE) && p->re->active != p) return; + if (((req->flags & F_ROA_VALID) && p->validity != + ROA_F_VALID) || ((req->flags & F_ROA_INVALID) && + p->validity != ROA_F_INVALID) || ((req->flags & + F_ROA_NOTFOUND) && p->validity != ROA_F_NOTFOUND)) + return; rde_dump_rib_as(p, p->aspath, req->pid, req->flags); } else if (req->flags & F_CTL_ADJ_OUT) { if (p->re->active != p) @@ -3015,7 +3074,7 @@ rde_softreconfig_in(struct rib_entry *re /* check if prefix changed */ if (rib->state == RECONF_RELOAD) { oa = rde_filter(rib->in_rules_tmp, &oasp, peer, - asp, &addr, pt->prefixlen, peer); + asp, &addr, pt->prefixlen, peer, p->validity); oasp = oasp != NULL ? oasp : asp; } else { /* make sure we update everything for RECONF_REINIT */ @@ -3023,7 +3082,7 @@ rde_softreconfig_in(struct rib_entry *re oasp = asp; } na = rde_filter(rib->in_rules, &nasp, peer, asp, - &addr, pt->prefixlen, peer); + &addr, pt->prefixlen, peer, p->validity); nasp = nasp != NULL ? nasp : asp; /* go through all 4 possible combinations */ @@ -3032,7 +3091,7 @@ rde_softreconfig_in(struct rib_entry *re if (oa == ACTION_DENY && na == ACTION_ALLOW) { /* update Local-RIB */ path_update(&rib->rib, peer, nasp, &addr, - pt->prefixlen); + pt->prefixlen, p->validity); } else if (oa == ACTION_ALLOW && na == ACTION_DENY) { /* remove from Local-RIB */ prefix_remove(&rib->rib, peer, &addr, pt->prefixlen, 0); @@ -3040,7 +3099,7 @@ rde_softreconfig_in(struct rib_entry *re if (path_compare(nasp, oasp) != 0) /* send update */ path_update(&rib->rib, peer, nasp, &addr, - pt->prefixlen); + pt->prefixlen, p->validity); } if (oasp != asp) @@ -3073,9 +3132,9 @@ rde_softreconfig_out(struct rib_entry *r return; oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath, - &addr, pt->prefixlen, p->aspath->peer); + &addr, pt->prefixlen, p->aspath->peer, p->validity); na = rde_filter(out_rules, &nasp, peer, p->aspath, - &addr, pt->prefixlen, p->aspath->peer); + &addr, pt->prefixlen, p->aspath->peer, p->validity); oasp = oasp != NULL ? oasp : p->aspath; nasp = nasp != NULL ? nasp : p->aspath; @@ -3118,7 +3177,7 @@ rde_softreconfig_unload_peer(struct rib_ return; oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath, - &addr, pt->prefixlen, p->aspath->peer); + &addr, pt->prefixlen, p->aspath->peer, p->validity); oasp = oasp != NULL ? oasp : p->aspath; if (oa == ACTION_DENY) @@ -3603,7 +3662,7 @@ network_add(struct network_config *nc, i if (*ribs[i].name == '\0') break; path_update(&ribs[i].rib, peerself, asp, &nc->prefix, - nc->prefixlen); + nc->prefixlen, ROA_F_DUMMY); } path_put(asp); filterset_free(&nc->attrset); Index: rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.162 diff -u -p -r1.162 rde.h --- rde.h 30 May 2017 18:08:15 -0000 1.162 +++ rde.h 26 Aug 2017 19:48:24 -0000 @@ -25,6 +25,7 @@ #include <stdint.h> #include "bgpd.h" +#include "roa.h" /* rde internal structures */ @@ -307,10 +308,20 @@ struct prefix { struct pt_entry *prefix; struct rib_entry *re; time_t lastchange; + u_int32_t validity; }; extern struct rde_memstats rdemem; +struct p_validate { + u_int8_t aid; + u_int8_t prefixlen; + struct in6_addr prefix6; + struct in_addr prefix4; + u_int32_t asn; + enum roa_status_flag status; +}; + /* prototypes */ /* mrt.c */ int mrt_dump_v2_hdr(struct mrt *, struct bgpd_config *, @@ -395,7 +406,8 @@ void prefix_evaluate(struct prefix *, /* rde_filter.c */ enum filter_actions rde_filter(struct filter_head *, struct rde_aspath **, struct rde_peer *, struct rde_aspath *, - struct bgpd_addr *, u_int8_t, struct rde_peer *); + struct bgpd_addr *, u_int8_t, struct rde_peer *, + enum roa_status_flag); void rde_apply_set(struct rde_aspath *, struct filter_set_head *, u_int8_t, struct rde_peer *, struct rde_peer *); int rde_filter_equal(struct filter_head *, struct filter_head *, @@ -428,6 +440,7 @@ int pt_prefix_cmp(const struct pt_entry /* rde_rib.c */ extern u_int16_t rib_size; extern struct rib_desc *ribs; +extern int roaready; struct rib *rib_new(char *, u_int, u_int16_t); struct rib *rib_find(char *); @@ -439,6 +452,10 @@ void rib_dump(struct rib *, void (*)(s void *, u_int8_t); void rib_dump_r(struct rib_context *); +void send_vpl_update(int, struct imsgbuf *); +void update_prefix_status(struct p_validate); +void reset_prefix_status(void); + static inline struct rib * re_rib(struct rib_entry *re) { @@ -449,7 +466,8 @@ void path_init(u_int32_t); void path_init(u_int32_t); void path_shutdown(void); int path_update(struct rib *, struct rde_peer *, - struct rde_aspath *, struct bgpd_addr *, int); + struct rde_aspath *, struct bgpd_addr *, int, + enum roa_status_flag); int path_compare(struct rde_aspath *, struct rde_aspath *); struct rde_aspath *path_lookup(struct rde_aspath *, struct rde_peer *); void path_remove(struct rde_aspath *); @@ -464,7 +482,7 @@ void path_put(struct rde_aspath *); struct prefix *prefix_get(struct rib *, struct rde_peer *, struct bgpd_addr *, int, u_int32_t); int prefix_add(struct rib *, struct rde_aspath *, - struct bgpd_addr *, int); + struct bgpd_addr *, int, enum roa_status_flag); void prefix_move(struct rde_aspath *, struct prefix *); int prefix_remove(struct rib *, struct rde_peer *, struct bgpd_addr *, int, u_int32_t); Index: rde_filter.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v retrieving revision 1.83 diff -u -p -r1.83 rde_filter.c --- rde_filter.c 12 Aug 2017 16:47:50 -0000 1.83 +++ rde_filter.c 26 Aug 2017 19:48:24 -0000 @@ -29,7 +29,8 @@ #include "log.h" int rde_filter_match(struct filter_rule *, struct rde_aspath *, - struct bgpd_addr *, u_int8_t, struct rde_peer *, struct rde_peer *); + struct bgpd_addr *, u_int8_t, struct rde_peer *, struct rde_peer *, + enum roa_status_flag); int filterset_equal(struct filter_set_head *, struct filter_set_head *); void @@ -331,7 +332,7 @@ rde_apply_set(struct rde_aspath *asp, st int rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer, - struct rde_peer *from) + struct rde_peer *from, enum roa_status_flag v) { u_int32_t pas; int cas, type; @@ -476,6 +477,8 @@ rde_filter_match(struct filter_rule *f, } /* NOTREACHED */ } + if ((f->match.roaflag != v) && (f->match.roaflag != ROA_F_DUMMY)) + return (0); if (f->match.nexthop.flags != 0) { struct bgpd_addr *nexthop, *cmpaddr; if (asp != NULL && asp->nexthop == NULL) @@ -942,7 +945,7 @@ rde_filter_calc_skip_steps(struct filter enum filter_actions rde_filter(struct filter_head *rules, struct rde_aspath **new, struct rde_peer *peer, struct rde_aspath *asp, struct bgpd_addr *prefix, - u_int8_t prefixlen, struct rde_peer *from) + u_int8_t prefixlen, struct rde_peer *from, enum roa_status_flag v) { struct filter_rule *f; enum filter_actions action = ACTION_ALLOW; /* default allow */ @@ -974,7 +977,7 @@ rde_filter(struct filter_head *rules, st (f->peer.peerid && f->peer.peerid != peer->conf.id), f->skip[RDE_FILTER_SKIP_PEERID].ptr); - if (rde_filter_match(f, asp, prefix, prefixlen, peer, from)) { + if (rde_filter_match(f, asp, prefix, prefixlen, peer, from, v)) { if (asp != NULL && new != NULL) { /* asp may get modified so create a copy */ if (*new == NULL) { Index: rde_rib.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v retrieving revision 1.154 diff -u -p -r1.154 rde_rib.c --- rde_rib.c 28 May 2017 12:21:36 -0000 1.154 +++ rde_rib.c 26 Aug 2017 19:48:24 -0000 @@ -43,10 +43,14 @@ int rib_compare(const struct rib_entry * void rib_remove(struct rib_entry *); int rib_empty(struct rib_entry *); struct rib_entry *rib_restart(struct rib_context *); +void set_prefix_status(struct prefix *, enum roa_status_flag); +void fill_pvalidate(struct p_validate *, struct prefix *); RB_PROTOTYPE(rib_tree, rib_entry, rib_e, rib_compare); RB_GENERATE(rib_tree, rib_entry, rib_e, rib_compare); +extern struct imsgbuf *ibuf_roa; + static inline void re_lock(struct rib_entry *re) { @@ -389,7 +393,7 @@ path_shutdown(void) int path_update(struct rib *rib, struct rde_peer *peer, struct rde_aspath *nasp, - struct bgpd_addr *prefix, int prefixlen) + struct bgpd_addr *prefix, int prefixlen, enum roa_status_flag validity) { struct rde_aspath *asp; struct prefix *p; @@ -406,6 +410,8 @@ path_update(struct rib *rib, struct rde_ if (path_compare(nasp, p->aspath) == 0) { /* no change, update last change */ p->lastchange = time(NULL); + if (validity != ROA_F_DUMMY) + p->validity = validity; return (0); } } @@ -422,10 +428,11 @@ path_update(struct rib *rib, struct rde_ } /* If the prefix was found move it else add it to the aspath. */ - if (p != NULL) + if (p != NULL) { + p->validity = validity; prefix_move(asp, p); - else - return (prefix_add(rib, asp, prefix, prefixlen)); + } else + return (prefix_add(rib, asp, prefix, prefixlen, validity)); return (0); } @@ -688,11 +695,12 @@ prefix_get(struct rib *rib, struct rde_p */ int prefix_add(struct rib *rib, struct rde_aspath *asp, struct bgpd_addr *prefix, - int prefixlen) + int prefixlen, enum roa_status_flag validity) { struct prefix *p; struct rib_entry *re; + struct p_validate pv; re = rib_get(rib, prefix, prefixlen); if (re == NULL) @@ -701,14 +709,26 @@ prefix_add(struct rib *rib, struct rde_a p = prefix_bypeer(re, asp->peer, asp->flags); if (p == NULL) { p = prefix_alloc(); + p->validity = validity; prefix_link(p, re, asp); + if (roaready) { + //memcpy(&pv.pte, p->prefix, sizeof(struct pt_entry)); + fill_pvalidate(&pv, p); + pv.status = ROA_F_NOTFOUND; + imsg_compose(ibuf_roa, IMSG_VALIDATE_PREFIX, 0, 0, -1, &pv, + sizeof(struct p_validate)); + } + return (1); } else { if (p->aspath != asp) { /* prefix belongs to a different aspath so move */ + p->validity = validity; prefix_move(asp, p); - } else + } else { + p->validity = validity; p->lastchange = time(NULL); + } return (0); } } @@ -730,6 +750,7 @@ prefix_move(struct rde_aspath *asp, stru np->aspath = asp; /* peer and prefix pointers are still equal */ np->prefix = p->prefix; + np->validity = p->validity; np->re = p->re; np->lastchange = time(NULL); @@ -1316,3 +1337,115 @@ nexthop_hash(struct bgpd_addr *nexthop) return (&nexthoptable.nexthop_hashtbl[h & nexthoptable.nexthop_hashmask]); } +void +fill_pvalidate(struct p_validate *pv, struct prefix *p) +{ + struct pt_entry4 *pte4; + struct pt_entry6 *pte6; + + switch (p->prefix->aid) { + case AID_INET: + pte4 = (struct pt_entry4 *)&p->prefix->pt_e; + memcpy(&pv->prefix4, &pte4->prefix4, sizeof(struct in_addr)); + pv->prefixlen = pte4->prefixlen; + break; + case AID_INET6: + pte6 = (struct pt_entry6 *)&p->prefix->pt_e; + memcpy(&pv->prefix6, &pte6->prefix6, sizeof(struct in6_addr)); + pv->prefixlen = pte6->prefixlen; + break; + } + pv->aid = p->prefix->aid; + pv->asn = aspath_getsourceas(p->aspath); + pv->status = ROA_F_NOTFOUND; +} + +void +send_vpl_update(int pid, struct imsgbuf *ibuf) +{ + struct prefix *p; + struct rib_entry *re; + struct p_validate pv; + + RB_FOREACH(re, rib_tree, rib_tree(&ribs[0].rib)) + LIST_FOREACH(p, &re->prefix_h, rib_l) { + fill_pvalidate(&pv, p); + imsg_compose(ibuf, IMSG_VALIDATE_PREFIX, 0, + pid, -1, &pv, sizeof(struct p_validate)); + } +} + +void +update_prefix_status(struct p_validate pv) +{ + struct prefix *p; + struct rib_entry *re; + struct pt_entry4 *pte4; + struct pt_entry6 *pte6; + + RB_FOREACH(re, rib_tree, rib_tree(&ribs[0].rib)) + LIST_FOREACH(p, &re->prefix_h, rib_l) { + + if ((p->prefix->aid != pv.aid) || + (p->prefix->prefixlen != pv.prefixlen)) + continue; + + switch (pv.aid) { + case AID_INET: + pte4 = (struct pt_entry4 *)&p->prefix->pt_e; + if (pte4->prefix4.s_addr == + pv.prefix4.s_addr) { + set_prefix_status(p, pv.status); + return; + } + break; + case AID_INET6: + pte6 = (struct pt_entry6 *)&p->prefix->pt_e; + if (memcmp(&pv.prefix6, &pte6->prefix6, + sizeof(struct in6_addr)) == 0) { + set_prefix_status(p, pv.status); + return; + } + break; + } + } +} + +void +reset_prefix_status(void) +{ + struct prefix *p; + struct rib_entry *re; + + RB_FOREACH(re, rib_tree, rib_tree(&ribs[0].rib)) + LIST_FOREACH(p, &re->prefix_h, rib_l) + set_prefix_status(p, ROA_F_NOTFOUND); +} + +void +set_prefix_status(struct prefix *p, enum roa_status_flag s) +{ + struct rde_aspath *fasp; + struct rde_peer *peer; + struct bgpd_addr addr; + enum filter_actions action; + u_int16_t i; + + for (i = 1; i < rib_size; i++) { + if (*ribs[i].name == '\0') + break; + + peer = p->aspath->peer; + pt_getaddr(p->prefix, &addr); + action = rde_filter(ribs[i].in_rules, &fasp, peer, p->aspath, + &addr, p->prefix->prefixlen, peer, s); + if (action == ACTION_ALLOW && fasp != NULL) + path_update(&ribs[i].rib, peer, fasp, &addr, + p->prefix->prefixlen, s); + else if (action == ACTION_DENY) + prefix_remove(&ribs[i].rib, peer, &addr, + p->prefix->prefixlen, 0); + + path_put(fasp); + } +} Index: rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.86 diff -u -p -r1.86 rde_update.c --- rde_update.c 30 May 2017 18:08:15 -0000 1.86 +++ rde_update.c 26 Aug 2017 19:48:24 -0000 @@ -419,7 +419,8 @@ withdraw: pt_getaddr(old->prefix, &addr); if (rde_filter(rules, NULL, peer, old->aspath, &addr, - old->prefix->prefixlen, old->aspath->peer) == ACTION_DENY) + old->prefix->prefixlen, old->aspath->peer, ROA_F_DUMMY) == + ACTION_DENY) return; /* withdraw prefix */ @@ -436,7 +437,8 @@ withdraw: pt_getaddr(new->prefix, &addr); if (rde_filter(rules, &asp, peer, new->aspath, &addr, - new->prefix->prefixlen, new->aspath->peer) == ACTION_DENY) { + new->prefix->prefixlen, new->aspath->peer, ROA_F_DUMMY) == + ACTION_DENY) { path_put(asp); goto withdraw; } @@ -477,7 +479,7 @@ up_generate_default(struct filter_head * bzero(&addr, sizeof(addr)); addr.aid = aid; - if (rde_filter(rules, &fasp, peer, asp, &addr, 0, NULL) == + if (rde_filter(rules, &fasp, peer, asp, &addr, 0, NULL, ROA_F_DUMMY) == ACTION_DENY) { path_put(fasp); path_put(asp); Index: roa.c =================================================================== RCS file: roa.c diff -N roa.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ roa.c 26 Aug 2017 19:48:24 -0000 @@ -0,0 +1,1135 @@ +/* + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/un.h> +#include <net/if_types.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <limits.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "bgpd.h" +#include "rde.h" +#include "session.h" +#include "log.h" + +#define PFD_PIPE_MAIN 0 +#define PFD_PIPE_ROUTE 1 +#define PFD_PIPE_CTL 2 +#define PFD_VALIDATORS_START 3 + +#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) + +void roa_sighdlr(int); +void init_validator(struct validator *); +void roa_fsm(struct validator *, enum roa_events); +int roa_dispatch_msg(struct pollfd *, struct validator *); +int roa_process_msg(struct validator *); +int roa_connect(struct validator *); +int parse_roa_header(struct validator *, u_char *, u_int16_t *, + struct roa_header *); +void roa_sync(struct validator *); +void roa_close(struct validator *); +void change_roa_state(struct validator *, enum roa_state, + enum roa_events); +void roa_dispatch_imsg(struct imsgbuf *, int); +void log_roa_statechange(struct validator *, enum roa_state, + enum roa_events); +void flush_vpl(struct validator *); +int pfx4compare(struct roa_entry4 *, struct roa_entry4 *); +int pfx6compare(struct roa_entry6 *, struct roa_entry6 *); +struct validator *getvalidatorbyaddr(struct bgpd_addr *); + +struct roa_timer *roatimer_get(struct validator *, enum ROATimer); +struct roa_timer *roatimer_nextisdue(struct validator *); +void roatimer_remove_all(struct validator *); +time_t roatimer_nextduein(struct validator *); +void roatimer_stop(struct validator *, enum ROATimer); +void roatimer_set(struct validator *, enum ROATimer, + u_int); +void roatimer_remove(struct validator *, enum ROATimer); +extern time_t getmonotime(void); + +volatile sig_atomic_t roa_quit; +struct imsgbuf *ibuf_rde, *ibuf_main; +struct imsgbuf *ibuf_se_ctl; +int pending_reconf; +struct validator *validators, *nvalidators, *vconf; +u_int validator_cnt; + +RB_PROTOTYPE(pfx4tree, roa_entry4, entry, pfx4compare); +RB_PROTOTYPE(pfx6tree, roa_entry6, entry, pfx6compare); +RB_GENERATE(pfx4tree, roa_entry4, entry, pfx4compare); +RB_GENERATE(pfx6tree, roa_entry6, entry, pfx6compare); + +void +roa_sighdlr(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + roa_quit = 1; + break; + } +} + +void +roa_main(int debug, int verbose) +{ + struct passwd *pw; + struct validator *v, **validator_l = NULL, *last, *next; + struct pollfd *pfd = NULL; + void *newv; + unsigned int i, j; + int timeout; + u_int validator_l_elms = 0, pfd_elms = 0; + u_int new_cnt; + short events; + + log_init(debug, LOG_DAEMON); + log_setverbose(verbose); + + bgpd_process = PROC_ROA; + log_procinit(log_procnames[bgpd_process]); + + if ((pw = getpwnam(BGPD_USER)) == NULL) + fatal(NULL); + + if (chroot(pw->pw_dir) == -1) + fatal("chroot"); + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + setproctitle("roa 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 inet recvfd", NULL) == -1) + fatal("pledge"); + + signal(SIGTERM, roa_sighdlr); + signal(SIGINT, roa_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); + + validator_cnt = 0; + + log_info("roa engine ready"); + + while (roa_quit == 0) { + /* check for validators to be initialized or deleted */ + last = NULL; + if (!pending_reconf) { + for (v = validators; v != NULL; v = next) { + next = v->next; + + /* new validator that needs init? */ + if (v->state == STATE_NONE) + init_validator(v); + + /* deletion due? */ + if (v->reconf_action == RECONF_DELETE) { + if (last != NULL) + last->next = next; + else + validators = next; + roatimer_remove_all(v); + roa_close(v); + free(v); + validator_cnt--; + continue; + } + v->reconf_action = RECONF_NONE; + last = v; + } + } + + if (validator_cnt > validator_l_elms) { + if ((newv = reallocarray(validator_l, validator_cnt, + sizeof(struct validator *))) == NULL) { + /* panic for now */ + log_warn("could not resize validator_l from %u" + "-> %u entries", validator_l_elms, + validator_cnt); + fatalx("exiting"); + } + validator_l = newv; + validator_l_elms = validator_cnt; + } + + new_cnt = PFD_VALIDATORS_START + validator_cnt; + if (new_cnt > pfd_elms) { + if ((newv = reallocarray(pfd, new_cnt, + sizeof(struct pollfd))) == NULL) { + /* panic for now */ + log_warn("could not resize pfd from %u -> %u" + " entries", pfd_elms, new_cnt); + fatalx("exiting"); + } + pfd = newv; + pfd_elms = new_cnt; + } + + bzero(pfd, sizeof(struct pollfd) * pfd_elms); + + set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main); + set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde); + set_pollfd(&pfd[PFD_PIPE_CTL], ibuf_se_ctl); + + i = PFD_VALIDATORS_START; + timeout = 240; /* loop every 240s at least */ + + for (v = validators; v != NULL; v = v->next) { + time_t nextaction; + struct roa_timer *rt; + + if ((rt = roatimer_nextisdue(v)) != NULL) { + switch (rt->type) { + case ROATimer_IdleHold: + roa_fsm(v, ROAEVNT_START); + break; + case ROATimer_ConnectRetry: + roa_fsm(v, ROAEVNT_TIMER_CONNRETRY); + break; + case ROATimer_ROAPollingInterval: + roa_fsm(v, ROAEVNT_TIMER_ROAPOLL); + break; + default: + fatalx("timebomb"); + } + } + + if ((nextaction = roatimer_nextduein(v)) != -1 && + nextaction < timeout) + timeout = nextaction; + + events = POLLIN; + if (v->wbuf.queued > 0 || v->state == ROA_STATE_CONNECT) + events |= POLLOUT; + if (v->rbuf && v->rbuf->wpos) + timeout = 0; + if (v->fd != -1 && events != 0) { + pfd[i].fd = v->fd; + pfd[i].events = events; + validator_l[i - PFD_VALIDATORS_START] = v; + i++; + } + } + + if (timeout < 0) + timeout = 0; + if (poll(pfd, i, timeout * 1000) == -1) + if (errno != EINTR) + fatal("poll error"); + + if (handle_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main) == -1) { + log_warnx("ROA: Lost connection to parent"); + roa_quit = 1; + continue; + } else + roa_dispatch_imsg(ibuf_main, PFD_PIPE_MAIN); + + if (handle_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde) == -1) { + log_warnx("ROA: Lost connection to RDE"); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + ibuf_rde = NULL; + } else + roa_dispatch_imsg(ibuf_rde, PFD_PIPE_ROUTE); + + if (handle_pollfd(&pfd[PFD_PIPE_CTL], ibuf_se_ctl) == -1) { + log_warnx("ROA: Lost connection to session ctl"); + msgbuf_clear(&ibuf_se_ctl->w); + free(ibuf_se_ctl); + ibuf_se_ctl = NULL; + } else + roa_dispatch_imsg(ibuf_se_ctl, PFD_PIPE_CTL); + + for (j = PFD_VALIDATORS_START; j < i; j++) + roa_dispatch_msg(&pfd[j], + validator_l[j - PFD_VALIDATORS_START]); + + for (v = validators; v != NULL; v = v->next) + if (v->rbuf && v->rbuf->wpos) + roa_process_msg(v); + } + + while ((v = validators) != NULL) { + validators = v->next; + roatimer_remove_all(v); + roa_close(v); + free(v); + } + + free(validator_l); + free(pfd); + + /* close pipes */ + if (ibuf_rde) { + msgbuf_write(&ibuf_rde->w); + msgbuf_clear(&ibuf_rde->w); + close(ibuf_rde->fd); + free(ibuf_rde); + } + if (ibuf_se_ctl) { + msgbuf_write(&ibuf_se_ctl->w); + msgbuf_clear(&ibuf_se_ctl->w); + close(ibuf_se_ctl->fd); + free(ibuf_se_ctl); + } + msgbuf_write(&ibuf_main->w); + msgbuf_clear(&ibuf_main->w); + close(ibuf_main->fd); + free(ibuf_main); + + log_info("roa engine exiting"); + exit(0); +} + +void +init_validator(struct validator *v) +{ + TAILQ_INIT(&v->timers); + v->fd = v->wbuf.fd = -1; + validator_cnt++; + v->prefixes.v4count = 0; + v->prefixes.v6count = 0; + + change_roa_state(v, ROA_STATE_IDLE, ROAEVNT_NONE); + roatimer_set(v, ROATimer_IdleHold, 0); +} + +void +roa_fsm(struct validator *v, enum roa_events event) +{ + switch (v->state) { + case ROA_STATE_IDLE: + switch (event) { + case ROAEVNT_START: + roatimer_stop(v, ROATimer_IdleHold); + /* allocate read buffer */ + v->rbuf = calloc(1, sizeof(struct ibuf_read)); + if (v->rbuf == NULL) + fatal(NULL); + /* init write buffer */ + msgbuf_init(&v->wbuf); + change_roa_state(v, ROA_STATE_CONNECT, event); + roatimer_set(v, ROATimer_ConnectRetry, 180); + roa_connect(v); + break; + default: + break; + } + break; + case ROA_STATE_CONNECT: + switch (event) { + case ROAEVNT_CON_OPEN: + change_roa_state(v, ROA_STATE_SYNCING, event); + break; + case ROAEVNT_TIMER_CONNRETRY: + change_roa_state(v, ROA_STATE_CONNECT, event); + roatimer_set(v, ROATimer_ConnectRetry, 180); + roa_connect(v); + break; + default: + roatimer_stop(v, ROATimer_ROAPollingInterval); + roa_close(v); + change_roa_state(v, ROA_STATE_CONNECT, event); + roatimer_set(v, ROATimer_ConnectRetry, 180); + break; + } + break; + case ROA_STATE_SYNCING: + switch (event) { + case ROAEVNT_RCVD_SERIALNOTIFY: + case ROAEVNT_TIMER_ROAPOLL: + roatimer_stop(v, ROATimer_ROAPollingInterval); + roa_sync(v); + break; + case ROAEVNT_RCVD_ENDOFDATA: + change_roa_state(v, ROA_STATE_SYNCED, event); + break; + default: + roatimer_stop(v, ROATimer_ROAPollingInterval); + roa_close(v); + change_roa_state(v, ROA_STATE_CONNECT, event); + roatimer_set(v, ROATimer_ConnectRetry, 180); + break; + } + break; + case ROA_STATE_SYNCED: + switch (event) { + case ROAEVNT_RCVD_SERIALNOTIFY: + case ROAEVNT_TIMER_ROAPOLL: + change_roa_state(v, ROA_STATE_SYNCING, event); + break; + default: + break; + } + break; + default: + /* nothing */ + break; + } +} + +void +change_roa_state(struct validator *v, enum roa_state s, enum roa_events e) +{ + log_roa_statechange(v, s, e); + + switch (s) { + case ROA_STATE_IDLE: + v->state = ROA_STATE_IDLE; + break; + case ROA_STATE_CONNECT: + v->state = ROA_STATE_CONNECT; + roatimer_stop(v, ROATimer_ConnectRetry); + break; + case ROA_STATE_SYNCING: + v->state = ROA_STATE_SYNCING; + roatimer_stop(v, ROATimer_ConnectRetry); + roatimer_set(v, ROATimer_ROAPollingInterval, 0); + break; + case ROA_STATE_SYNCED: + v->state = ROA_STATE_SYNCED; + roatimer_set(v, ROATimer_ROAPollingInterval, v->pollinterval); + break; + default: + log_info("ROA: unknown state"); + break; + } +} + +void +roa_dispatch_imsg(struct imsgbuf *ibuf, int idx) +{ + struct imsg imsg; + enum reconf_action reconf; + int n, fd; + struct imsgbuf *i; + struct validator *v, *next; + struct p_validate p; + u_int32_t v4addr, r, m; + struct roa_entry4 *re4; + struct roa_entry6 *re6; + struct ibuf *wbuf; + + while (ibuf) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("roa_dispatch_imsg: imsg_get error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_SOCKET_ROA: + case IMSG_SOCKET_CONN_CTL_ROA: + if (idx != PFD_PIPE_MAIN) + fatalx("reconf request not from parent"); + if ((fd = imsg.fd) == -1) { + log_warnx("expected to receive imsg fd to " + "RDE 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_CTL_ROA) { + if (ibuf_se_ctl) { + log_warnx("Unexpected imsg ctl " + "connection to SE received"); + msgbuf_clear(&ibuf_se_ctl->w); + free(ibuf_se_ctl); + } + ibuf_se_ctl = i; + } else { + if (ibuf_rde) { + log_warnx("Unexpected imsg connection " + "to RDE received"); + msgbuf_clear(&ibuf_rde->w); + free(ibuf_rde); + } + ibuf_rde = i; + } + break; + case IMSG_RECONF_CONF: + if (idx != PFD_PIPE_MAIN) + fatalx("reconf request not from parent"); + nvalidators = NULL; + pending_reconf = 1; + break; + case IMSG_RECONF_VALIDATOR: + if (idx != PFD_PIPE_MAIN) + fatalx("reconf request not from parent"); + vconf = imsg.data; + v = getvalidatorbyaddr(&vconf->remote_addr); + if (v) { + memcpy(v, vconf, sizeof(struct validator)); + reconf = RECONF_KEEP; + } else { + if ((v = calloc(1, sizeof(struct validator))) == + NULL) + fatal("new validator"); + memcpy(v, vconf, sizeof(struct validator)); + v->state = v->prev_state = ROA_STATE_NONE; + v->next = nvalidators; + nvalidators = v; + reconf = RECONF_REINIT; + } + v->reconf_action = reconf; + break; + case IMSG_RECONF_DONE: + if (idx != PFD_PIPE_MAIN) + fatalx("reconf request not from parent"); + /* add new validators */ + for (v = nvalidators; v != NULL; v = next) { + next = v->next; + v->next = validators; + validators = v; + } + for (v = validators; v != NULL; v = v->next) { + /* needs to be deleted? */ + if (v->reconf_action == RECONF_NONE) + v->reconf_action = RECONF_DELETE; + } + pending_reconf = 0; + log_info("ROA reconfigured"); + break; + case IMSG_VALIDATE_PREFIX: + if (idx != PFD_PIPE_ROUTE) + fatalx("prefix not from RDE"); + + memcpy(&p, imsg.data, sizeof(struct p_validate)); + switch (p.aid) { + case AID_INET: + v4addr = ntohl(p.prefix4.s_addr); + for (v = validators; v != NULL; v = v->next) + RB_FOREACH(re4, pfx4tree, &v->prefixes.pfx4_h) { + if (!(re4->addr ^ (v4addr & (0xffffffff + << (32 - re4->minlen))))) { + if (p.prefixlen >= + re4->minlen && + p.prefixlen <= + re4->maxlen && + re4->asn == p.asn) + p.status = ROA_F_VALID; + else + p.status = ROA_F_INVALID; + } + } + break; + case AID_INET6: + for (v = validators; v != NULL; v = v->next) + RB_FOREACH(re6, pfx6tree, &v->prefixes.pfx6_h) { + for (r = 0; r < re6->minlen / 8; r++) + if ((u_int8_t)re6->addr[r] != + p.prefix6.s6_addr[r]) + break; + + if (r != (re6->minlen / 8)) + continue; + + if ((r = re6->minlen % 8) != 0) { + m = 0xff00 >> r; + if ((re6->addr[re6->minlen / 8] & m) != + (p.prefix6.s6_addr[re6->minlen / 8] & m)) + continue; + } + + if (p.prefixlen >= re6->minlen && + p.prefixlen <= re6->maxlen && + re6->asn == p.asn) + p.status = ROA_F_VALID; + else + p.status = ROA_F_INVALID; + } + break; + } + if ((wbuf = imsg_create(ibuf_rde, IMSG_STATUS, -1, 0, + sizeof(struct p_validate))) == NULL) + fatal("imsg_create error"); + if (imsg_add(wbuf, &p, sizeof(struct p_validate)) == -1) + fatal("imsg_add error"); + imsg_close(ibuf_rde, wbuf); + break; + case IMSG_CTL_SHOW_VALIDATOR: + if (idx != PFD_PIPE_CTL) + fatal("request not from ctl"); + for (v = validators; v != NULL; v = v->next) + imsg_compose(ibuf_se_ctl, + IMSG_CTL_SHOW_VALIDATOR, 0, imsg.hdr.pid, + -1, v, sizeof(struct validator)); + imsg_compose(ibuf_se_ctl, IMSG_CTL_END, 0, + imsg.hdr.pid, -1, NULL, 0); + break; + default: + break; + } + imsg_free(&imsg); + } +} + +int +roa_dispatch_msg(struct pollfd *pfd, struct validator *v) +{ + ssize_t n; + + if (v->state == STATE_CONNECT) { + if (pfd->revents & POLLOUT) { + if (pfd->revents & POLLIN) { + /* error occurred */ + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (1); + } + roa_fsm(v, ROAEVNT_CON_OPEN); + return (1); + } + if (pfd->revents & POLLHUP) { + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (1); + } + if (pfd->revents & (POLLERR|POLLNVAL)) { + roa_fsm(v, ROAEVNT_CON_FATAL); + return (1); + } + return (0); + } + + if (pfd->revents & POLLHUP) { + roa_fsm(v, ROAEVNT_CON_CLOSED); + return (1); + } + if (pfd->revents & (POLLERR|POLLNVAL)) { + roa_fsm(v, ROAEVNT_CON_FATAL); + return (1); + } + if (pfd->revents & POLLOUT && v->wbuf.queued) { + if (msgbuf_write(&v->wbuf) <= 0 && errno != EAGAIN) { + roa_fsm(v, ROAEVNT_CON_FATAL); + return (1); + } + if (!(pfd->revents & POLLIN)) + return (1); + } + + if (v->rbuf && pfd->revents & POLLIN) { + if ((n = read(v->fd, v->rbuf->buf + v->rbuf->wpos, + sizeof(v->rbuf->buf) - v->rbuf->wpos)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + roa_fsm(v, ROAEVNT_CON_FATAL); + } + return (1); + } + + if (n == 0) { + /* connection closed */ + roa_fsm(v, ROAEVNT_CON_CLOSED); + return (1); + } + + v->rbuf->wpos += n; + /* XXX v->stats.last_read = time(NULL); */ + return (1); + } + return (0); +} + +int +roa_process_msg(struct validator *v) +{ + ssize_t rpos, av, left; + int processed = 0; + u_int16_t msglen; + u_int32_t sn; + struct roa_header hdr; + struct roa_prefixv6 pfx6; + struct roa_prefixv4 pfx4; + struct roa_entry6 *e6; + struct roa_entry4 *e4; + + rpos = 0; + av = v->rbuf->wpos; + + /* session might drop to IDLE -> buffers deallocated + * we MUST check rbuf != NULL before use + */ + for (;;) { + if (rpos + ROA_MSGSIZE_HEADER > av) + break; + if (v->rbuf == NULL) + break; + if (parse_roa_header(v, v->rbuf->buf + rpos, &msglen, + &hdr) == -1) + return (0); + if (rpos + msglen > av) + break; + v->rbuf->rptr = v->rbuf->buf + rpos; + + switch (hdr.type) { + case ROA_TYPE_SERIALNOTIFY: + if (ntohs(hdr.sid) == v->sessionid) + roa_fsm(v, ROAEVNT_RCVD_SERIALNOTIFY); + else + roa_fsm(v, ROAEVNT_CON_FATAL); + break; + case ROA_TYPE_CACHERESPONSE: + /* XXX ROA : perhaps should we have a state before + prefix PDU acceptance... */ + break; + case ROA_TYPE_IPV4PREFIX: + memcpy(&pfx4, v->rbuf->rptr + ROA_MSGSIZE_HEADER, + sizeof(pfx4)); + e4 = malloc(sizeof(struct roa_entry4)); + e4->asn = ntohl(pfx4.asn); + e4->minlen = pfx4.minlen; + e4->maxlen = pfx4.maxlen; + e4->addr = ntohl(pfx4.addr); + + if (pfx4.flags) { /* add prefix */ + if (RB_INSERT(pfx4tree, &v->prefixes.pfx4_h, + e4)) + fatal("RB_INSERT fails"); + v->prefixes.v4count++; + + } else { /* withdraw */ + if (!RB_REMOVE(pfx4tree, &v->prefixes.pfx4_h, + e4)) + fatal("RB_REMOVE fails"); + v->prefixes.v4count--; + } + break; + case ROA_TYPE_IPV6PREFIX: + memcpy(&pfx6, v->rbuf->rptr + ROA_MSGSIZE_HEADER, + sizeof(pfx6)); + e6 = malloc(sizeof(struct roa_entry6)); + e6->asn = ntohl(pfx6.asn); + e6->minlen = pfx6.minlen; + e6->maxlen = pfx6.maxlen; + memcpy(e6->addr, &pfx6.addr, sizeof(pfx6.addr)); + + if (pfx6.flags) { /* add prefix */ + if (RB_INSERT(pfx6tree, &v->prefixes.pfx6_h, + e6)) + fatal("RB_INSERT fails"); + v->prefixes.v6count++; + + } else { /* withdraw */ + if (!RB_REMOVE(pfx6tree, &v->prefixes.pfx6_h, + e6)) + fatal("RB_REMOVE fails"); + v->prefixes.v6count--; + } + break; + case ROA_TYPE_ENDOFDATA: + memcpy(&sn, v->rbuf->rptr + ROA_MSGSIZE_HEADER, + sizeof(v->serialnum)); + v->sessionid = ntohs(hdr.sid); + v->serialnum = ntohl(sn); + roa_fsm(v, ROAEVNT_RCVD_ENDOFDATA); + imsg_compose(ibuf_rde, IMSG_VPL_UPDATE, 0, 0, -1, 0, 0); + break; + case ROA_TYPE_CACHERESET: + v->sessionid = 0; + v->serialnum = 0; + flush_vpl(v); + roa_sync(v); + break; + case ROA_TYPE_ERRORREPORT: + log_info("ROA: Got error code from validator"); + break; + default: + log_info("ROA: received message with unknown type %u", + hdr.type); + roa_fsm(v, ROAEVNT_CON_FATAL); + break; + } + rpos += msglen; + if (++processed > MSG_PROCESS_LIMIT) + break; + } + + if (v->rbuf == NULL) + return (1); + + if (rpos < av) { + left = av - rpos; + memmove(&v->rbuf->buf, v->rbuf->buf + rpos, left); + v->rbuf->wpos = left; + } else + v->rbuf->wpos = 0; + + return (1); +} + +void +flush_vpl(struct validator *v) +{ + struct roa_entry4 *e4, *n4; + struct roa_entry6 *e6, *n6; + + /* Free IPv4 validated prefix list */ + RB_FOREACH_SAFE(e4, pfx4tree, &v->prefixes.pfx4_h, n4) { + RB_REMOVE(pfx4tree, &v->prefixes.pfx4_h, e4); + free(e4); + } + /* Free IPv6 validated prefix list */ + RB_FOREACH_SAFE(e6, pfx6tree, &v->prefixes.pfx6_h, n6) { + RB_REMOVE(pfx6tree, &v->prefixes.pfx6_h, e6); + free(e6); + } + + v->prefixes.v4count = 0; + v->prefixes.v6count = 0; + + imsg_compose(ibuf_rde, IMSG_ROA_NOTREADY, 0, 0, -1, 0, 0); +} + +int +roa_connect(struct validator *v) +{ + struct sockaddr *sa; + int nodelay = 1; + + if (v->fd != -1) + return (-1); + + if ((v->fd = socket(aid2af(v->remote_addr.aid), + SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) { + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (-1); + } + + v->wbuf.fd = v->fd; + + if ((sa = addr2sa(&v->local_addr, 0)) != NULL) { + if (bind(v->fd, sa, sa->sa_len) == -1) { + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (-1); + } + } + + /* set TCP_NODELAY */ + if (setsockopt(v->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, + sizeof(nodelay)) == -1) { + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (-1); + } + + sa = addr2sa(&v->remote_addr, v->port); + if (connect(v->fd, sa, sa->sa_len) == -1) { + if (errno != EINPROGRESS) { + v->lasterr = errno; + roa_fsm(v, ROAEVNT_CON_OPENFAIL); + return (-1); + } + } else + roa_fsm(v, ROAEVNT_CON_OPEN); + + return (0); +} + +struct roa_timer * +roatimer_get(struct validator *v, enum ROATimer timer) +{ + struct roa_timer *vt; + + TAILQ_FOREACH(vt, &v->timers, entry) + if (vt->type == timer) + break; + + return (vt); +} + +struct roa_timer * +roatimer_nextisdue(struct validator *v) +{ + struct roa_timer *vt; + + vt = TAILQ_FIRST(&v->timers); + if (vt != NULL && vt->val > 0 && vt->val <= getmonotime()) + return (vt); + + return (NULL); +} + +void +roatimer_remove_all(struct validator *v) +{ + struct roa_timer *vt; + + while ((vt = TAILQ_FIRST(&v->timers)) != NULL) { + TAILQ_REMOVE(&v->timers, vt, entry); + free(vt); + } +} + +void +roatimer_remove(struct validator *v, enum ROATimer timer) +{ + struct roa_timer *vt = roatimer_get(v, timer); + + if (vt != NULL) { + TAILQ_REMOVE(&v->timers, vt, entry); + free(vt); + } +} + +time_t +roatimer_nextduein(struct validator *v) +{ + struct roa_timer *vt; + + if ((vt = TAILQ_FIRST(&v->timers)) != NULL && vt->val > 0) + return (MAXIMUM(vt->val - getmonotime(), 0)); + + return (-1); +} + +void +roatimer_stop(struct validator *v, enum ROATimer timer) +{ + struct roa_timer *vt = roatimer_get(v, timer); + + if (vt != NULL) { + vt->val = 0; + TAILQ_REMOVE(&v->timers, vt, entry); + TAILQ_INSERT_TAIL(&v->timers, vt, entry); + } +} + +void +roatimer_set(struct validator *v, enum ROATimer timer, u_int offset) +{ + struct roa_timer *t, *vt = roatimer_get(v, timer); + + if (vt == NULL) { + if ((vt = malloc(sizeof(*vt))) == NULL) + fatal("roatimer_set"); + vt->type = timer; + } else { + if (vt->val == getmonotime() + (time_t)offset) + return; + TAILQ_REMOVE(&v->timers, vt, entry); + } + + vt->val = getmonotime() + (time_t)offset; + + TAILQ_FOREACH(t, &v->timers, entry) + if (t->val == 0 || t->val > vt->val) + break; + + if (t != NULL) + TAILQ_INSERT_BEFORE(t, vt, entry); + else + TAILQ_INSERT_TAIL(&v->timers, vt, entry); +} + +int +pfx4compare(struct roa_entry4 *e1, struct roa_entry4 *e2) +{ + if (e1->addr == e2->addr) { + if (e1->asn == e2->asn) { + if (e1->minlen == e2->minlen) { + return (e1->maxlen < e2->maxlen ? -1 : + e1->maxlen > e2->maxlen); + } else + return (e1->minlen < e2->minlen ? -1 : + e1->minlen > e2->minlen); + } else + return (e1->asn < e2->asn ? -1 : e1->asn > e2->asn); + } else + return (e1->addr < e2->addr ? -1 : e1->addr > e2->addr); +} + +int +pfx6compare(struct roa_entry6 *e1, struct roa_entry6 *e2) +{ + int i = 0, addrcmp = 0; + + for (; i < 16 && addrcmp == 0; i++) + addrcmp = (e1->addr[i] < e2->addr[i] ? -1 : + e1->addr[i] > e2->addr[i]); + + if (addrcmp == 0) { + if (e1->asn == e2->asn) { + if (e1->minlen == e2->minlen) { + return (e1->maxlen < e2->maxlen ? -1 : + e1->maxlen > e2->maxlen); + } else + return (e1->minlen < e2->minlen ? -1 : + e1->minlen > e2->minlen); + } else + return (e1->asn < e2->asn ? -1 : e1->asn > e2->asn); + } else + return (addrcmp); +} + +struct validator * +getvalidatorbyaddr(struct bgpd_addr *addr) +{ + struct validator *v; + + for (v = validators; v != NULL && memcmp(&v->remote_addr, + addr, sizeof(v->remote_addr)); v= v->next) + ; + return (v); +} + +void +roa_sync(struct validator *v) +{ + struct roa_header hdr; + struct ibuf *buf; + u_int32_t len = 8, sn; + int errs = 0; + + buf = NULL; + + bzero(&hdr, sizeof(hdr)); + hdr.version = ROA_PROTOCOL_VERSION; + + if (v->sessionid) { + hdr.type = ROA_TYPE_SERIALQUERY; + hdr.sid = htons(v->sessionid); + len += 4; + } else + hdr.type = ROA_TYPE_RESETQUERY; + + hdr.len = htonl(len); + buf = ibuf_open(len); + errs += ibuf_add(buf, &hdr.version, sizeof(hdr.version)); + errs += ibuf_add(buf, &hdr.type, sizeof(hdr.type)); + errs += ibuf_add(buf, &hdr.sid, sizeof(hdr.sid)); + errs += ibuf_add(buf, &hdr.len, sizeof(hdr.len)); + if (v->sessionid) { + sn = htonl(v->serialnum); + errs += ibuf_add(buf, &sn, sizeof(v->serialnum)); + } + + if (errs) { + ibuf_free(buf); + roa_fsm(v, ROAEVNT_CON_FATAL); + return; + } + + v->wbuf.fd = v->fd; + ibuf_close(&v->wbuf, buf); +} + +int +parse_roa_header(struct validator *v, u_char *data, u_int16_t *len, + struct roa_header *hdr) +{ + u_char *p; + struct roa_header h; + + p = data; + memcpy(&h, p, sizeof(h)); + + hdr->version = h.version; + hdr->type = h.type; + if (hdr->version != ROA_PROTOCOL_VERSION) + return (-1); + + hdr->sid = ntohs(h.sid); + hdr->len = ntohl(h.len); + *len = hdr->len; + + /* XXX ROA : ROA_MSGSIZE_ERROR */ + if (*len < ROA_MSGSIZE_HEADER || *len > ROA_MSGSIZE_ERROR) + return (-1); + + switch (hdr->type) { + case ROA_TYPE_SERIALNOTIFY: + if (*len != ROA_MSGSIZE_SERIALNOTIFY) + return (-1); + break; + case ROA_TYPE_CACHERESPONSE: + if (*len != ROA_MSGSIZE_CACHERESPONSE) + return (-1); + break; + case ROA_TYPE_IPV4PREFIX: + if (*len != ROA_MSGSIZE_IPV4PREFIX) + return (-1); + break; + case ROA_TYPE_IPV6PREFIX: + if (*len != ROA_MSGSIZE_IPV6PREFIX) + return (-1); + break; + case ROA_TYPE_ENDOFDATA: + if (*len != ROA_MSGSIZE_ENDOFDATA) + return (-1); + break; + case ROA_TYPE_CACHERESET: + if (*len != ROA_MSGSIZE_CACHERESET) + return (-1); + break; + case ROA_TYPE_ERRORREPORT: + if (*len > ROA_MSGSIZE_ERROR) + return (-1); + break; + default: + log_info("Unknown ROA header message"); + return (-1); + } + return (0); +} + +void +roa_close(struct validator *v) +{ + flush_vpl(v); + if (v->fd != -1) + close(v->fd); + v->fd = v->wbuf.fd = -1; +} + +void +log_roa_statechange(struct validator *v, enum roa_state s, + enum roa_events e) +{ + log_info("validator %s: state change %s -> %s, reason: %s", + log_addr(&v->remote_addr), roastatenames[v->state], + roastatenames[s], roaeventnames[e]); +} Index: roa.h =================================================================== RCS file: roa.h diff -N roa.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ roa.h 26 Aug 2017 19:48:24 -0000 @@ -0,0 +1,187 @@ +/* + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __ROA_H__ +#define __ROA_H__ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <stdint.h> + +#include "bgpd.h" + +#define ROA_PORT 323 +#define ROA_POLLING_INTERVAL 3600 +#define ROA_PREFERENCE 128 + +#define ROA_PROTOCOL_VERSION 0 + +#define ROA_MSGSIZE_HEADER 8 +#define ROA_MSGSIZE_SERIALNOTIFY 12 +#define ROA_MSGSIZE_CACHERESPONSE 8 +#define ROA_MSGSIZE_IPV4PREFIX 20 +#define ROA_MSGSIZE_IPV6PREFIX 32 +#define ROA_MSGSIZE_ENDOFDATA 12 +#define ROA_MSGSIZE_CACHERESET 8 +#define ROA_MSGSIZE_ERROR 2048 /* Arbitrary limit */ + +#define ROA_TYPE_SERIALNOTIFY 0x00 +#define ROA_TYPE_SERIALQUERY 0x01 +#define ROA_TYPE_RESETQUERY 0x02 +#define ROA_TYPE_CACHERESPONSE 0x03 +#define ROA_TYPE_IPV4PREFIX 0x04 +#define ROA_TYPE_IPV6PREFIX 0x06 +#define ROA_TYPE_ENDOFDATA 0x07 +#define ROA_TYPE_CACHERESET 0x08 +#define ROA_TYPE_ERRORREPORT 0x10 +#define ROA_TYPE_RESERVED 0xFF + +void roa_main(int, int); + +enum roa_status_flag { + ROA_F_NOTFOUND, + ROA_F_VALID, + ROA_F_INVALID, + ROA_F_DUMMY +}; + +enum roa_state { + ROA_STATE_NONE, + ROA_STATE_IDLE, + ROA_STATE_CONNECT, + ROA_STATE_SYNCING, + ROA_STATE_SYNCED +}; + +enum roa_events { + ROAEVNT_NONE, + ROAEVNT_START, + ROAEVNT_CON_OPEN, + ROAEVNT_CON_CLOSED, + ROAEVNT_CON_OPENFAIL, + ROAEVNT_CON_FATAL, + ROAEVNT_TIMER_CONNRETRY, + ROAEVNT_TIMER_ROAPOLL, + ROAEVNT_RCVD_ENDOFDATA, + ROAEVNT_RCVD_SERIALNOTIFY +}; + +static const char * const roastatenames[] = { + "None", + "Idle", + "Connect", + "Syncing", + "Synced" +}; + +static const char * const roaeventnames[] = { + "None", + "Start", + "Connection opened", + "Connection closed", + "Connection open failed", + "Fatal error", + "ConnectRetryTimer expired", + "ROA PollTimer expired", + "ENDOFDATA received", + "SERIALNOTIFY received" +}; + +enum ROATimer { + ROATimer_ConnectRetry, + ROATimer_IdleHold, + ROATimer_ROAPollingInterval +}; + +struct roa_timer { + TAILQ_ENTRY(roa_timer) entry; + enum ROATimer type; + time_t val; +}; + +TAILQ_HEAD(roa_timer_head, roa_timer); + +struct vpl { + u_int64_t v6count; /* beware of overflow... */ + u_int32_t v4count; + RB_HEAD(pfx4tree, roa_entry4) pfx4_h; + RB_HEAD(pfx6tree, roa_entry6) pfx6_h; +}; + +struct validator { + /* validator config */ + struct bgpd_addr remote_addr; + struct bgpd_addr local_addr; + u_int16_t port; + u_int16_t pollinterval; + u_int8_t preference; + u_int8_t id; + /* validator */ + u_int32_t serialnum; + u_int16_t sessionid; + enum roa_state state; + enum roa_state prev_state; + int fd; + int lasterr; + struct ibuf_read *rbuf; + struct msgbuf wbuf; + struct roa_timer_head timers; + enum reconf_action reconf_action; + struct vpl prefixes; + struct validator *next; +}; + +// RFC6810 +struct roa_header { + u_int8_t version; + u_int8_t type; + u_int16_t sid; + u_int32_t len; +}; + +struct roa_prefixv4 { + u_int8_t flags; + u_int8_t minlen; + u_int8_t maxlen; + u_int8_t dummy; + u_int32_t addr; + u_int32_t asn; +}; + +struct roa_prefixv6 { + u_int8_t flags; + u_int8_t minlen; + u_int8_t maxlen; + u_int8_t dummy; + u_int8_t addr[16]; + u_int32_t asn; +}; + +struct roa_entry4 { + RB_ENTRY(roa_entry4) entry; + u_int32_t addr; + u_int8_t minlen; + u_int8_t maxlen; + u_int32_t asn; +}; + +struct roa_entry6 { + RB_ENTRY(roa_entry6) entry; + u_int8_t addr[16]; + u_int8_t minlen; + u_int8_t maxlen; + u_int32_t asn; +}; + +#endif /* __ROA_H__ */ Index: session.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.c,v retrieving revision 1.364 diff -u -p -r1.364 session.c --- session.c 29 May 2017 14:22:51 -0000 1.364 +++ session.c 26 Aug 2017 19:48:25 -0000 @@ -45,16 +45,18 @@ #include "bgpd.h" #include "mrt.h" +#include "roa.h" #include "session.h" #include "log.h" #define PFD_PIPE_MAIN 0 #define PFD_PIPE_ROUTE 1 #define PFD_PIPE_ROUTE_CTL 2 -#define PFD_SOCK_CTL 3 -#define PFD_SOCK_RCTL 4 -#define PFD_SOCK_PFKEY 5 -#define PFD_LISTENERS_START 6 +#define PFD_PIPE_ROA_CTL 3 +#define PFD_SOCK_CTL 4 +#define PFD_SOCK_RCTL 5 +#define PFD_SOCK_PFKEY 6 +#define PFD_LISTENERS_START 7 void session_sighdlr(int); int setup_listeners(u_int *); @@ -112,7 +114,7 @@ int pending_reconf; int csock = -1, rcsock = -1; u_int peer_cnt; struct imsgbuf *ibuf_rde; -struct imsgbuf *ibuf_rde_ctl; +struct imsgbuf *ibuf_rde_ctl, *ibuf_roa_ctl; struct imsgbuf *ibuf_main; struct mrt_head mrthead; @@ -357,6 +359,7 @@ session_main(int debug, int verbose) set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main); set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde); set_pollfd(&pfd[PFD_PIPE_ROUTE_CTL], ibuf_rde_ctl); + set_pollfd(&pfd[PFD_PIPE_ROA_CTL], ibuf_roa_ctl); if (pauseaccept == 0) { pfd[PFD_SOCK_CTL].fd = csock; @@ -512,6 +515,16 @@ session_main(int debug, int verbose) session_dispatch_imsg(ibuf_rde_ctl, PFD_PIPE_ROUTE_CTL, &listener_cnt); + if (handle_pollfd(&pfd[PFD_PIPE_ROA_CTL], ibuf_roa_ctl) == + -1) { + log_warnx("SE: Lost connection to ROA control"); + msgbuf_clear(&ibuf_roa_ctl->w); + free(ibuf_roa_ctl); + ibuf_roa_ctl = NULL; + } else + session_dispatch_imsg(ibuf_roa_ctl, PFD_PIPE_ROA_CTL, + &listener_cnt); + if (pfd[PFD_SOCK_CTL].revents & POLLIN) ctl_cnt += control_accept(csock, 0); @@ -577,6 +590,11 @@ session_main(int debug, int verbose) close(ibuf_rde->fd); free(ibuf_rde); } + if (ibuf_roa_ctl) { + msgbuf_clear(&ibuf_roa_ctl->w); + close(ibuf_roa_ctl->fd); + free(ibuf_roa_ctl); + } if (ibuf_rde_ctl) { msgbuf_clear(&ibuf_rde_ctl->w); close(ibuf_rde_ctl->fd); @@ -2593,6 +2611,7 @@ session_dispatch_imsg(struct imsgbuf *ib switch (imsg.hdr.type) { case IMSG_SOCKET_CONN: case IMSG_SOCKET_CONN_CTL: + case IMSG_SOCKET_CONN_CTL_ROA: if (idx != PFD_PIPE_MAIN) fatalx("reconf request not from parent"); if ((fd = imsg.fd) == -1) { @@ -2603,7 +2622,15 @@ session_dispatch_imsg(struct imsgbuf *ib if ((i = malloc(sizeof(struct imsgbuf))) == NULL) fatal(NULL); imsg_init(i, fd); - if (imsg.hdr.type == IMSG_SOCKET_CONN) { + if (imsg.hdr.type == IMSG_SOCKET_CONN_CTL_ROA) { + if (ibuf_roa_ctl) { + log_warnx("Unexpected imsg ctl " + "connection to ROA received"); + msgbuf_clear(&ibuf_roa_ctl->w); + free(ibuf_roa_ctl); + } + ibuf_roa_ctl = i; + } else if (imsg.hdr.type == IMSG_SOCKET_CONN) { if (ibuf_rde) { log_warnx("Unexpected imsg connection " "to RDE received"); @@ -2884,6 +2911,7 @@ session_dispatch_imsg(struct imsgbuf *ib control_imsg_relay(&imsg); break; case IMSG_CTL_END: + case IMSG_CTL_SHOW_VALIDATOR: case IMSG_CTL_RESULT: control_imsg_relay(&imsg); break; @@ -3227,6 +3255,16 @@ imsg_ctl_rde(int type, pid_t pid, void * * regular imsg socket. */ return (imsg_compose(ibuf_rde_ctl, type, 0, pid, -1, data, datalen)); +} + +int +imsg_ctl_roa(int type, pid_t pid, void *data, u_int16_t datalen) +{ + /* + * Use control socket to talk to ROA engine to bypass the queue of the + * regular imsg socket. + */ + return (imsg_compose(ibuf_roa_ctl, type, 0, pid, -1, data, datalen)); } void Index: session.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.h,v retrieving revision 1.123 diff -u -p -r1.123 session.h --- session.h 28 May 2017 12:21:36 -0000 1.123 +++ session.h 26 Aug 2017 19:48:25 -0000 @@ -19,6 +19,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <time.h> +#include "roa.h" #define MAX_BACKLOG 5 #define INTERVAL_CONNECTRETRY 120 @@ -272,7 +273,8 @@ void mrt_dump_state(struct mrt *, u_in void mrt_done(void *); /* parse.y */ -int parse_config(char *, struct bgpd_config *, struct peer **); +int parse_config(char *, struct bgpd_config *, struct peer **, + struct validator **); /* pfkey.c */ int pfkey_read(int, struct sadb_msg *); @@ -282,8 +284,8 @@ int pfkey_init(struct bgpd_sysdep *); /* printconf.c */ void print_config(struct bgpd_config *, struct rib_names *, - struct network_head *, struct peer *, struct filter_head *, - struct mrt_head *, struct rdomain_head *); + struct network_head *, struct peer *, struct validator *, + struct filter_head *, struct mrt_head *, struct rdomain_head *); /* rde.c */ void rde_main(int, int); @@ -296,6 +298,7 @@ struct peer *getpeerbyaddr(struct bgpd_a struct peer *getpeerbydesc(const char *); int imsg_ctl_parent(int, u_int32_t, pid_t, void *, u_int16_t); int imsg_ctl_rde(int, pid_t, void *, u_int16_t); +int imsg_ctl_roa(int, pid_t, void *, u_int16_t); void session_stop(struct peer *, u_int8_t); /* timer.c */ Index: timer.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/timer.c,v retrieving revision 1.17 diff -u -p -r1.17 timer.c --- timer.c 24 Jan 2017 04:22:42 -0000 1.17 +++ timer.c 26 Aug 2017 19:48:25 -0000 @@ -20,6 +20,7 @@ #include <stdlib.h> #include "bgpd.h" +#include "roa.h" #include "session.h" #include "log.h" Index: util.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/util.c,v retrieving revision 1.25 diff -u -p -r1.25 util.c --- util.c 31 May 2017 10:44:00 -0000 1.25 +++ util.c 26 Aug 2017 19:48:25 -0000 @@ -601,3 +601,28 @@ sa2addr(struct sockaddr *sa, struct bgpd break; } } + +u_int32_t +aspath_getsourceas(struct rde_aspath *asp) +{ + int final = 0; + u_int8_t *seg = asp->aspath->data; + u_int16_t len = asp->aspath->len; + u_int8_t seg_type, seg_len; + u_int16_t seg_size; + + for (; len > 0; len -= seg_size, seg += seg_size) { + seg_type = seg[0]; + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int32_t) * seg_len; + + final = (len == seg_size); + if (!final) + continue; + + return aspath_extract(seg, seg_len - 1); + } + + return (0); +} +