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