xref: /openbsd-src/usr.sbin/bgpd/mrt.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: mrt.c,v 1.77 2014/04/19 15:43:17 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@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 OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 
30 #include "bgpd.h"
31 #include "rde.h"
32 #include "session.h"
33 
34 #include "mrt.h"
35 
36 int mrt_attr_dump(struct ibuf *, struct rde_aspath *, struct bgpd_addr *, int);
37 int mrt_dump_entry_mp(struct mrt *, struct prefix *, u_int16_t,
38     struct rde_peer*);
39 int mrt_dump_entry(struct mrt *, struct prefix *, u_int16_t, struct rde_peer*);
40 int mrt_dump_entry_v2(struct mrt *, struct rib_entry *, u_int32_t);
41 int mrt_dump_peer(struct ibuf *, struct rde_peer *);
42 int mrt_dump_hdr_se(struct ibuf **, struct peer *, u_int16_t, u_int16_t,
43     u_int32_t, int);
44 int mrt_dump_hdr_rde(struct ibuf **, u_int16_t type, u_int16_t, u_int32_t);
45 int mrt_open(struct mrt *, time_t);
46 
47 #define DUMP_BYTE(x, b)							\
48 	do {								\
49 		u_char		t = (b);				\
50 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
51 			log_warn("mrt_dump1: ibuf_add error");		\
52 			goto fail;					\
53 		}							\
54 	} while (0)
55 
56 #define DUMP_SHORT(x, s)						\
57 	do {								\
58 		u_int16_t	t;					\
59 		t = htons((s));						\
60 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
61 			log_warn("mrt_dump2: ibuf_add error");		\
62 			goto fail;					\
63 		}							\
64 	} while (0)
65 
66 #define DUMP_LONG(x, l)							\
67 	do {								\
68 		u_int32_t	t;					\
69 		t = htonl((l));						\
70 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
71 			log_warn("mrt_dump3: ibuf_add error");		\
72 			goto fail;					\
73 		}							\
74 	} while (0)
75 
76 #define DUMP_NLONG(x, l)						\
77 	do {								\
78 		u_int32_t	t = (l);				\
79 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
80 			log_warn("mrt_dump4: ibuf_add error");		\
81 			goto fail;					\
82 		}							\
83 	} while (0)
84 
85 #define RDEIDX		0
86 #define SEIDX		1
87 #define TYPE2IDX(x)	((x == MRT_TABLE_DUMP ||			\
88 			    x == MRT_TABLE_DUMP_MP ||			\
89 			    x == MRT_TABLE_DUMP_V2) ? RDEIDX : SEIDX	\
90 			)
91 
92 void
93 mrt_dump_bgp_msg(struct mrt *mrt, void *pkg, u_int16_t pkglen,
94     struct peer *peer)
95 {
96 	struct ibuf	*buf;
97 	int		 incoming = 0;
98 	u_int16_t	 subtype = BGP4MP_MESSAGE;
99 
100 	if (peer->capa.neg.as4byte)
101 		subtype = BGP4MP_MESSAGE_AS4;
102 
103 	/* get the direction of the message to swap address and AS fields */
104 	if (mrt->type == MRT_ALL_IN || mrt->type == MRT_UPDATE_IN)
105 		incoming = 1;
106 
107 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
108 	    pkglen, incoming) == -1)
109 		return;
110 
111 	if (ibuf_add(buf, pkg, pkglen) == -1) {
112 		log_warn("mrt_dump_bgp_msg: ibuf_add error");
113 		ibuf_free(buf);
114 		return;
115 	}
116 
117 	ibuf_close(&mrt->wbuf, buf);
118 }
119 
120 void
121 mrt_dump_state(struct mrt *mrt, u_int16_t old_state, u_int16_t new_state,
122     struct peer *peer)
123 {
124 	struct ibuf	*buf;
125 	u_int16_t	 subtype = BGP4MP_STATE_CHANGE;
126 
127 	if (peer->capa.neg.as4byte)
128 		subtype = BGP4MP_STATE_CHANGE_AS4;
129 
130 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
131 	    2 * sizeof(short), 0) == -1)
132 		return;
133 
134 	DUMP_SHORT(buf, old_state);
135 	DUMP_SHORT(buf, new_state);
136 
137 	ibuf_close(&mrt->wbuf, buf);
138 	return;
139 
140 fail:
141 	ibuf_free(buf);
142 }
143 
144 int
145 mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop,
146     int v2)
147 {
148 	struct attr	*oa;
149 	u_char		*pdata;
150 	u_int32_t	 tmp;
151 	int		 neednewpath = 0;
152 	u_int16_t	 plen, afi;
153 	u_int8_t	 l, safi;
154 
155 	/* origin */
156 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN,
157 	    &a->origin, 1) == -1)
158 		return (-1);
159 
160 	/* aspath */
161 	pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
162 	if (!v2)
163 		pdata = aspath_deflate(pdata, &plen, &neednewpath);
164 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata,
165 	    plen) == -1) {
166 		free(pdata);
167 		return (-1);
168 	}
169 	free(pdata);
170 
171 	if (nexthop && nexthop->aid == AID_INET) {
172 		/* nexthop, already network byte order */
173 		if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_NEXTHOP,
174 		    &nexthop->v4.s_addr, 4) ==	-1)
175 			return (-1);
176 	}
177 
178 	/* MED, non transitive */
179 	if (a->med != 0) {
180 		tmp = htonl(a->med);
181 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MED, &tmp, 4) == -1)
182 			return (-1);
183 	}
184 
185 	/* local preference */
186 	tmp = htonl(a->lpref);
187 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp, 4) == -1)
188 		return (-1);
189 
190 	/* dump all other path attributes without modification */
191 	for (l = 0; l < a->others_len; l++) {
192 		if ((oa = a->others[l]) == NULL)
193 			break;
194 		if (attr_writebuf(buf, oa->flags, oa->type,
195 		    oa->data, oa->len) == -1)
196 			return (-1);
197 	}
198 
199 	if (nexthop && nexthop->aid != AID_INET) {
200 		struct ibuf *nhbuf;
201 
202 		if ((nhbuf = ibuf_dynamic(0, UCHAR_MAX)) == NULL)
203 			return (-1);
204 		if (!v2) {
205 			if (aid2afi(nexthop->aid, &afi, &safi))
206 				return (-1);
207 			DUMP_SHORT(nhbuf, afi);
208 			DUMP_BYTE(nhbuf, safi);
209 		}
210 		switch (nexthop->aid) {
211 		case AID_INET6:
212 			DUMP_BYTE(nhbuf, sizeof(struct in6_addr));
213 			if (ibuf_add(nhbuf, &nexthop->v6,
214 			    sizeof(struct in6_addr)) == -1) {
215 			}
216 			break;
217 		case AID_VPN_IPv4:
218 			DUMP_BYTE(nhbuf, sizeof(u_int64_t) +
219 			    sizeof(struct in_addr));
220 			DUMP_NLONG(nhbuf, 0);	/* set RD to 0 */
221 			DUMP_NLONG(nhbuf, 0);
222 			DUMP_NLONG(nhbuf, nexthop->v4.s_addr);
223 			break;
224 		}
225 		if (!v2)
226 			DUMP_BYTE(nhbuf, 0);
227 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI,
228 		    nhbuf->buf, ibuf_size(nhbuf)) == -1) {
229 fail:
230 			ibuf_free(nhbuf);
231 			return (-1);
232 		}
233 		ibuf_free(nhbuf);
234 	}
235 
236 	if (neednewpath) {
237 		pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
238 		if (plen != 0)
239 			if (attr_writebuf(buf, ATTR_OPTIONAL|ATTR_TRANSITIVE,
240 			    ATTR_AS4_PATH, pdata, plen) == -1) {
241 				free(pdata);
242 				return (-1);
243 			}
244 		free(pdata);
245 	}
246 
247 	return (0);
248 }
249 
250 int
251 mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
252     struct rde_peer *peer)
253 {
254 	struct ibuf	*buf, *hbuf = NULL, *h2buf = NULL;
255 	struct bgpd_addr addr, nexthop, *nh;
256 	u_int16_t	 len;
257 	u_int8_t	 aid;
258 
259 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
260 		log_warn("mrt_dump_entry_mp: ibuf_dynamic");
261 		return (-1);
262 	}
263 
264 	if (mrt_attr_dump(buf, p->aspath, NULL, 0) == -1) {
265 		log_warnx("mrt_dump_entry_mp: mrt_attr_dump error");
266 		goto fail;
267 	}
268 	len = ibuf_size(buf);
269 
270 	if ((h2buf = ibuf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE +
271 	    MRT_BGP4MP_IPv4_ENTRY_SIZE, MRT_BGP4MP_IPv6_HEADER_SIZE +
272 	    MRT_BGP4MP_IPv6_ENTRY_SIZE + MRT_BGP4MP_MAX_PREFIXLEN)) == NULL) {
273 		log_warn("mrt_dump_entry_mp: ibuf_dynamic");
274 		goto fail;
275 	}
276 
277 	DUMP_SHORT(h2buf, rde_local_as());
278 	DUMP_SHORT(h2buf, peer->short_as);
279 	DUMP_SHORT(h2buf, /* ifindex */ 0);
280 
281 	/* XXX is this for peer self? */
282 	aid = peer->remote_addr.aid == AID_UNSPEC ? p->prefix->aid :
283 	     peer->remote_addr.aid;
284 	switch (aid) {
285 	case AID_INET:
286 		DUMP_SHORT(h2buf, AFI_IPv4);
287 		DUMP_NLONG(h2buf, peer->local_v4_addr.v4.s_addr);
288 		DUMP_NLONG(h2buf, peer->remote_addr.v4.s_addr);
289 		break;
290 	case AID_INET6:
291 		DUMP_SHORT(h2buf, AFI_IPv6);
292 		if (ibuf_add(h2buf, &peer->local_v6_addr.v6,
293 		    sizeof(struct in6_addr)) == -1 ||
294 		    ibuf_add(h2buf, &peer->remote_addr.v6,
295 		    sizeof(struct in6_addr)) == -1) {
296 			log_warn("mrt_dump_entry_mp: ibuf_add error");
297 			goto fail;
298 		}
299 		break;
300 	default:
301 		log_warnx("king bula found new AF in mrt_dump_entry_mp");
302 		goto fail;
303 	}
304 
305 	DUMP_SHORT(h2buf, 0);		/* view */
306 	DUMP_SHORT(h2buf, 1);		/* status */
307 	DUMP_LONG(h2buf, p->lastchange);	/* originated */
308 
309 	pt_getaddr(p->prefix, &addr);
310 
311 	if (p->aspath->nexthop == NULL) {
312 		bzero(&nexthop, sizeof(struct bgpd_addr));
313 		nexthop.aid = addr.aid;
314 		nh = &nexthop;
315 	} else
316 		nh = &p->aspath->nexthop->exit_nexthop;
317 
318 	switch (addr.aid) {
319 	case AID_INET:
320 		DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
321 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
322 		DUMP_BYTE(h2buf, 4);		/* nhlen */
323 		DUMP_NLONG(h2buf, nh->v4.s_addr);	/* nexthop */
324 		break;
325 	case AID_INET6:
326 		DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
327 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
328 		DUMP_BYTE(h2buf, 16);		/* nhlen */
329 		if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
330 			log_warn("mrt_dump_entry_mp: ibuf_add error");
331 			goto fail;
332 		}
333 		break;
334 	default:
335 		log_warnx("king bula found new AF in mrt_dump_entry_mp");
336 		goto fail;
337 	}
338 
339 	if (prefix_writebuf(h2buf, &addr, p->prefix->prefixlen) == -1) {
340 		log_warn("mrt_dump_entry_mp: prefix_writebuf error");
341 		goto fail;
342 	}
343 
344 	DUMP_SHORT(h2buf, len);
345 	len += ibuf_size(h2buf);
346 
347 	if (mrt_dump_hdr_rde(&hbuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY,
348 	    len) == -1)
349 		goto fail;
350 
351 	ibuf_close(&mrt->wbuf, hbuf);
352 	ibuf_close(&mrt->wbuf, h2buf);
353 	ibuf_close(&mrt->wbuf, buf);
354 
355 	return (len + MRT_HEADER_SIZE);
356 
357 fail:
358 	if (hbuf)
359 		ibuf_free(hbuf);
360 	if (h2buf)
361 		ibuf_free(h2buf);
362 	ibuf_free(buf);
363 	return (-1);
364 }
365 
366 int
367 mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum,
368     struct rde_peer *peer)
369 {
370 	struct ibuf	*buf, *hbuf;
371 	struct bgpd_addr addr, *nh;
372 	size_t		 len;
373 	u_int16_t	 subtype;
374 	u_int8_t	 dummy;
375 
376 	if (p->prefix->aid != peer->remote_addr.aid &&
377 	    p->prefix->aid != AID_INET && p->prefix->aid != AID_INET6)
378 		/* only able to dump pure IPv4/IPv6 */
379 		return (0);
380 
381 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
382 		log_warn("mrt_dump_entry: ibuf_dynamic");
383 		return (-1);
384 	}
385 
386 	if (p->aspath->nexthop == NULL) {
387 		bzero(&addr, sizeof(struct bgpd_addr));
388 		addr.aid = p->prefix->aid;
389 		nh = &addr;
390 	} else
391 		nh = &p->aspath->nexthop->exit_nexthop;
392 	if (mrt_attr_dump(buf, p->aspath, nh, 0) == -1) {
393 		log_warnx("mrt_dump_entry: mrt_attr_dump error");
394 		ibuf_free(buf);
395 		return (-1);
396 	}
397 	len = ibuf_size(buf);
398 	aid2afi(p->prefix->aid, &subtype, &dummy);
399 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, subtype, len) == -1) {
400 		ibuf_free(buf);
401 		return (-1);
402 	}
403 
404 	DUMP_SHORT(hbuf, 0);
405 	DUMP_SHORT(hbuf, snum);
406 
407 	pt_getaddr(p->prefix, &addr);
408 	switch (p->prefix->aid) {
409 	case AID_INET:
410 		DUMP_NLONG(hbuf, addr.v4.s_addr);
411 		break;
412 	case AID_INET6:
413 		if (ibuf_add(hbuf, &addr.v6, sizeof(struct in6_addr)) == -1) {
414 			log_warn("mrt_dump_entry: ibuf_add error");
415 			goto fail;
416 		}
417 		break;
418 	}
419 	DUMP_BYTE(hbuf, p->prefix->prefixlen);
420 
421 	DUMP_BYTE(hbuf, 1);		/* state */
422 	DUMP_LONG(hbuf, p->lastchange);	/* originated */
423 	switch (p->prefix->aid) {
424 	case AID_INET:
425 		DUMP_NLONG(hbuf, peer->remote_addr.v4.s_addr);
426 		break;
427 	case AID_INET6:
428 		if (ibuf_add(hbuf, &peer->remote_addr.v6,
429 		    sizeof(struct in6_addr)) == -1) {
430 			log_warn("mrt_dump_entry: ibuf_add error");
431 			goto fail;
432 		}
433 		break;
434 	}
435 	DUMP_SHORT(hbuf, peer->short_as);
436 	DUMP_SHORT(hbuf, len);
437 
438 	ibuf_close(&mrt->wbuf, hbuf);
439 	ibuf_close(&mrt->wbuf, buf);
440 
441 	return (len + MRT_HEADER_SIZE);
442 
443 fail:
444 	ibuf_free(hbuf);
445 	ibuf_free(buf);
446 	return (-1);
447 }
448 
449 int
450 mrt_dump_entry_v2(struct mrt *mrt, struct rib_entry *re, u_int32_t snum)
451 {
452 	struct ibuf	*buf, *hbuf = NULL;
453 	struct prefix	*p;
454 	struct bgpd_addr addr;
455 	size_t		 len, off;
456 	u_int16_t	 subtype, nump;
457 
458 	switch (re->prefix->aid) {
459 	case AID_INET:
460 		subtype = MRT_DUMP_V2_RIB_IPV4_UNICAST;
461 		break;
462 	case AID_INET6:
463 		subtype = MRT_DUMP_V2_RIB_IPV6_UNICAST;
464 		break;
465 	default:
466 		subtype = MRT_DUMP_V2_RIB_GENERIC;
467 		break;
468 	}
469 
470 	if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
471 		log_warn("mrt_dump_entry: ibuf_dynamic");
472 		return (-1);
473 	}
474 
475 	DUMP_LONG(buf, snum);
476 	pt_getaddr(re->prefix, &addr);
477 	if (subtype == MRT_DUMP_V2_RIB_GENERIC) {
478 		u_int16_t afi;
479 		u_int8_t safi;
480 
481 		aid2afi(re->prefix->aid, &afi, &safi);
482 		DUMP_SHORT(buf, afi);
483 		DUMP_BYTE(buf, safi);
484 	}
485 	if (prefix_writebuf(buf, &addr, re->prefix->prefixlen) == -1) {
486 		log_warn("mrt_dump_entry_mp: prefix_writebuf error");
487 		goto fail;
488 	}
489 
490 	off = ibuf_size(buf);
491 	if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
492 		log_warn("mrt_dump_v2_hdr: ibuf_reserve error");
493 		goto fail;
494 	}
495 	nump = 0;
496 	LIST_FOREACH(p, &re->prefix_h, rib_l) {
497 		struct bgpd_addr	*nh;
498 		struct ibuf		*tbuf;
499 
500 		if (p->aspath->nexthop == NULL) {
501 			bzero(&addr, sizeof(struct bgpd_addr));
502 			addr.aid = p->prefix->aid;
503 			nh = &addr;
504 		} else
505 			nh = &p->aspath->nexthop->exit_nexthop;
506 
507 		DUMP_SHORT(buf, p->aspath->peer->mrt_idx);
508 		DUMP_LONG(buf, p->lastchange); /* originated */
509 
510 		if ((tbuf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
511 			log_warn("mrt_dump_entry_v2: ibuf_dynamic");
512 			return (-1);
513 		}
514 		if (mrt_attr_dump(tbuf, p->aspath, nh, 1) == -1) {
515 			log_warnx("mrt_dump_entry_v2: mrt_attr_dump error");
516 			ibuf_free(buf);
517 			return (-1);
518 		}
519 		len = ibuf_size(tbuf);
520 		DUMP_SHORT(buf, (u_int16_t)len);
521 		if (ibuf_add(buf, tbuf->buf, ibuf_size(tbuf)) == -1) {
522 			log_warn("mrt_dump_entry_v2: ibuf_add error");
523 			ibuf_free(tbuf);
524 			return (-1);
525 		}
526 		ibuf_free(tbuf);
527 		nump++;
528 	}
529 	nump = htons(nump);
530 	memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
531 
532 	len = ibuf_size(buf);
533 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, subtype, len) == -1) {
534 		ibuf_free(buf);
535 		return (-1);
536 	}
537 
538 	ibuf_close(&mrt->wbuf, hbuf);
539 	ibuf_close(&mrt->wbuf, buf);
540 
541 	return (0);
542 fail:
543 	if (hbuf)
544 		ibuf_free(hbuf);
545 	ibuf_free(buf);
546 	return (-1);
547 }
548 
549 int
550 mrt_dump_v2_hdr(struct mrt *mrt, struct bgpd_config *conf,
551     struct rde_peer_head *ph)
552 {
553 	struct rde_peer	*peer;
554 	struct ibuf	*buf, *hbuf = NULL;
555 	size_t		 len, off;
556 	u_int16_t	 nlen, nump;
557 
558 	if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
559 		log_warn("mrt_dump_v2_hdr: ibuf_dynamic");
560 		return (-1);
561 	}
562 
563 	DUMP_NLONG(buf, conf->bgpid);
564 	nlen = strlen(mrt->rib);
565 	if (nlen > 0)
566 		nlen += 1;
567 	DUMP_SHORT(buf, nlen);
568 	if (ibuf_add(buf, mrt->rib, nlen) == -1) {
569 		log_warn("mrt_dump_v2_hdr: ibuf_add error");
570 		goto fail;
571 	}
572 
573 	off = ibuf_size(buf);
574 	if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
575 		log_warn("mrt_dump_v2_hdr: ibuf_reserve error");
576 		goto fail;
577 	}
578 	nump = 0;
579 	LIST_FOREACH(peer, ph, peer_l) {
580 		peer->mrt_idx = nump;
581 		if (mrt_dump_peer(buf, peer) == -1)
582 			goto fail;
583 		nump++;
584 	}
585 	nump = htons(nump);
586 	memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
587 
588 	len = ibuf_size(buf);
589 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2,
590 	    MRT_DUMP_V2_PEER_INDEX_TABLE, len) == -1)
591 		goto fail;
592 
593 	ibuf_close(&mrt->wbuf, hbuf);
594 	ibuf_close(&mrt->wbuf, buf);
595 
596 	return (0);
597 fail:
598 	if (hbuf)
599 		ibuf_free(hbuf);
600 	ibuf_free(buf);
601 	return (-1);
602 }
603 
604 int
605 mrt_dump_peer(struct ibuf *buf, struct rde_peer *peer)
606 {
607 	u_int8_t	type = 0;
608 
609 	if (peer->capa.as4byte)
610 		type |= MRT_DUMP_V2_PEER_BIT_A;
611 	if (peer->remote_addr.aid == AID_INET6)
612 		type |= MRT_DUMP_V2_PEER_BIT_I;
613 
614 	DUMP_BYTE(buf, type);
615 	DUMP_LONG(buf, peer->remote_bgpid);
616 
617 	switch (peer->remote_addr.aid) {
618 	case AID_INET:
619 		DUMP_NLONG(buf, peer->remote_addr.v4.s_addr);
620 		break;
621 	case AID_INET6:
622 		if (ibuf_add(buf, &peer->remote_addr.v6,
623 		    sizeof(struct in6_addr)) == -1) {
624 			log_warn("mrt_dump_peer: ibuf_add error");
625 			goto fail;
626 		}
627 		break;
628 	case AID_UNSPEC: /* XXX special handling for peer_self? */
629 		DUMP_NLONG(buf, 0);
630 		break;
631 	default:
632 		log_warnx("king bula found new AF in mrt_dump_entry_mp");
633 		goto fail;
634 	}
635 
636 	if (peer->capa.as4byte)
637 		DUMP_LONG(buf, peer->conf.remote_as);
638 	else
639 		DUMP_SHORT(buf, peer->short_as);
640 
641 	return (0);
642 fail:
643 	return (-1);
644 }
645 
646 void
647 mrt_dump_upcall(struct rib_entry *re, void *ptr)
648 {
649 	struct mrt		*mrtbuf = ptr;
650 	struct prefix		*p;
651 
652 	if (mrtbuf->type == MRT_TABLE_DUMP_V2) {
653 		mrt_dump_entry_v2(mrtbuf, re, mrtbuf->seqnum++);
654 		return;
655 	}
656 
657 	/*
658 	 * dump all prefixes even the inactive ones. That is the way zebra
659 	 * dumps the table so we do the same. If only the active route should
660 	 * be dumped p should be set to p = pt->active.
661 	 */
662 	LIST_FOREACH(p, &re->prefix_h, rib_l) {
663 		if (mrtbuf->type == MRT_TABLE_DUMP)
664 			mrt_dump_entry(mrtbuf, p, mrtbuf->seqnum++,
665 			    p->aspath->peer);
666 		else
667 			mrt_dump_entry_mp(mrtbuf, p, mrtbuf->seqnum++,
668 			    p->aspath->peer);
669 	}
670 }
671 
672 void
673 mrt_done(void *ptr)
674 {
675 	struct mrt		*mrtbuf = ptr;
676 
677 	mrtbuf->state = MRT_STATE_REMOVE;
678 }
679 
680 int
681 mrt_dump_hdr_se(struct ibuf ** bp, struct peer *peer, u_int16_t type,
682     u_int16_t subtype, u_int32_t len, int swap)
683 {
684 	time_t	 	now;
685 
686 	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
687 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + len)) == NULL) {
688 		log_warn("mrt_dump_hdr_se: ibuf_dynamic error");
689 		return (-1);
690 	}
691 
692 	now = time(NULL);
693 
694 	DUMP_LONG(*bp, now);
695 	DUMP_SHORT(*bp, type);
696 	DUMP_SHORT(*bp, subtype);
697 
698 	switch (peer->sa_local.ss_family) {
699 	case AF_INET:
700 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
701 		    subtype == BGP4MP_MESSAGE_AS4)
702 			len += MRT_BGP4MP_AS4_IPv4_HEADER_SIZE;
703 		else
704 			len += MRT_BGP4MP_IPv4_HEADER_SIZE;
705 		break;
706 	case AF_INET6:
707 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
708 		    subtype == BGP4MP_MESSAGE_AS4)
709 			len += MRT_BGP4MP_AS4_IPv6_HEADER_SIZE;
710 		else
711 			len += MRT_BGP4MP_IPv6_HEADER_SIZE;
712 		break;
713 	case 0:
714 		goto fail;
715 	default:
716 		log_warnx("king bula found new AF in mrt_dump_hdr_se");
717 		goto fail;
718 	}
719 
720 	DUMP_LONG(*bp, len);
721 
722 	if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
723 	    subtype == BGP4MP_MESSAGE_AS4) {
724 		if (!swap)
725 			DUMP_LONG(*bp, peer->conf.local_as);
726 		DUMP_LONG(*bp, peer->conf.remote_as);
727 		if (swap)
728 			DUMP_LONG(*bp, peer->conf.local_as);
729 	} else {
730 		if (!swap)
731 			DUMP_SHORT(*bp, peer->conf.local_short_as);
732 		DUMP_SHORT(*bp, peer->short_as);
733 		if (swap)
734 			DUMP_SHORT(*bp, peer->conf.local_short_as);
735 	}
736 
737 	DUMP_SHORT(*bp, /* ifindex */ 0);
738 
739 	switch (peer->sa_local.ss_family) {
740 	case AF_INET:
741 		DUMP_SHORT(*bp, AFI_IPv4);
742 		if (!swap)
743 			DUMP_NLONG(*bp, ((struct sockaddr_in *)
744 			    &peer->sa_local)->sin_addr.s_addr);
745 		DUMP_NLONG(*bp,
746 		    ((struct sockaddr_in *)&peer->sa_remote)->sin_addr.s_addr);
747 		if (swap)
748 			DUMP_NLONG(*bp, ((struct sockaddr_in *)
749 			    &peer->sa_local)->sin_addr.s_addr);
750 		break;
751 	case AF_INET6:
752 		DUMP_SHORT(*bp, AFI_IPv6);
753 		if (!swap)
754 			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
755 			    &peer->sa_local)->sin6_addr,
756 			    sizeof(struct in6_addr)) == -1) {
757 				log_warn("mrt_dump_hdr_se: ibuf_add error");
758 				goto fail;
759 			}
760 		if (ibuf_add(*bp,
761 		    &((struct sockaddr_in6 *)&peer->sa_remote)->sin6_addr,
762 		    sizeof(struct in6_addr)) == -1) {
763 			log_warn("mrt_dump_hdr_se: ibuf_add error");
764 			goto fail;
765 		}
766 		if (swap)
767 			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
768 			    &peer->sa_local)->sin6_addr,
769 			    sizeof(struct in6_addr)) == -1) {
770 				log_warn("mrt_dump_hdr_se: ibuf_add error");
771 				goto fail;
772 			}
773 		break;
774 	}
775 
776 	return (0);
777 
778 fail:
779 	ibuf_free(*bp);
780 	return (-1);
781 }
782 
783 int
784 mrt_dump_hdr_rde(struct ibuf **bp, u_int16_t type, u_int16_t subtype,
785     u_int32_t len)
786 {
787 	time_t		 now;
788 
789 	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
790 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + MRT_BGP4MP_IPv6_ENTRY_SIZE)) ==
791 	    NULL) {
792 		log_warn("mrt_dump_hdr_rde: ibuf_dynamic error");
793 		return (-1);
794 	}
795 
796 	now = time(NULL);
797 	DUMP_LONG(*bp, now);
798 	DUMP_SHORT(*bp, type);
799 	DUMP_SHORT(*bp, subtype);
800 
801 	switch (type) {
802 	case MSG_TABLE_DUMP:
803 		switch (subtype) {
804 		case AFI_IPv4:
805 			len += MRT_DUMP_HEADER_SIZE;
806 			break;
807 		case AFI_IPv6:
808 			len += MRT_DUMP_HEADER_SIZE_V6;
809 			break;
810 		}
811 		DUMP_LONG(*bp, len);
812 		break;
813 	case MSG_PROTOCOL_BGP4MP:
814 	case MSG_TABLE_DUMP_V2:
815 		DUMP_LONG(*bp, len);
816 		break;
817 	default:
818 		log_warnx("mrt_dump_hdr_rde: unsupported type");
819 		goto fail;
820 	}
821 	return (0);
822 
823 fail:
824 	ibuf_free(*bp);
825 	return (-1);
826 }
827 
828 void
829 mrt_write(struct mrt *mrt)
830 {
831 	int	r;
832 
833 	if ((r = ibuf_write(&mrt->wbuf)) < 0 && errno != EAGAIN) {
834 		log_warn("mrt dump aborted, mrt_write");
835 		mrt_clean(mrt);
836 		mrt_done(mrt);
837 	}
838 }
839 
840 void
841 mrt_clean(struct mrt *mrt)
842 {
843 	struct ibuf	*b;
844 
845 	close(mrt->wbuf.fd);
846 	while ((b = TAILQ_FIRST(&mrt->wbuf.bufs))) {
847 		TAILQ_REMOVE(&mrt->wbuf.bufs, b, entry);
848 		ibuf_free(b);
849 	}
850 	mrt->wbuf.queued = 0;
851 }
852 
853 static struct imsgbuf	*mrt_imsgbuf[2];
854 
855 void
856 mrt_init(struct imsgbuf *rde, struct imsgbuf *se)
857 {
858 	mrt_imsgbuf[RDEIDX] = rde;
859 	mrt_imsgbuf[SEIDX] = se;
860 }
861 
862 int
863 mrt_open(struct mrt *mrt, time_t now)
864 {
865 	enum imsg_type	type;
866 	int		fd;
867 
868 	if (strftime(MRT2MC(mrt)->file, sizeof(MRT2MC(mrt)->file),
869 	    MRT2MC(mrt)->name, localtime(&now)) == 0) {
870 		log_warnx("mrt_open: strftime conversion failed");
871 		return (-1);
872 	}
873 
874 	fd = open(MRT2MC(mrt)->file,
875 	    O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC, 0644);
876 	if (fd == -1) {
877 		log_warn("mrt_open %s", MRT2MC(mrt)->file);
878 		return (1);
879 	}
880 
881 	if (mrt->state == MRT_STATE_OPEN)
882 		type = IMSG_MRT_OPEN;
883 	else
884 		type = IMSG_MRT_REOPEN;
885 
886 	if (imsg_compose(mrt_imsgbuf[TYPE2IDX(mrt->type)], type, 0, 0, fd,
887 	    mrt, sizeof(struct mrt)) == -1)
888 		log_warn("mrt_open");
889 
890 	return (1);
891 }
892 
893 int
894 mrt_timeout(struct mrt_head *mrt)
895 {
896 	struct mrt	*m;
897 	time_t		 now;
898 	int		 timeout = MRT_MAX_TIMEOUT;
899 
900 	now = time(NULL);
901 	LIST_FOREACH(m, mrt, entry) {
902 		if (m->state == MRT_STATE_RUNNING &&
903 		    MRT2MC(m)->ReopenTimerInterval != 0) {
904 			if (MRT2MC(m)->ReopenTimer <= now) {
905 				mrt_open(m, now);
906 				MRT2MC(m)->ReopenTimer =
907 				    now + MRT2MC(m)->ReopenTimerInterval;
908 			}
909 			if (MRT2MC(m)->ReopenTimer - now < timeout)
910 				timeout = MRT2MC(m)->ReopenTimer - now;
911 		}
912 	}
913 	return (timeout > 0 ? timeout : 0);
914 }
915 
916 void
917 mrt_reconfigure(struct mrt_head *mrt)
918 {
919 	struct mrt	*m, *xm;
920 	time_t		 now;
921 
922 	now = time(NULL);
923 	for (m = LIST_FIRST(mrt); m != NULL; m = xm) {
924 		xm = LIST_NEXT(m, entry);
925 		if (m->state == MRT_STATE_OPEN ||
926 		    m->state == MRT_STATE_REOPEN) {
927 			if (mrt_open(m, now) == -1)
928 				continue;
929 			if (MRT2MC(m)->ReopenTimerInterval != 0)
930 				MRT2MC(m)->ReopenTimer =
931 				    now + MRT2MC(m)->ReopenTimerInterval;
932 			m->state = MRT_STATE_RUNNING;
933 		}
934 		if (m->state == MRT_STATE_REMOVE) {
935 			if (imsg_compose(mrt_imsgbuf[TYPE2IDX(m->type)],
936 			    IMSG_MRT_CLOSE, 0, 0, -1, m, sizeof(struct mrt)) ==
937 			    -1)
938 				log_warn("mrt_reconfigure");
939 			LIST_REMOVE(m, entry);
940 			free(m);
941 			continue;
942 		}
943 	}
944 }
945 
946 void
947 mrt_handler(struct mrt_head *mrt)
948 {
949 	struct mrt	*m;
950 	time_t		 now;
951 
952 	now = time(NULL);
953 	LIST_FOREACH(m, mrt, entry) {
954 		if (m->state == MRT_STATE_RUNNING &&
955 		    (MRT2MC(m)->ReopenTimerInterval != 0 ||
956 		     m->type == MRT_TABLE_DUMP ||
957 		     m->type == MRT_TABLE_DUMP_MP ||
958 		     m->type == MRT_TABLE_DUMP_V2)) {
959 			if (mrt_open(m, now) == -1)
960 				continue;
961 			MRT2MC(m)->ReopenTimer =
962 			    now + MRT2MC(m)->ReopenTimerInterval;
963 		}
964 	}
965 }
966 
967 struct mrt *
968 mrt_get(struct mrt_head *c, struct mrt *m)
969 {
970 	struct mrt	*t;
971 
972 	LIST_FOREACH(t, c, entry) {
973 		if (t->type != m->type)
974 			continue;
975 		if (strcmp(t->rib, m->rib))
976 			continue;
977 		if (t->peer_id == m->peer_id &&
978 		    t->group_id == m->group_id)
979 			return (t);
980 	}
981 	return (NULL);
982 }
983 
984 int
985 mrt_mergeconfig(struct mrt_head *xconf, struct mrt_head *nconf)
986 {
987 	struct mrt	*m, *xm;
988 
989 	/* both lists here are actually struct mrt_conifg nodes */
990 	LIST_FOREACH(m, nconf, entry) {
991 		if ((xm = mrt_get(xconf, m)) == NULL) {
992 			/* NEW */
993 			if ((xm = (struct mrt *)calloc(1,
994 			    sizeof(struct mrt_config))) == NULL)
995 				fatal("mrt_mergeconfig");
996 			memcpy(xm, m, sizeof(struct mrt_config));
997 			xm->state = MRT_STATE_OPEN;
998 			LIST_INSERT_HEAD(xconf, xm, entry);
999 		} else {
1000 			/* MERGE */
1001 			if (strlcpy(MRT2MC(xm)->name, MRT2MC(m)->name,
1002 			    sizeof(MRT2MC(xm)->name)) >=
1003 			    sizeof(MRT2MC(xm)->name))
1004 				fatalx("mrt_mergeconfig: strlcpy");
1005 			MRT2MC(xm)->ReopenTimerInterval =
1006 			    MRT2MC(m)->ReopenTimerInterval;
1007 			xm->state = MRT_STATE_REOPEN;
1008 		}
1009 	}
1010 
1011 	LIST_FOREACH(xm, xconf, entry)
1012 		if (mrt_get(nconf, xm) == NULL)
1013 			/* REMOVE */
1014 			xm->state = MRT_STATE_REMOVE;
1015 
1016 	/* free config */
1017 	while ((m = LIST_FIRST(nconf)) != NULL) {
1018 		LIST_REMOVE(m, entry);
1019 		free(m);
1020 	}
1021 
1022 	return (0);
1023 }
1024