src/usr.sbin/bgpctl/mrtparser.c

1288 lines
28 KiB
C
Raw Normal View History

/* $OpenBSD: mrtparser.c,v 1.19 2022/10/18 09:30:29 job 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 <time.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);
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);
int mrt_extract_attr(struct mrt_rib_entry *, u_char *, int, uint8_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 *, uint16_t, uint16_t *);
int mrt_extract_addr(void *, u_int, struct bgpd_addr *, uint8_t);
int mrt_extract_prefix(void *, u_int, uint8_t, struct bgpd_addr *,
uint8_t *, int);
struct mrt_bgp_state *mrt_parse_state(struct mrt_hdr *, void *, int);
struct mrt_bgp_msg *mrt_parse_msg(struct mrt_hdr *, void *, int);
void *
mrt_read_msg(int fd, struct mrt_hdr *hdr)
{
void *buf;
memset(hdr, 0, 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;
struct mrt_bgp_state *s;
struct mrt_bgp_msg *m;
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("unsupported MRT type %d\n",
ntohs(h.type));
break;
case MSG_TABLE_DUMP:
switch (ntohs(h.subtype)) {
case MRT_DUMP_AFI_IP:
case MRT_DUMP_AFI_IPv6:
if (p->dump == NULL)
break;
if (mrt_parse_dump(&h, msg, &pctx, &r) == 0) {
if (p->dump)
p->dump(r, pctx, p->arg);
mrt_free_rib(r);
}
break;
default:
if (verbose)
printf("unknown AFI %d in table dump\n",
ntohs(h.subtype));
break;
}
break;
case MSG_TABLE_DUMP_V2:
switch (ntohs(h.subtype)) {
case MRT_DUMP_V2_PEER_INDEX_TABLE:
if (p->dump == NULL)
break;
if (pctx)
mrt_free_peers(pctx);
pctx = mrt_parse_v2_peer(&h, msg);
break;
case MRT_DUMP_V2_RIB_IPV4_UNICAST:
case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
case MRT_DUMP_V2_RIB_IPV6_UNICAST:
case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
case MRT_DUMP_V2_RIB_GENERIC:
case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH:
case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH:
case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH:
case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH:
case MRT_DUMP_V2_RIB_GENERIC_ADDPATH:
if (p->dump == NULL)
break;
r = mrt_parse_v2_rib(&h, msg, verbose);
if (r) {
if (p->dump)
p->dump(r, pctx, p->arg);
mrt_free_rib(r);
}
break;
default:
if (verbose)
printf("unhandled DUMP_V2 subtype %d\n",
ntohs(h.subtype));
break;
}
break;
case MSG_PROTOCOL_BGP4MP_ET:
case MSG_PROTOCOL_BGP4MP:
switch (ntohs(h.subtype)) {
case BGP4MP_STATE_CHANGE:
case BGP4MP_STATE_CHANGE_AS4:
if ((s = mrt_parse_state(&h, msg, verbose))) {
if (p->state)
p->state(s, p->arg);
free(s);
}
break;
case BGP4MP_MESSAGE:
case BGP4MP_MESSAGE_AS4:
case BGP4MP_MESSAGE_LOCAL:
case BGP4MP_MESSAGE_AS4_LOCAL:
case BGP4MP_MESSAGE_ADDPATH:
case BGP4MP_MESSAGE_AS4_ADDPATH:
case BGP4MP_MESSAGE_LOCAL_ADDPATH:
case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH:
if ((m = mrt_parse_msg(&h, msg, verbose))) {
if (p->message)
p->message(m, p->arg);
free(m->msg);
free(m);
}
break;
case BGP4MP_ENTRY:
if (p->dump == NULL)
break;
if (mrt_parse_dump_mp(&h, msg, &pctx, &r,
verbose) == 0) {
if (p->dump)
p->dump(r, pctx, p->arg);
mrt_free_rib(r);
}
break;
default:
if (verbose)
printf("unhandled BGP4MP subtype %d\n",
ntohs(h.subtype));
break;
}
break;
default:
if (verbose)
printf("unknown MRT type %d\n", ntohs(h.type));
break;
}
free(msg);
}
if (pctx)
mrt_free_peers(pctx);
}
static int
mrt_afi2aid(int afi, int safi, int verbose)
{
switch (afi) {
case MRT_DUMP_AFI_IP:
if (safi == -1 || safi == 1 || safi == 2)
return AID_INET;
else if (safi == 128)
return AID_VPN_IPv4;
break;
case MRT_DUMP_AFI_IPv6:
if (safi == -1 || safi == 1 || safi == 2)
return AID_INET6;
else if (safi == 128)
return AID_VPN_IPv6;
break;
default:
break;
}
if (verbose)
printf("unhandled AFI/SAFI %d/%d\n", afi, safi);
return AID_UNSPEC;
}
struct mrt_peer *
mrt_parse_v2_peer(struct mrt_hdr *hdr, void *msg)
{
struct mrt_peer_entry *peers = NULL;
struct mrt_peer *p;
uint8_t *b = msg;
uint32_t bid, as4;
uint16_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++) {
uint8_t type;
if (len < sizeof(uint8_t) + sizeof(uint32_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 (mrt_extract_addr(b, len, &peers[i].addr,
AID_INET6) == -1)
goto fail;
b += sizeof(struct in6_addr);
len -= sizeof(struct in6_addr);
} else {
if (mrt_extract_addr(b, len, &peers[i].addr,
AID_INET) == -1)
goto fail;
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);
free(peers);
return (NULL);
}
struct mrt_rib *
mrt_parse_v2_rib(struct mrt_hdr *hdr, void *msg, int verbose)
{
struct mrt_rib_entry *entries = NULL;
struct mrt_rib *r;
uint8_t *b = msg;
u_int len = ntohl(hdr->length);
uint32_t snum, path_id = 0;
uint16_t cnt, i, afi;
uint8_t safi, aid;
int ret;
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_ADDPATH:
case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH:
r->add_path = 1;
/* FALLTHROUGH */
case MRT_DUMP_V2_RIB_IPV4_UNICAST:
case MRT_DUMP_V2_RIB_IPV4_MULTICAST:
/* prefix */
ret = mrt_extract_prefix(b, len, AID_INET, &r->prefix,
&r->prefixlen, verbose);
if (ret == 1)
goto fail;
break;
case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH:
case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH:
r->add_path = 1;
/* FALLTHROUGH */
case MRT_DUMP_V2_RIB_IPV6_UNICAST:
case MRT_DUMP_V2_RIB_IPV6_MULTICAST:
/* prefix */
ret = mrt_extract_prefix(b, len, AID_INET6, &r->prefix,
&r->prefixlen, verbose);
if (ret == 1)
goto fail;
break;
case MRT_DUMP_V2_RIB_GENERIC_ADDPATH:
/*
* RFC8050 handling for add-path has special handling for
* RIB_GENERIC_ADDPATH but nobody implements it that way.
* So just use the same way as for the other _ADDPATH types.
*/
r->add_path = 1;
/* FALLTHROUGH */
case MRT_DUMP_V2_RIB_GENERIC:
/* fetch AFI/SAFI pair */
if (len < 3)
goto fail;
memcpy(&afi, b, sizeof(afi));
b += sizeof(afi);
len -= sizeof(afi);
afi = ntohs(afi);
safi = *b++;
len -= 1;
if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC)
goto fail;
/* prefix */
ret = mrt_extract_prefix(b, len, aid, &r->prefix,
&r->prefixlen, verbose);
if (ret == 1)
goto fail;
break;
default:
errx(1, "unknown subtype %hd", ntohs(hdr->subtype));
}
/* adjust length */
b += ret;
len -= ret;
/* 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++) {
uint32_t otm;
uint16_t pix, alen;
if (len < 2 * sizeof(uint16_t) + sizeof(uint32_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);
if (r->add_path) {
if (len < sizeof(path_id) + sizeof(alen))
goto fail;
memcpy(&path_id, b, sizeof(path_id));
b += sizeof(path_id);
len -= sizeof(path_id);
path_id = ntohl(path_id);
}
entries[i].path_id = path_id;
/* 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.aid, 1) == -1)
goto fail;
b += alen;
len -= alen;
}
r->entries = entries;
return (r);
fail:
mrt_free_rib(r);
free(entries);
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;
uint8_t *b = msg;
u_int len = ntohl(hdr->length);
uint16_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(uint16_t))
goto fail;
/* view */
b += sizeof(uint16_t);
len -= sizeof(uint16_t);
/* seqnum */
memcpy(&r->seqnum, b, sizeof(uint16_t));
b += sizeof(uint16_t);
len -= sizeof(uint16_t);
r->seqnum = ntohs(r->seqnum);
switch (ntohs(hdr->subtype)) {
case MRT_DUMP_AFI_IP:
if (mrt_extract_addr(b, len, &r->prefix, AID_INET) == -1)
goto fail;
b += sizeof(struct in_addr);
len -= sizeof(struct in_addr);
break;
case MRT_DUMP_AFI_IPv6:
if (mrt_extract_addr(b, len, &r->prefix, AID_INET6) == -1)
goto fail;
b += sizeof(struct in6_addr);
len -= sizeof(struct in6_addr);
break;
}
if (len < 2 * sizeof(uint32_t) + 2 * sizeof(uint16_t) + 2)
goto fail;
r->prefixlen = *b++;
len -= 1;
/* status */
b += 1;
len -= 1;
/* originated */
memcpy(&re->originated, b, sizeof(uint32_t));
b += sizeof(uint32_t);
len -= sizeof(uint32_t);
re->originated = ntohl(re->originated);
/* peer ip */
switch (ntohs(hdr->subtype)) {
case MRT_DUMP_AFI_IP:
if (mrt_extract_addr(b, len, &p->peers->addr, AID_INET) == -1)
goto fail;
b += sizeof(struct in_addr);
len -= sizeof(struct in_addr);
break;
case MRT_DUMP_AFI_IPv6:
if (mrt_extract_addr(b, len, &p->peers->addr, AID_INET6) == -1)
goto fail;
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.aid, 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, int verbose)
{
struct mrt_peer *p;
struct mrt_rib *r;
struct mrt_rib_entry *re;
uint8_t *b = msg;
u_int len = ntohl(hdr->length);
uint16_t asnum, alen, afi;
uint8_t safi, nhlen, aid;
int ret;
/* just ignore the microsec field for _ET header for now */
if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
b = (char *)b + sizeof(uint32_t);
len -= sizeof(uint32_t);
}
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(uint16_t))
goto fail;
/* source AS */
b += sizeof(uint16_t);
len -= sizeof(uint16_t);
/* dest AS */
memcpy(&asnum, b, sizeof(asnum));
b += sizeof(asnum);
len -= sizeof(asnum);
p->peers->asnum = ntohs(asnum);
/* iface index */
b += sizeof(uint16_t);
len -= sizeof(uint16_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 */
if (mrt_extract_addr(b, len, &p->peers->addr, AID_INET) == -1)
goto fail;
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 */
if (mrt_extract_addr(b, len, &p->peers->addr, AID_INET6) == -1)
goto fail;
b += sizeof(struct in6_addr);
len -= sizeof(struct in6_addr);
break;
}
if (len < 2 * sizeof(uint16_t) + 2 * sizeof(uint32_t))
goto fail;
/* view + status */
b += 2 * sizeof(uint16_t);
len -= 2 * sizeof(uint16_t);
/* originated */
memcpy(&re->originated, b, sizeof(uint32_t));
b += sizeof(uint32_t);
len -= sizeof(uint32_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;
if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC)
goto fail;
/* nhlen */
nhlen = *b++;
len -= 1;
/* nexthop */
if (mrt_extract_addr(b, len, &re->nexthop, aid) == -1)
goto fail;
if (len < nhlen)
goto fail;
b += nhlen;
len -= nhlen;
/* prefix */
ret = mrt_extract_prefix(b, len, aid, &r->prefix, &r->prefixlen,
verbose);
if (ret == 1)
goto fail;
b += ret;
len -= ret;
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.aid, 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, uint8_t aid,
int as4)
{
struct mrt_attr *ap;
uint32_t tmp;
uint16_t attr_len;
uint8_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 (aid != AID_INET)
break;
memcpy(&tmp, a, sizeof(tmp));
re->nexthop.aid = AID_INET;
re->nexthop.v4.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. In short the abbreviated MP_NLRI
* hack in the standard is not used in real life.
* Detect the two cases by looking at the first byte
* of the payload (either the nexthop addr length (RFC)
* or the high byte of the AFI (old form)). If the
* first byte matches the expected nexthop length it
* is expected to be the RFC 6396 encoding.
*/
if (*a != attr_len - 1) {
a += 3;
alen -= 3;
attr_len -= 3;
}
switch (aid) {
case AID_INET6:
if (attr_len < sizeof(struct in6_addr) + 1)
return (-1);
re->nexthop.aid = aid;
memcpy(&re->nexthop.v6, a + 1,
sizeof(struct in6_addr));
break;
case AID_VPN_IPv4:
if (attr_len < sizeof(uint64_t) +
sizeof(struct in_addr))
return (-1);
re->nexthop.aid = aid;
memcpy(&tmp, a + 1 + sizeof(uint64_t),
sizeof(tmp));
re->nexthop.v4.s_addr = tmp;
break;
case AID_VPN_IPv6:
if (attr_len < sizeof(uint64_t) +
sizeof(struct in6_addr))
return (-1);
re->nexthop.aid = aid;
memcpy(&re->nexthop.v6,
a + 1 + sizeof(uint64_t),
sizeof(struct in6_addr));
break;
}
break;
case MRT_ATTR_AS4PATH:
if (!as4) {
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 = reallocarray(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)
{
uint16_t i, j;
for (i = 0; i < r->nentries && r->entries; i++) {
for (j = 0; j < r->entries[i].nattrs; j++)
free(r->entries[i].attrs[j].attr);
free(r->entries[i].attrs);
free(r->entries[i].aspath);
}
free(r->entries);
free(r);
}
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, uint16_t len, uint16_t *newlen)
{
uint8_t *seg, *nseg, *ndata;
uint16_t seg_size, olen, nlen;
uint8_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(uint16_t) * seg_len;
nlen += 2 + sizeof(uint32_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);
}
int
mrt_extract_addr(void *msg, u_int len, struct bgpd_addr *addr, uint8_t aid)
{
uint8_t *b = msg;
memset(addr, 0, sizeof(*addr));
switch (aid) {
case AID_INET:
if (len < sizeof(struct in_addr))
return (-1);
addr->aid = aid;
memcpy(&addr->v4, b, sizeof(struct in_addr));
return sizeof(struct in_addr);
case AID_INET6:
if (len < sizeof(struct in6_addr))
return (-1);
addr->aid = aid;
memcpy(&addr->v6, b, sizeof(struct in6_addr));
return sizeof(struct in6_addr);
case AID_VPN_IPv4:
if (len < sizeof(uint64_t) + sizeof(struct in_addr))
return (-1);
addr->aid = aid;
/* XXX labelstack and rd missing */
memcpy(&addr->v4, b + sizeof(uint64_t),
sizeof(struct in_addr));
return (sizeof(uint64_t) + sizeof(struct in_addr));
case AID_VPN_IPv6:
if (len < sizeof(uint64_t) + sizeof(struct in6_addr))
return (-1);
addr->aid = aid;
/* XXX labelstack and rd missing */
memcpy(&addr->v6, b + sizeof(uint64_t),
sizeof(struct in6_addr));
return (sizeof(uint64_t) + sizeof(struct in6_addr));
default:
return (-1);
}
}
int
mrt_extract_prefix(void *msg, u_int len, uint8_t aid,
struct bgpd_addr *prefix, uint8_t *prefixlen, int verbose)
{
int r;
switch (aid) {
case AID_INET:
r = nlri_get_prefix(msg, len, prefix, prefixlen);
break;
case AID_INET6:
r = nlri_get_prefix6(msg, len, prefix, prefixlen);
break;
case AID_VPN_IPv4:
r = nlri_get_vpn4(msg, len, prefix, prefixlen, 0);
break;
case AID_VPN_IPv6:
r = nlri_get_vpn6(msg, len, prefix, prefixlen, 0);
break;
default:
if (verbose)
printf("unknown prefix AID %d\n", aid);
return -1;
}
if (r == -1 && verbose)
printf("failed to parse prefix of AID %d\n", aid);
return r;
}
struct mrt_bgp_state *
mrt_parse_state(struct mrt_hdr *hdr, void *msg, int verbose)
{
struct timespec t;
struct mrt_bgp_state *s;
uint8_t *b = msg;
u_int len = ntohl(hdr->length);
uint32_t sas, das, usec;
uint16_t tmp16, afi;
int r;
uint8_t aid;
t.tv_sec = ntohl(hdr->timestamp);
t.tv_nsec = 0;
/* handle the microsec field for _ET header */
if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
memcpy(&usec, b, sizeof(usec));
b += sizeof(usec);
len -= sizeof(usec);
t.tv_nsec = ntohl(usec) * 1000;
}
switch (ntohs(hdr->subtype)) {
case BGP4MP_STATE_CHANGE:
if (len < 8)
return (0);
/* source as */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
sas = ntohs(tmp16);
/* dest as */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
das = ntohs(tmp16);
/* if_index, ignored */
b += sizeof(tmp16);
len -= sizeof(tmp16);
/* afi */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
afi = ntohs(tmp16);
break;
case BGP4MP_STATE_CHANGE_AS4:
if (len < 12)
return (0);
/* source as */
memcpy(&sas, b, sizeof(sas));
b += sizeof(sas);
len -= sizeof(sas);
sas = ntohl(sas);
/* dest as */
memcpy(&das, b, sizeof(das));
b += sizeof(das);
len -= sizeof(das);
das = ntohl(das);
/* if_index, ignored */
b += sizeof(tmp16);
len -= sizeof(tmp16);
/* afi */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
afi = ntohs(tmp16);
break;
default:
errx(1, "mrt_parse_state: bad subtype");
}
/* src & dst addr */
if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC)
return (NULL);
if ((s = calloc(1, sizeof(struct mrt_bgp_state))) == NULL)
err(1, "calloc");
s->time = t;
s->src_as = sas;
s->dst_as = das;
if ((r = mrt_extract_addr(b, len, &s->src, aid)) == -1)
goto fail;
b += r;
len -= r;
if ((r = mrt_extract_addr(b, len, &s->dst, aid)) == -1)
goto fail;
b += r;
len -= r;
/* states */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
s->old_state = ntohs(tmp16);
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
s->new_state = ntohs(tmp16);
return (s);
fail:
free(s);
return (NULL);
}
struct mrt_bgp_msg *
mrt_parse_msg(struct mrt_hdr *hdr, void *msg, int verbose)
{
struct timespec t;
struct mrt_bgp_msg *m;
uint8_t *b = msg;
u_int len = ntohl(hdr->length);
uint32_t sas, das, usec;
uint16_t tmp16, afi;
int r, addpath = 0;
uint8_t aid;
t.tv_sec = ntohl(hdr->timestamp);
t.tv_nsec = 0;
/* handle the microsec field for _ET header */
if (ntohs(hdr->type) == MSG_PROTOCOL_BGP4MP_ET) {
memcpy(&usec, b, sizeof(usec));
b += sizeof(usec);
len -= sizeof(usec);
t.tv_nsec = ntohl(usec) * 1000;
}
switch (ntohs(hdr->subtype)) {
case BGP4MP_MESSAGE_ADDPATH:
case BGP4MP_MESSAGE_LOCAL_ADDPATH:
addpath = 1;
/* FALLTHROUGH */
case BGP4MP_MESSAGE:
case BGP4MP_MESSAGE_LOCAL:
if (len < 8)
return (0);
/* source as */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
sas = ntohs(tmp16);
/* dest as */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
das = ntohs(tmp16);
/* if_index, ignored */
b += sizeof(tmp16);
len -= sizeof(tmp16);
/* afi */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
afi = ntohs(tmp16);
break;
case BGP4MP_MESSAGE_AS4_ADDPATH:
case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH:
addpath = 1;
/* FALLTHROUGH */
case BGP4MP_MESSAGE_AS4:
case BGP4MP_MESSAGE_AS4_LOCAL:
if (len < 12)
return (0);
/* source as */
memcpy(&sas, b, sizeof(sas));
b += sizeof(sas);
len -= sizeof(sas);
sas = ntohl(sas);
/* dest as */
memcpy(&das, b, sizeof(das));
b += sizeof(das);
len -= sizeof(das);
das = ntohl(das);
/* if_index, ignored */
b += sizeof(tmp16);
len -= sizeof(tmp16);
/* afi */
memcpy(&tmp16, b, sizeof(tmp16));
b += sizeof(tmp16);
len -= sizeof(tmp16);
afi = ntohs(tmp16);
break;
default:
errx(1, "mrt_parse_msg: bad subtype");
}
/* src & dst addr */
if ((aid = mrt_afi2aid(afi, -1, verbose)) == AID_UNSPEC)
return (NULL);
if ((m = calloc(1, sizeof(struct mrt_bgp_msg))) == NULL)
err(1, "calloc");
m->time = t;
m->src_as = sas;
m->dst_as = das;
m->add_path = addpath;
if ((r = mrt_extract_addr(b, len, &m->src, aid)) == -1)
goto fail;
b += r;
len -= r;
if ((r = mrt_extract_addr(b, len, &m->dst, aid)) == -1)
goto fail;
b += r;
len -= r;
/* msg */
if (len > 0) {
m->msg_len = len;
if ((m->msg = malloc(len)) == NULL)
err(1, "malloc");
memcpy(m->msg, b, len);
}
return (m);
fail:
free(m->msg);
free(m);
return (NULL);
}