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