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