blob: c350e83a6542e67b8f72d684bea1c4e2a3d04425 [file] [log] [blame]
/* 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 "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 */
};
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;
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)
{
umask(oldumask);
return NULL;
}
umask(oldumask);
return bgp_dump->fp;
}
static int
bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
{
int interval2, secs_into_day;
time_t t;
struct tm *tm;
if (interval > 0 )
{
if ((interval < 86400) && ((86400 % interval) == 0))
{
(void) time(&t);
tm = localtime(&t);
secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*60*tm->tm_hour;
interval2 = interval - secs_into_day % interval;
if(interval2 == 0) interval2 = interval;
}
else
{
interval2 = interval;
}
bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
bgp_dump, interval2);
}
else
{
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_entry (struct prefix *p, struct bgp_info *info, int afi,
int type, unsigned int seq)
{
struct stream *obuf;
struct attr *attr;
struct peer *peer;
int plen;
int safi = 0;
/* Make dump stream. */
obuf = bgp_dump_obuf;
stream_reset (obuf);
attr = info->attr;
peer = info->peer;
/* We support MRT's old format. */
if (type == MSG_TABLE_DUMP)
{
bgp_dump_header (obuf, MSG_TABLE_DUMP, afi);
stream_putw (obuf, 0); /* View # */
stream_putw (obuf, seq); /* Sequence number. */
}
else
{
bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY);
stream_putl (obuf, info->uptime); /* Time Last Change */
stream_putw (obuf, afi); /* Address Family */
stream_putc (obuf, safi); /* SAFI */
}
if (afi == AFI_IP)
{
if (type == MSG_TABLE_DUMP)
{
/* Prefix */
stream_put_in_addr (obuf, &p->u.prefix4);
stream_putc (obuf, p->prefixlen);
/* Status */
stream_putc (obuf, 1);
/* Originated */
stream_putl (obuf, info->uptime);
/* Peer's IP address */
stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
/* Peer's AS number. */
stream_putw (obuf, peer->as);
/* Dump attribute. */
bgp_dump_routes_attr (obuf, attr, p);
}
else
{
/* Next-Hop-Len */
stream_putc (obuf, IPV4_MAX_BYTELEN);
stream_put_in_addr (obuf, &attr->nexthop);
stream_putc (obuf, p->prefixlen);
plen = PSIZE (p->prefixlen);
stream_put (obuf, &p->u.prefix4, plen);
bgp_dump_routes_attr (obuf, attr, p);
}
}
#ifdef HAVE_IPV6
else if (afi == AFI_IP6)
{
if (type == MSG_TABLE_DUMP)
{
/* Prefix */
stream_write (obuf, (u_char *)&p->u.prefix6, IPV6_MAX_BYTELEN);
stream_putc (obuf, p->prefixlen);
/* Status */
stream_putc (obuf, 1);
/* Originated */
stream_putl (obuf, info->uptime);
/* Peer's IP address */
stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
IPV6_MAX_BYTELEN);
/* Peer's AS number. */
stream_putw (obuf, peer->as);
/* Dump attribute. */
bgp_dump_routes_attr (obuf, attr, p);
}
else
{
;
}
}
#endif /* HAVE_IPV6 */
/* Set length. */
bgp_dump_set_size (obuf, type);
fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
fflush (bgp_dump_routes.fp);
}
/* Runs under child process. */
static void
bgp_dump_routes_func (int afi)
{
struct stream *obuf;
struct bgp_node *rn;
struct bgp_info *info;
struct bgp *bgp;
struct bgp_table *table;
unsigned int seq = 0;
obuf = bgp_dump_obuf;
bgp = bgp_get_default ();
if (!bgp)
return;
if (bgp_dump_routes.fp == NULL)
return;
/* Walk down each BGP route. */
table = bgp->rib[afi][SAFI_UNICAST];
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
for (info = rn->info; info; info = info->next)
bgp_dump_routes_entry (&rn->p, info, afi, MSG_TABLE_DUMP, 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)
{
bgp_dump_routes_func (AFI_IP);
#ifdef HAVE_IPV6
bgp_dump_routes_func (AFI_IP6);
#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)
{
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. */
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)
{
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);
bgp_dump_common (obuf, peer);
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);
}
static void
bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
struct stream *packet)
{
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. */
bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE);
bgp_dump_common (obuf, peer);
/* 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. */
fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
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, int 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;
}
/* 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. */
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);
}