xref: /openbsd-src/usr.sbin/bgpd/printconf.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: printconf.c,v 1.97 2016/07/13 20:07:38 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA, PROFITS OR MIND, WHETHER IN
15  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "bgpd.h"
25 #include "mrt.h"
26 #include "session.h"
27 #include "rde.h"
28 
29 void		 print_op(enum comp_ops);
30 void		 print_community(int, int);
31 void		 print_extcommunity(struct filter_extcommunity *);
32 void		 print_origin(u_int8_t);
33 void		 print_set(struct filter_set_head *);
34 void		 print_mainconf(struct bgpd_config *);
35 void		 print_rdomain_targets(struct filter_set_head *, const char *);
36 void		 print_rdomain(struct rdomain *);
37 const char	*print_af(u_int8_t);
38 void		 print_network(struct network_config *, const char *);
39 void		 print_peer(struct peer_config *, struct bgpd_config *,
40 		    const char *);
41 const char	*print_auth_alg(u_int8_t);
42 const char	*print_enc_alg(u_int8_t);
43 void		 print_announce(struct peer_config *, const char *);
44 void		 print_as(struct filter_rule *);
45 void		 print_rule(struct peer *, struct filter_rule *);
46 const char	*mrt_type(enum mrt_type);
47 void		 print_mrt(struct bgpd_config *, u_int32_t, u_int32_t,
48 		    const char *, const char *);
49 void		 print_groups(struct bgpd_config *, struct peer *);
50 int		 peer_compare(const void *, const void *);
51 
52 void
53 print_op(enum comp_ops op)
54 {
55 	switch (op) {
56 	case OP_RANGE:
57 		printf("-");
58 		break;
59 	case OP_XRANGE:
60 		printf("><");
61 		break;
62 	case OP_EQ:
63 		printf("=");
64 		break;
65 	case OP_NE:
66 		printf("!=");
67 		break;
68 	case OP_LE:
69 		printf("<=");
70 		break;
71 	case OP_LT:
72 		printf("<");
73 		break;
74 	case OP_GE:
75 		printf(">=");
76 		break;
77 	case OP_GT:
78 		printf(">");
79 		break;
80 	default:
81 		printf("?");
82 		break;
83 	}
84 }
85 
86 void
87 print_community(int as, int type)
88 {
89 	if (as == COMMUNITY_ANY)
90 		printf("*:");
91 	else if (as == COMMUNITY_NEIGHBOR_AS)
92 		printf("neighbor-as:");
93 	else
94 		printf("%u:", (unsigned int)as);
95 
96 	if (type == COMMUNITY_ANY)
97 		printf("* ");
98 	else if (type == COMMUNITY_NEIGHBOR_AS)
99 		printf("neighbor-as ");
100 	else
101 		printf("%d ", type);
102 }
103 
104 void
105 print_extcommunity(struct filter_extcommunity *c)
106 {
107 	switch (c->type & EXT_COMMUNITY_VALUE) {
108 	case EXT_COMMUNITY_TWO_AS:
109 		printf("%s %hu:%u ", log_ext_subtype(c->subtype),
110 		    c->data.ext_as.as, c->data.ext_as.val);
111 		break;
112 	case EXT_COMMUNITY_IPV4:
113 		printf("%s %s:%u ", log_ext_subtype(c->subtype),
114 		    inet_ntoa(c->data.ext_ip.addr), c->data.ext_ip.val);
115 		break;
116 	case EXT_COMMUNITY_FOUR_AS:
117 		printf("%s %s:%u ", log_ext_subtype(c->subtype),
118 		    log_as(c->data.ext_as4.as4), c->data.ext_as.val);
119 		break;
120 	case EXT_COMMUNITY_OPAQUE:
121 		printf("%s 0x%llx ", log_ext_subtype(c->subtype),
122 		    c->data.ext_opaq);
123 		break;
124 	default:
125 		printf("0x%x 0x%llx ", c->type, c->data.ext_opaq);
126 		break;
127 	}
128 }
129 
130 void
131 print_origin(u_int8_t o)
132 {
133 	if (o == ORIGIN_IGP)
134 		printf("igp ");
135 	else if (o == ORIGIN_EGP)
136 		printf("egp ");
137 	else if (o == ORIGIN_INCOMPLETE)
138 		printf("incomplete ");
139 	else
140 		printf("%u ", o);
141 }
142 
143 void
144 print_set(struct filter_set_head *set)
145 {
146 	struct filter_set	*s;
147 
148 	if (TAILQ_EMPTY(set))
149 		return;
150 
151 	printf("set { ");
152 	TAILQ_FOREACH(s, set, entry) {
153 		switch (s->type) {
154 		case ACTION_SET_LOCALPREF:
155 			printf("localpref %u ", s->action.metric);
156 			break;
157 		case ACTION_SET_RELATIVE_LOCALPREF:
158 			printf("localpref %+d ", s->action.relative);
159 			break;
160 		case ACTION_SET_MED:
161 			printf("metric %u ", s->action.metric);
162 			break;
163 		case ACTION_SET_RELATIVE_MED:
164 			printf("metric %+d ", s->action.relative);
165 			break;
166 		case ACTION_SET_WEIGHT:
167 			printf("weight %u ", s->action.metric);
168 			break;
169 		case ACTION_SET_RELATIVE_WEIGHT:
170 			printf("weight %+d ", s->action.relative);
171 			break;
172 		case ACTION_SET_NEXTHOP:
173 			printf("nexthop %s ", log_addr(&s->action.nexthop));
174 			break;
175 		case ACTION_SET_NEXTHOP_REJECT:
176 			printf("nexthop reject ");
177 			break;
178 		case ACTION_SET_NEXTHOP_BLACKHOLE:
179 			printf("nexthop blackhole ");
180 			break;
181 		case ACTION_SET_NEXTHOP_NOMODIFY:
182 			printf("nexthop no-modify ");
183 			break;
184 		case ACTION_SET_NEXTHOP_SELF:
185 			printf("nexthop self ");
186 			break;
187 		case ACTION_SET_PREPEND_SELF:
188 			printf("prepend-self %u ", s->action.prepend);
189 			break;
190 		case ACTION_SET_PREPEND_PEER:
191 			printf("prepend-neighbor %u ", s->action.prepend);
192 			break;
193 		case ACTION_DEL_COMMUNITY:
194 			printf("community delete ");
195 			print_community(s->action.community.as,
196 			    s->action.community.type);
197 			printf(" ");
198 			break;
199 		case ACTION_SET_COMMUNITY:
200 			printf("community ");
201 			print_community(s->action.community.as,
202 			    s->action.community.type);
203 			printf(" ");
204 			break;
205 		case ACTION_PFTABLE:
206 			printf("pftable %s ", s->action.pftable);
207 			break;
208 		case ACTION_RTLABEL:
209 			printf("rtlabel %s ", s->action.rtlabel);
210 			break;
211 		case ACTION_SET_ORIGIN:
212 			printf("origin ");
213 			print_origin(s->action.origin);
214 			break;
215 		case ACTION_RTLABEL_ID:
216 		case ACTION_PFTABLE_ID:
217 			/* not possible */
218 			printf("king bula saiz: config broken");
219 			break;
220 		case ACTION_SET_EXT_COMMUNITY:
221 			printf("ext-community ");
222 			print_extcommunity(&s->action.ext_community);
223 			break;
224 		case ACTION_DEL_EXT_COMMUNITY:
225 			printf("ext-community delete ");
226 			print_extcommunity(&s->action.ext_community);
227 			break;
228 		}
229 	}
230 	printf("}");
231 }
232 
233 void
234 print_mainconf(struct bgpd_config *conf)
235 {
236 	struct in_addr		 ina;
237 	struct listen_addr	*la;
238 
239 	printf("AS %s", log_as(conf->as));
240 	if (conf->as > USHRT_MAX && conf->short_as != AS_TRANS)
241 		printf(" %u", conf->short_as);
242 	ina.s_addr = conf->bgpid;
243 	printf("\nrouter-id %s\n", inet_ntoa(ina));
244 
245 	printf("socket \"%s\"\n", conf->csock);
246 	if (conf->rcsock)
247 		printf("socket \"%s\" restricted\n", conf->rcsock);
248 	if (conf->holdtime)
249 		printf("holdtime %u\n", conf->holdtime);
250 	if (conf->min_holdtime)
251 		printf("holdtime min %u\n", conf->min_holdtime);
252 	if (conf->connectretry)
253 		printf("connect-retry %u\n", conf->connectretry);
254 
255 	if (conf->flags & BGPD_FLAG_NO_EVALUATE)
256 		printf("route-collector yes\n");
257 
258 	if (conf->flags & BGPD_FLAG_DECISION_ROUTEAGE)
259 		printf("rde route-age evaluate\n");
260 
261 	if (conf->flags & BGPD_FLAG_DECISION_MED_ALWAYS)
262 		printf("rde med compare always\n");
263 
264 	if (conf->log & BGPD_LOG_UPDATES)
265 		printf("log updates\n");
266 
267 	TAILQ_FOREACH(la, conf->listen_addrs, entry)
268 		printf("listen on %s\n",
269 		    log_sockaddr((struct sockaddr *)&la->sa));
270 
271 	if (conf->flags & BGPD_FLAG_NEXTHOP_BGP)
272 		printf("nexthop qualify via bgp\n");
273 	if (conf->flags & BGPD_FLAG_NEXTHOP_DEFAULT)
274 		printf("nexthop qualify via default\n");
275 	printf("fib-priority %hhu", conf->fib_priority);
276 }
277 
278 void
279 print_rdomain_targets(struct filter_set_head *set, const char *tgt)
280 {
281 	struct filter_set	*s;
282 	TAILQ_FOREACH(s, set, entry) {
283 		printf("\t%s ", tgt);
284 		print_extcommunity(&s->action.ext_community);
285 		printf("\n");
286 	}
287 }
288 
289 void
290 print_rdomain(struct rdomain *r)
291 {
292 	struct network *n;
293 
294 	printf("rdomain %u {\n", r->rtableid);
295 	if (*r->descr)
296 		printf("\tdescr \"%s\"\n", r->descr);
297 	if (r->flags & F_RIB_NOFIBSYNC)
298 		printf("\tfib-update no\n");
299 	else
300 		printf("\tfib-update yes\n");
301 	printf("\tdepend on %s\n", r->ifmpe);
302 
303 	TAILQ_FOREACH(n, &r->net_l, entry)
304 		print_network(&n->net, "\t");
305 
306 	printf("\n\t%s\n", log_rd(r->rd));
307 
308 	print_rdomain_targets(&r->export, "export-target");
309 	print_rdomain_targets(&r->import, "import-target");
310 
311 	printf("}\n");
312 }
313 
314 const char *
315 print_af(u_int8_t aid)
316 {
317 	/*
318 	 * Hack around the fact that aid2str() will return "IPv4 unicast"
319 	 * for AID_INET. AID_INET and AID_INET6 need special handling and
320 	 * the other AID should never end up here (at least for now).
321 	 */
322 	if (aid == AID_INET)
323 		return ("inet");
324 	if (aid == AID_INET6)
325 		return ("inet6");
326 	return (aid2str(aid));
327 }
328 
329 void
330 print_network(struct network_config *n, const char *c)
331 {
332 	switch (n->type) {
333 	case NETWORK_STATIC:
334 		printf("%snetwork %s static", c, print_af(n->prefix.aid));
335 		break;
336 	case NETWORK_CONNECTED:
337 		printf("%snetwork %s connected", c, print_af(n->prefix.aid));
338 		break;
339 	default:
340 		printf("%snetwork %s/%u", c, log_addr(&n->prefix),
341 		    n->prefixlen);
342 		break;
343 	}
344 	if (!TAILQ_EMPTY(&n->attrset))
345 		printf(" ");
346 	print_set(&n->attrset);
347 	printf("\n");
348 }
349 
350 void
351 print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c)
352 {
353 	char		*method;
354 	struct in_addr	 ina;
355 
356 	if ((p->remote_addr.aid == AID_INET && p->remote_masklen != 32) ||
357 	    (p->remote_addr.aid == AID_INET6 && p->remote_masklen != 128))
358 		printf("%sneighbor %s/%u {\n", c, log_addr(&p->remote_addr),
359 		    p->remote_masklen);
360 	else
361 		printf("%sneighbor %s {\n", c, log_addr(&p->remote_addr));
362 	if (p->descr[0])
363 		printf("%s\tdescr \"%s\"\n", c, p->descr);
364 	if (p->rib[0])
365 		printf("%s\trib \"%s\"\n", c, p->rib);
366 	if (p->remote_as)
367 		printf("%s\tremote-as %s\n", c, log_as(p->remote_as));
368 	if (p->down)
369 		printf("%s\tdown\n", c);
370 	if (p->distance > 1)
371 		printf("%s\tmultihop %u\n", c, p->distance);
372 	if (p->passive)
373 		printf("%s\tpassive\n", c);
374 	if (p->local_addr.aid)
375 		printf("%s\tlocal-address %s\n", c, log_addr(&p->local_addr));
376 	if (p->max_prefix) {
377 		printf("%s\tmax-prefix %u", c, p->max_prefix);
378 		if (p->max_prefix_restart)
379 			printf(" restart %u", p->max_prefix_restart);
380 		printf("\n");
381 	}
382 	if (p->holdtime)
383 		printf("%s\tholdtime %u\n", c, p->holdtime);
384 	if (p->min_holdtime)
385 		printf("%s\tholdtime min %u\n", c, p->min_holdtime);
386 	if (p->announce_capa == 0)
387 		printf("%s\tannounce capabilities no\n", c);
388 	if (p->capabilities.refresh == 0)
389 		printf("%s\tannounce refresh no\n", c);
390 	if (p->capabilities.grestart.restart == 0)
391 		printf("%s\tannounce restart no\n", c);
392 	if (p->capabilities.as4byte == 0)
393 		printf("%s\tannounce as4byte no\n", c);
394 	if (p->announce_type == ANNOUNCE_SELF)
395 		printf("%s\tannounce self\n", c);
396 	else if (p->announce_type == ANNOUNCE_NONE)
397 		printf("%s\tannounce none\n", c);
398 	else if (p->announce_type == ANNOUNCE_ALL)
399 		printf("%s\tannounce all\n", c);
400 	else if (p->announce_type == ANNOUNCE_DEFAULT_ROUTE)
401 		printf("%s\tannounce default-route\n", c);
402 	else
403 		printf("%s\tannounce ???\n", c);
404 	if (p->enforce_as == ENFORCE_AS_ON)
405 		printf("%s\tenforce neighbor-as yes\n", c);
406 	else
407 		printf("%s\tenforce neighbor-as no\n", c);
408 	if (p->reflector_client) {
409 		if (conf->clusterid == 0)
410 			printf("%s\troute-reflector\n", c);
411 		else {
412 			ina.s_addr = conf->clusterid;
413 			printf("%s\troute-reflector %s\n", c,
414 			    inet_ntoa(ina));
415 		}
416 	}
417 	if (p->demote_group[0])
418 		printf("%s\tdemote %s\n", c, p->demote_group);
419 	if (p->if_depend[0])
420 		printf("%s\tdepend on \"%s\"\n", c, p->if_depend);
421 	if (p->flags & PEERFLAG_TRANS_AS)
422 		printf("%s\ttransparent-as yes\n", c);
423 
424 	if (p->flags & PEERFLAG_LOG_UPDATES)
425 		printf("%s\tlog updates\n", c);
426 
427 	if (p->auth.method == AUTH_MD5SIG)
428 		printf("%s\ttcp md5sig\n", c);
429 	else if (p->auth.method == AUTH_IPSEC_MANUAL_ESP ||
430 	    p->auth.method == AUTH_IPSEC_MANUAL_AH) {
431 		if (p->auth.method == AUTH_IPSEC_MANUAL_ESP)
432 			method = "esp";
433 		else
434 			method = "ah";
435 
436 		printf("%s\tipsec %s in spi %u %s XXXXXX", c, method,
437 		    p->auth.spi_in, print_auth_alg(p->auth.auth_alg_in));
438 		if (p->auth.enc_alg_in)
439 			printf(" %s XXXXXX", print_enc_alg(p->auth.enc_alg_in));
440 		printf("\n");
441 
442 		printf("%s\tipsec %s out spi %u %s XXXXXX", c, method,
443 		    p->auth.spi_out, print_auth_alg(p->auth.auth_alg_out));
444 		if (p->auth.enc_alg_out)
445 			printf(" %s XXXXXX",
446 			    print_enc_alg(p->auth.enc_alg_out));
447 		printf("\n");
448 	} else if (p->auth.method == AUTH_IPSEC_IKE_AH)
449 		printf("%s\tipsec ah ike\n", c);
450 	else if (p->auth.method == AUTH_IPSEC_IKE_ESP)
451 		printf("%s\tipsec esp ike\n", c);
452 
453 	if (p->ttlsec)
454 		printf("%s\tttl-security yes\n", c);
455 
456 	print_announce(p, c);
457 
458 	if (p->softreconfig_in == 1)
459 		printf("%s\tsoftreconfig in yes\n", c);
460 	else
461 		printf("%s\tsoftreconfig in no\n", c);
462 
463 	if (p->softreconfig_out == 1)
464 		printf("%s\tsoftreconfig out yes\n", c);
465 	else
466 		printf("%s\tsoftreconfig out no\n", c);
467 
468 
469 	print_mrt(conf, p->id, p->groupid, c, "\t");
470 
471 	printf("%s}\n", c);
472 }
473 
474 const char *
475 print_auth_alg(u_int8_t alg)
476 {
477 	switch (alg) {
478 	case SADB_AALG_SHA1HMAC:
479 		return ("sha1");
480 	case SADB_AALG_MD5HMAC:
481 		return ("md5");
482 	default:
483 		return ("???");
484 	}
485 }
486 
487 const char *
488 print_enc_alg(u_int8_t alg)
489 {
490 	switch (alg) {
491 	case SADB_EALG_3DESCBC:
492 		return ("3des");
493 	case SADB_X_EALG_AES:
494 		return ("aes");
495 	default:
496 		return ("???");
497 	}
498 }
499 
500 void
501 print_announce(struct peer_config *p, const char *c)
502 {
503 	u_int8_t	aid;
504 
505 	for (aid = 0; aid < AID_MAX; aid++)
506 		if (p->capabilities.mp[aid])
507 			printf("%s\tannounce %s\n", c, aid2str(aid));
508 }
509 
510 void print_as(struct filter_rule *r)
511 {
512 	switch(r->match.as.op) {
513 	case OP_RANGE:
514 		printf("%s - ", log_as(r->match.as.as_min));
515 		printf("%s ", log_as(r->match.as.as_max));
516 		break;
517 	case OP_XRANGE:
518 		printf("%s >< ", log_as(r->match.as.as_min));
519 		printf("%s ", log_as(r->match.as.as_max));
520 		break;
521 	case OP_NE:
522 		printf("!= %s ", log_as(r->match.as.as));
523 		break;
524 	default:
525 		printf("%s ", log_as(r->match.as.as));
526 		break;
527 	}
528 }
529 
530 void
531 print_rule(struct peer *peer_l, struct filter_rule *r)
532 {
533 	struct peer	*p;
534 
535 	if (r->action == ACTION_ALLOW)
536 		printf("allow ");
537 	else if (r->action == ACTION_DENY)
538 		printf("deny ");
539 	else
540 		printf("match ");
541 	if (r->quick)
542 		printf("quick ");
543 
544 	if (r->rib[0])
545 		printf("rib %s ", r->rib);
546 
547 	if (r->dir == DIR_IN)
548 		printf("from ");
549 	else if (r->dir == DIR_OUT)
550 		printf("to ");
551 	else
552 		printf("eeeeeeeps. ");
553 
554 	if (r->peer.peerid) {
555 		for (p = peer_l; p != NULL && p->conf.id != r->peer.peerid;
556 		    p = p->next)
557 			;	/* nothing */
558 		if (p == NULL)
559 			printf("? ");
560 		else
561 			printf("%s ", log_addr(&p->conf.remote_addr));
562 	} else if (r->peer.groupid) {
563 		for (p = peer_l; p != NULL &&
564 		    p->conf.groupid != r->peer.groupid; p = p->next)
565 			;	/* nothing */
566 		if (p == NULL)
567 			printf("group ? ");
568 		else
569 			printf("group \"%s\" ", p->conf.group);
570 	} else if (r->peer.remote_as) {
571 		printf("AS %s ", log_as(r->peer.remote_as));
572 	} else
573 		printf("any ");
574 
575 	if (r->match.prefix.addr.aid)
576 		printf("prefix %s/%u ", log_addr(&r->match.prefix.addr),
577 		    r->match.prefix.len);
578 
579 	if (r->match.prefix.op) {
580 		if (r->match.prefix.op == OP_RANGE ||
581 		    r->match.prefix.op == OP_XRANGE) {
582 			printf("prefixlen %u ", r->match.prefix.len_min);
583 			print_op(r->match.prefix.op);
584 			printf(" %u ", r->match.prefix.len_max);
585 		} else {
586 			printf("prefixlen ");
587 			print_op(r->match.prefix.op);
588 			printf(" %u ", r->match.prefix.len_min);
589 		}
590 	}
591 
592 	if (r->match.nexthop.flags) {
593 		if (r->match.nexthop.flags == FILTER_NEXTHOP_NEIGHBOR)
594 			printf("nexthop neighbor ");
595 		else
596 			printf("nexthop %s ", log_addr(&r->match.nexthop.addr));
597 	}
598 
599 	if (r->match.as.type) {
600 		if (r->match.as.type == AS_ALL)
601 			printf("AS ");
602 		else if (r->match.as.type == AS_SOURCE)
603 			printf("source-as ");
604 		else if (r->match.as.type == AS_TRANSIT)
605 			printf("transit-as ");
606 		else if (r->match.as.type == AS_PEER)
607 			printf("peer-as ");
608 		else
609 			printf("unfluffy-as ");
610 		print_as(r);
611 	}
612 
613 	if (r->match.aslen.type) {
614 		printf("%s %u ", r->match.aslen.type == ASLEN_MAX ?
615 		    "max-as-len" : "max-as-seq", r->match.aslen.aslen);
616 	}
617 
618 	if (r->match.community.as != COMMUNITY_UNSET) {
619 		printf("community ");
620 		print_community(r->match.community.as,
621 		    r->match.community.type);
622 	}
623 	if (r->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) {
624 		printf("ext-community ");
625 		print_extcommunity(&r->match.ext_community);
626 	}
627 
628 	print_set(&r->set);
629 
630 	printf("\n");
631 }
632 
633 const char *
634 mrt_type(enum mrt_type t)
635 {
636 	switch (t) {
637 	case MRT_NONE:
638 		break;
639 	case MRT_TABLE_DUMP:
640 		return "table";
641 	case MRT_TABLE_DUMP_MP:
642 		return "table-mp";
643 	case MRT_TABLE_DUMP_V2:
644 		return "table-v2";
645 	case MRT_ALL_IN:
646 		return "all in";
647 	case MRT_ALL_OUT:
648 		return "all out";
649 	case MRT_UPDATE_IN:
650 		return "updates in";
651 	case MRT_UPDATE_OUT:
652 		return "updates out";
653 	}
654 	return "unfluffy MRT";
655 }
656 
657 void
658 print_mrt(struct bgpd_config *conf, u_int32_t pid, u_int32_t gid,
659     const char *prep, const char *prep2)
660 {
661 	struct mrt	*m;
662 
663 	if (conf->mrt == NULL)
664 		return;
665 
666 	LIST_FOREACH(m, conf->mrt, entry)
667 		if ((gid != 0 && m->group_id == gid) ||
668 		    (m->peer_id == pid && m->group_id == gid)) {
669 			printf("%s%sdump ", prep, prep2);
670 			if (m->rib[0])
671 				printf("rib %s ", m->rib);
672 			printf("%s \"%s\"", mrt_type(m->type),
673 			    MRT2MC(m)->name);
674 			if (MRT2MC(m)->ReopenTimerInterval == 0)
675 				printf("\n");
676 			else
677 				printf(" %d\n", MRT2MC(m)->ReopenTimerInterval);
678 		}
679 }
680 
681 void
682 print_groups(struct bgpd_config *conf, struct peer *peer_l)
683 {
684 	struct peer_config	**peerlist;
685 	struct peer		 *p;
686 	u_int			  peer_cnt, i;
687 	u_int32_t		  prev_groupid;
688 	const char		 *tab	= "\t";
689 	const char		 *nada	= "";
690 	const char		 *c;
691 
692 	peer_cnt = 0;
693 	for (p = peer_l; p != NULL; p = p->next)
694 		peer_cnt++;
695 
696 	if ((peerlist = calloc(peer_cnt, sizeof(struct peer_config *))) == NULL)
697 		fatal("print_groups calloc");
698 
699 	i = 0;
700 	for (p = peer_l; p != NULL; p = p->next)
701 		peerlist[i++] = &p->conf;
702 
703 	qsort(peerlist, peer_cnt, sizeof(struct peer_config *), peer_compare);
704 
705 	prev_groupid = 0;
706 	for (i = 0; i < peer_cnt; i++) {
707 		if (peerlist[i]->groupid) {
708 			c = tab;
709 			if (peerlist[i]->groupid != prev_groupid) {
710 				if (prev_groupid)
711 					printf("}\n\n");
712 				printf("group \"%s\" {\n", peerlist[i]->group);
713 				prev_groupid = peerlist[i]->groupid;
714 			}
715 		} else
716 			c = nada;
717 
718 		print_peer(peerlist[i], conf, c);
719 	}
720 
721 	if (prev_groupid)
722 		printf("}\n\n");
723 
724 	free(peerlist);
725 }
726 
727 int
728 peer_compare(const void *aa, const void *bb)
729 {
730 	const struct peer_config * const *a;
731 	const struct peer_config * const *b;
732 
733 	a = aa;
734 	b = bb;
735 
736 	return ((*a)->groupid - (*b)->groupid);
737 }
738 
739 void
740 print_config(struct bgpd_config *conf, struct rib_names *rib_l,
741     struct network_head *net_l, struct peer *peer_l,
742     struct filter_head *rules_l, struct mrt_head *mrt_l,
743     struct rdomain_head *rdom_l)
744 {
745 	struct filter_rule	*r;
746 	struct network		*n;
747 	struct rde_rib		*rr;
748 	struct rdomain		*rd;
749 
750 	print_mainconf(conf);
751 	printf("\n");
752 	TAILQ_FOREACH(n, net_l, entry)
753 		print_network(&n->net, "");
754 	printf("\n");
755 	SIMPLEQ_FOREACH(rd, rdom_l, entry)
756 		print_rdomain(rd);
757 	printf("\n");
758 	SIMPLEQ_FOREACH(rr, rib_l, entry) {
759 		if (rr->flags & F_RIB_NOEVALUATE)
760 			printf("rde rib %s no evaluate\n", rr->name);
761 		else if (rr->flags & F_RIB_NOFIB)
762 			printf("rde rib %s\n", rr->name);
763 		else
764 			printf("rde rib %s rtable %u fib-update %s\n", rr->name,
765 			    rr->rtableid, rr->flags & F_RIB_NOFIBSYNC ?
766 			    "no" : "yes");
767 	}
768 	printf("\n");
769 	print_mrt(conf, 0, 0, "", "");
770 	printf("\n");
771 	print_groups(conf, peer_l);
772 	printf("\n");
773 	TAILQ_FOREACH(r, rules_l, entry)
774 		print_rule(peer_l, r);
775 }
776