summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2011-09-21 10:37:52 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2011-09-21 10:37:52 +0000
commit1722612503da6b85a79ff90c9977f23a53af8543 (patch)
treea564ea3aeb5b2ddee567344f939ded63848dbd3f /usr.sbin
parentd84f7ec0e4299fd9228d25660d9c9c0339a9033c (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/Makefile4
-rw-r--r--usr.sbin/bgpctl/bgpctl.c470
-rw-r--r--usr.sbin/bgpctl/mrtparser.c971
-rw-r--r--usr.sbin/bgpctl/mrtparser.h115
-rw-r--r--usr.sbin/bgpctl/parser.c76
-rw-r--r--usr.sbin/bgpctl/parser.h4
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);