summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_dump.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-09-02 00:53:59 +0100
committerChris Hall <chris.hall@highwayman.com>2011-09-02 00:53:59 +0100
commit3690074a486cfada568975e287d9cbb9e687501f (patch)
treef6394c1ab1ca8381e9a77acca84f03dc701d10b2 /bgpd/bgp_dump.c
parent3f515315d5b17e432453eef67d7ac9e27bc39461 (diff)
downloadquagga-3690074a486cfada568975e287d9cbb9e687501f.tar.bz2
quagga-3690074a486cfada568975e287d9cbb9e687501f.tar.xz
Merging of euro_ix branch into pipework.
Bring in fixes for bgp dumping with pthreaded BGP Engine. Bring in new "show nexus xxx" command. Fix removal of '~' prompt.
Diffstat (limited to 'bgpd/bgp_dump.c')
-rw-r--r--bgpd/bgp_dump.c1871
1 files changed, 1359 insertions, 512 deletions
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
index 7c36825c..860ed0cb 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -1,4 +1,4 @@
-/* BGP-4 dump routine
+/* BGP-4 bd routine
Copyright (C) 1999 Kunihiro Ishiguro
This file is part of GNU Zebra.
@@ -26,660 +26,1212 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "sockunion.h"
#include "command.h"
#include "prefix.h"
-#include "thread.h"
#include "linklist.h"
-#include "bgpd/bgp_table.h"
#include "qpath.h"
#include "qstring.h"
+#include "memory.h"
+#include "qtimers.h"
+#include "mqueue.h"
+#include "qiovec.h"
+#include "bgpd/bgp_dump.h"
#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
-#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_engine.h"
+
+/*==============================================================================
+ * This will bd BGP state in MRT ("Multi-threaded Routing Toolkit") form.
+ * See draft-ietf-grow-mrt.
+ *
+ * Three, independent, dumps are supported (simultaneously):
+ *
+ * ALL: dumps all BGP messages received, plus FSM state transitions.
+ *
+ * UPDATES: dumps all BGP UPDATE messages received
+ *
+ * TABLE: periodically dumps entire BGP RIB
+ *
+ * For ALL and UPDATES, a new file is opened periodically. For TABLE, a new
+ * file is opened for each bd.
+ *
+ * The ALL and UPDATES dumps are done in the BGP Engine, though the periodic
+ * opening of new files is done in the Routing Engine and the result passed to
+ * the BGP Engine.
+ */
+
+/* MRT message types
+ */
+enum MRT_MT_TYPES {
+ MRT_MT_NULL = 0, /* deprecated */
+ MRT_MT_START = 1, /* deprecated */
+ MRT_MT_DIE = 2, /* deprecated */
+ MRT_MT_I_AM_DEAD = 3, /* deprecated */
+ MRT_MT_PEER_DOWN = 4, /* deprecated */
+ MRT_MT_BGP = 5, /* deprecated */
+ MRT_MT_RIP = 6, /* deprecated */
+ MRT_MT_IDRP = 7, /* deprecated */
+ MRT_MT_RIPNG = 8, /* deprecated */
+ MRT_MT_BGP4PLUS = 9, /* deprecated */
+ MRT_MT_BGP4PLUS_01 = 10, /* deprecated */
+
+ MRT_MT_OSPFv2 = 11,
+ MRT_MT_TABLE_DUMP = 12, /* BGP routing table dump */
+ MRT_MT_TABLE_DUMP_V2 = 13, /* BGP routing table dump, v2 */
+ MRT_MT_BGP4MP = 16, /* BGP4 with MP extensions */
+ MRT_MT_BGP4MP_ET = 17, /* as above with Extended Times */
+ MRT_MT_ISIS = 32,
+ MRT_MT_ISIS_ET = 33, /* as above with Extended Times */
+ MRT_MT_OSPFv3 = 48,
+ MRT_MT_OSPFv3_ET = 49, /* as above with Extended Times */
+
+} ;
+
+/* MRT Common Header and other sizes
+ */
+enum
+{
+ MRT_COMMON_HEADER_SIZE = 12, /* Timestamp(4), Type(2), Subtype(2)
+ * Length(4)
+ * NB: length excludes header */
+ MRT_BGP4MP_HEADER_SIZE = 44 /* Peer AS(4), Local AS(4),
+ * Interface Index(2),
+ * Address Family(2),
+ * Peer IP(16), Local IP(16) */
+} ;
+
+/* MRT subtypes of MRT_MT_BGP4MP
+ */
+enum MRT_MT_BGP4MP_SUBTYPES
+{
+ MRT_MST_BGP4MP_STATE_CHANGE = 0,
+ MRT_MST_BGP4MP_MESSAGE = 1,
+
+ MRT_MST_BGP4MP_ENTRY = 2, /* deprecated */
+ MRT_MST_BGP4MP_SNAPSHOT = 3, /* deprecated */
+
+ MRT_MST_BGP4MP_MESSAGE_AS4 = 4,
+ MRT_MST_BGP4MP_STATE_CHANGE_AS4 = 5,
+
+ MRT_MST_BGP4MP_MESSAGE_LOCAL = 6,
+ MRT_MST_BGP4MP_MESSAGE_AS4_LOCAL = 7,
+} ;
-enum bgp_dump_type
+/* MRT subtypes of MRT_MT_TABLE_DUMP_V2
+ */
+enum MRT_MT_TABLE_DUMP_V2_SUBTYPES
+{
+ MRT_MST_TDV2_PEER_INDEX_TABLE = 1,
+ MRT_MST_TDV2_RIB_IPV4_UNICAST = 2,
+ MRT_MST_TDV2_RIB_IPV4_MULTICAST = 3,
+ MRT_MST_TDV2_RIB_IPV6_UNICAST = 4,
+ MRT_MST_TDV2_RIB_IPV6_MULTICAST = 5,
+ MRT_MST_TDV2_RIB_GENERIC = 6,
+} ;
+
+/* Values for MRT_MST_TDV2_PEER_INDEX_TABLE message
+ */
+enum
+{
+ MRT_TDV2_PEER_INDEX_TABLE_IPV4 = 0,
+ MRT_TDV2_PEER_INDEX_TABLE_IPV6 = 1,
+ MRT_TDV2_PEER_INDEX_TABLE_AS2 = 0,
+ MRT_TDV2_PEER_INDEX_TABLE_AS4 = 2,
+} ;
+
+/* Values for FSM states
+ */
+enum
+{
+ MRT_FSM_UNDEF = 0, /* Not defined in the standard */
+
+ MRT_FSM_Idle = 1,
+ MRT_FSM_Connect = 2,
+ MRT_FSM_Active = 3,
+ MRT_FSM_OpenSent = 4,
+ MRT_FSM_OpenConfirm = 5,
+ MRT_FSM_Established = 6,
+} ;
+
+/* Values for AFI in BGP4MP messages
+ */
+enum
+{
+ MRT_AFI_IPv4 = 1,
+ MRT_AFI_IPv6 = 2,
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dump control definitions, structures etc.
+ */
+enum
+{
+ /* This is the size allocated for MRT messages.
+ *
+ * For dumping packets in MRT_MST_BGP4MP_MESSAGE_AS4 form, the maximum
+ * required is as per the CONFIRM below.
+ *
+ * The previous code used a buffer of that size. UNFORTUNATELY, the TABLE
+ * dumps actually require indefinite size buffering -- so we here allocate
+ * a sizeable lump.
+ *
+ * TODO: fix TABLE dump and indefinite size MRT messages.
+ */
+ BGP_DUMP_BUFFER_SIZE = 16 * 1024
+} ;
+
+CONFIRM(BGP_DUMP_BUFFER_SIZE >= (MRT_COMMON_HEADER_SIZE +
+ MRT_BGP4MP_HEADER_SIZE +
+ (BGP_MAX_PACKET_SIZE * 2))) ;
+
+typedef enum bgp_dump_type
{
BGP_DUMP_ALL,
BGP_DUMP_UPDATES,
- BGP_DUMP_ROUTES
-};
+ BGP_DUMP_TABLE,
-enum MRT_MSG_TYPES {
- MSG_NULL,
- MSG_START, /* sender is starting up */
- MSG_DIE, /* receiver should shut down */
- MSG_I_AM_DEAD, /* sender is shutting down */
- MSG_PEER_DOWN, /* sender's peer is down */
- MSG_PROTOCOL_BGP, /* msg is a BGP packet */
- MSG_PROTOCOL_RIP, /* msg is a RIP packet */
- MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */
- MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */
- MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */
- MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
- MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */
- MSG_TABLE_DUMP, /* routing table dump */
- MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */
-};
+ BGP_DUMP_TABLE_NOW, /* One shot */
-static int bgp_dump_interval_func (struct thread *);
+ BGP_DUMP_TYPE_COUNT, /* for arrays of dumps */
+} bgp_dump_type_t ;
struct bgp_dump
{
- enum bgp_dump_type type;
+ const char* typename ; /* for logging */
- char *filename;
+ char* filename ; /* complete path, as opened */
+ int fd ; /* < 0 if not open */
- FILE *fp;
+ struct stream* obuf ; /* set up when file is opened */
- unsigned int interval;
+ uint seq ; /* for TABLE (and TABLE_NOW) dumps */
- char *interval_str;
+ char* buf ; /* for TABLE (and TABLE_NOW) dumps... */
+ char* p ; /* ...put pointer */
+ uint s ; /* ...buffer size */
+ uint h ; /* ...amount of space have left */
+} ;
- struct thread *t_interval;
-};
+typedef struct bgp_dump bgp_dump_t ;
+typedef struct bgp_dump* bgp_dump ;
-/* BGP packet dump output buffer. */
-struct stream *bgp_dump_obuf;
+struct bgp_dump_control
+{
+ bgp_dump_type_t type ;
+
+ uint interval;
+
+ char* template ;
+ char* interval_str;
+
+ qtimer qtr ;
+ bgp_dump bd ;
+} ;
-/* BGP dump strucuture for 'dump bgp all' */
-struct bgp_dump bgp_dump_all;
+typedef struct bgp_dump_control bgp_dump_control_t ;
+typedef struct bgp_dump_control* bgp_dump_control ;
-/* BGP dump structure for 'dump bgp updates' */
-struct bgp_dump bgp_dump_updates;
+/* The control structures for the various bd types -- these belong to the
+ * Routing Engine.
+ *
+ * Start with none at all.
+ */
+static bgp_dump_control bgp_dumps[BGP_DUMP_TYPE_COUNT] = { NULL } ;
-/* BGP dump structure for 'dump bgp routes' */
-struct bgp_dump bgp_dump_routes;
+/* These belong to the BGP Engine
+ */
+static bgp_dump bd_all = NULL ;
+static bgp_dump bd_updates = NULL ;
-/* Dump whole BGP table is very heavy process. */
-struct thread *t_bgp_dump_routes;
+bool bgp_dump_state_flag = false ;
+bool bgp_dump_packet_flag = false ;
-/* Some define for BGP packet dump. */
-static FILE *
-bgp_dump_open_file (struct bgp_dump *bgp_dump)
+/* Types of dump -- in CLI form for ALL, UPDATES and TABLE
+ */
+static const char* bgp_dump_name[] =
+{
+ [BGP_DUMP_ALL] = "all",
+ [BGP_DUMP_UPDATES] = "updates",
+ [BGP_DUMP_TABLE] = "routes-mrt",
+
+ [BGP_DUMP_TABLE_NOW] = "one-shot routes-mrt",
+} ;
+
+/*==============================================================================
+ * Dump control
+ */
+static void bgp_dump_engine_set(bgp_dump bd, bgp_dump_type_t type) ;
+static void bgp_dump_timer_expired(qtimer qtr, void* timer_info,
+ qtime_mono_t when) ;
+static void bgp_dump_table(bgp_dump_control bdc) ;
+static void bgp_dump_control_free(bgp_dump_control bdc) ;
+static bgp_dump bgp_dump_free(bgp_dump bd) ;
+
+static bgp_dump bgp_dump_bytes(bgp_dump bd, void* bytes, uint n) ;
+static bgp_dump bgp_dump_flush(bgp_dump bd) ;
+static bgp_dump bgp_dump_put(bgp_dump bd, void* p0, uint n0, void* p1, uint n1);
+static bgp_dump bgp_dump_truncate(bgp_dump bd) ;
+
+/*------------------------------------------------------------------------------
+ * Open new file for the given dump
+ *
+ * This is called when an interval timer goes off, or a new ALL or UPDATES
+ * dump is set, or a TABLES_NOW dump is set.
+ *
+ * If this is ALL or UPDATES, then after successful file open, pass the bd
+ * to the BGP Engine ! Note that does nothing if file open fails, so any
+ * existing dump will continue.
+ *
+ * NB: no file can currently be open on the Routing Engine side.
+ *
+ * NB: creates file if required. Does not, however, truncate -- that is done
+ * just before the file is to be used. (This means that if the same file
+ * is opened for ALL or UPDATES, then the file is truncated on the
+ * BGP Engine side, so between writes to the file !)
+ */
+static bool
+bgp_dump_open_file (bgp_dump_control bdc, struct vty* vty)
{
int ret;
time_t clock;
struct tm tm;
qpath path ;
qstring name ;
- mode_t oldumask;
+ const char* typename ;
+ int fd ;
+ bgp_dump bd ;
+
+ assert(bdc->bd == NULL) ; /* Nothing should be open */
+ typename = bgp_dump_name[bdc->type] ;
+
+ /* Construct filename from template and attempt to open the file.
+ */
time (&clock);
localtime_r(&clock, &tm);
- if (bgp_dump->filename[0] != DIRECTORY_SEP)
- {
- path = vty_getcwd(NULL) ;
- qpath_append_str(path, bgp_dump->filename) ;
- }
- else
- path = qpath_set(NULL, bgp_dump->filename) ;
+ name = qs_new_size(NULL, PATH_MAX) ;
- name = qs_new_size(NULL, qpath_len(path) + 60) ;
+ ret = strftime (qs_char_nn(name), qs_size_nn(name), bdc->template, &tm);
- ret = strftime (qs_char_nn(name), qs_len_nn(name), qpath_string(path), &tm);
+ if (ret != 0)
+ {
+ mode_t oldumask ;
- qpath_free(path) ;
+ qs_set_len_nn(name, ret) ;
+ path = qpath_complete(qpath_set_qs(NULL, name), vty_getcwd(NULL)) ;
- if (ret == 0)
- {
- zlog_warn ("bgp_dump_open_file: strftime error");
- return NULL;
- }
+ oldumask = umask(0777 & ~LOGFILE_MASK);
+ fd = open(qpath_string(path), O_WRONLY | O_CREAT, LOGFILE_MASK) ;
- if (bgp_dump->fp)
- fclose (bgp_dump->fp);
+ if (fd < 0)
+ {
+ int err ;
+ err = errno ;
- oldumask = umask(0777 & ~LOGFILE_MASK);
- bgp_dump->fp = fopen (qs_char_nn(name), "w");
+ zlog_warn("Failed to open %s dump file %s: %s", typename,
+ qpath_string(path), errtoa(err, 0).str) ;
+ if (vty != NULL)
+ vty_out(vty, "Failed to open file %s: %s\n",
+ qpath_string(path), errtoa(err, 0).str) ;
+ } ;
- if (bgp_dump->fp == NULL)
- {
- zlog_warn("bgp_dump_open_file: %s: %s", qs_char_nn(name),
- errtoa(errno, 0).str);
- umask(oldumask);
- return NULL;
+ umask(oldumask) ;
}
- umask(oldumask);
+ else
+ {
+ fd = -1 ;
+ path = NULL ;
+
+ zlog_warn ("Failed to open %s dump file: strftime failed on '%s'",
+ typename, bdc->template) ;
+ if (vty != NULL)
+ vty_out(vty, "Failed in strftime on '%s'\n", bdc->template) ;
+ } ;
+
+ /* If opened OK, construct the bgp_dump object, log success and then either
+ * set the bdc->bd or pass the bd to the BGP Engine.
+ */
+ if (fd >= 0)
+ {
+ bd = XCALLOC(MTYPE_BGP_DUMP, sizeof(bgp_dump_t)) ;
+
+ /* Zeroising sets bd->buf etc NULL
+ */
+ bd->typename = typename ;
+ bd->filename = XSTRDUP(MTYPE_BGP_DUMP, qpath_string(path)) ;
+ bd->fd = fd ;
+ bd->obuf = stream_new(BGP_DUMP_BUFFER_SIZE) ;
+
+ zlog_info("Opened %s dump file: %s", bd->typename, bd->filename) ;
+
+ /* If this is ALL or UPDATE, then we now pass the open file to the
+ * BGP Engine, and forget all about it.
+ *
+ * Otherwise, set the bgp_dump_control's bgp_dump
+ */
+ if ( (bdc->type == BGP_DUMP_ALL) || (bdc->type == BGP_DUMP_UPDATES) )
+ bgp_dump_engine_set(bd, bdc->type) ;
+ else
+ bdc->bd = bd ;
+ } ;
- return bgp_dump->fp;
+ /* Release buffer and return success/failure.
+ */
+ qs_free(name) ;
+ qpath_free(path) ;
+
+ return (fd >= 0) ;
}
-static int
-bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
+/*------------------------------------------------------------------------------
+ * Set a new interval -- if zero, turn off timer.
+ */
+static void
+bgp_dump_set_timer(bgp_dump_control bdc)
{
- int secs_into_day;
- time_t t;
- struct tm tm;
+ uint interval = bdc->interval ;
if (interval > 0)
{
- /* Periodic dump every interval seconds */
- if ((interval < 86400) && ((86400 % interval) == 0))
+ /* Periodic dump every interval seconds
+ */
+ if ((interval < (24 * 60 * 60)) && (((24 * 60 * 60) % interval) == 0))
{
- /* Dump at predictable times: if a day has a whole number of
- * intervals, dump every interval seconds starting from midnight
- */
+ /* Dump at predictable times: if a day has a whole number of
+ * intervals, dump every interval seconds starting from midnight
+ */
+ int secs_into_day;
+ time_t t;
+ struct tm tm;
+
(void) time(&t);
localtime_r(&t, &tm);
- secs_into_day = tm.tm_sec + 60*tm.tm_min + 60*60*tm.tm_hour;
+ secs_into_day = tm.tm_sec + 60 * (tm.tm_min + 60 * tm.tm_hour) ;
interval = interval - secs_into_day % interval; /* always > 0 */
}
- bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
- bgp_dump, interval);
+
+ if (bdc->qtr == NULL)
+ bdc->qtr = qtimer_init_new(NULL, routing_nexus->pile,
+ bgp_dump_timer_expired, bdc) ;
+ qtimer_set(bdc->qtr, qt_add_monotonic(QTIME(interval)), NULL) ;
}
else
{
- /* One-off dump: execute immediately, don't affect any scheduled dumps */
- bgp_dump->t_interval = thread_add_event (master, bgp_dump_interval_func,
- bgp_dump, 0);
- }
+ if (bdc->qtr != NULL)
+ qtimer_unset(bdc->qtr) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Timer expired function -- called by qtimer.
+ *
+ * For all except BGP_DUMP_TABLE_NOW, open a new dump file.
+ *
+ * For BGP_DUMP_TABLE and BGP_DUMP_TABLE_NOW dump the table and close the
+ * dump file.
+ *
+ * For BGP_DUMP_ALL and BGP_DUMP_UPDATES, pass the new file to the BGP Engine,
+ * which will close the current and adopt the new.
+ *
+ * For all but BGP_DUMP_TABLE_NOW, set a new interval time.
+ */
+static void
+bgp_dump_timer_expired(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ bgp_dump_control bdc = timer_info ;
- return 0;
-}
+ assert(bdc->qtr == qtr) ;
-/* Dump common header. */
+ switch(bdc->type)
+ {
+ case BGP_DUMP_ALL:
+ case BGP_DUMP_UPDATES:
+ bgp_dump_open_file(bdc, NULL) ; /* rotate -- posts new file to
+ * the BGP Engine */
+ break ;
+
+ case BGP_DUMP_TABLE:
+ if (bgp_dump_open_file(bdc, NULL))
+ bgp_dump_table(bdc) ; /* if open succeeds */
+ break ;
+
+ case BGP_DUMP_TABLE_NOW:
+ bgp_dump_table(bdc) ; /* file already open */
+ bdc->interval = 0 ; /* do not repeat */
+ break ;
+
+ default: /* should not happen ! */
+ bdc->interval = 0 ;
+ break ;
+ } ;
+
+ /* Unless is BGP_DUMP_TABLE_NOW, set a new interval time. Note that for a
+ * TABLE dump, if the current dump took longer than the interval, then will
+ * skip one or more dumps.
+ *
+ * Otherwise, this is a BGP_DUMP_TABLE_NOW, which has completed, and can
+ * now be forgotten.
+ */
+ if (bdc->type != BGP_DUMP_TABLE_NOW)
+ bgp_dump_set_timer(bdc) ;
+ else
+ bgp_dump_control_free(bdc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given bgp_dump control, completely.
+ *
+ * Closes and frees any bgp_dump.
+ */
static void
-bgp_dump_header (struct stream *obuf, int type, int subtype)
+bgp_dump_control_free(bgp_dump_control bdc)
{
- time_t now;
+ if (bdc != NULL)
+ {
+ bgp_dump_free(bdc->bd) ;
- /* Set header. */
- time (&now);
+ XFREE(MTYPE_BGP_DUMP, bdc->template) ;
+ XFREE(MTYPE_BGP_DUMP, bdc->interval_str) ;
- /* Put dump packet header. */
- stream_putl (obuf, now);
- stream_putw (obuf, type);
- stream_putw (obuf, subtype);
+ qtimer_free(bdc->qtr) ; /* unsets any running timer */
- stream_putl (obuf, 0); /* len */
-}
+ assert(bgp_dumps[bdc->type] == bdc) ;
+ bgp_dumps[bdc->type] = NULL ;
-static void
-bgp_dump_set_size (struct stream *s, int type)
+ XFREE(MTYPE_BGP_DUMP, bdc) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given bgp_dump, closing any file.
+ *
+ * Touches nothing beyond the bgp_dump object, so can be used in both the
+ * Routing Engine and the BGP Engine.
+ */
+static bgp_dump
+bgp_dump_free(bgp_dump bd)
{
- stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE);
-}
+ if (bd != NULL)
+ {
+ if (bd->fd >= 0)
+ {
+ close(bd->fd) ;
+ zlog_info("Closed %s dump file: %s", bd->typename, bd->filename) ;
+ } ;
+
+ XFREE(MTYPE_BGP_DUMP, bd->filename) ;
+ stream_free(bd->obuf) ;
+ XFREE(MTYPE_TMP, bd->buf) ;
+
+ XFREE(MTYPE_BGP_DUMP, bd) ;
+ } ;
+
+ return NULL ;
+} ;
+/*==============================================================================
+ * TABLE (and TABLE_NOW) dumps
+ */
+static bgp_dump bgp_dump_routes_index_table(bgp_dump bd, struct bgp *bgp) ;
+static bgp_dump bgp_dump_routes_family(bgp_dump bd, struct bgp *bgp, afi_t afi);
+
+static struct stream* bgp_dump_header (bgp_dump bd, int type, int subtype) ;
+static void bgp_dump_set_size (struct stream *s, int plus) ;
+
+/*------------------------------------------------------------------------------
+ * Perform a TABLE or TABLE_NOW dump, and close the dump file when finished.
+ */
static void
-bgp_dump_routes_index_table(struct bgp *bgp)
+bgp_dump_table(bgp_dump_control bdc)
{
- struct peer *peer;
- struct listnode *node;
- uint16_t peerno = 0;
- struct stream *obuf;
+ struct bgp *bgp ;
+ bgp_dump bd ;
- obuf = bgp_dump_obuf;
- stream_reset (obuf);
+ if (qdebug)
+ assert((bdc->bd != NULL) && (bdc->bd->fd >= 0)) ; /* will be open */
- /* MRT header */
- bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE);
+ bd = bgp_dump_truncate(bdc->bd) ; /* could (conceivably) fail */
- /* Collector BGP ID */
- stream_put_in_addr (obuf, &bgp->router_id);
+ bgp = bgp_get_default();
- /* View name */
- if(bgp->name)
+ if ((bgp != NULL) && (bd != NULL))
{
- stream_putw (obuf, strlen(bgp->name));
- stream_put(obuf, bgp->name, strlen(bgp->name));
- }
- else
- {
- stream_putw(obuf, 0);
- }
+ /* Set up buffer for dumping
+ */
+ bd->s = 128 * 1024 ; /* a chunky buffer */
- /* Peer count */
- stream_putw (obuf, listcount(bgp->peer));
-
- /* Walk down all peers */
- for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
- {
+ bd->buf = XMALLOC(MTYPE_TMP, bd->s) ;
+ bd->p = bd->buf ;
+ bd->h = bd->s ;
- /* Peer's type */
- if (sockunion_family(&peer->su) == AF_INET)
- {
- stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
- }
-#ifdef HAVE_IPV6
- else if (sockunion_family(&peer->su) == AF_INET6)
- {
- stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
- }
-#endif /* HAVE_IPV6 */
+ bd->seq = 0 ;
- /* Peer's BGP ID */
- stream_put_in_addr (obuf, &peer->remote_id);
+ /* Construct the index table for all peers and output it.
+ */
+ bd = bgp_dump_routes_index_table(bd, bgp) ;
- /* Peer's IP address */
- if (sockunion_family(&peer->su) == AF_INET)
- {
- stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
- }
+ /* Now dump all the routes
+ */
+ bd = bgp_dump_routes_family(bd, bgp, AFI_IP) ;
#ifdef HAVE_IPV6
- else if (sockunion_family(&peer->su) == AF_INET6)
- {
- stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
- IPV6_MAX_BYTELEN);
- }
+ bd = bgp_dump_routes_family(bd, bgp, AFI_IP6) ;
#endif /* HAVE_IPV6 */
- /* Peer's AS number. */
- /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */
- stream_putl (obuf, peer->as);
+ /* Flush anything left to go
+ */
+ bgp_dump_flush(bd) ;
+ } ;
+
+ bdc->bd = bgp_dump_free(bd) ; /* Frees the buf, if any */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put the Peer Index Table for the coming route dump
+ *
+ * Returns: bd if OK, or NULL if failed (or was NULL already).
+ */
+static bgp_dump
+bgp_dump_routes_index_table(bgp_dump bd, struct bgp *bgp)
+{
+ struct stream* s ;
+ struct peer *peer ;
+ struct listnode *node ;
+ uint16_t peerno, len ;
+
+ if (bd == NULL)
+ return NULL ; /* get out, quick, if no file */
+
+ /* MRT header
+ */
+ s = bgp_dump_header(bd, MRT_MT_TABLE_DUMP_V2, MRT_MST_TDV2_PEER_INDEX_TABLE) ;
+
+ stream_put_in_addr(s, &bgp->router_id) ; /* Collector BGP ID */
+
+ len = (bgp->name != NULL) ? strlen(bgp->name) : 0 ;
+ stream_putw(s, len) ; /* View name */
+ if (len != 0)
+ stream_put(s, bgp->name, len) ;
+
+ stream_putw (s, listcount(bgp->peer)); /* Peer count */
+
+ /* Walk down all peers and construct peer index entries for the known
+ * address families.
+ *
+ * Note that the type field gives only the type of the address of the peer
+ * and the form of the AS number -- it has nothing to do with the
+ * capabilities of the peer; in particular nothing to do with whether the
+ * peer is an AS4 speaker.
+ */
+ peerno = 0 ; /* index for first entry */
+ for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
+ {
+ sockunion su ;
+ uint type ;
- /* Store the peer number for this peer */
- peer->table_dump_index = peerno;
- peerno++;
- }
+ su = &peer->su ;
- bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
+ type = MRT_TDV2_PEER_INDEX_TABLE_AS4 ; /* always, for simplicity */
- fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
- fflush (bgp_dump_routes.fp);
-}
+ switch (sockunion_family(su))
+ {
+ case AF_INET:
+ type |= MRT_TDV2_PEER_INDEX_TABLE_IPV4 ;
+ break ;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ type |= MRT_TDV2_PEER_INDEX_TABLE_IPV6 ;
+ break ;
+#endif /* HAVE_IPV6 */
-/* Runs under child process. */
-static unsigned int
-bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
+ default:
+ continue ; /* Ignore if not known family ! */
+ } ;
+
+ stream_putc (s, type) ; /* Peer's type */
+ stream_put_in_addr (s, &peer->remote_id); /* Peer's BGP ID */
+ stream_write (s, sockunion_get_addr(su), /* Peer's IP address */
+ sockunion_get_addr_len(su)) ;
+ stream_putl (s, peer->as); /* Peer's AS (AS4-wise) */
+
+ peer->table_dump_index = peerno ; /* set peer number */
+
+ ++peerno ;
+ } ;
+
+ bgp_dump_set_size(s, 0);
+
+ return bgp_dump_bytes(bd, STREAM_DATA(s), stream_get_endp (s)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dump all routes for the given address family.
+ *
+ * Note: previous comments suggest that this might run under a child
+ * process -- which would be a good idea -- but does not appear to
+ * ever have been the case ?
+ *
+ * Returns: bd if OK, or NULL if failed (or was NULL already).
+ *
+ * Updates the sequence number.
+ *
+ * NB: assumes that th afi is known !
+ */
+static bgp_dump
+bgp_dump_routes_family(bgp_dump bd, struct bgp *bgp, afi_t afi)
{
- struct stream *obuf;
- struct bgp_info *info;
struct bgp_node *rn;
- struct bgp *bgp;
struct bgp_table *table;
+ int subtype ;
- bgp = bgp_get_default ();
- if (!bgp)
- return seq;
-
- if (bgp_dump_routes.fp == NULL)
- return seq;
+ if (bd == NULL)
+ return NULL ; /* get out, quick, if no file */
- /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
- so this should only be done on the first call to bgp_dump_routes_func.
- ( this function will be called once for ipv4 and once for ipv6 ) */
- if(first_run)
- bgp_dump_routes_index_table(bgp);
-
- obuf = bgp_dump_obuf;
- stream_reset(obuf);
-
- /* Walk down each BGP route. */
- table = bgp->rib[afi][SAFI_UNICAST];
-
- for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ /* Establish subtype of dump entries.
+ */
+ switch (afi)
{
- if(!rn->info)
- continue;
+ case AFI_IP:
+ subtype = MRT_MST_TDV2_RIB_IPV4_UNICAST ;
+ break ;
- stream_reset(obuf);
-
- /* MRT header */
- if (afi == AFI_IP)
- {
- bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST);
- }
#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- {
- bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST);
- }
+ case AFI_IP6:
+ subtype = MRT_MST_TDV2_RIB_IPV6_UNICAST ;
+ break ;
#endif /* HAVE_IPV6 */
- /* Sequence number */
- stream_putl(obuf, seq);
+ default:
+ return bd ; /* do nothing if family unknown */
+ } ;
- /* Prefix length */
- stream_putc (obuf, rn->p.prefixlen);
+ /* Get the required table -- exit, quick, if none
+ */
+ table = bgp->rib[afi][SAFI_UNICAST] ;
- /* Prefix */
- if (afi == AFI_IP)
- {
- /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
- stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8);
- }
-#ifdef HAVE_IPV6
- else if (afi == AFI_IP6)
- {
- /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
- stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8);
- }
-#endif /* HAVE_IPV6 */
+ if (table == NULL)
+ return bd ;
+
+ /* Walk down each BGP route
+ */
+ for (rn = bgp_table_top (table) ; rn != NULL ; rn = bgp_route_next (rn))
+ {
+ struct stream* s;
+ struct bgp_info *info;
+ int sizep ;
+ uint16_t entry_count ;
+
+ if (rn->info == NULL)
+ continue;
- /* Save where we are now, so we can overwride the entry count later */
- int sizep = stream_get_endp(obuf);
+ /* MRT header for MRT_MT_TABLE_DUMP_V2 type message
+ */
+ s = bgp_dump_header (bd, MRT_MT_TABLE_DUMP_V2, subtype) ;
- /* Entry count */
- uint16_t entry_count = 0;
+ stream_putl(s, bd->seq) ; /* Sequence number */
+ stream_putc(s, rn->p.prefixlen) ; /* Prefix length */
+ stream_write(s, &rn->p.u.prefix, /* Prefix */
+ (rn->p.prefixlen+7)/8) ; /* (zero is OK) */
- /* Entry count, note that this is overwritten later */
- stream_putw(obuf, 0);
+ sizep = stream_get_endp(s); /* will set count later */
+ entry_count = 0;
+ stream_putw(s, entry_count); /* entry count, so far */
- for (info = rn->info; info; info = info->info_next)
+ /* Cycle through the known attributes for this prefix */
+ for (info = rn->info ; info != NULL ; info = info->info_next)
{
entry_count++;
/* Peer index */
- stream_putw(obuf, info->peer->table_dump_index);
+ stream_putw(s, info->peer->table_dump_index);
/* Originated */
- stream_putl (obuf, bgp_wall_clock(info->uptime));
+ stream_putl (s, bgp_wall_clock(info->uptime));
- /* Dump attribute. */
- /* Skip prefix & AFI/SAFI for MP_NLRI */
- bgp_dump_routes_attr (obuf, info->attr, &rn->p);
+ /* Dump attribute. */
+ /* Skip prefix & AFI/SAFI for MP_NLRI */
+ bgp_dump_routes_attr (s, info->attr, &rn->p);
}
/* Overwrite the entry count, now that we know the right number */
- stream_putw_at (obuf, sizep, entry_count);
+ stream_putw_at (s, sizep, entry_count);
- seq++;
+ bgp_dump_set_size(s, 0) ;
- bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
- fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
+ bd = bgp_dump_bytes(bd, STREAM_DATA(s), stream_get_endp (s)) ;
+ if (bd == NULL)
+ break ;
+ ++bd->seq ;
}
- fflush (bgp_dump_routes.fp);
+ return bd ;
+} ;
- return seq;
-}
+/*==============================================================================
+ * TABLE (and TABLE_NOW) dumps
+ */
-static int
-bgp_dump_interval_func (struct thread *t)
+/*------------------------------------------------------------------------------
+ * Reset given stream and construct common header for all MRT packets
+ */
+static struct stream*
+bgp_dump_header (bgp_dump bd, int type, int subtype)
{
- struct bgp_dump *bgp_dump;
- bgp_dump = THREAD_ARG (t);
- bgp_dump->t_interval = NULL;
+ static struct stream* s ;
- /* Reschedule dump even if file couldn't be opened this time... */
- if (bgp_dump_open_file (bgp_dump) != NULL)
- {
- /* In case of bgp_dump_routes, we need special route dump function. */
- if (bgp_dump->type == BGP_DUMP_ROUTES)
- {
- unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0);
-#ifdef HAVE_IPV6
- bgp_dump_routes_func (AFI_IP6, 0, seq);
-#endif /* HAVE_IPV6 */
- /* Close the file now. For a RIB dump there's no point in leaving
- * it open until the next scheduled dump starts. */
- fclose(bgp_dump->fp); bgp_dump->fp = NULL;
- }
- }
+ s = bd->obuf ;
+ stream_reset (s);
- /* if interval is set reschedule */
- if (bgp_dump->interval > 0)
- bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
+ stream_putl (s, time(NULL));
+ stream_putw (s, type);
+ stream_putw (s, subtype);
+ stream_putl (s, 0); /* len */
- return 0;
+ return s ;
}
-/* Dump common information. */
+/*------------------------------------------------------------------------------
+ * Set size of MRT packet to be size of stuff in the obuf, less the header,
+ * plus the given size.
+ *
+ * NB: depends on the header being at the start of the given stream.
+ */
static void
-bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4)
+bgp_dump_set_size (struct stream *s, int plus)
{
- char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
- /* Source AS number and Destination AS number. */
- if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
- {
- stream_putl (obuf, peer->as);
- stream_putl (obuf, peer->local_as);
- }
- else
- {
- stream_putw (obuf, peer->as);
- stream_putw (obuf, peer->local_as);
- }
-
- if (peer->su.sa.sa_family == AF_INET)
- {
- stream_putw (obuf, peer->ifindex);
- stream_putw (obuf, AFI_IP);
+ stream_putl_at (s, 8, stream_get_endp (s) - MRT_COMMON_HEADER_SIZE + plus);
+}
- stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
+/*------------------------------------------------------------------------------
+ * Put bytes to dump buffer
+ *
+ * Returns: bd if OK, or NULL if failed (or was NULL already).
+ *
+ * If fails, closes the file and frees the bd.
+ */
+static bgp_dump
+bgp_dump_bytes(bgp_dump bd, void* bytes, uint n)
+{
+ if (bd == NULL)
+ return NULL ; /* get out, quick, if no file */
- if (peer->su_local)
- stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN);
- else
- stream_put (obuf, empty, IPV4_MAX_BYTELEN);
- }
-#ifdef HAVE_IPV6
- else if (peer->su.sa.sa_family == AF_INET6)
+ while (n > 0)
{
- /* Interface Index and Address family. */
- stream_putw (obuf, peer->ifindex);
- stream_putw (obuf, AFI_IP6);
+ uint t ;
- /* Source IP Address and Destination IP Address. */
- stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
+ t = bd->h ;
- if (peer->su_local)
- stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
+ if (t >= n)
+ t = n ;
else
- stream_put (obuf, empty, IPV6_MAX_BYTELEN);
- }
-#endif /* HAVE_IPV6 */
-}
-
-/* Dump BGP status change. */
-void
-bgp_dump_state (struct peer *peer, int status_old, int status_new)
+ {
+ if (t == 0)
+ {
+ if ((bd = bgp_dump_flush(bd)) == NULL)
+ break ;
+
+ t = bd->h ;
+ if (t >= n)
+ t = n ;
+ } ;
+ } ;
+
+ memcpy(bd->p, bytes, t) ;
+
+ bd->p += t ;
+ bd->h -= t ;
+
+ bytes = (char*)bytes + t ;
+ n -= t ;
+ } ;
+
+ return bd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush dump buffer to dump
+ *
+ * Returns: bd if OK, or NULL if failed (or was NULL already).
+ *
+ * If fails, closes the file and frees the bd.
+ */
+static bgp_dump
+bgp_dump_flush(bgp_dump bd)
{
- struct stream *obuf;
-
- /* If dump file pointer is disabled return immediately. */
- if (bgp_dump_all.fp == NULL)
- return;
-
- /* Make dump stream. */
- obuf = bgp_dump_obuf;
- stream_reset (obuf);
-
- bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4);
- bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/
+ uint n ;
- stream_putw (obuf, status_old);
- stream_putw (obuf, status_new);
-
- /* Set length. */
- bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
-
- /* Write to the stream. */
- fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp);
- fflush (bgp_dump_all.fp);
-}
+ if (bd != NULL)
+ {
+ n = bd->p - bd->buf ;
+ if (n > 0)
+ bd = bgp_dump_put(bd, bd->buf, n, NULL, 0) ;
-static void
-bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
- struct stream *packet)
+ if (bd != NULL)
+ {
+ bd->p = bd->buf ;
+ bd->h = bd->s ;
+ } ;
+ } ;
+
+ return bd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put one or two lumps of data to dump -- first lump may NOT be empty.
+ *
+ * Returns: bd if OK, or NULL if failed (or was NULL already).
+ *
+ * If fails, closes the file and frees the bd.
+ */
+static bgp_dump
+bgp_dump_put(bgp_dump bd, void* p0, uint n0, void* p1, uint n1)
{
- struct stream *obuf;
+ struct iovec iov[2] ;
+ int n ;
+ int w ;
- /* If dump file pointer is disabled return immediately. */
- if (bgp_dump->fp == NULL)
- return;
+ if (bd == NULL)
+ return NULL ; /* get out, quick, if no file */
- /* Make dump stream. */
- obuf = bgp_dump_obuf;
- stream_reset (obuf);
+ iov[0].iov_base = p0 ;
+ iov[0].iov_len = n0 ;
- /* Dump header and common part. */
- if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
+ if (n1 > 0)
{
- bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4);
+ iov[1].iov_base = p1 ;
+ iov[1].iov_len = n1 ;
+
+ n = 2 ;
}
else
+ n = 1 ;
+
+ do
+ w = iovec_write_nb(bd->fd, iov, n) ;
+ while (w > 0) ;
+
+ if (w == 0)
+ return bd ;
+
+ /* Error writing to the dump file.
+ */
+ zlog_warn("failed writing %s dump file %s: %s", bd->typename, bd->filename,
+ errtoa(errno, 0).str) ;
+ return bgp_dump_free(bd) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Truncate the dump file (if any) and seek to start of (now) empty file
+ *
+ * Returns: bd if OK, or NULL if failed (or was already NULL).
+ *
+ * If fails, closes the file and frees the bd.
+ */
+static bgp_dump
+bgp_dump_truncate(bgp_dump bd)
+{
+ if (bd != NULL)
{
- bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE);
- }
- bgp_dump_common (obuf, peer, 0);
+ /* Truncate: extremely unlikely to fail, but let's be careful out there.
+ *
+ * Something very odd is going on if does fail, so log as "error".
+ */
+ lseek(bd->fd, 0, SEEK_SET) ; /* start from the beginning */
- /* Packet contents. */
- stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
+ while (1)
+ {
+ int r ;
- /* Set length. */
- bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
+ r = ftruncate(bd->fd, 0) ; /* redundant in most cases */
- /* Write to the stream. */
- fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
- fflush (bgp_dump->fp);
-}
+ if (r >= 0)
+ break ; /* OK */
-/* Called from bgp_packet.c when BGP packet is received. */
-void
-bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
-{
- /* bgp_dump_all. */
- bgp_dump_packet_func (&bgp_dump_all, peer, packet);
+ if (errno == EINTR)
+ continue ; /* Try again */
- /* bgp_dump_updates. */
- if (type == BGP_MSG_UPDATE)
- bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
-}
+ zlog_err("failed truncating %s dump file %s: %s", bd->typename,
+ bd->filename, errtoa(errno, 0).str) ;
+ bd = bgp_dump_free(bd) ;
+
+ break ; /* Failed immediately ! */
+ } ;
+ } ;
-static unsigned int
+ return bd ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Parse interval string:
+ *
+ * (\d+[hH])?(\d+[mM])\d+[sS]? returns -1 if invalid
+ */
+static int
bgp_dump_parse_time (const char *str)
{
int i;
int len;
- int seen_h;
- int seen_m;
+ bool seen_h, seen_m, seen_d ;
int time;
- unsigned int total;
+ int total;
time = 0;
total = 0;
- seen_h = 0;
- seen_m = 0;
+ seen_h = false ;
+ seen_m = false ;
+ seen_d = false ;
len = strlen (str);
for (i = 0; i < len; i++)
{
if (isdigit ((int) str[i]))
{
+ seen_d = true ;
time *= 10;
time += str[i] - '0';
}
- else if (str[i] == 'H' || str[i] == 'h')
- {
- if (seen_h)
- return 0;
- if (seen_m)
- return 0;
- total += time * 60 *60;
- time = 0;
- seen_h = 1;
- }
- else if (str[i] == 'M' || str[i] == 'm')
- {
- if (seen_m)
- return 0;
- total += time * 60;
- time = 0;
- seen_h = 1;
- }
else
- return 0;
- }
- return total + time;
-}
-
+ {
+ if (!seen_d)
+ return -1 ; /* must have digit before non-digit */
+
+ switch (str[i])
+ {
+ case 'H':
+ case 'h':
+ if (seen_h || seen_m)
+ return -1 ; /* must not have seen 'h' or 'm' */
+
+ seen_h = true ;
+ total += time * 60 * 60;
+ break ;
+
+ case 'M':
+ case 'm':
+ if (seen_m)
+ return -1 ; /* must not have seen 'm' */
+
+ seen_m = true ;
+ total += time * 60;
+ break ;
+
+ case 'S':
+ case 's':
+ if (i != (len - 1))
+ return -1 ; /* must be at end */
+
+ default:
+ return -1 ; /* unknown character */
+ } ;
+
+ seen_d = false ; /* accept only digit or end */
+ time = 0 ; /* no further value, yet */
+ } ;
+ } ;
+
+ return total + time ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command function to set (or change) state of a dump.
+ *
+ * If an interval is given, then:
+ *
+ * If a dump is set, and both the interval and the path are unchanged, then
+ * do nothing -- debounce.
+ *
+ * Otherwise, close any existing dump, and set the given one running.
+ *
+ * If no interval is given, then:
+ *
+ * TABLE: schedule an immediate, one off TABLE_NOW dump to the given file.
+ * Leaves any existing periodic dump alone.
+ *
+ * ALL:
+ * UPDATES: set a new dump going.
+ *
+ * If a dump is already exists with the same file name, then the
+ * existing file will be truncated.
+ *
+ * In any case, leave with no rotation interval set.
+ */
static int
-bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump,
- enum bgp_dump_type type, const char *path,
- const char *interval_str)
+bgp_dump_set (struct vty *vty, bgp_dump_type_t type, const char *template,
+ const char *interval_str)
{
- unsigned int interval;
-
- if (interval_str)
+ enum cmd_return_code ret ;
+ uint interval;
+ bgp_dump_control bdc ;
+ bool unchanged ;
+
+ assert((type >= 0) && (type < BGP_DUMP_TYPE_COUNT)) ;
+
+ bdc = bgp_dumps[type] ;
+ unchanged = false ;
+
+ /* If we have an interval string then extract interval, and if no actual
+ * change in the bd specification, do nothing.
+ *
+ * If no interval string, then look out for special TABLE_NOW
+ */
+ if (interval_str != NULL)
{
-
/* Check interval string. */
- interval = bgp_dump_parse_time (interval_str);
- if (interval == 0)
+ int get = bgp_dump_parse_time (interval_str);
+ if (get < 60)
{
- vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ if (get < 0)
+ vty_out (vty, "Malformed interval string%s", VTY_NEWLINE) ;
+ else
+ vty_out (vty, "Interval < 60 seconds%s", VTY_NEWLINE) ;
- /* Don't schedule duplicate dumps if the dump command is given twice */
- if (interval == bgp_dump->interval &&
- type == bgp_dump->type &&
- path && bgp_dump->filename && !strcmp (path, bgp_dump->filename))
- {
- return CMD_SUCCESS;
- }
+ return CMD_WARNING;
+ } ;
- /* Set interval. */
- bgp_dump->interval = interval;
- if (bgp_dump->interval_str)
- free (bgp_dump->interval_str);
- bgp_dump->interval_str = strdup (interval_str);
+ interval = get ;
+ /* Don't schedule duplicate dumps if the dump command is given twice */
+ if (bdc != NULL)
+ unchanged = (interval == bdc->interval) &&
+ (strcmp(template, bdc->template) == 0) ;
}
else
{
- interval = 0;
- }
-
- /* Create interval thread. */
- bgp_dump_interval_add (bgp_dump, interval);
-
- /* Set type. */
- bgp_dump->type = type;
+ interval = 0 ;
- /* Set file name. */
- if (bgp_dump->filename)
- free (bgp_dump->filename);
- bgp_dump->filename = strdup (path);
-
- /* This should be called when interval is expired. */
- bgp_dump_open_file (bgp_dump);
-
- return CMD_SUCCESS;
+ if (type == BGP_DUMP_TABLE)
+ {
+ /* A "routes-mrt" type dump with no interval is a one-shot dump, so
+ * set actual type.
+ *
+ * There can be at most one one-shot dump active at any time !
+ *
+ * NB: it's a bit klunky, but we arrange for BGP_DUMP_TABLE_NOW to
+ * run in 1 second's time -- so that command completes before the
+ * big work starts.
+ */
+ type = BGP_DUMP_TABLE_NOW ;
+ interval = 1 ;
+ bdc = NULL ;
+
+ if (bgp_dumps[BGP_DUMP_TABLE_NOW] != NULL)
+ {
+ vty_out (vty, "one-shot routes-mrt dump already pending%s",
+ VTY_NEWLINE) ;
+ return CMD_WARNING;
+ } ;
+ } ;
+ } ;
+
+ /* If required, create the bgp bd control.
+ *
+ * Zeroising sets:
+ *
+ * type -- 0 -- set below
+ *
+ * interval -- 0, none, yet
+ * template -- NULL, none, yet
+ * interval_str -- NULL none, yet
+ * qtr -- NULL none, yet
+ *
+ * bd -- NULL, no file, yet
+ */
+ if (bdc == NULL)
+ {
+ bdc = (bgp_dump_control)XCALLOC(MTYPE_BGP_DUMP,
+ sizeof(bgp_dump_control_t)) ;
+ bdc->type = type ;
+
+ bgp_dumps[type] = bdc ;
+ } ;
+
+ /* Set interval, interval string and template.
+ *
+ * Even if the interval has not changed, we keep the latest interval string,
+ * just in case that has changed.
+ */
+ bdc->interval = interval ;
+
+ XFREE(MTYPE_BGP_DUMP, bdc->interval_str) ; /* sets NULL */
+ if (interval_str != NULL)
+ bdc->interval_str = XSTRDUP(MTYPE_BGP_DUMP, interval_str) ;
+
+ XFREE(MTYPE_BGP_DUMP, bdc->template) ;
+ bdc->template = XSTRDUP(MTYPE_BGP_DUMP, template) ;
+
+ /* If is, in fact, unchanged, get out now.
+ */
+ if (unchanged)
+ return CMD_SUCCESS ;
+
+ /* Unless this is TABLE dump, open a file now if either the template or
+ * the interval have changed.
+ *
+ * Note that for BGP_DUMP_TABLE_NOW changed is always true.
+ *
+ * For ALL and UPDATES dumps, the new file is passed to the BGP Engine. If
+ * the file open fails, any existing file will continue.
+ *
+ * If fails to open, and no interval was set (so won't be able to try again),
+ * then will return CMD_WARNING. For TABLE_NOW, will discard the abortive
+ * bdc -- inter alia, it will not then appear in the configuration output.
+ */
+ ret = CMD_SUCCESS ;
+
+ if (type != BGP_DUMP_TABLE)
+ {
+ if (!bgp_dump_open_file(bdc, vty))
+ {
+ if (interval_str == NULL)
+ {
+ ret = CMD_WARNING ;
+
+ if (type == BGP_DUMP_TABLE_NOW)
+ {
+ bgp_dump_control_free(bdc) ; /* Discard abortive bdc */
+ return ret ;
+ } ;
+ } ;
+ } ;
+ } ;
+
+ /* Set interval timer if required, or stop the current timer
+ */
+ bgp_dump_set_timer(bdc) ;
+
+ return ret ;
}
+/*------------------------------------------------------------------------------
+ * Command function to unset a dump.
+ */
static int
-bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
+bgp_dump_unset (struct vty *vty, bgp_dump_type_t type)
{
- /* Set file name. */
- if (bgp_dump->filename)
- {
- free (bgp_dump->filename);
- bgp_dump->filename = NULL;
- }
-
- /* This should be called when interval is expired. */
- if (bgp_dump->fp)
- {
- fclose (bgp_dump->fp);
- bgp_dump->fp = NULL;
- }
+ bgp_dump_control bdc ;
- /* Create interval thread. */
- if (bgp_dump->t_interval)
- {
- thread_cancel (bgp_dump->t_interval);
- bgp_dump->t_interval = NULL;
- }
+ assert((type >= 0) && (type < BGP_DUMP_TYPE_COUNT)) ;
- bgp_dump->interval = 0;
-
- if (bgp_dump->interval_str)
+ bdc = bgp_dumps[type] ;
+ if (bdc != NULL)
{
- free (bgp_dump->interval_str);
- bgp_dump->interval_str = NULL;
- }
+ bgp_dump_control_free(bdc) ;
+ if ( (type == BGP_DUMP_ALL) || (type == BGP_DUMP_UPDATES) )
+ bgp_dump_engine_set(NULL, type) ;
+ } ;
return CMD_SUCCESS;
}
+/*==============================================================================
+ * The CLI Commands
+ */
+
DEFUN (dump_bgp_all,
dump_bgp_all_cmd,
"dump bgp all PATH",
@@ -688,7 +1240,7 @@ DEFUN (dump_bgp_all,
"Dump all BGP packets\n"
"Output filename\n")
{
- return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL);
+ return bgp_dump_set (vty, BGP_DUMP_ALL, argv[0], NULL);
}
DEFUN (dump_bgp_all_interval,
@@ -700,7 +1252,7 @@ DEFUN (dump_bgp_all_interval,
"Output filename\n"
"Interval of output\n")
{
- return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]);
+ return bgp_dump_set (vty, BGP_DUMP_ALL, argv[0], argv[1]);
}
DEFUN (no_dump_bgp_all,
@@ -711,7 +1263,7 @@ DEFUN (no_dump_bgp_all,
"BGP packet dump\n"
"Dump all BGP packets\n")
{
- return bgp_dump_unset (vty, &bgp_dump_all);
+ return bgp_dump_unset (vty, BGP_DUMP_ALL);
}
DEFUN (dump_bgp_updates,
@@ -722,7 +1274,7 @@ DEFUN (dump_bgp_updates,
"Dump BGP updates only\n"
"Output filename\n")
{
- return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL);
+ return bgp_dump_set (vty, BGP_DUMP_UPDATES, argv[0], NULL);
}
DEFUN (dump_bgp_updates_interval,
@@ -734,7 +1286,7 @@ DEFUN (dump_bgp_updates_interval,
"Output filename\n"
"Interval of output\n")
{
- return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]);
+ return bgp_dump_set (vty, BGP_DUMP_UPDATES, argv[0], argv[1]);
}
DEFUN (no_dump_bgp_updates,
@@ -745,7 +1297,7 @@ DEFUN (no_dump_bgp_updates,
"BGP packet dump\n"
"Dump BGP updates only\n")
{
- return bgp_dump_unset (vty, &bgp_dump_updates);
+ return bgp_dump_unset (vty, BGP_DUMP_UPDATES);
}
DEFUN (dump_bgp_routes,
@@ -756,7 +1308,7 @@ DEFUN (dump_bgp_routes,
"Dump whole BGP routing table\n"
"Output filename\n")
{
- return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL);
+ return bgp_dump_set (vty, BGP_DUMP_TABLE, argv[0], NULL);
}
DEFUN (dump_bgp_routes_interval,
@@ -768,7 +1320,7 @@ DEFUN (dump_bgp_routes_interval,
"Output filename\n"
"Interval of output\n")
{
- return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]);
+ return bgp_dump_set (vty, BGP_DUMP_TABLE, argv[0], argv[1]);
}
DEFUN (no_dump_bgp_routes,
@@ -779,7 +1331,7 @@ DEFUN (no_dump_bgp_routes,
"BGP packet dump\n"
"Dump whole BGP routing table\n")
{
- return bgp_dump_unset (vty, &bgp_dump_routes);
+ return bgp_dump_unset (vty, BGP_DUMP_TABLE);
}
/* BGP node structure. */
@@ -816,53 +1368,41 @@ config_time2str (unsigned int interval)
}
#endif
+/*------------------------------------------------------------------------------
+ * Output the configuration for bgp dumping
+ */
static int
config_write_bgp_dump (struct vty *vty)
{
- if (bgp_dump_all.filename)
- {
- if (bgp_dump_all.interval_str)
- vty_out (vty, "dump bgp all %s %s%s",
- bgp_dump_all.filename, bgp_dump_all.interval_str,
- VTY_NEWLINE);
- else
- vty_out (vty, "dump bgp all %s%s",
- bgp_dump_all.filename, VTY_NEWLINE);
- }
- if (bgp_dump_updates.filename)
- {
- if (bgp_dump_updates.interval_str)
- vty_out (vty, "dump bgp updates %s %s%s",
- bgp_dump_updates.filename, bgp_dump_updates.interval_str,
- VTY_NEWLINE);
- else
- vty_out (vty, "dump bgp updates %s%s",
- bgp_dump_updates.filename, VTY_NEWLINE);
- }
- if (bgp_dump_routes.filename)
+ bgp_dump_type_t type ;
+
+ for (type = 0 ; type < BGP_DUMP_TYPE_COUNT ; ++type)
{
- if (bgp_dump_routes.interval_str)
- vty_out (vty, "dump bgp routes-mrt %s %s%s",
- bgp_dump_routes.filename, bgp_dump_routes.interval_str,
- VTY_NEWLINE);
- else
- vty_out (vty, "dump bgp routes-mrt %s%s",
- bgp_dump_routes.filename, VTY_NEWLINE);
- }
+ bgp_dump_control bdc ;
+
+ bdc = bgp_dumps[type] ;
+
+ if ((bdc == NULL) || (type == BGP_DUMP_TABLE_NOW))
+ continue ;
+
+ vty_out (vty, "dump bgp %s %s", bgp_dump_name[type], bdc->template) ;
+
+ if (bdc->interval_str != NULL)
+ vty_out (vty, " %s", bdc->interval_str) ;
+
+ vty_out (vty, VTY_NEWLINE) ;
+ } ;
return 0;
}
-/* Initialize BGP packet dump functionality. */
-void
+/*------------------------------------------------------------------------------
+ * Initialize BGP MRT dumping.
+ *
+ * NB: second stage initialisation -- after pthreads start.
+ */
+extern void
bgp_dump_init (void)
{
- memset (&bgp_dump_all, 0, sizeof (struct bgp_dump));
- memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump));
- memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump));
-
- bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
- + BGP_DUMP_HEADER_SIZE);
-
install_node (&bgp_dump_node, config_write_bgp_dump);
install_element (CONFIG_NODE, &dump_bgp_all_cmd);
@@ -874,11 +1414,318 @@ bgp_dump_init (void)
install_element (CONFIG_NODE, &dump_bgp_routes_cmd);
install_element (CONFIG_NODE, &dump_bgp_routes_interval_cmd);
install_element (CONFIG_NODE, &no_dump_bgp_routes_cmd);
-}
-
-void
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close down all dumping.
+ *
+ * This is called after the BGP Engine has stopped -- so can here free the
+ * BGP Engine stuff, too.
+ */
+extern void
bgp_dump_finish (void)
{
- stream_free (bgp_dump_obuf);
- bgp_dump_obuf = NULL;
-}
+ uint d ;
+
+ for (d = 0 ; d < BGP_DUMP_TYPE_COUNT ; ++d)
+ bgp_dump_control_free(bgp_dumps[d]) ;
+
+ bd_all = bgp_dump_free(bd_all) ;
+ bd_updates = bgp_dump_free(bd_updates) ;
+} ;
+
+/*==============================================================================
+ * The BGP_Engine side of bgp_dump.
+ *
+ * For ALL and UPDATES dumps the BGP_Engine is responsible for all actual
+ * I/O. The Routing Engine will open/rotate files, but pass those to the
+ * BGP_Engine for action. The BGP_Engine looks after the bgp_dump structures,
+ * and is responsible for closing files.
+ */
+struct bgp_dump_engine_set_args /* to BGP Engine */
+{
+ bgp_dump_type_t type ;
+ bgp_dump bd ;
+} ;
+MQB_ARGS_SIZE_OK(bgp_dump_engine_set_args) ;
+
+static void bgp_dump_engine_do_set(mqueue_block mqb, mqb_flag_t flag) ;
+static struct stream* bgp_dump_common (bgp_dump bd, bgp_connection connection,
+ int subtype, bool as4) ;
+static void bgp_dump_set_flags(void) ;
+static int bgp_dump_fsm_state(bgp_fsm_state_t state) ;
+
+/*------------------------------------------------------------------------------
+ * Set given dump in BGP Engine.
+ *
+ * Passes the given bgp_dump to the BGP Engine. Responsibility for the fd,
+ * the filename and the stream buffer pass to the BGP Engine.
+ *
+ * Pass NULL to stop the relevant dump.
+ */
+static void
+bgp_dump_engine_set(bgp_dump bd, bgp_dump_type_t type)
+{
+ struct bgp_dump_engine_set_args* args ;
+ mqueue_block mqb ;
+
+ mqb = mqb_init_new(NULL, bgp_dump_engine_do_set, bgp_dumps) ;
+
+ args = mqb_get_args(mqb) ;
+
+ args->type = type ;
+ args->bd = bd ;
+
+ bgp_to_bgp_engine(mqb, mqb_priority) ; /* change file ASAP */
+} ;
+
+/*------------------------------------------------------------------------------
+* BGP Engine: set the given dump.
+*
+* Note that this explicitly truncates the dump file. This deals with the
+* fringe case of a dump being started using the same name as an existing,
+* active dump !! (If the old dump wrote something to the file after the
+* new dump had opened the file, then we want to discard the old stuff, now.)
+*/
+static void
+bgp_dump_engine_do_set(mqueue_block mqb, mqb_flag_t flag)
+{
+ bgp_dump* pbd ;
+ bgp_dump bd ;
+ struct bgp_dump_engine_set_args* args = mqb_get_args(mqb) ;
+
+ pbd = (args->type == BGP_DUMP_UPDATES) ? &bd_updates : &bd_all ;
+
+ *pbd = bgp_dump_free(*pbd) ; /* close down any existing dump */
+
+ bd = bgp_dump_truncate(args->bd) ; /* truncate before use */
+
+ if (flag == mqb_action)
+ *pbd = bd ; /* set new dump */
+ else
+ *pbd = bgp_dump_free(bd) ; /* close down new dump */
+
+ bgp_dump_set_flags() ; /* reflect current state */
+
+ mqb_free(mqb) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dump BGP status change, if required -- BGP Engine
+ *
+ * Does nothing if no bd_all.
+ *
+ * Frees the dump in the event of any I/O error.
+ */
+extern void
+bgp_dump_state (bgp_connection connection, bgp_fsm_state_t new_state)
+{
+ struct stream *s;
+
+ if (bd_all != NULL)
+ {
+ s = bgp_dump_common(bd_all, connection, MRT_MST_BGP4MP_STATE_CHANGE_AS4,
+ true) ;
+ stream_putw(s, bgp_dump_fsm_state(connection->state));
+ stream_putw(s, bgp_dump_fsm_state(new_state)) ;
+
+ bgp_dump_set_size (s, 0);
+
+ bd_all = bgp_dump_put(bd_all, STREAM_DATA (s),
+ stream_get_endp (s), NULL, 0) ;
+ if (bd_all != NULL)
+ return ; /* OK, so no flag change */
+ } ;
+
+ bgp_dump_set_flags() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dump BGP packet received, if required -- BGP Engine
+ *
+ * Does nothing if no bd_all and no bd_updates.
+ *
+ * Frees a dump in the event of any I/O error on it.
+ */
+extern void
+bgp_dump_packet (bgp_connection connection)
+{
+ bgp_dump bd ;
+ struct stream* s ;
+ uint plen ;
+ bool du ;
+
+ /* If we have nothing to do, get out, quick.
+ *
+ * If we have something to do, select one of the stream buffers for the
+ * MRT header. Note that in the (unlikely) event of having both ALL and
+ * UPDATES dumps, selects the UPDATES obuf.
+ */
+ du = (bd_updates != NULL) && (connection->msg_type == BGP_MSG_UPDATE) ;
+
+ if (du)
+ bd = bd_updates ;
+ else if (bd_all != NULL)
+ bd = bd_all ;
+ else
+ return bgp_dump_set_flags() ; /* nothing to do */
+
+ /* Construct message header for packet dump
+ *
+ * Note that in the (unlikely) event of having both ALL and UPDATES dumps,
+ * we construct just the one header.
+ */
+ s = bgp_dump_common(bd, connection,
+ connection->as4 ? MRT_MST_BGP4MP_MESSAGE_AS4
+ : MRT_MST_BGP4MP_MESSAGE,
+ connection->as4) ;
+ plen = stream_get_endp(connection->ibuf) ;
+ bgp_dump_set_size (s, plen) ;
+
+ /* Output the MRT header and the packet
+ *
+ * In the event of an I/O failure, free the dump.
+ *
+ * Note that in the (unlikely) event of having both ALL and UPDATES dumps,
+ * we output the UPDATES last -- so that if the ALL fails the message
+ * header for the UPDATES dump is still there !
+ */
+ if (bd_all != NULL)
+ {
+ bd_all = bgp_dump_put(bd_all, STREAM_DATA(s),
+ stream_get_endp(s),
+ STREAM_DATA(connection->ibuf), plen) ;
+ if (bd_all == NULL)
+ bgp_dump_set_flags() ;
+ } ;
+
+ if (du)
+ {
+ bd_updates = bgp_dump_put(bd_updates, STREAM_DATA(s),
+ stream_get_endp(s),
+ STREAM_DATA(connection->ibuf), plen) ;
+ if (bd_updates == NULL)
+ bgp_dump_set_flags() ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct header and common parts of a MSG_PROTOCOL_BGP4MP MRT message
+ */
+static struct stream*
+bgp_dump_common (bgp_dump bd, bgp_connection connection, int subtype, bool as4)
+{
+ static const char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ struct stream *s ;
+ asn_t remote_as ;
+ asn_t local_as ;
+ sockunion su_remote ;
+ sockunion su_local ;
+ int sal ;
+
+ s = bgp_dump_header(bd, MRT_MT_BGP4MP, subtype) ;
+
+ /* Source AS number and Destination AS number
+ */
+ remote_as = connection->session->as_peer ;
+ local_as = connection->session->open_send->my_as ;
+
+ if (as4)
+ {
+ stream_putl (s, remote_as);
+ stream_putl (s, local_as);
+ }
+ else
+ {
+ stream_putw (s, remote_as);
+ stream_putw (s, local_as);
+ }
+
+ /* Interface index
+ */
+ stream_putw (s, connection->session->ifindex);
+
+ /* Remote and local IP addresses
+ *
+ * If no connection has been made, there will not be a local IP address.
+ *
+ * Note that we expect the local and remote addresses to be of the same
+ * family. We use the length from the remote address in both cases -- so if
+ * something "impossible" has happened, then the message will still be
+ * "syntactically" well formed.
+ */
+ su_remote = connection->session->su_peer ;
+ su_local = connection->su_local ;
+
+ stream_putw (s, sockunion_get_afi(su_remote)) ;
+
+ confirm(MRT_AFI_IPv4 == AFI_IP) ;
+#ifdef HAVE_IPV6
+ confirm(MRT_AFI_IPv6 == AFI_IP6) ;
+#endif
+
+ sal = sockunion_get_addr_len(su_remote) ;
+ stream_put(s, sockunion_get_addr(su_remote), sal) ;
+
+ if (su_local != NULL)
+ stream_put(s, sockunion_get_addr(su_local), sal) ;
+ else
+ stream_put(s, empty, sal) ;
+
+ return s ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the bgp_dump_state_flag and the bgp_dump_packet_flag as required.
+ *
+ * These flags simply reflect the state of the ALL and UPDATES dumps,
+ * but are used to avoid calling the dump functions when not required (which
+ * is most of the time !).
+ *
+ * Note that it doesn't matter if these flags were to not properly reflect the
+ * dump state !
+ */
+static void
+bgp_dump_set_flags(void)
+{
+ bgp_dump_state_flag = (bd_all != NULL) ;
+ bgp_dump_packet_flag = (bd_all != NULL) || (bd_updates != NULL) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Map an internal bgp_fsm_state_t value to the MRT Values
+ */
+static int
+bgp_dump_fsm_state(bgp_fsm_state_t state)
+{
+ switch (state)
+ {
+ case bgp_fsm_sInitial:
+ return MRT_FSM_UNDEF ;
+
+ case bgp_fsm_sIdle:
+ return MRT_FSM_Idle ;
+
+ case bgp_fsm_sConnect:
+ return MRT_FSM_Connect ;
+
+ case bgp_fsm_sActive:
+ return MRT_FSM_Active ;
+
+ case bgp_fsm_sOpenSent:
+ return MRT_FSM_OpenSent ;
+
+ case bgp_fsm_sOpenConfirm:
+ return MRT_FSM_OpenConfirm ;
+
+ case bgp_fsm_sEstablished:
+ return MRT_FSM_Established ;
+
+ case bgp_fsm_sStopping:
+ return MRT_FSM_UNDEF ;
+
+ default:
+ return MRT_FSM_UNDEF ;
+ } ;
+} ;