1/*	$OpenBSD: printconf.c,v 1.173 2024/05/22 08:41:14 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
31void		 print_prefix(struct filter_prefix *p);
32const char	*community_type(struct community *c);
33void		 print_community(struct community *c);
34void		 print_origin(uint8_t);
35void		 print_set(struct filter_set_head *);
36void		 print_mainconf(struct bgpd_config *);
37void		 print_l3vpn_targets(struct filter_set_head *, const char *);
38void		 print_l3vpn(struct l3vpn *);
39const char	*print_af(uint8_t);
40void		 print_network(struct network_config *, const char *);
41void		 print_flowspec(struct flowspec_config *, const char *);
42void		 print_as_sets(struct as_set_head *);
43void		 print_prefixsets(struct prefixset_head *);
44void		 print_originsets(struct prefixset_head *);
45void		 print_roa(struct roa_tree *);
46void		 print_aspa(struct aspa_tree *);
47void		 print_rtrs(struct rtr_config_head *);
48void		 print_peer(struct peer_config *, struct bgpd_config *,
49		    const char *);
50const char	*print_auth_alg(enum auth_alg);
51const char	*print_enc_alg(enum auth_enc_alg);
52void		 print_announce(struct peer_config *, const char *);
53void		 print_as(struct filter_rule *);
54void		 print_rule(struct bgpd_config *, struct filter_rule *);
55const char	*mrt_type(enum mrt_type);
56void		 print_mrt(struct bgpd_config *, uint32_t, uint32_t,
57		    const char *, const char *);
58void		 print_groups(struct bgpd_config *);
59int		 peer_compare(const void *, const void *);
60
61void
62print_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
109const char *
110community_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
124void
125print_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
281void
282print_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
294void
295print_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
377void
378print_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 = htonl(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
433void
434print_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
444void
445print_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
466const char *
467print_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
481void
482print_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
513static void
514print_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
538static void
539print_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
579static void
580print_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
593void
594print_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
622void
623print_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
644void
645print_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
664void
665print_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
686void
687print_roa(struct roa_tree *r)
688{
689	struct roa	*roa;
690
691	if (RB_EMPTY(r))
692		return;
693
694	printf("roa-set {");
695	RB_FOREACH(roa, roa_tree, r) {
696		printf("\n\t%s", log_roa(roa));
697	}
698	printf("\n}\n\n");
699}
700
701void
702print_aspa(struct aspa_tree *a)
703{
704	struct aspa_set	*aspa;
705
706	if (RB_EMPTY(a))
707		return;
708
709	printf("aspa-set {");
710	RB_FOREACH(aspa, aspa_tree, a) {
711		printf("\n\t%s", log_aspa(aspa));
712	}
713	printf("\n}\n\n");
714}
715
716void
717print_rtrs(struct rtr_config_head *rh)
718{
719	struct rtr_config *r;
720
721	SIMPLEQ_FOREACH(r, rh, entry) {
722		printf("rtr %s {\n", log_addr(&r->remote_addr));
723		printf("\tdescr \"%s\"\n", r->descr);
724		printf("\tport %u\n", r->remote_port);
725		if (r->local_addr.aid != AID_UNSPEC)
726			printf("local-addr %s\n", log_addr(&r->local_addr));
727		printf("}\n\n");
728	}
729}
730
731void
732print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
733{
734	char		*method;
735	struct in_addr	 ina;
736
737	if ((p->remote_addr.aid == AID_INET && p->remote_masklen != 32) ||
738	    (p->remote_addr.aid == AID_INET6 && p->remote_masklen != 128))
739		printf("%sneighbor %s/%u {\n", c, log_addr(&p->remote_addr),
740		    p->remote_masklen);
741	else
742		printf("%sneighbor %s {\n", c, log_addr(&p->remote_addr));
743	if (p->descr[0])
744		printf("%s\tdescr \"%s\"\n", c, p->descr);
745	if (p->rib[0])
746		printf("%s\trib \"%s\"\n", c, p->rib);
747	if (p->remote_as)
748		printf("%s\tremote-as %s\n", c, log_as(p->remote_as));
749	if (p->local_as != conf->as) {
750		printf("%s\tlocal-as %s", c, log_as(p->local_as));
751		if (p->local_as > USHRT_MAX && p->local_short_as != AS_TRANS)
752			printf(" %u", p->local_short_as);
753		printf("\n");
754	}
755	if (p->down)
756		printf("%s\tdown\n", c);
757	if (p->distance > 1)
758		printf("%s\tmultihop %u\n", c, p->distance);
759	if (p->passive)
760		printf("%s\tpassive\n", c);
761	if (p->local_addr_v4.aid)
762		printf("%s\tlocal-address %s\n", c,
763		    log_addr(&p->local_addr_v4));
764	if (p->local_addr_v6.aid)
765		printf("%s\tlocal-address %s\n", c,
766		    log_addr(&p->local_addr_v6));
767	if (p->remote_port != BGP_PORT)
768		printf("%s\tport %hu\n", c, p->remote_port);
769	if (p->role != ROLE_NONE)
770		printf("%s\trole %s\n", c, log_policy(p->role));
771	if (p->max_prefix) {
772		printf("%s\tmax-prefix %u", c, p->max_prefix);
773		if (p->max_prefix_restart)
774			printf(" restart %u", p->max_prefix_restart);
775		printf("\n");
776	}
777	if (p->max_out_prefix) {
778		printf("%s\tmax-prefix %u out", c, p->max_out_prefix);
779		if (p->max_out_prefix_restart)
780			printf(" restart %u", p->max_out_prefix_restart);
781		printf("\n");
782	}
783	if (p->holdtime)
784		printf("%s\tholdtime %u\n", c, p->holdtime);
785	if (p->min_holdtime)
786		printf("%s\tholdtime min %u\n", c, p->min_holdtime);
787	if (p->export_type == EXPORT_NONE)
788		printf("%s\texport none\n", c);
789	else if (p->export_type == EXPORT_DEFAULT_ROUTE)
790		printf("%s\texport default-route\n", c);
791	if (p->enforce_as == ENFORCE_AS_ON)
792		printf("%s\tenforce neighbor-as yes\n", c);
793	else
794		printf("%s\tenforce neighbor-as no\n", c);
795	if (p->enforce_local_as == ENFORCE_AS_ON)
796		printf("%s\tenforce local-as yes\n", c);
797	else
798		printf("%s\tenforce local-as no\n", c);
799	if (p->reflector_client) {
800		if (conf->clusterid == 0)
801			printf("%s\troute-reflector\n", c);
802		else {
803			ina.s_addr = htonl(conf->clusterid);
804			printf("%s\troute-reflector %s\n", c,
805			    inet_ntoa(ina));
806		}
807	}
808	if (p->demote_group[0])
809		printf("%s\tdemote %s\n", c, p->demote_group);
810	if (p->if_depend[0])
811		printf("%s\tdepend on \"%s\"\n", c, p->if_depend);
812	if (p->flags & PEERFLAG_TRANS_AS)
813		printf("%s\ttransparent-as yes\n", c);
814
815	if (conf->flags & BGPD_FLAG_DECISION_ALL_PATHS) {
816		if (!(p->flags & PEERFLAG_EVALUATE_ALL))
817			printf("%s\trde evaluate default\n", c);
818	} else {
819		if (p->flags & PEERFLAG_EVALUATE_ALL)
820			printf("%s\trde evaluate all\n", c);
821	}
822
823	if (conf->flags & BGPD_FLAG_NO_AS_SET) {
824		if (!(p->flags & PEERFLAG_NO_AS_SET))
825			printf("%s\treject as-set no\n", c);
826	} else {
827		if (p->flags & PEERFLAG_NO_AS_SET)
828			printf("%s\treject as-set yes\n", c);
829	}
830
831	if (p->flags & PEERFLAG_LOG_UPDATES)
832		printf("%s\tlog updates\n", c);
833
834	if (p->auth.method == AUTH_MD5SIG)
835		printf("%s\ttcp md5sig\n", c);
836	else if (p->auth.method == AUTH_IPSEC_MANUAL_ESP ||
837	    p->auth.method == AUTH_IPSEC_MANUAL_AH) {
838		if (p->auth.method == AUTH_IPSEC_MANUAL_ESP)
839			method = "esp";
840		else
841			method = "ah";
842
843		printf("%s\tipsec %s in spi %u %s XXXXXX", c, method,
844		    p->auth.spi_in, print_auth_alg(p->auth.auth_alg_in));
845		if (p->auth.enc_alg_in)
846			printf(" %s XXXXXX", print_enc_alg(p->auth.enc_alg_in));
847		printf("\n");
848
849		printf("%s\tipsec %s out spi %u %s XXXXXX", c, method,
850		    p->auth.spi_out, print_auth_alg(p->auth.auth_alg_out));
851		if (p->auth.enc_alg_out)
852			printf(" %s XXXXXX",
853			    print_enc_alg(p->auth.enc_alg_out));
854		printf("\n");
855	} else if (p->auth.method == AUTH_IPSEC_IKE_AH)
856		printf("%s\tipsec ah ike\n", c);
857	else if (p->auth.method == AUTH_IPSEC_IKE_ESP)
858		printf("%s\tipsec esp ike\n", c);
859
860	if (p->ttlsec)
861		printf("%s\tttl-security yes\n", c);
862
863	print_announce(p, c);
864
865	print_mrt(conf, p->id, p->groupid, c, "\t");
866
867	printf("%s}\n", c);
868}
869
870const char *
871print_auth_alg(enum auth_alg alg)
872{
873	switch (alg) {
874	case AUTH_AALG_SHA1HMAC:
875		return ("sha1");
876	case AUTH_AALG_MD5HMAC:
877		return ("md5");
878	default:
879		return ("???");
880	}
881}
882
883const char *
884print_enc_alg(enum auth_enc_alg alg)
885{
886	switch (alg) {
887	case AUTH_EALG_3DESCBC:
888		return ("3des");
889	case AUTH_EALG_AES:
890		return ("aes");
891	default:
892		return ("???");
893	}
894}
895
896static const char *
897print_addpath_mode(enum addpath_mode mode)
898{
899	switch (mode) {
900	case ADDPATH_EVAL_NONE:
901		return "none";
902	case ADDPATH_EVAL_BEST:
903		return "best";
904	case ADDPATH_EVAL_ECMP:
905		return "ecmp";
906	case ADDPATH_EVAL_AS_WIDE:
907		return "as-wide-best";
908	case ADDPATH_EVAL_ALL:
909		return "all";
910	default:
911		return "???";
912	}
913}
914
915void
916print_announce(struct peer_config *p, const char *c)
917{
918	uint8_t	aid;
919	int match = 0;
920
921	for (aid = AID_MIN; aid < AID_MAX; aid++)
922		if (p->capabilities.mp[aid] == 2) {
923			printf("%s\tannounce %s enforce\n", c, aid2str(aid));
924			match = 1;
925		} else if (p->capabilities.mp[aid]) {
926			printf("%s\tannounce %s\n", c, aid2str(aid));
927			match = 1;
928		}
929	if (!match) {
930		printf("%s\tannounce IPv4 none\n", c);
931		printf("%s\tannounce IPv6 none\n", c);
932	}
933
934	if (p->capabilities.refresh == 2)
935		printf("%s\tannounce refresh enforce\n", c);
936	else if (p->capabilities.refresh == 0)
937		printf("%s\tannounce refresh no\n", c);
938
939	if (p->capabilities.enhanced_rr == 2)
940		printf("%s\tannounce enhanced refresh enforce\n", c);
941	else if (p->capabilities.enhanced_rr == 1)
942		printf("%s\tannounce enhanced refresh yes\n", c);
943
944	if (p->capabilities.grestart.restart == 2)
945		printf("%s\tannounce restart enforce\n", c);
946	else if (p->capabilities.grestart.restart == 0)
947		printf("%s\tannounce restart no\n", c);
948
949	if (p->capabilities.as4byte == 2)
950		printf("%s\tannounce as4byte enforce\n", c);
951	else if (p->capabilities.as4byte == 0)
952		printf("%s\tannounce as4byte no\n", c);
953
954	if (p->capabilities.add_path[AID_MIN] & CAPA_AP_RECV_ENFORCE)
955		printf("%s\tannounce add-path recv enforce\n", c);
956	else if (p->capabilities.add_path[AID_MIN] & CAPA_AP_RECV)
957		printf("%s\tannounce add-path recv yes\n", c);
958
959	if (p->capabilities.add_path[AID_MIN] & CAPA_AP_SEND) {
960		printf("%s\tannounce add-path send %s", c,
961		    print_addpath_mode(p->eval.mode));
962		if (p->eval.extrapaths != 0)
963			printf(" plus %d", p->eval.extrapaths);
964		if (p->eval.maxpaths != 0)
965			printf(" max %d", p->eval.maxpaths);
966		if (p->capabilities.add_path[AID_MIN] & CAPA_AP_SEND_ENFORCE)
967			printf(" enforce");
968		printf("\n");
969	}
970
971	if (p->capabilities.policy == 2)
972		printf("%s\tannounce policy enforce\n", c);
973	else if (p->capabilities.policy == 1)
974		printf("%s\tannounce policy yes\n", c);
975	else
976		printf("%s\tannounce policy no\n", c);
977}
978
979void
980print_as(struct filter_rule *r)
981{
982	if (r->match.as.flags & AS_FLAG_AS_SET_NAME) {
983		printf("as-set \"%s\" ", r->match.as.name);
984		return;
985	}
986	switch (r->match.as.op) {
987	case OP_RANGE:
988		printf("%s - ", log_as(r->match.as.as_min));
989		printf("%s ", log_as(r->match.as.as_max));
990		break;
991	case OP_XRANGE:
992		printf("%s >< ", log_as(r->match.as.as_min));
993		printf("%s ", log_as(r->match.as.as_max));
994		break;
995	case OP_NE:
996		printf("!= %s ", log_as(r->match.as.as_min));
997		break;
998	default:
999		printf("%s ", log_as(r->match.as.as_min));
1000		break;
1001	}
1002}
1003
1004void
1005print_rule(struct bgpd_config *conf, struct filter_rule *r)
1006{
1007	struct peer *p;
1008	int i;
1009
1010	if (r->action == ACTION_ALLOW)
1011		printf("allow ");
1012	else if (r->action == ACTION_DENY)
1013		printf("deny ");
1014	else
1015		printf("match ");
1016	if (r->quick)
1017		printf("quick ");
1018
1019	if (r->rib[0])
1020		printf("rib %s ", r->rib);
1021
1022	if (r->dir == DIR_IN)
1023		printf("from ");
1024	else if (r->dir == DIR_OUT)
1025		printf("to ");
1026	else
1027		printf("eeeeeeeps. ");
1028
1029	if (r->peer.peerid) {
1030		RB_FOREACH(p, peer_head, &conf->peers)
1031			if (p->conf.id == r->peer.peerid)
1032				break;
1033		if (p == NULL)
1034			printf("? ");
1035		else
1036			printf("%s ", log_addr(&p->conf.remote_addr));
1037	} else if (r->peer.groupid) {
1038		RB_FOREACH(p, peer_head, &conf->peers)
1039			if (p->conf.groupid == r->peer.groupid)
1040				break;
1041		if (p == NULL)
1042			printf("group ? ");
1043		else
1044			printf("group \"%s\" ", p->conf.group);
1045	} else if (r->peer.remote_as) {
1046		printf("AS %s ", log_as(r->peer.remote_as));
1047	} else if (r->peer.ebgp) {
1048		printf("ebgp ");
1049	} else if (r->peer.ibgp) {
1050		printf("ibgp ");
1051	} else
1052		printf("any ");
1053
1054	if (r->match.ovs.is_set) {
1055		switch (r->match.ovs.validity) {
1056		case ROA_VALID:
1057			printf("ovs valid ");
1058			break;
1059		case ROA_INVALID:
1060			printf("ovs invalid ");
1061			break;
1062		case ROA_NOTFOUND:
1063			printf("ovs not-found ");
1064			break;
1065		default:
1066			printf("ovs ??? %d ??? ", r->match.ovs.validity);
1067		}
1068	}
1069
1070	if (r->match.avs.is_set) {
1071		switch (r->match.avs.validity) {
1072		case ASPA_VALID:
1073			printf("avs valid ");
1074			break;
1075		case ASPA_INVALID:
1076			printf("avs invalid ");
1077			break;
1078		case ASPA_UNKNOWN:
1079			printf("avs unknown ");
1080			break;
1081		default:
1082			printf("avs ??? %d ??? ", r->match.avs.validity);
1083		}
1084	}
1085
1086	if (r->match.prefix.addr.aid != AID_UNSPEC) {
1087		printf("prefix ");
1088		print_prefix(&r->match.prefix);
1089		printf(" ");
1090	}
1091
1092	if (r->match.prefixset.name[0] != '\0')
1093		printf("prefix-set \"%s\" ", r->match.prefixset.name);
1094	if (r->match.prefixset.flags & PREFIXSET_FLAG_LONGER)
1095		printf("or-longer ");
1096
1097	if (r->match.originset.name[0] != '\0')
1098		printf("origin-set \"%s\" ", r->match.originset.name);
1099
1100	if (r->match.nexthop.flags) {
1101		if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
1102			printf("nexthop neighbor ");
1103		else
1104			printf("nexthop %s ", log_addr(&r->match.nexthop.addr));
1105	}
1106
1107	if (r->match.as.type) {
1108		if (r->match.as.type == AS_ALL)
1109			printf("AS ");
1110		else if (r->match.as.type == AS_SOURCE)
1111			printf("source-as ");
1112		else if (r->match.as.type == AS_TRANSIT)
1113			printf("transit-as ");
1114		else if (r->match.as.type == AS_PEER)
1115			printf("peer-as ");
1116		else
1117			printf("unfluffy-as ");
1118		print_as(r);
1119	}
1120
1121	if (r->match.aslen.type) {
1122		printf("%s %u ", r->match.aslen.type == ASLEN_MAX ?
1123		    "max-as-len" : "max-as-seq", r->match.aslen.aslen);
1124	}
1125
1126	for (i = 0; i < MAX_COMM_MATCH; i++) {
1127		struct community *c = &r->match.community[i];
1128		if (c->flags != 0) {
1129			printf("%s ", community_type(c));
1130			print_community(c);
1131		}
1132	}
1133
1134	if (r->match.maxcomm != 0)
1135		printf("max-communities %d ", r->match.maxcomm - 1);
1136	if (r->match.maxextcomm != 0)
1137		printf("max-ext-communities %d ", r->match.maxextcomm - 1);
1138	if (r->match.maxlargecomm != 0)
1139		printf("max-large-communities %d ", r->match.maxlargecomm - 1);
1140
1141	print_set(&r->set);
1142
1143	printf("\n");
1144}
1145
1146const char *
1147mrt_type(enum mrt_type t)
1148{
1149	switch (t) {
1150	case MRT_NONE:
1151		break;
1152	case MRT_TABLE_DUMP:
1153		return "table";
1154	case MRT_TABLE_DUMP_MP:
1155		return "table-mp";
1156	case MRT_TABLE_DUMP_V2:
1157		return "table-v2";
1158	case MRT_ALL_IN:
1159		return "all in";
1160	case MRT_ALL_OUT:
1161		return "all out";
1162	case MRT_UPDATE_IN:
1163		return "updates in";
1164	case MRT_UPDATE_OUT:
1165		return "updates out";
1166	}
1167	return "unfluffy MRT";
1168}
1169
1170void
1171print_mrt(struct bgpd_config *conf, uint32_t pid, uint32_t gid,
1172    const char *prep, const char *prep2)
1173{
1174	struct mrt	*m;
1175
1176	if (conf->mrt == NULL)
1177		return;
1178
1179	LIST_FOREACH(m, conf->mrt, entry)
1180		if ((gid != 0 && m->group_id == gid) ||
1181		    (m->peer_id == pid && m->group_id == gid)) {
1182			printf("%s%sdump ", prep, prep2);
1183			if (m->rib[0])
1184				printf("rib %s ", m->rib);
1185			printf("%s \"%s\"", mrt_type(m->type),
1186			    MRT2MC(m)->name);
1187			if (MRT2MC(m)->ReopenTimerInterval == 0)
1188				printf("\n");
1189			else
1190				printf(" %d\n", MRT2MC(m)->ReopenTimerInterval);
1191		}
1192	if (!LIST_EMPTY(conf->mrt))
1193		printf("\n");
1194}
1195
1196void
1197print_groups(struct bgpd_config *conf)
1198{
1199	struct peer_config	**peerlist;
1200	struct peer		 *p;
1201	u_int			  peer_cnt, i;
1202	uint32_t		  prev_groupid;
1203	const char		 *tab	= "\t";
1204	const char		 *nada	= "";
1205	const char		 *c;
1206
1207	peer_cnt = 0;
1208	RB_FOREACH(p, peer_head, &conf->peers)
1209		peer_cnt++;
1210
1211	if ((peerlist = calloc(peer_cnt, sizeof(struct peer_config *))) == NULL)
1212		fatal("print_groups calloc");
1213
1214	i = 0;
1215	RB_FOREACH(p, peer_head, &conf->peers)
1216		peerlist[i++] = &p->conf;
1217
1218	qsort(peerlist, peer_cnt, sizeof(struct peer_config *), peer_compare);
1219
1220	prev_groupid = 0;
1221	for (i = 0; i < peer_cnt; i++) {
1222		if (peerlist[i]->groupid) {
1223			c = tab;
1224			if (peerlist[i]->groupid != prev_groupid) {
1225				if (prev_groupid)
1226					printf("}\n\n");
1227				printf("group \"%s\" {\n", peerlist[i]->group);
1228				prev_groupid = peerlist[i]->groupid;
1229			}
1230		} else
1231			c = nada;
1232
1233		print_peer(peerlist[i], conf, c);
1234	}
1235
1236	if (prev_groupid)
1237		printf("}\n\n");
1238
1239	free(peerlist);
1240}
1241
1242int
1243peer_compare(const void *aa, const void *bb)
1244{
1245	const struct peer_config * const *a;
1246	const struct peer_config * const *b;
1247
1248	a = aa;
1249	b = bb;
1250
1251	return ((*a)->groupid - (*b)->groupid);
1252}
1253
1254void
1255print_config(struct bgpd_config *conf, struct rib_names *rib_l)
1256{
1257	struct filter_rule	*r;
1258	struct network		*n;
1259	struct flowspec_config	*f;
1260	struct rde_rib		*rr;
1261	struct l3vpn		*vpn;
1262
1263	print_mainconf(conf);
1264	print_rtrs(&conf->rtrs);
1265	print_roa(&conf->roa);
1266	print_aspa(&conf->aspa);
1267	print_as_sets(&conf->as_sets);
1268	print_prefixsets(&conf->prefixsets);
1269	print_originsets(&conf->originsets);
1270	TAILQ_FOREACH(n, &conf->networks, entry)
1271		print_network(&n->net, "");
1272	RB_FOREACH(f, flowspec_tree, &conf->flowspecs)
1273		print_flowspec(f, "");
1274	if (!SIMPLEQ_EMPTY(&conf->l3vpns))
1275		printf("\n");
1276	SIMPLEQ_FOREACH(vpn, &conf->l3vpns, entry)
1277		print_l3vpn(vpn);
1278	printf("\n");
1279	SIMPLEQ_FOREACH(rr, rib_l, entry) {
1280		if (rr->flags & F_RIB_NOEVALUATE)
1281			printf("rde rib %s no evaluate\n", rr->name);
1282		else if (rr->flags & F_RIB_NOFIB)
1283			printf("rde rib %s\n", rr->name);
1284		else
1285			printf("rde rib %s rtable %u fib-update %s\n", rr->name,
1286			    rr->rtableid, rr->flags & F_RIB_NOFIBSYNC ?
1287			    "no" : "yes");
1288	}
1289	printf("\n");
1290	print_mrt(conf, 0, 0, "", "");
1291	print_groups(conf);
1292	TAILQ_FOREACH(r, conf->filters, entry)
1293		print_rule(conf, r);
1294}
1295