]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Integrate MRT Table Dump (RFC 6396) into BIRD
authorPavel Tvrdík <pawel.tvrdik@gmail.cz>
Tue, 26 May 2015 10:40:40 +0000 (12:40 +0200)
committerPavel Tvrdík <pawel.tvrdik@gmail.cz>
Tue, 28 Jul 2015 15:19:04 +0000 (17:19 +0200)
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>]

26 files changed:
conf/conf.c
conf/conf.h
doc/bird.sgml
lib/patmatch.c
lib/string.h
nest/Makefile
nest/config.Y
nest/mrtdump.c
nest/mrtdump.h
nest/mrtdump_test.c
nest/route.h
nest/rt-fib.c
nest/rt-table.c
proto/bgp/Makefile
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/mrt.c [new file with mode: 0644]
proto/bgp/mrt.h [new file with mode: 0644]
proto/bgp/packets.c
proto/rip/rip.c
sysdep/unix/config.Y
sysdep/unix/io.c
sysdep/unix/log.c
sysdep/unix/timer.h
sysdep/unix/unix.h
test/birdtest_support.h

index a907402d936ef34062326ce65b196213de7ce8f8..02494d810dddc5ccfa73b20ea7bd88bc8350228c 100644 (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);
index 3d12b5d0d952bd827757d1a00eed42eb358438ea..cea7958efdea5f6d7577295cf273064aa3d5f0db 100644 (file)
 #include "lib/resource.h"
 #include "lib/timer.h"
 
+/* Configuration structures */
+
+#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 structure */
+/* 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 */
index 1c2dda4b872fc8860f692eb428b00b89a5583a57..0cc5cab90dbe5e7e526b54b7f3713592a219f0fe 100644 (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
 
index 923e8f862a845f4399598c54deb47d7ea1ad45c7..d2e1e313d9620caba931672e5cbbbad1a8c4ace6 100644 (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)
     {
index d23c1d946010c6865609eabe968fb13b52782103..33bb73eae580966aeb13cc5ee68fb67b38f98b33 100644 (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
index e69286684f0a3c9a6943ea9ff030af7c50a5a2a2..4def57b8fcb537c041a74bd090a0f6792b8407a2 100644 (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
 
index 939bed6a8c527a27b0e40184ec3fbcff5e7ff6d2..4172f3b39bc6abd1539170542574e3841783620a 100644 (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"); } ;
 
index 0c79ba7cce4d89b47888d11366e17cd17f482c84..115bbeb40f959643a75ec1dc58168da4dacacf12 100644 (file)
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
+#undef LOCAL_DEBUG
+
 #include "nest/mrtdump.h"
+#include "nest/route.h"
+
+/*
+ * MRTDump: Table Dump: Base
+ */
+
+static void
+mrt_buffer_reset(struct mrt_buffer *buf)
+{
+  buf->msg_capacity = MRT_BUFFER_DEFAULT_CAPACITY;
+  buf->msg_length = MRT_HDR_LENGTH;    /* Reserved for the main MRT header */
+}
 
 void
-mrt_msg_init(struct mrt_msg *msg, pool *mem_pool)
+mrt_buffer_alloc(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);
+  mrt_buffer_reset(buf);
+  buf->msg = mb_allocz(&root_pool, buf->msg_capacity);
 }
 
 void
-mrt_msg_free(struct mrt_msg *msg)
+mrt_buffer_free(struct mrt_buffer *buf)
 {
-  mb_free(msg->msg);
+  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);
+
+  memcpy(mrt_buffer_get_cursor(buf), data, data_size);
+  buf->msg_length += data_size;
+
+  mrt_buffer_write_show_debug(buf, data_size);
+}
+
+static void
+mrt_buffer_put_ipa(struct mrt_buffer *buf, ip_addr addr, size_t write_size)
+{
+  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;
 
-  size_t required_size = data_size + msg->msg_length;
-  if (msg->msg_capacity < required_size)
-    mrt_grow_msg_buffer(msg, required_size);
+  byte *actual_position;
 
-  memcpy(&msg->msg[msg->msg_length], data, data_size);
-  msg->msg_length += data_size;
+  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_write_to_msg_(msg, data) mrt_write_to_msg(msg, &data, sizeof(data))
+#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_init(struct mrt_peer_index_table *pit_msg, u32 collector_bgp_id, const char *name)
+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;
-  pit_msg->peer_count = 0;
-  pit_msg->name_length = strlen(name);
+  struct mrt_buffer *buf = &state->msg;
+  mrt_buffer_alloc(buf);
 
-  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");
+  state->peer_count = 0;
+  u16 name_length = 0;
+  if (name != NULL)
+    name_length = strlen(name);
+
+  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_peer_index_table_inc_peer_count(struct mrt_peer_index_table *pit_msg)
+mrt_peer_index_table_inc_peer_count(struct mrt_peer_index_table *state)
 {
-  pit_msg->peer_count++;
-  byte *peer_count = mrt_peer_index_table_get_peer_count(pit_msg);
-  put_u16(peer_count, pit_msg->peer_count);
+  state->peer_count++;
+  byte *peer_count = &state->msg.msg[state->peer_count_offset];
+  put_u16(peer_count, state->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)
+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 = pit_msg->msg;
+  struct mrt_buffer *msg = &state->msg;
 
-  u8 peer_type = PEER_TYPE_AS_32BIT;
-  if (sizeof(*peer_ip_addr) > sizeof(ip4_addr))
-    peer_type |= PEER_TYPE_IPV6;
+  u8 peer_type = MRT_PEER_TYPE_32BIT_ASN;
+  if (sizeof(peer_ip_addr) > sizeof(ip4_addr))
+    peer_type |= MRT_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_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(pit_msg);
-  debug("\n");
+  mrt_peer_index_table_inc_peer_count(state);
+  DBG("\n");
 }
 
 void
-mrt_rib_table_init(struct mrt_rib_table *rt_msg, u32 sequence_number, u8 prefix_length, ip_addr *prefix)
+mrt_peer_index_table_dump(struct mrt_peer_index_table *state, int file_descriptor)
 {
-  struct mrt_msg *msg = rt_msg->msg;
+  byte *msg = state->msg.msg;
+  u32 msg_length = state->msg.msg_length;
 
-  rt_msg->entry_count = 0;
+  mrt_dump_message(file_descriptor, MRT_TABLE_DUMP_V2, MRT_PEER_INDEX_TABLE, msg, msg_length);
+}
 
-  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");
+void
+bgp_mrt_peer_index_table_free(struct mrt_peer_index_table *state)
+{
+  mrt_buffer_free(&state->msg);
 }
 
-static byte *
-mrt_rib_table_get_entry_count(struct mrt_rib_table *rt_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)
 {
-  struct mrt_msg *msg = rt_msg->msg;
-  u32 sequence_number_size = 4;
-  u32 prefix_length_size = 1;
+  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);
 
-  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!");
+#define CEILING(a, b) (((a)+(b)-1) / (b))
+  u32 prefix_bytes = CEILING(prefix_length, 8);
+  mrt_buffer_put_ipa(msg, prefix, prefix_bytes);
 
-  u32 offset = sequence_number_size + prefix_length_size + prefix_size;
-  return &msg->msg[offset];
+  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 *rt_msg)
+mrt_rib_table_inc_entry_count(struct mrt_rib_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->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 *rt_msg, const struct mrt_rib_entry *rib)
+mrt_rib_table_add_entry(struct mrt_rib_table *state, const struct mrt_rib_entry *entry)
 {
-  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);
+  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(rt_msg);
-  debug("\n");
+  mrt_rib_table_inc_entry_count(state);
+  DBG("\n");
 }
index 53d1c84bd9a89d4f25f3fcfc4ca737128b8ffb20..eef1f6772a942342ffedb09ba3462c0306ae8942 100644 (file)
  *     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"
+
+#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 */
 
-/* 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 */
+#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 */
+};
+
+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_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);
+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_ */
index df6ffd74528470f0d690135073a18a8454a4d04a..f7071108873100865637da446ab16ec13fa015dc 100644 (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);
+
+  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,
+  };
 
-  mrt_msg_free(&msg);
+  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();
 }
index 821f5cdb1b4fb9489f81973fb6539f12de13f940..095a2474d58b4d0c3895fbfb98934eb3644f5baa 100644 (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
index ec77311c8a620b37c78e03ce61b23f22e086c2ef..32aa842487143ad1fc1da0f47e77eae44548962b 100644 (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;
 
index d5af41c76746e2f17609957c1ea40c31024a64cf..884e28275b87cccffb581791cef723017e46026a 100644 (file)
  * 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
  */
index a634cf0de166a080243475fe828a7dc7098c38ef..30f87c5b92d07fce2e33fde39dd473a1ec639f81 100644 (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
 
index e48b643bfb1ad37845a9a9c9ebe9ecb7e55b7e69..2eb2ff17fd7596878b668bdfaf3093ee86907fe6 100644 (file)
 #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;
 }
index 446fc857dc11269f818f4983e4d31b6cce18a2fd..b40ee9b9123ccaecfa0c2de10fb6fa4b238593bd 100644 (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;
 
diff --git a/proto/bgp/mrt.c b/proto/bgp/mrt.c
new file mode 100644 (file)
index 0000000..a9021cd
--- /dev/null
@@ -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;
+}
diff --git a/proto/bgp/mrt.h b/proto/bgp/mrt.h
new file mode 100644 (file)
index 0000000..fdaa9e6
--- /dev/null
@@ -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_ */
index 378f5ab1a986321d8fdeb967d05947d07f565f6f..74fb637c4406323646a8e0b286d66b3e5478c3df 100644 (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 *
index b77cf409da69a4902ea9c9d9e4786534b55ddbed..9a1183f9f0d15148a5e98c5118ab501452e1e8e8 100644 (file)
@@ -46,7 +46,6 @@
  */
 
 #undef LOCAL_DEBUG
-#define LOCAL_DEBUG 1
 
 #include "nest/bird.h"
 #include "nest/iface.h"
index d6ab8cab736d8650ee04fddc3308e2556ef34915..4b893e1ba966ead6b4bf734b34fe75f461a14d65 100644 (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:
index 0724667d18cdaeb5099bff18c468ad3dc1171c2b..d6383e2a92d68d4aab893ff64a1165ed05a62d5e 100644 (file)
  *     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;
 }
 
 /**
index 6d623f9537ab8bbb37a51bca1db22c7d3d79f982..e049473228c5492467ee1156d88068ec270860bc 100644 (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);
 }
index 99d43932884b93c242c6df2dceeb6306078e06b6..8c7dd6cf544ea8b33ca7efd5902d5df2a3174a82 100644 (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;
index 4e0ff84110e17cb7b87ea95fef8b44dfb5ba8f69..4e3f78ac8864a17f3ce9fea442893fd143b1f585 100644 (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 */
 
index 753cb5902ead5ca7809406f1c92f4c67ef7f9257..895e1447524347c762a4c8e56a5819c4543a2a82 100644 (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 */