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);
+}
+

Reply via email to