xref: /openbsd-src/usr.sbin/bgpd/mrt.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: mrt.c,v 1.114 2023/04/19 09:03:00 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 *, uint16_t,
40     struct rde_peer*);
41 int mrt_dump_entry(struct mrt *, struct prefix *, uint16_t, struct rde_peer*);
42 int mrt_dump_entry_v2(struct mrt *, struct rib_entry *, uint32_t);
43 int mrt_dump_peer(struct ibuf *, struct rde_peer *);
44 int mrt_dump_hdr_se(struct ibuf **, struct peer *, uint16_t, uint16_t,
45     uint32_t, int);
46 int mrt_dump_hdr_rde(struct ibuf **, uint16_t type, uint16_t, uint32_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 		uint16_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 		uint32_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 		uint32_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 static uint8_t
95 mrt_update_msg_guess_aid(uint8_t *pkg, uint16_t pkglen)
96 {
97 	uint16_t wlen, alen, len, afi;
98 	uint8_t type, aid;
99 
100 	pkg += MSGSIZE_HEADER;
101 	pkglen -= MSGSIZE_HEADER;
102 
103 	if (pkglen < 4)
104 		goto bad;
105 
106 	memcpy(&wlen, pkg, 2);
107 	wlen = ntohs(wlen);
108 	pkg += 2;
109 	pkglen -= 2;
110 
111 	if (wlen > 0) {
112 		/* UPDATE has withdraw routes, therefore IPv4 */
113 		return AID_INET;
114 	}
115 
116 	memcpy(&alen, pkg, 2);
117 	alen = ntohs(alen);
118 	pkg += 2;
119 	pkglen -= 2;
120 
121 	if (alen < pkglen) {
122 		/* UPDATE has NLRI prefixes, therefore IPv4 */
123 		return AID_INET;
124 	}
125 
126 	if (wlen == 0 && alen == 0) {
127 		/* UPDATE is an IPv4 EoR marker */
128 		return AID_INET;
129 	}
130 
131 	/* bad attribute length */
132 	if (alen > pkglen)
133 		goto bad;
134 
135 	/* try to extract AFI/SAFI from the MP attributes */
136 	while (alen > 0) {
137 		if (alen < 3)
138 			goto bad;
139 		type = pkg[1];
140 		if (pkg[0] & ATTR_EXTLEN) {
141 			if (alen < 4)
142 				goto bad;
143 			memcpy(&len, pkg + 2, 2);
144 			len = ntohs(len);
145 			pkg += 4;
146 			alen -= 4;
147 		} else {
148 			len = pkg[2];
149 			pkg += 3;
150 			alen -= 3;
151 		}
152 		if (len > alen)
153 			goto bad;
154 
155 		if (type == ATTR_MP_REACH_NLRI ||
156 		    type == ATTR_MP_UNREACH_NLRI) {
157 			if (alen < 3)
158 				goto bad;
159 			memcpy(&afi, pkg, 2);
160 			afi = ntohs(afi);
161 			if (afi2aid(afi, pkg[2], &aid) == -1)
162 				goto bad;
163 			return aid;
164 		}
165 
166 		pkg += len;
167 		alen -= len;
168 	}
169 
170 bad:
171 	return AID_UNSPEC;
172 }
173 
174 static uint16_t
175 mrt_bgp_msg_subtype(struct mrt *mrt, void *pkg, uint16_t pkglen,
176     struct peer *peer, enum msg_type msgtype, int in)
177 {
178 	uint16_t subtype = BGP4MP_MESSAGE;
179 	uint8_t aid, mask;
180 
181 	if (peer->capa.neg.as4byte)
182 		subtype = BGP4MP_MESSAGE_AS4;
183 
184 	if (msgtype != UPDATE)
185 		return subtype;
186 
187 	/*
188 	 * RFC8050 adjust types for add-path enabled sessions.
189 	 * It is necessary to extract the AID from UPDATES to decide
190 	 * if the add-path types are needed or not. The ADDPATH
191 	 * subtypes only matter for BGP UPDATES.
192 	 */
193 
194 	mask = in ? CAPA_AP_RECV : CAPA_AP_SEND;
195 	/* only guess if add-path could be active */
196 	if (peer->capa.neg.add_path[0] & mask) {
197 		aid = mrt_update_msg_guess_aid(pkg, pkglen);
198 		if (aid != AID_UNSPEC &&
199 		    (peer->capa.neg.add_path[aid] & mask)) {
200 			if (peer->capa.neg.as4byte)
201 				subtype = BGP4MP_MESSAGE_AS4_ADDPATH;
202 			else
203 				subtype = BGP4MP_MESSAGE_ADDPATH;
204 		}
205 	}
206 
207 	return subtype;
208 }
209 
210 void
211 mrt_dump_bgp_msg(struct mrt *mrt, void *pkg, uint16_t pkglen,
212     struct peer *peer, enum msg_type msgtype)
213 {
214 	struct ibuf	*buf;
215 	int		 in = 0;
216 	uint16_t	 subtype = BGP4MP_MESSAGE;
217 
218 	/* get the direction of the message to swap address and AS fields */
219 	if (mrt->type == MRT_ALL_IN || mrt->type == MRT_UPDATE_IN)
220 		in = 1;
221 
222 	subtype = mrt_bgp_msg_subtype(mrt, pkg, pkglen, peer, msgtype, in);
223 
224 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP_ET, subtype,
225 	    pkglen, in) == -1)
226 		return;
227 
228 	if (ibuf_add(buf, pkg, pkglen) == -1) {
229 		log_warn("mrt_dump_bgp_msg: ibuf_add error");
230 		ibuf_free(buf);
231 		return;
232 	}
233 
234 	ibuf_close(&mrt->wbuf, buf);
235 }
236 
237 void
238 mrt_dump_state(struct mrt *mrt, uint16_t old_state, uint16_t new_state,
239     struct peer *peer)
240 {
241 	struct ibuf	*buf;
242 	uint16_t	 subtype = BGP4MP_STATE_CHANGE;
243 
244 	if (peer->capa.neg.as4byte)
245 		subtype = BGP4MP_STATE_CHANGE_AS4;
246 
247 	if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP_ET, subtype,
248 	    2 * sizeof(short), 0) == -1)
249 		return;
250 
251 	DUMP_SHORT(buf, old_state);
252 	DUMP_SHORT(buf, new_state);
253 
254 	ibuf_close(&mrt->wbuf, buf);
255 	return;
256 
257 fail:
258 	ibuf_free(buf);
259 }
260 
261 int
262 mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct rde_community *c,
263     struct bgpd_addr *nexthop, int v2)
264 {
265 	struct attr	*oa;
266 	u_char		*pdata;
267 	uint32_t	 tmp;
268 	int		 neednewpath = 0;
269 	uint16_t	 plen, afi;
270 	uint8_t		 l, safi;
271 
272 	/* origin */
273 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN,
274 	    &a->origin, 1) == -1)
275 		return (-1);
276 
277 	/* aspath */
278 	plen = aspath_length(a->aspath);
279 	pdata = aspath_dump(a->aspath);
280 
281 	if (!v2)
282 		pdata = aspath_deflate(pdata, &plen, &neednewpath);
283 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata,
284 	    plen) == -1) {
285 		if (!v2)
286 			free(pdata);
287 		return (-1);
288 	}
289 	if (!v2)
290 		free(pdata);
291 
292 	if (nexthop && nexthop->aid == AID_INET) {
293 		/* nexthop, already network byte order */
294 		if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_NEXTHOP,
295 		    &nexthop->v4.s_addr, 4) ==	-1)
296 			return (-1);
297 	}
298 
299 	/* MED, non transitive */
300 	if (a->med != 0) {
301 		tmp = htonl(a->med);
302 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MED, &tmp, 4) == -1)
303 			return (-1);
304 	}
305 
306 	/* local preference */
307 	tmp = htonl(a->lpref);
308 	if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp, 4) == -1)
309 		return (-1);
310 
311 	/* communities */
312 	if (community_writebuf(buf, c) == -1)
313 		return (-1);
314 
315 	/* dump all other path attributes without modification */
316 	for (l = 0; l < a->others_len; l++) {
317 		if ((oa = a->others[l]) == NULL)
318 			break;
319 		if (attr_writebuf(buf, oa->flags, oa->type,
320 		    oa->data, oa->len) == -1)
321 			return (-1);
322 	}
323 
324 	if (nexthop && nexthop->aid != AID_INET) {
325 		struct ibuf *nhbuf;
326 
327 		if ((nhbuf = ibuf_dynamic(0, UCHAR_MAX)) == NULL)
328 			return (-1);
329 		if (!v2) {
330 			if (aid2afi(nexthop->aid, &afi, &safi))
331 				return (-1);
332 			DUMP_SHORT(nhbuf, afi);
333 			DUMP_BYTE(nhbuf, safi);
334 		}
335 		switch (nexthop->aid) {
336 		case AID_INET6:
337 			DUMP_BYTE(nhbuf, sizeof(struct in6_addr));
338 			if (ibuf_add(nhbuf, &nexthop->v6,
339 			    sizeof(struct in6_addr)) == -1)
340 				goto fail;
341 			break;
342 		case AID_VPN_IPv4:
343 			DUMP_BYTE(nhbuf, sizeof(uint64_t) +
344 			    sizeof(struct in_addr));
345 			DUMP_NLONG(nhbuf, 0);	/* set RD to 0 */
346 			DUMP_NLONG(nhbuf, 0);
347 			DUMP_NLONG(nhbuf, nexthop->v4.s_addr);
348 			break;
349 		case AID_VPN_IPv6:
350 			DUMP_BYTE(nhbuf, sizeof(uint64_t) +
351 			    sizeof(struct in6_addr));
352 			DUMP_NLONG(nhbuf, 0);	/* set RD to 0 */
353 			DUMP_NLONG(nhbuf, 0);
354 			if (ibuf_add(nhbuf, &nexthop->v6,
355 			    sizeof(struct in6_addr)) == -1)
356 				goto fail;
357 			break;
358 		}
359 		if (!v2)
360 			DUMP_BYTE(nhbuf, 0);
361 		if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI,
362 		    nhbuf->buf, ibuf_size(nhbuf)) == -1) {
363 fail:
364 			ibuf_free(nhbuf);
365 			return (-1);
366 		}
367 		ibuf_free(nhbuf);
368 	}
369 
370 	if (neednewpath) {
371 		pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen);
372 		if (plen != 0)
373 			if (attr_writebuf(buf, ATTR_OPTIONAL|ATTR_TRANSITIVE,
374 			    ATTR_AS4_PATH, pdata, plen) == -1) {
375 				free(pdata);
376 				return (-1);
377 			}
378 		free(pdata);
379 	}
380 
381 	return (0);
382 }
383 
384 int
385 mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, uint16_t snum,
386     struct rde_peer *peer)
387 {
388 	struct ibuf	*buf, *hbuf = NULL, *h2buf = NULL;
389 	struct nexthop	*n;
390 	struct bgpd_addr nexthop, *nh;
391 	uint16_t	 len;
392 	uint8_t		 aid;
393 
394 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
395 		log_warn("mrt_dump_entry_mp: ibuf_dynamic");
396 		return (-1);
397 	}
398 
399 	if (mrt_attr_dump(buf, prefix_aspath(p), prefix_communities(p),
400 	    NULL, 0) == -1) {
401 		log_warnx("mrt_dump_entry_mp: mrt_attr_dump error");
402 		goto fail;
403 	}
404 	len = ibuf_size(buf);
405 
406 	if ((h2buf = ibuf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE +
407 	    MRT_BGP4MP_IPv4_ENTRY_SIZE, MRT_BGP4MP_IPv6_HEADER_SIZE +
408 	    MRT_BGP4MP_IPv6_ENTRY_SIZE + MRT_BGP4MP_MAX_PREFIXLEN)) == NULL) {
409 		log_warn("mrt_dump_entry_mp: ibuf_dynamic");
410 		goto fail;
411 	}
412 
413 	DUMP_SHORT(h2buf, peer->conf.local_short_as);
414 	DUMP_SHORT(h2buf, peer->short_as);
415 	DUMP_SHORT(h2buf, /* ifindex */ 0);
416 
417 	/* XXX is this for peer self? */
418 	aid = peer->remote_addr.aid == AID_UNSPEC ? p->pt->aid :
419 	    peer->remote_addr.aid;
420 	switch (aid) {
421 	case AID_INET:
422 	case AID_VPN_IPv4:
423 		DUMP_SHORT(h2buf, AFI_IPv4);
424 		DUMP_NLONG(h2buf, peer->local_v4_addr.v4.s_addr);
425 		DUMP_NLONG(h2buf, peer->remote_addr.v4.s_addr);
426 		break;
427 	case AID_INET6:
428 	case AID_VPN_IPv6:
429 		DUMP_SHORT(h2buf, AFI_IPv6);
430 		if (ibuf_add(h2buf, &peer->local_v6_addr.v6,
431 		    sizeof(struct in6_addr)) == -1 ||
432 		    ibuf_add(h2buf, &peer->remote_addr.v6,
433 		    sizeof(struct in6_addr)) == -1) {
434 			log_warn("mrt_dump_entry_mp: ibuf_add error");
435 			goto fail;
436 		}
437 		break;
438 	default:
439 		log_warnx("king bula found new AF %d in %s", aid, __func__);
440 		goto fail;
441 	}
442 
443 	DUMP_SHORT(h2buf, 0);		/* view */
444 	DUMP_SHORT(h2buf, 1);		/* status */
445 	/* originated timestamp */
446 	DUMP_LONG(h2buf, time(NULL) - (getmonotime() - p->lastchange));
447 
448 	n = prefix_nexthop(p);
449 	if (n == NULL) {
450 		memset(&nexthop, 0, sizeof(struct bgpd_addr));
451 		nexthop.aid = p->pt->aid;
452 		nh = &nexthop;
453 	} else
454 		nh = &n->exit_nexthop;
455 
456 	switch (p->pt->aid) {
457 	case AID_INET:
458 		DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
459 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
460 		DUMP_BYTE(h2buf, 4);		/* nhlen */
461 		DUMP_NLONG(h2buf, nh->v4.s_addr);	/* nexthop */
462 		break;
463 	case AID_INET6:
464 		DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
465 		DUMP_BYTE(h2buf, SAFI_UNICAST);	/* safi */
466 		DUMP_BYTE(h2buf, 16);		/* nhlen */
467 		if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
468 			log_warn("mrt_dump_entry_mp: ibuf_add error");
469 			goto fail;
470 		}
471 		break;
472 	case AID_VPN_IPv4:
473 		DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
474 		DUMP_BYTE(h2buf, SAFI_MPLSVPN);	/* safi */
475 		DUMP_BYTE(h2buf, sizeof(uint64_t) + sizeof(struct in_addr));
476 		DUMP_NLONG(h2buf, 0);	/* set RD to 0 */
477 		DUMP_NLONG(h2buf, 0);
478 		DUMP_NLONG(h2buf, nh->v4.s_addr);	/* nexthop */
479 		break;
480 	case AID_VPN_IPv6:
481 		DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
482 		DUMP_BYTE(h2buf, SAFI_MPLSVPN);	/* safi */
483 		DUMP_BYTE(h2buf, sizeof(uint64_t) + sizeof(struct in6_addr));
484 		DUMP_NLONG(h2buf, 0);	/* set RD to 0 */
485 		DUMP_NLONG(h2buf, 0);
486 		if (ibuf_add(h2buf, &nh->v6, sizeof(struct in6_addr)) == -1) {
487 			log_warn("mrt_dump_entry_mp: ibuf_add error");
488 			goto fail;
489 		}
490 		break;
491 	case AID_FLOWSPECv4:
492 	case AID_FLOWSPECv6:
493 		if (p->pt->aid == AID_FLOWSPECv4)
494 			DUMP_SHORT(h2buf, AFI_IPv4);	/* afi */
495 		else
496 			DUMP_SHORT(h2buf, AFI_IPv6);	/* afi */
497 		DUMP_BYTE(h2buf, SAFI_FLOWSPEC);	/* safi */
498 		DUMP_BYTE(h2buf, 0);			/* nhlen */
499 		break;
500 	default:
501 		log_warnx("king bula found new AF in %s", __func__);
502 		goto fail;
503 	}
504 
505 	if (pt_writebuf(h2buf, p->pt) == -1) {
506 		log_warnx("%s: pt_writebuf error", __func__);
507 		goto fail;
508 	}
509 
510 	DUMP_SHORT(h2buf, len);
511 	len += ibuf_size(h2buf);
512 
513 	if (mrt_dump_hdr_rde(&hbuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY,
514 	    len) == -1)
515 		goto fail;
516 
517 	ibuf_close(&mrt->wbuf, hbuf);
518 	ibuf_close(&mrt->wbuf, h2buf);
519 	ibuf_close(&mrt->wbuf, buf);
520 
521 	return (len + MRT_HEADER_SIZE);
522 
523 fail:
524 	ibuf_free(hbuf);
525 	ibuf_free(h2buf);
526 	ibuf_free(buf);
527 	return (-1);
528 }
529 
530 int
531 mrt_dump_entry(struct mrt *mrt, struct prefix *p, uint16_t snum,
532     struct rde_peer *peer)
533 {
534 	struct ibuf	*buf, *hbuf;
535 	struct nexthop	*nexthop;
536 	struct bgpd_addr addr, *nh;
537 	size_t		 len;
538 	uint16_t	 subtype;
539 	uint8_t		 dummy;
540 
541 	if (p->pt->aid != peer->remote_addr.aid &&
542 	    p->pt->aid != AID_INET && p->pt->aid != AID_INET6)
543 		/* only able to dump pure IPv4/IPv6 */
544 		return (0);
545 
546 	if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
547 		log_warn("mrt_dump_entry: ibuf_dynamic");
548 		return (-1);
549 	}
550 
551 	nexthop = prefix_nexthop(p);
552 	if (nexthop == NULL) {
553 		memset(&addr, 0, sizeof(struct bgpd_addr));
554 		addr.aid = p->pt->aid;
555 		nh = &addr;
556 	} else
557 		nh = &nexthop->exit_nexthop;
558 	if (mrt_attr_dump(buf, prefix_aspath(p), prefix_communities(p),
559 	    nh, 0) == -1) {
560 		log_warnx("mrt_dump_entry: mrt_attr_dump error");
561 		ibuf_free(buf);
562 		return (-1);
563 	}
564 	len = ibuf_size(buf);
565 	aid2afi(p->pt->aid, &subtype, &dummy);
566 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, subtype, len) == -1) {
567 		ibuf_free(buf);
568 		return (-1);
569 	}
570 
571 	DUMP_SHORT(hbuf, 0);
572 	DUMP_SHORT(hbuf, snum);
573 
574 	pt_getaddr(p->pt, &addr);
575 	switch (p->pt->aid) {
576 	case AID_INET:
577 		DUMP_NLONG(hbuf, addr.v4.s_addr);
578 		break;
579 	case AID_INET6:
580 		if (ibuf_add(hbuf, &addr.v6, sizeof(struct in6_addr)) == -1) {
581 			log_warn("mrt_dump_entry: ibuf_add error");
582 			goto fail;
583 		}
584 		break;
585 	}
586 	DUMP_BYTE(hbuf, p->pt->prefixlen);
587 
588 	DUMP_BYTE(hbuf, 1);		/* state */
589 	/* originated timestamp */
590 	DUMP_LONG(hbuf, time(NULL) - (getmonotime() - p->lastchange));
591 	switch (p->pt->aid) {
592 	case AID_INET:
593 		DUMP_NLONG(hbuf, peer->remote_addr.v4.s_addr);
594 		break;
595 	case AID_INET6:
596 		if (ibuf_add(hbuf, &peer->remote_addr.v6,
597 		    sizeof(struct in6_addr)) == -1) {
598 			log_warn("mrt_dump_entry: ibuf_add error");
599 			goto fail;
600 		}
601 		break;
602 	}
603 	DUMP_SHORT(hbuf, peer->short_as);
604 	DUMP_SHORT(hbuf, len);
605 
606 	ibuf_close(&mrt->wbuf, hbuf);
607 	ibuf_close(&mrt->wbuf, buf);
608 
609 	return (len + MRT_HEADER_SIZE);
610 
611 fail:
612 	ibuf_free(hbuf);
613 	ibuf_free(buf);
614 	return (-1);
615 }
616 
617 static int
618 mrt_dump_entry_v2_rib(struct rib_entry *re, struct ibuf **nb, struct ibuf **apb,
619     uint16_t *np, uint16_t *app)
620 {
621 	struct bgpd_addr addr;
622 	struct ibuf *buf, **bp;
623 	struct prefix *p;
624 	size_t len;
625 	int addpath;
626 
627 	*np = 0;
628 	*app = 0;
629 
630 	TAILQ_FOREACH(p, &re->prefix_h, entry.list.rib) {
631 		struct nexthop		*nexthop;
632 		struct bgpd_addr	*nh;
633 		struct ibuf		*tbuf;
634 
635 		addpath = peer_has_add_path(prefix_peer(p), re->prefix->aid,
636 		    CAPA_AP_RECV);
637 
638 		if (addpath) {
639 			bp = apb;
640 			*app += 1;
641 		} else {
642 			bp = nb;
643 			*np += 1;
644 		}
645 		if ((buf = *bp) == NULL) {
646 			if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
647 				log_warn("%s: ibuf_dynamic", __func__);
648 				goto fail;
649 			}
650 			*bp = buf;
651 		}
652 
653 		nexthop = prefix_nexthop(p);
654 		if (nexthop == NULL) {
655 			memset(&addr, 0, sizeof(struct bgpd_addr));
656 			addr.aid = re->prefix->aid;
657 			nh = &addr;
658 		} else
659 			nh = &nexthop->exit_nexthop;
660 
661 		DUMP_SHORT(buf, prefix_peer(p)->mrt_idx);
662 		/* originated timestamp */
663 		DUMP_LONG(buf, time(NULL) - (getmonotime() - p->lastchange));
664 
665 		/* RFC8050: path-id if add-path is used */
666 		if (addpath)
667 			DUMP_LONG(buf, p->path_id);
668 
669 		if ((tbuf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) {
670 			log_warn("%s: ibuf_dynamic", __func__);
671 			goto fail;
672 		}
673 		if (mrt_attr_dump(tbuf, prefix_aspath(p), prefix_communities(p),
674 		    nh, 1) == -1) {
675 			log_warnx("%s: mrt_attr_dump error", __func__);
676 			ibuf_free(tbuf);
677 			goto fail;
678 		}
679 		len = ibuf_size(tbuf);
680 		DUMP_SHORT(buf, (uint16_t)len);
681 		if (ibuf_add(buf, tbuf->buf, len) == -1) {
682 			log_warn("%s: ibuf_add error", __func__);
683 			ibuf_free(tbuf);
684 			goto fail;
685 		}
686 		ibuf_free(tbuf);
687 	}
688 
689 	return 0;
690 
691 fail:
692 	return -1;
693 }
694 
695 int
696 mrt_dump_entry_v2(struct mrt *mrt, struct rib_entry *re, uint32_t snum)
697 {
698 	struct ibuf	*hbuf = NULL, *nbuf = NULL, *apbuf = NULL, *pbuf;
699 	size_t		 hlen, len;
700 	uint16_t	 subtype, apsubtype, nump, apnump, afi;
701 	uint8_t		 safi;
702 
703 	if ((pbuf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
704 		log_warn("%s: ibuf_dynamic", __func__);
705 		return -1;
706 	}
707 
708 	switch (re->prefix->aid) {
709 	case AID_INET:
710 		subtype = MRT_DUMP_V2_RIB_IPV4_UNICAST;
711 		apsubtype = MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH;
712 		break;
713 	case AID_INET6:
714 		subtype = MRT_DUMP_V2_RIB_IPV6_UNICAST;
715 		apsubtype = MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH;
716 		break;
717 	default:
718 		/*
719 		 * XXX The RFC defined the format for this type differently
720 		 * and it is prohibitly expensive to implement that format.
721 		 * Instead do what gobgp does and encode it like the other
722 		 * types.
723 		 */
724 		subtype = MRT_DUMP_V2_RIB_GENERIC;
725 		apsubtype = MRT_DUMP_V2_RIB_GENERIC_ADDPATH;
726 		aid2afi(re->prefix->aid, &afi, &safi);
727 
728 		/* first add 3-bytes AFI/SAFI */
729 		DUMP_SHORT(pbuf, afi);
730 		DUMP_BYTE(pbuf, safi);
731 		break;
732 	}
733 
734 	if (pt_writebuf(pbuf, re->prefix) == -1) {
735 		log_warnx("%s: pt_writebuf error", __func__);
736 		goto fail;
737 	}
738 
739 	hlen = sizeof(snum) + sizeof(nump) + ibuf_size(pbuf);
740 
741 	if (mrt_dump_entry_v2_rib(re, &nbuf, &apbuf, &nump, &apnump))
742 		goto fail;
743 
744 	if (nump > 0) {
745 		len = ibuf_size(nbuf) + hlen;
746 		if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, subtype,
747 		    len) == -1)
748 			goto fail;
749 
750 		DUMP_LONG(hbuf, snum);
751 		if (ibuf_add(hbuf, pbuf->buf, ibuf_size(pbuf)) == -1) {
752 			log_warn("%s: ibuf_add error", __func__);
753 			goto fail;
754 		}
755 		DUMP_SHORT(hbuf, nump);
756 
757 		ibuf_close(&mrt->wbuf, hbuf);
758 		ibuf_close(&mrt->wbuf, nbuf);
759 		hbuf = NULL;
760 		nbuf = NULL;
761 	}
762 
763 	if (apnump > 0) {
764 		len = ibuf_size(apbuf) + hlen;
765 		if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, apsubtype,
766 		    len) == -1)
767 			goto fail;
768 
769 		DUMP_LONG(hbuf, snum);
770 		if (ibuf_add(hbuf, pbuf->buf, ibuf_size(pbuf)) == -1) {
771 			log_warn("%s: ibuf_add error", __func__);
772 			goto fail;
773 		}
774 		DUMP_SHORT(hbuf, apnump);
775 
776 		ibuf_close(&mrt->wbuf, hbuf);
777 		ibuf_close(&mrt->wbuf, apbuf);
778 		hbuf = NULL;
779 		apbuf = NULL;
780 	}
781 
782 	ibuf_free(pbuf);
783 	return (0);
784 fail:
785 	ibuf_free(apbuf);
786 	ibuf_free(nbuf);
787 	ibuf_free(hbuf);
788 	ibuf_free(pbuf);
789 	return (-1);
790 }
791 
792 struct cb_arg {
793 	struct ibuf	*buf;
794 	int		 nump;
795 };
796 
797 static void
798 mrt_dump_v2_hdr_peer(struct rde_peer *peer, void *arg)
799 {
800 	struct cb_arg *a = arg;
801 
802 	if (a->nump == -1)
803 		return;
804 	peer->mrt_idx = a->nump;
805 	if (mrt_dump_peer(a->buf, peer) == -1) {
806 		a->nump = -1;
807 		return;
808 	}
809 	a->nump++;
810 }
811 
812 int
813 mrt_dump_v2_hdr(struct mrt *mrt, struct bgpd_config *conf)
814 {
815 	struct ibuf	*buf, *hbuf = NULL;
816 	size_t		 len, off;
817 	uint16_t	 nlen, nump;
818 	struct cb_arg	 arg;
819 
820 	if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) {
821 		log_warn("%s: ibuf_dynamic", __func__);
822 		return (-1);
823 	}
824 
825 	DUMP_NLONG(buf, conf->bgpid);
826 	nlen = strlen(mrt->rib);
827 	if (nlen > 0)
828 		nlen += 1;
829 	DUMP_SHORT(buf, nlen);
830 	if (ibuf_add(buf, mrt->rib, nlen) == -1) {
831 		log_warn("%s: ibuf_add error", __func__);
832 		goto fail;
833 	}
834 
835 	off = ibuf_size(buf);
836 	if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
837 		log_warn("%s: ibuf_reserve error", __func__);
838 		goto fail;
839 	}
840 	arg.nump = 0;
841 	arg.buf = buf;
842 	peer_foreach(mrt_dump_v2_hdr_peer, &arg);
843 	if (arg.nump == -1)
844 		goto fail;
845 
846 	nump = htons(arg.nump);
847 	memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
848 
849 	len = ibuf_size(buf);
850 	if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2,
851 	    MRT_DUMP_V2_PEER_INDEX_TABLE, len) == -1)
852 		goto fail;
853 
854 	ibuf_close(&mrt->wbuf, hbuf);
855 	ibuf_close(&mrt->wbuf, buf);
856 
857 	return (0);
858 fail:
859 	ibuf_free(hbuf);
860 	ibuf_free(buf);
861 	return (-1);
862 }
863 
864 int
865 mrt_dump_peer(struct ibuf *buf, struct rde_peer *peer)
866 {
867 	uint8_t	type = 0;
868 
869 	if (peer->capa.as4byte)
870 		type |= MRT_DUMP_V2_PEER_BIT_A;
871 	if (peer->remote_addr.aid == AID_INET6)
872 		type |= MRT_DUMP_V2_PEER_BIT_I;
873 
874 	DUMP_BYTE(buf, type);
875 	DUMP_LONG(buf, peer->remote_bgpid);
876 
877 	switch (peer->remote_addr.aid) {
878 	case AID_INET:
879 		DUMP_NLONG(buf, peer->remote_addr.v4.s_addr);
880 		break;
881 	case AID_INET6:
882 		if (ibuf_add(buf, &peer->remote_addr.v6,
883 		    sizeof(struct in6_addr)) == -1) {
884 			log_warn("mrt_dump_peer: ibuf_add error");
885 			goto fail;
886 		}
887 		break;
888 	case AID_UNSPEC: /* XXX special handling for peerself? */
889 		DUMP_NLONG(buf, 0);
890 		break;
891 	default:
892 		log_warnx("king bula found new AF in %s", __func__);
893 		goto fail;
894 	}
895 
896 	if (peer->capa.as4byte)
897 		DUMP_LONG(buf, peer->conf.remote_as);
898 	else
899 		DUMP_SHORT(buf, peer->short_as);
900 
901 	return (0);
902 fail:
903 	return (-1);
904 }
905 
906 void
907 mrt_dump_upcall(struct rib_entry *re, void *ptr)
908 {
909 	struct mrt		*mrtbuf = ptr;
910 	struct prefix		*p;
911 
912 	if (mrtbuf->type == MRT_TABLE_DUMP_V2) {
913 		mrt_dump_entry_v2(mrtbuf, re, mrtbuf->seqnum++);
914 		return;
915 	}
916 
917 	/*
918 	 * dump all prefixes even the inactive ones. That is the way zebra
919 	 * dumps the table so we do the same. If only the active route should
920 	 * be dumped p should be set to p = pt->active.
921 	 */
922 	TAILQ_FOREACH(p, &re->prefix_h, entry.list.rib) {
923 		if (mrtbuf->type == MRT_TABLE_DUMP)
924 			mrt_dump_entry(mrtbuf, p, mrtbuf->seqnum++,
925 			    prefix_peer(p));
926 		else
927 			mrt_dump_entry_mp(mrtbuf, p, mrtbuf->seqnum++,
928 			    prefix_peer(p));
929 	}
930 }
931 
932 void
933 mrt_done(struct mrt *mrtbuf)
934 {
935 	mrtbuf->state = MRT_STATE_REMOVE;
936 }
937 
938 int
939 mrt_dump_hdr_se(struct ibuf ** bp, struct peer *peer, uint16_t type,
940     uint16_t subtype, uint32_t len, int swap)
941 {
942 	struct timespec	time;
943 
944 	if ((*bp = ibuf_dynamic(MRT_ET_HEADER_SIZE, MRT_ET_HEADER_SIZE +
945 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + len)) == NULL) {
946 		log_warn("mrt_dump_hdr_se: ibuf_dynamic error");
947 		return (-1);
948 	}
949 
950 	clock_gettime(CLOCK_REALTIME, &time);
951 
952 	DUMP_LONG(*bp, time.tv_sec);
953 	DUMP_SHORT(*bp, type);
954 	DUMP_SHORT(*bp, subtype);
955 
956 	switch (peer->local.aid) {
957 	case AID_INET:
958 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
959 		    subtype == BGP4MP_MESSAGE_AS4 ||
960 		    subtype == BGP4MP_MESSAGE_AS4_ADDPATH)
961 			len += MRT_BGP4MP_ET_AS4_IPv4_HEADER_SIZE;
962 		else
963 			len += MRT_BGP4MP_ET_IPv4_HEADER_SIZE;
964 		break;
965 	case AID_INET6:
966 		if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
967 		    subtype == BGP4MP_MESSAGE_AS4 ||
968 		    subtype == BGP4MP_MESSAGE_AS4_ADDPATH)
969 			len += MRT_BGP4MP_ET_AS4_IPv6_HEADER_SIZE;
970 		else
971 			len += MRT_BGP4MP_ET_IPv6_HEADER_SIZE;
972 		break;
973 	case 0:
974 		goto fail;
975 	default:
976 		log_warnx("king bula found new AF in %s", __func__);
977 		goto fail;
978 	}
979 
980 	DUMP_LONG(*bp, len);
981 	/* millisecond field use by the _ET format */
982 	DUMP_LONG(*bp, time.tv_nsec / 1000);
983 
984 	if (subtype == BGP4MP_STATE_CHANGE_AS4 ||
985 	    subtype == BGP4MP_MESSAGE_AS4 ||
986 	    subtype == BGP4MP_MESSAGE_AS4_ADDPATH) {
987 		if (!swap)
988 			DUMP_LONG(*bp, peer->conf.local_as);
989 		DUMP_LONG(*bp, peer->conf.remote_as);
990 		if (swap)
991 			DUMP_LONG(*bp, peer->conf.local_as);
992 	} else {
993 		if (!swap)
994 			DUMP_SHORT(*bp, peer->conf.local_short_as);
995 		DUMP_SHORT(*bp, peer->short_as);
996 		if (swap)
997 			DUMP_SHORT(*bp, peer->conf.local_short_as);
998 	}
999 
1000 	DUMP_SHORT(*bp, /* ifindex */ 0);
1001 
1002 	switch (peer->local.aid) {
1003 	case AID_INET:
1004 		DUMP_SHORT(*bp, AFI_IPv4);
1005 		if (!swap)
1006 			DUMP_NLONG(*bp, peer->local.v4.s_addr);
1007 		DUMP_NLONG(*bp, peer->remote.v4.s_addr);
1008 		if (swap)
1009 			DUMP_NLONG(*bp, peer->local.v4.s_addr);
1010 		break;
1011 	case AID_INET6:
1012 		DUMP_SHORT(*bp, AFI_IPv6);
1013 		if (!swap)
1014 			if (ibuf_add(*bp, &peer->local.v6,
1015 			    sizeof(struct in6_addr)) == -1) {
1016 				log_warn("mrt_dump_hdr_se: ibuf_add error");
1017 				goto fail;
1018 			}
1019 		if (ibuf_add(*bp, &peer->remote.v6,
1020 		    sizeof(struct in6_addr)) == -1) {
1021 			log_warn("mrt_dump_hdr_se: ibuf_add error");
1022 			goto fail;
1023 		}
1024 		if (swap)
1025 			if (ibuf_add(*bp, &peer->local.v6,
1026 			    sizeof(struct in6_addr)) == -1) {
1027 				log_warn("mrt_dump_hdr_se: ibuf_add error");
1028 				goto fail;
1029 			}
1030 		break;
1031 	}
1032 
1033 	return (0);
1034 
1035 fail:
1036 	ibuf_free(*bp);
1037 	return (-1);
1038 }
1039 
1040 int
1041 mrt_dump_hdr_rde(struct ibuf **bp, uint16_t type, uint16_t subtype,
1042     uint32_t len)
1043 {
1044 	struct timespec	time;
1045 
1046 	if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE +
1047 	    MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + MRT_BGP4MP_IPv6_ENTRY_SIZE)) ==
1048 	    NULL) {
1049 		log_warn("mrt_dump_hdr_rde: ibuf_dynamic error");
1050 		return (-1);
1051 	}
1052 
1053 	clock_gettime(CLOCK_REALTIME, &time);
1054 
1055 	DUMP_LONG(*bp, time.tv_sec);
1056 	DUMP_SHORT(*bp, type);
1057 	DUMP_SHORT(*bp, subtype);
1058 
1059 	switch (type) {
1060 	case MSG_TABLE_DUMP:
1061 		switch (subtype) {
1062 		case AFI_IPv4:
1063 			len += MRT_DUMP_HEADER_SIZE;
1064 			break;
1065 		case AFI_IPv6:
1066 			len += MRT_DUMP_HEADER_SIZE_V6;
1067 			break;
1068 		}
1069 		DUMP_LONG(*bp, len);
1070 		break;
1071 	case MSG_PROTOCOL_BGP4MP:
1072 	case MSG_TABLE_DUMP_V2:
1073 		DUMP_LONG(*bp, len);
1074 		break;
1075 	default:
1076 		log_warnx("mrt_dump_hdr_rde: unsupported type");
1077 		goto fail;
1078 	}
1079 	return (0);
1080 
1081 fail:
1082 	ibuf_free(*bp);
1083 	*bp = NULL;
1084 	return (-1);
1085 }
1086 
1087 void
1088 mrt_write(struct mrt *mrt)
1089 {
1090 	int	r;
1091 
1092 	if ((r = ibuf_write(&mrt->wbuf)) == -1 && errno != EAGAIN) {
1093 		log_warn("mrt dump aborted, mrt_write");
1094 		mrt_clean(mrt);
1095 		mrt_done(mrt);
1096 	}
1097 }
1098 
1099 void
1100 mrt_clean(struct mrt *mrt)
1101 {
1102 	struct ibuf	*b;
1103 
1104 	close(mrt->wbuf.fd);
1105 	while ((b = TAILQ_FIRST(&mrt->wbuf.bufs))) {
1106 		TAILQ_REMOVE(&mrt->wbuf.bufs, b, entry);
1107 		ibuf_free(b);
1108 	}
1109 	mrt->wbuf.queued = 0;
1110 }
1111 
1112 static struct imsgbuf	*mrt_imsgbuf[2];
1113 
1114 void
1115 mrt_init(struct imsgbuf *rde, struct imsgbuf *se)
1116 {
1117 	mrt_imsgbuf[RDEIDX] = rde;
1118 	mrt_imsgbuf[SEIDX] = se;
1119 }
1120 
1121 int
1122 mrt_open(struct mrt *mrt, time_t now)
1123 {
1124 	enum imsg_type	type;
1125 	int		fd;
1126 
1127 	if (strftime(MRT2MC(mrt)->file, sizeof(MRT2MC(mrt)->file),
1128 	    MRT2MC(mrt)->name, localtime(&now)) == 0) {
1129 		log_warnx("mrt_open: strftime conversion failed");
1130 		return (-1);
1131 	}
1132 
1133 	fd = open(MRT2MC(mrt)->file,
1134 	    O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC|O_CLOEXEC, 0644);
1135 	if (fd == -1) {
1136 		log_warn("mrt_open %s", MRT2MC(mrt)->file);
1137 		return (1);
1138 	}
1139 
1140 	if (mrt->state == MRT_STATE_OPEN)
1141 		type = IMSG_MRT_OPEN;
1142 	else
1143 		type = IMSG_MRT_REOPEN;
1144 
1145 	if (imsg_compose(mrt_imsgbuf[TYPE2IDX(mrt->type)], type, 0, 0, fd,
1146 	    mrt, sizeof(struct mrt)) == -1)
1147 		log_warn("mrt_open");
1148 
1149 	return (1);
1150 }
1151 
1152 time_t
1153 mrt_timeout(struct mrt_head *mrt)
1154 {
1155 	struct mrt	*m;
1156 	time_t		 now;
1157 	time_t		 timeout = -1;
1158 
1159 	now = time(NULL);
1160 	LIST_FOREACH(m, mrt, entry) {
1161 		if (m->state == MRT_STATE_RUNNING &&
1162 		    MRT2MC(m)->ReopenTimerInterval != 0) {
1163 			if (MRT2MC(m)->ReopenTimer <= now) {
1164 				mrt_open(m, now);
1165 				MRT2MC(m)->ReopenTimer =
1166 				    now + MRT2MC(m)->ReopenTimerInterval;
1167 			}
1168 			if (timeout == -1 ||
1169 			    MRT2MC(m)->ReopenTimer - now < timeout)
1170 				timeout = MRT2MC(m)->ReopenTimer - now;
1171 		}
1172 	}
1173 	return (timeout);
1174 }
1175 
1176 void
1177 mrt_reconfigure(struct mrt_head *mrt)
1178 {
1179 	struct mrt	*m, *xm;
1180 	time_t		 now;
1181 
1182 	now = time(NULL);
1183 	for (m = LIST_FIRST(mrt); m != NULL; m = xm) {
1184 		xm = LIST_NEXT(m, entry);
1185 		if (m->state == MRT_STATE_OPEN ||
1186 		    m->state == MRT_STATE_REOPEN) {
1187 			if (mrt_open(m, now) == -1)
1188 				continue;
1189 			if (MRT2MC(m)->ReopenTimerInterval != 0)
1190 				MRT2MC(m)->ReopenTimer =
1191 				    now + MRT2MC(m)->ReopenTimerInterval;
1192 			m->state = MRT_STATE_RUNNING;
1193 		}
1194 		if (m->state == MRT_STATE_REMOVE) {
1195 			if (imsg_compose(mrt_imsgbuf[TYPE2IDX(m->type)],
1196 			    IMSG_MRT_CLOSE, 0, 0, -1, m, sizeof(struct mrt)) ==
1197 			    -1)
1198 				log_warn("mrt_reconfigure");
1199 			LIST_REMOVE(m, entry);
1200 			free(m);
1201 			continue;
1202 		}
1203 	}
1204 }
1205 
1206 void
1207 mrt_handler(struct mrt_head *mrt)
1208 {
1209 	struct mrt	*m;
1210 	time_t		 now;
1211 
1212 	now = time(NULL);
1213 	LIST_FOREACH(m, mrt, entry) {
1214 		if (m->state == MRT_STATE_RUNNING &&
1215 		    (MRT2MC(m)->ReopenTimerInterval != 0 ||
1216 		     m->type == MRT_TABLE_DUMP ||
1217 		     m->type == MRT_TABLE_DUMP_MP ||
1218 		     m->type == MRT_TABLE_DUMP_V2)) {
1219 			if (mrt_open(m, now) == -1)
1220 				continue;
1221 			MRT2MC(m)->ReopenTimer =
1222 			    now + MRT2MC(m)->ReopenTimerInterval;
1223 		}
1224 	}
1225 }
1226 
1227 struct mrt *
1228 mrt_get(struct mrt_head *c, struct mrt *m)
1229 {
1230 	struct mrt	*t;
1231 
1232 	LIST_FOREACH(t, c, entry) {
1233 		if (t->type != m->type)
1234 			continue;
1235 		if (strcmp(t->rib, m->rib))
1236 			continue;
1237 		if (t->peer_id == m->peer_id &&
1238 		    t->group_id == m->group_id)
1239 			return (t);
1240 	}
1241 	return (NULL);
1242 }
1243 
1244 void
1245 mrt_mergeconfig(struct mrt_head *xconf, struct mrt_head *nconf)
1246 {
1247 	struct mrt	*m, *xm;
1248 
1249 	/* both lists here are actually struct mrt_conifg nodes */
1250 	LIST_FOREACH(m, nconf, entry) {
1251 		if ((xm = mrt_get(xconf, m)) == NULL) {
1252 			/* NEW */
1253 			if ((xm = malloc(sizeof(struct mrt_config))) == NULL)
1254 				fatal("mrt_mergeconfig");
1255 			memcpy(xm, m, sizeof(struct mrt_config));
1256 			xm->state = MRT_STATE_OPEN;
1257 			LIST_INSERT_HEAD(xconf, xm, entry);
1258 		} else {
1259 			/* MERGE */
1260 			if (strlcpy(MRT2MC(xm)->name, MRT2MC(m)->name,
1261 			    sizeof(MRT2MC(xm)->name)) >=
1262 			    sizeof(MRT2MC(xm)->name))
1263 				fatalx("mrt_mergeconfig: strlcpy");
1264 			MRT2MC(xm)->ReopenTimerInterval =
1265 			    MRT2MC(m)->ReopenTimerInterval;
1266 			xm->state = MRT_STATE_REOPEN;
1267 		}
1268 	}
1269 
1270 	LIST_FOREACH(xm, xconf, entry)
1271 		if (mrt_get(nconf, xm) == NULL)
1272 			/* REMOVE */
1273 			xm->state = MRT_STATE_REMOVE;
1274 
1275 	/* free config */
1276 	while ((m = LIST_FIRST(nconf)) != NULL) {
1277 		LIST_REMOVE(m, entry);
1278 		free(m);
1279 	}
1280 }
1281