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