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