| /* 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; |
| 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; |
| } |
| 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) |
| { |
| 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); |
| |
| fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); |
| fflush (bgp_dump_routes.fp); |
| } |
| |
| |
| /* Runs under child process. */ |
| static unsigned int |
| bgp_dump_routes_func (int afi, int first_run, unsigned int seq) |
| { |
| 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); |
| fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); |
| |
| } |
| |
| 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) |
| { |
| 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. */ |
| 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. */ |
| 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. */ |
| 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, |
| 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; |
| } |