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