mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-17 08:11:54 +00:00

flock()ing the BGP dump files helps consumers determine when they're safe to read. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
909 lines
22 KiB
C
909 lines
22 KiB
C
/* BGP-4 dump routine
|
||
Copyright (C) 1999 Kunihiro Ishiguro
|
||
|
||
This file is part of GNU Zebra.
|
||
|
||
GNU Zebra is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 2, or (at your option) any
|
||
later version.
|
||
|
||
GNU Zebra is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Zebra; see the file COPYING. If not, write to the Free
|
||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
02111-1307, USA. */
|
||
|
||
#include <zebra.h>
|
||
|
||
#include "log.h"
|
||
#include "stream.h"
|
||
#include "sockunion.h"
|
||
#include "command.h"
|
||
#include "prefix.h"
|
||
#include "thread.h"
|
||
#include "linklist.h"
|
||
#include "bgpd/bgp_table.h"
|
||
|
||
#include "bgpd/bgpd.h"
|
||
#include "bgpd/bgp_route.h"
|
||
#include "bgpd/bgp_attr.h"
|
||
#include "bgpd/bgp_dump.h"
|
||
|
||
enum bgp_dump_type
|
||
{
|
||
BGP_DUMP_ALL,
|
||
BGP_DUMP_UPDATES,
|
||
BGP_DUMP_ROUTES
|
||
};
|
||
|
||
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 */
|
||
};
|
||
|
||
static int bgp_dump_interval_func (struct thread *);
|
||
|
||
struct bgp_dump
|
||
{
|
||
enum bgp_dump_type type;
|
||
|
||
char *filename;
|
||
|
||
FILE *fp;
|
||
|
||
unsigned int interval;
|
||
|
||
char *interval_str;
|
||
|
||
struct thread *t_interval;
|
||
};
|
||
|
||
/* BGP packet dump output buffer. */
|
||
struct stream *bgp_dump_obuf;
|
||
|
||
/* BGP dump strucuture for 'dump bgp all' */
|
||
struct bgp_dump bgp_dump_all;
|
||
|
||
/* BGP dump structure for 'dump bgp updates' */
|
||
struct bgp_dump bgp_dump_updates;
|
||
|
||
/* BGP dump structure for 'dump bgp routes' */
|
||
struct bgp_dump bgp_dump_routes;
|
||
|
||
/* Dump whole BGP table is very heavy process. */
|
||
struct thread *t_bgp_dump_routes;
|
||
|
||
/* Some define for BGP packet dump. */
|
||
static FILE *
|
||
bgp_dump_open_file (struct bgp_dump *bgp_dump)
|
||
{
|
||
int ret;
|
||
int fd;
|
||
time_t clock;
|
||
struct tm *tm;
|
||
char fullpath[MAXPATHLEN];
|
||
char realpath[MAXPATHLEN];
|
||
mode_t oldumask;
|
||
|
||
time (&clock);
|
||
tm = localtime (&clock);
|
||
|
||
if (bgp_dump->filename[0] != DIRECTORY_SEP)
|
||
{
|
||
sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
|
||
ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
|
||
}
|
||
else
|
||
ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
|
||
|
||
if (ret == 0)
|
||
{
|
||
zlog_warn ("bgp_dump_open_file: strftime error");
|
||
return NULL;
|
||
}
|
||
|
||
if (bgp_dump->fp)
|
||
fclose (bgp_dump->fp);
|
||
|
||
|
||
oldumask = umask(0777 & ~LOGFILE_MASK);
|
||
bgp_dump->fp = fopen (realpath, "w");
|
||
|
||
if (bgp_dump->fp == NULL)
|
||
{
|
||
zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno));
|
||
umask(oldumask);
|
||
return NULL;
|
||
}
|
||
else
|
||
{
|
||
fd = fileno(bgp_dump->fp);
|
||
ret = flock( fd, LOCK_EX );
|
||
if (ret != 0)
|
||
{
|
||
zlog_warn ("bgp_dump_open_file: Unable to flock() file %s: %s", realpath, strerror (errno));
|
||
}
|
||
}
|
||
|
||
umask(oldumask);
|
||
|
||
return bgp_dump->fp;
|
||
}
|
||
|
||
static int
|
||
bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
|
||
{
|
||
int secs_into_day;
|
||
time_t t;
|
||
struct tm *tm;
|
||
|
||
if (interval > 0)
|
||
{
|
||
/* Periodic dump every interval seconds */
|
||
if ((interval < 86400) && ((86400 % interval) == 0))
|
||
{
|
||
/* Dump at predictable times: if a day has a whole number of
|
||
* intervals, dump every interval seconds starting from midnight
|
||
*/
|
||
(void) time(&t);
|
||
tm = localtime(&t);
|
||
secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*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);
|
||
}
|
||
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);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Dump common header. */
|
||
static void
|
||
bgp_dump_header (struct stream *obuf, int type, int subtype)
|
||
{
|
||
time_t now;
|
||
|
||
/* Set header. */
|
||
time (&now);
|
||
|
||
/* Put dump packet header. */
|
||
stream_putl (obuf, now);
|
||
stream_putw (obuf, type);
|
||
stream_putw (obuf, subtype);
|
||
|
||
stream_putl (obuf, 0); /* len */
|
||
}
|
||
|
||
static void
|
||
bgp_dump_set_size (struct stream *s, int type)
|
||
{
|
||
stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE);
|
||
}
|
||
|
||
static void
|
||
bgp_dump_routes_index_table(struct bgp *bgp)
|
||
{
|
||
int ret;
|
||
struct peer *peer;
|
||
struct listnode *node;
|
||
uint16_t peerno = 0;
|
||
struct stream *obuf;
|
||
|
||
obuf = bgp_dump_obuf;
|
||
stream_reset (obuf);
|
||
|
||
/* MRT header */
|
||
bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE);
|
||
|
||
/* Collector BGP ID */
|
||
stream_put_in_addr (obuf, &bgp->router_id);
|
||
|
||
/* View name */
|
||
if(bgp->name)
|
||
{
|
||
stream_putw (obuf, strlen(bgp->name));
|
||
stream_put(obuf, bgp->name, strlen(bgp->name));
|
||
}
|
||
else
|
||
{
|
||
stream_putw(obuf, 0);
|
||
}
|
||
|
||
/* Peer count */
|
||
stream_putw (obuf, listcount(bgp->peer));
|
||
|
||
/* Walk down all peers */
|
||
for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
|
||
{
|
||
|
||
/* 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 */
|
||
|
||
/* Peer's BGP ID */
|
||
stream_put_in_addr (obuf, &peer->remote_id);
|
||
|
||
/* Peer's IP address */
|
||
if (sockunion_family(&peer->su) == AF_INET)
|
||
{
|
||
stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
|
||
}
|
||
#ifdef HAVE_IPV6
|
||
else if (sockunion_family(&peer->su) == AF_INET6)
|
||
{
|
||
stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
|
||
IPV6_MAX_BYTELEN);
|
||
}
|
||
#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);
|
||
|
||
/* Store the peer number for this peer */
|
||
peer->table_dump_index = peerno;
|
||
peerno++;
|
||
}
|
||
|
||
bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
|
||
|
||
ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
|
||
if (ret != 1)
|
||
{
|
||
zlog_warn ("bgp_dump_routes_index_table: fwrite returned %d, expected 1: %s", ret, strerror (errno));
|
||
}
|
||
fflush (bgp_dump_routes.fp);
|
||
}
|
||
|
||
|
||
/* Runs under child process. */
|
||
static unsigned int
|
||
bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
|
||
{
|
||
int ret;
|
||
struct stream *obuf;
|
||
struct bgp_info *info;
|
||
struct bgp_node *rn;
|
||
struct bgp *bgp;
|
||
struct bgp_table *table;
|
||
|
||
bgp = bgp_get_default ();
|
||
if (!bgp)
|
||
return seq;
|
||
|
||
if (bgp_dump_routes.fp == NULL)
|
||
return seq;
|
||
|
||
/* 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))
|
||
{
|
||
if(!rn->info)
|
||
continue;
|
||
|
||
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);
|
||
}
|
||
#endif /* HAVE_IPV6 */
|
||
|
||
/* Sequence number */
|
||
stream_putl(obuf, seq);
|
||
|
||
/* Prefix length */
|
||
stream_putc (obuf, rn->p.prefixlen);
|
||
|
||
/* 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 */
|
||
|
||
/* Save where we are now, so we can overwride the entry count later */
|
||
int sizep = stream_get_endp(obuf);
|
||
|
||
/* Entry count */
|
||
uint16_t entry_count = 0;
|
||
|
||
/* Entry count, note that this is overwritten later */
|
||
stream_putw(obuf, 0);
|
||
|
||
for (info = rn->info; info; info = info->next)
|
||
{
|
||
entry_count++;
|
||
|
||
/* Peer index */
|
||
stream_putw(obuf, info->peer->table_dump_index);
|
||
|
||
/* Originated */
|
||
#ifdef HAVE_CLOCK_MONOTONIC
|
||
stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime));
|
||
#else
|
||
stream_putl (obuf, info->uptime);
|
||
#endif /* HAVE_CLOCK_MONOTONIC */
|
||
|
||
/* Dump attribute. */
|
||
/* Skip prefix & AFI/SAFI for MP_NLRI */
|
||
bgp_dump_routes_attr (obuf, info->attr, &rn->p);
|
||
}
|
||
|
||
/* Overwrite the entry count, now that we know the right number */
|
||
stream_putw_at (obuf, sizep, entry_count);
|
||
|
||
seq++;
|
||
|
||
bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
|
||
ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
|
||
if (ret != 1)
|
||
{
|
||
zlog_warn ("bgp_dump_routes_func: fwrite returned %d, expected 1: %s", ret, strerror (errno));
|
||
}
|
||
}
|
||
|
||
fflush (bgp_dump_routes.fp);
|
||
|
||
return seq;
|
||
}
|
||
|
||
static int
|
||
bgp_dump_interval_func (struct thread *t)
|
||
{
|
||
struct bgp_dump *bgp_dump;
|
||
bgp_dump = THREAD_ARG (t);
|
||
bgp_dump->t_interval = NULL;
|
||
|
||
/* 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;
|
||
}
|
||
}
|
||
|
||
/* if interval is set reschedule */
|
||
if (bgp_dump->interval > 0)
|
||
bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Dump common information. */
|
||
static void
|
||
bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4)
|
||
{
|
||
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_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
|
||
|
||
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)
|
||
{
|
||
/* Interface Index and Address family. */
|
||
stream_putw (obuf, peer->ifindex);
|
||
stream_putw (obuf, AFI_IP6);
|
||
|
||
/* Source IP Address and Destination IP Address. */
|
||
stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
|
||
|
||
if (peer->su_local)
|
||
stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
|
||
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)
|
||
{
|
||
int ret;
|
||
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*/
|
||
|
||
stream_putw (obuf, status_old);
|
||
stream_putw (obuf, status_new);
|
||
|
||
/* Set length. */
|
||
bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
|
||
|
||
/* Write to the stream. */
|
||
ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp);
|
||
if (ret != 1)
|
||
{
|
||
zlog_warn ("bgp_dump_state: fwrite returned %d, expected 1: %s", ret, strerror (errno));
|
||
}
|
||
fflush (bgp_dump_all.fp);
|
||
}
|
||
|
||
static void
|
||
bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
|
||
struct stream *packet)
|
||
{
|
||
int ret;
|
||
struct stream *obuf;
|
||
|
||
/* If dump file pointer is disabled return immediately. */
|
||
if (bgp_dump->fp == NULL)
|
||
return;
|
||
|
||
/* Make dump stream. */
|
||
obuf = bgp_dump_obuf;
|
||
stream_reset (obuf);
|
||
|
||
/* Dump header and common part. */
|
||
if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
|
||
{
|
||
bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4);
|
||
}
|
||
else
|
||
{
|
||
bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE);
|
||
}
|
||
bgp_dump_common (obuf, peer, 0);
|
||
|
||
/* Packet contents. */
|
||
stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
|
||
|
||
/* Set length. */
|
||
bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
|
||
|
||
/* Write to the stream. */
|
||
ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
|
||
if (ret != 1)
|
||
{
|
||
zlog_warn ("bgp_dump_packet_func: fwrite returned %d, expected 1: %s", ret, strerror (errno));
|
||
}
|
||
fflush (bgp_dump->fp);
|
||
}
|
||
|
||
/* 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);
|
||
|
||
/* bgp_dump_updates. */
|
||
if (type == BGP_MSG_UPDATE)
|
||
bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
|
||
}
|
||
|
||
static unsigned int
|
||
bgp_dump_parse_time (const char *str)
|
||
{
|
||
int i;
|
||
int len;
|
||
int seen_h;
|
||
int seen_m;
|
||
int time;
|
||
unsigned int total;
|
||
|
||
time = 0;
|
||
total = 0;
|
||
seen_h = 0;
|
||
seen_m = 0;
|
||
len = strlen (str);
|
||
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
if (isdigit ((int) str[i]))
|
||
{
|
||
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;
|
||
}
|
||
|
||
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)
|
||
{
|
||
unsigned int interval;
|
||
|
||
if (interval_str)
|
||
{
|
||
|
||
/* Check interval string. */
|
||
interval = bgp_dump_parse_time (interval_str);
|
||
if (interval == 0)
|
||
{
|
||
vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
|
||
return CMD_WARNING;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* Set interval. */
|
||
bgp_dump->interval = interval;
|
||
if (bgp_dump->interval_str)
|
||
free (bgp_dump->interval_str);
|
||
bgp_dump->interval_str = strdup (interval_str);
|
||
|
||
}
|
||
else
|
||
{
|
||
interval = 0;
|
||
}
|
||
|
||
/* Create interval thread. */
|
||
bgp_dump_interval_add (bgp_dump, interval);
|
||
|
||
/* Set type. */
|
||
bgp_dump->type = type;
|
||
|
||
/* 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;
|
||
}
|
||
|
||
static int
|
||
bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
|
||
{
|
||
/* 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;
|
||
}
|
||
|
||
/* Create interval thread. */
|
||
if (bgp_dump->t_interval)
|
||
{
|
||
thread_cancel (bgp_dump->t_interval);
|
||
bgp_dump->t_interval = NULL;
|
||
}
|
||
|
||
bgp_dump->interval = 0;
|
||
|
||
if (bgp_dump->interval_str)
|
||
{
|
||
free (bgp_dump->interval_str);
|
||
bgp_dump->interval_str = NULL;
|
||
}
|
||
|
||
|
||
return CMD_SUCCESS;
|
||
}
|
||
|
||
DEFUN (dump_bgp_all,
|
||
dump_bgp_all_cmd,
|
||
"dump bgp all PATH",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump all BGP packets\n"
|
||
"Output filename\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL);
|
||
}
|
||
|
||
DEFUN (dump_bgp_all_interval,
|
||
dump_bgp_all_interval_cmd,
|
||
"dump bgp all PATH INTERVAL",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump all BGP packets\n"
|
||
"Output filename\n"
|
||
"Interval of output\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]);
|
||
}
|
||
|
||
DEFUN (no_dump_bgp_all,
|
||
no_dump_bgp_all_cmd,
|
||
"no dump bgp all [PATH] [INTERVAL]",
|
||
NO_STR
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump all BGP packets\n")
|
||
{
|
||
return bgp_dump_unset (vty, &bgp_dump_all);
|
||
}
|
||
|
||
DEFUN (dump_bgp_updates,
|
||
dump_bgp_updates_cmd,
|
||
"dump bgp updates PATH",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump BGP updates only\n"
|
||
"Output filename\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL);
|
||
}
|
||
|
||
DEFUN (dump_bgp_updates_interval,
|
||
dump_bgp_updates_interval_cmd,
|
||
"dump bgp updates PATH INTERVAL",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump BGP updates only\n"
|
||
"Output filename\n"
|
||
"Interval of output\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]);
|
||
}
|
||
|
||
DEFUN (no_dump_bgp_updates,
|
||
no_dump_bgp_updates_cmd,
|
||
"no dump bgp updates [PATH] [INTERVAL]",
|
||
NO_STR
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump BGP updates only\n")
|
||
{
|
||
return bgp_dump_unset (vty, &bgp_dump_updates);
|
||
}
|
||
|
||
DEFUN (dump_bgp_routes,
|
||
dump_bgp_routes_cmd,
|
||
"dump bgp routes-mrt PATH",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump whole BGP routing table\n"
|
||
"Output filename\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL);
|
||
}
|
||
|
||
DEFUN (dump_bgp_routes_interval,
|
||
dump_bgp_routes_interval_cmd,
|
||
"dump bgp routes-mrt PATH INTERVAL",
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump whole BGP routing table\n"
|
||
"Output filename\n"
|
||
"Interval of output\n")
|
||
{
|
||
return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]);
|
||
}
|
||
|
||
DEFUN (no_dump_bgp_routes,
|
||
no_dump_bgp_routes_cmd,
|
||
"no dump bgp routes-mrt [PATH] [INTERVAL]",
|
||
NO_STR
|
||
"Dump packet\n"
|
||
"BGP packet dump\n"
|
||
"Dump whole BGP routing table\n")
|
||
{
|
||
return bgp_dump_unset (vty, &bgp_dump_routes);
|
||
}
|
||
|
||
/* BGP node structure. */
|
||
static struct cmd_node bgp_dump_node =
|
||
{
|
||
DUMP_NODE,
|
||
"",
|
||
1
|
||
};
|
||
|
||
#if 0
|
||
char *
|
||
config_time2str (unsigned int interval)
|
||
{
|
||
static char buf[BUFSIZ];
|
||
|
||
buf[0] = '\0';
|
||
|
||
if (interval / 3600)
|
||
{
|
||
sprintf (buf, "%dh", interval / 3600);
|
||
interval %= 3600;
|
||
}
|
||
if (interval / 60)
|
||
{
|
||
sprintf (buf + strlen (buf), "%dm", interval /60);
|
||
interval %= 60;
|
||
}
|
||
if (interval)
|
||
{
|
||
sprintf (buf + strlen (buf), "%d", interval);
|
||
}
|
||
return buf;
|
||
}
|
||
#endif
|
||
|
||
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)
|
||
{
|
||
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);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Initialize BGP packet dump functionality. */
|
||
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);
|
||
install_element (CONFIG_NODE, &dump_bgp_all_interval_cmd);
|
||
install_element (CONFIG_NODE, &no_dump_bgp_all_cmd);
|
||
install_element (CONFIG_NODE, &dump_bgp_updates_cmd);
|
||
install_element (CONFIG_NODE, &dump_bgp_updates_interval_cmd);
|
||
install_element (CONFIG_NODE, &no_dump_bgp_updates_cmd);
|
||
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
|
||
bgp_dump_finish (void)
|
||
{
|
||
stream_free (bgp_dump_obuf);
|
||
bgp_dump_obuf = NULL;
|
||
}
|