xref: /openbsd-src/usr.sbin/bgpctl/bgpctl.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: bgpctl.c,v 1.161 2010/09/02 14:03:21 sobrado Exp $ */
2 
3 /*
4  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <net/if.h>
23 #include <net/if_media.h>
24 #include <net/if_types.h>
25 
26 #include <err.h>
27 #include <netdb.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <util.h>
33 
34 #include "bgpd.h"
35 #include "session.h"
36 #include "rde.h"
37 #include "log.h"
38 #include "parser.h"
39 #include "irrfilter.h"
40 
41 enum neighbor_views {
42 	NV_DEFAULT,
43 	NV_TIMERS
44 };
45 
46 int		 main(int, char *[]);
47 char		*fmt_peer(const char *, const struct bgpd_addr *, int, int);
48 void		 show_summary_head(void);
49 int		 show_summary_msg(struct imsg *, int);
50 int		 show_summary_terse_msg(struct imsg *, int);
51 int		 show_neighbor_terse(struct imsg *);
52 int		 show_neighbor_msg(struct imsg *, enum neighbor_views);
53 void		 print_neighbor_capa_mp(struct peer *);
54 void		 print_neighbor_msgstats(struct peer *);
55 void		 print_timer(const char *, time_t);
56 static char	*fmt_timeframe(time_t t);
57 static char	*fmt_timeframe_core(time_t t);
58 void		 show_fib_head(void);
59 void		 show_fib_tables_head(void);
60 void		 show_network_head(void);
61 void		 show_fib_flags(u_int16_t);
62 int		 show_fib_msg(struct imsg *);
63 void		 show_nexthop_head(void);
64 int		 show_nexthop_msg(struct imsg *);
65 void		 show_interface_head(void);
66 int		 ift2ifm(int);
67 const char *	 get_media_descr(int);
68 const char *	 get_linkstate(int, int);
69 const char *	 get_baudrate(u_int64_t, char *);
70 int		 show_interface_msg(struct imsg *);
71 void		 show_rib_summary_head(void);
72 void		 print_prefix(struct bgpd_addr *, u_int8_t, u_int8_t);
73 const char *	 print_origin(u_int8_t, int);
74 void		 print_flags(u_int8_t, int);
75 int		 show_rib_summary_msg(struct imsg *);
76 int		 show_rib_detail_msg(struct imsg *, int);
77 void		 show_community(u_char *, u_int16_t);
78 void		 show_ext_community(u_char *, u_int16_t);
79 char		*fmt_mem(int64_t);
80 int		 show_rib_memory_msg(struct imsg *);
81 void		 send_filterset(struct imsgbuf *, struct filter_set_head *);
82 static const char	*get_errstr(u_int8_t, u_int8_t);
83 int		 show_result(struct imsg *);
84 
85 struct imsgbuf	*ibuf;
86 
87 __dead void
88 usage(void)
89 {
90 	extern char	*__progname;
91 
92 	fprintf(stderr, "usage: %s [-n] [-s socket] command [argument ...]\n",
93 	    __progname);
94 	exit(1);
95 }
96 
97 int
98 main(int argc, char *argv[])
99 {
100 	struct sockaddr_un	 sun;
101 	int			 fd, n, done, ch, nodescr = 0, verbose = 0;
102 	struct imsg		 imsg;
103 	struct network_config	 net;
104 	struct parse_result	*res;
105 	struct ctl_neighbor	 neighbor;
106 	struct ctl_show_rib_request	ribreq;
107 	char			*sockname;
108 	enum imsg_type		 type;
109 
110 	sockname = SOCKET_NAME;
111 	while ((ch = getopt(argc, argv, "ns:")) != -1) {
112 		switch (ch) {
113 		case 'n':
114 			if (++nodescr > 1)
115 				usage();
116 			break;
117 		case 's':
118 			sockname = optarg;
119 			break;
120 		default:
121 			usage();
122 			/* NOTREACHED */
123 		}
124 	}
125 	argc -= optind;
126 	argv += optind;
127 
128 	if ((res = parse(argc, argv)) == NULL)
129 		exit(1);
130 
131 	if (res->action == IRRFILTER) {
132 		if (!(res->flags & (F_IPV4|F_IPV6)))
133 			res->flags |= (F_IPV4|F_IPV6);
134 		irr_main(res->as.as, res->flags, res->irr_outdir);
135 	}
136 
137 	memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr));
138 	strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr));
139 
140 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
141 		err(1, "control_init: socket");
142 
143 	bzero(&sun, sizeof(sun));
144 	sun.sun_family = AF_UNIX;
145 	if (strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path)) >=
146 	    sizeof(sun.sun_path))
147 		errx(1, "socket name too long");
148 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
149 		err(1, "connect: %s", sockname);
150 
151 	if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
152 		err(1, NULL);
153 	imsg_init(ibuf, fd);
154 	done = 0;
155 
156 	switch (res->action) {
157 	case NONE:
158 	case IRRFILTER:
159 		usage();
160 		/* not reached */
161 	case SHOW:
162 	case SHOW_SUMMARY:
163 		imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, NULL, 0);
164 		show_summary_head();
165 		break;
166 	case SHOW_SUMMARY_TERSE:
167 		imsg_compose(ibuf, IMSG_CTL_SHOW_TERSE, 0, 0, -1, NULL, 0);
168 		break;
169 	case SHOW_FIB:
170 		if (!res->addr.aid) {
171 			struct ibuf	*msg;
172 			sa_family_t	 af;
173 
174 			af = aid2af(res->aid);
175 			if ((msg = imsg_create(ibuf, IMSG_CTL_KROUTE,
176 			    res->rtableid, 0, sizeof(res->flags) +
177 			    sizeof(af))) == NULL)
178 				errx(1, "imsg_create failure");
179 			if (imsg_add(msg, &res->flags, sizeof(res->flags)) ==
180 			    -1 ||
181 			    imsg_add(msg, &af, sizeof(af)) == -1)
182 				errx(1, "imsg_add failure");
183 			imsg_close(ibuf, msg);
184 		} else
185 			imsg_compose(ibuf, IMSG_CTL_KROUTE_ADDR, res->rtableid,
186 			    0, -1, &res->addr, sizeof(res->addr));
187 		show_fib_head();
188 		break;
189 	case SHOW_FIB_TABLES:
190 		imsg_compose(ibuf, IMSG_CTL_SHOW_FIB_TABLES, 0, 0, -1, NULL, 0);
191 		show_fib_tables_head();
192 		break;
193 	case SHOW_NEXTHOP:
194 		imsg_compose(ibuf, IMSG_CTL_SHOW_NEXTHOP, res->rtableid, 0, -1,
195 		    NULL, 0);
196 		show_nexthop_head();
197 		break;
198 	case SHOW_INTERFACE:
199 		imsg_compose(ibuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, NULL, 0);
200 		show_interface_head();
201 		break;
202 	case SHOW_NEIGHBOR:
203 	case SHOW_NEIGHBOR_TIMERS:
204 	case SHOW_NEIGHBOR_TERSE:
205 		neighbor.show_timers = (res->action == SHOW_NEIGHBOR_TIMERS);
206 		if (res->peeraddr.aid || res->peerdesc[0])
207 			imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
208 			    &neighbor, sizeof(neighbor));
209 		else
210 			imsg_compose(ibuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1,
211 			    NULL, 0);
212 		break;
213 	case SHOW_RIB:
214 		bzero(&ribreq, sizeof(ribreq));
215 		type = IMSG_CTL_SHOW_RIB;
216 		if (res->as.type != AS_NONE) {
217 			memcpy(&ribreq.as, &res->as, sizeof(res->as));
218 			type = IMSG_CTL_SHOW_RIB_AS;
219 		}
220 		if (res->addr.aid) {
221 			memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr));
222 			ribreq.prefixlen = res->prefixlen;
223 			type = IMSG_CTL_SHOW_RIB_PREFIX;
224 		}
225 		if (res->community.as != COMMUNITY_UNSET &&
226 		    res->community.type != COMMUNITY_UNSET) {
227 			memcpy(&ribreq.community, &res->community,
228 			    sizeof(res->community));
229 			type = IMSG_CTL_SHOW_RIB_COMMUNITY;
230 		}
231 		memcpy(&ribreq.neighbor, &neighbor,
232 		    sizeof(ribreq.neighbor));
233 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
234 		ribreq.aid = res->aid;
235 		ribreq.flags = res->flags;
236 		imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq));
237 		if (!(res->flags & F_CTL_DETAIL))
238 			show_rib_summary_head();
239 		break;
240 	case SHOW_RIB_MEM:
241 		imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0);
242 		break;
243 	case RELOAD:
244 		imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0);
245 		printf("reload request sent.\n");
246 		break;
247 	case FIB:
248 		errx(1, "action==FIB");
249 		break;
250 	case FIB_COUPLE:
251 		imsg_compose(ibuf, IMSG_CTL_FIB_COUPLE, res->rtableid, 0, -1,
252 		    NULL, 0);
253 		printf("couple request sent.\n");
254 		done = 1;
255 		break;
256 	case FIB_DECOUPLE:
257 		imsg_compose(ibuf, IMSG_CTL_FIB_DECOUPLE, res->rtableid, 0, -1,
258 		    NULL, 0);
259 		printf("decouple request sent.\n");
260 		done = 1;
261 		break;
262 	case NEIGHBOR:
263 		errx(1, "action==NEIGHBOR");
264 		break;
265 	case NEIGHBOR_UP:
266 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_UP, 0, 0, -1,
267 		    &neighbor, sizeof(neighbor));
268 		break;
269 	case NEIGHBOR_DOWN:
270 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_DOWN, 0, 0, -1,
271 		    &neighbor, sizeof(neighbor));
272 		break;
273 	case NEIGHBOR_CLEAR:
274 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_CLEAR, 0, 0, -1,
275 		    &neighbor, sizeof(neighbor));
276 		break;
277 	case NEIGHBOR_RREFRESH:
278 		imsg_compose(ibuf, IMSG_CTL_NEIGHBOR_RREFRESH, 0, 0, -1,
279 		    &neighbor, sizeof(neighbor));
280 		break;
281 	case NETWORK_ADD:
282 	case NETWORK_REMOVE:
283 		bzero(&net, sizeof(net));
284 		memcpy(&net.prefix, &res->addr, sizeof(res->addr));
285 		net.prefixlen = res->prefixlen;
286 		/* attribute sets are not supported */
287 		if (res->action == NETWORK_ADD) {
288 			imsg_compose(ibuf, IMSG_NETWORK_ADD, 0, 0, -1,
289 			    &net, sizeof(net));
290 			send_filterset(ibuf, &res->set);
291 			imsg_compose(ibuf, IMSG_NETWORK_DONE, 0, 0, -1,
292 			    NULL, 0);
293 		} else
294 			imsg_compose(ibuf, IMSG_NETWORK_REMOVE, 0, 0, -1,
295 			    &net, sizeof(net));
296 		printf("request sent.\n");
297 		done = 1;
298 		break;
299 	case NETWORK_FLUSH:
300 		imsg_compose(ibuf, IMSG_NETWORK_FLUSH, 0, 0, -1, NULL, 0);
301 		printf("request sent.\n");
302 		done = 1;
303 		break;
304 	case NETWORK_SHOW:
305 		bzero(&ribreq, sizeof(ribreq));
306 		ribreq.aid = res->aid;
307 		strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
308 		imsg_compose(ibuf, IMSG_CTL_SHOW_NETWORK, 0, 0, -1,
309 		    &ribreq, sizeof(ribreq));
310 		show_network_head();
311 		break;
312 	case LOG_VERBOSE:
313 		verbose = 1;
314 		/* FALLTHROUGH */
315 	case LOG_BRIEF:
316 		imsg_compose(ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
317 		    &verbose, sizeof(verbose));
318 		printf("logging request sent.\n");
319 		done = 1;
320 		break;
321 	}
322 
323 	while (ibuf->w.queued)
324 		if (msgbuf_write(&ibuf->w) < 0)
325 			err(1, "write error");
326 
327 	while (!done) {
328 		if ((n = imsg_read(ibuf)) == -1)
329 			err(1, "imsg_read error");
330 		if (n == 0)
331 			errx(1, "pipe closed");
332 
333 		while (!done) {
334 			if ((n = imsg_get(ibuf, &imsg)) == -1)
335 				err(1, "imsg_get error");
336 			if (n == 0)
337 				break;
338 
339 			if (imsg.hdr.type == IMSG_CTL_RESULT) {
340 				done = show_result(&imsg);
341 				imsg_free(&imsg);
342 				continue;
343 			}
344 
345 			switch (res->action) {
346 			case SHOW:
347 			case SHOW_SUMMARY:
348 				done = show_summary_msg(&imsg, nodescr);
349 				break;
350 			case SHOW_SUMMARY_TERSE:
351 				done = show_summary_terse_msg(&imsg, nodescr);
352 				break;
353 			case SHOW_FIB:
354 			case SHOW_FIB_TABLES:
355 			case NETWORK_SHOW:
356 				done = show_fib_msg(&imsg);
357 				break;
358 			case SHOW_NEXTHOP:
359 				done = show_nexthop_msg(&imsg);
360 				break;
361 			case SHOW_INTERFACE:
362 				done = show_interface_msg(&imsg);
363 				break;
364 			case SHOW_NEIGHBOR:
365 				done = show_neighbor_msg(&imsg, NV_DEFAULT);
366 				break;
367 			case SHOW_NEIGHBOR_TIMERS:
368 				done = show_neighbor_msg(&imsg, NV_TIMERS);
369 				break;
370 			case SHOW_NEIGHBOR_TERSE:
371 				done = show_neighbor_terse(&imsg);
372 				break;
373 			case SHOW_RIB:
374 				if (res->flags & F_CTL_DETAIL)
375 					done = show_rib_detail_msg(&imsg,
376 					    nodescr);
377 				else
378 					done = show_rib_summary_msg(&imsg);
379 				break;
380 			case SHOW_RIB_MEM:
381 				done = show_rib_memory_msg(&imsg);
382 				break;
383 			case NEIGHBOR:
384 			case NEIGHBOR_UP:
385 			case NEIGHBOR_DOWN:
386 			case NEIGHBOR_CLEAR:
387 			case NEIGHBOR_RREFRESH:
388 			case NONE:
389 			case RELOAD:
390 			case FIB:
391 			case FIB_COUPLE:
392 			case FIB_DECOUPLE:
393 			case NETWORK_ADD:
394 			case NETWORK_REMOVE:
395 			case NETWORK_FLUSH:
396 			case IRRFILTER:
397 			case LOG_VERBOSE:
398 			case LOG_BRIEF:
399 				break;
400 			}
401 			imsg_free(&imsg);
402 		}
403 	}
404 	close(fd);
405 	free(ibuf);
406 
407 	exit(0);
408 }
409 
410 char *
411 fmt_peer(const char *descr, const struct bgpd_addr *remote_addr,
412     int masklen, int nodescr)
413 {
414 	const char	*ip;
415 	char		*p;
416 
417 	if (descr[0] && !nodescr) {
418 		if ((p = strdup(descr)) == NULL)
419 			err(1, NULL);
420 		return (p);
421 	}
422 
423 	ip = log_addr(remote_addr);
424 	if (masklen != -1 && ((remote_addr->aid == AID_INET && masklen != 32) ||
425 	    (remote_addr->aid == AID_INET6 && masklen != 128))) {
426 		if (asprintf(&p, "%s/%u", ip, masklen) == -1)
427 			err(1, NULL);
428 	} else {
429 		if ((p = strdup(ip)) == NULL)
430 			err(1, NULL);
431 	}
432 
433 	return (p);
434 }
435 
436 void
437 show_summary_head(void)
438 {
439 	printf("%-20s %8s %10s %10s %5s %-8s %s\n", "Neighbor", "AS",
440 	    "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrfRcvd");
441 }
442 
443 int
444 show_summary_msg(struct imsg *imsg, int nodescr)
445 {
446 	struct peer		*p;
447 	char			*s;
448 
449 	switch (imsg->hdr.type) {
450 	case IMSG_CTL_SHOW_NEIGHBOR:
451 		p = imsg->data;
452 		s = fmt_peer(p->conf.descr, &p->conf.remote_addr,
453 		    p->conf.remote_masklen, nodescr);
454 		if (strlen(s) >= 20)
455 			s[20] = 0;
456 		printf("%-20s %8s %10llu %10llu %5u %-8s ",
457 		    s, log_as(p->conf.remote_as),
458 		    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
459 		    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
460 		    p->stats.msg_rcvd_rrefresh,
461 		    p->stats.msg_sent_open + p->stats.msg_sent_notification +
462 		    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
463 		    p->stats.msg_sent_rrefresh,
464 		    p->wbuf.queued,
465 		    fmt_timeframe(p->stats.last_updown));
466 		if (p->state == STATE_ESTABLISHED) {
467 			printf("%6u", p->stats.prefix_cnt);
468 			if (p->conf.max_prefix != 0)
469 				printf("/%u", p->conf.max_prefix);
470 		} else if (p->conf.template)
471 			printf("Template");
472 		else
473 			printf("%s", statenames[p->state]);
474 		printf("\n");
475 		free(s);
476 		break;
477 	case IMSG_CTL_END:
478 		return (1);
479 	default:
480 		break;
481 	}
482 
483 	return (0);
484 }
485 
486 int
487 show_summary_terse_msg(struct imsg *imsg, int nodescr)
488 {
489 	struct peer		*p;
490 	char			*s;
491 
492 	switch (imsg->hdr.type) {
493 	case IMSG_CTL_SHOW_NEIGHBOR:
494 		p = imsg->data;
495 		s = fmt_peer(p->conf.descr, &p->conf.remote_addr,
496 		    p->conf.remote_masklen, nodescr);
497 		printf("%s %s %s\n", s, log_as(p->conf.remote_as),
498 		    p->conf.template ? "Template" : statenames[p->state]);
499 		free(s);
500 		break;
501 	case IMSG_CTL_END:
502 		return (1);
503 	default:
504 		break;
505 	}
506 
507 	return (0);
508 }
509 
510 int
511 show_neighbor_terse(struct imsg *imsg)
512 {
513 	struct peer		*p;
514 
515 	switch (imsg->hdr.type) {
516 	case IMSG_CTL_SHOW_NEIGHBOR:
517 		p = imsg->data;
518 		printf("%llu %llu %llu %llu %llu %llu %llu "
519 		    "%llu %llu %llu %u %u %llu %llu %llu %llu\n",
520 		    p->stats.msg_sent_open, p->stats.msg_rcvd_open,
521 		    p->stats.msg_sent_notification,
522 		    p->stats.msg_rcvd_notification,
523 		    p->stats.msg_sent_update, p->stats.msg_rcvd_update,
524 		    p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive,
525 		    p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh,
526 		    p->stats.prefix_cnt, p->conf.max_prefix,
527 		    p->stats.prefix_sent_update, p->stats.prefix_rcvd_update,
528 		    p->stats.prefix_sent_withdraw,
529 		    p->stats.prefix_rcvd_withdraw);
530 		break;
531 	case IMSG_CTL_END:
532 		return (1);
533 	default:
534 		break;
535 	}
536 
537 	return (0);
538 }
539 
540 int
541 show_neighbor_msg(struct imsg *imsg, enum neighbor_views nv)
542 {
543 	struct peer		*p;
544 	struct ctl_timer	*t;
545 	struct in_addr		 ina;
546 	char			 buf[NI_MAXHOST], pbuf[NI_MAXSERV], *s;
547 	int			 hascapamp = 0;
548 	u_int8_t		 i;
549 
550 	switch (imsg->hdr.type) {
551 	case IMSG_CTL_SHOW_NEIGHBOR:
552 		p = imsg->data;
553 		if ((p->conf.remote_addr.aid == AID_INET &&
554 		    p->conf.remote_masklen != 32) ||
555 		    (p->conf.remote_addr.aid == AID_INET6 &&
556 		    p->conf.remote_masklen != 128)) {
557 			if (asprintf(&s, "%s/%u",
558 			    log_addr(&p->conf.remote_addr),
559 			    p->conf.remote_masklen) == -1)
560 				err(1, NULL);
561 		} else
562 			if ((s = strdup(log_addr(&p->conf.remote_addr))) ==
563 			    NULL)
564 				err(1, "strdup");
565 
566 		ina.s_addr = p->remote_bgpid;
567 		printf("BGP neighbor is %s, ", s);
568 		free(s);
569 		if (p->conf.remote_as == 0 && p->conf.template)
570 			printf("remote AS: accept any");
571 		else
572 			printf("remote AS %s", log_as(p->conf.remote_as));
573 		if (p->conf.template)
574 			printf(", Template");
575 		if (p->conf.cloned)
576 			printf(", Cloned");
577 		if (p->conf.passive)
578 			printf(", Passive");
579 		if (p->conf.ebgp && p->conf.distance > 1)
580 			printf(", Multihop (%u)", (int)p->conf.distance);
581 		printf("\n");
582 		if (p->conf.descr[0])
583 			printf(" Description: %s\n", p->conf.descr);
584 		printf("  BGP version 4, remote router-id %s\n",
585 		    inet_ntoa(ina));
586 		printf("  BGP state = %s", statenames[p->state]);
587 		if (p->stats.last_updown != 0)
588 			printf(", %s for %s",
589 			    p->state == STATE_ESTABLISHED ? "up" : "down",
590 			    fmt_timeframe(p->stats.last_updown));
591 		printf("\n");
592 		printf("  Last read %s, holdtime %us, keepalive interval %us\n",
593 		    fmt_timeframe(p->stats.last_read),
594 		    p->holdtime, p->holdtime/3);
595 		for (i = 0; i < AID_MAX; i++)
596 			if (p->capa.peer.mp[i])
597 				hascapamp = 1;
598 		if (hascapamp || p->capa.peer.refresh ||
599 		    p->capa.peer.restart || p->capa.peer.as4byte) {
600 			printf("  Neighbor capabilities:\n");
601 			if (hascapamp) {
602 				printf("    Multiprotocol extensions: ");
603 				print_neighbor_capa_mp(p);
604 				printf("\n");
605 			}
606 			if (p->capa.peer.refresh)
607 				printf("    Route Refresh\n");
608 			if (p->capa.peer.restart)
609 				printf("    Graceful Restart\n");
610 			if (p->capa.peer.as4byte)
611 				printf("    4-byte AS numbers\n");
612 		}
613 		printf("\n");
614 		if (nv == NV_TIMERS)
615 			break;
616 		print_neighbor_msgstats(p);
617 		printf("\n");
618 		if (p->state == STATE_IDLE) {
619 			static const char	*errstr;
620 
621 			errstr = get_errstr(p->stats.last_sent_errcode,
622 			    p->stats.last_sent_suberr);
623 			if (errstr)
624 				printf("  Last error: %s\n\n", errstr);
625 		} else {
626 			if (getnameinfo((struct sockaddr *)&p->sa_local,
627 			    (socklen_t)p->sa_local.ss_len,
628 			    buf, sizeof(buf), pbuf, sizeof(pbuf),
629 			    NI_NUMERICHOST | NI_NUMERICSERV)) {
630 				strlcpy(buf, "(unknown)", sizeof(buf));
631 				strlcpy(pbuf, "", sizeof(pbuf));
632 			}
633 			printf("  Local host:  %20s, Local port:  %5s\n", buf,
634 			    pbuf);
635 
636 			if (getnameinfo((struct sockaddr *)&p->sa_remote,
637 			    (socklen_t)p->sa_remote.ss_len,
638 			    buf, sizeof(buf), pbuf, sizeof(pbuf),
639 			    NI_NUMERICHOST | NI_NUMERICSERV)) {
640 				strlcpy(buf, "(unknown)", sizeof(buf));
641 				strlcpy(pbuf, "", sizeof(pbuf));
642 			}
643 			printf("  Remote host: %20s, Remote port: %5s\n", buf,
644 			    pbuf);
645 			printf("\n");
646 		}
647 		break;
648 	case IMSG_CTL_SHOW_TIMER:
649 		t = imsg->data;
650 		if (t->type > 0 && t->type < Timer_Max)
651 			print_timer(timernames[t->type], t->val);
652 		break;
653 	case IMSG_CTL_END:
654 		return (1);
655 		break;
656 	default:
657 		break;
658 	}
659 
660 	return (0);
661 }
662 
663 void
664 print_neighbor_capa_mp(struct peer *p)
665 {
666 	int		comma;
667 	u_int8_t	i;
668 
669 	for (i = 0, comma = 0; i < AID_MAX; i++)
670 		if (p->capa.peer.mp[i]) {
671 			printf("%s%s", comma ? ", " : "", aid2str(i));
672 			comma = 1;
673 		}
674 }
675 
676 void
677 print_neighbor_msgstats(struct peer *p)
678 {
679 	printf("  Message statistics:\n");
680 	printf("  %-15s %-10s %-10s\n", "", "Sent", "Received");
681 	printf("  %-15s %10llu %10llu\n", "Opens",
682 	    p->stats.msg_sent_open, p->stats.msg_rcvd_open);
683 	printf("  %-15s %10llu %10llu\n", "Notifications",
684 	    p->stats.msg_sent_notification, p->stats.msg_rcvd_notification);
685 	printf("  %-15s %10llu %10llu\n", "Updates",
686 	    p->stats.msg_sent_update, p->stats.msg_rcvd_update);
687 	printf("  %-15s %10llu %10llu\n", "Keepalives",
688 	    p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive);
689 	printf("  %-15s %10llu %10llu\n", "Route Refresh",
690 	    p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh);
691 	printf("  %-15s %10llu %10llu\n\n", "Total",
692 	    p->stats.msg_sent_open + p->stats.msg_sent_notification +
693 	    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
694 	    p->stats.msg_sent_rrefresh,
695 	    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
696 	    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
697 	    p->stats.msg_rcvd_rrefresh);
698 	printf("  Update statistics:\n");
699 	printf("  %-15s %-10s %-10s\n", "", "Sent", "Received");
700 	printf("  %-15s %10llu %10llu\n", "Updates",
701 	    p->stats.prefix_sent_update, p->stats.prefix_rcvd_update);
702 	printf("  %-15s %10llu %10llu\n", "Withdraws",
703 	    p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw);
704 }
705 
706 void
707 print_timer(const char *name, timer_t d)
708 {
709 	printf("  %-20s ", name);
710 
711 	if (d <= 0)
712 		printf("%-20s\n", "due");
713 	else
714 		printf("due in %-13s\n", fmt_timeframe_core(d));
715 }
716 
717 #define TF_BUFS	8
718 #define TF_LEN	9
719 
720 static char *
721 fmt_timeframe(time_t t)
722 {
723 	if (t == 0)
724 		return ("Never");
725 	else
726 		return (fmt_timeframe_core(time(NULL) - t));
727 }
728 
729 static char *
730 fmt_timeframe_core(time_t t)
731 {
732 	char		*buf;
733 	static char	 tfbuf[TF_BUFS][TF_LEN];	/* ring buffer */
734 	static int	 idx = 0;
735 	unsigned int	 sec, min, hrs, day, week;
736 
737 	buf = tfbuf[idx++];
738 	if (idx == TF_BUFS)
739 		idx = 0;
740 
741 	week = t;
742 
743 	sec = week % 60;
744 	week /= 60;
745 	min = week % 60;
746 	week /= 60;
747 	hrs = week % 24;
748 	week /= 24;
749 	day = week % 7;
750 	week /= 7;
751 
752 	if (week > 0)
753 		snprintf(buf, TF_LEN, "%02uw%01ud%02uh", week, day, hrs);
754 	else if (day > 0)
755 		snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min);
756 	else
757 		snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec);
758 
759 	return (buf);
760 }
761 
762 void
763 show_fib_head(void)
764 {
765 	printf("flags: * = valid, B = BGP, C = Connected, S = Static\n");
766 	printf("       N = BGP Nexthop reachable via this route\n");
767 	printf("       r = reject route, b = blackhole route\n\n");
768 	printf("flags prio destination          gateway\n");
769 }
770 
771 void
772 show_fib_tables_head(void)
773 {
774 	printf("%-5s %-20s %-8s\n", "Table", "Description", "State");
775 }
776 
777 void
778 show_network_head(void)
779 {
780 	printf("flags: S = Static\n");
781 	printf("flags destination\n");
782 }
783 
784 void
785 show_fib_flags(u_int16_t flags)
786 {
787 	if (flags & F_DOWN)
788 		printf(" ");
789 	else
790 		printf("*");
791 
792 	if (flags & F_BGPD_INSERTED)
793 		printf("B");
794 	else if (flags & F_CONNECTED)
795 		printf("C");
796 	else if (flags & F_STATIC)
797 		printf("S");
798 	else
799 		printf(" ");
800 
801 	if (flags & F_NEXTHOP)
802 		printf("N");
803 	else
804 		printf(" ");
805 
806 	if (flags & F_REJECT && flags & F_BLACKHOLE)
807 		printf("f");
808 	else if (flags & F_REJECT)
809 		printf("r");
810 	else if (flags & F_BLACKHOLE)
811 		printf("b");
812 	else
813 		printf(" ");
814 
815 	printf("  ");
816 }
817 
818 int
819 show_fib_msg(struct imsg *imsg)
820 {
821 	struct kroute_full	*kf;
822 	struct ktable		*kt;
823 	char			*p;
824 
825 	switch (imsg->hdr.type) {
826 	case IMSG_CTL_KROUTE:
827 	case IMSG_CTL_SHOW_NETWORK:
828 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf))
829 			errx(1, "wrong imsg len");
830 		kf = imsg->data;
831 
832 		show_fib_flags(kf->flags);
833 
834 		if (asprintf(&p, "%s/%u", log_addr(&kf->prefix),
835 		    kf->prefixlen) == -1)
836 			err(1, NULL);
837 		printf("%4i %-20s ", kf->priority, p);
838 		free(p);
839 
840 		if (kf->flags & F_CONNECTED)
841 			printf("link#%u", kf->ifindex);
842 		else
843 			printf("%s", log_addr(&kf->nexthop));
844 		printf("\n");
845 
846 		break;
847 	case IMSG_CTL_SHOW_FIB_TABLES:
848 		if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt))
849 			errx(1, "wrong imsg len");
850 		kt = imsg->data;
851 
852 		printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr,
853 		    kt->fib_sync ? "coupled" : "decoupled",
854 		    kt->fib_sync != kt->fib_conf ? "*" : "");
855 
856 		break;
857 	case IMSG_CTL_END:
858 		return (1);
859 	default:
860 		break;
861 	}
862 
863 	return (0);
864 }
865 
866 void
867 show_nexthop_head(void)
868 {
869 	printf("Flags: * = nexthop valid\n");
870 	printf("\n  %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route",
871 	     "Prio", "Gateway", "Iface");
872 }
873 
874 int
875 show_nexthop_msg(struct imsg *imsg)
876 {
877 	struct ctl_show_nexthop	*p;
878 	struct kroute		*k;
879 	struct kroute6		*k6;
880 	char			*s;
881 
882 	switch (imsg->hdr.type) {
883 	case IMSG_CTL_SHOW_NEXTHOP:
884 		p = imsg->data;
885 		printf("%s %-15s ", p->valid ? "*" : " ", log_addr(&p->addr));
886 		if (!p->krvalid) {
887 			printf("\n");
888 			return (0);
889 		}
890 		switch (p->addr.aid) {
891 		case AID_INET:
892 			k = &p->kr.kr4;
893 			if (asprintf(&s, "%s/%u", inet_ntoa(k->prefix),
894 			    k->prefixlen) == -1)
895 				err(1, NULL);
896 			printf("%-20s", s);
897 			free(s);
898 			printf("%3i %-15s ", k->priority,
899 			    k->flags & F_CONNECTED ? "connected" :
900 			    inet_ntoa(k->nexthop));
901 			break;
902 		case AID_INET6:
903 			k6 = &p->kr.kr6;
904 			if (asprintf(&s, "%s/%u", log_in6addr(&k6->prefix),
905 			    k6->prefixlen) == -1)
906 				err(1, NULL);
907 			printf("%-20s", s);
908 			free(s);
909 			printf("%3i %-15s ", k6->priority,
910 			    k6->flags & F_CONNECTED ? "connected" :
911 			    log_in6addr(&k6->nexthop));
912 			break;
913 		default:
914 			printf("unknown address family\n");
915 			return (0);
916 		}
917 		if (p->kif.ifname[0]) {
918 			char *s1;
919 			if (p->kif.baudrate) {
920 				if (asprintf(&s1, ", %s",
921 				    get_baudrate(p->kif.baudrate,
922 				    "bps")) == -1)
923 					err(1, NULL);
924 			} else if (asprintf(&s1, ", %s", get_linkstate(
925 			    p->kif.media_type, p->kif.link_state)) == -1)
926 					err(1, NULL);
927 			if (asprintf(&s, "%s (%s%s)", p->kif.ifname,
928 			    p->kif.flags & IFF_UP ? "UP" : "DOWN", s1) == -1)
929 				err(1, NULL);
930 			printf("%-15s", s);
931 			free(s1);
932 			free(s);
933 		}
934 		printf("\n");
935 		break;
936 	case IMSG_CTL_END:
937 		return (1);
938 		break;
939 	default:
940 		break;
941 	}
942 
943 	return (0);
944 }
945 
946 
947 void
948 show_interface_head(void)
949 {
950 	printf("%-15s%-15s%-15s%s\n", "Interface", "Nexthop state", "Flags",
951 	    "Link state");
952 }
953 
954 const struct if_status_description
955 		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
956 const struct ifmedia_description
957 		ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS;
958 
959 int
960 ift2ifm(int media_type)
961 {
962 	switch (media_type) {
963 	case IFT_ETHER:
964 		return (IFM_ETHER);
965 	case IFT_FDDI:
966 		return (IFM_FDDI);
967 	case IFT_CARP:
968 		return (IFM_CARP);
969 	case IFT_IEEE80211:
970 		return (IFM_IEEE80211);
971 	default:
972 		return (0);
973 	}
974 }
975 
976 const char *
977 get_media_descr(int media_type)
978 {
979 	const struct ifmedia_description	*p;
980 
981 	for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++)
982 		if (media_type == p->ifmt_word)
983 			return (p->ifmt_string);
984 
985 	return ("unknown media");
986 }
987 
988 const char *
989 get_linkstate(int media_type, int link_state)
990 {
991 	const struct if_status_description *p;
992 	static char buf[8];
993 
994 	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
995 		if (LINK_STATE_DESC_MATCH(p, media_type, link_state))
996 			return (p->ifs_string);
997 	}
998 	snprintf(buf, sizeof(buf), "[#%d]", link_state);
999 	return (buf);
1000 }
1001 
1002 const char *
1003 get_baudrate(u_int64_t baudrate, char *unit)
1004 {
1005 	static char bbuf[16];
1006 
1007 	if (baudrate > IF_Gbps(1))
1008 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
1009 		    baudrate / IF_Gbps(1), unit);
1010 	else if (baudrate > IF_Mbps(1))
1011 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
1012 		    baudrate / IF_Mbps(1), unit);
1013 	else if (baudrate > IF_Kbps(1))
1014 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
1015 		    baudrate / IF_Kbps(1), unit);
1016 	else
1017 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
1018 		    baudrate, unit);
1019 
1020 	return (bbuf);
1021 }
1022 
1023 int
1024 show_interface_msg(struct imsg *imsg)
1025 {
1026 	struct kif	*k;
1027 	int		 ifms_type;
1028 
1029 	switch (imsg->hdr.type) {
1030 	case IMSG_CTL_SHOW_INTERFACE:
1031 		k = imsg->data;
1032 		printf("%-15s", k->ifname);
1033 		printf("%-15s", k->nh_reachable ? "ok" : "invalid");
1034 		printf("%-15s", k->flags & IFF_UP ? "UP" : "");
1035 
1036 		if ((ifms_type = ift2ifm(k->media_type)) != 0)
1037 			printf("%s, ", get_media_descr(ifms_type));
1038 
1039 		printf("%s", get_linkstate(k->media_type, k->link_state));
1040 
1041 		if (k->link_state != LINK_STATE_DOWN && k->baudrate > 0)
1042 			printf(", %s", get_baudrate(k->baudrate, "Bit/s"));
1043 		printf("\n");
1044 		break;
1045 	case IMSG_CTL_END:
1046 		return (1);
1047 		break;
1048 	default:
1049 		break;
1050 	}
1051 
1052 	return (0);
1053 }
1054 
1055 void
1056 show_rib_summary_head(void)
1057 {
1058 	printf(
1059 	    "flags: * = Valid, > = Selected, I = via IBGP, A = Announced\n");
1060 	printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n");
1061 	printf("%-5s %-20s %-15s  %5s %5s %s\n", "flags", "destination",
1062 	    "gateway", "lpref", "med", "aspath origin");
1063 }
1064 
1065 void
1066 print_prefix(struct bgpd_addr *prefix, u_int8_t prefixlen, u_int8_t flags)
1067 {
1068 	char			*p;
1069 
1070 	print_flags(flags, 1);
1071 	if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1)
1072 		err(1, NULL);
1073 	printf("%-20s", p);
1074 	free(p);
1075 }
1076 
1077 const char *
1078 print_origin(u_int8_t origin, int sum)
1079 {
1080 	switch (origin) {
1081 	case ORIGIN_IGP:
1082 		return (sum ? "i" : "IGP");
1083 	case ORIGIN_EGP:
1084 		return (sum ? "e" : "EGP");
1085 	case ORIGIN_INCOMPLETE:
1086 		return (sum ? "?" : "incomplete");
1087 	default:
1088 		return (sum ? "X" : "bad origin");
1089 	}
1090 }
1091 
1092 void
1093 print_flags(u_int8_t flags, int sum)
1094 {
1095 	char	 flagstr[5];
1096 	char	*p = flagstr;
1097 
1098 	if (sum) {
1099 		if (flags & F_PREF_ANNOUNCE)
1100 			*p++ = 'A';
1101 		if (flags & F_PREF_INTERNAL)
1102 			*p++ = 'I';
1103 		if (flags & F_PREF_ELIGIBLE)
1104 			*p++ = '*';
1105 		if (flags & F_PREF_ACTIVE)
1106 			*p++ = '>';
1107 		*p = '\0';
1108 		printf("%-5s ", flagstr);
1109 	} else {
1110 		if (flags & F_PREF_INTERNAL)
1111 			printf("internal");
1112 		else
1113 			printf("external");
1114 		if (flags & F_PREF_ELIGIBLE)
1115 			printf(", valid");
1116 		if (flags & F_PREF_ACTIVE)
1117 			printf(", best");
1118 		if (flags & F_PREF_ANNOUNCE)
1119 			printf(", announced");
1120 	}
1121 }
1122 
1123 int
1124 show_rib_summary_msg(struct imsg *imsg)
1125 {
1126 	struct ctl_show_rib	 rib;
1127 	char			*aspath;
1128 	u_char			*asdata;
1129 
1130 	switch (imsg->hdr.type) {
1131 	case IMSG_CTL_SHOW_RIB:
1132 		memcpy(&rib, imsg->data, sizeof(rib));
1133 
1134 		print_prefix(&rib.prefix, rib.prefixlen, rib.flags);
1135 		printf(" %-15s ", log_addr(&rib.exit_nexthop));
1136 
1137 		printf(" %5u %5u ", rib.local_pref, rib.med);
1138 
1139 		asdata = imsg->data;
1140 		asdata += sizeof(struct ctl_show_rib);
1141 		if (aspath_asprint(&aspath, asdata, rib.aspath_len) == -1)
1142 			err(1, NULL);
1143 		if (strlen(aspath) > 0)
1144 			printf("%s ", aspath);
1145 		free(aspath);
1146 
1147 		printf("%s\n", print_origin(rib.origin, 1));
1148 		break;
1149 	case IMSG_CTL_END:
1150 		return (1);
1151 	default:
1152 		break;
1153 	}
1154 
1155 	return (0);
1156 }
1157 
1158 int
1159 show_rib_detail_msg(struct imsg *imsg, int nodescr)
1160 {
1161 	struct ctl_show_rib	 rib;
1162 	struct in_addr		 id;
1163 	char			*aspath, *s;
1164 	u_char			*data;
1165 	u_int32_t		 as;
1166 	u_int16_t		 ilen, alen, ioff;
1167 	u_int8_t		 flags, type;
1168 	time_t			 now;
1169 
1170 	switch (imsg->hdr.type) {
1171 	case IMSG_CTL_SHOW_RIB:
1172 		memcpy(&rib, imsg->data, sizeof(rib));
1173 
1174 		printf("\nBGP routing table entry for %s/%u\n",
1175 		    log_addr(&rib.prefix), rib.prefixlen);
1176 
1177 		data = imsg->data;
1178 		data += sizeof(struct ctl_show_rib);
1179 		if (aspath_asprint(&aspath, data, rib.aspath_len) == -1)
1180 			err(1, NULL);
1181 		if (strlen(aspath) > 0)
1182 			printf("    %s\n", aspath);
1183 		free(aspath);
1184 
1185 		s = fmt_peer(rib.descr, &rib.remote_addr, -1, nodescr);
1186 		printf("    Nexthop %s ", log_addr(&rib.exit_nexthop));
1187 		printf("(via %s) from %s (", log_addr(&rib.true_nexthop), s);
1188 		free(s);
1189 		id.s_addr = htonl(rib.remote_id);
1190 		printf("%s)\n", inet_ntoa(id));
1191 
1192 		printf("    Origin %s, metric %u, localpref %u, ",
1193 		    print_origin(rib.origin, 0), rib.med, rib.local_pref);
1194 		print_flags(rib.flags, 0);
1195 
1196 		now = time(NULL);
1197 		if (now > rib.lastchange)
1198 			now -= rib.lastchange;
1199 		else
1200 			now = 0;
1201 
1202 		printf("\n    Last update: %s ago\n",
1203 		    fmt_timeframe_core(now));
1204 		break;
1205 	case IMSG_CTL_SHOW_RIB_ATTR:
1206 		ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
1207 		if (ilen < 3)
1208 			errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
1209 		data = imsg->data;
1210 		flags = data[0];
1211 		type = data[1];
1212 
1213 		/* get the attribute length */
1214 		if (flags & ATTR_EXTLEN) {
1215 			if (ilen < 4)
1216 				errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
1217 			memcpy(&alen, data+2, sizeof(u_int16_t));
1218 			alen = ntohs(alen);
1219 			data += 4;
1220 			ilen -= 4;
1221 		} else {
1222 			alen = data[2];
1223 			data += 3;
1224 			ilen -= 3;
1225 		}
1226 		/* bad imsg len how can that happen!? */
1227 		if (alen != ilen)
1228 			errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received");
1229 
1230 		switch (type) {
1231 		case ATTR_COMMUNITIES:
1232 			printf("    Communities: ");
1233 			show_community(data, alen);
1234 			printf("\n");
1235 			break;
1236 		case ATTR_AGGREGATOR:
1237 			memcpy(&as, data, sizeof(as));
1238 			memcpy(&id, data + sizeof(as), sizeof(id));
1239 			printf("    Aggregator: %s [%s]\n",
1240 			    log_as(ntohl(as)), inet_ntoa(id));
1241 			break;
1242 		case ATTR_ORIGINATOR_ID:
1243 			memcpy(&id, data, sizeof(id));
1244 			printf("    Originator Id: %s\n", inet_ntoa(id));
1245 			break;
1246 		case ATTR_CLUSTER_LIST:
1247 			printf("    Cluster ID List:");
1248 			for (ioff = 0; ioff + sizeof(id) <= ilen;
1249 			    ioff += sizeof(id)) {
1250 				memcpy(&id, data + ioff, sizeof(id));
1251 				printf(" %s", inet_ntoa(id));
1252 			}
1253 			printf("\n");
1254 			break;
1255 		case ATTR_EXT_COMMUNITIES:
1256 			printf("    Ext. communities: ");
1257 			show_ext_community(data, alen);
1258 			printf("\n");
1259 			break;
1260 		default:
1261 			/* ignore unknown attributes */
1262 			break;
1263 		}
1264 		break;
1265 	case IMSG_CTL_END:
1266 		printf("\n");
1267 		return (1);
1268 	default:
1269 		break;
1270 	}
1271 
1272 	return (0);
1273 }
1274 
1275 char *
1276 fmt_mem(int64_t num)
1277 {
1278 	static char	buf[16];
1279 
1280 	if (fmt_scaled(num, buf) == -1)
1281 		snprintf(buf, sizeof(buf), "%lldB", (long long)num);
1282 
1283 	return (buf);
1284 }
1285 
1286 size_t  pt_sizes[AID_MAX] = AID_PTSIZE;
1287 
1288 int
1289 show_rib_memory_msg(struct imsg *imsg)
1290 {
1291 	struct rde_memstats	stats;
1292 	size_t			pts = 0;
1293 	int			i;
1294 
1295 	switch (imsg->hdr.type) {
1296 	case IMSG_CTL_SHOW_RIB_MEM:
1297 		memcpy(&stats, imsg->data, sizeof(stats));
1298 		printf("RDE memory statistics\n");
1299 		for (i = 0; i < AID_MAX; i++) {
1300 			if (stats.pt_cnt[i] == 0)
1301 				continue;
1302 			pts += stats.pt_cnt[i] * pt_sizes[i];
1303 			printf("%10lld %s network entries using %s of memory\n",
1304 			    (long long)stats.pt_cnt[i], aid_vals[i].name,
1305 			    fmt_mem(stats.pt_cnt[i] * pt_sizes[i]));
1306 		}
1307 		printf("%10lld rib entries using %s of memory\n",
1308 		    (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt *
1309 		    sizeof(struct rib_entry)));
1310 		printf("%10lld prefix entries using %s of memory\n",
1311 		    (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt *
1312 		    sizeof(struct prefix)));
1313 		printf("%10lld BGP path attribute entries using %s of memory\n",
1314 		    (long long)stats.path_cnt, fmt_mem(stats.path_cnt *
1315 		    sizeof(struct rde_aspath)));
1316 		printf("%10lld BGP AS-PATH attribute entries using "
1317 		    "%s of memory,\n\t   and holding %lld references\n",
1318 		    (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size),
1319 		    (long long)stats.aspath_refs);
1320 		printf("%10lld BGP attributes entries using %s of memory\n",
1321 		    (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt *
1322 		    sizeof(struct attr)));
1323 		printf("\t   and holding %lld references\n",
1324 		    (long long)stats.attr_refs);
1325 		printf("%10lld BGP attributes using %s of memory\n",
1326 		    (long long)stats.attr_dcnt, fmt_mem(stats.attr_data));
1327 		printf("RIB using %s of memory\n", fmt_mem(pts +
1328 		    stats.prefix_cnt * sizeof(struct prefix) +
1329 		    stats.rib_cnt * sizeof(struct rib_entry) +
1330 		    stats.path_cnt * sizeof(struct rde_aspath) +
1331 		    stats.aspath_size + stats.attr_cnt * sizeof(struct attr) +
1332 		    stats.attr_data));
1333 		break;
1334 	default:
1335 		break;
1336 	}
1337 
1338 	return (1);
1339 }
1340 
1341 void
1342 show_community(u_char *data, u_int16_t len)
1343 {
1344 	u_int16_t	a, v;
1345 	u_int16_t	i;
1346 
1347 	if (len & 0x3)
1348 		return;
1349 
1350 	for (i = 0; i < len; i += 4) {
1351 		memcpy(&a, data + i, sizeof(a));
1352 		memcpy(&v, data + i + 2, sizeof(v));
1353 		a = ntohs(a);
1354 		v = ntohs(v);
1355 		if (a == COMMUNITY_WELLKNOWN)
1356 			switch (v) {
1357 			case COMMUNITY_NO_EXPORT:
1358 				printf("NO_EXPORT");
1359 				break;
1360 			case COMMUNITY_NO_ADVERTISE:
1361 				printf("NO_ADVERTISE");
1362 				break;
1363 			case COMMUNITY_NO_EXPSUBCONFED:
1364 				printf("NO_EXPORT_SUBCONFED");
1365 				break;
1366 			case COMMUNITY_NO_PEER:
1367 				printf("NO_PEER");
1368 				break;
1369 			default:
1370 				printf("WELLKNOWN:%hu", v);
1371 				break;
1372 			}
1373 		else
1374 			printf("%hu:%hu", a, v);
1375 
1376 		if (i + 4 < len)
1377 			printf(" ");
1378 	}
1379 }
1380 
1381 void
1382 show_ext_community(u_char *data, u_int16_t len)
1383 {
1384 	u_int64_t	ext;
1385 	struct in_addr	ip;
1386 	u_int32_t	as4, u32;
1387 	u_int16_t	i, as2, u16;
1388 	u_int8_t	type, subtype;
1389 
1390 	if (len & 0x7)
1391 		return;
1392 
1393 	for (i = 0; i < len; i += 8) {
1394 		type = data[i];
1395 		subtype = data[i + 1];
1396 
1397 		switch (type & EXT_COMMUNITY_VALUE) {
1398 		case EXT_COMMUNITY_TWO_AS:
1399 			memcpy(&as2, data + i + 2, sizeof(as2));
1400 			memcpy(&u32, data + i + 4, sizeof(u32));
1401 			printf("%s %s:%u", log_ext_subtype(subtype),
1402 			    log_as(ntohs(as2)), ntohl(u32));
1403 			break;
1404 		case EXT_COMMUNITY_IPV4:
1405 			memcpy(&ip, data + i + 2, sizeof(ip));
1406 			memcpy(&u16, data + i + 6, sizeof(u16));
1407 			printf("%s %s:%hu", log_ext_subtype(subtype),
1408 			    inet_ntoa(ip), ntohs(u16));
1409 			break;
1410 		case EXT_COMMUNITY_FOUR_AS:
1411 			memcpy(&as4, data + i + 2, sizeof(as4));
1412 			memcpy(&u16, data + i + 6, sizeof(u16));
1413 			printf("%s %s:%hu", log_ext_subtype(subtype),
1414 			    log_as(ntohl(as4)), ntohs(u16));
1415 			break;
1416 		case EXT_COMMUNITY_OPAQUE:
1417 			memcpy(&ext, data + i, sizeof(ext));
1418 			ext = betoh64(ext) & 0xffffffffffffLL;
1419 			printf("%s 0x%llx", log_ext_subtype(subtype), ext);
1420 			break;
1421 		default:
1422 			memcpy(&ext, data + i, sizeof(ext));
1423 			printf("0x%llx", betoh64(ext));
1424 		}
1425 		if (i + 8 < len)
1426 			printf(", ");
1427 	}
1428 }
1429 
1430 void
1431 send_filterset(struct imsgbuf *i, struct filter_set_head *set)
1432 {
1433 	struct filter_set	*s;
1434 
1435 	while ((s = TAILQ_FIRST(set)) != NULL) {
1436 		imsg_compose(i, IMSG_FILTER_SET, 0, 0, -1, s,
1437 		    sizeof(struct filter_set));
1438 		TAILQ_REMOVE(set, s, entry);
1439 		free(s);
1440 	}
1441 }
1442 
1443 static const char *
1444 get_errstr(u_int8_t errcode, u_int8_t subcode)
1445 {
1446 	static const char	*errstr = NULL;
1447 
1448 	if (errcode && errcode < sizeof(errnames)/sizeof(char *))
1449 		errstr = errnames[errcode];
1450 
1451 	switch (errcode) {
1452 	case ERR_HEADER:
1453 		if (subcode &&
1454 		    subcode < sizeof(suberr_header_names)/sizeof(char *))
1455 			errstr = suberr_header_names[subcode];
1456 		break;
1457 	case ERR_OPEN:
1458 		if (subcode &&
1459 		    subcode < sizeof(suberr_open_names)/sizeof(char *))
1460 			errstr = suberr_open_names[subcode];
1461 		break;
1462 	case ERR_UPDATE:
1463 		if (subcode &&
1464 		    subcode < sizeof(suberr_update_names)/sizeof(char *))
1465 			errstr = suberr_update_names[subcode];
1466 		break;
1467 	case ERR_HOLDTIMEREXPIRED:
1468 	case ERR_FSM:
1469 	case ERR_CEASE:
1470 		break;
1471 	default:
1472 		return ("unknown error code");
1473 	}
1474 
1475 	return (errstr);
1476 }
1477 
1478 int
1479 show_result(struct imsg *imsg)
1480 {
1481 	u_int	rescode;
1482 
1483 	if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode))
1484 		errx(1, "got IMSG_CTL_RESULT with wrong len");
1485 	memcpy(&rescode, imsg->data, sizeof(rescode));
1486 
1487 	if (rescode == 0)
1488 		printf("request processed\n");
1489 	else {
1490 		if (rescode >
1491 		    sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0]))
1492 			errx(1, "illegal error code %u", rescode);
1493 		printf("%s\n", ctl_res_strerror[rescode]);
1494 	}
1495 
1496 	return (1);
1497 }
1498 
1499 /* following functions are necessary for imsg framework */
1500 void
1501 log_warnx(const char *emsg, ...)
1502 {
1503 	va_list	 ap;
1504 
1505 	va_start(ap, emsg);
1506 	vwarnx(emsg, ap);
1507 	va_end(ap);
1508 }
1509 
1510 void
1511 log_warn(const char *emsg, ...)
1512 {
1513 	va_list	 ap;
1514 
1515 	va_start(ap, emsg);
1516 	vwarn(emsg, ap);
1517 	va_end(ap);
1518 }
1519 
1520 void
1521 fatal(const char *emsg)
1522 {
1523 	err(1, emsg);
1524 }
1525