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