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 | |
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')
-rw-r--r-- | usr.sbin/bgpctl/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/bgpctl/bgpctl.c | 470 | ||||
-rw-r--r-- | usr.sbin/bgpctl/mrtparser.c | 971 | ||||
-rw-r--r-- | usr.sbin/bgpctl/mrtparser.h | 115 | ||||
-rw-r--r-- | usr.sbin/bgpctl/parser.c | 76 | ||||
-rw-r--r-- | usr.sbin/bgpctl/parser.h | 4 |
6 files changed, 1467 insertions, 173 deletions
diff --git a/usr.sbin/bgpctl/Makefile b/usr.sbin/bgpctl/Makefile index 2f81118ee5e..b3591eb1afd 100644 --- a/usr.sbin/bgpctl/Makefile +++ b/usr.sbin/bgpctl/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.11 2010/05/26 16:44:32 nicm Exp $ +# $OpenBSD: Makefile,v 1.12 2011/09/21 10:37:51 claudio Exp $ .PATH: ${.CURDIR}/../bgpd PROG= bgpctl SRCS= bgpctl.c parser.c util.c timer.c SRCS+= irrfilter.c whois.c irr_asset.c irr_prefix.c irr_output.c -SRCS+= irr_parser.c +SRCS+= irr_parser.c mrtparser.c CFLAGS+= -Wall CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c index e1256a21cf1..2a44def05fc 100644 --- a/usr.sbin/bgpctl/bgpctl.c +++ b/usr.sbin/bgpctl/bgpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.c,v 1.162 2011/09/21 08:52:55 claudio Exp $ */ +/* $OpenBSD: bgpctl.c,v 1.163 2011/09/21 10:37:51 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> @@ -37,6 +37,7 @@ #include "log.h" #include "parser.h" #include "irrfilter.h" +#include "mrtparser.h" enum neighbor_views { NV_DEFAULT, @@ -74,6 +75,9 @@ const char * print_origin(u_int8_t, int); void print_flags(u_int8_t, int); int show_rib_summary_msg(struct imsg *); int show_rib_detail_msg(struct imsg *, int); +void show_rib_brief(struct ctl_show_rib *, u_char *); +void show_rib_detail(struct ctl_show_rib *, u_char *, int); +void show_attr(void *, u_int16_t); void show_community(u_char *, u_int16_t); void show_ext_community(u_char *, u_int16_t); char *fmt_mem(int64_t); @@ -81,8 +85,13 @@ int show_rib_memory_msg(struct imsg *); void send_filterset(struct imsgbuf *, struct filter_set_head *); static const char *get_errstr(u_int8_t, u_int8_t); int show_result(struct imsg *); +void show_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *); +void show_mrt_state(struct mrt_bgp_state *, void *); +void show_mrt_msg(struct mrt_bgp_msg *, void *); +void mrt_to_bgpd_addr(union mrt_addr *, struct bgpd_addr *); struct imsgbuf *ibuf; +struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg }; __dead void usage(void) @@ -228,8 +237,7 @@ main(int argc, char *argv[]) sizeof(res->community)); type = IMSG_CTL_SHOW_RIB_COMMUNITY; } - memcpy(&ribreq.neighbor, &neighbor, - sizeof(ribreq.neighbor)); + memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); ribreq.aid = res->aid; ribreq.flags = res->flags; @@ -237,6 +245,27 @@ main(int argc, char *argv[]) if (!(res->flags & F_CTL_DETAIL)) show_rib_summary_head(); break; + case SHOW_MRT: + close(fd); + bzero(&ribreq, sizeof(ribreq)); + if (res->as.type != AS_NONE) + memcpy(&ribreq.as, &res->as, sizeof(res->as)); + if (res->addr.aid) { + memcpy(&ribreq.prefix, &res->addr, sizeof(res->addr)); + ribreq.prefixlen = res->prefixlen; + } + if (res->community.as != COMMUNITY_UNSET && + res->community.type != COMMUNITY_UNSET) + memcpy(&ribreq.community, &res->community, + sizeof(res->community)); + memcpy(&ribreq.neighbor, &neighbor, sizeof(ribreq.neighbor)); + ribreq.aid = res->aid; + ribreq.flags = res->flags; + show_mrt.arg = &ribreq; + if (!(res->flags & F_CTL_DETAIL)) + show_rib_summary_head(); + mrt_parse(res->mrtfd, &show_mrt, 1); + exit(0); case SHOW_RIB_MEM: imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); break; @@ -396,6 +425,7 @@ main(int argc, char *argv[]) case IRRFILTER: case LOG_VERBOSE: case LOG_BRIEF: + case SHOW_MRT: break; } imsg_free(&imsg); @@ -1124,27 +1154,14 @@ int show_rib_summary_msg(struct imsg *imsg) { struct ctl_show_rib rib; - char *aspath; u_char *asdata; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_RIB: memcpy(&rib, imsg->data, sizeof(rib)); - - print_prefix(&rib.prefix, rib.prefixlen, rib.flags); - printf(" %-15s ", log_addr(&rib.exit_nexthop)); - - printf(" %5u %5u ", rib.local_pref, rib.med); - asdata = imsg->data; asdata += sizeof(struct ctl_show_rib); - if (aspath_asprint(&aspath, asdata, rib.aspath_len) == -1) - err(1, NULL); - if (strlen(aspath) > 0) - printf("%s ", aspath); - free(aspath); - - printf("%s\n", print_origin(rib.origin, 1)); + show_rib_brief(&rib, asdata); break; case IMSG_CTL_END: return (1); @@ -1159,108 +1176,21 @@ int show_rib_detail_msg(struct imsg *imsg, int nodescr) { struct ctl_show_rib rib; - struct in_addr id; - char *aspath, *s; - u_char *data; - u_int32_t as; - u_int16_t ilen, alen, ioff; - u_int8_t flags, type; - time_t now; + u_char *asdata; + u_int16_t ilen; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_RIB: memcpy(&rib, imsg->data, sizeof(rib)); - - printf("\nBGP routing table entry for %s/%u\n", - log_addr(&rib.prefix), rib.prefixlen); - - data = imsg->data; - data += sizeof(struct ctl_show_rib); - if (aspath_asprint(&aspath, data, rib.aspath_len) == -1) - err(1, NULL); - if (strlen(aspath) > 0) - printf(" %s\n", aspath); - free(aspath); - - s = fmt_peer(rib.descr, &rib.remote_addr, -1, nodescr); - printf(" Nexthop %s ", log_addr(&rib.exit_nexthop)); - printf("(via %s) from %s (", log_addr(&rib.true_nexthop), s); - free(s); - id.s_addr = htonl(rib.remote_id); - printf("%s)\n", inet_ntoa(id)); - - printf(" Origin %s, metric %u, localpref %u, ", - print_origin(rib.origin, 0), rib.med, rib.local_pref); - print_flags(rib.flags, 0); - - now = time(NULL); - if (now > rib.lastchange) - now -= rib.lastchange; - else - now = 0; - - printf("\n Last update: %s ago\n", - fmt_timeframe_core(now)); + asdata = imsg->data; + asdata += sizeof(struct ctl_show_rib); + show_rib_detail(&rib, asdata, nodescr); break; case IMSG_CTL_SHOW_RIB_ATTR: ilen = imsg->hdr.len - IMSG_HEADER_SIZE; if (ilen < 3) errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received"); - data = imsg->data; - flags = data[0]; - type = data[1]; - - /* get the attribute length */ - if (flags & ATTR_EXTLEN) { - if (ilen < 4) - errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received"); - memcpy(&alen, data+2, sizeof(u_int16_t)); - alen = ntohs(alen); - data += 4; - ilen -= 4; - } else { - alen = data[2]; - data += 3; - ilen -= 3; - } - /* bad imsg len how can that happen!? */ - if (alen != ilen) - errx(1, "bad IMSG_CTL_SHOW_RIB_ATTR received"); - - switch (type) { - case ATTR_COMMUNITIES: - printf(" Communities: "); - show_community(data, alen); - printf("\n"); - break; - case ATTR_AGGREGATOR: - memcpy(&as, data, sizeof(as)); - memcpy(&id, data + sizeof(as), sizeof(id)); - printf(" Aggregator: %s [%s]\n", - log_as(ntohl(as)), inet_ntoa(id)); - break; - case ATTR_ORIGINATOR_ID: - memcpy(&id, data, sizeof(id)); - printf(" Originator Id: %s\n", inet_ntoa(id)); - break; - case ATTR_CLUSTER_LIST: - printf(" Cluster ID List:"); - for (ioff = 0; ioff + sizeof(id) <= ilen; - ioff += sizeof(id)) { - memcpy(&id, data + ioff, sizeof(id)); - printf(" %s", inet_ntoa(id)); - } - printf("\n"); - break; - case ATTR_EXT_COMMUNITIES: - printf(" Ext. communities: "); - show_ext_community(data, alen); - printf("\n"); - break; - default: - /* ignore unknown attributes */ - break; - } + show_attr(imsg->data, ilen); break; case IMSG_CTL_END: printf("\n"); @@ -1272,70 +1202,128 @@ show_rib_detail_msg(struct imsg *imsg, int nodescr) return (0); } -char * -fmt_mem(int64_t num) +void +show_rib_brief(struct ctl_show_rib *r, u_char *asdata) { - static char buf[16]; + char *aspath; - if (fmt_scaled(num, buf) == -1) - snprintf(buf, sizeof(buf), "%lldB", (long long)num); + print_prefix(&r->prefix, r->prefixlen, r->flags); + printf(" %-15s ", log_addr(&r->exit_nexthop)); + printf(" %5u %5u ", r->local_pref, r->med); - return (buf); + if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1) + err(1, NULL); + if (strlen(aspath) > 0) + printf("%s ", aspath); + free(aspath); + + printf("%s\n", print_origin(r->origin, 1)); } -size_t pt_sizes[AID_MAX] = AID_PTSIZE; +void +show_rib_detail(struct ctl_show_rib *r, u_char *asdata, int nodescr) +{ + struct in_addr id; + char *aspath, *s; + time_t now; -int -show_rib_memory_msg(struct imsg *imsg) + printf("\nBGP routing table entry for %s/%u\n", + log_addr(&r->prefix), r->prefixlen); + + if (aspath_asprint(&aspath, asdata, r->aspath_len) == -1) + err(1, NULL); + if (strlen(aspath) > 0) + printf(" %s\n", aspath); + free(aspath); + + s = fmt_peer(r->descr, &r->remote_addr, -1, nodescr); + printf(" Nexthop %s ", log_addr(&r->exit_nexthop)); + printf("(via %s) from %s (", log_addr(&r->true_nexthop), s); + free(s); + id.s_addr = htonl(r->remote_id); + printf("%s)\n", inet_ntoa(id)); + + printf(" Origin %s, metric %u, localpref %u, ", + print_origin(r->origin, 0), r->med, r->local_pref); + print_flags(r->flags, 0); + + now = time(NULL); + if (now > r->lastchange) + now -= r->lastchange; + else + now = 0; + + printf("\n Last update: %s ago\n", fmt_timeframe_core(now)); +} + +void +show_attr(void *b, u_int16_t len) { - struct rde_memstats stats; - size_t pts = 0; - int i; + char *data = b; + struct in_addr id; + u_int32_t as; + u_int16_t alen, ioff; + u_int8_t flags, type; + + data = b; + if (len < 3) + errx(1, "show_attr: too short bgp attr"); + + flags = data[0]; + type = data[1]; + + /* get the attribute length */ + if (flags & ATTR_EXTLEN) { + if (len < 4) + errx(1, "show_attr: too short bgp attr"); + memcpy(&alen, data+2, sizeof(u_int16_t)); + alen = ntohs(alen); + data += 4; + len -= 4; + } else { + alen = data[2]; + data += 3; + len -= 3; + } - switch (imsg->hdr.type) { - case IMSG_CTL_SHOW_RIB_MEM: - memcpy(&stats, imsg->data, sizeof(stats)); - printf("RDE memory statistics\n"); - for (i = 0; i < AID_MAX; i++) { - if (stats.pt_cnt[i] == 0) - continue; - pts += stats.pt_cnt[i] * pt_sizes[i]; - printf("%10lld %s network entries using %s of memory\n", - (long long)stats.pt_cnt[i], aid_vals[i].name, - fmt_mem(stats.pt_cnt[i] * pt_sizes[i])); + /* bad imsg len how can that happen!? */ + if (alen > len) + errx(1, "show_attr: bad length"); + + switch (type) { + case ATTR_COMMUNITIES: + printf(" Communities: "); + show_community(data, alen); + printf("\n"); + break; + case ATTR_AGGREGATOR: + memcpy(&as, data, sizeof(as)); + memcpy(&id, data + sizeof(as), sizeof(id)); + printf(" Aggregator: %s [%s]\n", + log_as(ntohl(as)), inet_ntoa(id)); + break; + case ATTR_ORIGINATOR_ID: + memcpy(&id, data, sizeof(id)); + printf(" Originator Id: %s\n", inet_ntoa(id)); + break; + case ATTR_CLUSTER_LIST: + printf(" Cluster ID List:"); + for (ioff = 0; ioff + sizeof(id) <= alen; + ioff += sizeof(id)) { + memcpy(&id, data + ioff, sizeof(id)); + printf(" %s", inet_ntoa(id)); } - printf("%10lld rib entries using %s of memory\n", - (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt * - sizeof(struct rib_entry))); - printf("%10lld prefix entries using %s of memory\n", - (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt * - sizeof(struct prefix))); - printf("%10lld BGP path attribute entries using %s of memory\n", - (long long)stats.path_cnt, fmt_mem(stats.path_cnt * - sizeof(struct rde_aspath))); - printf("%10lld BGP AS-PATH attribute entries using " - "%s of memory,\n\t and holding %lld references\n", - (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size), - (long long)stats.aspath_refs); - printf("%10lld BGP attributes entries using %s of memory\n", - (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt * - sizeof(struct attr))); - printf("\t and holding %lld references\n", - (long long)stats.attr_refs); - printf("%10lld BGP attributes using %s of memory\n", - (long long)stats.attr_dcnt, fmt_mem(stats.attr_data)); - printf("RIB using %s of memory\n", fmt_mem(pts + - stats.prefix_cnt * sizeof(struct prefix) + - stats.rib_cnt * sizeof(struct rib_entry) + - stats.path_cnt * sizeof(struct rde_aspath) + - stats.aspath_size + stats.attr_cnt * sizeof(struct attr) + - stats.attr_data)); + printf("\n"); + break; + case ATTR_EXT_COMMUNITIES: + printf(" Ext. communities: "); + show_ext_community(data, alen); + printf("\n"); break; default: + /* ignore unknown attributes */ break; } - - return (1); } void @@ -1427,6 +1415,72 @@ show_ext_community(u_char *data, u_int16_t len) } } +char * +fmt_mem(int64_t num) +{ + static char buf[16]; + + if (fmt_scaled(num, buf) == -1) + snprintf(buf, sizeof(buf), "%lldB", (long long)num); + + return (buf); +} + +size_t pt_sizes[AID_MAX] = AID_PTSIZE; + +int +show_rib_memory_msg(struct imsg *imsg) +{ + struct rde_memstats stats; + size_t pts = 0; + int i; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_RIB_MEM: + memcpy(&stats, imsg->data, sizeof(stats)); + printf("RDE memory statistics\n"); + for (i = 0; i < AID_MAX; i++) { + if (stats.pt_cnt[i] == 0) + continue; + pts += stats.pt_cnt[i] * pt_sizes[i]; + printf("%10lld %s network entries using %s of memory\n", + (long long)stats.pt_cnt[i], aid_vals[i].name, + fmt_mem(stats.pt_cnt[i] * pt_sizes[i])); + } + printf("%10lld rib entries using %s of memory\n", + (long long)stats.rib_cnt, fmt_mem(stats.rib_cnt * + sizeof(struct rib_entry))); + printf("%10lld prefix entries using %s of memory\n", + (long long)stats.prefix_cnt, fmt_mem(stats.prefix_cnt * + sizeof(struct prefix))); + printf("%10lld BGP path attribute entries using %s of memory\n", + (long long)stats.path_cnt, fmt_mem(stats.path_cnt * + sizeof(struct rde_aspath))); + printf("%10lld BGP AS-PATH attribute entries using " + "%s of memory,\n\t and holding %lld references\n", + (long long)stats.aspath_cnt, fmt_mem(stats.aspath_size), + (long long)stats.aspath_refs); + printf("%10lld BGP attributes entries using %s of memory\n", + (long long)stats.attr_cnt, fmt_mem(stats.attr_cnt * + sizeof(struct attr))); + printf("\t and holding %lld references\n", + (long long)stats.attr_refs); + printf("%10lld BGP attributes using %s of memory\n", + (long long)stats.attr_dcnt, fmt_mem(stats.attr_data)); + printf("RIB using %s of memory\n", fmt_mem(pts + + stats.prefix_cnt * sizeof(struct prefix) + + stats.rib_cnt * sizeof(struct rib_entry) + + stats.path_cnt * sizeof(struct rde_aspath) + + stats.aspath_size + stats.attr_cnt * sizeof(struct attr) + + stats.attr_data)); + break; + default: + break; + } + + return (1); +} + void send_filterset(struct imsgbuf *i, struct filter_set_head *set) { @@ -1496,6 +1550,100 @@ show_result(struct imsg *imsg) return (1); } +void +show_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) +{ + struct ctl_show_rib ctl; + struct ctl_show_rib_request *req = arg; + struct mrt_rib_entry *mre; + u_int16_t i, j; + + for (i = 0; i < mr->nentries; i++) { + mre = &mr->entries[i]; + bzero(&ctl, sizeof(ctl)); + mrt_to_bgpd_addr(&mr->prefix, &ctl.prefix); + ctl.prefixlen = mr->prefixlen; + ctl.lastchange = mre->originated; + mrt_to_bgpd_addr(&mre->nexthop, &ctl.true_nexthop); + mrt_to_bgpd_addr(&mre->nexthop, &ctl.exit_nexthop); + ctl.origin = mre->origin; + ctl.local_pref = mre->local_pref; + ctl.med = mre->med; + ctl.aspath_len = mre->aspath_len; + + if (mre->peer_idx < mp->npeers) { + mrt_to_bgpd_addr(&mp->peers[mre->peer_idx].addr, + &ctl.remote_addr); + ctl.remote_id = mp->peers[mre->peer_idx].bgp_id; + } + + /* filter by neighbor */ + if (req->neighbor.addr.aid != AID_UNSPEC && + memcmp(&req->neighbor.addr, &ctl.remote_addr, + sizeof(ctl.remote_addr)) != 0) + continue; + /* filter by AF */ + if (req->aid && req->aid != ctl.prefix.aid) + return; + /* filter by prefix */ + if (req->prefix.aid != AID_UNSPEC) { + if (!prefix_compare(&req->prefix, &ctl.prefix, + req->prefixlen)) { + if (req->flags & F_LONGER) { + if (req->prefixlen > ctl.prefixlen) + return; + } else if (req->prefixlen != ctl.prefixlen) + return; + } else + return; + } + /* filter by AS */ + if (req->as.type != AS_NONE && + !aspath_match(mre->aspath, mre->aspath_len, + req->as.type, req->as.as)) + return; + + if (req->flags & F_CTL_DETAIL) { + show_rib_detail(&ctl, mre->aspath, 1); + for (j = 0; j < mre->nattrs; j++) + show_attr(mre->attrs[j].attr, + mre->attrs[j].attr_len); + } else + show_rib_brief(&ctl, mre->aspath); + } +} + +void +show_mrt_state(struct mrt_bgp_state *ms, void *arg) +{ + printf("show_mrt_state\n"); +} + +void +show_mrt_msg(struct mrt_bgp_msg *mm, void *arg) +{ + printf("show_mrt_msg\n"); +} + +void +mrt_to_bgpd_addr(union mrt_addr *ma, struct bgpd_addr *ba) +{ + switch (ma->sa.sa_family) { + case AF_INET: + case AF_INET6: + sa2addr(&ma->sa, ba); + break; + case AF_VPNv4: + bzero(ba, sizeof(*ba)); + ba->aid = AID_VPN_IPv4; + ba->vpn4.rd = ma->svpn4.sv_rd; + ba->vpn4.addr.s_addr = ma->svpn4.sv_addr.s_addr; + memcpy(ba->vpn4.labelstack, ma->svpn4.sv_label, + sizeof(ba->vpn4.labelstack)); + break; + } +} + /* following functions are necessary for imsg framework */ void log_warnx(const char *emsg, ...) 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); +} diff --git a/usr.sbin/bgpctl/mrtparser.h b/usr.sbin/bgpctl/mrtparser.h new file mode 100644 index 00000000000..39144f8c893 --- /dev/null +++ b/usr.sbin/bgpctl/mrtparser.h @@ -0,0 +1,115 @@ +/* $OpenBSD: mrtparser.h,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. + */ + +struct sockaddr_vpn4 { + u_int8_t sv_len; + sa_family_t sv_family; + u_int8_t sv_labellen; + u_int8_t sv_pad; + struct in_addr sv_addr; + u_int64_t sv_rd; + u_int8_t sv_label[21]; + u_int8_t sv_pad2[3]; +}; + +#define AF_VPNv4 250 /* XXX high enough to not cause issues */ + +union mrt_addr { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct sockaddr_vpn4 svpn4; + struct sockaddr sa; +}; + +/* data structures for the MSG_TABLE_DUMP_V2 format */ +struct mrt_peer_entry { + union mrt_addr addr; + u_int32_t bgp_id; + u_int32_t asnum; +}; + +struct mrt_peer { + char *view; + struct mrt_peer_entry *peers; + u_int32_t bgp_id; + u_int16_t npeers; +}; + +struct mrt_attr { + void *attr; + size_t attr_len; +}; + +struct mrt_rib_entry { + void *aspath; + struct mrt_attr *attrs; + union mrt_addr nexthop; + time_t originated; + u_int32_t local_pref; + u_int32_t med; + u_int16_t peer_idx; + u_int16_t aspath_len; + u_int16_t nattrs; + u_int8_t origin; +}; + +struct mrt_rib { + struct mrt_rib_entry *entries; + union mrt_addr prefix; + u_int32_t seqnum; + u_int16_t nentries; + u_int8_t prefixlen; +}; + +/* data structures for the BGP4MP MESSAGE and STATE types */ +struct mrt_bgp_state { + union mrt_addr src; + union mrt_addr dst; + u_int32_t src_as; + u_int32_t dst_as; + u_int16_t old_state; + u_int16_t new_state; +}; + +struct mrt_bgp_msg { + union mrt_addr src; + union mrt_addr dst; + u_int32_t src_as; + u_int32_t dst_as; + u_int16_t msg_len; + void *msg; +}; + +#define MRT_ATTR_ORIGIN 1 +#define MRT_ATTR_ASPATH 2 +#define MRT_ATTR_NEXTHOP 3 +#define MRT_ATTR_MED 4 +#define MRT_ATTR_LOCALPREF 5 +#define MRT_ATTR_MP_REACH_NLRI 14 +#define MRT_ATTR_AS4PATH 17 +#define MRT_ATTR_EXTLEN 0x10 + +#define MRT_PREFIX_LEN(x) ((((u_int)x) + 7) / 8) + +struct mrt_parser { + void (*dump)(struct mrt_rib *, struct mrt_peer *, void *); + void (*state)(struct mrt_bgp_state *, void *); + void (*message)(struct mrt_bgp_msg *, void *); + void *arg; +}; + +void mrt_parse(int, struct mrt_parser *, int); diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c index 1854b7d0f60..13dc8e1cc0b 100644 --- a/usr.sbin/bgpctl/parser.c +++ b/usr.sbin/bgpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.62 2010/05/03 13:11:41 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.63 2011/09/21 10:37:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -21,6 +21,7 @@ #include <err.h> #include <errno.h> +#include <fcntl.h> #include <limits.h> #include <netdb.h> #include <stdio.h> @@ -53,7 +54,8 @@ enum token_type { WEIGHT, FAMILY, GETOPT, - RTABLE + RTABLE, + FILENAME }; enum getopts { @@ -73,14 +75,18 @@ static const struct token t_show[]; static const struct token t_show_summary[]; static const struct token t_show_fib[]; static const struct token t_show_rib[]; +static const struct token t_show_mrt[]; +static const struct token t_show_mrt_file[]; static const struct token t_show_rib_neigh[]; +static const struct token t_show_mrt_neigh[]; static const struct token t_show_rib_rib[]; static const struct token t_show_neighbor[]; static const struct token t_show_neighbor_modifiers[]; static const struct token t_fib[]; static const struct token t_neighbor[]; static const struct token t_neighbor_modifiers[]; -static const struct token t_show_as[]; +static const struct token t_show_rib_as[]; +static const struct token t_show_mrt_as[]; static const struct token t_show_prefix[]; static const struct token t_show_ip[]; static const struct token t_show_community[]; @@ -124,6 +130,7 @@ static const struct token t_show[] = { { KEYWORD, "tables", SHOW_FIB_TABLES, NULL}, { KEYWORD, "ip", NONE, t_show_ip}, { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, + { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, { ENDTOKEN, "", NONE, NULL} }; @@ -147,10 +154,10 @@ static const struct token t_show_fib[] = { static const struct token t_show_rib[] = { { NOTOKEN, "", NONE, NULL}, - { ASTYPE, "as", AS_ALL, t_show_as}, - { ASTYPE, "source-as", AS_SOURCE, t_show_as}, - { ASTYPE, "transit-as", AS_TRANSIT, t_show_as}, - { ASTYPE, "peer-as", AS_PEER, t_show_as}, + { ASTYPE, "as", AS_ALL, t_show_rib_as}, + { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as}, + { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as}, + { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, { KEYWORD, "community", NONE, t_show_community}, { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, @@ -165,12 +172,38 @@ static const struct token t_show_rib[] = { { ENDTOKEN, "", NONE, NULL} }; + +static const struct token t_show_mrt[] = { + { NOTOKEN, "", NONE, NULL}, + { ASTYPE, "as", AS_ALL, t_show_mrt_as}, + { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as}, + { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as}, + { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as}, + { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt}, + { FLAG, "detail", F_CTL_DETAIL, t_show_mrt}, + { KEYWORD, "neighbor", NONE, t_show_mrt_neigh}, + { KEYWORD, "file", NONE, t_show_mrt_file}, + { FAMILY, "", NONE, t_show_mrt}, + { PREFIX, "", NONE, t_show_prefix}, + { ENDTOKEN, "", NONE, NULL} +}; + +static const struct token t_show_mrt_file[] = { + { FILENAME, "", NONE, t_show_mrt}, + { ENDTOKEN, "", NONE, NULL} +}; + static const struct token t_show_rib_neigh[] = { { PEERADDRESS, "", NONE, t_show_rib}, { PEERDESC, "", NONE, t_show_rib}, { ENDTOKEN, "", NONE, NULL} }; +static const struct token t_show_mrt_neigh[] = { + { PEERADDRESS, "", NONE, t_show_mrt}, + { ENDTOKEN, "", NONE, NULL} +}; + static const struct token t_show_rib_rib[] = { { RIBNAME, "", NONE, t_show_rib}, { ENDTOKEN, "", NONE, NULL} @@ -212,11 +245,16 @@ static const struct token t_neighbor_modifiers[] = { { ENDTOKEN, "", NONE, NULL} }; -static const struct token t_show_as[] = { +static const struct token t_show_rib_as[] = { { ASNUM, "", NONE, t_show_rib}, { ENDTOKEN, "", NONE, NULL} }; +static const struct token t_show_mrt_as[] = { + { ASNUM, "", NONE, t_show_mrt}, + { ENDTOKEN, "", NONE, NULL} +}; + static const struct token t_show_prefix[] = { { NOTOKEN, "", NONE, NULL}, { FLAG, "all", F_LONGER, NULL}, @@ -550,6 +588,23 @@ match_token(int *argc, char **argv[], const struct token table[]) t = &table[i]; } break; + case FILENAME: + if (word != NULL && strlen(word) > 0) { + if ((res.mrtfd = open(word, O_RDONLY)) == -1) { + /* + * ignore error if path has no / and + * does not exist. In hope to print + * usage. + */ + if (errno == ENOENT && + !strchr(word, '/')) + break; + err(1, "mrt open(%s)", word); + } + match++; + t = &table[i]; + } + break; case ENDTOKEN: break; } @@ -624,6 +679,9 @@ show_valid_args(const struct token table[]) case GETOPT: fprintf(stderr, " <options>\n"); break; + case FILENAME: + fprintf(stderr, " <filename>\n"); + break; case ENDTOKEN: break; } @@ -676,7 +734,7 @@ parse_prefix(const char *word, struct bgpd_addr *addr, u_int8_t *prefixlen) if ((p = strrchr(word, '/')) != NULL) { mask = strtonum(p + 1, 0, 128, &errstr); if (errstr) - errx(1, "invalid netmask: %s", errstr); + errx(1, "netmask %s", errstr); if ((ps = malloc(strlen(word) - strlen(p) + 1)) == NULL) err(1, "parse_prefix: malloc"); diff --git a/usr.sbin/bgpctl/parser.h b/usr.sbin/bgpctl/parser.h index 3b6ce758a41..fe1ae218420 100644 --- a/usr.sbin/bgpctl/parser.h +++ b/usr.sbin/bgpctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.22 2010/05/03 13:11:41 claudio Exp $ */ +/* $OpenBSD: parser.h,v 1.23 2011/09/21 10:37:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -31,6 +31,7 @@ enum actions { SHOW_FIB, SHOW_FIB_TABLES, SHOW_RIB, + SHOW_MRT, SHOW_RIB_MEM, SHOW_NEXTHOP, SHOW_INTERFACE, @@ -66,6 +67,7 @@ struct parse_result { enum actions action; u_int8_t prefixlen; u_int8_t aid; + int mrtfd; }; __dead void usage(void); |