From de77e5cdd013d1bc64a31ea5e7b68262352e0c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Tvrd=C3=ADk?= Date: Tue, 19 May 2015 10:47:14 +0200 Subject: [PATCH] Add core support for MRT Table Dump (RFC 6396) --- lib/ip.c | 6 +- lib/ip.h | 4 +- lib/ip_test.c | 6 +- nest/mrtdump.c | 159 ++++++++++++++++++++++++++++++++++++++++ nest/mrtdump.h | 85 +++++++++++++++++---- nest/mrtdump_test.c | 115 +++++++++++++++++++++++++++++ nest/route.h | 12 +-- nest/rt-fib.c | 6 +- sysdep/unix/log.c | 2 +- test/birdtest_support.h | 8 +- 10 files changed, 370 insertions(+), 33 deletions(-) create mode 100644 nest/mrtdump.c create mode 100644 nest/mrtdump_test.c diff --git a/lib/ip.c b/lib/ip.c index 70f33783..87af5229 100644 --- a/lib/ip.c +++ b/lib/ip.c @@ -233,7 +233,7 @@ ip6_ntop(ip6_addr a, char *b) } int -ip4_pton(char *a, ip4_addr *o) +ip4_pton(const char *a, ip4_addr *o) { int i; unsigned long int l; @@ -258,11 +258,11 @@ ip4_pton(char *a, ip4_addr *o) } int -ip6_pton(char *a, ip6_addr *o) +ip6_pton(const char *a, ip6_addr *o) { u16 words[8]; int i, j, k, l, hfil; - char *start; + const char *start; if (a[0] == ':') /* Leading :: */ { diff --git a/lib/ip.h b/lib/ip.h index 90bb7f8a..e33adc6e 100644 --- a/lib/ip.h +++ b/lib/ip.h @@ -446,8 +446,8 @@ static inline char * ip4_ntox(ip4_addr a, char *b) static inline char * ip6_ntox(ip6_addr a, char *b) { return b + bsprintf(b, "%08x.%08x.%08x.%08x", _I0(a), _I1(a), _I2(a), _I3(a)); } -int ip4_pton(char *a, ip4_addr *o); -int ip6_pton(char *a, ip6_addr *o); +int ip4_pton(const char *a, ip4_addr *o); +int ip6_pton(const char *a, ip6_addr *o); // XXXX these functions must be redesigned or removed #ifdef IPV6 diff --git a/lib/ip_test.c b/lib/ip_test.c index 149fc048..e64a8764 100644 --- a/lib/ip_test.c +++ b/lib/ip_test.c @@ -20,10 +20,10 @@ build_ip4(u8 a, u8 b, u8 c, u8 d) } static u32 -ip4_pton_(char *s) +ip4_pton_(const char *s) { ip4_addr ip; - ip4_pton(s,&ip); + ip4_pton(s, &ip); return ip.addr; } @@ -54,7 +54,7 @@ t_ip4_pton(void) } static void -ip6_pton_(char *s, u32 (*addr)[4]) +ip6_pton_(const char *s, u32 (*addr)[4]) { static ip6_addr ip; ip6_pton(s, &ip); diff --git a/nest/mrtdump.c b/nest/mrtdump.c new file mode 100644 index 00000000..0c79ba7c --- /dev/null +++ b/nest/mrtdump.c @@ -0,0 +1,159 @@ +/* + * BIRD -- Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "nest/mrtdump.h" + +void +mrt_msg_init(struct mrt_msg *msg, pool *mem_pool) +{ + msg->mem_pool = mem_pool; + msg->msg_capacity = MRT_MSG_DEFAULT_CAPACITY; + msg->msg_length = 0; + msg->msg = mb_alloc(msg->mem_pool, msg->msg_capacity); +} + +void +mrt_msg_free(struct mrt_msg *msg) +{ + mb_free(msg->msg); +} + +static byte * +mrt_peer_index_table_get_peer_count(struct mrt_peer_index_table *pit_msg) +{ + struct mrt_msg * msg = pit_msg->msg; + uint collector_bgp_id_size = 4; + uint name_length_size = 2; + uint name_size = pit_msg->name_length; + uint peer_count_offset = collector_bgp_id_size + name_length_size + name_size; + return &(msg->msg[peer_count_offset]); +} + +static void +mrt_grow_msg_buffer(struct mrt_msg * msg, size_t min_required_capacity) +{ + msg->msg_capacity *= 2; + if (min_required_capacity > msg->msg_capacity) + msg->msg_capacity = min_required_capacity; + msg->msg = mb_realloc(msg->msg, msg->msg_capacity); +} + +static void +mrt_write_to_msg(struct mrt_msg * msg, const void *data, size_t data_size) +{ + if (data_size == 0) + return; + + u32 i; + for (i = 0; i < data_size; i++) + debug("%02X ", ((byte*)data)[i]); + debug("| "); + + size_t required_size = data_size + msg->msg_length; + if (msg->msg_capacity < required_size) + mrt_grow_msg_buffer(msg, required_size); + + memcpy(&msg->msg[msg->msg_length], data, data_size); + msg->msg_length += data_size; +} +#define mrt_write_to_msg_(msg, data) mrt_write_to_msg(msg, &data, sizeof(data)) + +void +mrt_peer_index_table_init(struct mrt_peer_index_table *pit_msg, u32 collector_bgp_id, const char *name) +{ + struct mrt_msg * msg = pit_msg->msg; + pit_msg->peer_count = 0; + pit_msg->name_length = strlen(name); + + mrt_write_to_msg_(msg, collector_bgp_id); + mrt_write_to_msg_(msg, pit_msg->name_length); + mrt_write_to_msg(msg, name, pit_msg->name_length); + mrt_write_to_msg_(msg, pit_msg->peer_count); + debug("\n"); +} + +static void +mrt_peer_index_table_inc_peer_count(struct mrt_peer_index_table *pit_msg) +{ + pit_msg->peer_count++; + byte *peer_count = mrt_peer_index_table_get_peer_count(pit_msg); + put_u16(peer_count, pit_msg->peer_count); +} + +void +mrt_peer_index_table_add_peer(struct mrt_peer_index_table *pit_msg, u32 peer_bgp_id, ip_addr *peer_ip_addr, u32 peer_as) +{ + struct mrt_msg * msg = pit_msg->msg; + + u8 peer_type = PEER_TYPE_AS_32BIT; + if (sizeof(*peer_ip_addr) > sizeof(ip4_addr)) + peer_type |= PEER_TYPE_IPV6; + + mrt_write_to_msg_(msg, peer_type); + mrt_write_to_msg_(msg, peer_bgp_id); + mrt_write_to_msg_(msg, *peer_ip_addr); + mrt_write_to_msg_(msg, peer_as); + + mrt_peer_index_table_inc_peer_count(pit_msg); + debug("\n"); +} + +void +mrt_rib_table_init(struct mrt_rib_table *rt_msg, u32 sequence_number, u8 prefix_length, ip_addr *prefix) +{ + struct mrt_msg *msg = rt_msg->msg; + + rt_msg->entry_count = 0; + + mrt_write_to_msg_(msg, sequence_number); + mrt_write_to_msg_(msg, prefix_length); + mrt_write_to_msg_(msg, *prefix); + mrt_write_to_msg_(msg, rt_msg->entry_count); + debug("\n"); +} + +static byte * +mrt_rib_table_get_entry_count(struct mrt_rib_table *rt_msg) +{ + struct mrt_msg *msg = rt_msg->msg; + u32 sequence_number_size = 4; + u32 prefix_length_size = 1; + + u32 prefix_size = 4; + if (rt_msg->type == RIB_IPV4_UNICAST) + prefix_size = 4; + else if (rt_msg->type == RIB_IPV6_UNICAST) + prefix_size = 16; + else + bug("mrt_rib_table_get_entry_count: unknown RIB type!"); + + u32 offset = sequence_number_size + prefix_length_size + prefix_size; + return &msg->msg[offset]; +} + +static void +mrt_rib_table_inc_entry_count(struct mrt_rib_table *rt_msg) +{ + rt_msg->entry_count++; + byte *entry_count = mrt_rib_table_get_entry_count(rt_msg); + put_u16(entry_count, rt_msg->entry_count); +} + +void +mrt_rib_table_add_entry(struct mrt_rib_table *rt_msg, const struct mrt_rib_entry *rib) +{ + struct mrt_msg *msg = rt_msg->msg; + + mrt_write_to_msg_(msg, rib->peer_index); + mrt_write_to_msg_(msg, rib->originated_time); + mrt_write_to_msg_(msg, rib->attributes_length); + mrt_write_to_msg(msg, rib->attributes, rib->attributes_length); + + mrt_rib_table_inc_entry_count(rt_msg); + debug("\n"); +} diff --git a/nest/mrtdump.h b/nest/mrtdump.h index 73932553..53d1c84b 100644 --- a/nest/mrtdump.h +++ b/nest/mrtdump.h @@ -1,31 +1,90 @@ /* - * BIRD -- MRTdump handling + * BIRD -- Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format * + * (c) 2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ -#ifndef MRTDUMP_H -#define MRTDUMP_H +#ifndef _MRTDUMP_H_ +#define _MRTDUMP_H_ + #include "nest/protocol.h" /* MRTDump values */ - #define MRTDUMP_HDR_LENGTH 12 +#define PEER_TYPE_AS_32BIT 0b00000010 /* MRT TABLE_DUMP_V2: PEER_INDEX_TABLE: Peer Type: Use 32bit ASN */ +#define PEER_TYPE_IPV6 0b00000001 /* MRT TABLE_DUMP_V2: PEER_INDEX_TABLE: Peer Type: Use IPv6 IP Address */ -/* MRTdump types */ +/* MRT Types */ +enum mrt_type +{ + TABLE_DUMP_V2 = 13, + BGP4MP = 16, +}; -#define BGP4MP 16 +/* MRT TABLE_DUMP_V2 Sub-Types */ +enum table_dump_v2_type +{ + PEER_INDEX_TABLE = 1, + RIB_IPV4_UNICAST = 2, + RIB_IPV4_MULTICAST = 3, + RIB_IPV6_UNICAST = 4, + RIB_IPV6_MULTICAST = 5, + RIB_GENERIC = 6, +}; -/* MRTdump subtypes */ +/* MRT BGP4MP Sub-Types */ +enum bgp4mp_subtype +{ + BGP4MP_MESSAGE = 1, + BGP4MP_MESSAGE_AS4 = 4, + BGP4MP_STATE_CHANGE_AS4 = 5, +}; -#define BGP4MP_MESSAGE 1 -#define BGP4MP_MESSAGE_AS4 4 -#define BGP4MP_STATE_CHANGE_AS4 5 +struct mrt_msg +{ + byte *msg; /* Buffer with final formatted data */ + size_t msg_length; /* Size of used buffer */ + size_t msg_capacity; /* Number of allocated bytes in msg */ +#define MRT_MSG_DEFAULT_CAPACITY 64 /* in bytes */ + pool *mem_pool; +}; +/* TABLE_DUMP_V2 -> PEER_INDEX_TABLE */ +struct mrt_peer_index_table +{ + struct mrt_msg *msg; + u16 peer_count; + u16 name_length; +}; + +/* TABLE_DUMP_V2 -> RIB_IPV4_UNICAST or RIB_IPV6_UNICAST */ +struct mrt_rib_table +{ + struct mrt_msg *msg; + enum table_dump_v2_type type; /* RIB_IPV4_UNICAST or RIB_IPV6_UNICAST */ + u16 entry_count; /* Number of RIB Entries */ + struct bgp_proto *bgp_proto; +}; + +/* TABLE_DUMP_V2 -> RIB Entry */ +struct mrt_rib_entry +{ + u16 peer_index; + u32 originated_time; + u16 attributes_length; + byte *attributes; +}; + +void mrt_msg_init(struct mrt_msg *msg, pool *mem_pool); +void mrt_msg_free(struct mrt_msg *msg); +void mrt_peer_index_table_init(struct mrt_peer_index_table *pit_msg, u32 collector_bgp_id, const char *name); +void mrt_peer_index_table_add_peer(struct mrt_peer_index_table *pit_msg, u32 peer_bgp_id, ip_addr *peer_ip_addr, u32 peer_as); +void mrt_rib_table_init(struct mrt_rib_table *rt_msg, u32 sequence_number, u8 prefix_length, ip_addr *prefix); +void mrt_rib_table_add_entry(struct mrt_rib_table *rt_msg, const struct mrt_rib_entry *rib); /* implemented in sysdep */ -void mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len); - -#endif +void mrt_dump_message(const struct proto *p, u16 type, u16 subtype, byte *buf, u32 len); +#endif /* _MRTDUMP_H_ */ diff --git a/nest/mrtdump_test.c b/nest/mrtdump_test.c new file mode 100644 index 00000000..df6ffd74 --- /dev/null +++ b/nest/mrtdump_test.c @@ -0,0 +1,115 @@ +/* + * BIRD -- Multi-Threaded Routing Toolkit (MRT) Routing Information Export Format Tests + * + * (c) 2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "test/birdtest.h" +#include "test/birdtest_support.h" /* REMOVE ME */ +#include "nest/mrtdump.h" +#include "nest/mrtdump.c" /* REMOVE ME */ + +static void +show_mrt_msg(struct mrt_msg *msg) +{ + uint i; + bt_debug("show_mrt_msg: \n "); + for(i = 0; i < msg->msg_length; i++) + { + if (i && (i % 16) == 0) + bt_debug("\n "); + bt_debug("%02X ", msg->msg[i]); + } + bt_debug("\n"); +} + +static int +t_peer_index_table(void) +{ + resource_init(); + + struct mrt_msg msg; + mrt_msg_init(&msg, &root_pool); + + struct mrt_peer_index_table pit_msg = { + .msg = &msg, + }; + u32 collector_bgp_id = 0x12345678; + const char *collector_name = "test"; + mrt_peer_index_table_init(&pit_msg, collector_bgp_id, collector_name); + + u32 i; + for(i = 0; i < 50; i++) + { + ip_addr addr; +#ifdef IPV6 + ip6_pton("1234:5678::9abc:def0", &addr); +#else + ip4_pton("12.34.56.78", &addr); +#endif + mrt_peer_index_table_add_peer(&pit_msg, i | 0x30303030, &addr, i | 0x08080808); + } + + show_mrt_msg(&msg); + + mrt_msg_free(&msg); + + return BT_SUCCESS; +} + +static int +t_rib_table(void) +{ + resource_init(); + + struct mrt_msg msg; + mrt_msg_init(&msg, &root_pool); + + struct mrt_rib_table rt_msg = { + .bgp_proto = NULL, + .msg = &msg, + }; + u32 sequence_number = 0x12345678; + u8 prefix_len = 24; + ip_addr prefix; +#ifdef IPV6 + rt_msg.type = RIB_IPV6_UNICAST; + ip6_pton("1234:5678::9abc:def0", &prefix); +#else + rt_msg.type = RIB_IPV4_UNICAST; + ip4_pton("12.34.56.78", &prefix); +#endif + mrt_rib_table_init(&rt_msg, sequence_number, prefix_len, &prefix); + + u32 i; + + for(i = 0; i < 50; i++) + { + struct mrt_rib_entry entry = { + .peer_index = i, + .originated_time = i | 0x08080808, + .attributes_length = 7, + .attributes = "abcdefg", + }; + mrt_rib_table_add_entry(&rt_msg, &entry); + } + + show_mrt_msg(&msg); + + mrt_msg_free(&msg); + + return BT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + bt_init(argc, argv); + + bt_test_suite(t_peer_index_table, "TABLE_DUMP_V2: Peer index table"); + bt_test_suite(t_rib_table, "TABLE_DUMP_V2: RIB table"); + + return bt_end(); +} diff --git a/nest/route.h b/nest/route.h index 3af75ad8..821f5cdb 100644 --- a/nest/route.h +++ b/nest/route.h @@ -72,13 +72,13 @@ void fib_delete(struct fib *, void *); /* Remove fib entry */ void fib_free(struct fib *); /* Destroy the fib */ void fib_check(struct fib *); /* Consistency check for debugging */ -void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */ -struct fib_node *fit_get(struct fib *, struct fib_iterator *); +void fit_init(struct fib_iterator *, const struct fib *); /* Internal functions, don't call */ +struct fib_node *fit_get(const struct fib *, struct fib_iterator *); void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_WALK(fib, z) do { \ struct fib_node *z, **ff = (fib)->hash_table; \ - uint count = (fib)->hash_size; \ + uint count = (fib)->hash_size; \ while (count--) \ for(z = *ff++; z; z=z->next) @@ -88,11 +88,11 @@ void fit_put(struct fib_iterator *, struct fib_node *); #define FIB_ITERATE_START(fib, it, z) do { \ struct fib_node *z = fit_get(fib, it); \ - uint count = (fib)->hash_size; \ - uint hpos = (it)->hash; \ + uint count = (fib)->hash_size; \ + uint hpos = (it)->hash; \ for(;;) { \ if (!z) \ - { \ + { \ if (++hpos >= count) \ break; \ z = (fib)->hash_table[hpos]; \ diff --git a/nest/rt-fib.c b/nest/rt-fib.c index aa5e2357..ec77311c 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -73,7 +73,7 @@ fib_ht_free(struct fib_node **h) } static inline unsigned -fib_hash(struct fib *f, ip_addr *a) +fib_hash(const struct fib *f, ip_addr *a) { return ipa_hash(*a) >> f->hash_shift; } @@ -368,7 +368,7 @@ fib_free(struct fib *f) } void -fit_init(struct fib_iterator *i, struct fib *f) +fit_init(struct fib_iterator *i, const struct fib *f) { unsigned h; struct fib_node *n; @@ -390,7 +390,7 @@ fit_init(struct fib_iterator *i, struct fib *f) } struct fib_node * -fit_get(struct fib *f, struct fib_iterator *i) +fit_get(const struct fib *f, struct fib_iterator *i) { struct fib_node *n; struct fib_iterator *j, *k; diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 7cb26360..6d623f95 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -316,7 +316,7 @@ log_init_debug(char *f) } void -mrt_dump_message(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len) +mrt_dump_message(const struct proto *p, u16 type, u16 subtype, byte *buf, u32 len) { /* Prepare header */ put_u32(buf+0, now_real); diff --git a/test/birdtest_support.h b/test/birdtest_support.h index 8fb76fec..753cb590 100644 --- a/test/birdtest_support.h +++ b/test/birdtest_support.h @@ -1,12 +1,14 @@ #include "sysdep/config.h" -#include "lib/event.c" /* REMOVE ME */ +#include "lib/event.c" /* REMOVE ME */ #include "lib/ip.c" /* REMOVE ME */ #include "lib/resource.c" /* REMOVE ME */ #include "lib/printf.c" /* REMOVE ME */ #include "lib/xmalloc.c" /* REMOVE ME */ #include "lib/bitops.c" /* REMOVE ME */ +#include "lib/mempool.c" /* REMOVE ME */ -#define bug(msg, ...) debug("BUG: " msg, ##__VA_ARGS__) +#define bug(msg, ...) debug("BUG: " msg, ##__VA_ARGS__) +#define log_msg(msg, ...) debug("LOG_MSG: " msg, ##__VA_ARGS__) void debug(const char *msg, ...) @@ -32,3 +34,5 @@ io_log_event(void *hook, void *data) { bt_debug("This is io_log_event mockup. \n"); }; + +#include "lib/slab.c" /* REMOVE ME */