xref: /openbsd-src/usr.sbin/bgpd/mrt.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: mrt.c,v 1.70 2010/09/02 14:03:21 sobrado 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 <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28 
29 #include "bgpd.h"
30 #include "rde.h"
31 #include "session.h"
32 
33 #include "mrt.h"
34 
35 int mrt_attr_dump(struct ibuf *, struct rde_aspath *, struct bgpd_addr *);
36 int mrt_dump_entry_mp(struct mrt *, struct prefix *, u_int16_t,
37     struct rde_peer*);
38 int mrt_dump_entry(struct mrt *, struct prefix *, u_int16_t, struct rde_peer*);
39 int mrt_dump_hdr_se(struct ibuf **, struct peer *, u_int16_t, u_int16_t,
40     u_int32_t, int);
41 int mrt_dump_hdr_rde(struct ibuf **, u_int16_t type, u_int16_t, u_int32_t);
42 int mrt_open(struct mrt *, time_t);
43 
44 #define DUMP_BYTE(x, b)							\
45 	do {								\
46 		u_char		t = (b);				\
47 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
48 			log_warnx("mrt_dump1: ibuf_add error");		\
49 			goto fail;					\
50 		}							\
51 	} while (0)
52 
53 #define DUMP_SHORT(x, s)						\
54 	do {								\
55 		u_int16_t	t;					\
56 		t = htons((s));						\
57 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
58 			log_warnx("mrt_dump2: ibuf_add error");		\
59 			goto fail;					\
60 		}							\
61 	} while (0)
62 
63 #define DUMP_LONG(x, l)							\
64 	do {								\
65 		u_int32_t	t;					\
66 		t = htonl((l));						\
67 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
68 			log_warnx("mrt_dump3: ibuf_add error");		\
69 			goto fail;					\
70 		}							\
71 	} while (0)
72 
73 #define DUMP_NLONG(x, l)						\
74 	do {								\
75 		u_int32_t	t = (l);				\
76 		if (ibuf_add((x), &t, sizeof(t)) == -1) {		\
77 			log_warnx("mrt_dump4: ibuf_add error");		\
78 			goto fail;					\
79 		}							\
80 	} while (0)
81 
82 void
83 mrt_dump_bgp_msg(struct mrt *mrt, void *pkg, u_int16_t pkglen,
84     struct peer *peer)
85 {
86 	struct ibuf	*buf;
87 	int		 incoming = 0;
88 	u_int16_t	 subtype = BGP4MP_MESSAGE;
89 
90 	if (peer->capa.neg.as4byte)
91 		subtype = BGP4MP_MESSAGE_AS4;
92 
93 	/* get the direction of the message to swap address and AS fields */
94 	if (mrt->type == MRT_ALL_IN || mrt->type == MRT_UPDATE_IN)
95 		incoming = 1;
96 
97 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
98 	    pkglen, incoming) == -1)
99 		return;
100 
101 	if (ibuf_add(buf, pkg, pkglen) == -1) {
102 		log_warnx("mrt_dump_bgp_msg: buf_add error");
103 		ibuf_free(buf);
104 		return;
105 	}
106 
107 	ibuf_close(&mrt->wbuf, buf);
108 }
109 
110 void
111 mrt_dump_state(struct mrt *mrt, u_int16_t old_state, u_int16_t new_state,
112     struct peer *peer)
113 {
114 	struct ibuf	*buf;
115 	u_int16_t	 subtype = BGP4MP_STATE_CHANGE;
116 
117 	if (peer->capa.neg.as4byte)
118 		subtype = BGP4MP_STATE_CHANGE_AS4;
119 
120 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP, subtype,
121 	    2 * sizeof(short), 0) == -1)
122 		return;
123 
124 	DUMP_SHORT(buf, old_state);
125 	DUMP_SHORT(buf, new_state);
126 
127 	ibuf_close(&mrt->wbuf, buf);
128 	return;
129 
130 fail:
131 	ibuf_free(buf);
132 }
133 
134 int
135 mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct bgpd_addr *nexthop)
136 {
137 	struct attr	*oa;
138 	u_char		*pdata;
139 	u_int32_t	 tmp;
140 	int		 neednewpath = 0;
141 	u_int16_t	 plen, afi;
142 	u_int8_t	 l, mpattr[21];
143 
144 	/* origin */
145 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN,
146 	    &a->origin, 1) == -1)
147 		return (-1);
148 
149 	/* aspath */
150 	pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
151 	pdata = aspath_deflate(pdata, &plen, &neednewpath);
152 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata,
153 	    plen) == -1) {
154 		free(pdata);
155 		return (-1);
156 	}
157 	free(pdata);
158 
159 	if (nexthop && nexthop->aid == AID_INET) {
160 		/* nexthop, already network byte order */
161 		if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_NEXTHOP,
162 		    &nexthop->v4.s_addr, 4) ==	-1)
163 			return (-1);
164 	}
165 
166 	/* MED, non transitive */
167 	if (a->med != 0) {
168 		tmp = htonl(a->med);
169 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MED, &tmp, 4) == -1)
170 			return (-1);
171 	}
172 
173 	/* local preference, only valid for ibgp */
174 	tmp = htonl(a->lpref);
175 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp, 4) == -1)
176 		return (-1);
177 
178 	/* dump all other path attributes without modification */
179 	for (l = 0; l < a->others_len; l++) {
180 		if ((oa = a->others[l]) == NULL)
181 			break;
182 		if (attr_writebuf(buf, oa->flags, oa->type,
183 		    oa->data, oa->len) == -1)
184 			return (-1);
185 	}
186 
187 	if (nexthop && nexthop->aid != AID_INET) {
188 		if (aid2afi(nexthop->aid, &afi, &mpattr[2]))
189 			return (-1);
190 		afi = htons(afi);
191 		memcpy(mpattr, &afi, sizeof(afi));
192 		mpattr[3] = sizeof(struct in6_addr);
193 		memcpy(&mpattr[4], &nexthop->v6, sizeof(struct in6_addr));
194 		mpattr[20] = 0; /* Reserved must be 0 */
195 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI,
196 		    mpattr, sizeof(mpattr)) == -1)
197 			return (-1);
198 	}
199 
200 	if (neednewpath) {
201 		pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
202 		if (plen != 0)
203 			if (attr_writebuf(buf, ATTR_OPTIONAL|ATTR_TRANSITIVE,
204 			    ATTR_AS4_PATH, pdata, plen) == -1) {
205 				free(pdata);
206 				return (-1);
207 			}
208 		free(pdata);
209 	}
210 
211 	return (0);
212 }
213 
214 int
215 mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, u_int16_t snum,
216     struct rde_peer *peer)
217 {
218 	struct ibuf	*buf, *hbuf = NULL, *h2buf = NULL;
219 	void		*bptr;
220 	struct bgpd_addr addr, nexthop, *nh;
221 	u_int16_t	 len;
222 	u_int8_t	 p_len;
223 	u_int8_t	 aid;
224 
225 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
226 		log_warn("mrt_dump_entry_mp: buf_dynamic");
227 		return (-1);
228 	}
229 
230 	if (mrt_attr_dump(buf, p->aspath, NULL) == -1) {
231 		log_warnx("mrt_dump_entry_mp: mrt_attr_dump error");
232 		goto fail;
233 	}
234 	len = ibuf_size(buf);
235 
236 	if ((h2buf = ibuf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE +
237 	    MRT_BGP4MP_IPv4_ENTRY_SIZE, MRT_BGP4MP_IPv6_HEADER_SIZE +
238 	    MRT_BGP4MP_IPv6_ENTRY_SIZE + MRT_BGP4MP_MAX_PREFIXLEN)) == NULL) {
239 		log_warn("mrt_dump_entry_mp: buf_dynamic");
240 		goto fail;
241 	}
242 
243 	DUMP_SHORT(h2buf, rde_local_as());
244 	DUMP_SHORT(h2buf, peer->short_as);
245 	DUMP_SHORT(h2buf, /* ifindex */ 0);
246 
247 	/* XXX is this for peer self? */
248 	aid = peer->remote_addr.aid == AID_UNSPEC ? p->prefix->aid :
249 	     peer->remote_addr.aid;
250 	switch (aid) {
251 	case AID_INET:
252 		DUMP_SHORT(h2buf, AFI_IPv4);
253 		DUMP_NLONG(h2buf, peer->local_v4_addr.v4.s_addr);
254 		DUMP_NLONG(h2buf, peer->remote_addr.v4.s_addr);
255 		break;
256 	case AID_INET6:
257 		DUMP_SHORT(h2buf, AFI_IPv6);
258 		if (ibuf_add(h2buf, &peer->local_v6_addr.v6,
259 		    sizeof(struct in6_addr)) == -1 ||
260 		    ibuf_add(h2buf, &peer->remote_addr.v6,
261 		    sizeof(struct in6_addr)) == -1) {
262 			log_warnx("mrt_dump_entry_mp: buf_add error");
263 			goto fail;
264 		}
265 		break;
266 	default:
267 		log_warnx("king bula found new AF in mrt_dump_entry_mp");
268 		goto fail;
269 	}
270 
271 	DUMP_SHORT(h2buf, 0);		/* view */
272 	DUMP_SHORT(h2buf, 1);		/* status */
273 	DUMP_LONG(h2buf, p->lastchange);	/* originated */
274 
275 	if (p->aspath->nexthop == NULL) {
276 		bzero(&nexthop, sizeof(struct bgpd_addr));
277 		nexthop.aid = addr.aid;
278 		nh = &nexthop;
279 	} else
280 		nh = &p->aspath->nexthop->exit_nexthop;
281 
282 	pt_getaddr(p->prefix, &addr);
283 	switch (addr.aid) {
284 	case AID_INET:
285 		DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
286 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
287 		DUMP_BYTE(h2buf, 4);		/* nhlen */
288 		DUMP_NLONG(h2buf, nh->v4.s_addr);	/* nexthop */
289 		break;
290 	case AID_INET6:
291 		DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
292 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
293 		DUMP_BYTE(h2buf, 16);		/* nhlen */
294 		if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
295 			log_warnx("mrt_dump_entry_mp: buf_add error");
296 			goto fail;
297 		}
298 		break;
299 	default:
300 		log_warnx("king bula found new AF in mrt_dump_entry_mp");
301 		goto fail;
302 	}
303 
304 	p_len = PREFIX_SIZE(p->prefix->prefixlen);
305 	if ((bptr = ibuf_reserve(h2buf, p_len)) == NULL) {
306 		log_warnx("mrt_dump_entry_mp: buf_reserve error");
307 		goto fail;
308 	}
309 	if (prefix_write(bptr, p_len, &addr, p->prefix->prefixlen) == -1) {
310 		log_warnx("mrt_dump_entry_mp: prefix_write error");
311 		goto fail;
312 	}
313 
314 	DUMP_SHORT(h2buf, len);
315 	len += ibuf_size(h2buf);
316 
317 	if (mrt_dump_hdr_rde(&hbuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY,
318 	    len) == -1)
319 		goto fail;
320 
321 	ibuf_close(&mrt->wbuf, hbuf);
322 	ibuf_close(&mrt->wbuf, h2buf);
323 	ibuf_close(&mrt->wbuf, buf);
324 
325 	return (len + MRT_HEADER_SIZE);
326 
327 fail:
328 	if (hbuf)
329 		ibuf_free(hbuf);
330 	if (h2buf)
331 		ibuf_free(h2buf);
332 	ibuf_free(buf);
333 	return (-1);
334 }
335 
336 int
337 mrt_dump_entry(struct mrt *mrt, struct prefix *p, u_int16_t snum,
338     struct rde_peer *peer)
339 {
340 	struct ibuf	*buf, *hbuf;
341 	struct bgpd_addr addr, *nh;
342 	size_t		 len;
343 	u_int16_t	 subtype;
344 	u_int8_t	 dummy;
345 
346 	if (p->prefix->aid != peer->remote_addr.aid &&
347 	    p->prefix->aid != AID_INET && p->prefix->aid != AID_INET6)
348 		/* only able to dump pure IPv4/IPv6 */
349 		return (0);
350 
351 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
352 		log_warnx("mrt_dump_entry: buf_dynamic");
353 		return (-1);
354 	}
355 
356 	if (p->aspath->nexthop == NULL) {
357 		bzero(&addr, sizeof(struct bgpd_addr));
358 		addr.aid = p->prefix->aid;
359 		nh = &addr;
360 	} else
361 		nh = &p->aspath->nexthop->exit_nexthop;
362 	if (mrt_attr_dump(buf, p->aspath, nh) == -1) {
363 		log_warnx("mrt_dump_entry: mrt_attr_dump error");
364 		ibuf_free(buf);
365 		return (-1);
366 	}
367 	len = ibuf_size(buf);
368 	aid2afi(p->prefix->aid, &subtype, &dummy);
369 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, subtype, len) == -1) {
370 		ibuf_free(buf);
371 		return (-1);
372 	}
373 
374 	DUMP_SHORT(hbuf, 0);
375 	DUMP_SHORT(hbuf, snum);
376 
377 	pt_getaddr(p->prefix, &addr);
378 	switch (p->prefix->aid) {
379 	case AID_INET:
380 		DUMP_NLONG(hbuf, addr.v4.s_addr);
381 		break;
382 	case AID_INET6:
383 		if (ibuf_add(hbuf, &addr.v6, sizeof(struct in6_addr)) == -1) {
384 			log_warnx("mrt_dump_entry: buf_add error");
385 			goto fail;
386 		}
387 		break;
388 	}
389 	DUMP_BYTE(hbuf, p->prefix->prefixlen);
390 
391 	DUMP_BYTE(hbuf, 1);		/* state */
392 	DUMP_LONG(hbuf, p->lastchange);	/* originated */
393 	switch (p->prefix->aid) {
394 	case AID_INET:
395 		DUMP_NLONG(hbuf, peer->remote_addr.v4.s_addr);
396 		break;
397 	case AID_INET6:
398 		if (ibuf_add(hbuf, &peer->remote_addr.v6,
399 		    sizeof(struct in6_addr)) == -1) {
400 			log_warnx("mrt_dump_entry: buf_add error");
401 			goto fail;
402 		}
403 		break;
404 	}
405 	DUMP_SHORT(hbuf, peer->short_as);
406 	DUMP_SHORT(hbuf, len);
407 
408 	ibuf_close(&mrt->wbuf, hbuf);
409 	ibuf_close(&mrt->wbuf, buf);
410 
411 	return (len + MRT_HEADER_SIZE);
412 
413 fail:
414 	ibuf_free(hbuf);
415 	ibuf_free(buf);
416 	return (-1);
417 }
418 
419 void
420 mrt_dump_upcall(struct rib_entry *re, void *ptr)
421 {
422 	struct mrt		*mrtbuf = ptr;
423 	struct prefix		*p;
424 
425 	/*
426 	 * dump all prefixes even the inactive ones. That is the way zebra
427 	 * dumps the table so we do the same. If only the active route should
428 	 * be dumped p should be set to p = pt->active.
429 	 */
430 	LIST_FOREACH(p, &re->prefix_h, rib_l) {
431 		if (mrtbuf->type == MRT_TABLE_DUMP)
432 			mrt_dump_entry(mrtbuf, p, mrtbuf->seqnum++,
433 			    p->aspath->peer);
434 		else
435 			mrt_dump_entry_mp(mrtbuf, p, mrtbuf->seqnum++,
436 			    p->aspath->peer);
437 	}
438 }
439 
440 void
441 mrt_done(void *ptr)
442 {
443 	struct mrt		*mrtbuf = ptr;
444 
445 	mrtbuf->state = MRT_STATE_REMOVE;
446 }
447 
448 int
449 mrt_dump_hdr_se(struct ibuf ** bp, struct peer *peer, u_int16_t type,
450     u_int16_t subtype, u_int32_t len, int swap)
451 {
452 	time_t	 	now;
453 
454 	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
455 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + len)) == NULL) {
456 		log_warnx("mrt_dump_hdr_se: buf_open error");
457 		return (-1);
458 	}
459 
460 	now = time(NULL);
461 
462 	DUMP_LONG(*bp, now);
463 	DUMP_SHORT(*bp, type);
464 	DUMP_SHORT(*bp, subtype);
465 
466 	switch (peer->sa_local.ss_family) {
467 	case AF_INET:
468 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
469 		    subtype == BGP4MP_MESSAGE_AS4)
470 			len += MRT_BGP4MP_AS4_IPv4_HEADER_SIZE;
471 		else
472 			len += MRT_BGP4MP_IPv4_HEADER_SIZE;
473 		break;
474 	case AF_INET6:
475 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
476 		    subtype == BGP4MP_MESSAGE_AS4)
477 			len += MRT_BGP4MP_AS4_IPv6_HEADER_SIZE;
478 		else
479 			len += MRT_BGP4MP_IPv6_HEADER_SIZE;
480 		break;
481 	case 0:
482 		goto fail;
483 	default:
484 		log_warnx("king bula found new AF in mrt_dump_hdr_se");
485 		goto fail;
486 	}
487 
488 	DUMP_LONG(*bp, len);
489 
490 	if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
491 	    subtype == BGP4MP_MESSAGE_AS4) {
492 		if (!swap)
493 			DUMP_LONG(*bp, peer->conf.local_as);
494 		DUMP_LONG(*bp, peer->conf.remote_as);
495 		if (swap)
496 			DUMP_LONG(*bp, peer->conf.local_as);
497 	} else {
498 		if (!swap)
499 			DUMP_SHORT(*bp, peer->conf.local_short_as);
500 		DUMP_SHORT(*bp, peer->short_as);
501 		if (swap)
502 			DUMP_SHORT(*bp, peer->conf.local_short_as);
503 	}
504 
505 	DUMP_SHORT(*bp, /* ifindex */ 0);
506 
507 	switch (peer->sa_local.ss_family) {
508 	case AF_INET:
509 		DUMP_SHORT(*bp, AFI_IPv4);
510 		if (!swap)
511 			DUMP_NLONG(*bp, ((struct sockaddr_in *)
512 			    &peer->sa_local)->sin_addr.s_addr);
513 		DUMP_NLONG(*bp,
514 		    ((struct sockaddr_in *)&peer->sa_remote)->sin_addr.s_addr);
515 		if (swap)
516 			DUMP_NLONG(*bp, ((struct sockaddr_in *)
517 			    &peer->sa_local)->sin_addr.s_addr);
518 		break;
519 	case AF_INET6:
520 		DUMP_SHORT(*bp, AFI_IPv6);
521 		if (!swap)
522 			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
523 			    &peer->sa_local)->sin6_addr,
524 			    sizeof(struct in6_addr)) == -1) {
525 				log_warnx("mrt_dump_hdr_se: buf_add error");
526 				goto fail;
527 			}
528 		if (ibuf_add(*bp,
529 		    &((struct sockaddr_in6 *)&peer->sa_remote)->sin6_addr,
530 		    sizeof(struct in6_addr)) == -1) {
531 			log_warnx("mrt_dump_hdr_se: buf_add error");
532 			goto fail;
533 		}
534 		if (swap)
535 			if (ibuf_add(*bp, &((struct sockaddr_in6 *)
536 			    &peer->sa_local)->sin6_addr,
537 			    sizeof(struct in6_addr)) == -1) {
538 				log_warnx("mrt_dump_hdr_se: buf_add error");
539 				goto fail;
540 			}
541 		break;
542 	}
543 
544 	return (0);
545 
546 fail:
547 	ibuf_free(*bp);
548 	return (-1);
549 }
550 
551 int
552 mrt_dump_hdr_rde(struct ibuf **bp, u_int16_t type, u_int16_t subtype,
553     u_int32_t len)
554 {
555 	time_t		 now;
556 
557 	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
558 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + MRT_BGP4MP_IPv6_ENTRY_SIZE)) ==
559 	    NULL) {
560 		log_warnx("mrt_dump_hdr_rde: buf_dynamic error");
561 		return (-1);
562 	}
563 
564 	now = time(NULL);
565 	DUMP_LONG(*bp, now);
566 	DUMP_SHORT(*bp, type);
567 	DUMP_SHORT(*bp, subtype);
568 
569 	switch (type) {
570 	case MSG_TABLE_DUMP:
571 		switch (subtype) {
572 		case AFI_IPv4:
573 			len += MRT_DUMP_HEADER_SIZE;
574 			break;
575 		case AFI_IPv6:
576 			len += MRT_DUMP_HEADER_SIZE_V6;
577 			break;
578 		}
579 		DUMP_LONG(*bp, len);
580 		break;
581 	case MSG_PROTOCOL_BGP4MP:
582 		DUMP_LONG(*bp, len);
583 		break;
584 	default:
585 		log_warnx("mrt_dump_hdr_rde: unsupported type");
586 		goto fail;
587 	}
588 	return (0);
589 
590 fail:
591 	ibuf_free(*bp);
592 	return (-1);
593 }
594 
595 void
596 mrt_write(struct mrt *mrt)
597 {
598 	int	r;
599 
600 	if ((r = ibuf_write(&mrt->wbuf)) < 0) {
601 		log_warn("mrt dump aborted, mrt_write");
602 		mrt_clean(mrt);
603 		mrt_done(mrt);
604 	}
605 }
606 
607 void
608 mrt_clean(struct mrt *mrt)
609 {
610 	struct ibuf	*b;
611 
612 	close(mrt->wbuf.fd);
613 	while ((b = TAILQ_FIRST(&mrt->wbuf.bufs))) {
614 		TAILQ_REMOVE(&mrt->wbuf.bufs, b, entry);
615 		ibuf_free(b);
616 	}
617 	mrt->wbuf.queued = 0;
618 }
619 
620 static struct imsgbuf	*mrt_imsgbuf[2];
621 
622 void
623 mrt_init(struct imsgbuf *rde, struct imsgbuf *se)
624 {
625 	mrt_imsgbuf[0] = rde;
626 	mrt_imsgbuf[1] = se;
627 }
628 
629 int
630 mrt_open(struct mrt *mrt, time_t now)
631 {
632 	enum imsg_type	type;
633 	int		i = 1, fd;
634 
635 	if (strftime(MRT2MC(mrt)->file, sizeof(MRT2MC(mrt)->file),
636 	    MRT2MC(mrt)->name, localtime(&now)) == 0) {
637 		log_warnx("mrt_open: strftime conversion failed");
638 		return (-1);
639 	}
640 
641 	fd = open(MRT2MC(mrt)->file,
642 	    O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC, 0644);
643 	if (fd == -1) {
644 		log_warn("mrt_open %s", MRT2MC(mrt)->file);
645 		return (1);
646 	}
647 
648 	if (mrt->state == MRT_STATE_OPEN)
649 		type = IMSG_MRT_OPEN;
650 	else
651 		type = IMSG_MRT_REOPEN;
652 
653 	if (mrt->type == MRT_TABLE_DUMP || mrt->type == MRT_TABLE_DUMP_MP)
654 		i = 0;
655 
656 	if (imsg_compose(mrt_imsgbuf[i], type, 0, 0, fd,
657 	    mrt, sizeof(struct mrt)) == -1)
658 		log_warn("mrt_open");
659 
660 	return (1);
661 }
662 
663 int
664 mrt_timeout(struct mrt_head *mrt)
665 {
666 	struct mrt	*m;
667 	time_t		 now;
668 	int		 timeout = MRT_MAX_TIMEOUT;
669 
670 	now = time(NULL);
671 	LIST_FOREACH(m, mrt, entry) {
672 		if (m->state == MRT_STATE_RUNNING &&
673 		    MRT2MC(m)->ReopenTimerInterval != 0) {
674 			if (MRT2MC(m)->ReopenTimer <= now) {
675 				mrt_open(m, now);
676 				MRT2MC(m)->ReopenTimer =
677 				    now + MRT2MC(m)->ReopenTimerInterval;
678 			}
679 			if (MRT2MC(m)->ReopenTimer - now < timeout)
680 				timeout = MRT2MC(m)->ReopenTimer - now;
681 		}
682 	}
683 	return (timeout > 0 ? timeout : 0);
684 }
685 
686 void
687 mrt_reconfigure(struct mrt_head *mrt)
688 {
689 	struct mrt	*m, *xm;
690 	time_t		 now;
691 
692 	now = time(NULL);
693 	for (m = LIST_FIRST(mrt); m != NULL; m = xm) {
694 		xm = LIST_NEXT(m, entry);
695 		if (m->state == MRT_STATE_OPEN ||
696 		    m->state == MRT_STATE_REOPEN) {
697 			if (mrt_open(m, now) == -1)
698 				continue;
699 			if (MRT2MC(m)->ReopenTimerInterval != 0)
700 				MRT2MC(m)->ReopenTimer =
701 				    now + MRT2MC(m)->ReopenTimerInterval;
702 			m->state = MRT_STATE_RUNNING;
703 		}
704 		if (m->state == MRT_STATE_REMOVE) {
705 			LIST_REMOVE(m, entry);
706 			free(m);
707 			continue;
708 		}
709 	}
710 }
711 
712 void
713 mrt_handler(struct mrt_head *mrt)
714 {
715 	struct mrt	*m;
716 	time_t		 now;
717 
718 	now = time(NULL);
719 	LIST_FOREACH(m, mrt, entry) {
720 		if (m->state == MRT_STATE_RUNNING &&
721 		    (MRT2MC(m)->ReopenTimerInterval != 0 ||
722 		     m->type == MRT_TABLE_DUMP)) {
723 			if (mrt_open(m, now) == -1)
724 				continue;
725 			MRT2MC(m)->ReopenTimer =
726 			    now + MRT2MC(m)->ReopenTimerInterval;
727 		}
728 	}
729 }
730 
731 struct mrt *
732 mrt_get(struct mrt_head *c, struct mrt *m)
733 {
734 	struct mrt	*t;
735 
736 	LIST_FOREACH(t, c, entry) {
737 		if (t->type != m->type)
738 			continue;
739 		if (strcmp(t->rib, m->rib))
740 			continue;
741 		if (t->peer_id == m->peer_id &&
742 		    t->group_id == m->group_id)
743 			return (t);
744 	}
745 	return (NULL);
746 }
747 
748 int
749 mrt_mergeconfig(struct mrt_head *xconf, struct mrt_head *nconf)
750 {
751 	struct mrt	*m, *xm;
752 
753 	LIST_FOREACH(m, nconf, entry) {
754 		if ((xm = mrt_get(xconf, m)) == NULL) {
755 			/* NEW */
756 			if ((xm = calloc(1, sizeof(struct mrt_config))) == NULL)
757 				fatal("mrt_mergeconfig");
758 			memcpy(xm, m, sizeof(struct mrt_config));
759 			xm->state = MRT_STATE_OPEN;
760 			LIST_INSERT_HEAD(xconf, xm, entry);
761 		} else {
762 			/* MERGE */
763 			if (strlcpy(MRT2MC(xm)->name, MRT2MC(m)->name,
764 			    sizeof(MRT2MC(xm)->name)) >=
765 			    sizeof(MRT2MC(xm)->name))
766 				fatalx("mrt_mergeconfig: strlcpy");
767 			MRT2MC(xm)->ReopenTimerInterval =
768 			    MRT2MC(m)->ReopenTimerInterval;
769 			xm->state = MRT_STATE_REOPEN;
770 		}
771 	}
772 
773 	LIST_FOREACH(xm, xconf, entry)
774 		if (mrt_get(nconf, xm) == NULL)
775 			/* REMOVE */
776 			xm->state = MRT_STATE_REMOVE;
777 
778 	/* free config */
779 	while ((m = LIST_FIRST(nconf)) != NULL) {
780 		LIST_REMOVE(m, entry);
781 		free(m);
782 	}
783 
784 	return (0);
785 }
786