Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tools/bgpreader
test/bgpstream-test
test/bgpstream-test-filters
test/bgpstream-test-rislive
test/bgpstream-test-eor
test/bgpstream-test-rpki
test/bgpstream-test-utils-addr
test/bgpstream-test-utils-pfx
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ AC_PREREQ([2.68])

# bgpstream package version
m4_define([PKG_MAJOR_VERSION], [2])
m4_define([PKG_MID_VERSION], [3])
m4_define([PKG_MID_VERSION], [4])
m4_define([PKG_MINOR_VERSION], [0])

AC_INIT([libbgpstream], PKG_MAJOR_VERSION.PKG_MID_VERSION.PKG_MINOR_VERSION, [bgpstream-info@caida.org])
Expand Down
10 changes: 10 additions & 0 deletions lib/bgpstream_bgpdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ char *bgpstream_record_elem_bgpdump_snprintf(char *buf, size_t len,
ssize_t c = 0; /* < how many chars were written */
char *buf_p = buf;

/* The original bgpdump tool does not represent End-of-RIB markers in its
* output (an EoR is an empty UPDATE, which it silently drops). To stay
* faithful to that format we emit nothing for them. Callers should skip empty
* output. */
if (elem->type == BGPSTREAM_ELEM_TYPE_END_OF_RIB) {
if (len > 0)
buf[0] = '\0';
return buf;
}

/* Record type */
switch (elem->type) {
case BGPSTREAM_ELEM_TYPE_RIB:
Expand Down
34 changes: 34 additions & 0 deletions lib/bgpstream_elem.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ void bgpstream_elem_clear(bgpstream_elem_t *elem)
{
bgpstream_as_path_clear(elem->as_path);
bgpstream_community_set_clear(elem->communities);

/* reset the remaining type-dependent fields so that a re-used elem does not
* leak stale values (e.g. path attributes from a previous elem) */
elem->nexthop.version = BGPSTREAM_ADDR_VERSION_UNKNOWN;
elem->has_origin = 0;
elem->has_med = 0;
elem->has_local_pref = 0;
elem->atomic_aggregate = 0;
elem->aggregator.has_aggregator = 0;
}

bgpstream_elem_t *bgpstream_elem_copy(bgpstream_elem_t *dst,
Expand Down Expand Up @@ -129,6 +138,7 @@ int bgpstream_elem_type_snprintf(char *buf, size_t len,
case BGPSTREAM_ELEM_TYPE_ANNOUNCEMENT: ch = 'A'; break;
case BGPSTREAM_ELEM_TYPE_WITHDRAWAL: ch = 'W'; break;
case BGPSTREAM_ELEM_TYPE_PEERSTATE: ch = 'S'; break;
case BGPSTREAM_ELEM_TYPE_END_OF_RIB: ch = 'E'; break;
default:
if (len > 0)
buf[0] = '\0';
Expand Down Expand Up @@ -378,6 +388,30 @@ char *bgpstream_elem_custom_snprintf(char *buf, size_t len,
/* END OF LINE */
break;

case BGPSTREAM_ELEM_TYPE_END_OF_RIB:

/* PREFIX (AFI indicator) */
if (bgpstream_pfx_snprintf(buf_p, B_REMAIN, &(elem->prefix)) == NULL) {
if (errno != ENOSPC)
bgpstream_log(BGPSTREAM_LOG_ERR, "Malformed prefix (E)");
return NULL;
}
SEEK_STR_END;
ADD_PIPE;
/* NEXT HOP (empty) */
ADD_PIPE;
/* AS PATH (empty) */
ADD_PIPE;
/* ORIGIN AS (empty) */
ADD_PIPE;
/* COMMUNITIES (empty) */
ADD_PIPE;
/* OLD STATE (empty) */
ADD_PIPE;
/* NEW STATE (empty) */
/* END OF LINE */
break;

default:
bgpstream_log(BGPSTREAM_LOG_ERR, "Error during elem processing");
return NULL;
Expand Down
3 changes: 3 additions & 0 deletions lib/bgpstream_elem.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ typedef enum {
/** Peer state change */
BGPSTREAM_ELEM_TYPE_PEERSTATE = 4,

/** End-of-RIB marker (RFC 4724) */
BGPSTREAM_ELEM_TYPE_END_OF_RIB = 5,

} bgpstream_elem_type_t;

typedef struct struct_bgpstream_annotations_t {
Expand Down
2 changes: 2 additions & 0 deletions lib/bgpstream_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ int bgpstream_filter_mgr_filter_add(bgpstream_filter_mgr_t *this,
this->elemtype_mask |= (BGPSTREAM_FILTER_ELEM_TYPE_WITHDRAWAL);
} else if (strcmp(filter_value, "peerstates") == 0) {
this->elemtype_mask |= (BGPSTREAM_FILTER_ELEM_TYPE_PEERSTATE);
} else if (strcmp(filter_value, "endofrib") == 0) {
this->elemtype_mask |= (BGPSTREAM_FILTER_ELEM_TYPE_END_OF_RIB);
} else {
bgpstream_log(BGPSTREAM_LOG_ERR,
"unknown element type '%s'", filter_value);
Expand Down
1 change: 1 addition & 0 deletions lib/bgpstream_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#define BGPSTREAM_FILTER_ELEM_TYPE_ANNOUNCEMENT 0x2
#define BGPSTREAM_FILTER_ELEM_TYPE_WITHDRAWAL 0x4
#define BGPSTREAM_FILTER_ELEM_TYPE_PEERSTATE 0x8
#define BGPSTREAM_FILTER_ELEM_TYPE_END_OF_RIB 0x10

/* hash table community filter:
* community -> filter mask (asn only, value only, both) */
Expand Down
5 changes: 5 additions & 0 deletions lib/bgpstream_record.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ static int elem_check_filters(bgpstream_record_t *record,
!(filter_mgr->elemtype_mask & BGPSTREAM_FILTER_ELEM_TYPE_WITHDRAWAL)) {
return 0;
}

if (elem->type == BGPSTREAM_ELEM_TYPE_END_OF_RIB &&
!(filter_mgr->elemtype_mask & BGPSTREAM_FILTER_ELEM_TYPE_END_OF_RIB)) {
return 0;
}
}

/* Checking peer ASNs: if the filter is on and the peer asn is not in the
Expand Down
74 changes: 74 additions & 0 deletions lib/formats/bgpstream_parsebgp_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,65 @@ static int handle_prefix(bgpstream_elem_t *elem,
} \
} while (0)

// Detect an RFC 4724 End-of-RIB marker and, if found, populate `elem` as an
// END_OF_RIB elem whose prefix encodes the AFI (0.0.0.0/0 for IPv4 unicast,
// ::/0 for IPv6 unicast). Returns 1 if an EoR was detected and `elem` was
// populated, 0 otherwise.
static int handle_end_of_rib(bgpstream_elem_t *elem,
parsebgp_bgp_update_t *update)
{
// a 16-byte zeroed source so we can build the 0.0.0.0/0 or ::/0 prefix that
// encodes the EoR AFI
static const uint8_t zero_addr[16] = { 0 };

Comment thread
alistairking marked this conversation as resolved.
// IPv4 unicast EoR: a completely empty UPDATE (no withdrawn or announced
// NLRIs, and no path attributes). Note that we must check the original NLRI
// counts (not the upd_state counters, which have been decremented to zero as
// elems were yielded) so that we don't mistake a withdrawal-only UPDATE for
// an EoR.
if (update->path_attrs.attrs_cnt == 0 &&
update->withdrawn_nlris.prefixes_cnt == 0 &&
update->announced_nlris.prefixes_cnt == 0) {
bgpstream_elem_clear(elem);
elem->type = BGPSTREAM_ELEM_TYPE_END_OF_RIB;
bgpstream_ipv4_addr_init(&elem->prefix.address, zero_addr);
elem->prefix.mask_len = 0;
return 1;
}

// IPv4/IPv6 unicast EoR: the UPDATE carries exactly one path attribute, an
// empty MP_UNREACH_NLRI (zero withdrawn NLRIs) for the IPv4 or IPv6 unicast
// address family. Per RFC 4724, a valid EoR must not carry any native NLRIs
// either, so reject UPDATEs that contained IPv4 withdrawals or announcements.
if (update->path_attrs.attrs_cnt != 1 ||
update->withdrawn_nlris.prefixes_cnt != 0 ||
update->announced_nlris.prefixes_cnt != 0) {
return 0;
}
parsebgp_bgp_update_path_attr_t *mp_unreach_attr =
&update->path_attrs.attrs[PARSEBGP_BGP_PATH_ATTR_TYPE_MP_UNREACH_NLRI];
if (mp_unreach_attr->type != PARSEBGP_BGP_PATH_ATTR_TYPE_MP_UNREACH_NLRI) {
return 0;
}
parsebgp_bgp_update_mp_unreach_t *mp_unreach = mp_unreach_attr->data.mp_unreach;
if (mp_unreach->withdrawn_nlris_cnt != 0 ||
mp_unreach->safi != PARSEBGP_BGP_SAFI_UNICAST) {
return 0;
}
Comment thread
alistairking marked this conversation as resolved.
bgpstream_elem_clear(elem);
elem->type = BGPSTREAM_ELEM_TYPE_END_OF_RIB;
if (mp_unreach->afi == PARSEBGP_BGP_AFI_IPV6) {
bgpstream_ipv6_addr_init(&elem->prefix.address, zero_addr);
} else if (mp_unreach->afi == PARSEBGP_BGP_AFI_IPV4) {
bgpstream_ipv4_addr_init(&elem->prefix.address, zero_addr);
} else {
// some other (unsupported) address family - not an EoR we represent
return 0;
}
elem->prefix.mask_len = 0;
return 1;
}

int bgpstream_parsebgp_process_update(bgpstream_parsebgp_upd_state_t *upd_state,
bgpstream_elem_t *elem,
parsebgp_bgp_msg_t *bgp)
Expand All @@ -325,6 +384,11 @@ int bgpstream_parsebgp_process_update(bgpstream_parsebgp_upd_state_t *upd_state,
return 0;
}

// the message claims to be an UPDATE, but carries no UPDATE payload
if (update == NULL) {
return 0;
}

// how many native withdrawals will we process?
upd_state->withdrawal_v4_cnt = update->withdrawn_nlris.prefixes_cnt;

Expand Down Expand Up @@ -355,6 +419,16 @@ int bgpstream_parsebgp_process_update(bgpstream_parsebgp_upd_state_t *upd_state,
// are we at end-of-elems?
if (upd_state->withdrawal_v4_cnt == 0 && upd_state->withdrawal_v6_cnt == 0 &&
upd_state->announce_v4_cnt == 0 && upd_state->announce_v6_cnt == 0) {

// Check if this is an End-of-RIB marker (RFC 4724). We only emit a single
// EoR elem per record, so guard with eor_done.
if (upd_state->eor_done == 0 && update != NULL) {
upd_state->eor_done = 1;
if (handle_end_of_rib(elem, update) != 0) {
return 1;
}
}
Comment thread
alistairking marked this conversation as resolved.

return 0;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/formats/bgpstream_parsebgp_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ typedef struct bgpstream_parsebgp_upd_state {
// has the BGP4MP state been prepared
int ready;

// has the End-of-RIB check been performed
int eor_done;

// how many native (IPv4) withdrawals still to yield
int withdrawal_v4_cnt;
int withdrawal_v4_idx;
Expand Down
1 change: 1 addition & 0 deletions lib/formats/bs_format_rislive.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ void bs_format_rislive_clear_data(bgpstream_format_t *format, void *data)
bgpstream_elem_clear(rd->elem);
rd->end_of_elems = 0;
rd->next_re = 0;
rd->msg_type = 0;
bgpstream_parsebgp_upd_state_reset(&rd->upd_state);
parsebgp_clear_msg(rd->msg);
}
Expand Down
6 changes: 6 additions & 0 deletions test/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ TESTS = \
bgpstream-test \
bgpstream-test-filters \
bgpstream-test-rislive \
bgpstream-test-eor \
bgpstream-test-utils-addr \
bgpstream-test-utils-pfx \
bgpstream-test-utils-patricia \
Expand All @@ -46,6 +47,7 @@ check_PROGRAMS = \
bgpstream-test \
bgpstream-test-filters \
bgpstream-test-rislive \
bgpstream-test-eor \
bgpstream-test-utils-addr \
bgpstream-test-utils-pfx \
bgpstream-test-utils-patricia \
Expand All @@ -56,6 +58,7 @@ check_PROGRAMS = \
EXTRA_DIST = sqlite_test.db \
csv_test.csv \
ris-live-stream.json \
eor-stream.json \
routeviews.route-views.jinx.ribs.1427846400.bz2 \
routeviews.route-views.jinx.updates.1427846400.bz2 \
ris.rrc06.updates.1427846400.gz \
Expand All @@ -70,6 +73,9 @@ bgpstream_test_filters_LDADD = $(top_builddir)/lib/libbgpstream.la
bgpstream_test_rislive_SOURCES = bgpstream-test-rislive.c bgpstream_test.h
bgpstream_test_rislive_LDADD = $(top_builddir)/lib/libbgpstream.la

bgpstream_test_eor_SOURCES = bgpstream-test-eor.c bgpstream_test.h
bgpstream_test_eor_LDADD = $(top_builddir)/lib/libbgpstream.la

bgpstream_test_rpki_SOURCES = bgpstream-test-rpki.c bgpstream-test-rpki.h bgpstream_test.h
bgpstream_test_rpki_LDADD = $(top_builddir)/lib/libbgpstream.la

Expand Down
Loading