On Tue, Jan 26, 2021 at 10:31:40AM +0100, Claudio Jeker wrote:
> This diff adds initial RTR (RPKI to Router) support to bgpd.
> Instead of loading the roa-set table via the configuration bgpd will use
> RTR to load the RPKI table from one or multiple RTR servers.
> This has the benefit that in large setups only a few systems need to run
> rpki-client instead of running it on every router.
>
> Currently only RTR via TCP is supported. Basic 'bgpctl show rtr' output is
> available to monitor sessions and 'bgpctl show sets' also shows the right
> info. There is a lot more that can be added here but this diff is already
> big enough.
>
> Enjoy
I know this is a large diff but I would like to have somebody review it
before commit. This will affect any kind of RPKI usage via roa-set or rtr
session.
--
:wq Claudio
Index: bgpctl/bgpctl.8
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.8,v
retrieving revision 1.95
diff -u -p -r1.95 bgpctl.8
--- bgpctl/bgpctl.8 10 May 2020 13:38:46 -0000 1.95
+++ bgpctl/bgpctl.8 26 Jan 2021 08:42:39 -0000
@@ -33,7 +33,7 @@ program controls the
.Xr bgpd 8
daemon.
Commands may be abbreviated to the minimum unambiguous prefix; for example,
-.Cm s s
+.Cm s su
for
.Cm show summary .
.Pp
@@ -409,6 +409,18 @@ or
Multiple options can be used at the same time and the
.Ar neighbor
filter can be combined with other filters.
+.It Cm show rtr
+Show a list of all
+.Em RTR
+sessions, including information about the session state.
+.It Cm show sets
+Show a list summarizing all
+.Em roa-set ,
+.Em as-set ,
+.Em prefix-set ,
+and
+.Em origin-set
+tables.
.It Cm show summary
Show a list of all neighbors, including information about the session state
and message counters:
Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.264
diff -u -p -r1.264 bgpctl.c
--- bgpctl/bgpctl.c 30 Dec 2020 07:31:19 -0000 1.264
+++ bgpctl/bgpctl.c 25 Jan 2021 18:06:13 -0000
@@ -216,6 +216,9 @@ main(int argc, char *argv[])
case SHOW_SET:
imsg_compose(ibuf, IMSG_CTL_SHOW_SET, 0, 0, -1, NULL, 0);
break;
+ case SHOW_RTR:
+ imsg_compose(ibuf, IMSG_CTL_SHOW_RTR, 0, 0, -1, NULL, 0);
+ break;
case SHOW_NEIGHBOR:
case SHOW_NEIGHBOR_TIMERS:
case SHOW_NEIGHBOR_TERSE:
@@ -393,18 +396,19 @@ int
show(struct imsg *imsg, struct parse_result *res)
{
struct peer *p;
- struct ctl_timer *t;
+ struct ctl_timer t;
struct ctl_show_interface *iface;
struct ctl_show_nexthop *nh;
- struct ctl_show_set *set;
+ struct ctl_show_set set;
+ struct ctl_show_rtr rtr;
struct kroute_full *kf;
struct ktable *kt;
struct ctl_show_rib rib;
+ struct rde_memstats stats;
+ struct rde_hashstats hash;
u_char *asdata;
- struct rde_memstats stats;
- struct rde_hashstats hash;
- u_int rescode, ilen;
- size_t aslen;
+ u_int rescode, ilen;
+ size_t aslen;
switch (imsg->hdr.type) {
case IMSG_CTL_SHOW_NEIGHBOR:
@@ -412,9 +416,11 @@ show(struct imsg *imsg, struct parse_res
output->neighbor(p, res);
break;
case IMSG_CTL_SHOW_TIMER:
- t = imsg->data;
- if (t->type > 0 && t->type < Timer_Max)
- output->timer(t);
+ if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(t))
+ errx(1, "wrong imsg len");
+ memcpy(&t, imsg->data, sizeof(t));
+ if (t.type > 0 && t.type < Timer_Max)
+ output->timer(&t);
break;
case IMSG_CTL_SHOW_INTERFACE:
iface = imsg->data;
@@ -463,16 +469,28 @@ show(struct imsg *imsg, struct parse_res
output->attr(imsg->data, ilen, res);
break;
case IMSG_CTL_SHOW_RIB_MEM:
+ if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(stats))
+ errx(1, "wrong imsg len");
memcpy(&stats, imsg->data, sizeof(stats));
output->rib_mem(&stats);
break;
case IMSG_CTL_SHOW_RIB_HASH:
+ if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(hash))
+ errx(1, "wrong imsg len");
memcpy(&hash, imsg->data, sizeof(hash));
output->rib_hash(&hash);
break;
case IMSG_CTL_SHOW_SET:
- set = imsg->data;
- output->set(set);
+ if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(set))
+ errx(1, "wrong imsg len");
+ memcpy(&set, imsg->data, sizeof(set));
+ output->set(&set);
+ break;
+ case IMSG_CTL_SHOW_RTR:
+ if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rtr))
+ errx(1, "wrong imsg len");
+ memcpy(&rtr, imsg->data, sizeof(rtr));
+ output->rtr(&rtr);
break;
case IMSG_CTL_RESULT:
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode)) {
Index: bgpctl/bgpctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.h,v
retrieving revision 1.8
diff -u -p -r1.8 bgpctl.h
--- bgpctl/bgpctl.h 30 Dec 2020 07:31:19 -0000 1.8
+++ bgpctl/bgpctl.h 25 Jan 2021 18:06:25 -0000
@@ -31,6 +31,7 @@ struct output {
void (*rib_hash)(struct rde_hashstats *);
void (*rib_mem)(struct rde_memstats *);
void (*set)(struct ctl_show_set *);
+ void (*rtr)(struct ctl_show_rtr *);
void (*result)(u_int);
void (*tail)(void);
};
Index: bgpctl/output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
retrieving revision 1.12
diff -u -p -r1.12 output.c
--- bgpctl/output.c 25 Jan 2021 09:17:33 -0000 1.12
+++ bgpctl/output.c 26 Jan 2021 09:21:43 -0000
@@ -266,7 +266,7 @@ show_neighbor_full(struct peer *p, struc
if (p->conf.down) {
printf(", marked down");
}
- if (*(p->conf.reason)) {
+ if (p->conf.reason[0]) {
printf(" with shutdown reason \"%s\"",
log_reason(p->conf.reason));
}
@@ -301,7 +301,7 @@ show_neighbor_full(struct peer *p, struc
show_neighbor_msgstats(p);
printf("\n");
- if (*(p->stats.last_reason)) {
+ if (p->stats.last_reason[0]) {
printf(" Last received shutdown reason: \"%s\"\n",
log_reason(p->stats.last_reason));
}
@@ -978,6 +978,45 @@ show_rib_set(struct ctl_show_set *set)
}
static void
+show_rtr(struct ctl_show_rtr *rtr)
+{
+ static int not_first;
+
+ if (not_first)
+ printf("\n");
+ not_first = 1;
+
+ printf("RTR neighbor is %s, port %u\n",
+ log_addr(&rtr->remote_addr), rtr->remote_port);
+ if (rtr->descr[0])
+ printf(" Description: %s\n", rtr->descr);
+ if (rtr->local_addr.aid != AID_UNSPEC)
+ printf(" Local Address: %s\n", log_addr(&rtr->local_addr));
+ if (rtr->session_id != -1)
+ printf (" Session ID: %d Serial #: %u\n",
+ rtr->session_id, rtr->serial);
+ printf(" Refresh: %u, Retry: %u, Expire: %u\n",
+ rtr->refresh, rtr->retry, rtr->expire);
+
+ if (rtr->last_sent_error != NO_ERROR) {
+ printf(" Last sent error: %s\n",
+ log_rtr_error(rtr->last_sent_error));
+ if (rtr->last_sent_msg[0])
+ printf(" with reason \"%s\"",
+ log_reason(rtr->last_sent_msg));
+ }
+ if (rtr->last_recv_error != NO_ERROR) {
+ printf("Last received error: %s\n",
+ log_rtr_error(rtr->last_recv_error));
+ if (rtr->last_recv_msg[0])
+ printf(" with reason \"%s\"",
+ log_reason(rtr->last_recv_msg));
+ }
+
+ printf("\n");
+}
+
+static void
show_result(u_int rescode)
{
if (rescode == 0)
@@ -1009,6 +1048,7 @@ const struct output show_output = {
.rib_mem = show_rib_mem,
.rib_hash = show_rib_hash,
.set = show_rib_set,
+ .rtr = show_rtr,
.result = show_result,
.tail = show_tail
};
Index: bgpctl/output_json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v
retrieving revision 1.6
diff -u -p -r1.6 output_json.c
--- bgpctl/output_json.c 25 Jan 2021 09:17:33 -0000 1.6
+++ bgpctl/output_json.c 26 Jan 2021 09:21:13 -0000
@@ -202,10 +202,10 @@ json_neighbor_full(struct peer *p)
json_neighbor_stats(p);
/* errors */
- if (*(p->conf.reason))
+ if (p->conf.reason[0])
json_do_printf("my_shutdown_reason", "%s",
log_reason(p->conf.reason));
- if (*(p->stats.last_reason))
+ if (p->stats.last_reason[0])
json_do_printf("last_shutdown_reason", "%s",
log_reason(p->stats.last_reason));
errstr = fmt_errstr(p->stats.last_sent_errcode,
@@ -937,6 +937,43 @@ json_rib_set(struct ctl_show_set *set)
}
static void
+json_rtr(struct ctl_show_rtr *rtr)
+{
+ json_do_array("rtrs");
+
+ json_do_object("rtr");
+ if (rtr->descr[0])
+ json_do_printf("descr", "%s", rtr->descr);
+ json_do_printf("remote_addr", "%s", log_addr(&rtr->remote_addr));
+ json_do_uint("remote_port", rtr->remote_port);
+ if (rtr->local_addr.aid != AID_UNSPEC)
+ json_do_printf("local_addr", "%s", log_addr(&rtr->local_addr));
+
+ if (rtr->session_id != -1) {
+ json_do_uint("session_id", rtr->session_id);
+ json_do_uint("serial", rtr->serial);
+ }
+ json_do_uint("refresh", rtr->refresh);
+ json_do_uint("retry", rtr->retry);
+ json_do_uint("expire", rtr->expire);
+
+ if (rtr->last_sent_error != NO_ERROR) {
+ json_do_printf("last_sent_error", "%s",
+ log_rtr_error(rtr->last_sent_error));
+ if (rtr->last_sent_msg[0])
+ json_do_printf("last_sent_msg", "%s",
+ log_reason(rtr->last_sent_msg));
+ }
+ if (rtr->last_recv_error != NO_ERROR) {
+ json_do_printf("last_recv_error", "%s",
+ log_rtr_error(rtr->last_recv_error));
+ if (rtr->last_recv_msg[0])
+ json_do_printf("last_recv_msg", "%s",
+ log_reason(rtr->last_recv_msg));
+ }
+}
+
+static void
json_result(u_int rescode)
{
if (rescode == 0)
@@ -971,6 +1008,7 @@ const struct output json_output = {
.rib_mem = json_rib_mem,
.rib_hash = json_rib_hash,
.set = json_rib_set,
+ .rtr = json_rtr,
.result = json_result,
.tail = json_tail
};
Index: bgpctl/parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v
retrieving revision 1.105
diff -u -p -r1.105 parser.c
--- bgpctl/parser.c 30 Dec 2020 07:31:19 -0000 1.105
+++ bgpctl/parser.c 25 Jan 2021 18:00:19 -0000
@@ -137,6 +137,7 @@ static const struct token t_show[] = {
{ KEYWORD, "ip", NONE, t_show_ip},
{ KEYWORD, "summary", SHOW_SUMMARY, t_show_summary},
{ KEYWORD, "sets", SHOW_SET, NULL},
+ { KEYWORD, "rtr", SHOW_RTR, NULL},
{ KEYWORD, "mrt", SHOW_MRT, t_show_mrt},
{ ENDTOKEN, "", NONE, NULL}
};
Index: bgpctl/parser.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v
retrieving revision 1.39
diff -u -p -r1.39 parser.h
--- bgpctl/parser.h 30 Dec 2020 07:31:19 -0000 1.39
+++ bgpctl/parser.h 25 Jan 2021 18:00:28 -0000
@@ -33,6 +33,7 @@ enum actions {
SHOW_RIB,
SHOW_MRT,
SHOW_SET,
+ SHOW_RTR,
SHOW_RIB_MEM,
SHOW_NEXTHOP,
SHOW_INTERFACE,
Index: bgpd/Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v
retrieving revision 1.36
diff -u -p -r1.36 Makefile
--- bgpd/Makefile 1 Jan 2020 07:25:04 -0000 1.36
+++ bgpd/Makefile 26 Dec 2020 11:43:42 -0000
@@ -5,7 +5,7 @@ SRCS= bgpd.c session.c log.c logmsg.c pa
rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c control.c \
pfkey.c rde_update.c rde_attr.c rde_community.c printconf.c \
rde_filter.c rde_sets.c rde_trie.c pftable.c name2id.c \
- util.c carp.c timer.c rde_peer.c
+ util.c carp.c timer.c rde_peer.c rtr.c rtr_proto.c
CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
Index: bgpd/bgpd.8
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.8,v
retrieving revision 1.63
diff -u -p -r1.63 bgpd.8
--- bgpd/bgpd.8 1 Feb 2021 07:39:59 -0000 1.63
+++ bgpd/bgpd.8 10 Feb 2021 16:02:25 -0000
@@ -413,6 +413,14 @@ has been started.
.Re
.Pp
.Rs
+.%A R. Bush
+.%A R. Austein
+.%D September 2017
+.%R RFC 8210
+.%T The Resource Public Key Infrastructure (RPKI) to Router Protocol, Version 1
+.Re
+.Pp
+.Rs
.%A J. Mauch
.%A J. Snijders
.%A G. Hankins
Index: bgpd/bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.233
diff -u -p -r1.233 bgpd.c
--- bgpd/bgpd.c 4 Jan 2021 17:44:14 -0000 1.233
+++ bgpd/bgpd.c 25 Jan 2021 18:31:46 -0000
@@ -47,7 +47,9 @@ int send_config(struct bgpd_config *);
int dispatch_imsg(struct imsgbuf *, int, struct bgpd_config *);
int control_setup(struct bgpd_config *);
static void getsockpair(int [2]);
-int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *);
+int imsg_send_sockets(struct imsgbuf *, struct imsgbuf *,
+ struct imsgbuf *);
+void bgpd_rtr_connect(struct rtr_config *);
int cflags;
volatile sig_atomic_t mrtdump;
@@ -57,6 +59,7 @@ pid_t reconfpid;
int reconfpending;
struct imsgbuf *ibuf_se;
struct imsgbuf *ibuf_rde;
+struct imsgbuf *ibuf_rtr;
struct rib_names ribnames = SIMPLEQ_HEAD_INITIALIZER(ribnames);
char *cname;
char *rcname;
@@ -91,9 +94,10 @@ usage(void)
#define PFD_PIPE_SESSION 0
#define PFD_PIPE_RDE 1
-#define PFD_SOCK_ROUTE 2
-#define PFD_SOCK_PFKEY 3
-#define POLL_MAX 4
+#define PFD_PIPE_RTR 2
+#define PFD_SOCK_ROUTE 3
+#define PFD_SOCK_PFKEY 4
+#define POLL_MAX 5
#define MAX_TIMEOUT 3600
int cmd_opts;
@@ -107,7 +111,7 @@ main(int argc, char *argv[])
struct peer *p;
struct pollfd pfd[POLL_MAX];
time_t timeout;
- pid_t se_pid = 0, rde_pid = 0, pid;
+ pid_t se_pid = 0, rde_pid = 0, rtr_pid = 0, pid;
char *conffile;
char *saved_argv0;
int debug = 0;
@@ -115,6 +119,7 @@ main(int argc, char *argv[])
int ch, status;
int pipe_m2s[2];
int pipe_m2r[2];
+ int pipe_m2roa[2];
conffile = CONFFILE;
@@ -126,7 +131,7 @@ main(int argc, char *argv[])
if (saved_argv0 == NULL)
saved_argv0 = "bgpd";
- while ((ch = getopt(argc, argv, "cdD:f:nRSv")) != -1) {
+ while ((ch = getopt(argc, argv, "cdD:f:nRSTv")) != -1) {
switch (ch) {
case 'c':
cmd_opts |= BGPD_OPT_FORCE_DEMOTE;
@@ -156,6 +161,9 @@ main(int argc, char *argv[])
case 'S':
proc = PROC_SE;
break;
+ case 'T':
+ proc = PROC_RTR;
+ break;
default:
usage();
/* NOTREACHED */
@@ -168,7 +176,7 @@ main(int argc, char *argv[])
usage();
if (cmd_opts & BGPD_OPT_NOACTION) {
- if ((conf = parse_config(conffile, NULL)) == NULL)
+ if ((conf = parse_config(conffile, NULL, NULL)) == NULL)
exit(1);
if (cmd_opts & BGPD_OPT_VERBOSE)
@@ -193,6 +201,9 @@ main(int argc, char *argv[])
case PROC_SE:
session_main(debug, cmd_opts & BGPD_OPT_VERBOSE);
/* NOTREACHED */
+ case PROC_RTR:
+ rtr_main(debug, cmd_opts & BGPD_OPT_VERBOSE);
+ /* NOTREACHED */
}
if (geteuid())
@@ -201,7 +212,7 @@ main(int argc, char *argv[])
if (getpwnam(BGPD_USER) == NULL)
errx(1, "unknown user %s", BGPD_USER);
- if ((conf = parse_config(conffile, NULL)) == NULL) {
+ if ((conf = parse_config(conffile, NULL, NULL)) == NULL) {
log_warnx("config file %s has errors", conffile);
exit(1);
}
@@ -219,12 +230,15 @@ main(int argc, char *argv[])
getsockpair(pipe_m2s);
getsockpair(pipe_m2r);
+ getsockpair(pipe_m2roa);
/* fork children */
rde_pid = start_child(PROC_RDE, saved_argv0, pipe_m2r[1], debug,
cmd_opts & BGPD_OPT_VERBOSE);
se_pid = start_child(PROC_SE, saved_argv0, pipe_m2s[1], debug,
cmd_opts & BGPD_OPT_VERBOSE);
+ rtr_pid = start_child(PROC_RTR, saved_argv0, pipe_m2roa[1], debug,
+ cmd_opts & BGPD_OPT_VERBOSE);
signal(SIGTERM, sighdlr);
signal(SIGINT, sighdlr);
@@ -234,10 +248,12 @@ main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
if ((ibuf_se = malloc(sizeof(struct imsgbuf))) == NULL ||
- (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ (ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL ||
+ (ibuf_rtr = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(ibuf_se, pipe_m2s[0]);
imsg_init(ibuf_rde, pipe_m2r[0]);
+ imsg_init(ibuf_rtr, pipe_m2roa[0]);
mrt_init(ibuf_rde, ibuf_se);
if (kr_init(&rfd) == -1)
quit = 1;
@@ -262,7 +278,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
fatal("pledge");
#endif
- if (imsg_send_sockets(ibuf_se, ibuf_rde))
+ if (imsg_send_sockets(ibuf_se, ibuf_rde, ibuf_rtr))
fatal("could not establish imsg links");
/* control setup needs to happen late since it sends imsgs */
if (control_setup(conf) == -1)
@@ -285,6 +301,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se);
set_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde);
+ set_pollfd(&pfd[PFD_PIPE_RTR], ibuf_rtr);
if (timeout < 0 || timeout > MAX_TIMEOUT)
timeout = MAX_TIMEOUT;
@@ -313,8 +330,18 @@ BROKEN if (pledge("stdio rpath wpath cpa
ibuf_rde = NULL;
quit = 1;
} else {
- if (dispatch_imsg(ibuf_rde, PFD_PIPE_RDE, conf) ==
- -1)
+ if (dispatch_imsg(ibuf_rde, PFD_PIPE_RDE, conf) == -1)
+ quit = 1;
+ }
+
+ if (handle_pollfd(&pfd[PFD_PIPE_RTR], ibuf_rtr) == -1) {
+ log_warnx("main: Lost connection to RTR");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ quit = 1;
+ } else {
+ if (dispatch_imsg(ibuf_rtr, PFD_PIPE_RTR, conf) == -1)
quit = 1;
}
@@ -377,6 +404,12 @@ BROKEN if (pledge("stdio rpath wpath cpa
free(ibuf_rde);
ibuf_rde = NULL;
}
+ if (ibuf_rtr) {
+ msgbuf_clear(&ibuf_rtr->w);
+ close(ibuf_rtr->fd);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ }
/* cleanup kernel data structures */
carp_demote_shutdown();
@@ -404,6 +437,8 @@ BROKEN if (pledge("stdio rpath wpath cpa
name = "route decision engine";
else if (pid == se_pid)
name = "session engine";
+ else if (pid == rtr_pid)
+ name = "rtr engine";
log_warnx("%s terminated; signal %d", name,
WTERMSIG(status));
}
@@ -449,6 +484,9 @@ start_child(enum bgpd_process p, char *a
case PROC_SE:
argv[argc++] = "-S";
break;
+ case PROC_RTR:
+ argv[argc++] = "-T";
+ break;
}
if (debug)
argv[argc++] = "-d";
@@ -481,7 +519,8 @@ reconfigure(char *conffile, struct bgpd_
return (2);
log_info("rereading config");
- if ((new_conf = parse_config(conffile, &conf->peers)) == NULL)
+ if ((new_conf = parse_config(conffile, &conf->peers,
+ &conf->rtrs)) == NULL)
return (1);
merge_config(conf, new_conf);
@@ -509,8 +548,9 @@ send_config(struct bgpd_config *conf)
struct prefixset *ps;
struct prefixset_item *psi, *npsi;
struct roa *roa, *nroa;
+ struct rtr_config *rtr;
- reconfpending = 2; /* one per child */
+ reconfpending = 3; /* one per child */
expand_networks(conf);
@@ -523,6 +563,9 @@ send_config(struct bgpd_config *conf)
if (imsg_compose(ibuf_rde, IMSG_RECONF_CONF, 0, 0, -1,
conf, sizeof(*conf)) == -1)
return (-1);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_CONF, 0, 0, -1,
+ conf, sizeof(*conf)) == -1)
+ return (-1);
TAILQ_FOREACH(la, conf->listen_addrs, entry) {
if (imsg_compose(ibuf_se, IMSG_RECONF_LISTENER, 0, 0, la->fd,
@@ -595,17 +638,18 @@ send_config(struct bgpd_config *conf)
free(ps);
}
- if (!RB_EMPTY(&conf->roa)) {
- if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1,
- NULL, 0) == -1)
+ /* roa table and rtr config are sent to the RTR engine */
+ RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nroa) {
+ RB_REMOVE(roa_tree, &conf->roa, roa);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_ROA_ITEM, 0, 0,
+ -1, roa, sizeof(*roa)) == -1)
+ return (-1);
+ free(roa);
+ }
+ SIMPLEQ_FOREACH(rtr, &conf->rtrs, entry) {
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_RTR_CONFIG, rtr->id,
+ 0, -1, rtr->descr, sizeof(rtr->descr)) == -1)
return (-1);
- RB_FOREACH_SAFE(roa, roa_tree, &conf->roa, nroa) {
- RB_REMOVE(roa_tree, &conf->roa, roa);
- if (imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0,
- -1, roa, sizeof(*roa)) == -1)
- return (-1);
- free(roa);
- }
}
/* as-sets for filters in the RDE */
@@ -695,6 +739,8 @@ send_config(struct bgpd_config *conf)
return (-1);
if (imsg_compose(ibuf_rde, IMSG_RECONF_DRAIN, 0, 0, -1, NULL, 0) == -1)
return (-1);
+ if (imsg_compose(ibuf_rtr, IMSG_RECONF_DRAIN, 0, 0, -1, NULL, 0) == -1)
+ return (-1);
/* mrt changes can be sent out of bound */
mrt_reconfigure(conf->mrt);
@@ -706,6 +752,7 @@ dispatch_imsg(struct imsgbuf *ibuf, int
{
struct imsg imsg;
struct peer *p;
+ struct rtr_config *r;
ssize_t n;
int rv, verbose;
@@ -874,6 +921,9 @@ dispatch_imsg(struct imsgbuf *ibuf, int
break;
}
if (idx == PFD_PIPE_SESSION) {
+ imsg_compose(ibuf_rtr, IMSG_RECONF_DONE, 0,
+ 0, -1, NULL, 0);
+ } else if (idx == PFD_PIPE_RTR) {
imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0,
0, -1, NULL, 0);
@@ -898,8 +948,65 @@ dispatch_imsg(struct imsgbuf *ibuf, int
*/
imsg_compose(ibuf_se, IMSG_RECONF_DONE, 0,
0, -1, NULL, 0);
- reconfpending = 2; /* expecting 2 DONE msg */
+ reconfpending = 3; /* expecting 2 DONE msg */
+ }
+ break;
+ case IMSG_SOCKET_CONN:
+ if (idx != PFD_PIPE_RTR) {
+ log_warnx("connect request not from RTR");
+ } else {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ if (imsg.hdr.peerid == r->id)
+ break;
+ }
+ if (r == NULL)
+ log_warnx("unknown rtr id %d",
+ imsg.hdr.peerid);
+ else
+ bgpd_rtr_connect(r);
+ }
+ break;
+ case IMSG_CTL_SHOW_RTR:
+ if (idx == PFD_PIPE_SESSION) {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ imsg_compose(ibuf_rtr, imsg.hdr.type,
+ r->id, imsg.hdr.pid, -1, NULL, 0);
+ }
+ imsg_compose(ibuf_rtr, IMSG_CTL_END,
+ 0, imsg.hdr.pid, -1, NULL, 0);
+ } else if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct ctl_show_rtr)) {
+ log_warnx("IMSG_CTL_SHOW_RTR with wrong len");
+ } else if (idx == PFD_PIPE_RTR) {
+ SIMPLEQ_FOREACH(r, &conf->rtrs, entry) {
+ if (imsg.hdr.peerid == r->id)
+ break;
+ }
+ if (r != NULL) {
+ struct ctl_show_rtr *msg;
+ msg = imsg.data;
+ strlcpy(msg->descr, r->descr,
+ sizeof(msg->descr));
+ msg->local_addr = r->local_addr;
+ msg->remote_addr = r->remote_addr;
+ msg->remote_port = r->remote_port;
+
+ imsg_compose(ibuf_se, imsg.hdr.type,
+ imsg.hdr.peerid, imsg.hdr.pid,
+ -1, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
+ }
+ }
+ break;
+ case IMSG_CTL_END:
+ case IMSG_CTL_SHOW_TIMER:
+ if (idx != PFD_PIPE_RTR) {
+ log_warnx("connect request not from RTR");
+ break;
}
+ imsg_compose(ibuf_se, imsg.hdr.type, imsg.hdr.peerid,
+ imsg.hdr.pid, -1, imsg.data,
+ imsg.hdr.len - IMSG_HEADER_SIZE);
break;
default:
break;
@@ -1117,13 +1224,15 @@ getsockpair(int pipe[2])
}
int
-imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde)
+imsg_send_sockets(struct imsgbuf *se, struct imsgbuf *rde, struct imsgbuf *roa)
{
int pipe_s2r[2];
int pipe_s2r_ctl[2];
+ int pipe_r2r[2];
getsockpair(pipe_s2r);
getsockpair(pipe_s2r_ctl);
+ getsockpair(pipe_r2r);
if (imsg_compose(se, IMSG_SOCKET_CONN, 0, 0, pipe_s2r[0],
NULL, 0) == -1)
@@ -1139,5 +1248,44 @@ imsg_send_sockets(struct imsgbuf *se, st
NULL, 0) == -1)
return (-1);
+ if (imsg_compose(roa, IMSG_SOCKET_CONN_RTR, 0, 0, pipe_r2r[0],
+ NULL, 0) == -1)
+ return (-1);
+ if (imsg_compose(rde, IMSG_SOCKET_CONN_RTR, 0, 0, pipe_r2r[1],
+ NULL, 0) == -1)
+ return (-1);
+
return (0);
+}
+
+void
+bgpd_rtr_connect(struct rtr_config *r)
+{
+ socklen_t len;
+ int fd;
+
+ /* XXX should be non-blocking */
+ fd = socket(aid2af(r->remote_addr.aid), SOCK_STREAM, 0);
+ if (fd == -1) {
+ log_warn("rtr %s", r->descr);
+ return;
+ }
+ if (r->local_addr.aid != AID_UNSPEC) {
+ if (bind(fd, addr2sa(&r->local_addr, 0, &len), len) == -1) {
+ log_warn("rtr %s: bind to %s", r->descr,
+ log_addr(&r->local_addr));
+ close(fd);
+ return;
+ }
+ }
+
+ if (connect(fd, addr2sa(&r->remote_addr, r->remote_port, &len), len) ==
+ -1) {
+ log_warn("rtr %s: connect to %s:%u", r->descr,
+ log_addr(&r->remote_addr), r->remote_port);
+ close(fd);
+ return;
+ }
+
+ imsg_compose(ibuf_rtr, IMSG_SOCKET_CONN, r->id, 0, fd, NULL, 0);
}
Index: bgpd/bgpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.207
diff -u -p -r1.207 bgpd.conf.5
--- bgpd/bgpd.conf.5 27 Jan 2021 14:57:29 -0000 1.207
+++ bgpd/bgpd.conf.5 10 Feb 2021 16:02:25 -0000
@@ -494,6 +494,39 @@ and the Origin Validation State (OVS) is
roa-set { 192.0.2.0/24 maxlen 24 source-as 64511
203.0.113.0/24 source-as 64496 }
.Ed
+.Pp
+.It Xo
+.Ic rtr Ar address
+.Ic { Ar ... Ic }
+.Xc
+The
+.Ic rtr
+block specifies a
+.Em RPKI to Router Protocol
+session.
+The rtr session properties are as follows:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic descr Ar description
+Add a description.
+The description is used in logging and status reports, but has no further
+meaning for
+.Xr bgpd 8 .
+.Pp
+.It Ic local-address Ar address
+Bind to the specific IP address before opening the TCP connection to the
+.Em rtr
+server.
+.Pp
+.It Ic port Ar number
+Specify the TCP destination port for the
+.Em rtr
+session.
+If not specified the default
+.Ic port
+is
+.Em 323 .
+.El
.El
.Sh NETWORK ANNOUNCEMENTS
.Ic network
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.411
diff -u -p -r1.411 bgpd.h
--- bgpd/bgpd.h 25 Jan 2021 09:15:23 -0000 1.411
+++ bgpd/bgpd.h 25 Jan 2021 16:52:41 -0000
@@ -120,7 +120,8 @@
enum bgpd_process {
PROC_MAIN,
PROC_SE,
- PROC_RDE
+ PROC_RDE,
+ PROC_RTR,
};
enum reconf_action {
@@ -266,6 +267,9 @@ SIMPLEQ_HEAD(as_set_head, as_set);
struct filter_rule;
TAILQ_HEAD(filter_head, filter_rule);
+struct rtr_config;
+SIMPLEQ_HEAD(rtr_config_head, rtr_config);
+
struct bgpd_config {
struct peer_head peers;
struct l3vpn_head l3vpns;
@@ -278,8 +282,8 @@ struct bgpd_config {
struct roa_tree roa;
struct rde_prefixset_head rde_prefixsets;
struct rde_prefixset_head rde_originsets;
- struct rde_prefixset rde_roa;
struct as_set_head as_sets;
+ struct rtr_config_head rtrs;
char *csock;
char *rcsock;
int flags;
@@ -433,6 +437,44 @@ struct network {
TAILQ_ENTRY(network) entry;
};
+enum rtr_error {
+ NO_ERROR = -1,
+ CORRUPT_DATA = 0,
+ INTERNAL_ERROR,
+ NO_DATA_AVAILABLE,
+ INVALID_REQUEST,
+ UNSUPP_PROTOCOL_VERS,
+ UNSUPP_PDU_TYPE,
+ UNK_REC_WDRAWL,
+ DUP_REC_RECV,
+ UNEXP_PROTOCOL_VERS,
+};
+
+struct rtr_config {
+ SIMPLEQ_ENTRY(rtr_config) entry;
+ char descr[PEER_DESCR_LEN];
+ struct bgpd_addr remote_addr;
+ struct bgpd_addr local_addr;
+ u_int32_t id;
+ in_addr_t remote_port;
+};
+
+struct ctl_show_rtr {
+ char descr[PEER_DESCR_LEN];
+ struct bgpd_addr remote_addr;
+ struct bgpd_addr local_addr;
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ int session_id;
+ in_addr_t remote_port;
+ enum rtr_error last_sent_error;
+ enum rtr_error last_recv_error;
+ char last_sent_msg[REASON_LEN];
+ char last_recv_msg[REASON_LEN];
+};
+
enum imsg_type {
IMSG_NONE,
IMSG_CTL_END,
@@ -462,6 +504,7 @@ enum imsg_type {
IMSG_CTL_LOG_VERBOSE,
IMSG_CTL_SHOW_FIB_TABLES,
IMSG_CTL_SHOW_SET,
+ IMSG_CTL_SHOW_RTR,
IMSG_CTL_TERMINATE,
IMSG_NETWORK_ADD,
IMSG_NETWORK_ASPATH,
@@ -472,6 +515,7 @@ enum imsg_type {
IMSG_FILTER_SET,
IMSG_SOCKET_CONN,
IMSG_SOCKET_CONN_CTL,
+ IMSG_SOCKET_CONN_RTR,
IMSG_RECONF_CONF,
IMSG_RECONF_RIB,
IMSG_RECONF_PEER,
@@ -490,6 +534,7 @@ enum imsg_type {
IMSG_RECONF_ORIGIN_SET,
IMSG_RECONF_ROA_SET,
IMSG_RECONF_ROA_ITEM,
+ IMSG_RECONF_RTR_CONFIG,
IMSG_RECONF_DRAIN,
IMSG_RECONF_DONE,
IMSG_UPDATE,
@@ -1194,11 +1239,13 @@ void free_prefixsets(struct prefixset_h
void free_rde_prefixsets(struct rde_prefixset_head *);
void free_prefixtree(struct prefixset_tree *);
void free_roatree(struct roa_tree *);
+void free_rtrs(struct rtr_config_head *);
void filterlist_free(struct filter_head *);
int host(const char *, struct bgpd_addr *, u_int8_t *);
u_int32_t get_bgpid(void);
void expand_networks(struct bgpd_config *);
RB_PROTOTYPE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
+int roa_cmp(struct roa *, struct roa *);
RB_PROTOTYPE(roa_tree, roa, entry, roa_cmp);
/* kroute.c */
@@ -1262,8 +1309,10 @@ void pftable_unref(u_int16_t);
u_int16_t pftable_ref(u_int16_t);
/* parse.y */
-int cmdline_symset(char *);
-struct prefixset *find_prefixset(char *, struct prefixset_head *);
+int cmdline_symset(char *);
+struct prefixset *find_prefixset(char *, struct prefixset_head *);
+struct bgpd_config *parse_config(char *, struct peer_head *,
+ struct rtr_config_head *);
/* pftable.c */
int pftable_exists(const char *);
@@ -1320,6 +1369,7 @@ const char *log_as(u_int32_t);
const char *log_rd(u_int64_t);
const char *log_ext_subtype(short, u_int8_t);
const char *log_reason(const char *);
+const char *log_rtr_error(enum rtr_error);
int aspath_snprint(char *, size_t, void *, u_int16_t);
int aspath_asprint(char **, void *, u_int16_t);
size_t aspath_strlen(void *, u_int16_t);
@@ -1356,7 +1406,8 @@ const char * get_baudrate(unsigned long
static const char * const log_procnames[] = {
"parent",
"SE",
- "RDE"
+ "RDE",
+ "RTR"
};
/* logmsg.c and needed by bgpctl */
@@ -1485,6 +1536,10 @@ static const char * const timernames[] =
"IdleHoldTimer",
"IdleHoldResetTimer",
"CarpUndemoteTimer",
+ "RestartTimer",
+ "RTR RefreshTimer",
+ "RTR RetryTimer",
+ "RTR ExpireTimer",
""
};
Index: bgpd/config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.98
diff -u -p -r1.98 config.c
--- bgpd/config.c 4 Jan 2021 13:42:11 -0000 1.98
+++ bgpd/config.c 4 Jan 2021 15:32:07 -0000
@@ -60,6 +60,7 @@ new_config(void)
SIMPLEQ_INIT(&conf->rde_originsets);
RB_INIT(&conf->roa);
SIMPLEQ_INIT(&conf->as_sets);
+ SIMPLEQ_INIT(&conf->rtrs);
TAILQ_INIT(conf->filters);
TAILQ_INIT(conf->listen_addrs);
@@ -163,6 +164,18 @@ free_roatree(struct roa_tree *r)
}
void
+free_rtrs(struct rtr_config_head *rh)
+{
+ struct rtr_config *r;
+
+ while (!SIMPLEQ_EMPTY(rh)) {
+ r = SIMPLEQ_FIRST(rh);
+ SIMPLEQ_REMOVE_HEAD(rh, entry);
+ free(r);
+ }
+}
+
+void
free_config(struct bgpd_config *conf)
{
struct peer *p, *next;
@@ -178,6 +191,7 @@ free_config(struct bgpd_config *conf)
free_rde_prefixsets(&conf->rde_originsets);
as_sets_free(&conf->as_sets);
free_roatree(&conf->roa);
+ free_rtrs(&conf->rtrs);
while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) {
TAILQ_REMOVE(conf->listen_addrs, la, entry);
@@ -246,6 +260,10 @@ merge_config(struct bgpd_config *xconf,
RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa);
RB_ROOT(&conf->roa) = NULL;
+ /* switch the rtr_configs, first remove the old ones */
+ free_rtrs(&xconf->rtrs);
+ SIMPLEQ_CONCAT(&xconf->rtrs, &conf->rtrs);
+
/* switch the prefixsets, first remove the old ones */
free_prefixsets(&xconf->prefixsets);
SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets);
@@ -582,7 +600,7 @@ prefixset_cmp(struct prefixset_item *a,
RB_GENERATE(prefixset_tree, prefixset_item, entry, prefixset_cmp);
-static inline int
+int
roa_cmp(struct roa *a, struct roa *b)
{
int i;
Index: bgpd/control.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/control.c,v
retrieving revision 1.103
diff -u -p -r1.103 control.c
--- bgpd/control.c 30 Dec 2020 07:29:56 -0000 1.103
+++ bgpd/control.c 25 Jan 2021 17:56:27 -0000
@@ -448,6 +448,7 @@ control_dispatch_msg(struct pollfd *pfd,
case IMSG_CTL_RELOAD:
case IMSG_CTL_SHOW_INTERFACE:
case IMSG_CTL_SHOW_FIB_TABLES:
+ case IMSG_CTL_SHOW_RTR:
c->ibuf.pid = imsg.hdr.pid;
imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
Index: bgpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.412
diff -u -p -r1.412 parse.y
--- bgpd/parse.y 25 Jan 2021 09:15:23 -0000 1.412
+++ bgpd/parse.y 26 Jan 2021 09:05:15 -0000
@@ -89,12 +89,14 @@ char *symget(const char *);
static struct bgpd_config *conf;
static struct network_head *netconf;
static struct peer_head *new_peers, *cur_peers;
+static struct rtr_config_head *cur_rtrs;
static struct peer *curpeer;
static struct peer *curgroup;
static struct rde_rib *currib;
static struct l3vpn *curvpn;
static struct prefixset *curpset, *curoset;
static struct roa_tree *curroatree;
+static struct rtr_config *currtr;
static struct filter_head *filter_l;
static struct filter_head *peerfilter_l;
static struct filter_head *groupfilter_l;
@@ -162,6 +164,7 @@ static void add_as_set(u_int32_t);
static void done_as_set(void);
static struct prefixset *new_prefix_set(char *, int);
static void add_roa_set(struct prefixset_item *, u_int32_t, u_int8_t);
+static struct rtr_config *get_rtr(struct bgpd_addr *);
typedef struct {
union {
@@ -196,7 +199,7 @@ typedef struct {
%token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE FIBPRIORITY RTABLE
%token NONE UNICAST VPN RD EXPORT EXPORTTRGT IMPORTTRGT DEFAULTROUTE
-%token RDE RIB EVALUATE IGNORE COMPARE
+%token RDE RIB EVALUATE IGNORE COMPARE RTR PORT
%token GROUP NEIGHBOR NETWORK
%token EBGP IBGP
%token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
@@ -252,6 +255,7 @@ grammar : /* empty */
| grammar prefixset '\n'
| grammar roa_set '\n'
| grammar origin_set '\n'
+ | grammar rtr '\n'
| grammar rib '\n'
| grammar conf_main '\n'
| grammar l3vpn '\n'
@@ -551,6 +555,53 @@ roa_set_l : prefixset_item SOURCEAS as4n
}
;
+rtr : RTR address {
+ currtr = get_rtr(&$2);
+ currtr->remote_port = 323;
+ SIMPLEQ_INSERT_TAIL(&conf->rtrs, currtr, entry);
+ currtr = NULL;
+ }
+ | RTR address {
+ currtr = get_rtr(&$2);
+ currtr->remote_port = 323;
+ } '{' optnl rtropt_l optnl '}' {
+ SIMPLEQ_INSERT_TAIL(&conf->rtrs, currtr, entry);
+ currtr = NULL;
+ }
+ ;
+
+rtropt_l : rtropt
+ | rtropt_l optnl rtropt
+
+rtropt : DESCR STRING {
+ if (strlcpy(currtr->descr, $2,
+ sizeof(currtr->descr)) >=
+ sizeof(currtr->descr)) {
+ yyerror("descr \"%s\" too long: max %zu",
+ $2, sizeof(currtr->descr) - 1);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | LOCALADDR address {
+ if ($2.aid != currtr->remote_addr.aid) {
+ yyerror("Bad address family %s for "
+ "local-addr", aid2str($2.aid));
+ YYERROR;
+ }
+ currtr->local_addr = $2;
+ }
+ | PORT NUMBER {
+ if ($2 < 1 || $2 > USHRT_MAX) {
+ yyerror("local-port must be between %u and %u",
+ 1, USHRT_MAX);
+ YYERROR;
+ }
+ currtr->remote_port = $2;
+ }
+ ;
+
conf_main : AS as4number {
conf->as = $2;
if ($2 > USHRT_MAX)
@@ -2866,6 +2917,7 @@ lookup(char *s)
{ "password", PASSWORD},
{ "peer-as", PEERAS},
{ "pftable", PFTABLE},
+ { "port", PORT},
{ "prefix", PREFIX},
{ "prefix-set", PREFIXSET},
{ "prefixlen", PREFIXLEN},
@@ -2887,6 +2939,7 @@ lookup(char *s)
{ "router-id", ROUTERID},
{ "rtable", RTABLE},
{ "rtlabel", RTLABEL},
+ { "rtr", RTR},
{ "self", SELF},
{ "set", SET},
{ "socket", SOCKET },
@@ -3285,7 +3338,7 @@ init_config(struct bgpd_config *c)
}
struct bgpd_config *
-parse_config(char *filename, struct peer_head *ph)
+parse_config(char *filename, struct peer_head *ph, struct rtr_config_head *rh)
{
struct sym *sym, *next;
struct rde_rib *rr;
@@ -3309,6 +3362,7 @@ parse_config(char *filename, struct peer
curgroup = NULL;
cur_peers = ph;
+ cur_rtrs = rh;
new_peers = &conf->peers;
netconf = &conf->networks;
@@ -4567,4 +4621,35 @@ add_roa_set(struct prefixset_item *npsi,
if (r != NULL)
/* just ignore duplicates */
free(roa);
+}
+
+static struct rtr_config *
+get_rtr(struct bgpd_addr *addr)
+{
+ static uint32_t id;
+ struct rtr_config *n, *p;
+
+ if (id == UINT32_MAX) {
+ yyerror("out of rtr session IDs");
+ return NULL;
+ }
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL) {
+ yyerror("out of memory");
+ return NULL;
+ }
+
+ n->remote_addr = *addr;
+ strlcpy(n->descr, log_addr(addr), sizeof(currtr->descr));
+
+ if (cur_rtrs)
+ SIMPLEQ_FOREACH(p, cur_rtrs, entry)
+ if (memcmp(&p->remote_addr, addr, sizeof(*addr)) == 0)
+ n->id = p->id;
+
+ if (n->id == 0)
+ n->id = ++id;
+
+ return n;
}
Index: bgpd/printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.145
diff -u -p -r1.145 printconf.c
--- bgpd/printconf.c 25 Jan 2021 09:15:23 -0000 1.145
+++ bgpd/printconf.c 26 Jan 2021 09:26:25 -0000
@@ -42,6 +42,7 @@ void print_as_sets(struct as_set_head
void print_prefixsets(struct prefixset_head *);
void print_originsets(struct prefixset_head *);
void print_roa(struct roa_tree *);
+void print_rtrs(struct rtr_config_head *);
void print_peer(struct peer_config *, struct bgpd_config *,
const char *);
const char *print_auth_alg(u_int8_t);
@@ -579,6 +580,21 @@ print_roa(struct roa_tree *r)
}
void
+print_rtrs(struct rtr_config_head *rh)
+{
+ struct rtr_config *r;
+
+ SIMPLEQ_FOREACH(r, rh, entry) {
+ printf("rtr %s {\n", log_addr(&r->remote_addr));
+ printf("\tdescr \"%s\"\n", r->descr);
+ printf("\tport %u\n", r->remote_port);
+ if (r->local_addr.aid != AID_UNSPEC)
+ printf("local-addr %s\n", log_addr(&r->local_addr));
+ printf("}\n\n");
+ }
+}
+
+void
print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
{
char *method;
@@ -1010,6 +1026,7 @@ print_config(struct bgpd_config *conf, s
struct l3vpn *vpn;
print_mainconf(conf);
+ print_rtrs(&conf->rtrs);
print_roa(&conf->roa);
print_as_sets(&conf->as_sets);
print_prefixsets(&conf->prefixsets);
Index: bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.514
diff -u -p -r1.514 rde.c
--- bgpd/rde.c 25 Jan 2021 09:15:24 -0000 1.514
+++ bgpd/rde.c 26 Jan 2021 07:33:18 -0000
@@ -32,7 +32,6 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
-#include <err.h>
#include "bgpd.h"
#include "rde.h"
@@ -42,11 +41,13 @@
#define PFD_PIPE_MAIN 0
#define PFD_PIPE_SESSION 1
#define PFD_PIPE_SESSION_CTL 2
-#define PFD_PIPE_COUNT 3
+#define PFD_PIPE_ROA 3
+#define PFD_PIPE_COUNT 4
void rde_sighdlr(int);
void rde_dispatch_imsg_session(struct imsgbuf *);
void rde_dispatch_imsg_parent(struct imsgbuf *);
+void rde_dispatch_imsg_rtr(struct imsgbuf *);
void rde_dispatch_imsg_peer(struct rde_peer *, void *);
void rde_update_dispatch(struct rde_peer *, struct imsg *);
int rde_update_update(struct rde_peer *, struct filterstate *,
@@ -79,6 +80,7 @@ static void rde_softreconfig_in(struct
static void rde_softreconfig_sync_reeval(struct rib_entry *, void *);
static void rde_softreconfig_sync_fib(struct rib_entry *, void *);
static void rde_softreconfig_sync_done(void *, u_int8_t);
+static void rde_roa_reload(void);
static int rde_no_as_set(struct rde_peer *);
int rde_update_queue_pending(void);
void rde_update_queue_runner(void);
@@ -102,8 +104,10 @@ int ovs_match(struct prefix *, u_int32
static struct imsgbuf *ibuf_se;
static struct imsgbuf *ibuf_se_ctl;
+static struct imsgbuf *ibuf_rtr;
static struct imsgbuf *ibuf_main;
static struct bgpd_config *conf, *nconf;
+static struct rde_prefixset rde_roa, roa_new;
volatile sig_atomic_t rde_quit = 0;
struct filter_head *out_rules, *out_rules_tmp;
@@ -231,6 +235,7 @@ rde_main(int debug, int verbose)
set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main);
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se);
set_pollfd(&pfd[PFD_PIPE_SESSION_CTL], ibuf_se_ctl);
+ set_pollfd(&pfd[PFD_PIPE_ROA], ibuf_rtr);
i = PFD_PIPE_COUNT;
for (mctx = LIST_FIRST(&rde_mrts); mctx != 0; mctx = xmctx) {
@@ -282,6 +287,14 @@ rde_main(int debug, int verbose)
} else
rde_dispatch_imsg_session(ibuf_se_ctl);
+ if (handle_pollfd(&pfd[PFD_PIPE_ROA], ibuf_rtr) == -1) {
+ log_warnx("RDE: Lost connection to ROA");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ ibuf_rtr = NULL;
+ } else
+ rde_dispatch_imsg_rtr(ibuf_rtr);
+
for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts);
j < i && mctx != 0; j++) {
if (pfd[j].fd == mctx->mrt.wbuf.fd &&
@@ -578,7 +591,7 @@ badnetdel:
break;
case IMSG_CTL_SHOW_SET:
/* first roa set */
- pset = &conf->rde_roa;
+ pset = &rde_roa;
memset(&cset, 0, sizeof(cset));
cset.type = ROA_SET;
strlcpy(cset.name, "RPKI ROA", sizeof(cset.name));
@@ -668,7 +681,6 @@ rde_dispatch_imsg_parent(struct imsgbuf
static struct l3vpn *vpn;
struct imsg imsg;
struct mrt xmrt;
- struct roa roa;
struct rde_rib rr;
struct filterstate state;
struct imsgbuf *i;
@@ -693,15 +705,17 @@ rde_dispatch_imsg_parent(struct imsgbuf
switch (imsg.hdr.type) {
case IMSG_SOCKET_CONN:
case IMSG_SOCKET_CONN_CTL:
+ case IMSG_SOCKET_CONN_RTR:
if ((fd = imsg.fd) == -1) {
- log_warnx("expected to receive imsg fd to "
- "SE but didn't receive any");
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
break;
}
if ((i = malloc(sizeof(struct imsgbuf))) == NULL)
fatal(NULL);
imsg_init(i, fd);
- if (imsg.hdr.type == IMSG_SOCKET_CONN) {
+ switch (imsg.hdr.type) {
+ case IMSG_SOCKET_CONN:
if (ibuf_se) {
log_warnx("Unexpected imsg connection "
"to SE received");
@@ -709,7 +723,8 @@ rde_dispatch_imsg_parent(struct imsgbuf
free(ibuf_se);
}
ibuf_se = i;
- } else {
+ break;
+ case IMSG_SOCKET_CONN_CTL:
if (ibuf_se_ctl) {
log_warnx("Unexpected imsg ctl "
"connection to SE received");
@@ -717,6 +732,16 @@ rde_dispatch_imsg_parent(struct imsgbuf
free(ibuf_se_ctl);
}
ibuf_se_ctl = i;
+ break;
+ case IMSG_SOCKET_CONN_RTR:
+ if (ibuf_rtr) {
+ log_warnx("Unexpected imsg ctl "
+ "connection to ROA received");
+ msgbuf_clear(&ibuf_rtr->w);
+ free(ibuf_rtr);
+ }
+ ibuf_rtr = i;
+ break;
}
break;
case IMSG_NETWORK_ADD:
@@ -865,17 +890,6 @@ rde_dispatch_imsg_parent(struct imsgbuf
}
last_prefixset = ps;
break;
- case IMSG_RECONF_ROA_SET:
- strlcpy(nconf->rde_roa.name, "RPKI ROA",
- sizeof(nconf->rde_roa.name));
- last_prefixset = &nconf->rde_roa;
- break;
- case IMSG_RECONF_ROA_ITEM:
- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(roa))
- fatalx("IMSG_RECONF_ROA_ITEM bad len");
- memcpy(&roa, imsg.data, sizeof(roa));
- rv = trie_roa_add(&last_prefixset->th, &roa);
- break;
case IMSG_RECONF_PREFIX_SET_ITEM:
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(psi))
fatalx("IMSG_RECONF_PREFIX_SET_ITEM bad len");
@@ -886,7 +900,7 @@ rde_dispatch_imsg_parent(struct imsgbuf
&psi.p.addr, psi.p.len,
psi.p.len_min, psi.p.len_max);
if (rv == -1)
- log_warnx("trie_add(%s) %s/%u) failed",
+ log_warnx("trie_add(%s) %s/%u failed",
last_prefixset->name, log_addr(&psi.p.addr),
psi.p.len);
break;
@@ -991,6 +1005,46 @@ rde_dispatch_imsg_parent(struct imsgbuf
/* ignore end message because a dump is atomic */
break;
default:
+ fatalx("unhandled IMSG %u", imsg.hdr.type);
+ }
+ imsg_free(&imsg);
+ }
+}
+
+void
+rde_dispatch_imsg_rtr(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ struct roa roa;
+ int n;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("rde_dispatch_imsg_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_RECONF_ROA_SET:
+ /* start of update */
+ break;
+ case IMSG_RECONF_ROA_ITEM:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(roa))
+ fatalx("IMSG_RECONF_ROA_ITEM bad len");
+ memcpy(&roa, imsg.data, sizeof(roa));
+ if (trie_roa_add(&roa_new.th, &roa) != 0) {
+ struct bgpd_addr p = {
+ .aid = roa.aid,
+ .v6 = roa.prefix.inet6
+ };
+ log_warnx("trie_roa_add %s/%u failed",
+ log_addr(&p), roa.prefixlen);
+ }
+ break;
+ case IMSG_RECONF_DONE:
+ /* end of update */
+ rde_roa_reload();
break;
}
imsg_free(&imsg);
@@ -1439,7 +1493,7 @@ rde_update_update(struct rde_peer *peer,
const char *wmsg = "filtered, withdraw";
peer->prefix_rcvd_update++;
- vstate = rde_roa_validity(&conf->rde_roa, prefix, prefixlen,
+ vstate = rde_roa_validity(&rde_roa, prefix, prefixlen,
aspath_origin(in->aspath.aspath));
/* add original path to the Adj-RIB-In */
@@ -3153,7 +3207,6 @@ rde_reload_done(void)
struct filter_head *fh;
struct rde_prefixset_head prefixsets_old;
struct rde_prefixset_head originsets_old;
- struct rde_prefixset roa_old;
struct as_set_head as_sets_old;
u_int16_t rid;
int reload = 0;
@@ -3166,7 +3219,6 @@ rde_reload_done(void)
SIMPLEQ_CONCAT(&prefixsets_old, &conf->rde_prefixsets);
SIMPLEQ_CONCAT(&originsets_old, &conf->rde_originsets);
SIMPLEQ_CONCAT(&as_sets_old, &conf->as_sets);
- roa_old = conf->rde_roa;
/* merge the main config */
copy_config(conf, nconf);
@@ -3176,10 +3228,6 @@ rde_reload_done(void)
SIMPLEQ_CONCAT(&conf->rde_originsets, &nconf->rde_originsets);
SIMPLEQ_CONCAT(&conf->as_sets, &nconf->as_sets);
- conf->rde_roa = nconf->rde_roa;
- conf->rde_roa.lastchange = roa_old.lastchange;
- memset(&nconf->rde_roa, 0, sizeof(nconf->rde_roa));
-
/* apply new set of l3vpn, sync will be done later */
free_l3vpns(&conf->l3vpns);
SIMPLEQ_CONCAT(&conf->l3vpns, &nconf->l3vpns);
@@ -3197,16 +3245,6 @@ rde_reload_done(void)
peerself->conf.remote_masklen = 32;
peerself->short_as = conf->short_as;
- /* check if roa changed */
- if (trie_equal(&conf->rde_roa.th, &roa_old.th) == 0) {
- log_debug("roa change: reloading Adj-RIB-In");
- conf->rde_roa.dirty = 1;
- conf->rde_roa.lastchange = getmonotime();
- reload++; /* run softreconf in */
- }
-
- trie_free(&roa_old.th); /* old roa no longer needed */
-
rde_mark_prefixsets_dirty(&prefixsets_old, &conf->rde_prefixsets);
rde_mark_prefixsets_dirty(&originsets_old, &conf->rde_originsets);
as_sets_mark_dirty(&as_sets_old, &conf->as_sets);
@@ -3443,8 +3481,6 @@ rde_softreconfig_in(struct rib_entry *re
struct rde_aspath *asp;
enum filter_actions action;
struct bgpd_addr prefix;
- int force_eval;
- u_int8_t vstate;
u_int16_t i;
pt = re->prefix;
@@ -3452,17 +3488,6 @@ rde_softreconfig_in(struct rib_entry *re
LIST_FOREACH(p, &re->prefix_h, entry.list.rib) {
asp = prefix_aspath(p);
peer = prefix_peer(p);
- force_eval = 0;
-
- if (conf->rde_roa.dirty) {
- /* ROA validation state update */
- vstate = rde_roa_validity(&conf->rde_roa,
- &prefix, pt->prefixlen, aspath_origin(asp->aspath));
- if (vstate != p->validation_state) {
- force_eval = 1;
- p->validation_state = vstate;
- }
- }
/* skip announced networks, they are never filtered */
if (asp->flags & F_PREFIX_ANNOUNCED)
@@ -3473,7 +3498,7 @@ rde_softreconfig_in(struct rib_entry *re
if (rib == NULL)
continue;
- if (rib->state != RECONF_RELOAD && !force_eval)
+ if (rib->state != RECONF_RELOAD)
continue;
rde_filterstate_prep(&state, asp, prefix_communities(p),
@@ -3575,6 +3600,99 @@ rde_softreconfig_sync_done(void *arg, u_
}
/*
+ * ROA specific functions. The roa set is updated independent of the config
+ * so this runs outside of the softreconfig handlers.
+ */
+static void
+rde_roa_softreload(struct rib_entry *re, void *bula)
+{
+ struct filterstate state;
+ struct rib *rib;
+ struct prefix *p;
+ struct pt_entry *pt;
+ struct rde_peer *peer;
+ struct rde_aspath *asp;
+ enum filter_actions action;
+ struct bgpd_addr prefix;
+ u_int8_t vstate;
+ u_int16_t i;
+
+ pt = re->prefix;
+ pt_getaddr(pt, &prefix);
+ LIST_FOREACH(p, &re->prefix_h, entry.list.rib) {
+ asp = prefix_aspath(p);
+ peer = prefix_peer(p);
+
+ /* ROA validation state update */
+ vstate = rde_roa_validity(&rde_roa,
+ &prefix, pt->prefixlen, aspath_origin(asp->aspath));
+ if (vstate == p->validation_state)
+ continue;
+ p->validation_state = vstate;
+
+ /* skip announced networks, they are never filtered */
+ if (asp->flags & F_PREFIX_ANNOUNCED)
+ continue;
+
+ for (i = RIB_LOC_START; i < rib_size; i++) {
+ rib = rib_byid(i);
+ if (rib == NULL)
+ continue;
+
+ rde_filterstate_prep(&state, asp, prefix_communities(p),
+ prefix_nexthop(p), prefix_nhflags(p));
+ action = rde_filter(rib->in_rules, peer, peer, &prefix,
+ pt->prefixlen, p->validation_state, &state);
+
+ if (action == ACTION_ALLOW) {
+ /* update Local-RIB */
+ prefix_update(rib, peer, &state, &prefix,
+ pt->prefixlen, p->validation_state);
+ } else if (action == ACTION_DENY) {
+ /* remove from Local-RIB */
+ prefix_withdraw(rib, peer, &prefix,
+ pt->prefixlen);
+ }
+
+ rde_filterstate_clean(&state);
+ }
+ }
+}
+
+static void
+rde_roa_softreload_done(void *arg, u_int8_t aid)
+{
+ /* the roa update is done */
+ log_info("ROA softreload done");
+}
+
+static void
+rde_roa_reload(void)
+{
+ struct rde_prefixset roa_old;
+
+ roa_old = rde_roa;
+ rde_roa = roa_new;
+ memset(&roa_new, 0, sizeof(roa_new));
+
+ /* check if roa changed */
+ if (trie_equal(&rde_roa.th, &roa_old.th)) {
+ rde_roa.lastchange = roa_old.lastchange;
+ trie_free(&roa_old.th); /* old roa no longer needed */
+ return;
+ }
+
+ rde_roa.lastchange = getmonotime();
+ trie_free(&roa_old.th); /* old roa no longer needed */
+
+ log_debug("ROA change: reloading Adj-RIB-In");
+ if (rib_dump_new(RIB_ADJ_IN, AID_UNSPEC, RDE_RUNNER_ROUNDS,
+ rib_byid(RIB_ADJ_IN), rde_roa_softreload,
+ rde_roa_softreload_done, NULL) == -1)
+ fatal("%s: rib_dump_new", __func__);
+}
+
+/*
* generic helper function
*/
u_int32_t
@@ -3737,7 +3855,7 @@ network_add(struct network_config *nc, s
rde_apply_set(vpnset, peerself, peerself, state,
nc->prefix.aid);
- vstate = rde_roa_validity(&conf->rde_roa, &nc->prefix,
+ vstate = rde_roa_validity(&rde_roa, &nc->prefix,
nc->prefixlen, aspath_origin(state->aspath.aspath));
if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, state, &nc->prefix,
nc->prefixlen, vstate) == 1)
Index: bgpd/rtr.c
===================================================================
RCS file: bgpd/rtr.c
diff -N bgpd/rtr.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ bgpd/rtr.c 26 Jan 2021 09:26:00 -0000
@@ -0,0 +1,334 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/tree.h>
+#include <errno.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "log.h"
+
+static void rtr_dispatch_imsg_parent(struct imsgbuf *);
+static void rtr_dispatch_imsg_rde(struct imsgbuf *);
+
+volatile sig_atomic_t rtr_quit;
+static struct imsgbuf *ibuf_main;
+static struct imsgbuf *ibuf_rde;
+static struct bgpd_config *conf, *nconf;
+
+static void
+rtr_sighdlr(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ rtr_quit = 1;
+ break;
+ }
+}
+
+#define PFD_PIPE_MAIN 0
+#define PFD_PIPE_RDE 1
+#define PFD_PIPE_COUNT 2
+
+void
+roa_insert(struct roa_tree *rt, struct roa *in)
+{
+ struct roa *roa;
+
+ if ((roa = malloc(sizeof(*roa))) == NULL)
+ fatal("roa alloc");
+ memcpy(roa, in, sizeof(*roa));
+ if (RB_INSERT(roa_tree, rt, roa) != NULL)
+ /* just ignore duplicates */
+ free(roa);
+}
+
+void
+rtr_main(int debug, int verbose)
+{
+ struct passwd *pw;
+ struct pollfd *pfd = NULL;
+ void *newp;
+ size_t pfd_elms = 0, i;
+ time_t now, timeout;
+
+ log_init(debug, LOG_DAEMON);
+ log_setverbose(verbose);
+
+ log_procinit(log_procnames[PROC_RTR]);
+
+ if ((pw = getpwnam(BGPD_USER)) == NULL)
+ fatal("getpwnam");
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ setproctitle("rtr engine");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ if (pledge("stdio recvfd", NULL) == -1)
+ fatal("pledge");
+
+ signal(SIGTERM, rtr_sighdlr);
+ signal(SIGINT, rtr_sighdlr);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+
+ if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_main, 3);
+
+ conf = new_config();
+ log_info("rtr engine ready");
+
+ while (rtr_quit == 0) {
+ i = rtr_count();
+ if (pfd_elms < PFD_PIPE_COUNT + i) {
+ if ((newp = reallocarray(pfd,
+ PFD_PIPE_COUNT + i,
+ sizeof(struct pollfd))) == NULL)
+ fatal("realloc pollfd");
+ pfd = newp;
+ pfd_elms = PFD_PIPE_COUNT + i;
+ }
+ timeout = 240; /* loop every 240s at least */
+ bzero(pfd, sizeof(struct pollfd) * pfd_elms);
+
+ set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main);
+ set_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde);
+
+ now = getmonotime();
+ i = PFD_PIPE_COUNT;
+ i += rtr_poll_events(pfd + i, pfd_elms - i, &timeout);
+
+ if (poll(pfd, i, timeout * 1000) == -1) {
+ if (errno != EINTR)
+ fatal("poll error");
+ continue;
+ }
+
+ if (handle_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main) == -1)
+ fatalx("Lost connection to parent");
+ else
+ rtr_dispatch_imsg_parent(ibuf_main);
+
+ if (handle_pollfd(&pfd[PFD_PIPE_RDE], ibuf_rde) == -1) {
+ log_warnx("ROA: Lost connection to RDE");
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ ibuf_rde = NULL;
+ } else
+ rtr_dispatch_imsg_rde(ibuf_rde);
+
+ now = getmonotime();
+ i = PFD_PIPE_COUNT;
+ rtr_check_events(pfd + i, pfd_elms - i);
+ }
+
+ rtr_shutdown();
+
+ free_config(conf);
+ free(pfd);
+
+ /* close pipes */
+ if (ibuf_rde) {
+ msgbuf_clear(&ibuf_rde->w);
+ close(ibuf_rde->fd);
+ free(ibuf_rde);
+ }
+ msgbuf_clear(&ibuf_main->w);
+ close(ibuf_main->fd);
+ free(ibuf_main);
+
+ log_info("rtr engine exiting");
+ exit(0);
+}
+
+static void
+rtr_dispatch_imsg_parent(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ struct roa *roa;
+ struct rtr_session *rs;
+ int n, fd;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_SOCKET_CONN_RTR:
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
+ break;
+ }
+ if (ibuf_rde) {
+ log_warnx("Unexpected imsg ctl "
+ "connection to RDE received");
+ msgbuf_clear(&ibuf_rde->w);
+ free(ibuf_rde);
+ }
+ if ((ibuf_rde = malloc(sizeof(struct imsgbuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf_rde, fd);
+ break;
+ case IMSG_SOCKET_CONN:
+ if ((fd = imsg.fd) == -1) {
+ log_warnx("expected to receive imsg fd "
+ "but didn't receive any");
+ break;
+ }
+ if ((rs = rtr_get(imsg.hdr.peerid)) == NULL) {
+ log_warnx("IMSG_SOCKET_CONN: unknown rtr id %d",
+ imsg.hdr.peerid);
+ break;
+ }
+ rtr_open(rs, fd);
+ break;
+ case IMSG_RECONF_CONF:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(struct bgpd_config))
+ fatalx("IMSG_RECONF_CONF bad len");
+ nconf = new_config();
+ copy_config(nconf, imsg.data);
+ rtr_config_prep();
+ break;
+ case IMSG_RECONF_ROA_ITEM:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE !=
+ sizeof(*roa))
+ fatalx("IMSG_RECONF_ROA_ITEM bad len");
+ roa_insert(&nconf->roa, imsg.data);
+ break;
+ case IMSG_RECONF_RTR_CONFIG:
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != PEER_DESCR_LEN)
+ fatalx("IMSG_RECONF_RTR_CONFIG bad len");
+ rs = rtr_get(imsg.hdr.peerid);
+ if (rs == NULL)
+ rtr_new(imsg.hdr.peerid, imsg.data);
+ else
+ rtr_config_keep(rs);
+ break;
+ case IMSG_RECONF_DRAIN:
+ imsg_compose(ibuf_main, IMSG_RECONF_DRAIN, 0, 0,
+ -1, NULL, 0);
+ break;
+ case IMSG_RECONF_DONE:
+ if (nconf == NULL)
+ fatalx("got IMSG_RECONF_DONE but no config");
+ copy_config(conf, nconf);
+ /* switch the roa, first remove the old one */
+ free_roatree(&conf->roa);
+ /* then move the RB tree root */
+ RB_ROOT(&conf->roa) = RB_ROOT(&nconf->roa);
+ RB_ROOT(&nconf->roa) = NULL;
+ /* finally merge the rtr session */
+ rtr_config_merge();
+ rtr_recalc();
+ log_info("ROA engine reconfigured");
+ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0,
+ -1, NULL, 0);
+ free_config(nconf);
+ nconf = NULL;
+ break;
+ case IMSG_CTL_SHOW_RTR:
+ if ((rs = rtr_get(imsg.hdr.peerid)) == NULL) {
+ log_warnx("IMSG_CTL_SHOW_RTR: "
+ "unknown rtr id %d", imsg.hdr.peerid);
+ break;
+ }
+ rtr_show(rs, imsg.hdr.pid);
+ break;
+ case IMSG_CTL_END:
+ imsg_compose(ibuf_main, IMSG_CTL_END, 0, imsg.hdr.pid,
+ -1, NULL, 0);
+ break;
+ }
+ imsg_free(&imsg);
+ }
+}
+
+static void
+rtr_dispatch_imsg_rde(struct imsgbuf *ibuf)
+{
+ struct imsg imsg;
+ int n;
+
+ while (ibuf) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatal("%s: imsg_get error", __func__);
+ if (n == 0)
+ break;
+
+ /* NOTHING */
+
+ imsg_free(&imsg);
+ }
+}
+
+void
+rtr_imsg_compose(int type, uint32_t id, pid_t pid, void *data, size_t datalen)
+{
+ imsg_compose(ibuf_main, type, id, pid, -1, data, datalen);
+}
+
+/*
+ * Merge all RPKI ROA trees into one as one big union.
+ * Simply try to add all roa entries into a new RB tree.
+ * This could be made a fair bit faster but for now this is good enough.
+ */
+void
+rtr_recalc(void)
+{
+ struct roa_tree rt;
+ struct roa *roa, *nr;
+
+ RB_INIT(&rt);
+
+ RB_FOREACH(roa, roa_tree, &conf->roa)
+ roa_insert(&rt, roa);
+ rtr_roa_merge(&rt);
+
+ imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, NULL, 0);
+ RB_FOREACH_SAFE(roa, roa_tree, &rt, nr) {
+ RB_REMOVE(roa_tree, &rt, roa);
+ imsg_compose(ibuf_rde, IMSG_RECONF_ROA_ITEM, 0, 0, -1,
+ roa, sizeof(*roa));
+ free(roa);
+ }
+ imsg_compose(ibuf_rde, IMSG_RECONF_DONE, 0, 0, -1, NULL, 0);
+}
Index: bgpd/rtr_proto.c
===================================================================
RCS file: bgpd/rtr_proto.c
diff -N bgpd/rtr_proto.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ bgpd/rtr_proto.c 26 Jan 2021 08:20:22 -0000
@@ -0,0 +1,1157 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/tree.h>
+#include <errno.h>
+#include <stdint.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "log.h"
+
+struct rtr_header {
+ uint8_t version;
+ uint8_t type;
+ uint16_t session_id; /* or error code */
+ uint32_t length;
+};
+
+#define RTR_MAX_LEN 2048
+#define RTR_DEFAULT_REFRESH 3600
+#define RTR_DEFAULT_RETRY 600
+#define RTR_DEFAULT_EXPIRE 7200
+
+enum rtr_pdu_type {
+ SERIAL_NOTIFY = 0,
+ SERIAL_QUERY,
+ RESET_QUERY,
+ CACHE_RESPONSE,
+ IPV4_PREFIX,
+ IPV6_PREFIX = 6,
+ END_OF_DATA = 7,
+ CACHE_RESET = 8,
+ ROUTER_KEY = 9,
+ ERROR_REPORT = 10,
+};
+
+#define FLAG_ANNOUNCE 0x1
+#define FLAG_MASK FLAG_ANNOUNCE
+struct rtr_ipv4 {
+ uint8_t flags;
+ uint8_t prefixlen;
+ uint8_t maxlen;
+ uint8_t zero;
+ uint32_t prefix;
+ uint32_t asnum;
+};
+
+struct rtr_ipv6 {
+ uint8_t flags;
+ uint8_t prefixlen;
+ uint8_t maxlen;
+ uint8_t zero;
+ uint32_t prefix[4];
+ uint32_t asnum;
+};
+
+struct rtr_endofdata {
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+};
+
+enum rtr_event {
+ RTR_EVNT_START,
+ RTR_EVNT_CON_OPEN,
+ RTR_EVNT_CON_CLOSED,
+ RTR_EVNT_TIMER_REFRESH,
+ RTR_EVNT_TIMER_RETRY,
+ RTR_EVNT_TIMER_EXPIRE,
+ RTR_EVNT_SEND_ERROR,
+ RTR_EVNT_SERIAL_NOTIFY,
+ RTR_EVNT_CACHE_RESPONSE,
+ RTR_EVNT_END_OF_DATA,
+ RTR_EVNT_CACHE_RESET,
+ RTR_EVNT_NO_DATA,
+};
+
+static const char *rtr_eventnames[] = {
+ "start",
+ "connection open",
+ "connection closed",
+ "refresh timer expired",
+ "retry timer expired",
+ "expire timer expired",
+ "sent error",
+ "serial notify received",
+ "cache response received",
+ "end of data received",
+ "cache reset received",
+ "no data"
+};
+
+enum rtr_state {
+ RTR_STATE_CLOSED,
+ RTR_STATE_ERROR,
+ RTR_STATE_IDLE,
+ RTR_STATE_ACTIVE,
+};
+
+static const char *rtr_statenames[] = {
+ "closed",
+ "error",
+ "idle",
+ "active"
+};
+
+struct rtr_session {
+ TAILQ_ENTRY(rtr_session) entry;
+ char descr[PEER_DESCR_LEN];
+ struct roa_tree roa_set;
+ struct ibuf_read r;
+ struct msgbuf w;
+ struct timer_head timers;
+ uint32_t id; /* rtr_config id */
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ int session_id;
+ int fd;
+ enum rtr_state state;
+ enum reconf_action reconf_action;
+ enum rtr_error last_sent_error;
+ enum rtr_error last_recv_error;
+ char last_sent_msg[REASON_LEN];
+ char last_recv_msg[REASON_LEN];
+};
+
+TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs);
+
+static void rtr_fsm(struct rtr_session *, enum rtr_event);
+
+static const char *
+log_rtr(struct rtr_session *rs)
+{
+ return rs->descr;
+}
+
+static const char *
+log_rtr_type(enum rtr_pdu_type type)
+{
+ static char buf[20];
+
+ switch (type) {
+ case SERIAL_NOTIFY:
+ return "serial notify";
+ case SERIAL_QUERY:
+ return "serial query";
+ case RESET_QUERY:
+ return "reset query";
+ case CACHE_RESPONSE:
+ return "cache response";
+ case IPV4_PREFIX:
+ return "IPv4 prefix";
+ case IPV6_PREFIX:
+ return "IPv6 prefix";
+ case END_OF_DATA:
+ return "end of data";
+ case CACHE_RESET:
+ return "cache reset";
+ case ROUTER_KEY:
+ return "router key";
+ case ERROR_REPORT:
+ return "error report";
+ default:
+ snprintf(buf, sizeof(buf), "unknown %u", type);
+ return buf;
+ }
+};
+
+static struct ibuf *
+rtr_newmsg(enum rtr_pdu_type type, uint32_t len, u_int16_t session_id)
+{
+ struct ibuf *buf;
+ struct rtr_header rh;
+
+ if (len > RTR_MAX_LEN) {
+ errno = ERANGE;
+ return NULL;
+ }
+ len += sizeof(rh);
+ if ((buf = ibuf_open(len)) == NULL)
+ return NULL;
+
+ memset(&rh, 0, sizeof(rh));
+ rh.version = 1;
+ rh.type = type;
+ rh.session_id = htons(session_id);
+ rh.length = htonl(len);
+
+ /* can not fail with fixed buffers */
+ ibuf_add(buf, &rh, sizeof(rh));
+ return buf;
+}
+
+/*
+ * Try to send an error PDU to cache, put connection into error
+ * state.
+ */
+static void
+rtr_send_error(struct rtr_session *rs, enum rtr_error err, char *msg,
+ void *pdu, size_t len)
+{
+ struct ibuf *buf;
+ size_t mlen = 0;
+ uint32_t hdrlen;
+
+ rs->last_sent_error = err;
+ if (msg) {
+ mlen = strlen(msg);
+ strlcpy(rs->last_sent_msg, msg, sizeof(rs->last_sent_msg));
+ } else
+ memset(rs->last_sent_msg, 0, sizeof(rs->last_sent_msg));
+
+ rtr_fsm(rs, RTR_EVNT_SEND_ERROR);
+
+ buf = rtr_newmsg(ERROR_REPORT, 2 * sizeof(hdrlen) + len + mlen, err);
+ if (buf == NULL) {
+ log_warn("rtr %s: send error report", log_rtr(rs));
+ return;
+ }
+
+ /* can not fail with fixed buffers */
+ hdrlen = ntohl(len);
+ ibuf_add(buf, &hdrlen, sizeof(hdrlen));
+ ibuf_add(buf, pdu, len);
+ hdrlen = ntohl(mlen);
+ ibuf_add(buf, &hdrlen, sizeof(hdrlen));
+ ibuf_add(buf, msg, mlen);
+ ibuf_close(&rs->w, buf);
+
+ log_warnx("%s: sending error report[%u] %s", log_rtr(rs), err,
+ msg ? msg : "");
+}
+
+static void
+rtr_reset_query(struct rtr_session *rs)
+{
+ struct ibuf *buf;
+
+ buf = rtr_newmsg(RESET_QUERY, 0, 0);
+ if (buf == NULL) {
+ log_warn("rtr %s: send reset query", log_rtr(rs));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return;
+ }
+ ibuf_close(&rs->w, buf);
+}
+
+static void
+rtr_serial_query(struct rtr_session *rs)
+{
+ struct ibuf *buf;
+ uint32_t s;
+
+ buf = rtr_newmsg(SERIAL_QUERY, sizeof(s), rs->session_id);
+ if (buf == NULL) {
+ log_warn("rtr %s: send serial query", log_rtr(rs));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return;
+ }
+
+ /* can not fail with fixed buffers */
+ s = htonl(rs->serial);
+ ibuf_add(buf, &s, sizeof(s));
+ ibuf_close(&rs->w, buf);
+}
+
+/*
+ * Validate the common rtr header (first 8 bytes) including the
+ * included length field.
+ * Returns -1 on failure. On success msgtype and msglen are set
+ * and the function return 0.
+ */
+static int
+rtr_parse_header(struct rtr_session *rs, void *buf,
+ size_t *msglen, enum rtr_pdu_type *msgtype)
+{
+ struct rtr_header rh;
+ uint32_t len = 16; /* default for ERROR_REPORT */
+ int session_id;
+
+ memcpy(&rh, buf, sizeof(rh));
+
+ if (rh.version != 1) {
+ log_warnx("rtr %s: received message with unsupported version",
+ log_rtr(rs));
+ rtr_send_error(rs, UNEXP_PROTOCOL_VERS, NULL, &rh, sizeof(rh));
+ return -1;
+ }
+
+ *msgtype = rh.type;
+ *msglen = ntohl(rh.length);
+
+ switch (*msgtype) {
+ case SERIAL_NOTIFY:
+ session_id = rs->session_id;
+ len = 12;
+ break;
+ case CACHE_RESPONSE:
+ /* set session_id if not yet happened */
+ if (rs->session_id == -1)
+ rs->session_id = ntohs(rh.session_id);
+ session_id = rs->session_id;
+ len = 8;
+ break;
+ case IPV4_PREFIX:
+ session_id = 0;
+ len = 20;
+ break;
+ case IPV6_PREFIX:
+ session_id = 0;
+ len = 32;
+ break;
+ case END_OF_DATA:
+ session_id = rs->session_id;
+ len = 24;
+ break;
+ case CACHE_RESET:
+ session_id = 0;
+ len = 8;
+ break;
+ case ROUTER_KEY:
+ len = 36; /* XXX probably too small, but we ignore it */
+ /* FALLTHROUGH */
+ case ERROR_REPORT:
+ if (*msglen > RTR_MAX_LEN) {
+ log_warnx("rtr %s: received %s: msg too big: %zu byte",
+ log_rtr(rs), log_rtr_type(*msgtype), *msglen);
+ rtr_send_error(rs, CORRUPT_DATA, "too big",
+ &rh, sizeof(rh));
+ return -1;
+ }
+ if (*msglen < len) {
+ log_warnx("rtr %s: received %s: msg too small: "
+ "%zu byte", log_rtr(rs), log_rtr_type(*msgtype),
+ *msglen);
+ rtr_send_error(rs, CORRUPT_DATA, "too small",
+ &rh, sizeof(rh));
+ return -1;
+ }
+ /*
+ * session_id check ommitted since ROUTER_KEY and ERROR_REPORT
+ * use the field for different things.
+ */
+ return 0;
+ default:
+ log_warnx("rtr %s: received unknown message: type %u",
+ log_rtr(rs), *msgtype);
+ rtr_send_error(rs, UNSUPP_PDU_TYPE, NULL, &rh, sizeof(rh));
+ return -1;
+ }
+
+ if (len != *msglen) {
+ log_warnx("rtr %s: received %s: illegal len: %zu byte not %u",
+ log_rtr(rs), log_rtr_type(*msgtype), *msglen, len);
+ rtr_send_error(rs, CORRUPT_DATA, "bad length",
+ &rh, sizeof(rh));
+ return -1;
+ }
+
+ if (session_id != ntohs(rh.session_id)) {
+ /* ignore SERIAL_NOTIFY during startup */
+ if (rs->session_id == -1 && *msgtype == SERIAL_NOTIFY)
+ return 0;
+
+ log_warnx("rtr %s: received %s: bad session_id: %d != %d",
+ log_rtr(rs), log_rtr_type(*msgtype), ntohs(rh.session_id),
+ session_id);
+ rtr_send_error(rs, CORRUPT_DATA, "bad session_id",
+ &rh, sizeof(rh));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+rtr_parse_notify(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state == RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: while active (ignored)",
+ log_rtr(rs), log_rtr_type(SERIAL_NOTIFY));
+ return 0;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_SERIAL_NOTIFY);
+ return 0;
+}
+
+static int
+rtr_parse_cache_response(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state != RTR_STATE_IDLE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(CACHE_RESPONSE));
+ return -1;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_CACHE_RESPONSE);
+ return 0;
+}
+
+static int
+rtr_parse_ipv4_prefix(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_ipv4 ip4;
+ struct roa *roa;
+
+ if (len != sizeof(struct rtr_header) + sizeof(ip4)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, "bad len", buf, len);
+ return -1;
+ }
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, NULL, buf, len);
+ return -1;
+ }
+
+ memcpy(&ip4, buf + sizeof(struct rtr_header), sizeof(ip4));
+
+ if ((roa = calloc(1, sizeof(*roa))) == NULL) {
+ log_warn("rtr %s: received %s",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return -1;
+ }
+ roa->aid = AID_INET;
+ roa->prefixlen = ip4.prefixlen;
+ roa->maxlen = ip4.maxlen;
+ roa->asnum = ntohl(ip4.asnum);
+ roa->prefix.inet.s_addr = ip4.prefix;
+
+ if (ip4.flags & FLAG_ANNOUNCE) {
+ if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
+ log_warnx("rtr %s: received %s: duplicate announcement",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, DUP_REC_RECV, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ } else {
+ struct roa *r;
+
+ r = RB_FIND(roa_tree, &rs->roa_set, roa);
+ if (r == NULL) {
+ log_warnx("rtr %s: received %s: unknown withdrawl",
+ log_rtr(rs), log_rtr_type(IPV4_PREFIX));
+ rtr_send_error(rs, UNK_REC_WDRAWL, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ RB_REMOVE(roa_tree, &rs->roa_set, r);
+ free(r);
+ free(roa);
+ }
+
+ return 0;
+}
+
+static int
+rtr_parse_ipv6_prefix(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_ipv6 ip6;
+ struct roa *roa;
+
+ if (len != sizeof(struct rtr_header) + sizeof(ip6)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, "bad len", buf, len);
+ return -1;
+ }
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, CORRUPT_DATA, NULL, buf, len);
+ return -1;
+ }
+
+ memcpy(&ip6, buf + sizeof(struct rtr_header), sizeof(ip6));
+
+ if ((roa = calloc(1, sizeof(*roa))) == NULL) {
+ log_warn("rtr %s: received %s",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0);
+ return -1;
+ }
+ roa->aid = AID_INET6;
+ roa->prefixlen = ip6.prefixlen;
+ roa->maxlen = ip6.maxlen;
+ roa->asnum = ntohl(ip6.asnum);
+ memcpy(&roa->prefix.inet6, ip6.prefix, sizeof(roa->prefix.inet6));
+
+ if (ip6.flags & FLAG_ANNOUNCE) {
+ if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) {
+ log_warnx("rtr %s: received %s: duplicate announcement",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, DUP_REC_RECV, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ } else {
+ struct roa *r;
+
+ r = RB_FIND(roa_tree, &rs->roa_set, roa);
+ if (r == NULL) {
+ log_warnx("rtr %s: received %s: unknown withdrawl",
+ log_rtr(rs), log_rtr_type(IPV6_PREFIX));
+ rtr_send_error(rs, UNK_REC_WDRAWL, NULL, buf, len);
+ free(roa);
+ return -1;
+ }
+ RB_REMOVE(roa_tree, &rs->roa_set, r);
+ free(r);
+ free(roa);
+ }
+ return 0;
+}
+
+static int
+rtr_parse_end_of_data(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_endofdata eod;
+ uint32_t t;
+
+ buf += sizeof(struct rtr_header);
+ len -= sizeof(struct rtr_header);
+
+ if (len != sizeof(eod)) {
+ log_warnx("rtr %s: received %s: bad pdu len",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+ }
+
+ memcpy(&eod, buf, sizeof(eod));
+
+ if (rs->state != RTR_STATE_ACTIVE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+ }
+
+ rs->serial = ntohl(eod.serial);
+ /* validate timer values to be in the right range */
+ t = ntohl(eod.refresh);
+ if (t < 1 || t > 86400)
+ goto bad;
+ rs->refresh = t;
+ t = ntohl(eod.retry);
+ if (t < 1 || t > 7200)
+ goto bad;
+ rs->retry = t;
+ t = ntohl(eod.expire);
+ if (t < 600 || t > 172800)
+ goto bad;
+ if (t <= rs->retry || t <= rs->refresh)
+ goto bad;
+ rs->expire = t;
+
+ rtr_fsm(rs, RTR_EVNT_END_OF_DATA);
+ return 0;
+
+bad:
+ log_warnx("rtr %s: received %s: bad timeout values",
+ log_rtr(rs), log_rtr_type(END_OF_DATA));
+ return -1;
+}
+
+static int
+rtr_parse_cache_reset(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ if (rs->state != RTR_STATE_IDLE) {
+ log_warnx("rtr %s: received %s: out of context",
+ log_rtr(rs), log_rtr_type(CACHE_RESET));
+ return -1;
+ }
+
+ rtr_fsm(rs, RTR_EVNT_CACHE_RESET);
+ return 0;
+}
+
+/*
+ * Parse an Error Response message. This function behaves a bit different
+ * from other parse functions since on error the connection needs to be
+ * dropped without sending an error response back.
+ */
+static int
+rtr_parse_error(struct rtr_session *rs, uint8_t *buf, size_t len)
+{
+ struct rtr_header rh;
+ uint32_t pdu_len, msg_len;
+ uint8_t *msg;
+ char *str = NULL;
+ uint16_t errcode;
+
+ memcpy(&rh, buf, sizeof(rh));
+ buf += sizeof(struct rtr_header);
+ len -= sizeof(struct rtr_header);
+ errcode = ntohs(rh.session_id);
+
+ memcpy(&pdu_len, buf, sizeof(pdu_len));
+ pdu_len = ntohs(pdu_len);
+
+ if (len < pdu_len + sizeof(pdu_len)) {
+ log_warnx("rtr %s: received %s: bad pdu len: %u byte",
+ log_rtr(rs), log_rtr_type(ERROR_REPORT), pdu_len);
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return -1;
+ }
+
+ /* for now just ignore the embedded pdu */
+ buf += pdu_len + sizeof(pdu_len);
+ len -= pdu_len + sizeof(pdu_len);
+
+ memcpy(&msg_len, buf, sizeof(msg_len));
+ msg_len = ntohs(msg_len);
+
+ if (len < msg_len + sizeof(msg_len)) {
+ log_warnx("rtr %s: received %s: bad msg len: %u byte",
+ log_rtr(rs), log_rtr_type(ERROR_REPORT), msg_len);
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return -1;
+ }
+
+ msg = buf + sizeof(msg_len);
+ if (msg_len != 0)
+ /* optional error msg, no need to check for failure */
+ str = strndup(msg, msg_len);
+
+ log_warnx("rtr %s: received error: %s%s%s", log_rtr(rs),
+ log_rtr_error(errcode), str ? ": " : "", str ? str : "");
+
+ if (errcode == NO_DATA_AVAILABLE) {
+ rtr_fsm(rs, RTR_EVNT_NO_DATA);
+ } else {
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ rs->last_recv_error = errcode;
+ if (str)
+ strlcpy(rs->last_recv_msg, str,
+ sizeof(rs->last_recv_msg));
+ else
+ memset(rs->last_recv_msg, 0,
+ sizeof(rs->last_recv_msg));
+
+ free(str);
+ return -1;
+ }
+ free(str);
+
+ return 0;
+}
+
+/*
+ * Try to process received rtr message, it is possible that not a full
+ * message is in the buffer. In that case stop, once new data is available
+ * a retry will be done.
+ */
+static void
+rtr_process_msg(struct rtr_session *rs)
+{
+ size_t rpos, av, left;
+ void *rptr;
+ size_t msglen;
+ enum rtr_pdu_type msgtype;
+
+ rpos = 0;
+ av = rs->r.wpos;
+
+ for (;;) {
+ if (rpos + sizeof(struct rtr_header) > av)
+ break;
+ rptr = rs->r.buf + rpos;
+ if (rtr_parse_header(rs, rptr, &msglen, &msgtype) == -1)
+ return;
+
+ /* missing data */
+ if (rpos + msglen > av)
+ break;
+
+ switch (msgtype) {
+ case SERIAL_NOTIFY:
+ if (rtr_parse_notify(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case CACHE_RESPONSE:
+ if (rtr_parse_cache_response(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case IPV4_PREFIX:
+ if (rtr_parse_ipv4_prefix(rs, rptr, msglen) == -1) {
+ return;
+ }
+ break;
+ case IPV6_PREFIX:
+ if (rtr_parse_ipv6_prefix(rs, rptr, msglen) == -1) {
+ return;
+ }
+ break;
+ case END_OF_DATA:
+ if (rtr_parse_end_of_data(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case CACHE_RESET:
+ if (rtr_parse_cache_reset(rs, rptr, msglen) == -1) {
+ rtr_send_error(rs, CORRUPT_DATA, NULL,
+ rptr, msglen);
+ return;
+ }
+ break;
+ case ROUTER_KEY:
+ /* silently ignore router key */
+ break;
+ case ERROR_REPORT:
+ if (rtr_parse_error(rs, rptr, msglen) == -1)
+ /* no need to send back an error */
+ return;
+ break;
+ default:
+ log_warnx("rtr %s: received %s: unexpected pdu type",
+ log_rtr(rs), log_rtr_type(msgtype));
+ rtr_send_error(rs, INVALID_REQUEST, NULL, rptr, msglen);
+ return;
+ }
+ rpos += msglen;
+ }
+
+ left = av - rpos;
+ memmove(&rs->r.buf, rs->r.buf + rpos, left);
+ rs->r.wpos = left;
+}
+
+/*
+ * Simple FSM for RTR sessions
+ */
+static void
+rtr_fsm(struct rtr_session *rs, enum rtr_event event)
+{
+ enum rtr_state prev_state = rs->state;
+
+ switch (event) {
+ case RTR_EVNT_CON_CLOSED:
+ if (rs->state != RTR_STATE_CLOSED) {
+ /* flush buffers */
+ msgbuf_clear(&rs->w);
+ rs->r.wpos = 0;
+ close(rs->fd);
+ rs->fd = -1;
+ }
+ rs->state = RTR_STATE_CLOSED;
+ /* try to reopen session */
+ timer_set(&rs->timers, Timer_Rtr_Retry,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_START:
+ case RTR_EVNT_TIMER_RETRY:
+ switch (rs->state) {
+ case RTR_STATE_ERROR:
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ case RTR_STATE_CLOSED:
+ timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
+ rtr_imsg_compose(IMSG_SOCKET_CONN, rs->id, 0, NULL, 0);
+ return;
+ default:
+ break;
+ }
+ /* FALLTHROUGH */
+ case RTR_EVNT_CON_OPEN:
+ timer_stop(&rs->timers, Timer_Rtr_Retry);
+ if (rs->session_id == -1)
+ rtr_reset_query(rs);
+ else
+ rtr_serial_query(rs);
+ break;
+ case RTR_EVNT_SERIAL_NOTIFY:
+ /* schedule a refresh after a quick wait */
+ timer_set(&rs->timers, Timer_Rtr_Refresh,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_TIMER_REFRESH:
+ /* send serial query */
+ rtr_serial_query(rs);
+ break;
+ case RTR_EVNT_TIMER_EXPIRE:
+ free_roatree(&rs->roa_set);
+ rtr_recalc();
+ break;
+ case RTR_EVNT_CACHE_RESPONSE:
+ rs->state = RTR_STATE_ACTIVE;
+ timer_stop(&rs->timers, Timer_Rtr_Refresh);
+ timer_stop(&rs->timers, Timer_Rtr_Retry);
+ break;
+ case RTR_EVNT_END_OF_DATA:
+ /* start refresh and expire timers */
+ timer_set(&rs->timers, Timer_Rtr_Refresh, rs->refresh);
+ timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
+ rs->state = RTR_STATE_IDLE;
+ rtr_recalc();
+ break;
+ case RTR_EVNT_CACHE_RESET:
+ /* reset session and retry after a quick wait */
+ rs->session_id = -1;
+ free_roatree(&rs->roa_set);
+ timer_set(&rs->timers, Timer_Rtr_Retry,
+ arc4random_uniform(10));
+ break;
+ case RTR_EVNT_NO_DATA:
+ /* start retry timer */
+ timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry);
+ /* stop refresh timer just to be sure */
+ timer_stop(&rs->timers, Timer_Rtr_Refresh);
+ break;
+ case RTR_EVNT_SEND_ERROR:
+ rs->state = RTR_STATE_ERROR;
+ /* flush receive buffer */
+ rs->r.wpos = 0;
+ break;
+ }
+
+ log_info("rtr %s: state change %s -> %s, reason: %s",
+ log_rtr(rs), rtr_statenames[prev_state], rtr_statenames[rs->state],
+ rtr_eventnames[event]);
+}
+
+/*
+ * IO handler for RTR sessions
+ */
+static void
+rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs)
+{
+ ssize_t n;
+ int error;
+
+ if (pfd->revents & POLLHUP) {
+ log_warnx("rtr %s: Connection closed, hangup", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (pfd->revents & (POLLERR|POLLNVAL)) {
+ log_warnx("rtr %s: Connection closed, error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (pfd->revents & POLLOUT && rs->w.queued) {
+ if ((error = ibuf_write(&rs->w)) <= 0 && errno != EAGAIN) {
+ if (error == 0)
+ log_warnx("rtr %s: Connection closed",
+ log_rtr(rs));
+ else if (error == -1)
+ log_warn("rtr %s: write error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ if (rs->w.queued == 0 && rs->state == RTR_STATE_ERROR)
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+ if (pfd->revents & POLLIN) {
+ if ((n = read(rs->fd, rs->r.buf + rs->r.wpos,
+ sizeof(rs->r.buf) - rs->r.wpos)) == -1) {
+ if (errno != EINTR && errno != EAGAIN) {
+ log_warn("rtr %s: read error", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+ return;
+ }
+ if (n == 0) {
+ log_warnx("rtr %s: Connection closed", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ return;
+ }
+ rs->r.wpos += n;
+
+ /* new data arrived, try to process it */
+ rtr_process_msg(rs);
+ }
+
+}
+
+void
+rtr_check_events(struct pollfd *pfds, size_t npfds)
+{
+ struct rtr_session *rs;
+ struct timer *t;
+ time_t now;
+ size_t i = 0;
+
+ for (i = 0; i < npfds; i++) {
+ if (pfds[i].revents == 0)
+ continue;
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if (rs->fd == pfds[i].fd) {
+ rtr_dispatch_msg(&pfds[i], rs);
+ break;
+ }
+ if (rs == NULL)
+ log_warnx("%s: unknown fd in pollfds", __func__);
+ }
+
+ /* run all timers */
+ now = getmonotime();
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if ((t = timer_nextisdue(&rs->timers, now)) != NULL) {
+ log_debug("rtr %s: %s triggered", log_rtr(rs),
+ timernames[t->type]);
+ /* stop timer so it does not trigger again */
+ timer_stop(&rs->timers, t->type);
+ switch (t->type) {
+ case Timer_Rtr_Refresh:
+ rtr_fsm(rs, RTR_EVNT_TIMER_REFRESH);
+ break;
+ case Timer_Rtr_Retry:
+ rtr_fsm(rs, RTR_EVNT_TIMER_RETRY);
+ break;
+ case Timer_Rtr_Expire:
+ rtr_fsm(rs, RTR_EVNT_TIMER_EXPIRE);
+ break;
+ default:
+ fatalx("King Bula lost in time");
+ }
+ }
+}
+
+size_t
+rtr_count(void)
+{
+ struct rtr_session *rs;
+ size_t count = 0;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ count++;
+ return count;
+}
+
+size_t
+rtr_poll_events(struct pollfd *pfds, size_t npfds, time_t *timeout)
+{
+ struct rtr_session *rs;
+ time_t now = getmonotime();
+ size_t i = 0;
+
+ TAILQ_FOREACH(rs, &rtrs, entry) {
+ time_t nextaction;
+ struct pollfd *pfd = pfds + i++;
+
+ if (i > npfds)
+ fatalx("%s: too many sessions for pollfd", __func__);
+
+ if ((nextaction = timer_nextduein(&rs->timers, now)) != -1 &&
+ nextaction < *timeout)
+ *timeout = nextaction;
+
+ if (rs->state == RTR_STATE_CLOSED) {
+ pfd->fd = -1;
+ continue;
+ }
+
+ pfd->fd = rs->fd;
+ pfd->events = 0;
+
+ if (rs->w.queued)
+ pfd->events |= POLLOUT;
+ if (rs->state >= RTR_STATE_IDLE)
+ pfd->events |= POLLIN;
+ }
+
+ return i;
+}
+
+struct rtr_session *
+rtr_new(uint32_t id, char *descr)
+{
+ struct rtr_session *rs;
+
+ if ((rs = calloc(1, sizeof(*rs))) == NULL)
+ fatal("RTR session %s", descr);
+
+ RB_INIT(&rs->roa_set);
+ TAILQ_INIT(&rs->timers);
+ msgbuf_init(&rs->w);
+
+ strlcpy(rs->descr, descr, sizeof(rs->descr));
+ rs->id = id;
+ rs->session_id = -1;
+ rs->refresh = RTR_DEFAULT_REFRESH;
+ rs->retry = RTR_DEFAULT_RETRY;
+ rs->expire = RTR_DEFAULT_EXPIRE;
+ rs->state = RTR_STATE_CLOSED;
+ rs->reconf_action = RECONF_REINIT;
+ rs->last_recv_error = NO_ERROR;
+ rs->last_sent_error = NO_ERROR;
+
+ /* make sure that some timer is running to abort bad sessions */
+ timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire);
+
+ log_debug("rtr %s: new session, start", log_rtr(rs));
+ TAILQ_INSERT_TAIL(&rtrs, rs, entry);
+ rtr_fsm(rs, RTR_EVNT_START);
+
+ return rs;
+}
+
+struct rtr_session *
+rtr_get(uint32_t id)
+{
+ struct rtr_session *rs;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ if (rs->id == id)
+ return rs;
+ return NULL;
+}
+
+void
+rtr_free(struct rtr_session *rs)
+{
+ if (rs == NULL)
+ return;
+
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ timer_remove_all(&rs->timers);
+ free_roatree(&rs->roa_set);
+ free(rs);
+}
+
+void
+rtr_open(struct rtr_session *rs, int fd)
+{
+ if (rs->state != RTR_STATE_CLOSED) {
+ log_warnx("rtr %s: bad session state", log_rtr(rs));
+ rtr_fsm(rs, RTR_EVNT_CON_CLOSED);
+ }
+
+ log_debug("rtr %s: connection opened", log_rtr(rs));
+
+ rs->fd = rs->w.fd = fd;
+ rs->state = RTR_STATE_IDLE;
+ rtr_fsm(rs, RTR_EVNT_CON_OPEN);
+}
+
+void
+rtr_config_prep(void)
+{
+ struct rtr_session *rs;
+
+ TAILQ_FOREACH(rs, &rtrs, entry)
+ rs->reconf_action = RECONF_DELETE;
+}
+
+void
+rtr_config_merge(void)
+{
+ struct rtr_session *rs, *nrs;
+
+ TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
+ if (rs->reconf_action == RECONF_DELETE) {
+ TAILQ_REMOVE(&rtrs, rs, entry);
+ rtr_free(rs);
+ }
+}
+
+void
+rtr_config_keep(struct rtr_session *rs)
+{
+ rs->reconf_action = RECONF_KEEP;
+}
+
+void
+rtr_roa_merge(struct roa_tree *rt)
+{
+ struct rtr_session *rs;
+ struct roa *roa;
+
+ TAILQ_FOREACH(rs, &rtrs, entry) {
+ RB_FOREACH(roa, roa_tree, &rs->roa_set)
+ roa_insert(rt, roa);
+ }
+}
+
+void
+rtr_shutdown(void)
+{
+ struct rtr_session *rs, *nrs;
+
+ TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs)
+ rtr_free(rs);
+}
+
+void
+rtr_show(struct rtr_session *rs, pid_t pid)
+{
+ struct ctl_show_rtr msg;
+ struct ctl_timer ct;
+ u_int i;
+ time_t d;
+
+ memset(&msg, 0, sizeof(msg));
+
+ /* descr, remote_addr, local_addr and remote_port set by parent */
+ msg.serial = rs->serial;
+ msg.refresh = rs->refresh;
+ msg.retry = rs->retry;
+ msg.expire = rs->expire;
+ msg.session_id = rs->session_id;
+ msg.last_sent_error = rs->last_sent_error;
+ msg.last_recv_error = rs->last_recv_error;
+ strlcpy(msg.last_sent_msg, rs->last_sent_msg,
+ sizeof(msg.last_sent_msg));
+ strlcpy(msg.last_recv_msg, rs->last_recv_msg,
+ sizeof(msg.last_recv_msg));
+
+ /* send back imsg */
+ rtr_imsg_compose(IMSG_CTL_SHOW_RTR, rs->id, pid, &msg, sizeof(msg));
+
+ /* send back timer imsgs */
+ for (i = 1; i < Timer_Max; i++) {
+ if (!timer_running(&rs->timers, i, &d))
+ continue;
+ ct.type = i;
+ ct.val = d;
+ rtr_imsg_compose(IMSG_CTL_SHOW_TIMER, rs->id, pid,
+ &ct, sizeof(ct));
+ }
+}
Index: bgpd/session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.410
diff -u -p -r1.410 session.c
--- bgpd/session.c 5 Jan 2021 10:02:44 -0000 1.410
+++ bgpd/session.c 25 Jan 2021 18:28:02 -0000
@@ -2829,6 +2829,8 @@ session_dispatch_imsg(struct imsgbuf *ib
case IMSG_CTL_SHOW_NEXTHOP:
case IMSG_CTL_SHOW_INTERFACE:
case IMSG_CTL_SHOW_FIB_TABLES:
+ case IMSG_CTL_SHOW_RTR:
+ case IMSG_CTL_SHOW_TIMER:
if (idx != PFD_PIPE_MAIN)
fatalx("ctl kroute request not from parent");
control_imsg_relay(&imsg);
Index: bgpd/session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.149
diff -u -p -r1.149 session.h
--- bgpd/session.h 23 Dec 2020 13:20:48 -0000 1.149
+++ bgpd/session.h 25 Jan 2021 18:13:49 -0000
@@ -186,6 +186,9 @@ enum Timer {
Timer_IdleHoldReset,
Timer_CarpUndemote,
Timer_RestartTimeout,
+ Timer_Rtr_Refresh,
+ Timer_Rtr_Retry,
+ Timer_Rtr_Expire,
Timer_Max
};
@@ -281,9 +284,6 @@ void mrt_dump_state(struct mrt *, u_in
struct peer *);
void mrt_done(struct mrt *);
-/* parse.y */
-struct bgpd_config *parse_config(char *, struct peer_head *);
-
/* pfkey.c */
int pfkey_read(int, struct sadb_msg *);
int pfkey_establish(struct peer *);
@@ -299,7 +299,30 @@ void tcp_md5_del_listener(struct bgpd_co
void print_config(struct bgpd_config *, struct rib_names *);
/* rde.c */
-void rde_main(int, int);
+void rde_main(int, int);
+
+/* rtr_proto.c */
+struct rtr_session;
+size_t rtr_count(void);
+void rtr_check_events(struct pollfd *, size_t);
+size_t rtr_poll_events(struct pollfd *, size_t, time_t *);
+struct rtr_session *rtr_new(uint32_t, char *);
+struct rtr_session *rtr_get(uint32_t);
+void rtr_free(struct rtr_session *);
+void rtr_open(struct rtr_session *, int);
+struct roa_tree *rtr_get_roa(struct rtr_session *);
+void rtr_config_prep(void);
+void rtr_config_merge(void);
+void rtr_config_keep(struct rtr_session *);
+void rtr_roa_merge(struct roa_tree *);
+void rtr_shutdown(void);
+void rtr_show(struct rtr_session *, pid_t);
+
+/* rtr.c */
+void roa_insert(struct roa_tree *, struct roa *);
+void rtr_main(int, int);
+void rtr_imsg_compose(int, uint32_t, pid_t, void *, size_t);
+void rtr_recalc(void);
/* session.c */
RB_PROTOTYPE(peer_head, peer, entry, peer_compare);
Index: bgpd/util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.60
diff -u -p -r1.60 util.c
--- bgpd/util.c 25 Jan 2021 09:15:24 -0000 1.60
+++ bgpd/util.c 25 Jan 2021 16:33:19 -0000
@@ -162,6 +162,38 @@ log_reason(const char *communication) {
}
const char *
+log_rtr_error(enum rtr_error err)
+{
+ static char buf[20];
+
+ switch (err) {
+ case NO_ERROR:
+ return "No Error";
+ case CORRUPT_DATA:
+ return "Corrupt Data";
+ case INTERNAL_ERROR:
+ return "Internal Error";
+ case NO_DATA_AVAILABLE:
+ return "No Data Available";
+ case INVALID_REQUEST:
+ return "Invalid Request";
+ case UNSUPP_PROTOCOL_VERS:
+ return "Unsupported Protocol Version";
+ case UNSUPP_PDU_TYPE:
+ return "Unsupported PDU Type";
+ case UNK_REC_WDRAWL:
+ return "Withdrawl of Unknown Record";
+ case DUP_REC_RECV:
+ return "Duplicate Announcement Received";
+ case UNEXP_PROTOCOL_VERS:
+ return "Unexpected Protocol Version";
+ default:
+ snprintf(buf, sizeof(buf), "unknown %u", err);
+ return buf;
+ }
+}
+
+const char *
aspath_delim(u_int8_t seg_type, int closing)
{
static char db[8];