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