xref: /openbsd-src/usr.sbin/bgpctl/bgpctl.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: bgpctl.c,v 1.273 2021/08/09 08:24:36 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2004-2019 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2016 Job Snijders <job@instituut.net>
7  * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/un.h>
26 
27 #include <endian.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <math.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <util.h>
38 
39 #include "bgpd.h"
40 #include "session.h"
41 #include "rde.h"
42 #include "version.h"
43 
44 #include "bgpctl.h"
45 #include "parser.h"
46 #include "mrtparser.h"
47 
48 int		 main(int, char *[]);
49 int		 show(struct imsg *, struct parse_result *);
50 void		 send_filterset(struct imsgbuf *, struct filter_set_head *);
51 void		 show_mrt_dump_neighbors(struct mrt_rib *, struct mrt_peer *,
52 		    void *);
53 void		 show_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *);
54 void		 network_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *);
55 void		 show_mrt_state(struct mrt_bgp_state *, void *);
56 void		 show_mrt_msg(struct mrt_bgp_msg *, void *);
57 const char	*msg_type(u_int8_t);
58 void		 network_bulk(struct parse_result *);
59 int		 match_aspath(void *, u_int16_t, struct filter_as *);
60 
61 struct imsgbuf	*ibuf;
62 struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg };
63 struct mrt_parser net_mrt = { network_mrt_dump, NULL, NULL };
64 const struct output	*output = &show_output;
65 int tableid;
66 int nodescr;
67 
68 __dead void
69 usage(void)
70 {
71 	extern char	*__progname;
72 
73 	fprintf(stderr, "usage: %s [-jnV] [-s socket] command [argument ...]\n",
74 	    __progname);
75 	exit(1);
76 }
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	struct sockaddr_un	 sun;
82 	int			 fd, n, done, ch, verbose = 0;
83 	struct imsg		 imsg;
84 	struct network_config	 net;
85 	struct parse_result	*res;
86 	struct ctl_neighbor	 neighbor;
87 	struct ctl_show_rib_request	ribreq;
88 	char			*sockname;
89 	enum imsg_type		 type;
90 
91 	if (pledge("stdio rpath wpath cpath unix inet dns", NULL) == -1)
92 		err(1, "pledge");
93 
94 	tableid = getrtable();
95 	if (asprintf(&sockname, "%s.%d", SOCKET_NAME, tableid) == -1)
96 		err(1, "asprintf");
97 
98 	while ((ch = getopt(argc, argv, "jns:V")) != -1) {
99 		switch (ch) {
100 		case 'n':
101 			if (++nodescr > 1)
102 				usage();
103 			break;
104 		case 'j':
105 			output = &json_output;
106 			break;
107 		case 's':
108 			sockname = optarg;
109 			break;
110 		case 'V':
111 			fprintf(stderr, "OpenBGPD %s\n", BGPD_VERSION);
112 			return 0;
113 		default:
114 			usage();
115 			/* NOTREACHED */
116 		}
117 	}
118 	argc -= optind;
119 	argv += optind;
120 
121 	if ((res = parse(argc, argv)) == NULL)
122 		exit(1);
123 
124 	memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr));
125 	strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr));
126 	neighbor.is_group = res->is_group;
127 	strlcpy(neighbor.reason, res->reason, sizeof(neighbor.reason));
128 
129 	switch (res->action) {
130 	case SHOW_MRT:
131 		if (pledge("stdio", NULL) == -1)
132 			err(1, "pledge");
133 
134 		bzero(&ribreq, sizeof(ribreq));
135 		if (res->as.type != AS_UNDEF)
136 			ribreq.as = res->as;
137 		if (res->addr.aid) {
138 			ribreq.prefix = res->addr;
139 			ribreq.prefixlen = res->prefixlen;
140 		}
141 		/* XXX currently no communities support */
142 		ribreq.neighbor = neighbor;
143 		ribreq.aid = res->aid;
144 		ribreq.flags = res->flags;
145 		ribreq.validation_state = res->validation_state;
146 		show_mrt.arg = &ribreq;
147 		if (res->flags & F_CTL_NEIGHBORS)
148 			show_mrt.dump = show_mrt_dump_neighbors;
149 		else
150 			output->head(res);
151 		mrt_parse(res->mrtfd, &show_mrt, 1);
152 		exit(0);
153 	default:
154 		break;
155 	}
156 
157 	if (pledge("stdio unix", NULL) == -1)
158 		err(1, "pledge");
159 
160 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
161 		err(1, "control_init: socket");
162 
163 	bzero(&sun, sizeof(sun));
164 	sun.sun_family = AF_UNIX;
165 	if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
166 	    sizeof(sun.sun_path))
167 		errx(1, "socket name too long");
168 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
169 		err(1, "connect: %s", sockname);
170 
171 	if (pledge("stdio", NULL) == -1)
172 		err(1, "pledge");
173 
174 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
175 		err(1, NULL);
176 	imsg_init(ibuf, fd);
177 	done = 0;
178 
179 	switch (res->action) {
180 	case NONE:
181 	case SHOW_MRT:
182 		usage();
183 		/* NOTREACHED */
184 	case SHOW:
185 	case SHOW_SUMMARY:
186 		imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0);
187 		break;
188 	case SHOW_SUMMARY_TERSE:
189 		imsg_compose(ibuf, IMSG_CTL_SHOW_TERSE, 0, 0, -1, NULL, 0);
190 		break;
191 	case SHOW_FIB:
192 		if (!res->addr.aid) {
193 			struct ibuf	*msg;
194 			sa_family_t	 af;
195 
196 			af = aid2af(res->aid);
197 			if ((msg = imsg_create(ibuf, IMSG_CTL_KROUTE,
198 			    res->rtableid, 0, sizeof(res->flags) +
199 			    sizeof(af))) == NULL)
200 				errx(1, "imsg_create failure");
201 			if (imsg_add(msg, &res->flags, sizeof(res->flags)) ==
202 			    -1 ||
203 			    imsg_add(msg, &af, sizeof(af)) == -1)
204 				errx(1, "imsg_add failure");
205 			imsg_close(ibuf, msg);
206 		} else
207 			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, res->rtableid,
208 			    0, -1, &res->addr, sizeof(res->addr));
209 		break;
210 	case SHOW_FIB_TABLES:
211 		imsg_compose(ibuf, IMSG_CTL_SHOW_FIB_TABLES, 0, 0, -1, NULL, 0);
212 		break;
213 	case SHOW_NEXTHOP:
214 		imsg_compose(ibuf, IMSG_CTL_SHOW_NEXTHOP, res->rtableid, 0, -1,
215 		    NULL, 0);
216 		break;
217 	case SHOW_INTERFACE:
218 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, NULL, 0);
219 		break;
220 	case SHOW_SET:
221 		imsg_compose(ibuf, IMSG_CTL_SHOW_SET, 0, 0, -1, NULL, 0);
222 		break;
223 	case SHOW_RTR:
224 		imsg_compose(ibuf, IMSG_CTL_SHOW_RTR, 0, 0, -1, NULL, 0);
225 		break;
226 	case SHOW_NEIGHBOR:
227 	case SHOW_NEIGHBOR_TIMERS:
228 	case SHOW_NEIGHBOR_TERSE:
229 		neighbor.show_timers = (res->action == SHOW_NEIGHBOR_TIMERS);
230 		if (res->peeraddr.aid || res->peerdesc[0])
231 			imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
232 			    &neighbor, sizeof(neighbor));
233 		else
234 			imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
235 			    NULL, 0);
236 		break;
237 	case SHOW_RIB:
238 		bzero(&ribreq, sizeof(ribreq));
239 		type = IMSG_CTL_SHOW_RIB;
240 		if (res->addr.aid) {
241 			ribreq.prefix = res->addr;
242 			ribreq.prefixlen = res->prefixlen;
243 			type = IMSG_CTL_SHOW_RIB_PREFIX;
244 		}
245 		if (res->as.type != AS_UNDEF)
246 			ribreq.as = res->as;
247 		if (res->community.flags != 0)
248 			ribreq.community = res->community;
249 		ribreq.neighbor = neighbor;
250 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
251 		ribreq.aid = res->aid;
252 		ribreq.path_id = res->pathid;
253 		ribreq.flags = res->flags;
254 		imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq));
255 		break;
256 	case SHOW_RIB_MEM:
257 		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0);
258 		break;
259 	case RELOAD:
260 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1,
261 		    res->reason, sizeof(res->reason));
262 		if (res->reason[0])
263 			printf("reload request sent: %s\n", res->reason);
264 		else
265 			printf("reload request sent.\n");
266 		break;
267 	case FIB:
268 		errx(1, "action==FIB");
269 		break;
270 	case FIB_COUPLE:
271 		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, res->rtableid, 0, -1,
272 		    NULL, 0);
273 		printf("couple request sent.\n");
274 		done = 1;
275 		break;
276 	case FIB_DECOUPLE:
277 		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, res->rtableid, 0, -1,
278 		    NULL, 0);
279 		printf("decouple request sent.\n");
280 		done = 1;
281 		break;
282 	case NEIGHBOR:
283 		errx(1, "action==NEIGHBOR");
284 		break;
285 	case NEIGHBOR_UP:
286 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_UP, 0, 0, -1,
287 		    &neighbor, sizeof(neighbor));
288 		break;
289 	case NEIGHBOR_DOWN:
290 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_DOWN, 0, 0, -1,
291 		    &neighbor, sizeof(neighbor));
292 		break;
293 	case NEIGHBOR_CLEAR:
294 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_CLEAR, 0, 0, -1,
295 		    &neighbor, sizeof(neighbor));
296 		break;
297 	case NEIGHBOR_RREFRESH:
298 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_RREFRESH, 0, 0, -1,
299 		    &neighbor, sizeof(neighbor));
300 		break;
301 	case NEIGHBOR_DESTROY:
302 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_DESTROY, 0, 0, -1,
303 		    &neighbor, sizeof(neighbor));
304 		break;
305 	case NETWORK_BULK_ADD:
306 	case NETWORK_BULK_REMOVE:
307 		network_bulk(res);
308 		printf("requests sent.\n");
309 		done = 1;
310 		break;
311 	case NETWORK_ADD:
312 	case NETWORK_REMOVE:
313 		bzero(&net, sizeof(net));
314 		net.prefix = res->addr;
315 		net.prefixlen = res->prefixlen;
316 		net.rd = res->rd;
317 		/* attribute sets are not supported */
318 		if (res->action == NETWORK_ADD) {
319 			imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
320 			    &net, sizeof(net));
321 			send_filterset(ibuf, &res->set);
322 			imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1,
323 			    NULL, 0);
324 		} else
325 			imsg_compose(ibuf, IMSG_NETWORK_REMOVE, 0, 0, -1,
326 			    &net, sizeof(net));
327 		printf("request sent.\n");
328 		done = 1;
329 		break;
330 	case NETWORK_FLUSH:
331 		imsg_compose(ibuf, IMSG_NETWORK_FLUSH, 0, 0, -1, NULL, 0);
332 		printf("request sent.\n");
333 		done = 1;
334 		break;
335 	case NETWORK_SHOW:
336 		bzero(&ribreq, sizeof(ribreq));
337 		ribreq.aid = res->aid;
338 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
339 		imsg_compose(ibuf, IMSG_CTL_SHOW_NETWORK, 0, 0, -1,
340 		    &ribreq, sizeof(ribreq));
341 		break;
342 	case NETWORK_MRT:
343 		bzero(&ribreq, sizeof(ribreq));
344 		if (res->as.type != AS_UNDEF)
345 			ribreq.as = res->as;
346 		if (res->addr.aid) {
347 			ribreq.prefix = res->addr;
348 			ribreq.prefixlen = res->prefixlen;
349 		}
350 		/* XXX currently no community support */
351 		ribreq.neighbor = neighbor;
352 		ribreq.aid = res->aid;
353 		ribreq.flags = res->flags;
354 		net_mrt.arg = &ribreq;
355 		mrt_parse(res->mrtfd, &net_mrt, 1);
356 		done = 1;
357 		break;
358 	case LOG_VERBOSE:
359 		verbose = 1;
360 		/* FALLTHROUGH */
361 	case LOG_BRIEF:
362 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
363 		    &verbose, sizeof(verbose));
364 		printf("logging request sent.\n");
365 		done = 1;
366 		break;
367 	}
368 
369 	while (ibuf->w.queued)
370 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
371 			err(1, "write error");
372 
373 	output->head(res);
374 
375 	while (!done) {
376 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
377 			err(1, "imsg_read error");
378 		if (n == 0)
379 			errx(1, "pipe closed");
380 
381 		while (!done) {
382 			if ((n = imsg_get(ibuf, &imsg)) == -1)
383 				err(1, "imsg_get error");
384 			if (n == 0)
385 				break;
386 
387 			done = show(&imsg, res);
388 			imsg_free(&imsg);
389 		}
390 	}
391 
392 	output->tail();
393 
394 	close(fd);
395 	free(ibuf);
396 
397 	exit(0);
398 }
399 
400 int
401 show(struct imsg *imsg, struct parse_result *res)
402 {
403 	struct peer		*p;
404 	struct ctl_timer	 t;
405 	struct ctl_show_interface	*iface;
406 	struct ctl_show_nexthop	*nh;
407 	struct ctl_show_set	 set;
408 	struct ctl_show_rtr	 rtr;
409 	struct kroute_full	*kf;
410 	struct ktable		*kt;
411 	struct ctl_show_rib	 rib;
412 	struct rde_memstats	 stats;
413 	struct rde_hashstats	 hash;
414 	u_char			*asdata;
415 	u_int			 rescode, ilen;
416 	size_t			 aslen;
417 
418 	switch (imsg->hdr.type) {
419 	case IMSG_CTL_SHOW_NEIGHBOR:
420 		p = imsg->data;
421 		output->neighbor(p, res);
422 		break;
423 	case IMSG_CTL_SHOW_TIMER:
424 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(t))
425 			errx(1, "wrong imsg len");
426 		memcpy(&t, imsg->data, sizeof(t));
427 		if (t.type > 0 && t.type < Timer_Max)
428 			output->timer(&t);
429 		break;
430 	case IMSG_CTL_SHOW_INTERFACE:
431 		iface = imsg->data;
432 		output->interface(iface);
433 		break;
434 	case IMSG_CTL_SHOW_NEXTHOP:
435 		nh = imsg->data;
436 		output->nexthop(nh);
437 		break;
438 	case IMSG_CTL_KROUTE:
439 	case IMSG_CTL_SHOW_NETWORK:
440 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf))
441 			errx(1, "wrong imsg len");
442 		kf = imsg->data;
443 		output->fib(kf);
444 		break;
445 	case IMSG_CTL_SHOW_FIB_TABLES:
446 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt))
447 			errx(1, "wrong imsg len");
448 		kt = imsg->data;
449 		output->fib_table(kt);
450 		break;
451 	case IMSG_CTL_SHOW_RIB:
452 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rib))
453 			errx(1, "wrong imsg len");
454 		memcpy(&rib, imsg->data, sizeof(rib));
455 		aslen = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof(rib);
456 		asdata = imsg->data;
457 		asdata += sizeof(rib);
458 		output->rib(&rib, asdata, aslen, res);
459 		break;
460 	case IMSG_CTL_SHOW_RIB_COMMUNITIES:
461 		ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
462 		if (ilen % sizeof(struct community)) {
463 			warnx("bad IMSG_CTL_SHOW_RIB_COMMUNITIES received");
464 			break;
465 		}
466 		output->communities(imsg->data, ilen, res);
467 		break;
468 	case IMSG_CTL_SHOW_RIB_ATTR:
469 		ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
470 		if (ilen < 3) {
471 			warnx("bad IMSG_CTL_SHOW_RIB_ATTR received");
472 			break;
473 		}
474 		output->attr(imsg->data, ilen, res->flags, 0);
475 		break;
476 	case IMSG_CTL_SHOW_RIB_MEM:
477 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(stats))
478 			errx(1, "wrong imsg len");
479 		memcpy(&stats, imsg->data, sizeof(stats));
480 		output->rib_mem(&stats);
481 		break;
482 	case IMSG_CTL_SHOW_RIB_HASH:
483 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(hash))
484 			errx(1, "wrong imsg len");
485 		memcpy(&hash, imsg->data, sizeof(hash));
486 		output->rib_hash(&hash);
487 		break;
488 	case IMSG_CTL_SHOW_SET:
489 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(set))
490 			errx(1, "wrong imsg len");
491 		memcpy(&set, imsg->data, sizeof(set));
492 		output->set(&set);
493 		break;
494 	case IMSG_CTL_SHOW_RTR:
495 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rtr))
496 			errx(1, "wrong imsg len");
497 		memcpy(&rtr, imsg->data, sizeof(rtr));
498 		output->rtr(&rtr);
499 		break;
500 	case IMSG_CTL_RESULT:
501 		if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode)) {
502 			warnx("got IMSG_CTL_RESULT with wrong len");
503 			break;
504 		}
505 		memcpy(&rescode, imsg->data, sizeof(rescode));
506 		output->result(rescode);
507 		return (1);
508 	case IMSG_CTL_END:
509 		return (1);
510 	default:
511 		warnx("unknown imsg %d received", imsg->hdr.type);
512 		break;
513 	}
514 
515 	return (0);
516 }
517 
518 time_t
519 get_monotime(time_t t)
520 {
521 	struct timespec ts;
522 
523 	if (t == 0)
524 		return -1;
525 	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
526 		err(1, "clock_gettime");
527 	if (t > ts.tv_sec)	/* time in the future is not possible */
528 		t = ts.tv_sec;
529 	return (ts.tv_sec - t);
530 }
531 
532 char *
533 fmt_peer(const char *descr, const struct bgpd_addr *remote_addr,
534     int masklen)
535 {
536 	const char	*ip;
537 	char		*p;
538 
539 	if (descr && descr[0] && !nodescr) {
540 		if ((p = strdup(descr)) == NULL)
541 			err(1, NULL);
542 		return (p);
543 	}
544 
545 	ip = log_addr(remote_addr);
546 	if (masklen != -1 && ((remote_addr->aid == AID_INET && masklen != 32) ||
547 	    (remote_addr->aid == AID_INET6 && masklen != 128))) {
548 		if (asprintf(&p, "%s/%u", ip, masklen) == -1)
549 			err(1, NULL);
550 	} else {
551 		if ((p = strdup(ip)) == NULL)
552 			err(1, NULL);
553 	}
554 
555 	return (p);
556 }
557 
558 const char *
559 fmt_auth_method(enum auth_method method)
560 {
561 	switch (method) {
562 	case AUTH_MD5SIG:
563 		return ", using md5sig";
564 	case AUTH_IPSEC_MANUAL_ESP:
565 		return ", using ipsec manual esp";
566 	case AUTH_IPSEC_MANUAL_AH:
567 		return ", using ipsec manual ah";
568 	case AUTH_IPSEC_IKE_ESP:
569 		return ", using ipsec ike esp";
570 	case AUTH_IPSEC_IKE_AH:
571 		return ", using ipsec ike ah";
572 	case AUTH_NONE:	/* FALLTHROUGH */
573 	default:
574 		return "";
575 	}
576 }
577 
578 #define TF_BUFS	8
579 #define TF_LEN	9
580 
581 const char *
582 fmt_timeframe(time_t t)
583 {
584 	char		*buf;
585 	static char	 tfbuf[TF_BUFS][TF_LEN];	/* ring buffer */
586 	static int	 idx = 0;
587 	unsigned int	 sec, min, hrs, day;
588 	unsigned long long	week;
589 
590 	buf = tfbuf[idx++];
591 	if (idx == TF_BUFS)
592 		idx = 0;
593 
594 	week = t;
595 
596 	sec = week % 60;
597 	week /= 60;
598 	min = week % 60;
599 	week /= 60;
600 	hrs = week % 24;
601 	week /= 24;
602 	day = week % 7;
603 	week /= 7;
604 
605 	if (week > 0)
606 		snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs);
607 	else if (day > 0)
608 		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
609 	else
610 		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
611 
612 	return (buf);
613 }
614 
615 const char *
616 fmt_monotime(time_t t)
617 {
618 	t = get_monotime(t);
619 
620 	if (t == -1)
621 		return ("Never");
622 
623 	return (fmt_timeframe(t));
624 }
625 
626 const char *
627 fmt_fib_flags(u_int16_t flags)
628 {
629 	static char buf[8];
630 
631 	if (flags & F_DOWN)
632 		strlcpy(buf, " ", sizeof(buf));
633 	else
634 		strlcpy(buf, "*", sizeof(buf));
635 
636 	if (flags & F_BGPD_INSERTED)
637 		strlcat(buf, "B", sizeof(buf));
638 	else if (flags & F_CONNECTED)
639 		strlcat(buf, "C", sizeof(buf));
640 	else if (flags & F_STATIC)
641 		strlcat(buf, "S", sizeof(buf));
642 	else if (flags & F_DYNAMIC)
643 		strlcat(buf, "D", sizeof(buf));
644 	else
645 		strlcat(buf, " ", sizeof(buf));
646 
647 	if (flags & F_NEXTHOP)
648 		strlcat(buf, "N", sizeof(buf));
649 	else
650 		strlcat(buf, " ", sizeof(buf));
651 
652 	if (flags & F_REJECT && flags & F_BLACKHOLE)
653 		strlcat(buf, "f", sizeof(buf));
654 	else if (flags & F_REJECT)
655 		strlcat(buf, "r", sizeof(buf));
656 	else if (flags & F_BLACKHOLE)
657 		strlcat(buf, "b", sizeof(buf));
658 	else
659 		strlcat(buf, " ", sizeof(buf));
660 
661 	if (strlcat(buf, " ", sizeof(buf)) >= sizeof(buf))
662 		errx(1, "%s buffer too small", __func__);
663 
664 	return buf;
665 }
666 
667 const char *
668 fmt_origin(u_int8_t origin, int sum)
669 {
670 	switch (origin) {
671 	case ORIGIN_IGP:
672 		return (sum ? "i" : "IGP");
673 	case ORIGIN_EGP:
674 		return (sum ? "e" : "EGP");
675 	case ORIGIN_INCOMPLETE:
676 		return (sum ? "?" : "incomplete");
677 	default:
678 		return (sum ? "X" : "bad origin");
679 	}
680 }
681 
682 const char *
683 fmt_flags(u_int8_t flags, int sum)
684 {
685 	static char buf[80];
686 	char	 flagstr[5];
687 	char	*p = flagstr;
688 
689 	if (sum) {
690 		if (flags & F_PREF_INVALID)
691 			*p++ = 'E';
692 		if (flags & F_PREF_ANNOUNCE)
693 			*p++ = 'A';
694 		if (flags & F_PREF_INTERNAL)
695 			*p++ = 'I';
696 		if (flags & F_PREF_STALE)
697 			*p++ = 'S';
698 		if (flags & F_PREF_ELIGIBLE)
699 			*p++ = '*';
700 		if (flags & F_PREF_ACTIVE)
701 			*p++ = '>';
702 		*p = '\0';
703 		snprintf(buf, sizeof(buf), "%-5s", flagstr);
704 	} else {
705 		if (flags & F_PREF_INTERNAL)
706 			strlcpy(buf, "internal", sizeof(buf));
707 		else
708 			strlcpy(buf, "external", sizeof(buf));
709 
710 		if (flags & F_PREF_STALE)
711 			strlcat(buf, ", stale", sizeof(buf));
712 		if (flags & F_PREF_ELIGIBLE)
713 			strlcat(buf, ", valid", sizeof(buf));
714 		if (flags & F_PREF_ACTIVE)
715 			strlcat(buf, ", best", sizeof(buf));
716 		if (flags & F_PREF_ANNOUNCE)
717 			strlcat(buf, ", announced", sizeof(buf));
718 		if (strlen(buf) >= sizeof(buf) - 1)
719 			errx(1, "%s buffer too small", __func__);
720 	}
721 
722 	return buf;
723 }
724 
725 const char *
726 fmt_ovs(u_int8_t validation_state, int sum)
727 {
728 	switch (validation_state) {
729 	case ROA_INVALID:
730 		return (sum ? "!" : "invalid");
731 	case ROA_VALID:
732 		return (sum ? "V" : "valid");
733 	default:
734 		return (sum ? "N" : "not-found");
735 	}
736 }
737 
738 const char *
739 fmt_mem(long long num)
740 {
741 	static char	buf[16];
742 
743 	if (fmt_scaled(num, buf) == -1)
744 		snprintf(buf, sizeof(buf), "%lldB", num);
745 
746 	return (buf);
747 }
748 
749 const char *
750 fmt_errstr(u_int8_t errcode, u_int8_t subcode)
751 {
752 	static char	 errbuf[256];
753 	const char	*errstr = NULL;
754 	const char	*suberr = NULL;
755 	int		 uk = 0;
756 
757 	if (errcode == 0)	/* no error */
758 		return NULL;
759 
760 	if (errcode < sizeof(errnames)/sizeof(char *))
761 		errstr = errnames[errcode];
762 
763 	switch (errcode) {
764 	case ERR_HEADER:
765 		if (subcode < sizeof(suberr_header_names)/sizeof(char *))
766 			suberr = suberr_header_names[subcode];
767 		else
768 			uk = 1;
769 		break;
770 	case ERR_OPEN:
771 		if (subcode < sizeof(suberr_open_names)/sizeof(char *))
772 			suberr = suberr_open_names[subcode];
773 		else
774 			uk = 1;
775 		break;
776 	case ERR_UPDATE:
777 		if (subcode < sizeof(suberr_update_names)/sizeof(char *))
778 			suberr = suberr_update_names[subcode];
779 		else
780 			uk = 1;
781 		break;
782 	case ERR_HOLDTIMEREXPIRED:
783 		if (subcode != 0)
784 			uk = 1;
785 		break;
786 	case ERR_FSM:
787 		if (subcode < sizeof(suberr_fsm_names)/sizeof(char *))
788 			suberr = suberr_fsm_names[subcode];
789 		else
790 			uk = 1;
791 		break;
792 	case ERR_CEASE:
793 		if (subcode < sizeof(suberr_cease_names)/sizeof(char *))
794 			suberr = suberr_cease_names[subcode];
795 		else
796 			uk = 1;
797 		break;
798 	default:
799 		snprintf(errbuf, sizeof(errbuf),
800 		    "unknown error code %u subcode %u", errcode, subcode);
801 		return (errbuf);
802 	}
803 
804 	if (uk)
805 		snprintf(errbuf, sizeof(errbuf),
806 		    "%s, unknown subcode %u", errstr, subcode);
807 	else if (suberr == NULL)
808 		return (errstr);
809 	else
810 		snprintf(errbuf, sizeof(errbuf),
811 		    "%s, %s", errstr, suberr);
812 
813 	return (errbuf);
814 }
815 
816 const char *
817 fmt_attr(u_int8_t type, int flags)
818 {
819 #define CHECK_FLAGS(s, t, m)	\
820 	if (((s) & ~(ATTR_DEFMASK | (m))) != (t)) pflags = 1
821 
822 	static char cstr[48];
823 	int pflags = 0;
824 
825 	switch (type) {
826 	case ATTR_ORIGIN:
827 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
828 		strlcpy(cstr, "Origin", sizeof(cstr));
829 		break;
830 	case ATTR_ASPATH:
831 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
832 		strlcpy(cstr, "AS-Path", sizeof(cstr));
833 		break;
834 	case ATTR_AS4_PATH:
835 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
836 		strlcpy(cstr, "AS4-Path", sizeof(cstr));
837 		break;
838 	case ATTR_NEXTHOP:
839 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
840 		strlcpy(cstr, "Nexthop", sizeof(cstr));
841 		break;
842 	case ATTR_MED:
843 		CHECK_FLAGS(flags, ATTR_OPTIONAL, 0);
844 		strlcpy(cstr, "Med", sizeof(cstr));
845 		break;
846 	case ATTR_LOCALPREF:
847 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
848 		strlcpy(cstr, "Localpref", sizeof(cstr));
849 		break;
850 	case ATTR_ATOMIC_AGGREGATE:
851 		CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0);
852 		strlcpy(cstr, "Atomic Aggregate", sizeof(cstr));
853 		break;
854 	case ATTR_AGGREGATOR:
855 		CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL);
856 		strlcpy(cstr, "Aggregator", sizeof(cstr));
857 		break;
858 	case ATTR_AS4_AGGREGATOR:
859 		CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL);
860 		strlcpy(cstr, "AS4-Aggregator", sizeof(cstr));
861 		break;
862 	case ATTR_COMMUNITIES:
863 		CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL);
864 		strlcpy(cstr, "Communities", sizeof(cstr));
865 		break;
866 	case ATTR_ORIGINATOR_ID:
867 		CHECK_FLAGS(flags, ATTR_OPTIONAL, 0);
868 		strlcpy(cstr, "Originator Id", sizeof(cstr));
869 		break;
870 	case ATTR_CLUSTER_LIST:
871 		CHECK_FLAGS(flags, ATTR_OPTIONAL, 0);
872 		strlcpy(cstr, "Cluster Id List", sizeof(cstr));
873 		break;
874 	case ATTR_MP_REACH_NLRI:
875 		CHECK_FLAGS(flags, ATTR_OPTIONAL, 0);
876 		strlcpy(cstr, "MP Reach NLRI", sizeof(cstr));
877 		break;
878 	case ATTR_MP_UNREACH_NLRI:
879 		CHECK_FLAGS(flags, ATTR_OPTIONAL, 0);
880 		strlcpy(cstr, "MP Unreach NLRI", sizeof(cstr));
881 		break;
882 	case ATTR_EXT_COMMUNITIES:
883 		CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL);
884 		strlcpy(cstr, "Ext. Communities", sizeof(cstr));
885 		break;
886 	case ATTR_LARGE_COMMUNITIES:
887 		CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL);
888 		strlcpy(cstr, "Large Communities", sizeof(cstr));
889 		break;
890 	default:
891 		/* ignore unknown attributes */
892 		snprintf(cstr, sizeof(cstr), "Unknown Attribute #%u", type);
893 		pflags = 1;
894 		break;
895 	}
896 	if (flags != -1 && pflags) {
897 		strlcat(cstr, " flags [", sizeof(cstr));
898 		if (flags & ATTR_OPTIONAL)
899 			strlcat(cstr, "O", sizeof(cstr));
900 		if (flags & ATTR_TRANSITIVE)
901 			strlcat(cstr, "T", sizeof(cstr));
902 		if (flags & ATTR_PARTIAL)
903 			strlcat(cstr, "P", sizeof(cstr));
904 		strlcat(cstr, "]", sizeof(cstr));
905 	}
906 	return (cstr);
907 
908 #undef CHECK_FLAGS
909 }
910 
911 const char *
912 fmt_community(u_int16_t a, u_int16_t v)
913 {
914 	static char buf[12];
915 
916 	if (a == COMMUNITY_WELLKNOWN)
917 		switch (v) {
918 		case COMMUNITY_GRACEFUL_SHUTDOWN:
919 			return "GRACEFUL_SHUTDOWN";
920 		case COMMUNITY_NO_EXPORT:
921 			return "NO_EXPORT";
922 		case COMMUNITY_NO_ADVERTISE:
923 			return "NO_ADVERTISE";
924 		case COMMUNITY_NO_EXPSUBCONFED:
925 			return "NO_EXPORT_SUBCONFED";
926 		case COMMUNITY_NO_PEER:
927 			return "NO_PEER";
928 		case COMMUNITY_BLACKHOLE:
929 			return "BLACKHOLE";
930 		default:
931 			break;
932 		}
933 
934 	snprintf(buf, sizeof(buf), "%hu:%hu", a, v);
935 	return buf;
936 }
937 
938 const char *
939 fmt_large_community(u_int32_t d1, u_int32_t d2, u_int32_t d3)
940 {
941 	static char buf[33];
942 
943 	snprintf(buf, sizeof(buf), "%u:%u:%u", d1, d2, d3);
944 	return buf;
945 }
946 
947 const char *
948 fmt_ext_community(u_int8_t *data)
949 {
950 	static char	buf[32];
951 	u_int64_t	ext;
952 	struct in_addr	ip;
953 	u_int32_t	as4, u32;
954 	u_int16_t	as2, u16;
955 	u_int8_t	type, subtype;
956 
957 	type = data[0];
958 	subtype = data[1];
959 
960 	switch (type) {
961 	case EXT_COMMUNITY_TRANS_TWO_AS:
962 		memcpy(&as2, data + 2, sizeof(as2));
963 		memcpy(&u32, data + 4, sizeof(u32));
964 		snprintf(buf, sizeof(buf), "%s %s:%u",
965 		    log_ext_subtype(type, subtype),
966 		    log_as(ntohs(as2)), ntohl(u32));
967 		return buf;
968 	case EXT_COMMUNITY_TRANS_IPV4:
969 		memcpy(&ip, data + 2, sizeof(ip));
970 		memcpy(&u16, data + 6, sizeof(u16));
971 		snprintf(buf, sizeof(buf), "%s %s:%hu",
972 		    log_ext_subtype(type, subtype),
973 		    inet_ntoa(ip), ntohs(u16));
974 		return buf;
975 	case EXT_COMMUNITY_TRANS_FOUR_AS:
976 		memcpy(&as4, data + 2, sizeof(as4));
977 		memcpy(&u16, data + 6, sizeof(u16));
978 		snprintf(buf, sizeof(buf), "%s %s:%hu",
979 		    log_ext_subtype(type, subtype),
980 		    log_as(ntohl(as4)), ntohs(u16));
981 		return buf;
982 	case EXT_COMMUNITY_TRANS_OPAQUE:
983 	case EXT_COMMUNITY_TRANS_EVPN:
984 		memcpy(&ext, data, sizeof(ext));
985 		ext = be64toh(ext) & 0xffffffffffffLL;
986 		snprintf(buf, sizeof(buf), "%s 0x%llx",
987 		    log_ext_subtype(type, subtype), (unsigned long long)ext);
988 		return buf;
989 	case EXT_COMMUNITY_NON_TRANS_OPAQUE:
990 		memcpy(&ext, data, sizeof(ext));
991 		ext = be64toh(ext) & 0xffffffffffffLL;
992 		switch (ext) {
993 		case EXT_COMMUNITY_OVS_VALID:
994 			snprintf(buf, sizeof(buf), "%s valid ",
995 			    log_ext_subtype(type, subtype));
996 			return buf;
997 		case EXT_COMMUNITY_OVS_NOTFOUND:
998 			snprintf(buf, sizeof(buf), "%s not-found ",
999 			    log_ext_subtype(type, subtype));
1000 			return buf;
1001 		case EXT_COMMUNITY_OVS_INVALID:
1002 			snprintf(buf, sizeof(buf), "%s invalid ",
1003 			    log_ext_subtype(type, subtype));
1004 			return buf;
1005 		default:
1006 			snprintf(buf, sizeof(buf), "%s 0x%llx ",
1007 			    log_ext_subtype(type, subtype),
1008 			    (unsigned long long)ext);
1009 			return buf;
1010 		}
1011 		break;
1012 	default:
1013 		memcpy(&ext, data, sizeof(ext));
1014 		snprintf(buf, sizeof(buf), "%s 0x%llx",
1015 		    log_ext_subtype(type, subtype),
1016 		    (unsigned long long)be64toh(ext));
1017 		return buf;
1018 	}
1019 }
1020 
1021 const char *
1022 fmt_set_type(struct ctl_show_set *set)
1023 {
1024 	switch (set->type) {
1025 	case ROA_SET:
1026 		return "ROA";
1027 	case PREFIX_SET:
1028 		return "PREFIX";
1029 	case ORIGIN_SET:
1030 		return "ORIGIN";
1031 	case ASNUM_SET:
1032 		return "ASNUM";
1033 	default:
1034 		return "BULA";
1035 	}
1036 }
1037 
1038 void
1039 send_filterset(struct imsgbuf *i, struct filter_set_head *set)
1040 {
1041 	struct filter_set	*s;
1042 
1043 	while ((s = TAILQ_FIRST(set)) != NULL) {
1044 		imsg_compose(i, IMSG_FILTER_SET, 0, 0, -1, s,
1045 		    sizeof(struct filter_set));
1046 		TAILQ_REMOVE(set, s, entry);
1047 		free(s);
1048 	}
1049 }
1050 
1051 void
1052 network_bulk(struct parse_result *res)
1053 {
1054 	struct network_config net;
1055 	struct filter_set *s = NULL;
1056 	struct bgpd_addr h;
1057 	char *line = NULL;
1058 	size_t linesize = 0;
1059 	ssize_t linelen;
1060 	u_int8_t len;
1061 	FILE *f;
1062 
1063 	if ((f = fdopen(STDIN_FILENO, "r")) == NULL)
1064 		err(1, "Failed to open stdin\n");
1065 
1066 	while ((linelen = getline(&line, &linesize, f)) != -1) {
1067 		char *b, *buf = line;
1068 		while ((b = strsep(&buf, " \t\n")) != NULL) {
1069 			if (*b == '\0')	/* skip empty tokens */
1070 				continue;
1071 			/* Stop processing after a comment */
1072 			if (*b == '#')
1073 				break;
1074 			bzero(&net, sizeof(net));
1075 			if (parse_prefix(b, strlen(b), &h, &len) != 1)
1076 				errx(1, "bad prefix: %s", b);
1077 			net.prefix = h;
1078 			net.prefixlen = len;
1079 			net.rd = res->rd;
1080 
1081 			if (res->action == NETWORK_BULK_ADD) {
1082 				imsg_compose(ibuf, IMSG_NETWORK_ADD,
1083 				    0, 0, -1, &net, sizeof(net));
1084 				/*
1085 				 * can't use send_filterset since that
1086 				 * would free the set.
1087 				 */
1088 				TAILQ_FOREACH(s, &res->set, entry) {
1089 					imsg_compose(ibuf,
1090 					    IMSG_FILTER_SET,
1091 					    0, 0, -1, s, sizeof(*s));
1092 				}
1093 				imsg_compose(ibuf, IMSG_NETWORK_DONE,
1094 				    0, 0, -1, NULL, 0);
1095 			} else
1096 				imsg_compose(ibuf, IMSG_NETWORK_REMOVE,
1097 				     0, 0, -1, &net, sizeof(net));
1098 		}
1099 	}
1100 	free(line);
1101 	if (ferror(f))
1102 		err(1, "getline");
1103 	fclose(f);
1104 }
1105 
1106 void
1107 show_mrt_dump_neighbors(struct mrt_rib *mr, struct mrt_peer *mp, void *arg)
1108 {
1109 	struct mrt_peer_entry *p;
1110 	struct in_addr ina;
1111 	u_int16_t i;
1112 
1113 	ina.s_addr = htonl(mp->bgp_id);
1114 	printf("view: %s BGP ID: %s Number of peers: %u\n\n",
1115 	    mp->view, inet_ntoa(ina), mp->npeers);
1116 	printf("%-30s %8s %15s\n", "Neighbor", "AS", "BGP ID");
1117 	for (i = 0; i < mp->npeers; i++) {
1118 		p = &mp->peers[i];
1119 		ina.s_addr = htonl(p->bgp_id);
1120 		printf("%-30s %8u %15s\n", log_addr(&p->addr), p->asnum,
1121 		    inet_ntoa(ina));
1122 	}
1123 	/* we only print the first message */
1124 	exit(0);
1125 }
1126 
1127 void
1128 show_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg)
1129 {
1130 	struct ctl_show_rib		 ctl;
1131 	struct parse_result		 res;
1132 	struct ctl_show_rib_request	*req = arg;
1133 	struct mrt_rib_entry		*mre;
1134 	time_t				 now;
1135 	u_int16_t			 i, j;
1136 
1137 	memset(&res, 0, sizeof(res));
1138 	res.flags = req->flags;
1139 	now = time(NULL);
1140 
1141 	for (i = 0; i < mr->nentries; i++) {
1142 		mre = &mr->entries[i];
1143 		bzero(&ctl, sizeof(ctl));
1144 		ctl.prefix = mr->prefix;
1145 		ctl.prefixlen = mr->prefixlen;
1146 		if (mre->originated <= now)
1147 			ctl.age = now - mre->originated;
1148 		ctl.true_nexthop = mre->nexthop;
1149 		ctl.exit_nexthop = mre->nexthop;
1150 		ctl.origin = mre->origin;
1151 		ctl.local_pref = mre->local_pref;
1152 		ctl.med = mre->med;
1153 		/* weight is not part of the mrt dump so it can't be set */
1154 		if (mr->add_path) {
1155 			ctl.flags |= F_PREF_PATH_ID;
1156 			ctl.path_id = mre->path_id;
1157 		}
1158 
1159 		if (mre->peer_idx < mp->npeers) {
1160 			ctl.remote_addr = mp->peers[mre->peer_idx].addr;
1161 			ctl.remote_id = mp->peers[mre->peer_idx].bgp_id;
1162 		}
1163 
1164 		/* filter by neighbor */
1165 		if (req->neighbor.addr.aid != AID_UNSPEC &&
1166 		    memcmp(&req->neighbor.addr, &ctl.remote_addr,
1167 		    sizeof(ctl.remote_addr)) != 0)
1168 			continue;
1169 		/* filter by AF */
1170 		if (req->aid && req->aid != ctl.prefix.aid)
1171 			return;
1172 		/* filter by prefix */
1173 		if (req->prefix.aid != AID_UNSPEC) {
1174 			if (req->flags & F_LONGER) {
1175 				if (req->prefixlen > ctl.prefixlen)
1176 					return;
1177 				if (prefix_compare(&req->prefix, &ctl.prefix,
1178 				    req->prefixlen))
1179 					return;
1180 			} else if (req->flags & F_SHORTER) {
1181 				if (req->prefixlen < ctl.prefixlen)
1182 					return;
1183 				if (prefix_compare(&req->prefix, &ctl.prefix,
1184 				    ctl.prefixlen))
1185 					return;
1186 			} else {
1187 				if (req->prefixlen != ctl.prefixlen)
1188 					return;
1189 				if (prefix_compare(&req->prefix, &ctl.prefix,
1190 				    req->prefixlen))
1191 					return;
1192 			}
1193 		}
1194 		/* filter by AS */
1195 		if (req->as.type != AS_UNDEF &&
1196 		    !match_aspath(mre->aspath, mre->aspath_len, &req->as))
1197 			continue;
1198 
1199 		output->rib(&ctl, mre->aspath, mre->aspath_len, &res);
1200 		if (req->flags & F_CTL_DETAIL) {
1201 			for (j = 0; j < mre->nattrs; j++)
1202 				output->attr(mre->attrs[j].attr,
1203 				    mre->attrs[j].attr_len, req->flags, 0);
1204 		}
1205 	}
1206 }
1207 
1208 void
1209 network_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg)
1210 {
1211 	struct ctl_show_rib		 ctl;
1212 	struct network_config		 net;
1213 	struct ctl_show_rib_request	*req = arg;
1214 	struct mrt_rib_entry		*mre;
1215 	struct ibuf			*msg;
1216 	time_t				 now;
1217 	u_int16_t			 i, j;
1218 
1219 	/* can't announce more than one path so ignore add-path */
1220 	if (mr->add_path)
1221 		return;
1222 
1223 	now = time(NULL);
1224 	for (i = 0; i < mr->nentries; i++) {
1225 		mre = &mr->entries[i];
1226 		bzero(&ctl, sizeof(ctl));
1227 		ctl.prefix = mr->prefix;
1228 		ctl.prefixlen = mr->prefixlen;
1229 		if (mre->originated <= now)
1230 			ctl.age = now - mre->originated;
1231 		ctl.true_nexthop = mre->nexthop;
1232 		ctl.exit_nexthop = mre->nexthop;
1233 		ctl.origin = mre->origin;
1234 		ctl.local_pref = mre->local_pref;
1235 		ctl.med = mre->med;
1236 
1237 		if (mre->peer_idx < mp->npeers) {
1238 			ctl.remote_addr = mp->peers[mre->peer_idx].addr;
1239 			ctl.remote_id = mp->peers[mre->peer_idx].bgp_id;
1240 		}
1241 
1242 		/* filter by neighbor */
1243 		if (req->neighbor.addr.aid != AID_UNSPEC &&
1244 		    memcmp(&req->neighbor.addr, &ctl.remote_addr,
1245 		    sizeof(ctl.remote_addr)) != 0)
1246 			continue;
1247 		/* filter by AF */
1248 		if (req->aid && req->aid != ctl.prefix.aid)
1249 			return;
1250 		/* filter by prefix */
1251 		if (req->prefix.aid != AID_UNSPEC) {
1252 			if (!prefix_compare(&req->prefix, &ctl.prefix,
1253 			    req->prefixlen)) {
1254 				if (req->flags & F_LONGER) {
1255 					if (req->prefixlen > ctl.prefixlen)
1256 						return;
1257 				} else if (req->prefixlen != ctl.prefixlen)
1258 					return;
1259 			} else
1260 				return;
1261 		}
1262 		/* filter by AS */
1263 		if (req->as.type != AS_UNDEF &&
1264 		    !match_aspath(mre->aspath, mre->aspath_len, &req->as))
1265 			continue;
1266 
1267 		bzero(&net, sizeof(net));
1268 		net.prefix = ctl.prefix;
1269 		net.prefixlen = ctl.prefixlen;
1270 		net.type = NETWORK_MRTCLONE;
1271 		/* XXX rd can't be set and will be 0 */
1272 
1273 		imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
1274 		    &net, sizeof(net));
1275 		if ((msg = imsg_create(ibuf, IMSG_NETWORK_ASPATH,
1276 		    0, 0, sizeof(ctl) + mre->aspath_len)) == NULL)
1277 			errx(1, "imsg_create failure");
1278 		if (imsg_add(msg, &ctl, sizeof(ctl)) == -1 ||
1279 		    imsg_add(msg, mre->aspath, mre->aspath_len) == -1)
1280 			errx(1, "imsg_add failure");
1281 		imsg_close(ibuf, msg);
1282 		for (j = 0; j < mre->nattrs; j++)
1283 			imsg_compose(ibuf, IMSG_NETWORK_ATTR, 0, 0, -1,
1284 			    mre->attrs[j].attr, mre->attrs[j].attr_len);
1285 		imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0);
1286 
1287 		while (ibuf->w.queued) {
1288 			if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
1289 				err(1, "write error");
1290 		}
1291 	}
1292 }
1293 
1294 static const char *
1295 fmt_time(struct timespec *t)
1296 {
1297 	static char timebuf[32];
1298 	static struct timespec prevtime;
1299 	struct timespec temp;
1300 
1301 	timespecsub(t, &prevtime, &temp);
1302 	snprintf(timebuf, sizeof(timebuf), "%lld.%06ld",
1303 	    (long long)temp.tv_sec, temp.tv_nsec / 1000);
1304 	prevtime = *t;
1305 	return (timebuf);
1306 }
1307 
1308 void
1309 show_mrt_state(struct mrt_bgp_state *ms, void *arg)
1310 {
1311 	printf("%s %s[%u] -> ", fmt_time(&ms->time),
1312 	    log_addr(&ms->src), ms->src_as);
1313 	printf("%s[%u]: %s -> %s\n", log_addr(&ms->dst), ms->dst_as,
1314 	    statenames[ms->old_state], statenames[ms->new_state]);
1315 }
1316 
1317 static void
1318 print_afi(u_char *p, u_int8_t len)
1319 {
1320 	u_int16_t afi;
1321 	u_int8_t safi, aid;
1322 
1323 	if (len != 4) {
1324 		printf("bad length");
1325 		return;
1326 	}
1327 
1328 	/* afi, 2 byte */
1329 	memcpy(&afi, p, sizeof(afi));
1330 	afi = ntohs(afi);
1331 	p += 2;
1332 	/* reserved, 1 byte */
1333 	p += 1;
1334 	/* safi, 1 byte */
1335 	memcpy(&safi, p, sizeof(safi));
1336 	if (afi2aid(afi, safi, &aid) == -1)
1337 		printf("unkown afi %u safi %u", afi, safi);
1338 	else
1339 		printf("%s", aid2str(aid));
1340 }
1341 
1342 static void
1343 print_capability(u_int8_t capa_code, u_char *p, u_int8_t len)
1344 {
1345 	switch (capa_code) {
1346 	case CAPA_MP:
1347 		printf("multiprotocol capability: ");
1348 		print_afi(p, len);
1349 		break;
1350 	case CAPA_REFRESH:
1351 		printf("route refresh capability");
1352 		break;
1353 	case CAPA_RESTART:
1354 		printf("graceful restart capability");
1355 		/* XXX there is more needed here */
1356 		break;
1357 	case CAPA_AS4BYTE:
1358 		printf("4-byte AS num capability: ");
1359 		if (len == 4) {
1360 			u_int32_t as;
1361 			memcpy(&as, p, sizeof(as));
1362 			as = ntohl(as);
1363 			printf("AS %u", as);
1364 		} else
1365 			printf("bad length");
1366 		break;
1367 	case CAPA_ADD_PATH:
1368 		printf("add-path capability");
1369 		/* XXX there is more needed here */
1370 		break;
1371 	case CAPA_ENHANCED_RR:
1372 		printf("enhanced route refresh capability");
1373 		break;
1374 	default:
1375 		printf("unknown capability %u length %u", capa_code, len);
1376 		break;
1377 	}
1378 }
1379 
1380 static void
1381 print_notification(u_int8_t errcode, u_int8_t subcode)
1382 {
1383 	const char *suberrname = NULL;
1384 	int uk = 0;
1385 
1386 	switch (errcode) {
1387 	case ERR_HEADER:
1388 		if (subcode >= sizeof(suberr_header_names)/sizeof(char *))
1389 			uk = 1;
1390 		else
1391 			suberrname = suberr_header_names[subcode];
1392 		break;
1393 	case ERR_OPEN:
1394 		if (subcode >= sizeof(suberr_open_names)/sizeof(char *))
1395 			uk = 1;
1396 		else
1397 			suberrname = suberr_open_names[subcode];
1398 		break;
1399 	case ERR_UPDATE:
1400 		if (subcode >= sizeof(suberr_update_names)/sizeof(char *))
1401 			uk = 1;
1402 		else
1403 			suberrname = suberr_update_names[subcode];
1404 		break;
1405 	case ERR_CEASE:
1406 		if (subcode >= sizeof(suberr_cease_names)/sizeof(char *))
1407 			uk = 1;
1408 		else
1409 			suberrname = suberr_cease_names[subcode];
1410 		break;
1411 	case ERR_HOLDTIMEREXPIRED:
1412 		if (subcode != 0)
1413 			uk = 1;
1414 		break;
1415 	case ERR_FSM:
1416 		if (subcode >= sizeof(suberr_fsm_names)/sizeof(char *))
1417 			uk = 1;
1418 		else
1419 			suberrname = suberr_fsm_names[subcode];
1420 		break;
1421 	default:
1422 		printf("unknown errcode %u, subcode %u",
1423 		    errcode, subcode);
1424 		return;
1425 	}
1426 
1427 	if (uk)
1428 		printf("%s, unknown subcode %u", errnames[errcode], subcode);
1429 	else {
1430 		if (suberrname == NULL)
1431 			printf("%s", errnames[errcode]);
1432 		else
1433 			printf("%s, %s", errnames[errcode], suberrname);
1434 	}
1435 }
1436 
1437 static int
1438 show_mrt_capabilities(u_char *p, u_int16_t len)
1439 {
1440 	u_int16_t totlen = len;
1441 	u_int8_t capa_code, capa_len;
1442 
1443 	while (len > 2) {
1444 		memcpy(&capa_code, p, sizeof(capa_code));
1445 		p += sizeof(capa_code);
1446 		len -= sizeof(capa_code);
1447 		memcpy(&capa_len, p, sizeof(capa_len));
1448 		p += sizeof(capa_len);
1449 		len -= sizeof(capa_len);
1450 		if (len < capa_len) {
1451 			printf("capa_len %u exceeds remaining length",
1452 			    capa_len);
1453 			return (-1);
1454 		}
1455 		printf("\n        ");
1456 		print_capability(capa_code, p, capa_len);
1457 		p += capa_len;
1458 		len -= capa_len;
1459 	}
1460 	if (len != 0) {
1461 		printf("length missmatch while capability parsing");
1462 		return (-1);
1463 	}
1464 	return (totlen);
1465 }
1466 
1467 static void
1468 show_mrt_open(u_char *p, u_int16_t len)
1469 {
1470 	u_int8_t version, optparamlen;
1471 	u_int16_t short_as, holdtime;
1472 	struct in_addr bgpid;
1473 
1474 	/* length check up to optparamlen already happened */
1475 	memcpy(&version, p, sizeof(version));
1476 	p += sizeof(version);
1477 	len -= sizeof(version);
1478 	memcpy(&short_as, p, sizeof(short_as));
1479 	p += sizeof(short_as);
1480 	len -= sizeof(short_as);
1481 	short_as = ntohs(short_as);
1482 	memcpy(&holdtime, p, sizeof(holdtime));
1483 	holdtime = ntohs(holdtime);
1484 	p += sizeof(holdtime);
1485 	len -= sizeof(holdtime);
1486 	memcpy(&bgpid, p, sizeof(bgpid));
1487 	p += sizeof(bgpid);
1488 	len -= sizeof(bgpid);
1489 	memcpy(&optparamlen, p, sizeof(optparamlen));
1490 	p += sizeof(optparamlen);
1491 	len -= sizeof(optparamlen);
1492 
1493 	printf("\n    ");
1494 	printf("Version: %d AS: %u Holdtime: %u BGP Id: %s Paramlen: %u",
1495 	    version, short_as, holdtime, inet_ntoa(bgpid), optparamlen);
1496 	if (optparamlen != len) {
1497 		printf("optional parameter length mismatch");
1498 		return;
1499 	}
1500 	while (len > 2) {
1501 		u_int8_t op_type, op_len;
1502 		int r;
1503 
1504 		memcpy(&op_type, p, sizeof(op_type));
1505 		p += sizeof(op_type);
1506 		len -= sizeof(op_type);
1507 		memcpy(&op_len, p, sizeof(op_len));
1508 		p += sizeof(op_len);
1509 		len -= sizeof(op_len);
1510 
1511 		printf("\n    ");
1512 		switch (op_type) {
1513 		case OPT_PARAM_CAPABILITIES:
1514 			printf("Capabilities: size %u", op_len);
1515 			r = show_mrt_capabilities(p, op_len);
1516 			if (r == -1)
1517 				return;
1518 			p += r;
1519 			len -= r;
1520 			break;
1521 		case OPT_PARAM_AUTH:
1522 		default:
1523 			printf("unsupported optional parameter: type %u",
1524 			    op_type);
1525 			return;
1526 		}
1527 	}
1528 	if (len != 0) {
1529 		printf("optional parameter encoding error");
1530 		return;
1531 	}
1532 }
1533 
1534 static void
1535 show_mrt_notification(u_char *p, u_int16_t len)
1536 {
1537 	u_int16_t i;
1538 	u_int8_t errcode, subcode;
1539 	size_t reason_len;
1540 	char reason[REASON_LEN];
1541 
1542 	memcpy(&errcode, p, sizeof(errcode));
1543 	p += sizeof(errcode);
1544 	len -= sizeof(errcode);
1545 
1546 	memcpy(&subcode, p, sizeof(subcode));
1547 	p += sizeof(subcode);
1548 	len -= sizeof(subcode);
1549 
1550 	printf("\n    ");
1551 	print_notification(errcode, subcode);
1552 
1553 	if (errcode == ERR_CEASE && (subcode == ERR_CEASE_ADMIN_DOWN ||
1554 	    subcode == ERR_CEASE_ADMIN_RESET)) {
1555 		if (len > 1) {
1556 			reason_len = *p++;
1557 			len--;
1558 			if (len < reason_len) {
1559 				printf("truncated shutdown reason");
1560 				return;
1561 			}
1562 			if (reason_len > REASON_LEN - 1) {
1563 				printf("overly long shutdown reason");
1564 				return;
1565 			}
1566 			memcpy(reason, p, reason_len);
1567 			reason[reason_len] = '\0';
1568 			printf("shutdown reason: \"%s\"",
1569 			    log_reason(reason));
1570 			p += reason_len;
1571 			len -= reason_len;
1572 		}
1573 	}
1574 	if (errcode == ERR_OPEN && subcode == ERR_OPEN_CAPA) {
1575 		int r;
1576 
1577 		r = show_mrt_capabilities(p, len);
1578 		if (r == -1)
1579 			return;
1580 		p += r;
1581 		len -= r;
1582 	}
1583 
1584 	if (len > 0) {
1585 		printf("\n    additional data %u bytes", len);
1586 		for (i = 0; i < len; i++) {
1587 			if (i % 16 == 0)
1588 				printf("\n    ");
1589 			if (i % 8 == 0)
1590 				printf("   ");
1591 			printf(" %02X", *p++);
1592 		}
1593 	}
1594 }
1595 
1596 /* XXX this function does not handle JSON output */
1597 static void
1598 show_mrt_update(u_char *p, u_int16_t len, int reqflags, int addpath)
1599 {
1600 	struct bgpd_addr prefix;
1601 	int pos;
1602 	u_int32_t pathid;
1603 	u_int16_t wlen, alen;
1604 	u_int8_t prefixlen;
1605 
1606 	if (len < sizeof(wlen)) {
1607 		printf("bad length");
1608 		return;
1609 	}
1610 	memcpy(&wlen, p, sizeof(wlen));
1611 	wlen = ntohs(wlen);
1612 	p += sizeof(wlen);
1613 	len -= sizeof(wlen);
1614 
1615 	if (len < wlen) {
1616 		printf("bad withdraw length");
1617 		return;
1618 	}
1619 	if (wlen > 0) {
1620 		printf("\n     Withdrawn prefixes:");
1621 		while (wlen > 0) {
1622 			if (addpath) {
1623 				if (wlen <= sizeof(pathid)) {
1624 					printf("bad withdraw prefix");
1625 					return;
1626 				}
1627 				memcpy(&pathid, p, sizeof(pathid));
1628 				pathid = ntohl(pathid);
1629 				p += sizeof(pathid);
1630 				len -= sizeof(pathid);
1631 				wlen -= sizeof(pathid);
1632 			}
1633 			if ((pos = nlri_get_prefix(p, wlen, &prefix,
1634 			    &prefixlen)) == -1) {
1635 				printf("bad withdraw prefix");
1636 				return;
1637 			}
1638 			printf(" %s/%u", log_addr(&prefix), prefixlen);
1639 			if (addpath)
1640 				printf(" path-id %u", pathid);
1641 			p += pos;
1642 			len -= pos;
1643 			wlen -= pos;
1644 		}
1645 	}
1646 
1647 	if (len < sizeof(alen)) {
1648 		printf("bad length");
1649 		return;
1650 	}
1651 	memcpy(&alen, p, sizeof(alen));
1652 	alen = ntohs(alen);
1653 	p += sizeof(alen);
1654 	len -= sizeof(alen);
1655 
1656 	if (len < alen) {
1657 		printf("bad attribute length");
1658 		return;
1659 	}
1660 	printf("\n");
1661 	/* alen attributes here */
1662 	while (alen > 3) {
1663 		u_int8_t flags;
1664 		u_int16_t attrlen;
1665 
1666 		flags = p[0];
1667 		/* type = p[1]; */
1668 
1669 		/* get the attribute length */
1670 		if (flags & ATTR_EXTLEN) {
1671 			if (len < sizeof(attrlen) + 2)
1672 				printf("bad attribute length");
1673 			memcpy(&attrlen, &p[2], sizeof(attrlen));
1674 			attrlen = ntohs(attrlen);
1675 			attrlen += sizeof(attrlen) + 2;
1676 		} else {
1677 			attrlen = p[2];
1678 			attrlen += 1 + 2;
1679 		}
1680 
1681 		output->attr(p, attrlen, reqflags, addpath);
1682 		p += attrlen;
1683 		alen -= attrlen;
1684 		len -= attrlen;
1685 	}
1686 
1687 	if (len > 0) {
1688 		printf("    NLRI prefixes:");
1689 		while (len > 0) {
1690 			if (addpath) {
1691 				if (len <= sizeof(pathid)) {
1692 					printf(" bad nlri prefix: pathid, len %d", len);
1693 					return;
1694 				}
1695 				memcpy(&pathid, p, sizeof(pathid));
1696 				pathid = ntohl(pathid);
1697 				p += sizeof(pathid);
1698 				len -= sizeof(pathid);
1699 			}
1700 			if ((pos = nlri_get_prefix(p, len, &prefix,
1701 			    &prefixlen)) == -1) {
1702 				printf(" bad nlri prefix");
1703 				return;
1704 			}
1705 			printf(" %s/%u", log_addr(&prefix), prefixlen);
1706 			if (addpath)
1707 				printf(" path-id %u", pathid);
1708 			p += pos;
1709 			len -= pos;
1710 		}
1711 	}
1712 }
1713 
1714 void
1715 show_mrt_msg(struct mrt_bgp_msg *mm, void *arg)
1716 {
1717 	static const u_int8_t marker[MSGSIZE_HEADER_MARKER] = {
1718 	    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1719 	    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
1720 	u_char *p;
1721 	u_int16_t len;
1722 	u_int8_t type;
1723 	struct ctl_show_rib_request *req = arg;
1724 
1725 	printf("%s %s[%u] -> ", fmt_time(&mm->time),
1726 	    log_addr(&mm->src), mm->src_as);
1727 	printf("%s[%u]: size %u%s ", log_addr(&mm->dst), mm->dst_as,
1728 	    mm->msg_len, mm->add_path ? " addpath" : "");
1729 	p = mm->msg;
1730 	len = mm->msg_len;
1731 
1732 	if (len < MSGSIZE_HEADER) {
1733 		printf("illegal header length: %u byte\n", len);
1734 		return;
1735 	}
1736 
1737 	/* parse BGP message header */
1738 	if (memcmp(p, marker, sizeof(marker))) {
1739 		printf("incorrect marker in BGP message\n");
1740 		return;
1741 	}
1742 	p += MSGSIZE_HEADER_MARKER;
1743 
1744 	memcpy(&len, p, 2);
1745 	len = ntohs(len);
1746 	p += 2;
1747 	memcpy(&type, p, 1);
1748 	p += 1;
1749 
1750 	if (len < MSGSIZE_HEADER || len > MAX_PKTSIZE) {
1751 		printf("illegal header length: %u byte\n", len);
1752 		return;
1753 	}
1754 
1755 	switch (type) {
1756 	case OPEN:
1757 		printf("%s ", msgtypenames[type]);
1758 		if (len < MSGSIZE_OPEN_MIN) {
1759 			printf("illegal length: %u byte\n", len);
1760 			return;
1761 		}
1762 		show_mrt_open(p, len - MSGSIZE_HEADER);
1763 		break;
1764 	case NOTIFICATION:
1765 		printf("%s ", msgtypenames[type]);
1766 		if (len < MSGSIZE_NOTIFICATION_MIN) {
1767 			printf("illegal length: %u byte\n", len);
1768 			return;
1769 		}
1770 		show_mrt_notification(p, len - MSGSIZE_HEADER);
1771 		break;
1772 	case UPDATE:
1773 		printf("%s ", msgtypenames[type]);
1774 		if (len < MSGSIZE_UPDATE_MIN) {
1775 			printf("illegal length: %u byte\n", len);
1776 			return;
1777 		}
1778 		show_mrt_update(p, len - MSGSIZE_HEADER, req->flags,
1779 		    mm->add_path);
1780 		break;
1781 	case KEEPALIVE:
1782 		printf("%s ", msgtypenames[type]);
1783 		if (len != MSGSIZE_KEEPALIVE) {
1784 			printf("illegal length: %u byte\n", len);
1785 			return;
1786 		}
1787 		/* nothing */
1788 		break;
1789 	case RREFRESH:
1790 		printf("%s ", msgtypenames[type]);
1791 		if (len != MSGSIZE_RREFRESH) {
1792 			printf("illegal length: %u byte\n", len);
1793 			return;
1794 		}
1795 		print_afi(p, len);
1796 		break;
1797 	default:
1798 		printf("unknown type %u\n", type);
1799 		return;
1800 	}
1801 	printf("\n");
1802 }
1803 
1804 const char *
1805 msg_type(u_int8_t type)
1806 {
1807 	if (type >= sizeof(msgtypenames)/sizeof(msgtypenames[0]))
1808 		return "BAD";
1809 	return (msgtypenames[type]);
1810 }
1811 
1812 int
1813 match_aspath(void *data, u_int16_t len, struct filter_as *f)
1814 {
1815 	u_int8_t	*seg;
1816 	int		 final;
1817 	u_int16_t	 seg_size;
1818 	u_int8_t	 i, seg_len;
1819 	u_int32_t	 as = 0;
1820 
1821 	if (f->type == AS_EMPTY) {
1822 		if (len == 0)
1823 			return (1);
1824 		else
1825 			return (0);
1826 	}
1827 
1828 	seg = data;
1829 
1830 	/* just check the leftmost AS */
1831 	if (f->type == AS_PEER && len >= 6) {
1832 		as = aspath_extract(seg, 0);
1833 		if (f->as_min == as)
1834 			return (1);
1835 		else
1836 			return (0);
1837 	}
1838 
1839 	for (; len >= 6; len -= seg_size, seg += seg_size) {
1840 		seg_len = seg[1];
1841 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
1842 
1843 		final = (len == seg_size);
1844 
1845 		if (f->type == AS_SOURCE) {
1846 			/*
1847 			 * Just extract the rightmost AS
1848 			 * but if that segment is an AS_SET then the rightmost
1849 			 * AS of a previous AS_SEQUENCE segment should be used.
1850 			 * Because of that just look at AS_SEQUENCE segments.
1851 			 */
1852 			if (seg[0] == AS_SEQUENCE)
1853 				as = aspath_extract(seg, seg_len - 1);
1854 			/* not yet in the final segment */
1855 			if (!final)
1856 				continue;
1857 			if (f->as_min == as)
1858 				return (1);
1859 			else
1860 				return (0);
1861 		}
1862 		/* AS_TRANSIT or AS_ALL */
1863 		for (i = 0; i < seg_len; i++) {
1864 			/*
1865 			 * the source (rightmost) AS is excluded from
1866 			 * AS_TRANSIT matches.
1867 			 */
1868 			if (final && i == seg_len - 1 && f->type == AS_TRANSIT)
1869 				return (0);
1870 			as = aspath_extract(seg, i);
1871 			if (f->as_min == as)
1872 				return (1);
1873 		}
1874 	}
1875 	return (0);
1876 }
1877