diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2011-09-21 10:37:52 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2011-09-21 10:37:52 +0000 |
commit | 1722612503da6b85a79ff90c9977f23a53af8543 (patch) | |
tree | a564ea3aeb5b2ddee567344f939ded63848dbd3f /usr.sbin/bgpctl/mrtparser.c | |
parent | d84f7ec0e4299fd9228d25660d9c9c0339a9033c (diff) |
Make it possible to parse MRT table dumps (all 3 formats) and display
them like the show rib / show rib detail output. It is also possible
to filter the output. e.g.
bgpctl show mrt file ./bview.20110914.1600 as 22512 204.209.0.0/16 all
OK sthen@, put it in henning@
Diffstat (limited to 'usr.sbin/bgpctl/mrtparser.c')
-rw-r--r-- | usr.sbin/bgpctl/mrtparser.c | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/usr.sbin/bgpctl/mrtparser.c b/usr.sbin/bgpctl/mrtparser.c new file mode 100644 index 00000000000..aba4ba94fe8 --- /dev/null +++ b/usr.sbin/bgpctl/mrtparser.c @@ -0,0 +1,971 @@ +/* $OpenBSD: mrtparser.c,v 1.1 2011/09/21 10:37:51 claudio Exp $ */ +/* + * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org> + * + * 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 <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "mrt.h" +#include "mrtparser.h" + +void *mrt_read_msg(int, struct mrt_hdr *); +size_t mrt_read_buf(int, void *, size_t); + +struct mrt_peer *mrt_parse_v2_peer(struct mrt_hdr *, void *); +struct mrt_rib *mrt_parse_v2_rib(struct mrt_hdr *, void *); +int mrt_parse_dump(struct mrt_hdr *, void *, struct mrt_peer **, + struct mrt_rib **); +int mrt_parse_dump_mp(struct mrt_hdr *, void *, struct mrt_peer **, + struct mrt_rib **); +int mrt_extract_attr(struct mrt_rib_entry *, u_char *, int, sa_family_t, + int); + +void mrt_free_peers(struct mrt_peer *); +void mrt_free_rib(struct mrt_rib *); +void mrt_free_bgp_state(struct mrt_bgp_state *); +void mrt_free_bgp_msg(struct mrt_bgp_msg *); + +u_char *mrt_aspath_inflate(void *, u_int16_t, u_int16_t *); + +void * +mrt_read_msg(int fd, struct mrt_hdr *hdr) +{ + void *buf; + + bzero(hdr, sizeof(*hdr)); + if (mrt_read_buf(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) + return (NULL); + + if ((buf = malloc(ntohl(hdr->length))) == NULL) + err(1, "malloc(%d)", hdr->length); + + if (mrt_read_buf(fd, buf, ntohl(hdr->length)) != ntohl(hdr->length)) { + free(buf); + return (NULL); + } + return (buf); +} + +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); +} + +void +mrt_parse(int fd, struct mrt_parser *p, int verbose) +{ + struct mrt_hdr h; + struct mrt_peer *pctx = NULL; + struct mrt_rib *r; + void *msg; + + while ((msg = mrt_read_msg(fd, &h))) { + 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("unsuported 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 (mrt_parse_dump(&h, msg, &pctx, &r) == 0) { + 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 (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: + r = mrt_parse_v2_rib(&h, msg); + if (r) { + 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; + case MSG_PROTOCOL_BGP4MP_ET: + /* currently just ignore the microsec field */ + msg = (char *)msg + sizeof(u_int32_t); + h.length -= sizeof(u_int32_t); + /* FALLTHROUGH */ + case MSG_PROTOCOL_BGP4MP: + switch (ntohs(h.subtype)) { + case BGP4MP_STATE_CHANGE: + case BGP4MP_STATE_CHANGE_AS4: + /* XXX p->state(s, p->arg); */ + errx(1, "BGP4MP subtype not yet implemented"); + break; + case BGP4MP_MESSAGE: + case BGP4MP_MESSAGE_AS4: + case BGP4MP_MESSAGE_LOCAL: + case BGP4MP_MESSAGE_AS4_LOCAL: + /* XXX p->message(m, p->arg); */ + errx(1, "BGP4MP subtype not yet implemented"); + break; + case BGP4MP_ENTRY: + if (mrt_parse_dump_mp(&h, msg, &pctx, &r) == + 0) { + 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; + } + free(msg); + } + if (pctx) + mrt_free_peers(pctx); +} + +struct mrt_peer * +mrt_parse_v2_peer(struct mrt_hdr *hdr, void *msg) +{ + struct mrt_peer_entry *peers; + struct mrt_peer *p; + u_int8_t *b = msg; + u_int32_t bid, as4; + u_int16_t cnt, i, as2; + u_int len = ntohl(hdr->length); + + if (len < 8) /* min msg size */ + return NULL; + + p = calloc(1, sizeof(struct mrt_peer)); + if (p == NULL) + err(1, "calloc"); + + /* collector bgp id */ + memcpy(&bid, b, sizeof(bid)); + b += sizeof(bid); + len -= sizeof(bid); + p->bgp_id = ntohl(bid); + + /* view name length */ + memcpy(&cnt, b, sizeof(cnt)); + b += sizeof(cnt); + len -= sizeof(cnt); + cnt = ntohs(cnt); + + /* view name */ + if (cnt > len) + goto fail; + if (cnt != 0) { + if ((p->view = malloc(cnt + 1)) == NULL) + err(1, "malloc"); + memcpy(p->view, b, cnt); + p->view[cnt] = 0; + } else + if ((p->view = strdup("")) == NULL) + err(1, "strdup"); + b += cnt; + len -= cnt; + + /* peer_count */ + if (len < sizeof(cnt)) + goto fail; + memcpy(&cnt, b, sizeof(cnt)); + b += sizeof(cnt); + len -= sizeof(cnt); + cnt = ntohs(cnt); + + /* peer entries */ + if ((peers = calloc(cnt, sizeof(struct mrt_peer_entry))) == NULL) + err(1, "calloc"); + for (i = 0; i < cnt; i++) { + u_int8_t type; + + if (len < sizeof(u_int8_t) + sizeof(u_int32_t)) + goto fail; + type = *b++; + len -= 1; + memcpy(&bid, b, sizeof(bid)); + b += sizeof(bid); + len -= sizeof(bid); + peers[i].bgp_id = ntohl(bid); + + if (type & MRT_DUMP_V2_PEER_BIT_I) { + if (len < sizeof(struct in6_addr)) + goto fail; + peers[i].addr.sin6.sin6_len = + sizeof(struct sockaddr_in6); + peers[i].addr.sin6.sin6_family = AF_INET6; + memcpy(&peers[i].addr.sin6.sin6_addr, b, + sizeof(struct in6_addr)); + b += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + } else { + if (len < sizeof(struct in_addr)) + goto fail; + peers[i].addr.sin.sin_len = sizeof(struct sockaddr_in); + peers[i].addr.sin.sin_family = AF_INET; + memcpy(&peers[i].addr.sin.sin_addr, b, + sizeof(struct in_addr)); + b += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + } + + if (type & MRT_DUMP_V2_PEER_BIT_A) { + memcpy(&as4, b, sizeof(as4)); + b += sizeof(as4); + len -= sizeof(as4); + as4 = ntohl(as4); + } else { + memcpy(&as2, b, sizeof(as2)); + b += sizeof(as2); + len -= sizeof(as2); + as4 = ntohs(as2); + } + peers[i].asnum = as4; + } + p->peers = peers; + p->npeers = cnt; + return (p); +fail: + mrt_free_peers(p); + return (NULL); +} + +struct mrt_rib * +mrt_parse_v2_rib(struct mrt_hdr *hdr, void *msg) +{ + struct mrt_rib_entry *entries; + struct mrt_rib *r; + u_int8_t *b = msg; + u_int len = ntohl(hdr->length); + u_int32_t snum; + u_int16_t cnt, i; + u_int8_t plen; + + if (len < sizeof(snum) + 1) + return NULL; + + r = calloc(1, sizeof(struct mrt_rib)); + if (r == NULL) + err(1, "calloc"); + + /* seq_num */ + memcpy(&snum, b, sizeof(snum)); + b += sizeof(snum); + len -= sizeof(snum); + r->seqnum = ntohl(snum); + + switch (ntohs(hdr->subtype)) { + case MRT_DUMP_V2_RIB_IPV4_UNICAST: + case MRT_DUMP_V2_RIB_IPV4_MULTICAST: + plen = *b++; + len -= 1; + if (len < MRT_PREFIX_LEN(plen)) + goto fail; + r->prefix.sin.sin_family = AF_INET; + r->prefix.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&r->prefix.sin.sin_addr, b, MRT_PREFIX_LEN(plen)); + b += MRT_PREFIX_LEN(plen); + len -= MRT_PREFIX_LEN(plen); + r->prefixlen = plen; + break; + case MRT_DUMP_V2_RIB_IPV6_UNICAST: + case MRT_DUMP_V2_RIB_IPV6_MULTICAST: + plen = *b++; + len -= 1; + if (len < MRT_PREFIX_LEN(plen)) + goto fail; + r->prefix.sin6.sin6_family = AF_INET6; + r->prefix.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&r->prefix.sin6.sin6_addr, b, MRT_PREFIX_LEN(plen)); + b += MRT_PREFIX_LEN(plen); + len -= MRT_PREFIX_LEN(plen); + r->prefixlen = plen; + break; + case MRT_DUMP_V2_RIB_GENERIC: + /* XXX unhandled */ + errx(1, "MRT_DUMP_V2_RIB_GENERIC subtype not yet implemented"); + goto fail; + } + + /* entries count */ + if (len < sizeof(cnt)) + goto fail; + memcpy(&cnt, b, sizeof(cnt)); + b += sizeof(cnt); + len -= sizeof(cnt); + cnt = ntohs(cnt); + r->nentries = cnt; + + /* entries */ + if ((entries = calloc(cnt, sizeof(struct mrt_rib_entry))) == NULL) + err(1, "calloc"); + for (i = 0; i < cnt; i++) { + u_int32_t otm; + u_int16_t pix, alen; + if (len < 2 * sizeof(u_int16_t) + sizeof(u_int32_t)) + goto fail; + /* peer index */ + memcpy(&pix, b, sizeof(pix)); + b += sizeof(pix); + len -= sizeof(pix); + entries[i].peer_idx = ntohs(pix); + + /* originated */ + memcpy(&otm, b, sizeof(otm)); + b += sizeof(otm); + len -= sizeof(otm); + entries[i].originated = ntohl(otm); + + /* attr_len */ + memcpy(&alen, b, sizeof(alen)); + b += sizeof(alen); + len -= sizeof(alen); + alen = ntohs(alen); + + /* attr */ + if (len < alen) + goto fail; + if (mrt_extract_attr(&entries[i], b, alen, + r->prefix.sa.sa_family, 1) == -1) + goto fail; + b += alen; + len -= alen; + } + r->entries = entries; + return (r); +fail: + mrt_free_rib(r); + return (NULL); +} + +int +mrt_parse_dump(struct mrt_hdr *hdr, void *msg, struct mrt_peer **pp, + struct mrt_rib **rp) +{ + struct mrt_peer *p; + struct mrt_rib *r; + struct mrt_rib_entry *re; + u_int8_t *b = msg; + u_int len = ntohl(hdr->length); + u_int16_t asnum, 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 (len < 2 * sizeof(u_int16_t)) + goto fail; + /* view */ + b += sizeof(u_int16_t); + len -= sizeof(u_int16_t); + /* seqnum */ + memcpy(&r->seqnum, b, sizeof(u_int16_t)); + b += sizeof(u_int16_t); + len -= sizeof(u_int16_t); + r->seqnum = ntohs(r->seqnum); + + switch (ntohs(hdr->subtype)) { + case MRT_DUMP_AFI_IP: + if (len < sizeof(struct in_addr)) + goto fail; + r->prefix.sin.sin_family = AF_INET; + r->prefix.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&r->prefix.sin.sin_addr, b, sizeof(struct in_addr)); + b += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + break; + case MRT_DUMP_AFI_IPv6: + if (len < sizeof(struct in6_addr)) + goto fail; + r->prefix.sin6.sin6_family = AF_INET6; + r->prefix.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&r->prefix.sin6.sin6_addr, b, sizeof(struct in6_addr)); + b += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + break; + } + if (len < 2 * sizeof(u_int32_t) + 2 * sizeof(u_int16_t) + 2) + goto fail; + r->prefixlen = *b++; + len -= 1; + /* status */ + b += 1; + len -= 1; + /* originated */ + memcpy(&re->originated, b, sizeof(u_int32_t)); + b += sizeof(u_int32_t); + len -= sizeof(u_int32_t); + re->originated = ntohl(re->originated); + /* peer ip */ + switch (ntohs(hdr->subtype)) { + case MRT_DUMP_AFI_IP: + if (len < sizeof(struct in_addr)) + goto fail; + p->peers->addr.sin.sin_family = AF_INET; + p->peers->addr.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&p->peers->addr.sin.sin_addr, b, sizeof(struct in_addr)); + b += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + break; + case MRT_DUMP_AFI_IPv6: + if (len < sizeof(struct in6_addr)) + goto fail; + p->peers->addr.sin6.sin6_family = AF_INET6; + p->peers->addr.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&p->peers->addr.sin6.sin6_addr, b, + sizeof(struct in6_addr)); + b += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + break; + } + memcpy(&asnum, b, sizeof(asnum)); + b += sizeof(asnum); + len -= sizeof(asnum); + p->peers->asnum = ntohs(asnum); + + memcpy(&alen, b, sizeof(alen)); + b += sizeof(alen); + len -= sizeof(alen); + alen = ntohs(alen); + + /* attr */ + if (len < alen) + goto fail; + if (mrt_extract_attr(re, b, alen, r->prefix.sa.sa_family, 0) == -1) + goto fail; + b += alen; + len -= alen; + + return (0); +fail: + mrt_free_rib(r); + return (-1); +} + +int +mrt_parse_dump_mp(struct mrt_hdr *hdr, void *msg, struct mrt_peer **pp, + struct mrt_rib **rp) +{ + struct mrt_peer *p; + struct mrt_rib *r; + struct mrt_rib_entry *re; + u_int8_t *b = msg; + u_int len = ntohl(hdr->length); + u_int16_t asnum, alen, afi; + u_int8_t safi, nhlen; + sa_family_t af; + + 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 (len < 4 * sizeof(u_int16_t)) + goto fail; + /* source AS */ + b += sizeof(u_int16_t); + len -= sizeof(u_int16_t); + /* dest AS */ + memcpy(&asnum, b, sizeof(asnum)); + b += sizeof(asnum); + len -= sizeof(asnum); + p->peers->asnum = ntohs(asnum); + /* iface index */ + b += sizeof(u_int16_t); + len -= sizeof(u_int16_t); + /* afi */ + memcpy(&afi, b, sizeof(afi)); + b += sizeof(afi); + len -= sizeof(afi); + afi = ntohs(afi); + + /* source + dest ip */ + switch (afi) { + case MRT_DUMP_AFI_IP: + if (len < 2 * sizeof(struct in_addr)) + goto fail; + /* source IP */ + b += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + /* dest IP */ + p->peers->addr.sin.sin_family = AF_INET; + p->peers->addr.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&p->peers->addr.sin.sin_addr, b, sizeof(struct in_addr)); + b += sizeof(struct in_addr); + len -= sizeof(struct in_addr); + break; + case MRT_DUMP_AFI_IPv6: + if (len < 2 * sizeof(struct in6_addr)) + goto fail; + /* source IP */ + b += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + /* dest IP */ + p->peers->addr.sin6.sin6_family = AF_INET6; + p->peers->addr.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&p->peers->addr.sin6.sin6_addr, b, + sizeof(struct in6_addr)); + b += sizeof(struct in6_addr); + len -= sizeof(struct in6_addr); + break; + } + + if (len < 2 * sizeof(u_int16_t) + 2 * sizeof(u_int32_t)) + goto fail; + /* view + status */ + b += 2 * sizeof(u_int16_t); + len -= 2 * sizeof(u_int16_t); + /* originated */ + memcpy(&re->originated, b, sizeof(u_int32_t)); + b += sizeof(u_int32_t); + len -= sizeof(u_int32_t); + re->originated = ntohl(re->originated); + + /* afi */ + memcpy(&afi, b, sizeof(afi)); + b += sizeof(afi); + len -= sizeof(afi); + afi = ntohs(afi); + + /* safi */ + safi = *b++; + len -= 1; + + switch (afi) { + case MRT_DUMP_AFI_IP: + if (safi == 1 || safi == 2) { + af = AF_INET; + break; + } else if (safi == 128) { + af = AF_VPNv4; + break; + } + goto fail; + case MRT_DUMP_AFI_IPv6: + if (safi != 1 && safi != 2) + goto fail; + af = AF_INET6; + break; + default: + goto fail; + } + + /* nhlen */ + nhlen = *b++; + len -= 1; + + /* nexthop */ + switch (af) { + case AF_INET: + if (len < sizeof(struct in_addr)) + goto fail; + re->nexthop.sin.sin_family = AF_INET; + re->nexthop.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&re->nexthop.sin.sin_addr, b, sizeof(struct in_addr)); + break; + case AF_INET6: + if (len < sizeof(struct in6_addr)) + goto fail; + re->nexthop.sin6.sin6_family = AF_INET6; + re->nexthop.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&re->nexthop.sin6.sin6_addr, b, sizeof(struct in6_addr)); + break; + case AF_VPNv4: + if (len < sizeof(u_int64_t) + sizeof(struct in_addr)) + goto fail; + re->nexthop.svpn4.sv_len = sizeof(struct sockaddr_vpn4); + re->nexthop.svpn4.sv_family = AF_VPNv4; + memcpy(&re->nexthop.svpn4.sv_addr, b + sizeof(u_int64_t), + sizeof(struct in_addr)); + break; + } + if (len < nhlen) + goto fail; + b += nhlen; + len -= nhlen; + + if (len < 1) + goto fail; + r->prefixlen = *b++; + len -= 1; + + /* prefix */ + switch (af) { + case AF_INET: + if (len < MRT_PREFIX_LEN(r->prefixlen)) + goto fail; + r->prefix.sin.sin_family = AF_INET; + r->prefix.sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&r->prefix.sin.sin_addr, b, + MRT_PREFIX_LEN(r->prefixlen)); + b += MRT_PREFIX_LEN(r->prefixlen); + len -= MRT_PREFIX_LEN(r->prefixlen); + break; + case AF_INET6: + if (len < MRT_PREFIX_LEN(r->prefixlen)) + goto fail; + r->prefix.sin6.sin6_family = AF_INET6; + r->prefix.sin6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&r->prefix.sin6.sin6_addr, b, + MRT_PREFIX_LEN(r->prefixlen)); + b += MRT_PREFIX_LEN(r->prefixlen); + len -= MRT_PREFIX_LEN(r->prefixlen); + break; + case AF_VPNv4: + if (len < MRT_PREFIX_LEN(r->prefixlen)) + goto fail; + errx(1, "AF_VPNv4 handling not yet implemented"); + goto fail; + } + + memcpy(&alen, b, sizeof(alen)); + b += sizeof(alen); + len -= sizeof(alen); + alen = ntohs(alen); + + /* attr */ + if (len < alen) + goto fail; + if (mrt_extract_attr(re, b, alen, r->prefix.sa.sa_family, 0) == -1) + goto fail; + b += alen; + len -= alen; + + return (0); +fail: + mrt_free_rib(r); + return (-1); +} + +int +mrt_extract_attr(struct mrt_rib_entry *re, u_char *a, int alen, sa_family_t af, + int as4) +{ + struct mrt_attr *ap; + u_int32_t tmp; + u_int16_t attr_len; + u_int8_t type, flags, *attr; + + do { + if (alen < 3) + return (-1); + attr = a; + flags = *a++; + alen -= 1; + type = *a++; + alen -= 1; + + if (flags & MRT_ATTR_EXTLEN) { + if (alen < 2) + return (-1); + memcpy(&attr_len, a, sizeof(attr_len)); + attr_len = ntohs(attr_len); + a += sizeof(attr_len); + alen -= sizeof(attr_len); + } else { + attr_len = *a++; + alen -= 1; + } + switch (type) { + case MRT_ATTR_ORIGIN: + if (attr_len != 1) + return (-1); + re->origin = *a; + break; + case MRT_ATTR_ASPATH: + if (as4) { + re->aspath_len = attr_len; + if ((re->aspath = malloc(attr_len)) == NULL) + err(1, "malloc"); + memcpy(re->aspath, a, attr_len); + } else { + re->aspath = mrt_aspath_inflate(a, attr_len, + &re->aspath_len); + if (re->aspath == NULL) + return (-1); + } + break; + case MRT_ATTR_NEXTHOP: + if (attr_len != 4) + return (-1); + if (af != AF_INET) + break; + memcpy(&tmp, a, sizeof(tmp)); + re->nexthop.sin.sin_len = sizeof(struct sockaddr_in); + re->nexthop.sin.sin_family = AF_INET; + re->nexthop.sin.sin_addr.s_addr = tmp; + break; + case MRT_ATTR_MED: + if (attr_len != 4) + return (-1); + memcpy(&tmp, a, sizeof(tmp)); + re->med = ntohl(tmp); + break; + case MRT_ATTR_LOCALPREF: + if (attr_len != 4) + return (-1); + memcpy(&tmp, a, sizeof(tmp)); + re->local_pref = ntohl(tmp); + break; + case MRT_ATTR_MP_REACH_NLRI: + /* + * XXX horrible hack: + * Once again IETF and the real world differ in the + * implementation. So the abbreviated MP_NLRI hack + * in the standard is not used in real life. + * Detect the two cases by looking at the first byte + * and if it is 0 it is assumed to be a AFI/SAFI + * encoding. + */ + if (*a == 0) { + a += 3; + alen -= 3; + attr_len -= 3; + } + switch (af) { + case AF_INET6: + if (attr_len < sizeof(struct in6_addr) + 1) + return (-1); + re->nexthop.sin6.sin6_len = + sizeof(struct sockaddr_in6); + re->nexthop.sin6.sin6_family = AF_INET6; + memcpy(&re->nexthop.sin6.sin6_addr, a + 1, + sizeof(struct in6_addr)); + break; + case AF_VPNv4: + if (attr_len < sizeof(u_int64_t) + + sizeof(struct in_addr)) + return (-1); + re->nexthop.svpn4.sv_len = + sizeof(struct sockaddr_vpn4); + re->nexthop.svpn4.sv_family = AF_VPNv4; + memcpy(&tmp, a + 1 + sizeof(u_int64_t), + sizeof(tmp)); + re->nexthop.svpn4.sv_addr.s_addr = tmp; + break; + } + break; + case MRT_ATTR_AS4PATH: + if (!as4) { + if (re->aspath) + free(re->aspath); + re->aspath_len = attr_len; + if ((re->aspath = malloc(attr_len)) == NULL) + err(1, "malloc"); + memcpy(re->aspath, a, attr_len); + break; + } + /* FALLTHROUGH */ + default: + re->nattrs++; + if (re->nattrs >= UCHAR_MAX) + err(1, "too many attributes"); + ap = realloc(re->attrs, + re->nattrs * sizeof(struct mrt_attr)); + if (ap == NULL) + err(1, "realloc"); + re->attrs = ap; + ap = re->attrs + re->nattrs - 1; + ap->attr_len = a + attr_len - attr; + if ((ap->attr = malloc(ap->attr_len)) == NULL) + err(1, "malloc"); + memcpy(ap->attr, attr, ap->attr_len); + break; + } + a += attr_len; + alen -= attr_len; + } while (alen > 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) +{ + u_int16_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); +} + +void +mrt_free_bgp_state(struct mrt_bgp_state *s) +{ + free(s); +} + +void +mrt_free_bgp_msg(struct mrt_bgp_msg *m) +{ + free(m->msg); + free(m); +} + +u_char * +mrt_aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen) +{ + u_int8_t *seg, *nseg, *ndata; + u_int16_t seg_size, olen, nlen; + u_int8_t seg_len; + + /* first calculate the length of the aspath */ + seg = data; + nlen = 0; + for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) { + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int16_t) * seg_len; + nlen += 2 + sizeof(u_int32_t) * seg_len; + + if (seg_size > olen) + return NULL; + } + + *newlen = nlen; + if ((ndata = malloc(nlen)) == NULL) + err(1, "malloc"); + + /* then copy the aspath */ + seg = data; + for (nseg = ndata; nseg < ndata + nlen; ) { + *nseg++ = *seg++; + *nseg++ = seg_len = *seg++; + for (; seg_len > 0; seg_len--) { + *nseg++ = 0; + *nseg++ = 0; + *nseg++ = *seg++; + *nseg++ = *seg++; + } + } + + return (ndata); +} |