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