1/*	$OpenBSD: mrtparser.c,v 1.22 2024/02/01 11:37:10 claudio Exp $ */
2/*
3 * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include <sys/types.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <err.h>
21#include <errno.h>
22#include <limits.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <time.h>
27#include <unistd.h>
28
29#include "mrt.h"
30#include "mrtparser.h"
31
32struct mrt_peer	*mrt_parse_v2_peer(struct mrt_hdr *, struct ibuf *);
33struct mrt_rib	*mrt_parse_v2_rib(struct mrt_hdr *, struct ibuf *, int);
34int	mrt_parse_dump(struct mrt_hdr *, struct ibuf *, struct mrt_peer **,
35	    struct mrt_rib **);
36int	mrt_parse_dump_mp(struct mrt_hdr *, struct ibuf *, struct mrt_peer **,
37	    struct mrt_rib **, int);
38int	mrt_extract_attr(struct mrt_rib_entry *, struct ibuf *, uint8_t, int);
39
40void	mrt_free_peers(struct mrt_peer *);
41void	mrt_free_rib(struct mrt_rib *);
42
43u_char *mrt_aspath_inflate(struct ibuf *, uint16_t *);
44int	mrt_extract_addr(struct ibuf *, struct bgpd_addr *, uint8_t);
45int	mrt_extract_prefix(struct ibuf *, uint8_t, struct bgpd_addr *,
46	    uint8_t *, int);
47
48int	mrt_parse_state(struct mrt_bgp_state *, struct mrt_hdr *,
49	    struct ibuf *, int);
50int	mrt_parse_msg(struct mrt_bgp_msg *, struct mrt_hdr *,
51	    struct ibuf *, int);
52
53static size_t
54mrt_read_buf(int fd, void *buf, size_t len)
55{
56	char *b = buf;
57	ssize_t n;
58
59	while (len > 0) {
60		if ((n = read(fd, b, len)) == -1) {
61			if (errno == EINTR)
62				continue;
63			err(1, "read");
64		}
65		if (n == 0)
66			break;
67		b += n;
68		len -= n;
69	}
70
71	return (b - (char *)buf);
72}
73
74static struct ibuf *
75mrt_read_msg(int fd, struct mrt_hdr *hdr)
76{
77	struct ibuf *buf;
78	size_t len;
79
80	memset(hdr, 0, sizeof(*hdr));
81	if (mrt_read_buf(fd, hdr, sizeof(*hdr)) != sizeof(*hdr))
82		return (NULL);
83
84	len = ntohl(hdr->length);
85	if ((buf = ibuf_open(len)) == NULL)
86		err(1, "ibuf_open(%zu)", len);
87
88	if (mrt_read_buf(fd, ibuf_reserve(buf, len), len) != len) {
89		ibuf_free(buf);
90		return (NULL);
91	}
92	return (buf);
93}
94
95void
96mrt_parse(int fd, struct mrt_parser *p, int verbose)
97{
98	struct mrt_hdr		h;
99	struct mrt_bgp_state	s;
100	struct mrt_bgp_msg	m;
101	struct mrt_peer		*pctx = NULL;
102	struct mrt_rib		*r;
103	struct ibuf		*msg;
104
105	while ((msg = mrt_read_msg(fd, &h)) != NULL) {
106		if (ibuf_size(msg) != ntohl(h.length))
107			errx(1, "corrupt message, %zu vs %u", ibuf_size(msg),
108			    ntohl(h.length));
109		switch (ntohs(h.type)) {
110		case MSG_NULL:
111		case MSG_START:
112		case MSG_DIE:
113		case MSG_I_AM_DEAD:
114		case MSG_PEER_DOWN:
115		case MSG_PROTOCOL_BGP:
116		case MSG_PROTOCOL_IDRP:
117		case MSG_PROTOCOL_BGP4PLUS:
118		case MSG_PROTOCOL_BGP4PLUS1:
119			if (verbose)
120				printf("deprecated MRT type %d\n",
121				    ntohs(h.type));
122			break;
123		case MSG_PROTOCOL_RIP:
124		case MSG_PROTOCOL_RIPNG:
125		case MSG_PROTOCOL_OSPF:
126		case MSG_PROTOCOL_ISIS_ET:
127		case MSG_PROTOCOL_ISIS:
128		case MSG_PROTOCOL_OSPFV3_ET:
129		case MSG_PROTOCOL_OSPFV3:
130			if (verbose)
131				printf("unsupported MRT type %d\n",
132				    ntohs(h.type));
133			break;
134		case MSG_TABLE_DUMP:
135			switch (ntohs(h.subtype)) {
136			case MRT_DUMP_AFI_IP:
137			case MRT_DUMP_AFI_IPv6:
138				if (p->dump == NULL)
139					break;
140				if (mrt_parse_dump(&h, msg, &pctx, &r) == 0) {
141					if (p->dump)
142						p->dump(r, pctx, p->arg);
143					mrt_free_rib(r);
144				}
145				break;
146			default:
147				if (verbose)
148					printf("unknown AFI %d in table dump\n",
149					    ntohs(h.subtype));
150				break;
151			}
152			break;
153		case MSG_TABLE_DUMP_V2:
154			switch (ntohs(h.subtype)) {
155			case MRT_DUMP_V2_PEER_INDEX_TABLE:
156				if (p->dump == NULL)
157					break;
158				if (pctx)
159					mrt_free_peers(pctx);
160				pctx = mrt_parse_v2_peer(&h, msg);
161				break;
162			case MRT_DUMP_V2_RIB_IPV4_UNICAST:
163			case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
164			case MRT_DUMP_V2_RIB_IPV6_UNICAST:
165			case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
166			case MRT_DUMP_V2_RIB_GENERIC:
167			case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH:
168			case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH:
169			case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH:
170			case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH:
171			case MRT_DUMP_V2_RIB_GENERIC_ADDPATH:
172				if (p->dump == NULL)
173					break;
174				r = mrt_parse_v2_rib(&h, msg, verbose);
175				if (r) {
176					if (p->dump)
177						p->dump(r, pctx, p->arg);
178					mrt_free_rib(r);
179				}
180				break;
181			default:
182				if (verbose)
183					printf("unhandled DUMP_V2 subtype %d\n",
184					    ntohs(h.subtype));
185				break;
186			}
187			break;
188		case MSG_PROTOCOL_BGP4MP_ET:
189		case MSG_PROTOCOL_BGP4MP:
190			switch (ntohs(h.subtype)) {
191			case BGP4MP_STATE_CHANGE:
192			case BGP4MP_STATE_CHANGE_AS4:
193				if (mrt_parse_state(&s, &h, msg,
194				    verbose) != -1) {
195					if (p->state)
196						p->state(&s, p->arg);
197				}
198				break;
199			case BGP4MP_MESSAGE:
200			case BGP4MP_MESSAGE_AS4:
201			case BGP4MP_MESSAGE_LOCAL:
202			case BGP4MP_MESSAGE_AS4_LOCAL:
203			case BGP4MP_MESSAGE_ADDPATH:
204			case BGP4MP_MESSAGE_AS4_ADDPATH:
205			case BGP4MP_MESSAGE_LOCAL_ADDPATH:
206			case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH:
207				if (mrt_parse_msg(&m, &h, msg, verbose) != -1) {
208					if (p->message)
209						p->message(&m, p->arg);
210				}
211				break;
212			case BGP4MP_ENTRY:
213				if (p->dump == NULL)
214					break;
215				if (mrt_parse_dump_mp(&h, msg, &pctx, &r,
216				    verbose) == 0) {
217					if (p->dump)
218						p->dump(r, pctx, p->arg);
219					mrt_free_rib(r);
220				}
221				break;
222			default:
223				if (verbose)
224					printf("unhandled BGP4MP subtype %d\n",
225					    ntohs(h.subtype));
226				break;
227			}
228			break;
229		default:
230			if (verbose)
231				printf("unknown MRT type %d\n", ntohs(h.type));
232			break;
233		}
234		ibuf_free(msg);
235	}
236	if (pctx)
237		mrt_free_peers(pctx);
238}
239
240static int
241mrt_afi2aid(int afi, int safi, int verbose)
242{
243	switch (afi) {
244	case MRT_DUMP_AFI_IP:
245		if (safi == -1 || safi == 1 || safi == 2)
246			return AID_INET;
247		else if (safi == 128)
248			return AID_VPN_IPv4;
249		break;
250	case MRT_DUMP_AFI_IPv6:
251		if (safi == -1 || safi == 1 || safi == 2)
252			return AID_INET6;
253		else if (safi == 128)
254			return AID_VPN_IPv6;
255		break;
256	default:
257		break;
258	}
259	if (verbose)
260		printf("unhandled AFI/SAFI %d/%d\n", afi, safi);
261	return AID_UNSPEC;
262}
263
264struct mrt_peer *
265mrt_parse_v2_peer(struct mrt_hdr *hdr, struct ibuf *msg)
266{
267	struct mrt_peer_entry	*peers = NULL;
268	struct mrt_peer	*p;
269	uint32_t	bid;
270	uint16_t	cnt, i;
271
272	if (ibuf_size(msg) < 8)	/* min msg size */
273		return NULL;
274
275	p = calloc(1, sizeof(struct mrt_peer));
276	if (p == NULL)
277		err(1, "calloc");
278
279	/* collector bgp id */
280	if (ibuf_get_n32(msg, &bid) == -1 ||
281	    ibuf_get_n16(msg, &cnt) == -1)
282		goto fail;
283
284	/* view name */
285	if (cnt != 0) {
286		if ((p->view = malloc(cnt + 1)) == NULL)
287			err(1, "malloc");
288		if (ibuf_get(msg, p->view, cnt) == -1)
289			goto fail;
290		p->view[cnt] = 0;
291	} else
292		if ((p->view = strdup("")) == NULL)
293			err(1, "strdup");
294
295	/* peer_count */
296	if (ibuf_get_n16(msg, &cnt) == -1)
297		goto fail;
298
299	/* peer entries */
300	if ((peers = calloc(cnt, sizeof(struct mrt_peer_entry))) == NULL)
301		err(1, "calloc");
302	for (i = 0; i < cnt; i++) {
303		uint8_t type;
304
305		if (ibuf_get_n8(msg, &type) == -1 ||
306		    ibuf_get_n32(msg, &peers[i].bgp_id) == -1)
307			goto fail;
308
309		if (type & MRT_DUMP_V2_PEER_BIT_I) {
310			if (mrt_extract_addr(msg, &peers[i].addr,
311			    AID_INET6) == -1)
312				goto fail;
313		} else {
314			if (mrt_extract_addr(msg, &peers[i].addr,
315			    AID_INET) == -1)
316				goto fail;
317		}
318
319		if (type & MRT_DUMP_V2_PEER_BIT_A) {
320			if (ibuf_get_n32(msg, &peers[i].asnum) == -1)
321				goto fail;
322		} else {
323			uint16_t as2;
324
325			if (ibuf_get_n16(msg, &as2) == -1)
326				goto fail;
327			peers[i].asnum = as2;
328		}
329	}
330	p->peers = peers;
331	p->npeers = cnt;
332	return (p);
333fail:
334	mrt_free_peers(p);
335	free(peers);
336	return (NULL);
337}
338
339struct mrt_rib *
340mrt_parse_v2_rib(struct mrt_hdr *hdr, struct ibuf *msg, int verbose)
341{
342	struct mrt_rib_entry *entries = NULL;
343	struct mrt_rib	*r;
344	uint16_t	i, afi;
345	uint8_t		safi, aid;
346
347	r = calloc(1, sizeof(struct mrt_rib));
348	if (r == NULL)
349		err(1, "calloc");
350
351	/* seq_num */
352	if (ibuf_get_n32(msg, &r->seqnum) == -1)
353		goto fail;
354
355	switch (ntohs(hdr->subtype)) {
356	case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH:
357	case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH:
358		r->add_path = 1;
359		/* FALLTHROUGH */
360	case MRT_DUMP_V2_RIB_IPV4_UNICAST:
361	case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
362		/* prefix */
363		if (mrt_extract_prefix(msg, AID_INET, &r->prefix,
364		    &r->prefixlen, verbose) == -1)
365			goto fail;
366		break;
367	case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH:
368	case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH:
369		r->add_path = 1;
370		/* FALLTHROUGH */
371	case MRT_DUMP_V2_RIB_IPV6_UNICAST:
372	case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
373		/* prefix */
374		if (mrt_extract_prefix(msg, AID_INET6, &r->prefix,
375		    &r->prefixlen, verbose) == -1)
376			goto fail;
377		break;
378	case MRT_DUMP_V2_RIB_GENERIC_ADDPATH:
379		/*
380		 * RFC8050 handling for add-path has special handling for
381		 * RIB_GENERIC_ADDPATH but nobody implements it that way.
382		 * So just use the same way as for the other _ADDPATH types.
383		 */
384		r->add_path = 1;
385		/* FALLTHROUGH */
386	case MRT_DUMP_V2_RIB_GENERIC:
387		/* fetch AFI/SAFI pair */
388		if (ibuf_get_n16(msg, &afi) == -1 ||
389		    ibuf_get_n8(msg, &safi) == -1)
390			goto fail;
391
392		if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC)
393			goto fail;
394
395		/* prefix */
396		if (mrt_extract_prefix(msg, aid, &r->prefix,
397		    &r->prefixlen, verbose) == -1)
398			goto fail;
399		break;
400	default:
401		errx(1, "unknown subtype %hd", ntohs(hdr->subtype));
402	}
403
404	/* entries count */
405	if (ibuf_get_n16(msg, &r->nentries) == -1)
406		goto fail;
407
408	/* entries */
409	if ((entries = calloc(r->nentries, sizeof(struct mrt_rib_entry))) ==
410	    NULL)
411		err(1, "calloc");
412	for (i = 0; i < r->nentries; i++) {
413		struct ibuf	abuf;
414		uint32_t	otm;
415		uint16_t	alen;
416
417		/* peer index */
418		if (ibuf_get_n16(msg, &entries[i].peer_idx) == -1)
419			goto fail;
420
421		/* originated */
422		if (ibuf_get_n32(msg, &otm) == -1)
423			goto fail;
424		entries[i].originated = otm;
425
426		if (r->add_path) {
427			if (ibuf_get_n32(msg, &entries[i].path_id) == -1)
428				goto fail;
429		}
430
431		/* attr_len */
432		if (ibuf_get_n16(msg, &alen) == -1 ||
433		    ibuf_get_ibuf(msg, alen, &abuf) == -1)
434			goto fail;
435
436		/* attr */
437		if (mrt_extract_attr(&entries[i], &abuf, r->prefix.aid,
438		    1) == -1)
439			goto fail;
440	}
441	r->entries = entries;
442	return (r);
443fail:
444	mrt_free_rib(r);
445	free(entries);
446	return (NULL);
447}
448
449int
450mrt_parse_dump(struct mrt_hdr *hdr, struct ibuf *msg, struct mrt_peer **pp,
451    struct mrt_rib **rp)
452{
453	struct ibuf		 abuf;
454	struct mrt_peer		*p;
455	struct mrt_rib		*r;
456	struct mrt_rib_entry	*re;
457	uint32_t		 tmp32;
458	uint16_t		 tmp16, alen;
459
460	if (*pp == NULL) {
461		*pp = calloc(1, sizeof(struct mrt_peer));
462		if (*pp == NULL)
463			err(1, "calloc");
464		(*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry));
465		if ((*pp)->peers == NULL)
466			err(1, "calloc");
467		(*pp)->npeers = 1;
468	}
469	p = *pp;
470
471	*rp = r = calloc(1, sizeof(struct mrt_rib));
472	if (r == NULL)
473		err(1, "calloc");
474	re = calloc(1, sizeof(struct mrt_rib_entry));
475	if (re == NULL)
476		err(1, "calloc");
477	r->nentries = 1;
478	r->entries = re;
479
480	if (ibuf_skip(msg, sizeof(uint16_t)) == -1 ||	/* view */
481	    ibuf_get_n16(msg, &tmp16) == -1)		/* seqnum */
482		goto fail;
483	r->seqnum = tmp16;
484
485	switch (ntohs(hdr->subtype)) {
486	case MRT_DUMP_AFI_IP:
487		if (mrt_extract_addr(msg, &r->prefix, AID_INET) == -1)
488			goto fail;
489		break;
490	case MRT_DUMP_AFI_IPv6:
491		if (mrt_extract_addr(msg, &r->prefix, AID_INET6) == -1)
492			goto fail;
493		break;
494	}
495	if (ibuf_get_n8(msg, &r->prefixlen) == -1 ||	/* prefixlen */
496	    ibuf_skip(msg, 1) == -1 ||			/* status */
497	    ibuf_get_n32(msg, &tmp32) == -1)		/* originated */
498		goto fail;
499	re->originated = tmp32;
500	/* peer ip */
501	switch (ntohs(hdr->subtype)) {
502	case MRT_DUMP_AFI_IP:
503		if (mrt_extract_addr(msg, &p->peers->addr, AID_INET) == -1)
504			goto fail;
505		break;
506	case MRT_DUMP_AFI_IPv6:
507		if (mrt_extract_addr(msg, &p->peers->addr, AID_INET6) == -1)
508			goto fail;
509		break;
510	}
511	if (ibuf_get_n16(msg, &tmp16) == -1)
512		goto fail;
513	p->peers->asnum = tmp16;
514
515	if (ibuf_get_n16(msg, &alen) == -1 ||
516	    ibuf_get_ibuf(msg, alen, &abuf) == -1)
517		goto fail;
518
519	/* attr */
520	if (mrt_extract_attr(re, &abuf, r->prefix.aid, 0) == -1)
521		goto fail;
522	return (0);
523fail:
524	mrt_free_rib(r);
525	return (-1);
526}
527
528int
529mrt_parse_dump_mp(struct mrt_hdr *hdr, struct ibuf *msg, struct mrt_peer **pp,
530    struct mrt_rib **rp, int verbose)
531{
532	struct ibuf		 abuf;
533	struct mrt_peer		*p;
534	struct mrt_rib		*r;
535	struct mrt_rib_entry	*re;
536	uint32_t		 tmp32;
537	uint16_t		 asnum, alen, afi;
538	uint8_t			 safi, nhlen, aid;
539
540	if (*pp == NULL) {
541		*pp = calloc(1, sizeof(struct mrt_peer));
542		if (*pp == NULL)
543			err(1, "calloc");
544		(*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry));
545		if ((*pp)->peers == NULL)
546			err(1, "calloc");
547		(*pp)->npeers = 1;
548	}
549	p = *pp;
550
551	*rp = r = calloc(1, sizeof(struct mrt_rib));
552	if (r == NULL)
553		err(1, "calloc");
554	re = calloc(1, sizeof(struct mrt_rib_entry));
555	if (re == NULL)
556		err(1, "calloc");
557	r->nentries = 1;
558	r->entries = re;
559
560	/* just ignore the microsec field for _ET header for now */
561	if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
562		if (ibuf_skip(msg, sizeof(uint32_t)) == -1)
563			goto fail;
564	}
565
566	if (ibuf_skip(msg, sizeof(uint16_t)) == -1 ||	/* source AS */
567	    ibuf_get_n16(msg, &asnum) == -1 ||		/* dest AS */
568	    ibuf_skip(msg, sizeof(uint16_t)) == -1 ||	/* iface index */
569	    ibuf_get_n16(msg, &afi) == -1)
570		goto fail;
571	p->peers->asnum = asnum;
572
573	/* source + dest ip */
574	switch (afi) {
575	case MRT_DUMP_AFI_IP:
576		/* source IP */
577		if (ibuf_skip(msg, sizeof(struct in_addr)) == -1)
578			goto fail;
579		/* dest IP */
580		if (mrt_extract_addr(msg, &p->peers->addr, AID_INET) == -1)
581			goto fail;
582		break;
583	case MRT_DUMP_AFI_IPv6:
584		/* source IP */
585		if (ibuf_skip(msg, sizeof(struct in6_addr)) == -1)
586			goto fail;
587		/* dest IP */
588		if (mrt_extract_addr(msg, &p->peers->addr, AID_INET6) == -1)
589			goto fail;
590		break;
591	}
592
593	if (ibuf_skip(msg, sizeof(uint16_t)) == -1 ||	/* view */
594	    ibuf_skip(msg, sizeof(uint16_t)) == -1 ||	/* status */
595	    ibuf_get_n32(msg, &tmp32) == -1)		/* originated */
596		goto fail;
597	re->originated = tmp32;
598
599	if (ibuf_get_n16(msg, &afi) == -1 ||		/* afi */
600	    ibuf_get_n8(msg, &safi) == -1)		/* safi */
601		goto fail;
602	if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC)
603		goto fail;
604
605	if (ibuf_get_n8(msg, &nhlen) == -1)		/* nhlen */
606		goto fail;
607
608	/* nexthop */
609	if (mrt_extract_addr(msg, &re->nexthop, aid) == -1)
610		goto fail;
611
612	/* prefix */
613	if (mrt_extract_prefix(msg, aid, &r->prefix, &r->prefixlen,
614	    verbose) == -1)
615		goto fail;
616
617	if (ibuf_get_n16(msg, &alen) == -1 ||
618	    ibuf_get_ibuf(msg, alen, &abuf) == -1)
619		goto fail;
620	if (mrt_extract_attr(re, &abuf, r->prefix.aid, 0) == -1)
621		goto fail;
622
623	return (0);
624fail:
625	mrt_free_rib(r);
626	return (-1);
627}
628
629int
630mrt_extract_attr(struct mrt_rib_entry *re, struct ibuf *buf, uint8_t aid,
631    int as4)
632{
633	struct ibuf	abuf;
634	struct mrt_attr	*ap;
635	size_t		alen, hlen;
636	uint8_t		type, flags;
637
638	do {
639		ibuf_from_ibuf(&abuf, buf);
640		if (ibuf_get_n8(&abuf, &flags) == -1 ||
641		    ibuf_get_n8(&abuf, &type) == -1)
642			return (-1);
643
644		if (flags & MRT_ATTR_EXTLEN) {
645			uint16_t tmp16;
646			if (ibuf_get_n16(&abuf, &tmp16) == -1)
647				return (-1);
648			alen = tmp16;
649			hlen = 4;
650		} else {
651			uint8_t tmp8;
652			if (ibuf_get_n8(&abuf, &tmp8) == -1)
653				return (-1);
654			alen = tmp8;
655			hlen = 3;
656		}
657		if (ibuf_truncate(&abuf, alen) == -1)
658			return (-1);
659		/* consume the attribute in buf before moving forward */
660		if (ibuf_skip(buf, hlen + alen) == -1)
661			return (-1);
662
663		switch (type) {
664		case MRT_ATTR_ORIGIN:
665			if (alen != 1)
666				return (-1);
667			if (ibuf_get_n8(&abuf, &re->origin) == -1)
668				return (-1);
669			break;
670		case MRT_ATTR_ASPATH:
671			if (as4) {
672				re->aspath_len = alen;
673				if ((re->aspath = malloc(alen)) == NULL)
674					err(1, "malloc");
675				if (ibuf_get(&abuf, re->aspath, alen) == -1)
676					return (-1);
677			} else {
678				re->aspath = mrt_aspath_inflate(&abuf,
679				    &re->aspath_len);
680				if (re->aspath == NULL)
681					return (-1);
682			}
683			break;
684		case MRT_ATTR_NEXTHOP:
685			if (alen != 4)
686				return (-1);
687			if (aid != AID_INET)
688				break;
689			if (ibuf_get(&abuf, &re->nexthop.v4,
690			    sizeof(re->nexthop.v4)) == -1)
691				return (-1);
692			re->nexthop.aid = AID_INET;
693			break;
694		case MRT_ATTR_MED:
695			if (alen != 4)
696				return (-1);
697			if (ibuf_get_n32(&abuf, &re->med) == -1)
698				return (-1);
699			break;
700		case MRT_ATTR_LOCALPREF:
701			if (alen != 4)
702				return (-1);
703			if (ibuf_get_n32(&abuf, &re->local_pref) == -1)
704				return (-1);
705			break;
706		case MRT_ATTR_MP_REACH_NLRI:
707			/*
708			 * XXX horrible hack:
709			 * Once again IETF and the real world differ in the
710			 * implementation. In short the abbreviated MP_NLRI
711			 * hack in the standard is not used in real life.
712			 * Detect the two cases by looking at the first byte
713			 * of the payload (either the nexthop addr length (RFC)
714			 * or the high byte of the AFI (old form)). If the
715			 * first byte matches the expected nexthop length it
716			 * is expected to be the RFC 6396 encoding.
717			 *
718			 * Checking for the hack skips over the nhlen.
719			 */
720			{
721				uint8_t	hack;
722				if (ibuf_get_n8(&abuf, &hack) == -1)
723					return (-1);
724				if (hack != alen - 1) {
725					if (ibuf_skip(&abuf, 3) == -1)
726						return (-1);
727				}
728			}
729			switch (aid) {
730			case AID_INET6:
731				if (ibuf_get(&abuf, &re->nexthop.v6,
732				    sizeof(re->nexthop.v6)) == -1)
733					return (-1);
734				re->nexthop.aid = aid;
735				break;
736			case AID_VPN_IPv4:
737				if (ibuf_skip(&abuf, sizeof(uint64_t)) == -1 ||
738				    ibuf_get(&abuf, &re->nexthop.v4,
739				    sizeof(re->nexthop.v4)) == -1)
740					return (-1);
741				re->nexthop.aid = aid;
742				break;
743			case AID_VPN_IPv6:
744				if (ibuf_skip(&abuf, sizeof(uint64_t)) == -1 ||
745				    ibuf_get(&abuf, &re->nexthop.v6,
746				    sizeof(re->nexthop.v6)) == -1)
747					return (-1);
748				re->nexthop.aid = aid;
749				break;
750			}
751			break;
752		case MRT_ATTR_AS4PATH:
753			if (!as4) {
754				free(re->aspath);
755				re->aspath_len = alen;
756				if ((re->aspath = malloc(alen)) == NULL)
757					err(1, "malloc");
758				if (ibuf_get(&abuf, re->aspath, alen) == -1)
759					return (-1);
760				break;
761			}
762			/* FALLTHROUGH */
763		default:
764			re->nattrs++;
765			if (re->nattrs >= UCHAR_MAX)
766				err(1, "too many attributes");
767			ap = reallocarray(re->attrs,
768			    re->nattrs, sizeof(struct mrt_attr));
769			if (ap == NULL)
770				err(1, "realloc");
771			re->attrs = ap;
772			ap = re->attrs + re->nattrs - 1;
773			ibuf_rewind(&abuf);
774			ap->attr_len = ibuf_size(&abuf);
775			if ((ap->attr = malloc(ap->attr_len)) == NULL)
776				err(1, "malloc");
777			if (ibuf_get(&abuf, ap->attr, ap->attr_len) == -1)
778				return (-1);
779			break;
780		}
781	} while (ibuf_size(buf) > 0);
782
783	return (0);
784}
785
786void
787mrt_free_peers(struct mrt_peer *p)
788{
789	free(p->peers);
790	free(p->view);
791	free(p);
792}
793
794void
795mrt_free_rib(struct mrt_rib *r)
796{
797	uint16_t	i, j;
798
799	for (i = 0; i < r->nentries && r->entries; i++) {
800		for (j = 0; j < r->entries[i].nattrs; j++)
801			 free(r->entries[i].attrs[j].attr);
802		free(r->entries[i].attrs);
803		free(r->entries[i].aspath);
804	}
805
806	free(r->entries);
807	free(r);
808}
809
810u_char *
811mrt_aspath_inflate(struct ibuf *buf, uint16_t *newlen)
812{
813	struct ibuf *asbuf;
814	u_char *data;
815	size_t len;
816
817	*newlen = 0;
818	asbuf = aspath_inflate(buf);
819	if (asbuf == NULL)
820		return NULL;
821
822	len = ibuf_size(asbuf);
823	if ((data = malloc(len)) == NULL)
824		err(1, "malloc");
825	if (ibuf_get(asbuf, data, len) == -1) {
826		ibuf_free(asbuf);
827		return (NULL);
828	}
829	ibuf_free(asbuf);
830	*newlen = len;
831	return (data);
832}
833
834int
835mrt_extract_addr(struct ibuf *msg, struct bgpd_addr *addr, uint8_t aid)
836{
837	memset(addr, 0, sizeof(*addr));
838	switch (aid) {
839	case AID_INET:
840		if (ibuf_get(msg, &addr->v4, sizeof(addr->v4)) == -1)
841			return (-1);
842		break;
843	case AID_INET6:
844		if (ibuf_get(msg, &addr->v6, sizeof(addr->v6)) == -1)
845			return (-1);
846		break;
847	case AID_VPN_IPv4:
848		/* XXX labelstack and rd missing */
849		if (ibuf_skip(msg, sizeof(uint64_t)) == -1 ||
850		    ibuf_get(msg, &addr->v4, sizeof(addr->v4)) == -1)
851			return (-1);
852		break;
853	case AID_VPN_IPv6:
854		/* XXX labelstack and rd missing */
855		if (ibuf_skip(msg, sizeof(uint64_t)) == -1 ||
856		    ibuf_get(msg, &addr->v6, sizeof(addr->v6)) == -1)
857			return (-1);
858		break;
859	default:
860		return (-1);
861	}
862	addr->aid = aid;
863	return 0;
864}
865
866int
867mrt_extract_prefix(struct ibuf *msg, uint8_t aid, struct bgpd_addr *prefix,
868    uint8_t *prefixlen, int verbose)
869{
870	int r;
871
872	switch (aid) {
873	case AID_INET:
874		r = nlri_get_prefix(msg, prefix, prefixlen);
875		break;
876	case AID_INET6:
877		r = nlri_get_prefix6(msg, prefix, prefixlen);
878		break;
879	case AID_VPN_IPv4:
880		r = nlri_get_vpn4(msg, prefix, prefixlen, 0);
881		break;
882	case AID_VPN_IPv6:
883		r = nlri_get_vpn6(msg, prefix, prefixlen, 0);
884		break;
885	default:
886		if (verbose)
887			printf("unknown prefix AID %d\n", aid);
888		return -1;
889	}
890	if (r == -1 && verbose)
891		printf("failed to parse prefix of AID %d\n", aid);
892	return r;
893}
894
895int
896mrt_parse_state(struct mrt_bgp_state *s, struct mrt_hdr *hdr, struct ibuf *msg,
897    int verbose)
898{
899	struct timespec		 t;
900	uint32_t		 sas, das, usec;
901	uint16_t		 sas16, das16, afi;
902	uint8_t			 aid;
903
904	t.tv_sec = ntohl(hdr->timestamp);
905	t.tv_nsec = 0;
906
907	/* handle the microsec field for _ET header */
908	if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
909		if (ibuf_get_n32(msg, &usec) == -1)
910			return (-1);
911		t.tv_nsec = usec * 1000;
912	}
913
914	switch (ntohs(hdr->subtype)) {
915	case BGP4MP_STATE_CHANGE:
916		if (ibuf_get_n16(msg, &sas16) == -1 ||	/* source as */
917		    ibuf_get_n16(msg, &das16) == -1 ||	/* dest as */
918		    ibuf_skip(msg, 2) == -1 ||		/* if_index */
919		    ibuf_get_n16(msg, &afi) == -1)	/* afi */
920			return (-1);
921		sas = sas16;
922		das = das16;
923		break;
924	case BGP4MP_STATE_CHANGE_AS4:
925		if (ibuf_get_n32(msg, &sas) == -1 ||	/* source as */
926		    ibuf_get_n32(msg, &das) == -1 ||	/* dest as */
927		    ibuf_skip(msg, 2) == -1 ||		/* if_index */
928		    ibuf_get_n16(msg, &afi) == -1)	/* afi */
929			return (-1);
930		break;
931	default:
932		errx(1, "mrt_parse_state: bad subtype");
933	}
934
935	/* src & dst addr */
936	if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC)
937		return (-1);
938
939	memset(s, 0, sizeof(*s));
940	s->time = t;
941	s->src_as = sas;
942	s->dst_as = das;
943
944	if (mrt_extract_addr(msg, &s->src, aid) == -1)
945		return (-1);
946	if (mrt_extract_addr(msg, &s->dst, aid) == -1)
947		return (-1);
948
949	/* states */
950	if (ibuf_get_n16(msg, &s->old_state) == -1 ||
951	    ibuf_get_n16(msg, &s->new_state) == -1)
952		return (-1);
953
954	return (0);
955}
956
957int
958mrt_parse_msg(struct mrt_bgp_msg *m, struct mrt_hdr *hdr, struct ibuf *msg,
959    int verbose)
960{
961	struct timespec		 t;
962	uint32_t		 sas, das, usec;
963	uint16_t		 sas16, das16, afi;
964	int			 addpath = 0;
965	uint8_t			 aid;
966
967	t.tv_sec = ntohl(hdr->timestamp);
968	t.tv_nsec = 0;
969
970	/* handle the microsec field for _ET header */
971	if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
972		if (ibuf_get_n32(msg, &usec) == -1)
973			return (-1);
974		t.tv_nsec = usec * 1000;
975	}
976
977	switch (ntohs(hdr->subtype)) {
978	case BGP4MP_MESSAGE_ADDPATH:
979	case BGP4MP_MESSAGE_LOCAL_ADDPATH:
980		addpath = 1;
981		/* FALLTHROUGH */
982	case BGP4MP_MESSAGE:
983	case BGP4MP_MESSAGE_LOCAL:
984		if (ibuf_get_n16(msg, &sas16) == -1 ||	/* source as */
985		    ibuf_get_n16(msg, &das16) == -1 ||	/* dest as */
986		    ibuf_skip(msg, 2) == -1 ||		/* if_index */
987		    ibuf_get_n16(msg, &afi) == -1)	/* afi */
988			return (-1);
989		sas = sas16;
990		das = das16;
991		break;
992	case BGP4MP_MESSAGE_AS4_ADDPATH:
993	case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH:
994		addpath = 1;
995		/* FALLTHROUGH */
996	case BGP4MP_MESSAGE_AS4:
997	case BGP4MP_MESSAGE_AS4_LOCAL:
998		if (ibuf_get_n32(msg, &sas) == -1 ||	/* source as */
999		    ibuf_get_n32(msg, &das) == -1 ||	/* dest as */
1000		    ibuf_skip(msg, 2) == -1 ||		/* if_index */
1001		    ibuf_get_n16(msg, &afi) == -1)	/* afi */
1002			return (-1);
1003		break;
1004	default:
1005		errx(1, "mrt_parse_msg: bad subtype");
1006	}
1007
1008	/* src & dst addr */
1009	if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC)
1010		return (-1);
1011
1012	memset(m, 0, sizeof(*m));
1013	m->time = t;
1014	m->src_as = sas;
1015	m->dst_as = das;
1016	m->add_path = addpath;
1017
1018	if (mrt_extract_addr(msg, &m->src, aid) == -1 ||
1019	    mrt_extract_addr(msg, &m->dst, aid) == -1)
1020		return (-1);
1021
1022	/* msg */
1023	ibuf_from_ibuf(&m->msg, msg);
1024	return (0);
1025}
1026