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