/* $OpenBSD: mrtparser.c,v 1.22 2024/02/01 11:37:10 claudio Exp $ */ /* * Copyright (c) 2011 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "mrt.h" #include "mrtparser.h" struct mrt_peer *mrt_parse_v2_peer(struct mrt_hdr *, struct ibuf *); struct mrt_rib *mrt_parse_v2_rib(struct mrt_hdr *, struct ibuf *, int); int mrt_parse_dump(struct mrt_hdr *, struct ibuf *, struct mrt_peer **, struct mrt_rib **); int mrt_parse_dump_mp(struct mrt_hdr *, struct ibuf *, struct mrt_peer **, struct mrt_rib **, int); int mrt_extract_attr(struct mrt_rib_entry *, struct ibuf *, uint8_t, int); void mrt_free_peers(struct mrt_peer *); void mrt_free_rib(struct mrt_rib *); u_char *mrt_aspath_inflate(struct ibuf *, uint16_t *); int mrt_extract_addr(struct ibuf *, struct bgpd_addr *, uint8_t); int mrt_extract_prefix(struct ibuf *, uint8_t, struct bgpd_addr *, uint8_t *, int); int mrt_parse_state(struct mrt_bgp_state *, struct mrt_hdr *, struct ibuf *, int); int mrt_parse_msg(struct mrt_bgp_msg *, struct mrt_hdr *, struct ibuf *, int); static size_t mrt_read_buf(int fd, void *buf, size_t len) { char *b = buf; ssize_t n; while (len > 0) { if ((n = read(fd, b, len)) == -1) { if (errno == EINTR) continue; err(1, "read"); } if (n == 0) break; b += n; len -= n; } return (b - (char *)buf); } static struct ibuf * mrt_read_msg(int fd, struct mrt_hdr *hdr) { struct ibuf *buf; size_t len; memset(hdr, 0, sizeof(*hdr)); if (mrt_read_buf(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) return (NULL); len = ntohl(hdr->length); if ((buf = ibuf_open(len)) == NULL) err(1, "ibuf_open(%zu)", len); if (mrt_read_buf(fd, ibuf_reserve(buf, len), len) != len) { ibuf_free(buf); return (NULL); } return (buf); } void mrt_parse(int fd, struct mrt_parser *p, int verbose) { struct mrt_hdr h; struct mrt_bgp_state s; struct mrt_bgp_msg m; struct mrt_peer *pctx = NULL; struct mrt_rib *r; struct ibuf *msg; while ((msg = mrt_read_msg(fd, &h)) != NULL) { if (ibuf_size(msg) != ntohl(h.length)) errx(1, "corrupt message, %zu vs %u", ibuf_size(msg), ntohl(h.length)); switch (ntohs(h.type)) { case MSG_NULL: case MSG_START: case MSG_DIE: case MSG_I_AM_DEAD: case MSG_PEER_DOWN: case MSG_PROTOCOL_BGP: case MSG_PROTOCOL_IDRP: case MSG_PROTOCOL_BGP4PLUS: case MSG_PROTOCOL_BGP4PLUS1: if (verbose) printf("deprecated MRT type %d\n", ntohs(h.type)); break; case MSG_PROTOCOL_RIP: case MSG_PROTOCOL_RIPNG: case MSG_PROTOCOL_OSPF: case MSG_PROTOCOL_ISIS_ET: case MSG_PROTOCOL_ISIS: case MSG_PROTOCOL_OSPFV3_ET: case MSG_PROTOCOL_OSPFV3: if (verbose) printf("unsupported MRT type %d\n", ntohs(h.type)); break; case MSG_TABLE_DUMP: switch (ntohs(h.subtype)) { case MRT_DUMP_AFI_IP: case MRT_DUMP_AFI_IPv6: if (p->dump == NULL) break; if (mrt_parse_dump(&h, msg, &pctx, &r) == 0) { if (p->dump) p->dump(r, pctx, p->arg); mrt_free_rib(r); } break; default: if (verbose) printf("unknown AFI %d in table dump\n", ntohs(h.subtype)); break; } break; case MSG_TABLE_DUMP_V2: switch (ntohs(h.subtype)) { case MRT_DUMP_V2_PEER_INDEX_TABLE: if (p->dump == NULL) break; if (pctx) mrt_free_peers(pctx); pctx = mrt_parse_v2_peer(&h, msg); break; case MRT_DUMP_V2_RIB_IPV4_UNICAST: case MRT_DUMP_V2_RIB_IPV4_MULTICAST: case MRT_DUMP_V2_RIB_IPV6_UNICAST: case MRT_DUMP_V2_RIB_IPV6_MULTICAST: case MRT_DUMP_V2_RIB_GENERIC: case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH: case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH: case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH: case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH: case MRT_DUMP_V2_RIB_GENERIC_ADDPATH: if (p->dump == NULL) break; r = mrt_parse_v2_rib(&h, msg, verbose); if (r) { if (p->dump) p->dump(r, pctx, p->arg); mrt_free_rib(r); } break; default: if (verbose) printf("unhandled DUMP_V2 subtype %d\n", ntohs(h.subtype)); break; } break; case MSG_PROTOCOL_BGP4MP_ET: case MSG_PROTOCOL_BGP4MP: switch (ntohs(h.subtype)) { case BGP4MP_STATE_CHANGE: case BGP4MP_STATE_CHANGE_AS4: if (mrt_parse_state(&s, &h, msg, verbose) != -1) { if (p->state) p->state(&s, p->arg); } break; case BGP4MP_MESSAGE: case BGP4MP_MESSAGE_AS4: case BGP4MP_MESSAGE_LOCAL: case BGP4MP_MESSAGE_AS4_LOCAL: case BGP4MP_MESSAGE_ADDPATH: case BGP4MP_MESSAGE_AS4_ADDPATH: case BGP4MP_MESSAGE_LOCAL_ADDPATH: case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH: if (mrt_parse_msg(&m, &h, msg, verbose) != -1) { if (p->message) p->message(&m, p->arg); } break; case BGP4MP_ENTRY: if (p->dump == NULL) break; if (mrt_parse_dump_mp(&h, msg, &pctx, &r, verbose) == 0) { if (p->dump) p->dump(r, pctx, p->arg); mrt_free_rib(r); } break; default: if (verbose) printf("unhandled BGP4MP subtype %d\n", ntohs(h.subtype)); break; } break; default: if (verbose) printf("unknown MRT type %d\n", ntohs(h.type)); break; } ibuf_free(msg); } if (pctx) mrt_free_peers(pctx); } static int mrt_afi2aid(int afi, int safi, int verbose) { switch (afi) { case MRT_DUMP_AFI_IP: if (safi == -1 || safi == 1 || safi == 2) return AID_INET; else if (safi == 128) return AID_VPN_IPv4; break; case MRT_DUMP_AFI_IPv6: if (safi == -1 || safi == 1 || safi == 2) return AID_INET6; else if (safi == 128) return AID_VPN_IPv6; break; default: break; } if (verbose) printf("unhandled AFI/SAFI %d/%d\n", afi, safi); return AID_UNSPEC; } struct mrt_peer * mrt_parse_v2_peer(struct mrt_hdr *hdr, struct ibuf *msg) { struct mrt_peer_entry *peers = NULL; struct mrt_peer *p; uint32_t bid; uint16_t cnt, i; if (ibuf_size(msg) < 8) /* min msg size */ return NULL; p = calloc(1, sizeof(struct mrt_peer)); if (p == NULL) err(1, "calloc"); /* collector bgp id */ if (ibuf_get_n32(msg, &bid) == -1 || ibuf_get_n16(msg, &cnt) == -1) goto fail; /* view name */ if (cnt != 0) { if ((p->view = malloc(cnt + 1)) == NULL) err(1, "malloc"); if (ibuf_get(msg, p->view, cnt) == -1) goto fail; p->view[cnt] = 0; } else if ((p->view = strdup("")) == NULL) err(1, "strdup"); /* peer_count */ if (ibuf_get_n16(msg, &cnt) == -1) goto fail; /* peer entries */ if ((peers = calloc(cnt, sizeof(struct mrt_peer_entry))) == NULL) err(1, "calloc"); for (i = 0; i < cnt; i++) { uint8_t type; if (ibuf_get_n8(msg, &type) == -1 || ibuf_get_n32(msg, &peers[i].bgp_id) == -1) goto fail; if (type & MRT_DUMP_V2_PEER_BIT_I) { if (mrt_extract_addr(msg, &peers[i].addr, AID_INET6) == -1) goto fail; } else { if (mrt_extract_addr(msg, &peers[i].addr, AID_INET) == -1) goto fail; } if (type & MRT_DUMP_V2_PEER_BIT_A) { if (ibuf_get_n32(msg, &peers[i].asnum) == -1) goto fail; } else { uint16_t as2; if (ibuf_get_n16(msg, &as2) == -1) goto fail; peers[i].asnum = as2; } } p->peers = peers; p->npeers = cnt; return (p); fail: mrt_free_peers(p); free(peers); return (NULL); } struct mrt_rib * mrt_parse_v2_rib(struct mrt_hdr *hdr, struct ibuf *msg, int verbose) { struct mrt_rib_entry *entries = NULL; struct mrt_rib *r; uint16_t i, afi; uint8_t safi, aid; r = calloc(1, sizeof(struct mrt_rib)); if (r == NULL) err(1, "calloc"); /* seq_num */ if (ibuf_get_n32(msg, &r->seqnum) == -1) goto fail; switch (ntohs(hdr->subtype)) { case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH: case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH: r->add_path = 1; /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_IPV4_UNICAST: case MRT_DUMP_V2_RIB_IPV4_MULTICAST: /* prefix */ if (mrt_extract_prefix(msg, AID_INET, &r->prefix, &r->prefixlen, verbose) == -1) goto fail; break; case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH: case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH: r->add_path = 1; /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_IPV6_UNICAST: case MRT_DUMP_V2_RIB_IPV6_MULTICAST: /* prefix */ if (mrt_extract_prefix(msg, AID_INET6, &r->prefix, &r->prefixlen, verbose) == -1) goto fail; break; case MRT_DUMP_V2_RIB_GENERIC_ADDPATH: /* * RFC8050 handling for add-path has special handling for * RIB_GENERIC_ADDPATH but nobody implements it that way. * So just use the same way as for the other _ADDPATH types. */ r->add_path = 1; /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_GENERIC: /* fetch AFI/SAFI pair */ if (ibuf_get_n16(msg, &afi) == -1 || ibuf_get_n8(msg, &safi) == -1) goto fail; if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC) goto fail; /* prefix */ if (mrt_extract_prefix(msg, aid, &r->prefix, &r->prefixlen, verbose) == -1) goto fail; break; default: errx(1, "unknown subtype %hd", ntohs(hdr->subtype)); } /* entries count */ if (ibuf_get_n16(msg, &r->nentries) == -1) goto fail; /* entries */ if ((entries = calloc(r->nentries, sizeof(struct mrt_rib_entry))) == NULL) err(1, "calloc"); for (i = 0; i < r->nentries; i++) { struct ibuf abuf; uint32_t otm; uint16_t alen; /* peer index */ if (ibuf_get_n16(msg, &entries[i].peer_idx) == -1) goto fail; /* originated */ if (ibuf_get_n32(msg, &otm) == -1) goto fail; entries[i].originated = otm; if (r->add_path) { if (ibuf_get_n32(msg, &entries[i].path_id) == -1) goto fail; } /* attr_len */ if (ibuf_get_n16(msg, &alen) == -1 || ibuf_get_ibuf(msg, alen, &abuf) == -1) goto fail; /* attr */ if (mrt_extract_attr(&entries[i], &abuf, r->prefix.aid, 1) == -1) goto fail; } r->entries = entries; return (r); fail: mrt_free_rib(r); free(entries); return (NULL); } int mrt_parse_dump(struct mrt_hdr *hdr, struct ibuf *msg, struct mrt_peer **pp, struct mrt_rib **rp) { struct ibuf abuf; struct mrt_peer *p; struct mrt_rib *r; struct mrt_rib_entry *re; uint32_t tmp32; uint16_t tmp16, alen; if (*pp == NULL) { *pp = calloc(1, sizeof(struct mrt_peer)); if (*pp == NULL) err(1, "calloc"); (*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry)); if ((*pp)->peers == NULL) err(1, "calloc"); (*pp)->npeers = 1; } p = *pp; *rp = r = calloc(1, sizeof(struct mrt_rib)); if (r == NULL) err(1, "calloc"); re = calloc(1, sizeof(struct mrt_rib_entry)); if (re == NULL) err(1, "calloc"); r->nentries = 1; r->entries = re; if (ibuf_skip(msg, sizeof(uint16_t)) == -1 || /* view */ ibuf_get_n16(msg, &tmp16) == -1) /* seqnum */ goto fail; r->seqnum = tmp16; switch (ntohs(hdr->subtype)) { case MRT_DUMP_AFI_IP: if (mrt_extract_addr(msg, &r->prefix, AID_INET) == -1) goto fail; break; case MRT_DUMP_AFI_IPv6: if (mrt_extract_addr(msg, &r->prefix, AID_INET6) == -1) goto fail; break; } if (ibuf_get_n8(msg, &r->prefixlen) == -1 || /* prefixlen */ ibuf_skip(msg, 1) == -1 || /* status */ ibuf_get_n32(msg, &tmp32) == -1) /* originated */ goto fail; re->originated = tmp32; /* peer ip */ switch (ntohs(hdr->subtype)) { case MRT_DUMP_AFI_IP: if (mrt_extract_addr(msg, &p->peers->addr, AID_INET) == -1) goto fail; break; case MRT_DUMP_AFI_IPv6: if (mrt_extract_addr(msg, &p->peers->addr, AID_INET6) == -1) goto fail; break; } if (ibuf_get_n16(msg, &tmp16) == -1) goto fail; p->peers->asnum = tmp16; if (ibuf_get_n16(msg, &alen) == -1 || ibuf_get_ibuf(msg, alen, &abuf) == -1) goto fail; /* attr */ if (mrt_extract_attr(re, &abuf, r->prefix.aid, 0) == -1) goto fail; return (0); fail: mrt_free_rib(r); return (-1); } int mrt_parse_dump_mp(struct mrt_hdr *hdr, struct ibuf *msg, struct mrt_peer **pp, struct mrt_rib **rp, int verbose) { struct ibuf abuf; struct mrt_peer *p; struct mrt_rib *r; struct mrt_rib_entry *re; uint32_t tmp32; uint16_t asnum, alen, afi; uint8_t safi, nhlen, aid; if (*pp == NULL) { *pp = calloc(1, sizeof(struct mrt_peer)); if (*pp == NULL) err(1, "calloc"); (*pp)->peers = calloc(1, sizeof(struct mrt_peer_entry)); if ((*pp)->peers == NULL) err(1, "calloc"); (*pp)->npeers = 1; } p = *pp; *rp = r = calloc(1, sizeof(struct mrt_rib)); if (r == NULL) err(1, "calloc"); re = calloc(1, sizeof(struct mrt_rib_entry)); if (re == NULL) err(1, "calloc"); r->nentries = 1; r->entries = re; /* just ignore the microsec field for _ET header for now */ if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) { if (ibuf_skip(msg, sizeof(uint32_t)) == -1) goto fail; } if (ibuf_skip(msg, sizeof(uint16_t)) == -1 || /* source AS */ ibuf_get_n16(msg, &asnum) == -1 || /* dest AS */ ibuf_skip(msg, sizeof(uint16_t)) == -1 || /* iface index */ ibuf_get_n16(msg, &afi) == -1) goto fail; p->peers->asnum = asnum; /* source + dest ip */ switch (afi) { case MRT_DUMP_AFI_IP: /* source IP */ if (ibuf_skip(msg, sizeof(struct in_addr)) == -1) goto fail; /* dest IP */ if (mrt_extract_addr(msg, &p->peers->addr, AID_INET) == -1) goto fail; break; case MRT_DUMP_AFI_IPv6: /* source IP */ if (ibuf_skip(msg, sizeof(struct in6_addr)) == -1) goto fail; /* dest IP */ if (mrt_extract_addr(msg, &p->peers->addr, AID_INET6) == -1) goto fail; break; } if (ibuf_skip(msg, sizeof(uint16_t)) == -1 || /* view */ ibuf_skip(msg, sizeof(uint16_t)) == -1 || /* status */ ibuf_get_n32(msg, &tmp32) == -1) /* originated */ goto fail; re->originated = tmp32; if (ibuf_get_n16(msg, &afi) == -1 || /* afi */ ibuf_get_n8(msg, &safi) == -1) /* safi */ goto fail; if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC) goto fail; if (ibuf_get_n8(msg, &nhlen) == -1) /* nhlen */ goto fail; /* nexthop */ if (mrt_extract_addr(msg, &re->nexthop, aid) == -1) goto fail; /* prefix */ if (mrt_extract_prefix(msg, aid, &r->prefix, &r->prefixlen, verbose) == -1) goto fail; if (ibuf_get_n16(msg, &alen) == -1 || ibuf_get_ibuf(msg, alen, &abuf) == -1) goto fail; if (mrt_extract_attr(re, &abuf, r->prefix.aid, 0) == -1) goto fail; return (0); fail: mrt_free_rib(r); return (-1); } int mrt_extract_attr(struct mrt_rib_entry *re, struct ibuf *buf, uint8_t aid, int as4) { struct ibuf abuf; struct mrt_attr *ap; size_t alen, hlen; uint8_t type, flags; do { ibuf_from_ibuf(&abuf, buf); if (ibuf_get_n8(&abuf, &flags) == -1 || ibuf_get_n8(&abuf, &type) == -1) return (-1); if (flags & MRT_ATTR_EXTLEN) { uint16_t tmp16; if (ibuf_get_n16(&abuf, &tmp16) == -1) return (-1); alen = tmp16; hlen = 4; } else { uint8_t tmp8; if (ibuf_get_n8(&abuf, &tmp8) == -1) return (-1); alen = tmp8; hlen = 3; } if (ibuf_truncate(&abuf, alen) == -1) return (-1); /* consume the attribute in buf before moving forward */ if (ibuf_skip(buf, hlen + alen) == -1) return (-1); switch (type) { case MRT_ATTR_ORIGIN: if (alen != 1) return (-1); if (ibuf_get_n8(&abuf, &re->origin) == -1) return (-1); break; case MRT_ATTR_ASPATH: if (as4) { re->aspath_len = alen; if ((re->aspath = malloc(alen)) == NULL) err(1, "malloc"); if (ibuf_get(&abuf, re->aspath, alen) == -1) return (-1); } else { re->aspath = mrt_aspath_inflate(&abuf, &re->aspath_len); if (re->aspath == NULL) return (-1); } break; case MRT_ATTR_NEXTHOP: if (alen != 4) return (-1); if (aid != AID_INET) break; if (ibuf_get(&abuf, &re->nexthop.v4, sizeof(re->nexthop.v4)) == -1) return (-1); re->nexthop.aid = AID_INET; break; case MRT_ATTR_MED: if (alen != 4) return (-1); if (ibuf_get_n32(&abuf, &re->med) == -1) return (-1); break; case MRT_ATTR_LOCALPREF: if (alen != 4) return (-1); if (ibuf_get_n32(&abuf, &re->local_pref) == -1) return (-1); break; case MRT_ATTR_MP_REACH_NLRI: /* * XXX horrible hack: * Once again IETF and the real world differ in the * implementation. In short the abbreviated MP_NLRI * hack in the standard is not used in real life. * Detect the two cases by looking at the first byte * of the payload (either the nexthop addr length (RFC) * or the high byte of the AFI (old form)). If the * first byte matches the expected nexthop length it * is expected to be the RFC 6396 encoding. * * Checking for the hack skips over the nhlen. */ { uint8_t hack; if (ibuf_get_n8(&abuf, &hack) == -1) return (-1); if (hack != alen - 1) { if (ibuf_skip(&abuf, 3) == -1) return (-1); } } switch (aid) { case AID_INET6: if (ibuf_get(&abuf, &re->nexthop.v6, sizeof(re->nexthop.v6)) == -1) return (-1); re->nexthop.aid = aid; break; case AID_VPN_IPv4: if (ibuf_skip(&abuf, sizeof(uint64_t)) == -1 || ibuf_get(&abuf, &re->nexthop.v4, sizeof(re->nexthop.v4)) == -1) return (-1); re->nexthop.aid = aid; break; case AID_VPN_IPv6: if (ibuf_skip(&abuf, sizeof(uint64_t)) == -1 || ibuf_get(&abuf, &re->nexthop.v6, sizeof(re->nexthop.v6)) == -1) return (-1); re->nexthop.aid = aid; break; } break; case MRT_ATTR_AS4PATH: if (!as4) { free(re->aspath); re->aspath_len = alen; if ((re->aspath = malloc(alen)) == NULL) err(1, "malloc"); if (ibuf_get(&abuf, re->aspath, alen) == -1) return (-1); break; } /* FALLTHROUGH */ default: re->nattrs++; if (re->nattrs >= UCHAR_MAX) err(1, "too many attributes"); ap = reallocarray(re->attrs, re->nattrs, sizeof(struct mrt_attr)); if (ap == NULL) err(1, "realloc"); re->attrs = ap; ap = re->attrs + re->nattrs - 1; ibuf_rewind(&abuf); ap->attr_len = ibuf_size(&abuf); if ((ap->attr = malloc(ap->attr_len)) == NULL) err(1, "malloc"); if (ibuf_get(&abuf, ap->attr, ap->attr_len) == -1) return (-1); break; } } while (ibuf_size(buf) > 0); return (0); } void mrt_free_peers(struct mrt_peer *p) { free(p->peers); free(p->view); free(p); } void mrt_free_rib(struct mrt_rib *r) { uint16_t i, j; for (i = 0; i < r->nentries && r->entries; i++) { for (j = 0; j < r->entries[i].nattrs; j++) free(r->entries[i].attrs[j].attr); free(r->entries[i].attrs); free(r->entries[i].aspath); } free(r->entries); free(r); } u_char * mrt_aspath_inflate(struct ibuf *buf, uint16_t *newlen) { struct ibuf *asbuf; u_char *data; size_t len; *newlen = 0; asbuf = aspath_inflate(buf); if (asbuf == NULL) return NULL; len = ibuf_size(asbuf); if ((data = malloc(len)) == NULL) err(1, "malloc"); if (ibuf_get(asbuf, data, len) == -1) { ibuf_free(asbuf); return (NULL); } ibuf_free(asbuf); *newlen = len; return (data); } int mrt_extract_addr(struct ibuf *msg, struct bgpd_addr *addr, uint8_t aid) { memset(addr, 0, sizeof(*addr)); switch (aid) { case AID_INET: if (ibuf_get(msg, &addr->v4, sizeof(addr->v4)) == -1) return (-1); break; case AID_INET6: if (ibuf_get(msg, &addr->v6, sizeof(addr->v6)) == -1) return (-1); break; case AID_VPN_IPv4: /* XXX labelstack and rd missing */ if (ibuf_skip(msg, sizeof(uint64_t)) == -1 || ibuf_get(msg, &addr->v4, sizeof(addr->v4)) == -1) return (-1); break; case AID_VPN_IPv6: /* XXX labelstack and rd missing */ if (ibuf_skip(msg, sizeof(uint64_t)) == -1 || ibuf_get(msg, &addr->v6, sizeof(addr->v6)) == -1) return (-1); break; default: return (-1); } addr->aid = aid; return 0; } int mrt_extract_prefix(struct ibuf *msg, uint8_t aid, struct bgpd_addr *prefix, uint8_t *prefixlen, int verbose) { int r; switch (aid) { case AID_INET: r = nlri_get_prefix(msg, prefix, prefixlen); break; case AID_INET6: r = nlri_get_prefix6(msg, prefix, prefixlen); break; case AID_VPN_IPv4: r = nlri_get_vpn4(msg, prefix, prefixlen, 0); break; case AID_VPN_IPv6: r = nlri_get_vpn6(msg, prefix, prefixlen, 0); break; default: if (verbose) printf("unknown prefix AID %d\n", aid); return -1; } if (r == -1 && verbose) printf("failed to parse prefix of AID %d\n", aid); return r; } int mrt_parse_state(struct mrt_bgp_state *s, struct mrt_hdr *hdr, struct ibuf *msg, int verbose) { struct timespec t; uint32_t sas, das, usec; uint16_t sas16, das16, afi; uint8_t aid; t.tv_sec = ntohl(hdr->timestamp); t.tv_nsec = 0; /* handle the microsec field for _ET header */ if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) { if (ibuf_get_n32(msg, &usec) == -1) return (-1); t.tv_nsec = usec * 1000; } switch (ntohs(hdr->subtype)) { case BGP4MP_STATE_CHANGE: if (ibuf_get_n16(msg, &sas16) == -1 || /* source as */ ibuf_get_n16(msg, &das16) == -1 || /* dest as */ ibuf_skip(msg, 2) == -1 || /* if_index */ ibuf_get_n16(msg, &afi) == -1) /* afi */ return (-1); sas = sas16; das = das16; break; case BGP4MP_STATE_CHANGE_AS4: if (ibuf_get_n32(msg, &sas) == -1 || /* source as */ ibuf_get_n32(msg, &das) == -1 || /* dest as */ ibuf_skip(msg, 2) == -1 || /* if_index */ ibuf_get_n16(msg, &afi) == -1) /* afi */ return (-1); break; default: errx(1, "mrt_parse_state: bad subtype"); } /* src & dst addr */ if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC) return (-1); memset(s, 0, sizeof(*s)); s->time = t; s->src_as = sas; s->dst_as = das; if (mrt_extract_addr(msg, &s->src, aid) == -1) return (-1); if (mrt_extract_addr(msg, &s->dst, aid) == -1) return (-1); /* states */ if (ibuf_get_n16(msg, &s->old_state) == -1 || ibuf_get_n16(msg, &s->new_state) == -1) return (-1); return (0); } int mrt_parse_msg(struct mrt_bgp_msg *m, struct mrt_hdr *hdr, struct ibuf *msg, int verbose) { struct timespec t; uint32_t sas, das, usec; uint16_t sas16, das16, afi; int addpath = 0; uint8_t aid; t.tv_sec = ntohl(hdr->timestamp); t.tv_nsec = 0; /* handle the microsec field for _ET header */ if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) { if (ibuf_get_n32(msg, &usec) == -1) return (-1); t.tv_nsec = usec * 1000; } switch (ntohs(hdr->subtype)) { case BGP4MP_MESSAGE_ADDPATH: case BGP4MP_MESSAGE_LOCAL_ADDPATH: addpath = 1; /* FALLTHROUGH */ case BGP4MP_MESSAGE: case BGP4MP_MESSAGE_LOCAL: if (ibuf_get_n16(msg, &sas16) == -1 || /* source as */ ibuf_get_n16(msg, &das16) == -1 || /* dest as */ ibuf_skip(msg, 2) == -1 || /* if_index */ ibuf_get_n16(msg, &afi) == -1) /* afi */ return (-1); sas = sas16; das = das16; break; case BGP4MP_MESSAGE_AS4_ADDPATH: case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH: addpath = 1; /* FALLTHROUGH */ case BGP4MP_MESSAGE_AS4: case BGP4MP_MESSAGE_AS4_LOCAL: if (ibuf_get_n32(msg, &sas) == -1 || /* source as */ ibuf_get_n32(msg, &das) == -1 || /* dest as */ ibuf_skip(msg, 2) == -1 || /* if_index */ ibuf_get_n16(msg, &afi) == -1) /* afi */ return (-1); break; default: errx(1, "mrt_parse_msg: bad subtype"); } /* src & dst addr */ if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC) return (-1); memset(m, 0, sizeof(*m)); m->time = t; m->src_as = sas; m->dst_as = das; m->add_path = addpath; if (mrt_extract_addr(msg, &m->src, aid) == -1 || mrt_extract_addr(msg, &m->dst, aid) == -1) return (-1); /* msg */ ibuf_from_ibuf(&m->msg, msg); return (0); }