xref: /openbsd-src/usr.sbin/bgpd/printconf.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: printconf.c,v 1.167 2023/04/28 13:23:52 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2016 Job Snijders <job@instituut.net>
6  * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA, PROFITS OR MIND, WHETHER IN
17  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
18  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "bgpd.h"
27 #include "session.h"
28 #include "rde.h"
29 #include "log.h"
30 
31 void		 print_prefix(struct filter_prefix *p);
32 const char	*community_type(struct community *c);
33 void		 print_community(struct community *c);
34 void		 print_origin(uint8_t);
35 void		 print_set(struct filter_set_head *);
36 void		 print_mainconf(struct bgpd_config *);
37 void		 print_l3vpn_targets(struct filter_set_head *, const char *);
38 void		 print_l3vpn(struct l3vpn *);
39 const char	*print_af(uint8_t);
40 void		 print_network(struct network_config *, const char *);
41 void		 print_flowspec(struct flowspec_config *, const char *);
42 void		 print_as_sets(struct as_set_head *);
43 void		 print_prefixsets(struct prefixset_head *);
44 void		 print_originsets(struct prefixset_head *);
45 void		 print_roa(struct roa_tree *);
46 void		 print_aspa(struct aspa_tree *);
47 void		 print_rtrs(struct rtr_config_head *);
48 void		 print_peer(struct peer_config *, struct bgpd_config *,
49 		    const char *);
50 const char	*print_auth_alg(enum auth_alg);
51 const char	*print_enc_alg(enum auth_enc_alg);
52 void		 print_announce(struct peer_config *, const char *);
53 void		 print_as(struct filter_rule *);
54 void		 print_rule(struct bgpd_config *, struct filter_rule *);
55 const char	*mrt_type(enum mrt_type);
56 void		 print_mrt(struct bgpd_config *, uint32_t, uint32_t,
57 		    const char *, const char *);
58 void		 print_groups(struct bgpd_config *);
59 int		 peer_compare(const void *, const void *);
60 
61 void
62 print_prefix(struct filter_prefix *p)
63 {
64 	uint8_t max_len = 0;
65 
66 	switch (p->addr.aid) {
67 	case AID_INET:
68 	case AID_VPN_IPv4:
69 		max_len = 32;
70 		break;
71 	case AID_INET6:
72 	case AID_VPN_IPv6:
73 		max_len = 128;
74 		break;
75 	case AID_UNSPEC:
76 		/* no prefix to print */
77 		return;
78 	}
79 
80 	printf("%s/%u", log_addr(&p->addr), p->len);
81 
82 	switch (p->op) {
83 	case OP_NONE:
84 		break;
85 	case OP_NE:
86 		printf(" prefixlen != %u", p->len_min);
87 		break;
88 	case OP_XRANGE:
89 		printf(" prefixlen %u >< %u ", p->len_min, p->len_max);
90 		break;
91 	case OP_RANGE:
92 		if (p->len_min == p->len_max && p->len != p->len_min)
93 			printf(" prefixlen = %u", p->len_min);
94 		else if (p->len == p->len_min && p->len_max == max_len)
95 			printf(" or-longer");
96 		else if (p->len == p->len_min && p->len != p->len_max)
97 			printf(" maxlen %u", p->len_max);
98 		else if (p->len_max == max_len)
99 			printf(" prefixlen >= %u", p->len_min);
100 		else
101 			printf(" prefixlen %u - %u", p->len_min, p->len_max);
102 		break;
103 	default:
104 		printf(" prefixlen %u ??? %u", p->len_min, p->len_max);
105 		break;
106 	}
107 }
108 
109 const char *
110 community_type(struct community *c)
111 {
112 	switch ((uint8_t)c->flags) {
113 	case COMMUNITY_TYPE_BASIC:
114 		return "community";
115 	case COMMUNITY_TYPE_LARGE:
116 		return "large-community";
117 	case COMMUNITY_TYPE_EXT:
118 		return "ext-community";
119 	default:
120 		return "???";
121 	}
122 }
123 
124 void
125 print_community(struct community *c)
126 {
127 	struct in_addr addr;
128 	int type;
129 	uint8_t subtype;
130 
131 	switch ((uint8_t)c->flags) {
132 	case COMMUNITY_TYPE_BASIC:
133 		switch ((c->flags >> 8) & 0xff) {
134 		case COMMUNITY_ANY:
135 			printf("*:");
136 			break;
137 		case COMMUNITY_NEIGHBOR_AS:
138 			printf("neighbor-as:");
139 			break;
140 		case COMMUNITY_LOCAL_AS:
141 			printf("local-as:");
142 			break;
143 		default:
144 			printf("%u:", c->data1);
145 			break;
146 		}
147 		switch ((c->flags >> 16) & 0xff) {
148 		case COMMUNITY_ANY:
149 			printf("* ");
150 			break;
151 		case COMMUNITY_NEIGHBOR_AS:
152 			printf("neighbor-as ");
153 			break;
154 		case COMMUNITY_LOCAL_AS:
155 			printf("local-as ");
156 			break;
157 		default:
158 			printf("%u ", c->data2);
159 			break;
160 		}
161 		break;
162 	case COMMUNITY_TYPE_LARGE:
163 		switch ((c->flags >> 8) & 0xff) {
164 		case COMMUNITY_ANY:
165 			printf("*:");
166 			break;
167 		case COMMUNITY_NEIGHBOR_AS:
168 			printf("neighbor-as:");
169 			break;
170 		case COMMUNITY_LOCAL_AS:
171 			printf("local-as:");
172 			break;
173 		default:
174 			printf("%u:", c->data1);
175 			break;
176 		}
177 		switch ((c->flags >> 16) & 0xff) {
178 		case COMMUNITY_ANY:
179 			printf("*:");
180 			break;
181 		case COMMUNITY_NEIGHBOR_AS:
182 			printf("neighbor-as:");
183 			break;
184 		case COMMUNITY_LOCAL_AS:
185 			printf("local-as:");
186 			break;
187 		default:
188 			printf("%u:", c->data2);
189 			break;
190 		}
191 		switch ((c->flags >> 24) & 0xff) {
192 		case COMMUNITY_ANY:
193 			printf("* ");
194 			break;
195 		case COMMUNITY_NEIGHBOR_AS:
196 			printf("neighbor-as ");
197 			break;
198 		case COMMUNITY_LOCAL_AS:
199 			printf("local-as ");
200 			break;
201 		default:
202 			printf("%u ", c->data3);
203 			break;
204 		}
205 		break;
206 	case COMMUNITY_TYPE_EXT:
207 		if ((c->flags >> 24 & 0xff) == COMMUNITY_ANY) {
208 			printf("* * ");
209 			break;
210 		}
211 		type = (int32_t)c->data3 >> 8;
212 		subtype = c->data3;
213 		printf("%s ", log_ext_subtype(type, subtype));
214 		if ((c->flags >> 8 & 0xff) == COMMUNITY_ANY) {
215 			printf("* ");
216 			break;
217 		}
218 
219 		switch (type) {
220 		case EXT_COMMUNITY_TRANS_TWO_AS:
221 		case EXT_COMMUNITY_TRANS_FOUR_AS:
222 		case EXT_COMMUNITY_GEN_TWO_AS:
223 		case EXT_COMMUNITY_GEN_FOUR_AS:
224 			if ((c->flags >> 8 & 0xff) == COMMUNITY_NEIGHBOR_AS)
225 				printf("neighbor-as:");
226 			else if ((c->flags >> 8 & 0xff) == COMMUNITY_LOCAL_AS)
227 				printf("local-as:");
228 			else
229 				printf("%s:", log_as(c->data1));
230 			break;
231 		case EXT_COMMUNITY_TRANS_IPV4:
232 		case EXT_COMMUNITY_GEN_IPV4:
233 			addr.s_addr = htonl(c->data1);
234 			printf("%s:", inet_ntoa(addr));
235 			break;
236 		}
237 
238 		switch (type) {
239 		case EXT_COMMUNITY_TRANS_TWO_AS:
240 		case EXT_COMMUNITY_TRANS_FOUR_AS:
241 		case EXT_COMMUNITY_TRANS_IPV4:
242 		case EXT_COMMUNITY_GEN_TWO_AS:
243 		case EXT_COMMUNITY_GEN_FOUR_AS:
244 		case EXT_COMMUNITY_GEN_IPV4:
245 			if ((c->flags >> 16 & 0xff) == COMMUNITY_ANY)
246 				printf("* ");
247 			else if ((c->flags >> 16 & 0xff) ==
248 			    COMMUNITY_NEIGHBOR_AS)
249 				printf("neighbor-as ");
250 			else if ((c->flags >> 16 & 0xff) == COMMUNITY_LOCAL_AS)
251 				printf("local-as ");
252 			else
253 				printf("%u ", c->data2);
254 			break;
255 		case EXT_COMMUNITY_NON_TRANS_OPAQUE:
256 			if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
257 				switch (c->data2) {
258 				case EXT_COMMUNITY_OVS_VALID:
259 					printf("valid ");
260 					break;
261 				case EXT_COMMUNITY_OVS_NOTFOUND:
262 					printf("not-found ");
263 					break;
264 				case EXT_COMMUNITY_OVS_INVALID:
265 					printf("invalid ");
266 					break;
267 				}
268 				break;
269 			}
270 			printf("0x%x%08x ", c->data1 & 0xffff, c->data2);
271 			break;
272 		case EXT_COMMUNITY_TRANS_OPAQUE:
273 		case EXT_COMMUNITY_TRANS_EVPN:
274 		default:
275 			printf("0x%x%08x ", c->data1 & 0xffff, c->data2);
276 			break;
277 		}
278 	}
279 }
280 
281 void
282 print_origin(uint8_t o)
283 {
284 	if (o == ORIGIN_IGP)
285 		printf("igp ");
286 	else if (o == ORIGIN_EGP)
287 		printf("egp ");
288 	else if (o == ORIGIN_INCOMPLETE)
289 		printf("incomplete ");
290 	else
291 		printf("%u ", o);
292 }
293 
294 void
295 print_set(struct filter_set_head *set)
296 {
297 	struct filter_set	*s;
298 
299 	if (TAILQ_EMPTY(set))
300 		return;
301 
302 	printf("set { ");
303 	TAILQ_FOREACH(s, set, entry) {
304 		switch (s->type) {
305 		case ACTION_SET_LOCALPREF:
306 			printf("localpref %u ", s->action.metric);
307 			break;
308 		case ACTION_SET_RELATIVE_LOCALPREF:
309 			printf("localpref %+d ", s->action.relative);
310 			break;
311 		case ACTION_SET_MED:
312 			printf("metric %u ", s->action.metric);
313 			break;
314 		case ACTION_SET_RELATIVE_MED:
315 			printf("metric %+d ", s->action.relative);
316 			break;
317 		case ACTION_SET_WEIGHT:
318 			printf("weight %u ", s->action.metric);
319 			break;
320 		case ACTION_SET_RELATIVE_WEIGHT:
321 			printf("weight %+d ", s->action.relative);
322 			break;
323 		case ACTION_SET_NEXTHOP:
324 			printf("nexthop %s ", log_addr(&s->action.nexthop));
325 			break;
326 		case ACTION_SET_NEXTHOP_REJECT:
327 			printf("nexthop reject ");
328 			break;
329 		case ACTION_SET_NEXTHOP_BLACKHOLE:
330 			printf("nexthop blackhole ");
331 			break;
332 		case ACTION_SET_NEXTHOP_NOMODIFY:
333 			printf("nexthop no-modify ");
334 			break;
335 		case ACTION_SET_NEXTHOP_SELF:
336 			printf("nexthop self ");
337 			break;
338 		case ACTION_SET_PREPEND_SELF:
339 			printf("prepend-self %u ", s->action.prepend);
340 			break;
341 		case ACTION_SET_PREPEND_PEER:
342 			printf("prepend-neighbor %u ", s->action.prepend);
343 			break;
344 		case ACTION_SET_AS_OVERRIDE:
345 			printf("as-override ");
346 			break;
347 		case ACTION_DEL_COMMUNITY:
348 			printf("%s delete ",
349 			    community_type(&s->action.community));
350 			print_community(&s->action.community);
351 			break;
352 		case ACTION_SET_COMMUNITY:
353 			printf("%s ", community_type(&s->action.community));
354 			print_community(&s->action.community);
355 			break;
356 		case ACTION_PFTABLE:
357 			printf("pftable %s ", s->action.pftable);
358 			break;
359 		case ACTION_RTLABEL:
360 			printf("rtlabel %s ", s->action.rtlabel);
361 			break;
362 		case ACTION_SET_ORIGIN:
363 			printf("origin ");
364 			print_origin(s->action.origin);
365 			break;
366 		case ACTION_RTLABEL_ID:
367 		case ACTION_PFTABLE_ID:
368 		case ACTION_SET_NEXTHOP_REF:
369 			/* not possible */
370 			printf("king bula saiz: config broken");
371 			break;
372 		}
373 	}
374 	printf("}");
375 }
376 
377 void
378 print_mainconf(struct bgpd_config *conf)
379 {
380 	struct in_addr		 ina;
381 	struct listen_addr	*la;
382 
383 	printf("AS %s", log_as(conf->as));
384 	if (conf->as > USHRT_MAX && conf->short_as != AS_TRANS)
385 		printf(" %u", conf->short_as);
386 	ina.s_addr = conf->bgpid;
387 	printf("\nrouter-id %s\n", inet_ntoa(ina));
388 
389 	printf("socket \"%s\"\n", conf->csock);
390 	if (conf->rcsock)
391 		printf("socket \"%s\" restricted\n", conf->rcsock);
392 	if (conf->holdtime != INTERVAL_HOLD)
393 		printf("holdtime %u\n", conf->holdtime);
394 	if (conf->min_holdtime != MIN_HOLDTIME)
395 		printf("holdtime min %u\n", conf->min_holdtime);
396 	if (conf->connectretry != INTERVAL_CONNECTRETRY)
397 		printf("connect-retry %u\n", conf->connectretry);
398 
399 	if (conf->flags & BGPD_FLAG_DECISION_ROUTEAGE)
400 		printf("rde route-age evaluate\n");
401 	if (conf->flags & BGPD_FLAG_DECISION_MED_ALWAYS)
402 		printf("rde med compare always\n");
403 	if (conf->flags & BGPD_FLAG_DECISION_ALL_PATHS)
404 		printf("rde evaluate all\n");
405 
406 	if (conf->flags & BGPD_FLAG_NO_AS_SET)
407 		printf("reject as-set yes\n");
408 
409 	if (conf->log & BGPD_LOG_UPDATES)
410 		printf("log updates\n");
411 
412 	TAILQ_FOREACH(la, conf->listen_addrs, entry) {
413 		struct bgpd_addr addr;
414 		uint16_t port;
415 
416 		sa2addr((struct sockaddr *)&la->sa, &addr, &port);
417 		printf("listen on %s",
418 		    log_sockaddr((struct sockaddr *)&la->sa, la->sa_len));
419 		if (port != BGP_PORT)
420 			printf(" port %hu", port);
421 		printf("\n");
422 	}
423 
424 	if (conf->flags & BGPD_FLAG_NEXTHOP_BGP)
425 		printf("nexthop qualify via bgp\n");
426 	if (conf->flags & BGPD_FLAG_NEXTHOP_DEFAULT)
427 		printf("nexthop qualify via default\n");
428 	if (conf->fib_priority != kr_default_prio())
429 		printf("fib-priority %hhu\n", conf->fib_priority);
430 	printf("\n");
431 }
432 
433 void
434 print_l3vpn_targets(struct filter_set_head *set, const char *tgt)
435 {
436 	struct filter_set	*s;
437 	TAILQ_FOREACH(s, set, entry) {
438 		printf("\t%s ", tgt);
439 		print_community(&s->action.community);
440 		printf("\n");
441 	}
442 }
443 
444 void
445 print_l3vpn(struct l3vpn *vpn)
446 {
447 	struct network *n;
448 
449 	printf("vpn \"%s\" on %s {\n", vpn->descr, vpn->ifmpe);
450 	printf("\t%s\n", log_rd(vpn->rd));
451 
452 	print_l3vpn_targets(&vpn->export, "export-target");
453 	print_l3vpn_targets(&vpn->import, "import-target");
454 
455 	if (vpn->flags & F_RIB_NOFIBSYNC)
456 		printf("\tfib-update no\n");
457 	else
458 		printf("\tfib-update yes\n");
459 
460 	TAILQ_FOREACH(n, &vpn->net_l, entry)
461 		print_network(&n->net, "\t");
462 
463 	printf("}\n");
464 }
465 
466 const char *
467 print_af(uint8_t aid)
468 {
469 	/*
470 	 * Hack around the fact that aid2str() will return "IPv4 unicast"
471 	 * for AID_INET. AID_INET, AID_INET6 and the flowspec AID need
472 	 * special handling and the other AID should never end up here.
473 	 */
474 	if (aid == AID_INET || aid == AID_FLOWSPECv4)
475 		return ("inet");
476 	if (aid == AID_INET6 || aid == AID_FLOWSPECv6)
477 		return ("inet6");
478 	return (aid2str(aid));
479 }
480 
481 void
482 print_network(struct network_config *n, const char *c)
483 {
484 	switch (n->type) {
485 	case NETWORK_STATIC:
486 		printf("%snetwork %s static", c, print_af(n->prefix.aid));
487 		break;
488 	case NETWORK_CONNECTED:
489 		printf("%snetwork %s connected", c, print_af(n->prefix.aid));
490 		break;
491 	case NETWORK_RTLABEL:
492 		printf("%snetwork %s rtlabel \"%s\"", c,
493 		    print_af(n->prefix.aid), rtlabel_id2name(n->rtlabel));
494 		break;
495 	case NETWORK_PRIORITY:
496 		printf("%snetwork %s priority %d", c,
497 		    print_af(n->prefix.aid), n->priority);
498 		break;
499 	case NETWORK_PREFIXSET:
500 		printf("%snetwork prefix-set %s", c, n->psname);
501 		break;
502 	default:
503 		printf("%snetwork %s/%u", c, log_addr(&n->prefix),
504 		    n->prefixlen);
505 		break;
506 	}
507 	if (!TAILQ_EMPTY(&n->attrset))
508 		printf(" ");
509 	print_set(&n->attrset);
510 	printf("\n");
511 }
512 
513 static void
514 print_flowspec_list(struct flowspec *f, int type, int is_v6)
515 {
516 	const uint8_t *comp;
517 	const char *fmt;
518 	int complen, off = 0;
519 
520 	if (flowspec_get_component(f->data, f->len, type, is_v6,
521 	    &comp, &complen) != 1)
522 		return;
523 
524 	printf("%s ", flowspec_fmt_label(type));
525 	fmt = flowspec_fmt_num_op(comp, complen, &off);
526 	if (off == -1) {
527 		printf("%s ", fmt);
528 	} else {
529 		printf("{ %s ", fmt);
530 		do {
531 			fmt = flowspec_fmt_num_op(comp, complen, &off);
532 			printf("%s ", fmt);
533 		} while (off != -1);
534 		printf("} ");
535 	}
536 }
537 
538 static void
539 print_flowspec_flags(struct flowspec *f, int type, int is_v6)
540 {
541 	const uint8_t *comp;
542 	const char *fmt, *flags;
543 	int complen, off = 0;
544 
545 	switch (type) {
546 	case FLOWSPEC_TYPE_TCP_FLAGS:
547 		flags = FLOWSPEC_TCP_FLAG_STRING;
548 		break;
549 	case FLOWSPEC_TYPE_FRAG:
550 		if (!is_v6)
551 			flags = FLOWSPEC_FRAG_STRING4;
552 		else
553 			flags = FLOWSPEC_FRAG_STRING6;
554 		break;
555 	default:
556 		printf("??? ");
557 		return;
558 	}
559 
560 	if (flowspec_get_component(f->data, f->len, type, is_v6,
561 	    &comp, &complen) != 1)
562 		return;
563 
564 	printf("%s ", flowspec_fmt_label(type));
565 
566 	fmt = flowspec_fmt_bin_op(comp, complen, &off, flags);
567 	if (off == -1) {
568 		printf("%s ", fmt);
569 	} else {
570 		printf("{ %s ", fmt);
571 		do {
572 			fmt = flowspec_fmt_bin_op(comp, complen, &off, flags);
573 			printf("%s ", fmt);
574 		} while (off != -1);
575 		printf("} ");
576 	}
577 }
578 
579 static void
580 print_flowspec_addr(struct flowspec *f, int type, int is_v6)
581 {
582 	struct bgpd_addr addr;
583 	uint8_t plen;
584 
585 	flowspec_get_addr(f->data, f->len, type, is_v6, &addr, &plen, NULL);
586 	if (plen == 0)
587 		printf("%s any ", flowspec_fmt_label(type));
588 	else
589 		printf("%s %s/%u ", flowspec_fmt_label(type),
590 		    log_addr(&addr), plen);
591 }
592 
593 void
594 print_flowspec(struct flowspec_config *fconf, const char *c)
595 {
596 	struct flowspec *f = fconf->flow;
597 	int is_v6 = (f->aid == AID_FLOWSPECv6);
598 
599 	printf("%sflowspec %s ", c, print_af(f->aid));
600 
601 	print_flowspec_list(f, FLOWSPEC_TYPE_PROTO, is_v6);
602 
603 	print_flowspec_addr(f, FLOWSPEC_TYPE_SOURCE, is_v6);
604 	print_flowspec_list(f, FLOWSPEC_TYPE_SRC_PORT, is_v6);
605 
606 	print_flowspec_addr(f, FLOWSPEC_TYPE_DEST, is_v6);
607 	print_flowspec_list(f, FLOWSPEC_TYPE_DST_PORT, is_v6);
608 
609 	print_flowspec_list(f, FLOWSPEC_TYPE_DSCP, is_v6);
610 	print_flowspec_list(f, FLOWSPEC_TYPE_PKT_LEN, is_v6);
611 	print_flowspec_flags(f, FLOWSPEC_TYPE_TCP_FLAGS, is_v6);
612 	print_flowspec_flags(f, FLOWSPEC_TYPE_FRAG, is_v6);
613 
614 	/* TODO: fixup the code handling to be like in the parser */
615 	print_flowspec_list(f, FLOWSPEC_TYPE_ICMP_TYPE, is_v6);
616 	print_flowspec_list(f, FLOWSPEC_TYPE_ICMP_CODE, is_v6);
617 
618 	print_set(&fconf->attrset);
619 	printf("\n");
620 }
621 
622 void
623 print_as_sets(struct as_set_head *as_sets)
624 {
625 	struct as_set *aset;
626 	uint32_t *as;
627 	size_t i, n;
628 	int len;
629 
630 	SIMPLEQ_FOREACH(aset, as_sets, entry) {
631 		printf("as-set \"%s\" {\n\t", aset->name);
632 		as = set_get(aset->set, &n);
633 		for (i = 0, len = 8; i < n; i++) {
634 			if (len > 72) {
635 				printf("\n\t");
636 				len = 8;
637 			}
638 			len += printf("%u ", as[i]);
639 		}
640 		printf("\n}\n\n");
641 	}
642 }
643 
644 void
645 print_prefixsets(struct prefixset_head *psh)
646 {
647 	struct prefixset	*ps;
648 	struct prefixset_item	*psi;
649 
650 	SIMPLEQ_FOREACH(ps, psh, entry) {
651 		int count = 0;
652 		printf("prefix-set \"%s\" {", ps->name);
653 		RB_FOREACH(psi, prefixset_tree, &ps->psitems) {
654 			if (count++ % 2 == 0)
655 				printf("\n\t");
656 			else
657 				printf(", ");
658 			print_prefix(&psi->p);
659 		}
660 		printf("\n}\n\n");
661 	}
662 }
663 
664 void
665 print_originsets(struct prefixset_head *psh)
666 {
667 	struct prefixset	*ps;
668 	struct roa		*roa;
669 	struct bgpd_addr	 addr;
670 
671 	SIMPLEQ_FOREACH(ps, psh, entry) {
672 		printf("origin-set \"%s\" {", ps->name);
673 		RB_FOREACH(roa, roa_tree, &ps->roaitems) {
674 			printf("\n\t");
675 			addr.aid = roa->aid;
676 			addr.v6 = roa->prefix.inet6;
677 			printf("%s/%u", log_addr(&addr), roa->prefixlen);
678 			if (roa->prefixlen != roa->maxlen)
679 				printf(" maxlen %u", roa->maxlen);
680 			printf(" source-as %u", roa->asnum);
681 		}
682 		printf("\n}\n\n");
683 	}
684 }
685 
686 void
687 print_roa(struct roa_tree *r)
688 {
689 	struct roa	*roa;
690 	struct bgpd_addr addr;
691 
692 	if (RB_EMPTY(r))
693 		return;
694 
695 	printf("roa-set {");
696 	RB_FOREACH(roa, roa_tree, r) {
697 		printf("\n\t");
698 		addr.aid = roa->aid;
699 		addr.v6 = roa->prefix.inet6;
700 		printf("%s/%u", log_addr(&addr), roa->prefixlen);
701 		if (roa->prefixlen != roa->maxlen)
702 			printf(" maxlen %u", roa->maxlen);
703 		printf(" source-as %u", roa->asnum);
704 		if (roa->expires != 0)
705 			printf(" expires %lld", (long long)roa->expires);
706 	}
707 	printf("\n}\n\n");
708 }
709 
710 void
711 print_aspa(struct aspa_tree *a)
712 {
713 	struct aspa_set	*aspa;
714 	uint32_t i;
715 
716 	if (RB_EMPTY(a))
717 		return;
718 
719 	printf("aspa-set {");
720 	RB_FOREACH(aspa, aspa_tree, a) {
721 		printf("\n\t");
722 		printf("customer-as %s", log_as(aspa->as));
723 		if (aspa->expires != 0)
724 			printf(" expires %lld", (long long)aspa->expires);
725 		printf(" provider-as { ");
726 		for (i = 0; i < aspa->num; i++) {
727 			printf("%s ", log_as(aspa->tas[i]));
728 			if (aspa->tas_aid[i] != AID_UNSPEC)
729 				printf("%s ", print_af(aspa->tas_aid[i]));
730 		}
731 		printf("}");
732 	}
733 	printf("\n}\n\n");
734 }
735 
736 void
737 print_rtrs(struct rtr_config_head *rh)
738 {
739 	struct rtr_config *r;
740 
741 	SIMPLEQ_FOREACH(r, rh, entry) {
742 		printf("rtr %s {\n", log_addr(&r->remote_addr));
743 		printf("\tdescr \"%s\"\n", r->descr);
744 		printf("\tport %u\n", r->remote_port);
745 		if (r->local_addr.aid != AID_UNSPEC)
746 			printf("local-addr %s\n", log_addr(&r->local_addr));
747 		printf("}\n\n");
748 	}
749 }
750 
751 void
752 print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
753 {
754 	char		*method;
755 	struct in_addr	 ina;
756 
757 	if ((p->remote_addr.aid == AID_INET && p->remote_masklen != 32) ||
758 	    (p->remote_addr.aid == AID_INET6 && p->remote_masklen != 128))
759 		printf("%sneighbor %s/%u {\n", c, log_addr(&p->remote_addr),
760 		    p->remote_masklen);
761 	else
762 		printf("%sneighbor %s {\n", c, log_addr(&p->remote_addr));
763 	if (p->descr[0])
764 		printf("%s\tdescr \"%s\"\n", c, p->descr);
765 	if (p->rib[0])
766 		printf("%s\trib \"%s\"\n", c, p->rib);
767 	if (p->remote_as)
768 		printf("%s\tremote-as %s\n", c, log_as(p->remote_as));
769 	if (p->local_as != conf->as) {
770 		printf("%s\tlocal-as %s", c, log_as(p->local_as));
771 		if (p->local_as > USHRT_MAX && p->local_short_as != AS_TRANS)
772 			printf(" %u", p->local_short_as);
773 		printf("\n");
774 	}
775 	if (p->down)
776 		printf("%s\tdown\n", c);
777 	if (p->distance > 1)
778 		printf("%s\tmultihop %u\n", c, p->distance);
779 	if (p->passive)
780 		printf("%s\tpassive\n", c);
781 	if (p->local_addr_v4.aid)
782 		printf("%s\tlocal-address %s\n", c,
783 		    log_addr(&p->local_addr_v4));
784 	if (p->local_addr_v6.aid)
785 		printf("%s\tlocal-address %s\n", c,
786 		    log_addr(&p->local_addr_v6));
787 	if (p->remote_port != BGP_PORT)
788 		printf("%s\tport %hu\n", c, p->remote_port);
789 	if (p->role != ROLE_NONE)
790 		printf("%s\trole %s\n", c, log_policy(p->role));
791 	if (p->max_prefix) {
792 		printf("%s\tmax-prefix %u", c, p->max_prefix);
793 		if (p->max_prefix_restart)
794 			printf(" restart %u", p->max_prefix_restart);
795 		printf("\n");
796 	}
797 	if (p->max_out_prefix) {
798 		printf("%s\tmax-prefix %u out", c, p->max_out_prefix);
799 		if (p->max_out_prefix_restart)
800 			printf(" restart %u", p->max_out_prefix_restart);
801 		printf("\n");
802 	}
803 	if (p->holdtime)
804 		printf("%s\tholdtime %u\n", c, p->holdtime);
805 	if (p->min_holdtime)
806 		printf("%s\tholdtime min %u\n", c, p->min_holdtime);
807 	if (p->export_type == EXPORT_NONE)
808 		printf("%s\texport none\n", c);
809 	else if (p->export_type == EXPORT_DEFAULT_ROUTE)
810 		printf("%s\texport default-route\n", c);
811 	if (p->enforce_as == ENFORCE_AS_ON)
812 		printf("%s\tenforce neighbor-as yes\n", c);
813 	else
814 		printf("%s\tenforce neighbor-as no\n", c);
815 	if (p->enforce_local_as == ENFORCE_AS_ON)
816 		printf("%s\tenforce local-as yes\n", c);
817 	else
818 		printf("%s\tenforce local-as no\n", c);
819 	if (p->reflector_client) {
820 		if (conf->clusterid == 0)
821 			printf("%s\troute-reflector\n", c);
822 		else {
823 			ina.s_addr = conf->clusterid;
824 			printf("%s\troute-reflector %s\n", c,
825 			    inet_ntoa(ina));
826 		}
827 	}
828 	if (p->demote_group[0])
829 		printf("%s\tdemote %s\n", c, p->demote_group);
830 	if (p->if_depend[0])
831 		printf("%s\tdepend on \"%s\"\n", c, p->if_depend);
832 	if (p->flags & PEERFLAG_TRANS_AS)
833 		printf("%s\ttransparent-as yes\n", c);
834 
835 	if (conf->flags & BGPD_FLAG_DECISION_ALL_PATHS) {
836 		if (!(p->flags & PEERFLAG_EVALUATE_ALL))
837 			printf("%s\trde evaluate default\n", c);
838 	} else {
839 		if (p->flags & PEERFLAG_EVALUATE_ALL)
840 			printf("%s\trde evaluate all\n", c);
841 	}
842 
843 	if (conf->flags & BGPD_FLAG_NO_AS_SET) {
844 		if (!(p->flags & PEERFLAG_NO_AS_SET))
845 			printf("%s\treject as-set no\n", c);
846 	} else {
847 		if (p->flags & PEERFLAG_NO_AS_SET)
848 			printf("%s\treject as-set yes\n", c);
849 	}
850 
851 	if (p->flags & PEERFLAG_LOG_UPDATES)
852 		printf("%s\tlog updates\n", c);
853 
854 	if (p->auth.method == AUTH_MD5SIG)
855 		printf("%s\ttcp md5sig\n", c);
856 	else if (p->auth.method == AUTH_IPSEC_MANUAL_ESP ||
857 	    p->auth.method == AUTH_IPSEC_MANUAL_AH) {
858 		if (p->auth.method == AUTH_IPSEC_MANUAL_ESP)
859 			method = "esp";
860 		else
861 			method = "ah";
862 
863 		printf("%s\tipsec %s in spi %u %s XXXXXX", c, method,
864 		    p->auth.spi_in, print_auth_alg(p->auth.auth_alg_in));
865 		if (p->auth.enc_alg_in)
866 			printf(" %s XXXXXX", print_enc_alg(p->auth.enc_alg_in));
867 		printf("\n");
868 
869 		printf("%s\tipsec %s out spi %u %s XXXXXX", c, method,
870 		    p->auth.spi_out, print_auth_alg(p->auth.auth_alg_out));
871 		if (p->auth.enc_alg_out)
872 			printf(" %s XXXXXX",
873 			    print_enc_alg(p->auth.enc_alg_out));
874 		printf("\n");
875 	} else if (p->auth.method == AUTH_IPSEC_IKE_AH)
876 		printf("%s\tipsec ah ike\n", c);
877 	else if (p->auth.method == AUTH_IPSEC_IKE_ESP)
878 		printf("%s\tipsec esp ike\n", c);
879 
880 	if (p->ttlsec)
881 		printf("%s\tttl-security yes\n", c);
882 
883 	print_announce(p, c);
884 
885 	print_mrt(conf, p->id, p->groupid, c, "\t");
886 
887 	printf("%s}\n", c);
888 }
889 
890 const char *
891 print_auth_alg(enum auth_alg alg)
892 {
893 	switch (alg) {
894 	case AUTH_AALG_SHA1HMAC:
895 		return ("sha1");
896 	case AUTH_AALG_MD5HMAC:
897 		return ("md5");
898 	default:
899 		return ("???");
900 	}
901 }
902 
903 const char *
904 print_enc_alg(enum auth_enc_alg alg)
905 {
906 	switch (alg) {
907 	case AUTH_EALG_3DESCBC:
908 		return ("3des");
909 	case AUTH_EALG_AES:
910 		return ("aes");
911 	default:
912 		return ("???");
913 	}
914 }
915 
916 static const char *
917 print_addpath_mode(enum addpath_mode mode)
918 {
919 	switch (mode) {
920 	case ADDPATH_EVAL_NONE:
921 		return "none";
922 	case ADDPATH_EVAL_BEST:
923 		return "best";
924 	case ADDPATH_EVAL_ECMP:
925 		return "ecmp";
926 	case ADDPATH_EVAL_AS_WIDE:
927 		return "as-wide-best";
928 	case ADDPATH_EVAL_ALL:
929 		return "all";
930 	default:
931 		return "???";
932 	}
933 }
934 
935 void
936 print_announce(struct peer_config *p, const char *c)
937 {
938 	uint8_t	aid;
939 
940 	if (p->announce_capa == 0)
941 		printf("%s\tannounce capabilities no\n", c);
942 
943 	for (aid = 0; aid < AID_MAX; aid++)
944 		if (p->capabilities.mp[aid])
945 			printf("%s\tannounce %s\n", c, aid2str(aid));
946 
947 	if (p->capabilities.refresh == 0)
948 		printf("%s\tannounce refresh no\n", c);
949 	if (p->capabilities.enhanced_rr == 1)
950 		printf("%s\tannounce enhanced refresh yes\n", c);
951 	if (p->capabilities.grestart.restart == 0)
952 		printf("%s\tannounce restart no\n", c);
953 	if (p->capabilities.as4byte == 0)
954 		printf("%s\tannounce as4byte no\n", c);
955 	if (p->capabilities.add_path[0] & CAPA_AP_RECV)
956 		printf("%s\tannounce add-path recv yes\n", c);
957 	if (p->capabilities.add_path[0] & CAPA_AP_SEND) {
958 		printf("%s\tannounce add-path send %s", c,
959 		    print_addpath_mode(p->eval.mode));
960 		if (p->eval.extrapaths != 0)
961 			printf(" plus %d", p->eval.extrapaths);
962 		if (p->eval.maxpaths != 0)
963 			printf(" max %d", p->eval.maxpaths);
964 		printf("\n");
965 	}
966 	if (p->capabilities.policy) {
967 		printf("%s\tannounce policy %s\n", c,
968 		    p->capabilities.policy == 2 ? "enforce" : "yes");
969 	}
970 }
971 
972 void
973 print_as(struct filter_rule *r)
974 {
975 	if (r->match.as.flags & AS_FLAG_AS_SET_NAME) {
976 		printf("as-set \"%s\" ", r->match.as.name);
977 		return;
978 	}
979 	switch (r->match.as.op) {
980 	case OP_RANGE:
981 		printf("%s - ", log_as(r->match.as.as_min));
982 		printf("%s ", log_as(r->match.as.as_max));
983 		break;
984 	case OP_XRANGE:
985 		printf("%s >< ", log_as(r->match.as.as_min));
986 		printf("%s ", log_as(r->match.as.as_max));
987 		break;
988 	case OP_NE:
989 		printf("!= %s ", log_as(r->match.as.as_min));
990 		break;
991 	default:
992 		printf("%s ", log_as(r->match.as.as_min));
993 		break;
994 	}
995 }
996 
997 void
998 print_rule(struct bgpd_config *conf, struct filter_rule *r)
999 {
1000 	struct peer *p;
1001 	int i;
1002 
1003 	if (r->action == ACTION_ALLOW)
1004 		printf("allow ");
1005 	else if (r->action == ACTION_DENY)
1006 		printf("deny ");
1007 	else
1008 		printf("match ");
1009 	if (r->quick)
1010 		printf("quick ");
1011 
1012 	if (r->rib[0])
1013 		printf("rib %s ", r->rib);
1014 
1015 	if (r->dir == DIR_IN)
1016 		printf("from ");
1017 	else if (r->dir == DIR_OUT)
1018 		printf("to ");
1019 	else
1020 		printf("eeeeeeeps. ");
1021 
1022 	if (r->peer.peerid) {
1023 		RB_FOREACH(p, peer_head, &conf->peers)
1024 			if (p->conf.id == r->peer.peerid)
1025 				break;
1026 		if (p == NULL)
1027 			printf("? ");
1028 		else
1029 			printf("%s ", log_addr(&p->conf.remote_addr));
1030 	} else if (r->peer.groupid) {
1031 		RB_FOREACH(p, peer_head, &conf->peers)
1032 			if (p->conf.groupid == r->peer.groupid)
1033 				break;
1034 		if (p == NULL)
1035 			printf("group ? ");
1036 		else
1037 			printf("group \"%s\" ", p->conf.group);
1038 	} else if (r->peer.remote_as) {
1039 		printf("AS %s ", log_as(r->peer.remote_as));
1040 	} else if (r->peer.ebgp) {
1041 		printf("ebgp ");
1042 	} else if (r->peer.ibgp) {
1043 		printf("ibgp ");
1044 	} else
1045 		printf("any ");
1046 
1047 	if (r->match.ovs.is_set) {
1048 		switch (r->match.ovs.validity) {
1049 		case ROA_VALID:
1050 			printf("ovs valid ");
1051 			break;
1052 		case ROA_INVALID:
1053 			printf("ovs invalid ");
1054 			break;
1055 		case ROA_NOTFOUND:
1056 			printf("ovs not-found ");
1057 			break;
1058 		default:
1059 			printf("ovs ??? %d ??? ", r->match.ovs.validity);
1060 		}
1061 	}
1062 
1063 	if (r->match.avs.is_set) {
1064 		switch (r->match.avs.validity) {
1065 		case ASPA_VALID:
1066 			printf("avs valid ");
1067 			break;
1068 		case ASPA_INVALID:
1069 			printf("avs invalid ");
1070 			break;
1071 		case ASPA_UNKNOWN:
1072 			printf("avs unknown ");
1073 			break;
1074 		default:
1075 			printf("avs ??? %d ??? ", r->match.avs.validity);
1076 		}
1077 	}
1078 
1079 	if (r->match.prefix.addr.aid != AID_UNSPEC) {
1080 		printf("prefix ");
1081 		print_prefix(&r->match.prefix);
1082 		printf(" ");
1083 	}
1084 
1085 	if (r->match.prefixset.name[0] != '\0')
1086 		printf("prefix-set \"%s\" ", r->match.prefixset.name);
1087 	if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER)
1088 		printf("or-longer ");
1089 
1090 	if (r->match.originset.name[0] != '\0')
1091 		printf("origin-set \"%s\" ", r->match.originset.name);
1092 
1093 	if (r->match.nexthop.flags) {
1094 		if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
1095 			printf("nexthop neighbor ");
1096 		else
1097 			printf("nexthop %s ", log_addr(&r->match.nexthop.addr));
1098 	}
1099 
1100 	if (r->match.as.type) {
1101 		if (r->match.as.type == AS_ALL)
1102 			printf("AS ");
1103 		else if (r->match.as.type == AS_SOURCE)
1104 			printf("source-as ");
1105 		else if (r->match.as.type == AS_TRANSIT)
1106 			printf("transit-as ");
1107 		else if (r->match.as.type == AS_PEER)
1108 			printf("peer-as ");
1109 		else
1110 			printf("unfluffy-as ");
1111 		print_as(r);
1112 	}
1113 
1114 	if (r->match.aslen.type) {
1115 		printf("%s %u ", r->match.aslen.type == ASLEN_MAX ?
1116 		    "max-as-len" : "max-as-seq", r->match.aslen.aslen);
1117 	}
1118 
1119 	for (i = 0; i < MAX_COMM_MATCH; i++) {
1120 		struct community *c = &r->match.community[i];
1121 		if (c->flags != 0) {
1122 			printf("%s ", community_type(c));
1123 			print_community(c);
1124 		}
1125 	}
1126 
1127 	if (r->match.maxcomm != 0)
1128 		printf("max-communities %d ", r->match.maxcomm - 1);
1129 	if (r->match.maxextcomm != 0)
1130 		printf("max-ext-communities %d ", r->match.maxextcomm - 1);
1131 	if (r->match.maxlargecomm != 0)
1132 		printf("max-large-communities %d ", r->match.maxlargecomm - 1);
1133 
1134 	print_set(&r->set);
1135 
1136 	printf("\n");
1137 }
1138 
1139 const char *
1140 mrt_type(enum mrt_type t)
1141 {
1142 	switch (t) {
1143 	case MRT_NONE:
1144 		break;
1145 	case MRT_TABLE_DUMP:
1146 		return "table";
1147 	case MRT_TABLE_DUMP_MP:
1148 		return "table-mp";
1149 	case MRT_TABLE_DUMP_V2:
1150 		return "table-v2";
1151 	case MRT_ALL_IN:
1152 		return "all in";
1153 	case MRT_ALL_OUT:
1154 		return "all out";
1155 	case MRT_UPDATE_IN:
1156 		return "updates in";
1157 	case MRT_UPDATE_OUT:
1158 		return "updates out";
1159 	}
1160 	return "unfluffy MRT";
1161 }
1162 
1163 void
1164 print_mrt(struct bgpd_config *conf, uint32_t pid, uint32_t gid,
1165     const char *prep, const char *prep2)
1166 {
1167 	struct mrt	*m;
1168 
1169 	if (conf->mrt == NULL)
1170 		return;
1171 
1172 	LIST_FOREACH(m, conf->mrt, entry)
1173 		if ((gid != 0 && m->group_id == gid) ||
1174 		    (m->peer_id == pid && m->group_id == gid)) {
1175 			printf("%s%sdump ", prep, prep2);
1176 			if (m->rib[0])
1177 				printf("rib %s ", m->rib);
1178 			printf("%s \"%s\"", mrt_type(m->type),
1179 			    MRT2MC(m)->name);
1180 			if (MRT2MC(m)->ReopenTimerInterval == 0)
1181 				printf("\n");
1182 			else
1183 				printf(" %d\n", MRT2MC(m)->ReopenTimerInterval);
1184 		}
1185 	if (!LIST_EMPTY(conf->mrt))
1186 		printf("\n");
1187 }
1188 
1189 void
1190 print_groups(struct bgpd_config *conf)
1191 {
1192 	struct peer_config	**peerlist;
1193 	struct peer		 *p;
1194 	u_int			  peer_cnt, i;
1195 	uint32_t		  prev_groupid;
1196 	const char		 *tab	= "\t";
1197 	const char		 *nada	= "";
1198 	const char		 *c;
1199 
1200 	peer_cnt = 0;
1201 	RB_FOREACH(p, peer_head, &conf->peers)
1202 		peer_cnt++;
1203 
1204 	if ((peerlist = calloc(peer_cnt, sizeof(struct peer_config *))) == NULL)
1205 		fatal("print_groups calloc");
1206 
1207 	i = 0;
1208 	RB_FOREACH(p, peer_head, &conf->peers)
1209 		peerlist[i++] = &p->conf;
1210 
1211 	qsort(peerlist, peer_cnt, sizeof(struct peer_config *), peer_compare);
1212 
1213 	prev_groupid = 0;
1214 	for (i = 0; i < peer_cnt; i++) {
1215 		if (peerlist[i]->groupid) {
1216 			c = tab;
1217 			if (peerlist[i]->groupid != prev_groupid) {
1218 				if (prev_groupid)
1219 					printf("}\n\n");
1220 				printf("group \"%s\" {\n", peerlist[i]->group);
1221 				prev_groupid = peerlist[i]->groupid;
1222 			}
1223 		} else
1224 			c = nada;
1225 
1226 		print_peer(peerlist[i], conf, c);
1227 	}
1228 
1229 	if (prev_groupid)
1230 		printf("}\n\n");
1231 
1232 	free(peerlist);
1233 }
1234 
1235 int
1236 peer_compare(const void *aa, const void *bb)
1237 {
1238 	const struct peer_config * const *a;
1239 	const struct peer_config * const *b;
1240 
1241 	a = aa;
1242 	b = bb;
1243 
1244 	return ((*a)->groupid - (*b)->groupid);
1245 }
1246 
1247 void
1248 print_config(struct bgpd_config *conf, struct rib_names *rib_l)
1249 {
1250 	struct filter_rule	*r;
1251 	struct network		*n;
1252 	struct flowspec_config	*f;
1253 	struct rde_rib		*rr;
1254 	struct l3vpn		*vpn;
1255 
1256 	print_mainconf(conf);
1257 	print_rtrs(&conf->rtrs);
1258 	print_roa(&conf->roa);
1259 	print_aspa(&conf->aspa);
1260 	print_as_sets(&conf->as_sets);
1261 	print_prefixsets(&conf->prefixsets);
1262 	print_originsets(&conf->originsets);
1263 	TAILQ_FOREACH(n, &conf->networks, entry)
1264 		print_network(&n->net, "");
1265 	RB_FOREACH(f, flowspec_tree, &conf->flowspecs)
1266 		print_flowspec(f, "");
1267 	if (!SIMPLEQ_EMPTY(&conf->l3vpns))
1268 		printf("\n");
1269 	SIMPLEQ_FOREACH(vpn, &conf->l3vpns, entry)
1270 		print_l3vpn(vpn);
1271 	printf("\n");
1272 	SIMPLEQ_FOREACH(rr, rib_l, entry) {
1273 		if (rr->flags & F_RIB_NOEVALUATE)
1274 			printf("rde rib %s no evaluate\n", rr->name);
1275 		else if (rr->flags & F_RIB_NOFIB)
1276 			printf("rde rib %s\n", rr->name);
1277 		else
1278 			printf("rde rib %s rtable %u fib-update %s\n", rr->name,
1279 			    rr->rtableid, rr->flags & F_RIB_NOFIBSYNC ?
1280 			    "no" : "yes");
1281 	}
1282 	printf("\n");
1283 	print_mrt(conf, 0, 0, "", "");
1284 	print_groups(conf);
1285 	TAILQ_FOREACH(r, conf->filters, entry)
1286 		print_rule(conf, r);
1287 }
1288