xref: /openbsd/usr.sbin/bgpctl/mrtparser.c (revision 6b941460)
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 
32 struct mrt_peer	*mrt_parse_v2_peer(struct mrt_hdr *, struct ibuf *);
33 struct mrt_rib	*mrt_parse_v2_rib(struct mrt_hdr *, struct ibuf *, int);
34 int	mrt_parse_dump(struct mrt_hdr *, struct ibuf *, struct mrt_peer **,
35 	    struct mrt_rib **);
36 int	mrt_parse_dump_mp(struct mrt_hdr *, struct ibuf *, struct mrt_peer **,
37 	    struct mrt_rib **, int);
38 int	mrt_extract_attr(struct mrt_rib_entry *, struct ibuf *, uint8_t, int);
39 
40 void	mrt_free_peers(struct mrt_peer *);
41 void	mrt_free_rib(struct mrt_rib *);
42 
43 u_char *mrt_aspath_inflate(struct ibuf *, uint16_t *);
44 int	mrt_extract_addr(struct ibuf *, struct bgpd_addr *, uint8_t);
45 int	mrt_extract_prefix(struct ibuf *, uint8_t, struct bgpd_addr *,
46 	    uint8_t *, int);
47 
48 int	mrt_parse_state(struct mrt_bgp_state *, struct mrt_hdr *,
49 	    struct ibuf *, int);
50 int	mrt_parse_msg(struct mrt_bgp_msg *, struct mrt_hdr *,
51 	    struct ibuf *, int);
52 
53 static size_t
mrt_read_buf(int fd,void * buf,size_t len)54 mrt_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 
74 static struct ibuf *
mrt_read_msg(int fd,struct mrt_hdr * hdr)75 mrt_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 
95 void
mrt_parse(int fd,struct mrt_parser * p,int verbose)96 mrt_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 
240 static int
mrt_afi2aid(int afi,int safi,int verbose)241 mrt_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 
264 struct mrt_peer *
mrt_parse_v2_peer(struct mrt_hdr * hdr,struct ibuf * msg)265 mrt_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);
333 fail:
334 	mrt_free_peers(p);
335 	free(peers);
336 	return (NULL);
337 }
338 
339 struct mrt_rib *
mrt_parse_v2_rib(struct mrt_hdr * hdr,struct ibuf * msg,int verbose)340 mrt_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);
443 fail:
444 	mrt_free_rib(r);
445 	free(entries);
446 	return (NULL);
447 }
448 
449 int
mrt_parse_dump(struct mrt_hdr * hdr,struct ibuf * msg,struct mrt_peer ** pp,struct mrt_rib ** rp)450 mrt_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);
523 fail:
524 	mrt_free_rib(r);
525 	return (-1);
526 }
527 
528 int
mrt_parse_dump_mp(struct mrt_hdr * hdr,struct ibuf * msg,struct mrt_peer ** pp,struct mrt_rib ** rp,int verbose)529 mrt_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);
624 fail:
625 	mrt_free_rib(r);
626 	return (-1);
627 }
628 
629 int
mrt_extract_attr(struct mrt_rib_entry * re,struct ibuf * buf,uint8_t aid,int as4)630 mrt_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 
786 void
mrt_free_peers(struct mrt_peer * p)787 mrt_free_peers(struct mrt_peer *p)
788 {
789 	free(p->peers);
790 	free(p->view);
791 	free(p);
792 }
793 
794 void
mrt_free_rib(struct mrt_rib * r)795 mrt_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 
810 u_char *
mrt_aspath_inflate(struct ibuf * buf,uint16_t * newlen)811 mrt_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 
834 int
mrt_extract_addr(struct ibuf * msg,struct bgpd_addr * addr,uint8_t aid)835 mrt_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 
866 int
mrt_extract_prefix(struct ibuf * msg,uint8_t aid,struct bgpd_addr * prefix,uint8_t * prefixlen,int verbose)867 mrt_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 
895 int
mrt_parse_state(struct mrt_bgp_state * s,struct mrt_hdr * hdr,struct ibuf * msg,int verbose)896 mrt_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 
957 int
mrt_parse_msg(struct mrt_bgp_msg * m,struct mrt_hdr * hdr,struct ibuf * msg,int verbose)958 mrt_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