xref: /openbsd-src/usr.sbin/bgpctl/output.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: output.c,v 1.16 2021/04/26 18:23:20 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 <endian.h>
23 #include <err.h>
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "bgpd.h"
30 #include "session.h"
31 #include "rde.h"
32 
33 #include "bgpctl.h"
34 #include "parser.h"
35 
36 const size_t  pt_sizes[AID_MAX] = AID_PTSIZE;
37 
38 static void
39 show_head(struct parse_result *res)
40 {
41 	switch (res->action) {
42 	case SHOW:
43 	case SHOW_SUMMARY:
44 		printf("%-20s %8s %10s %10s %5s %-8s %s\n", "Neighbor", "AS",
45 		    "MsgRcvd", "MsgSent", "OutQ", "Up/Down", "State/PrfRcvd");
46 		break;
47 	case SHOW_FIB:
48 		printf("flags: * = valid, B = BGP, C = Connected, "
49 		    "S = Static, D = Dynamic\n");
50 		printf("       "
51 		    "N = BGP Nexthop reachable via this route\n");
52 		printf("       r = reject route, b = blackhole route\n\n");
53 		printf("flags prio destination          gateway\n");
54 		break;
55 	case SHOW_FIB_TABLES:
56 		printf("%-5s %-20s %-8s\n", "Table", "Description", "State");
57 		break;
58 	case SHOW_NEXTHOP:
59 		printf("Flags: * = nexthop valid\n");
60 		printf("\n  %-15s %-19s%-4s %-15s %-20s\n", "Nexthop", "Route",
61 		     "Prio", "Gateway", "Iface");
62 		break;
63 	case SHOW_INTERFACE:
64 		printf("%-15s%-9s%-9s%-7s%s\n", "Interface", "rdomain",
65 		    "Nexthop", "Flags", "Link state");
66 		break;
67 	case SHOW_RIB:
68 		if (res->flags & F_CTL_DETAIL)
69 			break;
70 		printf("flags: "
71 		    "* = Valid, > = Selected, I = via IBGP, A = Announced,\n"
72 		    "       S = Stale, E = Error\n");
73 		printf("origin validation state: "
74 		    "N = not-found, V = valid, ! = invalid\n");
75 		printf("origin: i = IGP, e = EGP, ? = Incomplete\n\n");
76 		printf("%-5s %3s %-20s %-15s  %5s %5s %s\n",
77 		    "flags", "ovs", "destination", "gateway", "lpref", "med",
78 		    "aspath origin");
79 		break;
80 	case SHOW_SET:
81 		printf("%-6s %-34s %7s %7s %6s %11s\n", "Type", "Name",
82 		    "#IPv4", "#IPv6", "#ASnum", "Last Change");
83 		break;
84 	case NETWORK_SHOW:
85 		printf("flags: S = Static\n");
86 		printf("flags prio destination          gateway\n");
87 		break;
88 	default:
89 		break;
90 	}
91 }
92 
93 static void
94 show_summary(struct peer *p)
95 {
96 	char		*s;
97 	const char	*a;
98 	size_t		alen;
99 
100 	s = fmt_peer(p->conf.descr, &p->conf.remote_addr,
101 	    p->conf.remote_masklen);
102 
103 	a = log_as(p->conf.remote_as);
104 	alen = strlen(a);
105 	/* max displayed length of the peers name is 28 */
106 	if (alen < 28) {
107 		if (strlen(s) > 28 - alen)
108 			s[28 - alen] = '\0';
109 	} else
110 		alen = 0;
111 
112 	printf("%-*s %s %10llu %10llu %5u %-8s ",
113 	    (28 - (int)alen), s, a,
114 	    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
115 	    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
116 	    p->stats.msg_rcvd_rrefresh,
117 	    p->stats.msg_sent_open + p->stats.msg_sent_notification +
118 	    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
119 	    p->stats.msg_sent_rrefresh,
120 	    p->wbuf.queued,
121 	    fmt_monotime(p->stats.last_updown));
122 	if (p->state == STATE_ESTABLISHED) {
123 		printf("%6u", p->stats.prefix_cnt);
124 		if (p->conf.max_prefix != 0)
125 			printf("/%u", p->conf.max_prefix);
126 	} else if (p->conf.template)
127 		printf("Template");
128 	else
129 		printf("%s", statenames[p->state]);
130 	printf("\n");
131 	free(s);
132 }
133 
134 static void
135 show_neighbor_capa_mp(struct capabilities *capa)
136 {
137 	int		comma;
138 	u_int8_t	i;
139 
140 	printf("    Multiprotocol extensions: ");
141 	for (i = 0, comma = 0; i < AID_MAX; i++)
142 		if (capa->mp[i]) {
143 			printf("%s%s", comma ? ", " : "", aid2str(i));
144 			comma = 1;
145 		}
146 	printf("\n");
147 }
148 
149 static void
150 show_neighbor_capa_restart(struct capabilities *capa)
151 {
152 	int		comma;
153 	u_int8_t	i;
154 
155 	printf("    Graceful Restart");
156 	if (capa->grestart.timeout)
157 		printf(": Timeout: %d, ", capa->grestart.timeout);
158 	for (i = 0, comma = 0; i < AID_MAX; i++)
159 		if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
160 			if (!comma &&
161 			    capa->grestart.flags[i] & CAPA_GR_RESTART)
162 				printf("restarted, ");
163 			if (comma)
164 				printf(", ");
165 			printf("%s", aid2str(i));
166 			if (capa->grestart.flags[i] & CAPA_GR_FORWARD)
167 				printf(" (preserved)");
168 			comma = 1;
169 		}
170 	printf("\n");
171 }
172 
173 static void
174 show_neighbor_msgstats(struct peer *p)
175 {
176 	printf("  Message statistics:\n");
177 	printf("  %-15s %-10s %-10s\n", "", "Sent", "Received");
178 	printf("  %-15s %10llu %10llu\n", "Opens",
179 	    p->stats.msg_sent_open, p->stats.msg_rcvd_open);
180 	printf("  %-15s %10llu %10llu\n", "Notifications",
181 	    p->stats.msg_sent_notification, p->stats.msg_rcvd_notification);
182 	printf("  %-15s %10llu %10llu\n", "Updates",
183 	    p->stats.msg_sent_update, p->stats.msg_rcvd_update);
184 	printf("  %-15s %10llu %10llu\n", "Keepalives",
185 	    p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive);
186 	printf("  %-15s %10llu %10llu\n", "Route Refresh",
187 	    p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh);
188 	printf("  %-15s %10llu %10llu\n\n", "Total",
189 	    p->stats.msg_sent_open + p->stats.msg_sent_notification +
190 	    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
191 	    p->stats.msg_sent_rrefresh,
192 	    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
193 	    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
194 	    p->stats.msg_rcvd_rrefresh);
195 	printf("  Update statistics:\n");
196 	printf("  %-15s %-10s %-10s\n", "", "Sent", "Received");
197 	printf("  %-15s %10u %10u\n", "Prefixes",
198 	    p->stats.prefix_out_cnt, p->stats.prefix_cnt);
199 	printf("  %-15s %10llu %10llu\n", "Updates",
200 	    p->stats.prefix_sent_update, p->stats.prefix_rcvd_update);
201 	printf("  %-15s %10llu %10llu\n", "Withdraws",
202 	    p->stats.prefix_sent_withdraw, p->stats.prefix_rcvd_withdraw);
203 	printf("  %-15s %10llu %10llu\n", "End-of-Rib",
204 	    p->stats.prefix_sent_eor, p->stats.prefix_rcvd_eor);
205 }
206 
207 static void
208 show_neighbor_full(struct peer *p, struct parse_result *res)
209 {
210 	const char		*errstr;
211 	struct in_addr		 ina;
212 	char			*s;
213 	int			 hascapamp = 0;
214 	u_int8_t		 i;
215 
216 	if ((p->conf.remote_addr.aid == AID_INET &&
217 	    p->conf.remote_masklen != 32) ||
218 	    (p->conf.remote_addr.aid == AID_INET6 &&
219 	    p->conf.remote_masklen != 128)) {
220 		if (asprintf(&s, "%s/%u",
221 		    log_addr(&p->conf.remote_addr),
222 		    p->conf.remote_masklen) == -1)
223 			err(1, NULL);
224 	} else if ((s = strdup(log_addr(&p->conf.remote_addr))) == NULL)
225 			err(1, "strdup");
226 
227 	printf("BGP neighbor is %s, ", s);
228 	free(s);
229 	if (p->conf.remote_as == 0 && p->conf.template)
230 		printf("remote AS: accept any");
231 	else
232 		printf("remote AS %s", log_as(p->conf.remote_as));
233 	if (p->conf.template)
234 		printf(", Template");
235 	if (p->template)
236 		printf(", Cloned");
237 	if (p->conf.passive)
238 		printf(", Passive");
239 	if (p->conf.ebgp && p->conf.distance > 1)
240 		printf(", Multihop (%u)", (int)p->conf.distance);
241 	printf("\n");
242 	if (p->conf.descr[0])
243 		printf(" Description: %s\n", p->conf.descr);
244 	if (p->conf.max_prefix) {
245 		printf(" Max-prefix: %u", p->conf.max_prefix);
246 		if (p->conf.max_prefix_restart)
247 			printf(" (restart %u)",
248 			    p->conf.max_prefix_restart);
249 	}
250 	if (p->conf.max_out_prefix) {
251 		printf(" Max-prefix out: %u", p->conf.max_out_prefix);
252 		if (p->conf.max_out_prefix_restart)
253 			printf(" (restart %u)",
254 			    p->conf.max_out_prefix_restart);
255 	}
256 	if (p->conf.max_prefix || p->conf.max_out_prefix)
257 		printf("\n");
258 
259 	if (p->state == STATE_ESTABLISHED) {
260 		ina.s_addr = p->remote_bgpid;
261 		printf("  BGP version 4, remote router-id %s",
262 		    inet_ntoa(ina));
263 		printf("%s\n", fmt_auth_method(p->auth.method));
264 	}
265 	printf("  BGP state = %s", statenames[p->state]);
266 	if (p->conf.down) {
267 		printf(", marked down");
268 	}
269 	if (p->conf.reason[0]) {
270 		printf(" with shutdown reason \"%s\"",
271 		    log_reason(p->conf.reason));
272 	}
273 	if (p->stats.last_updown != 0)
274 		printf(", %s for %s",
275 		    p->state == STATE_ESTABLISHED ? "up" : "down",
276 		    fmt_monotime(p->stats.last_updown));
277 	printf("\n");
278 	printf("  Last read %s, holdtime %us, keepalive interval %us\n",
279 	    fmt_monotime(p->stats.last_read),
280 	    p->holdtime, p->holdtime/3);
281 	printf("  Last write %s\n", fmt_monotime(p->stats.last_write));
282 	for (i = 0; i < AID_MAX; i++)
283 		if (p->capa.peer.mp[i])
284 			hascapamp = 1;
285 	if (hascapamp || p->capa.peer.refresh ||
286 	    p->capa.peer.grestart.restart || p->capa.peer.as4byte) {
287 		printf("  Neighbor capabilities:\n");
288 		if (hascapamp)
289 			show_neighbor_capa_mp(&p->capa.peer);
290 		if (p->capa.peer.refresh)
291 			printf("    Route Refresh\n");
292 		if (p->capa.peer.grestart.restart)
293 			show_neighbor_capa_restart(&p->capa.peer);
294 		if (p->capa.peer.as4byte)
295 			printf("    4-byte AS numbers\n");
296 	}
297 	for (i = 0; i < AID_MAX; i++)
298 		if (p->capa.neg.mp[i])
299 			hascapamp = 1;
300 	if (hascapamp || p->capa.neg.refresh ||
301 	    p->capa.neg.grestart.restart || p->capa.neg.as4byte) {
302 		printf("  Negotiated capabilities:\n");
303 		if (hascapamp)
304 			show_neighbor_capa_mp(&p->capa.neg);
305 		if (p->capa.neg.refresh)
306 			printf("    Route Refresh\n");
307 		if (p->capa.neg.grestart.restart)
308 			show_neighbor_capa_restart(&p->capa.neg);
309 		if (p->capa.neg.as4byte)
310 			printf("    4-byte AS numbers\n");
311 	}
312 	printf("\n");
313 
314 	if (res->action == SHOW_NEIGHBOR_TIMERS)
315 		return;
316 
317 	show_neighbor_msgstats(p);
318 	printf("\n");
319 	if (p->stats.last_reason[0]) {
320 		printf("  Last received shutdown reason: \"%s\"\n",
321 		    log_reason(p->stats.last_reason));
322 	}
323 
324 	errstr = fmt_errstr(p->stats.last_sent_errcode,
325 	    p->stats.last_sent_suberr);
326 	if (errstr)
327 		printf("  Last error sent: %s\n", errstr);
328 	errstr = fmt_errstr(p->stats.last_rcvd_errcode,
329 	    p->stats.last_rcvd_suberr);
330 	if (errstr)
331 		printf("  Last error received: %s\n", errstr);
332 
333 	if (p->state >= STATE_OPENSENT) {
334 		printf("  Local host:  %20s, Local port:  %5u\n",
335 		    log_addr(&p->local), p->local_port);
336 
337 		printf("  Remote host: %20s, Remote port: %5u\n",
338 		    log_addr(&p->remote), p->remote_port);
339 		printf("\n");
340 	}
341 }
342 
343 static void
344 show_neighbor(struct peer *p, struct parse_result *res)
345 {
346 	char *s;
347 
348 	switch (res->action) {
349 	case SHOW:
350 	case SHOW_SUMMARY:
351 		show_summary(p);
352 		break;
353 	case SHOW_SUMMARY_TERSE:
354 		s = fmt_peer(p->conf.descr, &p->conf.remote_addr,
355 		    p->conf.remote_masklen);
356 		printf("%s %s %s\n", s, log_as(p->conf.remote_as),
357 		    p->conf.template ? "Template" : statenames[p->state]);
358 		free(s);
359 		break;
360 	case SHOW_NEIGHBOR:
361 	case SHOW_NEIGHBOR_TIMERS:
362 		show_neighbor_full(p, res);
363 		break;
364 	case SHOW_NEIGHBOR_TERSE:
365 		s = fmt_peer(NULL, &p->conf.remote_addr,
366 		    p->conf.remote_masklen);
367 		printf("%llu %llu %llu %llu %llu %llu %llu %llu %llu "
368 		    "%llu %u %u %llu %llu %llu %llu %s %s \"%s\"\n",
369 		    p->stats.msg_sent_open, p->stats.msg_rcvd_open,
370 		    p->stats.msg_sent_notification,
371 		    p->stats.msg_rcvd_notification,
372 		    p->stats.msg_sent_update, p->stats.msg_rcvd_update,
373 		    p->stats.msg_sent_keepalive, p->stats.msg_rcvd_keepalive,
374 		    p->stats.msg_sent_rrefresh, p->stats.msg_rcvd_rrefresh,
375 		    p->stats.prefix_cnt, p->conf.max_prefix,
376 		    p->stats.prefix_sent_update, p->stats.prefix_rcvd_update,
377 		    p->stats.prefix_sent_withdraw,
378 		    p->stats.prefix_rcvd_withdraw, s,
379 		    log_as(p->conf.remote_as), p->conf.descr);
380 		free(s);
381 		break;
382 	default:
383 		break;
384 	}
385 }
386 
387 static void
388 show_timer(struct ctl_timer *t)
389 {
390 	printf("  %-20s ", timernames[t->type]);
391 
392 	if (t->val <= 0)
393 		printf("%-20s\n", "due");
394 	else
395 		printf("due in %-13s\n", fmt_timeframe(t->val));
396 }
397 
398 static void
399 show_fib(struct kroute_full *kf)
400 {
401 	char			*p;
402 
403 	if (asprintf(&p, "%s/%u", log_addr(&kf->prefix), kf->prefixlen) == -1)
404 		err(1, NULL);
405 	printf("%s%4i %-20s ", fmt_fib_flags(kf->flags), kf->priority, p);
406 	free(p);
407 
408 	if (kf->flags & F_CONNECTED)
409 		printf("link#%u", kf->ifindex);
410 	else
411 		printf("%s", log_addr(&kf->nexthop));
412 	printf("\n");
413 }
414 
415 static void
416 show_fib_table(struct ktable *kt)
417 {
418 	printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr,
419 	    kt->fib_sync ? "coupled" : "decoupled",
420 	    kt->fib_sync != kt->fib_conf ? "*" : "");
421 }
422 
423 static void
424 show_nexthop(struct ctl_show_nexthop *nh)
425 {
426 	struct kroute		*k;
427 	struct kroute6		*k6;
428 	char			*s;
429 
430 	printf("%s %-15s ", nh->valid ? "*" : " ", log_addr(&nh->addr));
431 	if (!nh->krvalid) {
432 		printf("\n");
433 		return;
434 	}
435 	switch (nh->addr.aid) {
436 	case AID_INET:
437 		k = &nh->kr.kr4;
438 		if (asprintf(&s, "%s/%u", inet_ntoa(k->prefix),
439 		    k->prefixlen) == -1)
440 			err(1, NULL);
441 		printf("%-20s", s);
442 		free(s);
443 		printf("%3i %-15s ", k->priority,
444 		    k->flags & F_CONNECTED ? "connected" :
445 		    inet_ntoa(k->nexthop));
446 		break;
447 	case AID_INET6:
448 		k6 = &nh->kr.kr6;
449 		if (asprintf(&s, "%s/%u", log_in6addr(&k6->prefix),
450 		    k6->prefixlen) == -1)
451 			err(1, NULL);
452 		printf("%-20s", s);
453 		free(s);
454 		printf("%3i %-15s ", k6->priority,
455 		    k6->flags & F_CONNECTED ? "connected" :
456 		    log_in6addr(&k6->nexthop));
457 		break;
458 	default:
459 		printf("unknown address family\n");
460 		return;
461 	}
462 	if (nh->iface.ifname[0]) {
463 		printf("%s (%s, %s)", nh->iface.ifname,
464 		    nh->iface.is_up ? "UP" : "DOWN",
465 		    nh->iface.baudrate ?
466 		    get_baudrate(nh->iface.baudrate, "bps") :
467 		    nh->iface.linkstate);
468 	}
469 	printf("\n");
470 }
471 
472 static void
473 show_interface(struct ctl_show_interface *iface)
474 {
475 	printf("%-15s", iface->ifname);
476 	printf("%-9u", iface->rdomain);
477 	printf("%-9s", iface->nh_reachable ? "ok" : "invalid");
478 	printf("%-7s", iface->is_up ? "UP" : "");
479 
480 	if (iface->media[0])
481 		printf("%s, ", iface->media);
482 	printf("%s", iface->linkstate);
483 
484 	if (iface->baudrate > 0)
485 		printf(", %s", get_baudrate(iface->baudrate, "Bit/s"));
486 	printf("\n");
487 }
488 
489 static void
490 show_communities(u_char *data, size_t len, struct parse_result *res)
491 {
492 	struct community c;
493 	size_t	i;
494 	u_int64_t ext;
495 	u_int8_t type = 0;
496 
497 	if (len % sizeof(c))
498 		return;
499 
500 	for (i = 0; i < len; i += sizeof(c)) {
501 		memcpy(&c, data + i, sizeof(c));
502 
503 		if (type != c.flags) {
504 			if (type != 0)
505 				printf("%c", EOL0(res->flags));
506 			printf("    %s:", fmt_attr(c.flags,
507 			    ATTR_OPTIONAL | ATTR_TRANSITIVE));
508 			type = c.flags;
509 		}
510 
511 		switch (c.flags) {
512 		case COMMUNITY_TYPE_BASIC:
513 			printf(" %s", fmt_community(c.data1, c.data2));
514 			break;
515 		case COMMUNITY_TYPE_LARGE:
516 			printf(" %s",
517 			    fmt_large_community(c.data1, c.data2, c.data3));
518 			break;
519 		case COMMUNITY_TYPE_EXT:
520 			ext = (u_int64_t)c.data3 << 48;
521 			switch (c.data3 >> 8) {
522 			case EXT_COMMUNITY_TRANS_TWO_AS:
523 			case EXT_COMMUNITY_TRANS_OPAQUE:
524 			case EXT_COMMUNITY_TRANS_EVPN:
525 			case EXT_COMMUNITY_NON_TRANS_OPAQUE:
526 				ext |= ((u_int64_t)c.data1 & 0xffff) << 32;
527 				ext |= (u_int64_t)c.data2;
528 				break;
529 			case EXT_COMMUNITY_TRANS_FOUR_AS:
530 			case EXT_COMMUNITY_TRANS_IPV4:
531 				ext |= (u_int64_t)c.data1 << 16;
532 				ext |= (u_int64_t)c.data2 & 0xffff;
533 				break;
534 			}
535 			ext = htobe64(ext);
536 
537 			printf(" %s", fmt_ext_community((void *)&ext));
538 			break;
539 		}
540 	}
541 
542 	printf("%c", EOL0(res->flags));
543 }
544 
545 static void
546 show_community(u_char *data, u_int16_t len)
547 {
548 	u_int16_t	a, v;
549 	u_int16_t	i;
550 
551 	if (len & 0x3) {
552 		printf("bad length");
553 		return;
554 	}
555 
556 	for (i = 0; i < len; i += 4) {
557 		memcpy(&a, data + i, sizeof(a));
558 		memcpy(&v, data + i + 2, sizeof(v));
559 		a = ntohs(a);
560 		v = ntohs(v);
561 		printf("%s", fmt_community(a, v));
562 
563 		if (i + 4 < len)
564 			printf(" ");
565 	}
566 }
567 
568 static void
569 show_large_community(u_char *data, u_int16_t len)
570 {
571 	u_int32_t	a, l1, l2;
572 	u_int16_t	i;
573 
574 	if (len % 12) {
575 		printf("bad length");
576 		return;
577 	}
578 
579 	for (i = 0; i < len; i += 12) {
580 		memcpy(&a, data + i, sizeof(a));
581 		memcpy(&l1, data + i + 4, sizeof(l1));
582 		memcpy(&l2, data + i + 8, sizeof(l2));
583 		a = ntohl(a);
584 		l1 = ntohl(l1);
585 		l2 = ntohl(l2);
586 		printf("%s", fmt_large_community(a, l1, l2));
587 
588 		if (i + 12 < len)
589 			printf(" ");
590 	}
591 }
592 
593 static void
594 show_ext_community(u_char *data, u_int16_t len)
595 {
596 	u_int16_t	i;
597 
598 	if (len & 0x7) {
599 		printf("bad length");
600 		return;
601 	}
602 
603 	for (i = 0; i < len; i += 8) {
604 		printf("%s", fmt_ext_community(data + i));
605 
606 		if (i + 8 < len)
607 			printf(" ");
608 	}
609 }
610 
611 static void
612 show_attr(u_char *data, size_t len, int reqflags)
613 {
614 	u_char		*path;
615 	struct in_addr	 id;
616 	struct bgpd_addr prefix;
617 	char		*aspath;
618 	u_int32_t	 as;
619 	u_int16_t	 alen, ioff, short_as, afi;
620 	u_int8_t	 flags, type, safi, aid, prefixlen;
621 	int		 i, pos, e2, e4;
622 
623 	if (len < 3) {
624 		warnx("Too short BGP attrbute");
625 		return;
626 	}
627 
628 	flags = data[0];
629 	type = data[1];
630 
631 	/* get the attribute length */
632 	if (flags & ATTR_EXTLEN) {
633 		if (len < 4) {
634 			warnx("Too short BGP attrbute");
635 			return;
636 		}
637 		memcpy(&alen, data+2, sizeof(u_int16_t));
638 		alen = ntohs(alen);
639 		data += 4;
640 		len -= 4;
641 	} else {
642 		alen = data[2];
643 		data += 3;
644 		len -= 3;
645 	}
646 
647 	/* bad imsg len how can that happen!? */
648 	if (alen > len) {
649 		warnx("Bad BGP attrbute length");
650 		return;
651 	}
652 
653 	printf("    %s: ", fmt_attr(type, flags));
654 
655 	switch (type) {
656 	case ATTR_ORIGIN:
657 		if (alen == 1)
658 			printf("%s", fmt_origin(*data, 0));
659 		else
660 			printf("bad length");
661 		break;
662 	case ATTR_ASPATH:
663 	case ATTR_AS4_PATH:
664 		/* prefer 4-byte AS here */
665 		e4 = aspath_verify(data, alen, 1, 0);
666 		e2 = aspath_verify(data, alen, 0, 0);
667 		if (e4 == 0 || e4 == AS_ERR_SOFT) {
668 			path = data;
669 		} else if (e2 == 0 || e2 == AS_ERR_SOFT) {
670 			path = aspath_inflate(data, alen, &alen);
671 			if (path == NULL)
672 				errx(1, "aspath_inflate failed");
673 		} else {
674 			printf("bad AS-Path");
675 			break;
676 		}
677 		if (aspath_asprint(&aspath, path, alen) == -1)
678 			err(1, NULL);
679 		printf("%s", aspath);
680 		free(aspath);
681 		if (path != data)
682 			free(path);
683 		break;
684 	case ATTR_NEXTHOP:
685 		if (alen == 4) {
686 			memcpy(&id, data, sizeof(id));
687 			printf("%s", inet_ntoa(id));
688 		} else
689 			printf("bad length");
690 		break;
691 	case ATTR_MED:
692 	case ATTR_LOCALPREF:
693 		if (alen == 4) {
694 			u_int32_t val;
695 			memcpy(&val, data, sizeof(val));
696 			val = ntohl(val);
697 			printf("%u", val);
698 		} else
699 			printf("bad length");
700 		break;
701 	case ATTR_AGGREGATOR:
702 	case ATTR_AS4_AGGREGATOR:
703 		if (alen == 8) {
704 			memcpy(&as, data, sizeof(as));
705 			memcpy(&id, data + sizeof(as), sizeof(id));
706 			as = ntohl(as);
707 		} else if (alen == 6) {
708 			memcpy(&short_as, data, sizeof(short_as));
709 			memcpy(&id, data + sizeof(short_as), sizeof(id));
710 			as = ntohs(short_as);
711 		} else {
712 			printf("bad length");
713 			break;
714 		}
715 		printf("%s [%s]", log_as(as), inet_ntoa(id));
716 		break;
717 	case ATTR_COMMUNITIES:
718 		show_community(data, alen);
719 		break;
720 	case ATTR_ORIGINATOR_ID:
721 		if (alen == 4) {
722 			memcpy(&id, data, sizeof(id));
723 			printf("%s", inet_ntoa(id));
724 		} else
725 			printf("bad length");
726 		break;
727 	case ATTR_CLUSTER_LIST:
728 		for (ioff = 0; ioff + sizeof(id) <= alen;
729 		    ioff += sizeof(id)) {
730 			memcpy(&id, data + ioff, sizeof(id));
731 			printf(" %s", inet_ntoa(id));
732 		}
733 		break;
734 	case ATTR_MP_REACH_NLRI:
735 	case ATTR_MP_UNREACH_NLRI:
736 		if (alen < 3) {
737  bad_len:
738 			printf("bad length");
739 			break;
740 		}
741 		memcpy(&afi, data, 2);
742 		data += 2;
743 		alen -= 2;
744 		afi = ntohs(afi);
745 		safi = *data++;
746 		alen--;
747 
748 		if (afi2aid(afi, safi, &aid) == -1) {
749 			printf("bad AFI/SAFI pair");
750 			break;
751 		}
752 		printf(" %s", aid2str(aid));
753 
754 		if (type == ATTR_MP_REACH_NLRI) {
755 			struct bgpd_addr nexthop;
756 			u_int8_t nhlen;
757 			if (len == 0)
758 				goto bad_len;
759 			nhlen = *data++;
760 			alen--;
761 			if (nhlen > len)
762 				goto bad_len;
763 			memset(&nexthop, 0, sizeof(nexthop));
764 			switch (aid) {
765 			case AID_INET6:
766 				nexthop.aid = aid;
767 				if (nhlen != 16 && nhlen != 32)
768 					goto bad_len;
769 				memcpy(&nexthop.v6.s6_addr, data, 16);
770 				break;
771 			case AID_VPN_IPv4:
772 				if (nhlen != 12)
773 					goto bad_len;
774 				nexthop.aid = AID_INET;
775 				memcpy(&nexthop.v4, data + sizeof(u_int64_t),
776 				    sizeof(nexthop.v4));
777 				break;
778 			case AID_VPN_IPv6:
779 				if (nhlen != 24)
780 					goto bad_len;
781 				nexthop.aid = AID_INET6;
782 				memcpy(&nexthop.v6, data + sizeof(u_int64_t),
783 				    sizeof(nexthop.v6));
784 				break;
785 			default:
786 				printf("unhandled AID #%u", aid);
787 				goto done;
788 			}
789 			/* ignore reserved (old SNPA) field as per RFC4760 */
790 			data += nhlen + 1;
791 			alen -= nhlen + 1;
792 
793 			printf(" nexthop: %s", log_addr(&nexthop));
794 		}
795 
796 		while (alen > 0) {
797 			switch (aid) {
798 			case AID_INET6:
799 				pos = nlri_get_prefix6(data, alen, &prefix,
800 				    &prefixlen);
801 				break;
802 			case AID_VPN_IPv4:
803 				pos = nlri_get_vpn4(data, alen, &prefix,
804 				    &prefixlen, 1);
805 				break;
806 			case AID_VPN_IPv6:
807 				pos = nlri_get_vpn6(data, alen, &prefix,
808 				    &prefixlen, 1);
809 				break;
810 			default:
811 				printf("unhandled AID #%u", aid);
812 				goto done;
813 			}
814 			if (pos == -1) {
815 				printf("bad %s prefix", aid2str(aid));
816 				break;
817 			}
818 			printf(" %s/%u", log_addr(&prefix), prefixlen);
819 			data += pos;
820 			alen -= pos;
821 		}
822 		break;
823 	case ATTR_EXT_COMMUNITIES:
824 		show_ext_community(data, alen);
825 		break;
826 	case ATTR_LARGE_COMMUNITIES:
827 		show_large_community(data, alen);
828 		break;
829 	case ATTR_ATOMIC_AGGREGATE:
830 	default:
831 		printf(" len %u", alen);
832 		if (alen) {
833 			printf(":");
834 			for (i=0; i < alen; i++)
835 				printf(" %02x", *(data+i));
836 		}
837 		break;
838 	}
839  done:
840 	printf("%c", EOL0(reqflags));
841 }
842 
843 static void
844 show_rib_brief(struct ctl_show_rib *r, u_char *asdata, size_t aslen)
845 {
846 	char *p, *aspath;
847 
848 	if (asprintf(&p, "%s/%u", log_addr(&r->prefix), r->prefixlen) == -1)
849 		err(1, NULL);
850 	printf("%s %3s %-20s %-15s %5u %5u ",
851 	    fmt_flags(r->flags, 1), fmt_ovs(r->validation_state, 1), p,
852 	    log_addr(&r->exit_nexthop), r->local_pref, r->med);
853 	free(p);
854 
855 	if (aspath_asprint(&aspath, asdata, aslen) == -1)
856 		err(1, NULL);
857 	if (strlen(aspath) > 0)
858 		printf("%s ", aspath);
859 	free(aspath);
860 
861 	printf("%s\n", fmt_origin(r->origin, 1));
862 }
863 
864 static void
865 show_rib_detail(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
866     int flag0)
867 {
868 	struct in_addr		 id;
869 	char			*aspath, *s;
870 
871 	printf("\nBGP routing table entry for %s/%u%c",
872 	    log_addr(&r->prefix), r->prefixlen,
873 	    EOL0(flag0));
874 
875 	if (aspath_asprint(&aspath, asdata, aslen) == -1)
876 		err(1, NULL);
877 	if (strlen(aspath) > 0)
878 		printf("    %s%c", aspath, EOL0(flag0));
879 	free(aspath);
880 
881 	s = fmt_peer(r->descr, &r->remote_addr, -1);
882 	printf("    Nexthop %s ", log_addr(&r->exit_nexthop));
883 	printf("(via %s) Neighbor %s (", log_addr(&r->true_nexthop), s);
884 	free(s);
885 	id.s_addr = htonl(r->remote_id);
886 	printf("%s)%c", inet_ntoa(id), EOL0(flag0));
887 
888 	printf("    Origin %s, metric %u, localpref %u, weight %u, ovs %s, ",
889 	    fmt_origin(r->origin, 0), r->med, r->local_pref, r->weight,
890 	    fmt_ovs(r->validation_state, 0));
891 	printf("%s", fmt_flags(r->flags, 0));
892 
893 	printf("%c    Last update: %s ago%c", EOL0(flag0),
894 	    fmt_timeframe(r->age), EOL0(flag0));
895 }
896 
897 static void
898 show_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
899     struct parse_result *res)
900 {
901 	if (res->flags & F_CTL_DETAIL)
902 		show_rib_detail(r, asdata, aslen, res->flags);
903 	else
904 		show_rib_brief(r, asdata, aslen);
905 }
906 
907 static void
908 show_rib_mem(struct rde_memstats *stats)
909 {
910 	size_t			pts = 0;
911 	int			i;
912 
913 	printf("RDE memory statistics\n");
914 	for (i = 0; i < AID_MAX; i++) {
915 		if (stats->pt_cnt[i] == 0)
916 			continue;
917 		pts += stats->pt_cnt[i] * pt_sizes[i];
918 		printf("%10lld %s network entries using %s of memory\n",
919 		    stats->pt_cnt[i], aid_vals[i].name,
920 		    fmt_mem(stats->pt_cnt[i] * pt_sizes[i]));
921 	}
922 	printf("%10lld rib entries using %s of memory\n",
923 	    stats->rib_cnt, fmt_mem(stats->rib_cnt *
924 	    sizeof(struct rib_entry)));
925 	printf("%10lld prefix entries using %s of memory\n",
926 	    stats->prefix_cnt, fmt_mem(stats->prefix_cnt *
927 	    sizeof(struct prefix)));
928 	printf("%10lld BGP path attribute entries using %s of memory\n",
929 	    stats->path_cnt, fmt_mem(stats->path_cnt *
930 	    sizeof(struct rde_aspath)));
931 	printf("\t   and holding %lld references\n",
932 	    stats->path_refs);
933 	printf("%10lld BGP AS-PATH attribute entries using "
934 	    "%s of memory\n\t   and holding %lld references\n",
935 	    stats->aspath_cnt, fmt_mem(stats->aspath_size),
936 	    stats->aspath_refs);
937 	printf("%10lld entries for %lld BGP communities "
938 	    "using %s of memory\n", stats->comm_cnt, stats->comm_nmemb,
939 	    fmt_mem(stats->comm_cnt * sizeof(struct rde_community) +
940 	    stats->comm_size * sizeof(struct community)));
941 	printf("\t   and holding %lld references\n",
942 	    stats->comm_refs);
943 	printf("%10lld BGP attributes entries using %s of memory\n",
944 	    stats->attr_cnt, fmt_mem(stats->attr_cnt *
945 	    sizeof(struct attr)));
946 	printf("\t   and holding %lld references\n",
947 	    stats->attr_refs);
948 	printf("%10lld BGP attributes using %s of memory\n",
949 	    stats->attr_dcnt, fmt_mem(stats->attr_data));
950 	printf("%10lld as-set elements in %lld tables using "
951 	    "%s of memory\n", stats->aset_nmemb, stats->aset_cnt,
952 	    fmt_mem(stats->aset_size));
953 	printf("%10lld prefix-set elements using %s of memory\n",
954 	    stats->pset_cnt, fmt_mem(stats->pset_size));
955 	printf("RIB using %s of memory\n", fmt_mem(pts +
956 	    stats->prefix_cnt * sizeof(struct prefix) +
957 	    stats->rib_cnt * sizeof(struct rib_entry) +
958 	    stats->path_cnt * sizeof(struct rde_aspath) +
959 	    stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
960 	    stats->attr_data));
961 	printf("Sets using %s of memory\n", fmt_mem(stats->aset_size +
962 	    stats->pset_size));
963 	printf("\nRDE hash statistics\n");
964 }
965 
966 static void
967 show_rib_hash(struct rde_hashstats *hash)
968 {
969 	double avg, dev;
970 
971 	printf("\t%s: size %lld, %lld entries\n", hash->name, hash->num,
972 	    hash->sum);
973 	avg = (double)hash->sum / (double)hash->num;
974 	dev = sqrt(fmax(0, hash->sumq / hash->num - avg * avg));
975 	printf("\t    min %lld max %lld avg/std-dev = %.3f/%.3f\n",
976 	    hash->min, hash->max, avg, dev);
977 }
978 
979 static void
980 show_rib_set(struct ctl_show_set *set)
981 {
982 	char buf[64];
983 
984 	if (set->type == ASNUM_SET)
985 		snprintf(buf, sizeof(buf), "%7s %7s %6zu",
986 		    "-", "-", set->as_cnt);
987 	else
988 		snprintf(buf, sizeof(buf), "%7zu %7zu %6s",
989 		    set->v4_cnt, set->v6_cnt, "-");
990 
991 	printf("%-6s %-34s %s %11s\n", fmt_set_type(set), set->name,
992 	    buf, fmt_monotime(set->lastchange));
993 }
994 
995 static void
996 show_rtr(struct ctl_show_rtr *rtr)
997 {
998 	static int not_first;
999 
1000 	if (not_first)
1001 		printf("\n");
1002 	not_first = 1;
1003 
1004 	printf("RTR neighbor is %s, port %u\n",
1005 	    log_addr(&rtr->remote_addr), rtr->remote_port);
1006 	if (rtr->descr[0])
1007 		printf(" Description: %s\n", rtr->descr);
1008 	if (rtr->local_addr.aid != AID_UNSPEC)
1009 		printf(" Local Address: %s\n", log_addr(&rtr->local_addr));
1010 	if (rtr->session_id != -1)
1011 		printf (" Session ID: %d Serial #: %u\n",
1012 		    rtr->session_id, rtr->serial);
1013 	printf(" Refresh: %u, Retry: %u, Expire: %u\n",
1014 	    rtr->refresh, rtr->retry, rtr->expire);
1015 
1016 	if (rtr->last_sent_error != NO_ERROR) {
1017 		printf(" Last sent error: %s\n",
1018 		  log_rtr_error(rtr->last_sent_error));
1019 		if (rtr->last_sent_msg[0])
1020 			printf(" with reason \"%s\"",
1021 			    log_reason(rtr->last_sent_msg));
1022 	}
1023 	if (rtr->last_recv_error != NO_ERROR) {
1024 		printf("Last received error: %s\n",
1025 		  log_rtr_error(rtr->last_recv_error));
1026 		if (rtr->last_recv_msg[0])
1027 			printf(" with reason \"%s\"",
1028 			    log_reason(rtr->last_recv_msg));
1029 	}
1030 
1031 	printf("\n");
1032 }
1033 
1034 static void
1035 show_result(u_int rescode)
1036 {
1037 	if (rescode == 0)
1038 		printf("request processed\n");
1039 	else if (rescode >=
1040 	    sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0]))
1041 		printf("unknown result error code %u\n", rescode);
1042 	else
1043 		printf("%s\n", ctl_res_strerror[rescode]);
1044 }
1045 
1046 static void
1047 show_tail(void)
1048 {
1049 	/* nothing */
1050 }
1051 
1052 const struct output show_output = {
1053 	.head = show_head,
1054 	.neighbor = show_neighbor,
1055 	.timer = show_timer,
1056 	.fib = show_fib,
1057 	.fib_table = show_fib_table,
1058 	.nexthop = show_nexthop,
1059 	.interface = show_interface,
1060 	.communities = show_communities,
1061 	.attr = show_attr,
1062 	.rib = show_rib,
1063 	.rib_mem = show_rib_mem,
1064 	.rib_hash = show_rib_hash,
1065 	.set = show_rib_set,
1066 	.rtr = show_rtr,
1067 	.result = show_result,
1068 	.tail = show_tail
1069 };
1070