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