0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 09:58:43 +00:00

Integrate MRT Table Dump (RFC 6396) into BIRD

Asynchronous periodic mrt table dumps of routes configuration in bird.conf:
mrtdump routes {
  [ filename "<format>"; ]
  [ table <name>|"<wildcard name>"; ]
  [ period <num>; ]
  [ filter <filter>|where <condition>; ]
}

Synchronous mrt table dumps of routes in BIRD client:
mrtdump routes [table <name>|"<wildcard name>"] [to "<filename format>"] [filter <filter>|where <condition>]
This commit is contained in:
Pavel Tvrdík 2015-05-26 12:40:40 +02:00
parent de77e5cdd0
commit 11fabd2d6b
26 changed files with 1080 additions and 251 deletions

View File

@ -53,6 +53,7 @@
#include "lib/string.h"
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/unix.h"
#include "conf/conf.h"
#include "filter/filter.h"
@ -91,7 +92,7 @@ config_alloc(byte *name)
linpool *l = lp_new(p, 4080);
struct config *c = lp_allocz(l, sizeof(struct config));
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
c->mrt_proto_file = -1; /* Indication that the file descriptor should not be used */
c->pool = p;
cfg_mem = c->mem = l;
c->file_name = cfg_strdup(name);

View File

@ -14,8 +14,36 @@
#include "lib/resource.h"
#include "lib/timer.h"
/* Configuration structures */
/* Configuration structure */
#define MRT_TABLE_NOT_CONFIGURED -1
#define MRT_TABLE_DEFAULT_PERIOD 60
#define MRT_TABLE_DEFAULT_FILENAME_FMT "%f_%F_%T.mrt"
#define MRT_TABLE_DEFAULT_TABLENAME_PATTERN "*"
struct mrt_table_common_config {
u32 period; /* Time in seconds between Table Dump */
char *filename_fmt;
struct filter *filter;
struct cli *cli; /* Client console or NULL */
struct config *config; /* Configuration or NULL */
};
/* Template of configuration that can be apply at any table */
struct mrt_table_config {
node n; /* Node in config->mrt_table_dumps */
struct mrt_table_common_config c;
char *rtable_wildcard_name;
};
/* Configuration that is specific per table */
struct mrt_table_individual_config {
node n; /* Node in rtable_config->mrt_table_dumps */
struct mrt_table_common_config c;
timer *timer; /* Timer for periodic dumps */
struct rtable_config *table_cf;
bird_clock_t next_dump;
};
struct config {
pool *pool; /* Pool the configuration is stored in */
@ -23,9 +51,10 @@ struct config {
list protos; /* Configured protocol instances (struct proto_config) */
list tables; /* Configured routing tables (struct rtable_config) */
list roa_tables; /* Configured ROA tables (struct roa_table_config) */
list logfiles; /* Configured log fils (sysdep) */
list logfiles; /* Configured log files (sysdep) */
list mrt_table_dumps; /* Configured MRT table dumps (struct mrt_table_config) */
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
int mrt_proto_file; /* Configured MRTDump file (sysdep, fd in unix) for Protocols*/
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
struct rtable_config *master_rtc; /* Configuration of master routing table */
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */

View File

@ -362,16 +362,49 @@ protocol rip {
seconds, zero means disabled. Default: disabled (0).
<tag>mrtdump "<m/filename/"</tag>
Set MRTdump file name. This option must be specified to allow MRTdump
feature. Default: no dump file.
Set MRTdump protocols file name. This option must be specified to allow MRTdump
protocols feature. Default: no dump file.
<tag>mrtdump protocols all|off|{ states, messages }</tag>
Set global defaults of MRTdump options. See <cf/mrtdump/ in the
Set global defaults of MRTdump protocol options. See <ref id="protocol-mrtdump" name="mrtdump"> in the
following section. Default: off.
<tag>filter <m/name local variables/{ <m/commands/ }</tag>
Define a filter. You can learn more about filters in the following
chapter.
<tag><label id="dsc-mrtdump-routes">mrtdump routes [ { <m/option1; option2; .../ } ]</tag>
Configure periodic <htmlurl url="https://tools.ietf.org/html/rfc6396#section-4.3" name="MRT Table Dump v2 (RFC 6396)">
of routes (not only BGP).
All mrtdump options are optional. Default: disabled
<descrip>
<tag/ <cf>filename "<m/format/" </cf> /
Configure file to output.
Filename format is the <it/strftime(3)/-like format (see
<it/man strftime/ for details) extended of a key <cf/%f/ for replacing with the name of the table.
Default: <cf>"%f_%F_%T.mrt"</cf>
<tag/ <cf>table <m/name/|"<m/pattern/" </cf> /
Choosing tables from which will be routes taken.
Can be used wildcards <cf>*</cf> and <cf>?</cf>.
Default: <cf>"*"</cf> for all tables
<tag/ <cf>period <m/num/ </cf> /
Configures the interval in seconds between mrt table dumps.
Default: <cf>60</cf> seconds
<tag/ <cf>filter <m/filter/ | where <m/condition/</cf> /
Configure filtering dumping routes. Filters are more described in <ref id="chapt-filters" name="the chapter Filters">.
Default: Accept all
</descrip>
Example:
<p><code>
mrtdump routes {
filename "%f-%s.mrt"; # dumps will be saved into files named like 'tableA-1437119435.mrt'
table "t*"; # only routes from tables starts with character 't'
period 10; # 10 seconds interval between dumps
where source = RTS_BGP; # only routes from BGP protocols
}
mrtdump routes; # another mrtdump route config with default options</code>
<tag>filter <m/name local variables/ { <m/commands/ }</tag>
Define a filter. You can learn more about filters in <ref id="chapt-filters" name="the chapter Filters">.
<tag>function <m/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag>
Define a function. You can learn more about functions in the following chapter.
@ -453,13 +486,13 @@ protocol rip {
hh:mm:ss) for <cf/base/ and <cf/log/. These timeformats could be set by
<cf/old short/ and <cf/old long/ compatibility shorthands.
<tag>table <m/name/ [sorted]</tag>
Create a new routing table. The default routing table is created
<tag>table <m/name/ [sorted] </tag>
Create a new routing table. The default <cf/master/ routing table is created
implicitly, other routing tables have to be added by this command.
Option <cf/sorted/ can be used to enable sorting of routes, see
<ref id="dsc-sorted" name="sorted table"> description for details.
<tag>roa table <m/name/ [ { roa table options ... } ]</tag>
<tag>roa table <m/name/ [ { <m/roa table options/ } ]</tag>
Create a new ROA (Route Origin Authorization) table. ROA tables can be
used to validate route origination of BGP routes. A ROA table contains
ROA entries, each consist of a network prefix, a max prefix length and
@ -511,8 +544,9 @@ agreement").
protocol, <cf/events/ for events internal to the protocol and <cf/packets/
for packets sent and received by the protocol. Default: off.
<tag>mrtdump all|off|{ states, messages }</tag>
Set protocol MRTdump flags. MRTdump is a standard binary format for
<tag><label id="protocol-mrtdump">mrtdump all|off|{ states, messages }</tag>
Set protocol MRTdump flags. MRTdump is a standard binary format (see
<htmlurl url="https://tools.ietf.org/html/rfc6396#section-4.4" name="BGP4MP in RFC 6396">)) for
logging information from routing protocols and daemons. These flags
control what kind of information is logged from the protocol to the
MRTdump file (which must be specified by global <cf/mrtdump/ option, see
@ -752,7 +786,7 @@ This argument can be omitted if there exists only a single instance.
Show the list of symbols defined in the configuration (names of
protocols, routing tables etc.).
<tag>show route [[for] <m/prefix/|<m/IP/] [table <m/sym/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [<m/options/]</tag>
<tag>show route [[for] <m/prefix/|<m/IP/] [table <m/name/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [<m/options/]</tag>
Show contents of a routing table (by default of the main one or the
table attached to a respective protocol), that is routes, their metrics
and (in case the <cf/all/ switch is given) all their attributes.
@ -787,7 +821,7 @@ This argument can be omitted if there exists only a single instance.
number of networks, number of routes before and after filtering). If
you use <cf/count/ instead, only the statistics will be printed.
<tag>show roa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/t/>]</tag>
<tag>show roa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/name/]</tag>
Show contents of a ROA table (by default of the first one). You can
specify a <m/prefix/ to print ROA entries for a specific network. If you
use <cf>for <m/prefix/</cf>, you'll get all entries relevant for route
@ -796,16 +830,16 @@ This argument can be omitted if there exists only a single instance.
entries covered by the network prefix. You could also use <cf/as/ option
to show just entries for given AS.
<tag>add roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag>
<tag>add roa <m/prefix/ max <m/num/ as <m/num/ [table <m/name/]</tag>
Add a new ROA entry to a ROA table. Such entry is called <it/dynamic/
compared to <it/static/ entries specified in the config file. These
dynamic entries survive reconfiguration.
<tag>delete roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag>
<tag>delete roa <m/prefix/ max <m/num/ as <m/num/ [table <m/name/]</tag>
Delete the specified ROA entry from a ROA table. Only dynamic ROA
entries (i.e., the ones added by <cf/add roa/ command) can be deleted.
<tag>flush roa [table <m/t/>]</tag>
<tag>flush roa [table <m/name/]</tag>
Remove all dynamic ROA entries from a ROA table.
<tag>configure [soft] ["<m/config file/"] [timeout [<m/num/]]</tag>
@ -873,6 +907,11 @@ This argument can be omitted if there exists only a single instance.
<tag>dump resources|sockets|interfaces|neighbors|attributes|routes|protocols</tag>
Dump contents of internal data structures to the debugging output.
<tag>mrtdump routes [table <m/name/|"<m/pattern/"] [to "<m/filename/"] [filter <m/filter/ | where <m/condition/]</tag>
Once the specified routes will be saved into file(s) named based on <m/filename/ format.
For more details about mrtdump options see <ref id="dsc-mrtdump-routes" name="mrtdump routes in the chapter Configuration">.
<tag>echo all|off|{ <m/list of log classes/ } [ <m/buffer-size/ ]</tag>
Control echoing of log messages to the command-line output.
See <ref id="dsc-log" name="log option"> for a list of log classes.
@ -882,7 +921,7 @@ This argument can be omitted if there exists only a single instance.
</descrip>
<chapt>Filters
<chapt><label id="chapt-filters">Filters
<sect>Introduction

View File

@ -16,7 +16,7 @@
#endif
int
MATCH_FUNC_NAME(byte *p, byte *s)
MATCH_FUNC_NAME(const byte *p, const byte *s)
{
while (*p)
{

View File

@ -24,6 +24,6 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);
int patmatch(byte *pat, byte *str);
int patmatch(const byte *pat, const byte *str);
#endif

View File

@ -1,5 +1,5 @@
source=rt-table.c rt-fib.c rt-attr.c rt-roa.c proto.c iface.c rt-dev.c password.c cli.c locks.c cmds.c neighbor.c \
a-path.c a-set.c
a-path.c a-set.c mrtdump.c
root-rel=../
dir-name=nest

View File

@ -23,6 +23,7 @@ static struct roa_table_config *this_roa_table;
static list *this_p_list;
static struct password_item *this_p_item;
static int password_id;
static struct mrt_table_config *this_mrt_table_config;
static void
iface_patt_check(void)
@ -60,7 +61,7 @@ CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFA
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE, ROA)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS, PERIOD, FILENAME)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@ -80,6 +81,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode roa_mode limit_action tab_sorted tos
%type <ps> proto_patt proto_patt2
%type <g> limit_spec
%type <t> mrtdump_table_config_table_option
CF_GRAMMAR
@ -87,7 +89,7 @@ CF_GRAMMAR
CF_ADDTO(conf, rtrid)
rtrid:
rtrid:
ROUTER ID idval ';' { new_config->router_id = $3; }
| ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; }
;
@ -114,7 +116,7 @@ listen_opts:
| listen_opts listen_opt
;
listen_opt:
listen_opt:
ADDRESS ipa { new_config->listen_bgp_addr = $2; }
| PORT expr { new_config->listen_bgp_port = $2; }
| V6ONLY { new_config->listen_bgp_flags = 0; }
@ -280,7 +282,7 @@ iface_negate:
;
iface_patt_node:
iface_patt_node_init iface_negate iface_patt_node_body
iface_patt_node_init iface_negate iface_patt_node_body
;
@ -358,7 +360,7 @@ debug_flag:
| PACKETS { $$ = D_PACKETS; }
;
/* MRTDump flags */
/* MRTDump Protocol flags */
mrtdump_mask:
ALL { $$ = ~0; }
@ -376,6 +378,50 @@ mrtdump_flag:
| MESSAGES { $$ = MD_MESSAGES; }
;
/* MRT Table Dump */
mrtdump_table_config_init:
{
this_mrt_table_config = mrt_table_new_config();
}
;
mrtdump_table_config:
'{' mrtdump_table_config_init mrtdump_table_config_list '}'
| mrtdump_table_config_init ';'
;
mrtdump_table_filter_option:
where_filter {
if (this_mrt_table_config->c.filter != FILTER_ACCEPT)
cf_error("Filter specified twice");
this_mrt_table_config->c.filter = $1;
}
| FILTER filter {
if (this_mrt_table_config->c.filter != FILTER_ACCEPT)
cf_error("Filter specified twice");
this_mrt_table_config->c.filter = $2;
}
;
mrtdump_table_config_list:
/* empty */
| mrtdump_table_config_option ';'
| mrtdump_table_config_list mrtdump_table_config_option ';'
;
mrtdump_table_config_table_option:
rtable { $$ = $1->name; }
| TEXT { $$ = $1; }
;
mrtdump_table_config_option:
PERIOD expr { this_mrt_table_config->c.period = $2; }
| FILENAME text { this_mrt_table_config->c.filename_fmt = $2; }
| TABLE mrtdump_table_config_table_option { this_mrt_table_config->rtable_wildcard_name = $2; }
| mrtdump_table_filter_option
;
/* Password lists */
password_list:
@ -383,7 +429,7 @@ password_list:
| password_item
;
password_items:
password_items:
/* empty */
| password_item ';' password_items
;
@ -412,7 +458,7 @@ password_item_begin:
;
password_item_params:
/* empty */ { }
/* empty */ { }
| GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; }
| GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; }
| ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; }
@ -588,7 +634,7 @@ sym_args:
roa_table_arg:
/* empty */ {
/* empty */ {
if (roa_table_default == NULL)
cf_error("No ROA table defined");
$$ = roa_table_default;
@ -684,6 +730,30 @@ CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]])
{ proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
CF_CLI_HELP(MRTDUMP ROUTES, [table <name>|\"<pattern>\"] [to \"<file>\"] [filter <filter>|where <where filter>] , [[Save MRT Table Dump into a file]])
CF_CLI(MRTDUMP ROUTES, mrtdump_table_cli_config, [table <name>|\"<pattern>\"] [to \"<file>\"] [filter <filter>|where <where filter>], [[Save mrt table dump v2 of table name <t> right now]])
{
this_mrt_table_config->c.cli = this_cli;
this_mrt_table_config->c.config = NULL;
mrt_table_cli_cmd(this_mrt_table_config);
} ;
mrtdump_table_cli_config:
mrtdump_table_config_init mrtdump_table_cli_config_list
;
mrtdump_table_cli_config_list:
/* empty */
| mrtdump_table_cli_config_option
| mrtdump_table_cli_config_list mrtdump_table_cli_config_option
;
mrtdump_table_cli_config_option:
TABLE mrtdump_table_config_table_option { this_mrt_table_config->rtable_wildcard_name = $2; }
| TO text { this_mrt_table_config->c.filename_fmt = $2; }
| mrtdump_table_filter_option
;
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;

View File

@ -6,154 +6,259 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
#include "nest/mrtdump.h"
#include "nest/route.h"
void
mrt_msg_init(struct mrt_msg *msg, pool *mem_pool)
/*
* MRTDump: Table Dump: Base
*/
static void
mrt_buffer_reset(struct mrt_buffer *buf)
{
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);
buf->msg_capacity = MRT_BUFFER_DEFAULT_CAPACITY;
buf->msg_length = MRT_HDR_LENGTH; /* Reserved for the main MRT header */
}
void
mrt_msg_free(struct mrt_msg *msg)
mrt_buffer_alloc(struct mrt_buffer *buf)
{
mb_free(msg->msg);
mrt_buffer_reset(buf);
buf->msg = mb_allocz(&root_pool, buf->msg_capacity);
}
void
mrt_buffer_free(struct mrt_buffer *buf)
{
if (buf->msg != NULL)
{
mb_free(buf->msg);
buf->msg = NULL;
}
}
static void
mrt_buffer_enlarge(struct mrt_buffer *buf, size_t min_required_capacity)
{
if (min_required_capacity < buf->msg_capacity)
{
buf->msg_capacity *= 2;
if (min_required_capacity > buf->msg_capacity)
buf->msg_capacity = min_required_capacity;
buf->msg = mb_realloc(buf->msg, buf->msg_capacity);
}
}
/*
* Return pointer to the actual position in the msg buffer
*/
static byte *
mrt_peer_index_table_get_peer_count(struct mrt_peer_index_table *pit_msg)
mrt_buffer_get_cursor(struct mrt_buffer *buf)
{
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]);
return &buf->msg[buf->msg_length];
}
static void
mrt_grow_msg_buffer(struct mrt_msg * msg, size_t min_required_capacity)
mrt_buffer_write_show_debug(struct mrt_buffer *buf, size_t data_size)
{
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);
#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
byte *data = mrt_buffer_get_cursor(buf) - data_size;
#endif
DBG("(%d) ", data_size);
u32 i;
for (i = 0; i < data_size; i++)
DBG("%02X ", data[i]);
DBG("| ");
}
static void
mrt_write_to_msg(struct mrt_msg * msg, const void *data, size_t data_size)
mrt_buffer_put_raw(struct mrt_buffer *buf, 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 + buf->msg_length;
mrt_buffer_enlarge(buf, required_size);
size_t required_size = data_size + msg->msg_length;
if (msg->msg_capacity < required_size)
mrt_grow_msg_buffer(msg, required_size);
memcpy(mrt_buffer_get_cursor(buf), data, data_size);
buf->msg_length += data_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");
mrt_buffer_write_show_debug(buf, data_size);
}
static void
mrt_peer_index_table_inc_peer_count(struct mrt_peer_index_table *pit_msg)
mrt_buffer_put_ipa(struct mrt_buffer *buf, ip_addr addr, size_t write_size)
{
pit_msg->peer_count++;
byte *peer_count = mrt_peer_index_table_get_peer_count(pit_msg);
put_u16(peer_count, pit_msg->peer_count);
ip_addr addr_network_formatted = ipa_hton(addr);
mrt_buffer_put_raw(buf, &addr_network_formatted, write_size);
}
/*
* The data will be transformed (put_u16(), put_u32(), ...) to the network format before writing
*/
static void
mrt_buffer_put_var(struct mrt_buffer *buf, const void *data, size_t data_size)
{
if (data_size == 0)
return;
byte *actual_position;
size_t required_size = data_size + buf->msg_length;
mrt_buffer_enlarge(buf, required_size);
switch (data_size)
{
case 8:
put_u64(mrt_buffer_get_cursor(buf), *(u64*)data);
break;
case 4:
put_u32(mrt_buffer_get_cursor(buf), *(u32*)data);
break;
case 2:
put_u16(mrt_buffer_get_cursor(buf), *(u16*)data);
break;
case 1:
actual_position = mrt_buffer_get_cursor(buf);
*actual_position = *(byte*)data;
break;
default:
log(L_WARN "Unexpected size %zu byte(s) of data. Allowed are 1, 2, 4 or 8 bytes.", data_size);
}
buf->msg_length += data_size;
mrt_buffer_write_show_debug(buf, data_size);
}
#define mrt_buffer_put_var_autosize(msg, data) mrt_buffer_put_var(msg, &data, sizeof(data))
/*
* MRTDump: Table Dump: Peer Index Table
*/
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)
mrt_peer_index_table_header(struct mrt_peer_index_table *state, u32 collector_bgp_id, const char *name)
{
struct mrt_msg * msg = pit_msg->msg;
struct mrt_buffer *buf = &state->msg;
mrt_buffer_alloc(buf);
u8 peer_type = PEER_TYPE_AS_32BIT;
if (sizeof(*peer_ip_addr) > sizeof(ip4_addr))
peer_type |= PEER_TYPE_IPV6;
state->peer_count = 0;
u16 name_length = 0;
if (name != NULL)
name_length = strlen(name);
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];
mrt_buffer_put_var_autosize(buf, collector_bgp_id);
mrt_buffer_put_var_autosize(buf, name_length);
mrt_buffer_put_raw(buf, name, name_length);
state->peer_count_offset = state->msg.msg_length;
mrt_buffer_put_var(buf, &state->peer_count, sizeof(u16));
DBG("\n");
}
static void
mrt_rib_table_inc_entry_count(struct mrt_rib_table *rt_msg)
mrt_peer_index_table_inc_peer_count(struct mrt_peer_index_table *state)
{
rt_msg->entry_count++;
byte *entry_count = mrt_rib_table_get_entry_count(rt_msg);
put_u16(entry_count, rt_msg->entry_count);
state->peer_count++;
byte *peer_count = &state->msg.msg[state->peer_count_offset];
put_u16(peer_count, state->peer_count);
}
void
mrt_rib_table_add_entry(struct mrt_rib_table *rt_msg, const struct mrt_rib_entry *rib)
mrt_peer_index_table_add_peer(struct mrt_peer_index_table *state, u32 peer_bgp_id, u32 peer_as, ip_addr peer_ip_addr)
{
struct mrt_msg *msg = rt_msg->msg;
struct mrt_buffer *msg = &state->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);
u8 peer_type = MRT_PEER_TYPE_32BIT_ASN;
if (sizeof(peer_ip_addr) > sizeof(ip4_addr))
peer_type |= MRT_PEER_TYPE_IPV6;
mrt_rib_table_inc_entry_count(rt_msg);
debug("\n");
mrt_buffer_put_var_autosize(msg, peer_type);
mrt_buffer_put_var_autosize(msg, peer_bgp_id);
mrt_buffer_put_ipa(msg, peer_ip_addr, sizeof(ip_addr));
mrt_buffer_put_var_autosize(msg, peer_as);
mrt_peer_index_table_inc_peer_count(state);
DBG("\n");
}
void
mrt_peer_index_table_dump(struct mrt_peer_index_table *state, int file_descriptor)
{
byte *msg = state->msg.msg;
u32 msg_length = state->msg.msg_length;
mrt_dump_message(file_descriptor, MRT_TABLE_DUMP_V2, MRT_PEER_INDEX_TABLE, msg, msg_length);
}
void
bgp_mrt_peer_index_table_free(struct mrt_peer_index_table *state)
{
mrt_buffer_free(&state->msg);
}
/*
* MRTDump: Table Dump: RIB Table
*/
static void
mrt_rib_table_reset(struct mrt_rib_table *state)
{
state->entry_count = 0;
state->entry_count_offset = 0;
state->subtype = MRT_RIB_IPV4_UNICAST;
mrt_buffer_reset(&state->msg);
}
void
mrt_rib_table_alloc(struct mrt_rib_table *state)
{
mrt_buffer_alloc(&state->msg);
mrt_rib_table_reset(state);
}
void
mrt_rib_table_header(struct mrt_rib_table *state, u32 sequence_number, u8 prefix_length, ip_addr prefix)
{
mrt_rib_table_reset(state);
#ifdef IPV6
state->subtype = MRT_RIB_IPV6_UNICAST;
#else
state->subtype = MRT_RIB_IPV4_UNICAST;
#endif
struct mrt_buffer *msg = &state->msg;
mrt_buffer_put_var_autosize(msg, sequence_number);
mrt_buffer_put_var_autosize(msg, prefix_length);
#define CEILING(a, b) (((a)+(b)-1) / (b))
u32 prefix_bytes = CEILING(prefix_length, 8);
mrt_buffer_put_ipa(msg, prefix, prefix_bytes);
state->entry_count_offset = msg->msg_length;
mrt_buffer_put_var_autosize(msg, state->entry_count);
DBG("\n");
}
static void
mrt_rib_table_inc_entry_count(struct mrt_rib_table *state)
{
state->entry_count++;
byte *entry_count = &state->msg.msg[state->entry_count_offset];
put_u16(entry_count, state->entry_count);
}
void
mrt_rib_table_add_entry(struct mrt_rib_table *state, const struct mrt_rib_entry *entry)
{
struct mrt_buffer *msg = &state->msg;
mrt_buffer_put_var_autosize(msg, entry->peer_index);
mrt_buffer_put_var_autosize(msg, entry->originated_time);
mrt_buffer_put_var_autosize(msg, entry->attributes_length);
mrt_buffer_put_raw(msg, entry->attributes, entry->attributes_length);
mrt_rib_table_inc_entry_count(state);
DBG("\n");
}

View File

@ -6,85 +6,101 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _MRTDUMP_H_
#define _MRTDUMP_H_
#ifndef _BIRD_MRTDUMP_H_
#define _BIRD_MRTDUMP_H_
#include <limits.h>
#include "nest/protocol.h"
#include "lib/lists.h"
#include "nest/route.h"
#include "lib/event.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 */
#define MRT_HDR_LENGTH 12 /* MRT Timestamp + MRT Type + MRT Subtype + MRT Load Length */
#define MRT_PEER_TYPE_32BIT_ASN 0b00000010 /* MRT Table Dump: Peer Index Table: Peer Type: Use 32bit ASN */
#define MRT_PEER_TYPE_IPV6 0b00000001 /* MRT Table Dump: Peer Index Table: Peer Type: Use IPv6 IP Address */
#ifdef PATH_MAX
#define BIRD_PATH_MAX PATH_MAX
#else
#define BIRD_PATH_MAX 4096
#endif
/* MRT Types */
enum mrt_type
{
TABLE_DUMP_V2 = 13,
BGP4MP = 16,
};
#define MRT_TABLE_DUMP_V2 13
#define MRT_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,
};
/* MRT Table Dump v2 Subtypes */
#define MRT_PEER_INDEX_TABLE 1
#define MRT_RIB_IPV4_UNICAST 2
#define MRT_RIB_IPV4_MULTICAST 3
#define MRT_RIB_IPV6_UNICAST 4
#define MRT_RIB_IPV6_MULTICAST 5
#define MRT_RIB_GENERIC 6
/* MRT BGP4MP Sub-Types */
enum bgp4mp_subtype
{
BGP4MP_MESSAGE = 1,
BGP4MP_MESSAGE_AS4 = 4,
BGP4MP_STATE_CHANGE_AS4 = 5,
};
/* MRT BGP4MP Subtypes */
#define MRT_BGP4MP_MESSAGE 1
#define MRT_BGP4MP_MESSAGE_AS4 4
#define MRT_BGP4MP_STATE_CHANGE_AS4 5
struct mrt_msg
struct mrt_buffer
{
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;
#define MRT_BUFFER_DEFAULT_CAPACITY 64 /* Size in bytes */
};
/* TABLE_DUMP_V2 -> PEER_INDEX_TABLE */
struct mrt_peer_index_table
{
struct mrt_msg *msg;
u16 peer_count;
u16 name_length;
struct mrt_buffer msg;
u16 peer_count; /* Datatype u16 should fit with the size 16bit in MRT packet */
u32 peer_count_offset;
};
/* 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 */
struct mrt_buffer msg;
int subtype; /* RIB_IPV4_UNICAST or RIB_IPV6_UNICAST */
u16 entry_count; /* Number of RIB Entries */
struct bgp_proto *bgp_proto;
u32 entry_count_offset; /* Offset in msg->msg[?] to position where start the entries count */
};
/* TABLE_DUMP_V2 -> RIB Entry */
struct mrt_rib_entry
{
u16 peer_index;
u32 originated_time;
u16 attributes_length;
byte *attributes;
byte *attributes; /* encoded BGP 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);
struct mrt_table_dump_ctx {
struct rtable *rtable;
struct fib_iterator fit;
struct mrt_rib_table rib_table;
u32 rib_sequence_number;
struct rfile *rfile; /* tracking for mrt table dump file */
char *file_path; /* full path for mrt table dump file */
byte state;
#define MRT_STATE_RUNNING 0
#define MRT_STATE_COMPLETED 1
event *step;
struct mrt_table_individual_config config; /* Own special configuration of MRT */
};
void mrt_buffer_alloc(struct mrt_buffer *buf);
void mrt_buffer_free(struct mrt_buffer *buf);
void mrt_peer_index_table_header(struct mrt_peer_index_table *state, u32 collector_bgp_id, const char *name);
void mrt_peer_index_table_add_peer(struct mrt_peer_index_table *state, u32 peer_bgp_id, u32 peer_as, ip_addr peer_ip_addr);
void mrt_peer_index_table_dump(struct mrt_peer_index_table *state, int file_descriptor);
void mrt_rib_table_alloc(struct mrt_rib_table *state);
void mrt_rib_table_header(struct mrt_rib_table *state, u32 sequence_number, u8 prefix_length, ip_addr prefix);
void mrt_rib_table_add_entry(struct mrt_rib_table *state, const struct mrt_rib_entry *entry);
/* implemented in sysdep */
void mrt_dump_message(const struct proto *p, u16 type, u16 subtype, byte *buf, u32 len);
void mrt_dump_message_proto(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len);
void mrt_dump_message(int file_descriptor, u16 type, u16 subtype, byte *buf, u32 len);
#endif /* _MRTDUMP_H_ */
#endif /* _BIRD_MRTDUMP_H_ */

View File

@ -9,14 +9,15 @@
#include "test/birdtest.h"
#include "test/birdtest_support.h" /* REMOVE ME */
#include "nest/mrtdump.h"
#include "nest/rt-fib.c" /* REMOVE ME */
#include "nest/mrtdump.c" /* REMOVE ME */
static void
show_mrt_msg(struct mrt_msg *msg)
show_mrt_msg(struct mrt_buffer *msg)
{
uint i;
bt_debug("show_mrt_msg: \n ");
for(i = 0; i < msg->msg_length; i++)
for (i = 0; i < msg->msg_length; i++)
{
if (i && (i % 16) == 0)
bt_debug("\n ");
@ -25,23 +26,27 @@ show_mrt_msg(struct mrt_msg *msg)
bt_debug("\n");
}
static void
compare(const byte *expected, const byte *data, u32 length)
{
u32 i;
for(i = 0; i < length; i++)
bt_assert_msg(expected[i] == data[i], "Different at %d, 0x%02X != 0x%02X", i, expected[i], data[i]);
}
static int
t_peer_index_table(void)
{
resource_init();
struct mrt_msg msg;
mrt_msg_init(&msg, &root_pool);
struct mrt_peer_index_table state;
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);
mrt_peer_index_table_header(&state, collector_bgp_id, collector_name);
u32 i;
for(i = 0; i < 50; i++)
for (i = 0; i < 5; i++)
{
ip_addr addr;
#ifdef IPV6
@ -49,13 +54,36 @@ t_peer_index_table(void)
#else
ip4_pton("12.34.56.78", &addr);
#endif
mrt_peer_index_table_add_peer(&pit_msg, i | 0x30303030, &addr, i | 0x08080808);
mrt_peer_index_table_add_peer(&state, i | 0x30303030, i | 0x08080808, addr);
}
show_mrt_msg(&msg);
show_mrt_msg(&state.msg);
mrt_msg_free(&msg);
const byte expected[] = {
/* Reserve for the main MRT Header */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 12 bytes */
/* PEER_INDEX_TABLE Header */
0x12, 0x34, 0x56, 0x78, 0x00, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x05, /* 12 bytes */
/* Peer Entries */
#ifdef IPV6
0x03, 0x30, 0x30, 0x30, 0x30, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBC, 0xDE, 0xF0, 0x08, 0x08, 0x08, 0x08,
0x03, 0x30, 0x30, 0x30, 0x31, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBC, 0xDE, 0xF0, 0x08, 0x08, 0x08, 0x09,
0x03, 0x30, 0x30, 0x30, 0x32, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBC, 0xDE, 0xF0, 0x08, 0x08, 0x08, 0x0A,
0x03, 0x30, 0x30, 0x30, 0x33, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBC, 0xDE, 0xF0, 0x08, 0x08, 0x08, 0x0B,
0x03, 0x30, 0x30, 0x30, 0x34, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBC, 0xDE, 0xF0, 0x08, 0x08, 0x08, 0x0C,
#else
0x02, 0x30, 0x30, 0x30, 0x30, 0x0C, 0x22, 0x38, 0x4E, 0x08, 0x08, 0x08, 0x08,
0x02, 0x30, 0x30, 0x30, 0x31, 0x0C, 0x22, 0x38, 0x4E, 0x08, 0x08, 0x08, 0x09,
0x02, 0x30, 0x30, 0x30, 0x32, 0x0C, 0x22, 0x38, 0x4E, 0x08, 0x08, 0x08, 0x0A,
0x02, 0x30, 0x30, 0x30, 0x33, 0x0C, 0x22, 0x38, 0x4E, 0x08, 0x08, 0x08, 0x0B,
0x02, 0x30, 0x30, 0x30, 0x34, 0x0C, 0x22, 0x38, 0x4E, 0x08, 0x08, 0x08, 0x0C,
#endif
};
bt_assert(state.msg.msg_length == sizeof(expected));
compare(expected, state.msg.msg, sizeof(expected));
mrt_buffer_free(&state.msg);
return BT_SUCCESS;
}
@ -64,28 +92,20 @@ 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,
};
struct mrt_rib_table state;
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);
mrt_rib_table_alloc(&state);
mrt_rib_table_header(&state, sequence_number, prefix_len, prefix);
u32 i;
for(i = 0; i < 50; i++)
for(i = 0; i < 5; i++)
{
struct mrt_rib_entry entry = {
.peer_index = i,
@ -93,13 +113,32 @@ t_rib_table(void)
.attributes_length = 7,
.attributes = "abcdefg",
};
mrt_rib_table_add_entry(&rt_msg, &entry);
mrt_rib_table_add_entry(&state, &entry);
}
show_mrt_msg(&msg);
show_mrt_msg(&state.msg);
mrt_msg_free(&msg);
const byte expected[] = {
/* Reserve for the main MRT Header */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* AFI/SAFI-Specific RIB Table Header */
#ifdef IPV6
0x12, 0x34, 0x56, 0x78, 0x18, 0x12, 0x34, 0x56, 0x00, 0x05,
#else
0x12, 0x34, 0x56, 0x78, 0x18, 0x0C, 0x22, 0x38, 0x00, 0x05,
#endif
/* RIB Entries */
0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x00, 0x01, 0x08, 0x08, 0x08, 0x09, 0x00, 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x00, 0x02, 0x08, 0x08, 0x08, 0x0A, 0x00, 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x00, 0x03, 0x08, 0x08, 0x08, 0x0B, 0x00, 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x00, 0x04, 0x08, 0x08, 0x08, 0x0C, 0x00, 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
};
bt_assert(state.msg.msg_length == sizeof(expected));
compare(expected, state.msg.msg, sizeof(expected));
mrt_buffer_free(&state.msg);
return BT_SUCCESS;
}
@ -108,8 +147,8 @@ 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");
bt_test_suite(t_peer_index_table, "MRT Table Dump: Peer index table");
bt_test_suite(t_rib_table, "MRT Table Dump: RIB table");
return bt_end();
}

View File

@ -70,7 +70,7 @@ void *fib_get(struct fib *, ip_addr *, int); /* Find or create new if nonexiste
void *fib_route(struct fib *, ip_addr, int); /* Longest-match routing lookup */
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 fib_check(const struct fib *); /* Consistency check for debugging */
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 *);
@ -122,6 +122,7 @@ struct rtable_config {
int gc_max_ops; /* Maximum number of operations before GC is run */
int gc_min_time; /* Minimum time between two consecutive GC runs */
byte sorted; /* Routes of network are sorted according to rte_better() */
list mrt_table_dumps; /* Configured MRT tables dumps (struct mrt_table_individual_config) */
};
typedef struct rtable {
@ -607,5 +608,14 @@ void roa_preconfig(struct config *c);
void roa_commit(struct config *new, struct config *old);
void roa_show(struct roa_show_data *d);
void mrt_table_dump_init_periodic(struct rtable *tab, list *mrt_table_dumps);
struct mrt_table_dump_ctx *mrt_table_dump_cmd(struct mrt_table_individual_config *mrt_cfg);
void mrt_table_cli_cmd(struct mrt_table_config *mrt_config);
struct mrt_table_config *mrt_table_new_config(void);
struct mrt_table_dump_ctx;
int is_route_good_for_table_dump(struct mrt_table_dump_ctx *state, rte *e);
//char *mrt_table_dump_config_get_filename_fmt(struct rtable *rtable);
void mrt_table_dump_init_file_descriptor(struct mrt_table_dump_ctx *state);
#endif

View File

@ -440,7 +440,7 @@ fit_put(struct fib_iterator *i, struct fib_node *n)
* Use when you suspect somebody of corrupting innocent data structures.
*/
void
fib_check(struct fib *f)
fib_check(const struct fib *f)
{
uint i, ec, lo, nulls;

View File

@ -28,6 +28,10 @@
* routes in order to conserve memory.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#undef LOCAL_DEBUG
#include "nest/bird.h"
@ -35,6 +39,7 @@
#include "nest/protocol.h"
#include "nest/cli.h"
#include "nest/iface.h"
#include "nest/mrtdump.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/string.h"
@ -42,6 +47,9 @@
#include "filter/filter.h"
#include "lib/string.h"
#include "lib/alloca.h"
#include "lib/unix.h"
#include "proto/bgp/mrt.h"
pool *rt_table_pool;
@ -58,7 +66,8 @@ static void rt_next_hop_update(rtable *tab);
static inline int rt_prune_table(rtable *tab);
static inline void rt_schedule_gc(rtable *tab);
static inline void rt_schedule_prune(rtable *tab);
static void mrt_table_dump_stop_periodic(struct rtable *tab);
static int mrt_table_dump_cmd_step(void *mrt_table_dump_ctx);
static inline struct ea_list *
make_tmp_attrs(struct rte *rt, struct linpool *pool)
@ -1487,6 +1496,8 @@ rt_preconfig(struct config *c)
init_list(&c->tables);
c->master_rtc = rt_new_table(s);
init_list(&c->mrt_table_dumps);
}
@ -1653,6 +1664,9 @@ rt_new_table(struct symbol *s)
add_tail(&new_config->tables, &c->n);
c->gc_max_ops = 1000;
c->gc_min_time = 5;
init_list(&c->mrt_table_dumps);
return c;
}
@ -1685,6 +1699,7 @@ rt_unlock_table(rtable *r)
{
struct config *conf = r->deleted;
DBG("Deleting routing table %s\n", r->name);
if (r->hostcache)
rt_free_hostcache(r);
rem_node(&r->n);
@ -1718,6 +1733,7 @@ rt_commit(struct config *new, struct config *old)
WALK_LIST(o, old->tables)
{
rtable *ot = o->table;
mrt_table_dump_stop_periodic(ot);
if (!ot->deleted)
{
struct symbol *sym = cf_find_symbol(o->name);
@ -1744,6 +1760,7 @@ rt_commit(struct config *new, struct config *old)
}
WALK_LIST(r, new->tables)
{
if (!r->table)
{
rtable *t = mb_alloc(rt_table_pool, sizeof(struct rtable));
@ -1752,6 +1769,8 @@ rt_commit(struct config *new, struct config *old)
add_tail(&routing_tables, &t->n);
r->table = t;
}
mrt_table_dump_init_periodic(r->table, &new->mrt_table_dumps);
}
DBG("\tdone\n");
}
@ -2412,6 +2431,345 @@ rt_show(struct rt_show_data *d)
}
}
/*
* MRTDump Table Commands
*/
struct mrt_table_config *
mrt_table_new_config(void)
{
struct mrt_table_config *mrt_cfg = cfg_allocz(sizeof(struct mrt_table_config));
mrt_cfg->c.period = MRT_TABLE_NOT_CONFIGURED;
mrt_cfg->c.filter = FILTER_ACCEPT;
mrt_cfg->c.config = new_config;
return mrt_cfg;
}
static struct filter *
mrt_table_dump_config_get_filter(struct mrt_table_dump_ctx *state)
{
if (state->config.c.filter != FILTER_ACCEPT)
return state->config.c.filter;
return FILTER_ACCEPT;
}
uint
mrt_table_dump_config_get_period(struct mrt_table_individual_config *mrt_cfg)
{
if (mrt_cfg->c.period && mrt_cfg->c.period != (u32)MRT_TABLE_NOT_CONFIGURED)
return mrt_cfg->c.period;
return MRT_TABLE_DEFAULT_PERIOD;
}
/*
* Writing timestamp of the next mrt dump.
* Return seconds for wait for next mrt dump from now.
*/
static uint
mrt_table_dump_config_get_next_modular_period(struct mrt_table_individual_config *cfg)
{
bird_clock_t period = (bird_clock_t)mrt_table_dump_config_get_period(cfg);
bird_clock_t timestamp = now;
if (cfg->next_dump != 0)
{
cfg->next_dump += period;
}
else
{
bird_clock_t time_to_wait = period - (timestamp % period);
cfg->next_dump = timestamp + time_to_wait;
}
return (uint)(cfg->next_dump - timestamp);
}
static char *
mrt_table_dump_config_get_filename_fmt(struct mrt_table_dump_ctx *state)
{
if (state->config.c.filename_fmt != NULL)
return state->config.c.filename_fmt;
return MRT_TABLE_DEFAULT_FILENAME_FMT;
}
static const char *
mrt_table_dump_config_get_tablename_pattern(const struct mrt_table_config *mrt_config)
{
const char *table_name_pattern = MRT_TABLE_DEFAULT_TABLENAME_PATTERN;
if (mrt_config->rtable_wildcard_name)
table_name_pattern = mrt_config->rtable_wildcard_name;
return table_name_pattern;
}
static void
mrt_table_dump_timer_hook(struct timer *timer)
{
if (timer->expires)
{
struct mrt_table_individual_config *cfg = (struct mrt_table_individual_config *) timer->data;
mrt_table_dump_cmd(cfg);
tm_start(timer, mrt_table_dump_config_get_next_modular_period(cfg));
}
}
static struct mrt_table_individual_config *
mrt_table_new_individual_config(struct mrt_table_config *mrt_cfg, struct rtable *tab)
{
struct mrt_table_individual_config *cfg = mb_allocz(rt_table_pool, sizeof(struct mrt_table_individual_config));
cfg->c = mrt_cfg->c;
cfg->table_cf = tab->config;
return cfg;
}
void
mrt_table_dump_init_periodic(struct rtable *tab, list *mrt_table_dumps)
{
struct mrt_table_config *mrt_cfg;
WALK_LIST(mrt_cfg, *mrt_table_dumps)
{
if (patmatch(mrt_table_dump_config_get_tablename_pattern(mrt_cfg), tab->name))
{
struct mrt_table_individual_config *cfg = mrt_table_new_individual_config(mrt_cfg, tab);
add_tail(&cfg->table_cf->mrt_table_dumps, &cfg->n);
cfg->timer = tm_new_set(rt_table_pool, mrt_table_dump_timer_hook, cfg, 0, (uint)-1);
tm_start(cfg->timer, mrt_table_dump_config_get_next_modular_period(cfg));
}
}
}
static int
mrt_table_dump_cmd_step(void *mrt_table_dump_ctx)
{
struct mrt_table_dump_ctx *state = mrt_table_dump_ctx;
bgp_mrt_table_dump_step(state);
if (state->state != MRT_STATE_COMPLETED)
ev_schedule(state->step);
else
{
rfree(state->step);
if (state->config.c.config)
config_del_obstacle(state->config.c.config);
log(L_INFO "MRT dump of table %s was saved into file \"%s\"", state->rtable->name, state->file_path);
if (state->config.c.cli)
{
cli_printf(state->config.c.cli, 13, "Dump of table %s was saved into file \"%s\"", state->rtable->name, state->file_path);
}
rt_unlock_table(state->rtable);
mb_free(state->file_path);
mb_free(state);
return 0; /* End of entire mrt dump */
}
return 1; /* End of part in mrt dump */
}
static void
mrt_table_dump_cmd_step_void(void *mrt_table_dump_ctx)
{
mrt_table_dump_cmd_step(mrt_table_dump_ctx);
}
static void
mrt_table_dump_init(struct mrt_table_dump_ctx *state)
{
struct rtable *tab = state->rtable;
#ifdef DEBUGGING
fib_check(&tab->fib);
#endif
FIB_ITERATE_INIT(&state->fit, &tab->fib);
state->state = MRT_STATE_RUNNING;
state->rib_sequence_number = 0;
mrt_table_dump_init_file_descriptor(state);
mrt_rib_table_alloc(&state->rib_table);
bgp_mrt_peer_index_table_dump(state);
}
struct mrt_table_dump_ctx *
mrt_table_dump_cmd(struct mrt_table_individual_config *mrt_cfg)
{
struct mrt_table_dump_ctx *state = mb_allocz(rt_table_pool, sizeof(struct mrt_table_dump_ctx));
event *step_ev = ev_new(rt_table_pool);
step_ev->hook = mrt_table_dump_cmd_step_void;
step_ev->data = state;
state->step = step_ev;
state->config = *mrt_cfg;
state->rtable = state->config.table_cf->table;
rt_lock_table(state->rtable);
if (state->config.c.config)
config_add_obstacle(state->config.c.config);
mrt_table_dump_init(state);
if (mrt_table_dump_cmd_step(state) == 0)
return NULL;
return state;
}
int
is_route_good_for_table_dump(struct mrt_table_dump_ctx *state, rte *e)
{
if (e->attrs->src->proto->proto_state != PS_UP)
return 0; /* FAIL */
struct filter *filter = mrt_table_dump_config_get_filter(state);
rte *rte = e;
struct ea_list *tmp_attrs = make_tmp_attrs(rte, rte_update_pool);
int filter_result = f_run(filter, &rte, &tmp_attrs, rte_update_pool, REF_COW);
if (filter_result >= F_REJECT)
return 0; /* FAIL */
return 1; /* OK */
}
/* You must free the result if result is non-NULL. */
static char *
mrt_str_replace(const char *orig, const char *rep, const char *with)
{
if (!orig || !rep || !with)
return NULL;
char *result;
const char *ins_point;
char *tmp;
int len_rep;
int len_with;
int len_front;
int num_of_replacements;
len_rep = strlen(rep);
len_with = strlen(with);
ins_point = orig;
for (num_of_replacements = 0; tmp = strstr(ins_point, rep); ++num_of_replacements)
ins_point = tmp + len_rep;
/* first time through the loop, all the variable are set correctly
* from here on,
* tmp points to the end of the result string
* ins points to the next occurrence of rep in orig
* orig points to the remainder of orig after "end of rep"
*/
tmp = result = mb_alloc(&root_pool, strlen(orig) + (len_with - len_rep) * num_of_replacements + 1);
if (!result)
return NULL;
while (num_of_replacements--)
{
ins_point = strstr(orig, rep);
len_front = ins_point - orig;
tmp = strncpy(tmp, orig, len_front) + len_front;
tmp = strcpy(tmp, with) + len_with;
orig += len_front + len_rep; /* move to next "end of rep" */
}
strcpy(tmp, orig);
return result;
}
static const char *
mrt_table_dump_get_realpath(const char *filename)
{
static char path[BIRD_PATH_MAX];
if (realpath(filename, path) != path)
{
DBG("ERR: realpath(\"%s\") errno %d", filename, errno);
return filename;
}
return path;
}
void
mrt_table_dump_init_file_descriptor(struct mrt_table_dump_ctx *state)
{
struct timeformat timestamp_fmt = {
.fmt1 = mrt_table_dump_config_get_filename_fmt(state),
};
char timestamp[TM_DATETIME_BUFFER_SIZE];
tm_format_datetime(timestamp, &timestamp_fmt, now);
char *tablename = state->config.table_cf->name;
char *filename = mrt_str_replace(timestamp, "%f", tablename);
if (filename)
{
state->rfile = tracked_fopen(rt_table_pool, filename, "a");
const char *filename_fullpath = mrt_table_dump_get_realpath(filename);
state->file_path = mb_allocz(rt_table_pool, strlen(filename_fullpath)+1);
strcpy(state->file_path, filename_fullpath);
if (!state->rfile)
{
log(L_WARN "Unable to open file \"%s\" for MRT dump of table %s", state->file_path, tablename);
if (state->config.c.cli)
{
cli_msg(13, "Unable to open file \"%s\" for MRT dump of table %s", state->file_path, tablename);
}
}
else
{
log(L_INFO "MRT dump of table %s is saving into file \"%s\"", tablename, state->file_path);
if (state->config.c.cli)
{
cli_msg(13, "Dump of table %s is saving into file \"%s\"", tablename, state->file_path);
}
}
mb_free(filename);
}
else
{
log(L_ERR "Parsing MRT dump filename format \"%s\" for table %s failed", timestamp_fmt.fmt1, tablename);
if (state->config.c.cli)
{
cli_msg(13, "Parsing filename format \"%s\" for table %s failed", timestamp_fmt.fmt1, tablename);
}
}
}
static void
mrt_table_dump_stop_periodic(struct rtable *tab)
{
struct mrt_table_individual_config *mrt_cfg, *mrt_cfg_next;
WALK_LIST_DELSAFE(mrt_cfg, mrt_cfg_next, tab->config->mrt_table_dumps)
{
mrt_cfg->timer->recurrent = 0;
tm_stop(mrt_cfg->timer);
rfree(mrt_cfg->timer);
rem_node(&mrt_cfg->n);
mb_free(mrt_cfg);
}
}
void
mrt_table_cli_cmd(struct mrt_table_config *mrt_cfg)
{
struct rtable_config *r;
WALK_LIST(r, config->tables)
{
if (patmatch(mrt_table_dump_config_get_tablename_pattern(mrt_cfg), r->table->name))
{
struct mrt_table_individual_config *cfg = mrt_table_new_individual_config(mrt_cfg, r->table);
struct mrt_table_dump_ctx *state = mrt_table_dump_cmd(cfg);
if (state && state->state != MRT_STATE_COMPLETED)
while (mrt_table_dump_cmd_step(state));
mb_free(cfg);
}
}
cli_printf(mrt_cfg->c.cli, 0, "");
}
/*
* Documentation for functions declared inline in route.h
*/

View File

@ -1,4 +1,4 @@
source=bgp.c attrs.c packets.c
source=bgp.c attrs.c packets.c mrt.c
root-rel=../../
dir-name=proto/bgp

View File

@ -75,8 +75,10 @@
#include "lib/socket.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/slists.h"
#include "bgp.h"
#include "mrt.h"
struct linpool *bgp_linpool; /* Global temporary pool */
@ -1252,6 +1254,7 @@ bgp_init(struct proto_config *C)
p->rs_client = c->rs_client;
p->rr_client = c->rr_client;
p->igp_table = get_igp_table(c);
p->mrt_peer_index = 0;
return P;
}

View File

@ -152,6 +152,7 @@ struct bgp_proto {
u8 last_error_class; /* Error class of last error */
u32 last_error_code; /* Error code of last error. BGP protocol errors
are encoded as (bgp_err_code << 16 | bgp_err_subcode) */
u16 mrt_peer_index; /* Used for MRT Table Dump */
#ifdef IPV6
byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */
unsigned mp_reach_len, mp_unreach_len;
@ -183,6 +184,7 @@ struct bgp_bucket {
#define BGP_MAX_PACKET_LENGTH 4096
#define BGP_RX_BUFFER_SIZE 4096
#define BGP_TX_BUFFER_SIZE BGP_MAX_PACKET_LENGTH
#define BGP_ATTR_BUFFER_SIZE 2048 /* Default buffer size for encoded bgp attributes */
extern struct linpool *bgp_linpool;

129
proto/bgp/mrt.c Normal file
View File

@ -0,0 +1,129 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "nest/route.h"
#include "nest/iface.h"
#include "nest/cli.h"
#include "lib/socket.h"
#include "lib/ip.h"
#include "lib/unix.h"
#include "lib/krt.h"
#include "bgp.h"
#include "mrt.h"
/*
* MRTDump: Table Dump V2: BGP Specific Part
*/
void
bgp_mrt_peer_index_table_dump(struct mrt_table_dump_ctx *state)
{
struct proto *P;
struct mrt_peer_index_table pit;
u32 collector_bgp_id = config->router_id;
mrt_peer_index_table_header(&pit, collector_bgp_id, state->rtable->name);
mrt_peer_index_table_add_peer(&pit, 0, 0, IPA_NONE); /* at index 0 is fake zeroed-peer for all non-BGP routes */
WALK_LIST(P, active_proto_list)
{
if (P->proto_state == PS_UP && P->proto == &proto_bgp)
{
struct bgp_proto *p = (struct bgp_proto *) P;
p->mrt_peer_index = pit.peer_count;
ip_addr peer_ip = p->cf->remote_ip;
mrt_peer_index_table_add_peer(&pit, p->remote_id, p->remote_as, peer_ip);
}
}
mrt_peer_index_table_dump(&pit, fileno(state->rfile->f));
mrt_buffer_free(&pit.msg);
}
static void
bgp_mrt_rib_table_dump(struct mrt_table_dump_ctx *state)
{
byte *msg = state->rib_table.msg.msg;
u32 msg_length = state->rib_table.msg.msg_length;
mrt_dump_message(fileno(state->rfile->f), MRT_TABLE_DUMP_V2, state->rib_table.subtype, msg, msg_length);
}
/*
* Usage:
* struct mrt_table_dump_ctx ctx;
* bgp_mrt_table_dump_init(rtable, &ctx);
* while (ctx.state != MRT_STATE_COMPLETED)
* bgp_mrt_table_dump_step(&ctx);
*/
void
bgp_mrt_table_dump_step(struct mrt_table_dump_ctx *state)
{
if (state->state == MRT_STATE_COMPLETED)
return;
uint max_work_size = 1;
FIB_ITERATE_START(&state->rtable->fib, &state->fit, f)
{
if (!max_work_size--)
{
FIB_ITERATE_PUT(&state->fit, f);
bgp_mrt_rib_table_dump(state);
return;
}
net *n = (net *) f;
mrt_rib_table_header(&state->rib_table, state->rib_sequence_number++, n->n.pxlen, n->n.prefix);
rte *e;
for (e = n->routes; e; e = e->next)
{
struct proto *P = e->attrs->src->proto;
if (!is_route_good_for_table_dump(state, e))
continue;
u16 peer_index = 0; /* have to correspond with fake zeroed-peer in peer index table */
uint attributes_length = 0;
static byte attributes_buffer[BGP_ATTR_BUFFER_SIZE]; /* static intends to do better performance */
if (P->proto == &proto_bgp)
{
struct bgp_proto *p = (struct bgp_proto *) P;
if (p->mrt_peer_index == 0)
{
log(L_INFO "%s: MRT Table Dump for %I/%u: Skipping not-indexed BPG RIB (local ASN: %u, remote ASN: %u)", p->p.name, n->n.prefix, n->n.pxlen, p->local_as, p->remote_as);
continue;
}
attributes_length = bgp_encode_attrs(p, attributes_buffer, e->attrs->eattrs, BGP_ATTR_BUFFER_SIZE);
if (attributes_length == -1)
{
log(L_WARN "%s: MRT Table Dump for %I/%u: Attribute list too long, let it blank", p->p.name, n->n.prefix, n->n.pxlen);
attributes_length = 0;
}
peer_index = p->mrt_peer_index;
}
struct mrt_rib_entry entry = {
.peer_index = peer_index,
.originated_time = (u32) bird_clock_to_unix_timestamp(e->lastmod),
.attributes_length = attributes_length,
.attributes = attributes_buffer
};
mrt_rib_table_add_entry(&state->rib_table, &entry);
}
} FIB_ITERATE_END(f);
fit_get(&state->rtable->fib, &state->fit);
mrt_buffer_free(&state->rib_table.msg);
if (state->rfile)
rfree(state->rfile);
state->state = MRT_STATE_COMPLETED;
}

11
proto/bgp/mrt.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _BIRD_BGP_MRT_H_
#define _BIRD_BGP_MRT_H_
#include "nest/route.h"
#include "nest/mrtdump.h"
#include "bgp.h"
void bgp_mrt_table_dump_step(struct mrt_table_dump_ctx *state);
void bgp_mrt_peer_index_table_dump(struct mrt_table_dump_ctx *state);
#endif /* _BIRD_BGP_MRT_H_ */

View File

@ -85,14 +85,13 @@ static void
mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
{
byte buf[BGP_MAX_PACKET_LENGTH + 128];
byte *bp = buf + MRTDUMP_HDR_LENGTH;
byte *bp = buf + MRT_HDR_LENGTH;
int as4 = conn->bgp->as4_session;
bp = mrt_put_bgp4_hdr(bp, conn, as4);
memcpy(bp, pkt, len);
bp += len;
mrt_dump_message(&conn->bgp->p, BGP4MP, as4 ? BGP4MP_MESSAGE_AS4 : BGP4MP_MESSAGE,
buf, bp-buf);
mrt_dump_message_proto(&conn->bgp->p, MRT_BGP4MP, as4 ? MRT_BGP4MP_MESSAGE_AS4 : MRT_BGP4MP_MESSAGE, buf, bp-buf);
}
static inline u16
@ -106,13 +105,13 @@ void
mrt_dump_bgp_state_change(struct bgp_conn *conn, unsigned old, unsigned new)
{
byte buf[128];
byte *bp = buf + MRTDUMP_HDR_LENGTH;
byte *bp = buf + MRT_HDR_LENGTH;
bp = mrt_put_bgp4_hdr(bp, conn, 1);
put_u16(bp+0, convert_state(old));
put_u16(bp+2, convert_state(new));
bp += 4;
mrt_dump_message(&conn->bgp->p, BGP4MP, BGP4MP_STATE_CHANGE_AS4, buf, bp-buf);
mrt_dump_message_proto(&conn->bgp->p, MRT_BGP4MP, MRT_BGP4MP_STATE_CHANGE_AS4, buf, bp-buf);
}
static byte *

View File

@ -46,7 +46,6 @@
*/
#undef LOCAL_DEBUG
#define LOCAL_DEBUG 1
#include "nest/bird.h"
#include "nest/iface.h"

View File

@ -42,9 +42,9 @@ syslog_name:
log_file:
text {
FILE *f = tracked_fopen(new_config->pool, $1, "a");
if (!f) cf_error("Unable to open log file `%s': %m", $1);
$$ = f;
struct rfile *rf = tracked_fopen(new_config->pool, $1, "a");
if (!rf) cf_error("Unable to open log file `%s': %m", $1);
$$ = rf->f;
}
| SYSLOG syslog_name { $$ = NULL; new_config->syslog_name = $2; }
| STDERR { $$ = stderr; }
@ -77,14 +77,14 @@ CF_ADDTO(conf, mrtdump_base)
mrtdump_base:
MRTDUMP PROTOCOLS mrtdump_mask ';' { new_config->proto_default_mrtdump = $3; }
| MRTDUMP ROUTES mrtdump_table_config { add_tail(&new_config->mrt_table_dumps, &this_mrt_table_config->n); }
| MRTDUMP text ';' {
FILE *f = tracked_fopen(new_config->pool, $2, "a");
if (!f) cf_error("Unable to open MRTDump file '%s': %m", $2);
new_config->mrtdump_file = fileno(f);
struct rfile *rf = tracked_fopen(new_config->pool, $2, "a");
if (!rf) cf_error("Unable to open MRTDump file '%s': %m", $2);
new_config->mrt_proto_file = fileno(rf->f);
}
;
CF_ADDTO(conf, timeformat_base)
timeformat_which:

View File

@ -55,11 +55,6 @@
* Tracked Files
*/
struct rfile {
resource r;
FILE *f;
};
static void
rf_free(resource *r)
{
@ -85,17 +80,17 @@ static struct resclass rf_class = {
NULL
};
void *
tracked_fopen(pool *p, char *name, char *mode)
struct rfile *
tracked_fopen(pool *p, const char *name, const char *mode)
{
FILE *f = fopen(name, mode);
if (f)
{
struct rfile *r = ralloc(p, &rf_class);
r->f = f;
}
return f;
if (!f)
return NULL;
struct rfile *r = ralloc(p, &rf_class);
r->f = f;
return r;
}
/**

View File

@ -316,14 +316,20 @@ log_init_debug(char *f)
}
void
mrt_dump_message(const struct proto *p, u16 type, u16 subtype, byte *buf, u32 len)
mrt_dump_message_proto(struct proto *p, u16 type, u16 subtype, byte *buf, u32 len)
{
mrt_dump_message(p->cf->global->mrt_proto_file, type, subtype, buf, len);
}
void
mrt_dump_message(int file_descriptor, u16 type, u16 subtype, byte *buf, u32 len)
{
/* Prepare header */
put_u32(buf+0, now_real);
put_u16(buf+4, type);
put_u16(buf+6, subtype);
put_u32(buf+8, len - MRTDUMP_HDR_LENGTH);
put_u32(buf+8, len - MRT_HDR_LENGTH);
if (p->cf->global->mrtdump_file != -1)
write(p->cf->global->mrtdump_file, buf, len);
if (file_descriptor >= 0)
write(file_descriptor, buf, len);
}

View File

@ -64,6 +64,12 @@ tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, uns
return t;
}
static inline bird_clock_t
bird_clock_to_unix_timestamp(bird_clock_t bird_clock)
{
bird_clock_t delta = now - bird_clock;
return (now_real - delta);
}
struct timeformat {
char *fmt1, *fmt2;

View File

@ -9,6 +9,7 @@
#ifndef _BIRD_UNIX_H_
#define _BIRD_UNIX_H_
#include <stdio.h>
#include <sys/socket.h>
struct pool;
@ -103,9 +104,14 @@ void io_init(void);
void io_loop(void);
void io_log_dump(void);
int sk_open_unix(struct birdsock *s, char *name);
void *tracked_fopen(struct pool *, char *name, char *mode);
void test_old_bird(char *path);
/* Tracked Files */
struct rfile {
resource r;
FILE *f;
};
struct rfile *tracked_fopen(struct pool *, const char *name, const char *mode);
/* krt.c bits */

View File

@ -35,4 +35,10 @@ io_log_event(void *hook, void *data)
bt_debug("This is io_log_event mockup. \n");
};
void
mrt_dump_message(int file_descriptor, u16 type, u16 subtype, byte *buf, u32 len)
{
debug("mrt_dump_message: file_descriptor %d, type %02X, subtype %02X, %s (%u) \n", file_descriptor, type, subtype, buf, len);
}
#include "lib/slab.c" /* REMOVE ME */