1/* BGP-4 dump routine
2   Copyright (C) 1999 Kunihiro Ishiguro
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING.  If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-1307, USA.  */
20
21#include <zebra.h>
22
23#include "log.h"
24#include "stream.h"
25#include "sockunion.h"
26#include "command.h"
27#include "prefix.h"
28#include "thread.h"
29#include "linklist.h"
30#include "bgpd/bgp_table.h"
31
32#include "bgpd/bgpd.h"
33#include "bgpd/bgp_route.h"
34#include "bgpd/bgp_attr.h"
35#include "bgpd/bgp_dump.h"
36
37enum bgp_dump_type
38{
39  BGP_DUMP_ALL,
40  BGP_DUMP_UPDATES,
41  BGP_DUMP_ROUTES
42};
43
44enum MRT_MSG_TYPES {
45   MSG_NULL,
46   MSG_START,                   /* sender is starting up */
47   MSG_DIE,                     /* receiver should shut down */
48   MSG_I_AM_DEAD,               /* sender is shutting down */
49   MSG_PEER_DOWN,               /* sender's peer is down */
50   MSG_PROTOCOL_BGP,            /* msg is a BGP packet */
51   MSG_PROTOCOL_RIP,            /* msg is a RIP packet */
52   MSG_PROTOCOL_IDRP,           /* msg is an IDRP packet */
53   MSG_PROTOCOL_RIPNG,          /* msg is a RIPNG packet */
54   MSG_PROTOCOL_BGP4PLUS,       /* msg is a BGP4+ packet */
55   MSG_PROTOCOL_BGP4PLUS_01,    /* msg is a BGP4+ (draft 01) packet */
56   MSG_PROTOCOL_OSPF,           /* msg is an OSPF packet */
57   MSG_TABLE_DUMP,              /* routing table dump */
58   MSG_TABLE_DUMP_V2            /* routing table dump, version 2 */
59};
60
61static int bgp_dump_interval_func (struct thread *);
62
63struct bgp_dump
64{
65  enum bgp_dump_type type;
66
67  char *filename;
68
69  FILE *fp;
70
71  unsigned int interval;
72
73  char *interval_str;
74
75  struct thread *t_interval;
76};
77
78/* BGP packet dump output buffer. */
79struct stream *bgp_dump_obuf;
80
81/* BGP dump strucuture for 'dump bgp all' */
82struct bgp_dump bgp_dump_all;
83
84/* BGP dump structure for 'dump bgp updates' */
85struct bgp_dump bgp_dump_updates;
86
87/* BGP dump structure for 'dump bgp routes' */
88struct bgp_dump bgp_dump_routes;
89
90/* Dump whole BGP table is very heavy process.  */
91struct thread *t_bgp_dump_routes;
92
93/* Some define for BGP packet dump. */
94static FILE *
95bgp_dump_open_file (struct bgp_dump *bgp_dump)
96{
97  int ret;
98  time_t clock;
99  struct tm *tm;
100  char fullpath[MAXPATHLEN];
101  char realpath[MAXPATHLEN];
102  mode_t oldumask;
103
104  time (&clock);
105  tm = localtime (&clock);
106
107  if (bgp_dump->filename[0] != DIRECTORY_SEP)
108    {
109      sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
110      ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
111    }
112  else
113    ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
114
115  if (ret == 0)
116    {
117      zlog_warn ("bgp_dump_open_file: strftime error");
118      return NULL;
119    }
120
121  if (bgp_dump->fp)
122    fclose (bgp_dump->fp);
123
124
125  oldumask = umask(0777 & ~LOGFILE_MASK);
126  bgp_dump->fp = fopen (realpath, "w");
127
128  if (bgp_dump->fp == NULL)
129    {
130      zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno));
131      umask(oldumask);
132      return NULL;
133    }
134  umask(oldumask);
135
136  return bgp_dump->fp;
137}
138
139static int
140bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
141{
142  int secs_into_day;
143  time_t t;
144  struct tm *tm;
145
146  if (interval > 0)
147    {
148      /* Periodic dump every interval seconds */
149      if ((interval < 86400) && ((86400 % interval) == 0))
150	{
151	  /* Dump at predictable times: if a day has a whole number of
152	   * intervals, dump every interval seconds starting from midnight
153	   */
154	  (void) time(&t);
155	  tm = localtime(&t);
156	  secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*60*tm->tm_hour;
157	  interval = interval - secs_into_day % interval; /* always > 0 */
158	}
159      bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
160					       bgp_dump, interval);
161    }
162  else
163    {
164      /* One-off dump: execute immediately, don't affect any scheduled dumps */
165      bgp_dump->t_interval = thread_add_event (master, bgp_dump_interval_func,
166					       bgp_dump, 0);
167    }
168
169  return 0;
170}
171
172/* Dump common header. */
173static void
174bgp_dump_header (struct stream *obuf, int type, int subtype)
175{
176  time_t now;
177
178  /* Set header. */
179  time (&now);
180
181  /* Put dump packet header. */
182  stream_putl (obuf, now);
183  stream_putw (obuf, type);
184  stream_putw (obuf, subtype);
185
186  stream_putl (obuf, 0);	/* len */
187}
188
189static void
190bgp_dump_set_size (struct stream *s, int type)
191{
192  stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE);
193}
194
195static void
196bgp_dump_routes_index_table(struct bgp *bgp)
197{
198  struct peer *peer;
199  struct listnode *node;
200  uint16_t peerno = 0;
201  struct stream *obuf;
202
203  obuf = bgp_dump_obuf;
204  stream_reset (obuf);
205
206  /* MRT header */
207  bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE);
208
209  /* Collector BGP ID */
210  stream_put_in_addr (obuf, &bgp->router_id);
211
212  /* View name */
213  if(bgp->name)
214    {
215      stream_putw (obuf, strlen(bgp->name));
216      stream_put(obuf, bgp->name, strlen(bgp->name));
217    }
218  else
219    {
220      stream_putw(obuf, 0);
221    }
222
223  /* Peer count */
224  stream_putw (obuf, listcount(bgp->peer));
225
226  /* Walk down all peers */
227  for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
228    {
229
230      /* Peer's type */
231      if (sockunion_family(&peer->su) == AF_INET)
232        {
233          stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
234        }
235#ifdef HAVE_IPV6
236      else if (sockunion_family(&peer->su) == AF_INET6)
237        {
238          stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
239        }
240#endif /* HAVE_IPV6 */
241
242      /* Peer's BGP ID */
243      stream_put_in_addr (obuf, &peer->remote_id);
244
245      /* Peer's IP address */
246      if (sockunion_family(&peer->su) == AF_INET)
247        {
248          stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
249        }
250#ifdef HAVE_IPV6
251      else if (sockunion_family(&peer->su) == AF_INET6)
252        {
253          stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
254                        IPV6_MAX_BYTELEN);
255        }
256#endif /* HAVE_IPV6 */
257
258      /* Peer's AS number. */
259      /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */
260      stream_putl (obuf, peer->as);
261
262      /* Store the peer number for this peer */
263      peer->table_dump_index = peerno;
264      peerno++;
265    }
266
267  bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
268
269  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
270  fflush (bgp_dump_routes.fp);
271}
272
273
274/* Runs under child process. */
275static unsigned int
276bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
277{
278  struct stream *obuf;
279  struct bgp_info *info;
280  struct bgp_node *rn;
281  struct bgp *bgp;
282  struct bgp_table *table;
283
284  bgp = bgp_get_default ();
285  if (!bgp)
286    return seq;
287
288  if (bgp_dump_routes.fp == NULL)
289    return seq;
290
291  /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
292     so this should only be done on the first call to bgp_dump_routes_func.
293     ( this function will be called once for ipv4 and once for ipv6 ) */
294  if(first_run)
295    bgp_dump_routes_index_table(bgp);
296
297  obuf = bgp_dump_obuf;
298  stream_reset(obuf);
299
300  /* Walk down each BGP route. */
301  table = bgp->rib[afi][SAFI_UNICAST];
302
303  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
304    {
305      if(!rn->info)
306        continue;
307
308      stream_reset(obuf);
309
310      /* MRT header */
311      if (afi == AFI_IP)
312        {
313          bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST);
314        }
315#ifdef HAVE_IPV6
316      else if (afi == AFI_IP6)
317        {
318          bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST);
319        }
320#endif /* HAVE_IPV6 */
321
322      /* Sequence number */
323      stream_putl(obuf, seq);
324
325      /* Prefix length */
326      stream_putc (obuf, rn->p.prefixlen);
327
328      /* Prefix */
329      if (afi == AFI_IP)
330        {
331          /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
332          stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8);
333        }
334#ifdef HAVE_IPV6
335      else if (afi == AFI_IP6)
336        {
337          /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
338          stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8);
339        }
340#endif /* HAVE_IPV6 */
341
342      /* Save where we are now, so we can overwride the entry count later */
343      int sizep = stream_get_endp(obuf);
344
345      /* Entry count */
346      uint16_t entry_count = 0;
347
348      /* Entry count, note that this is overwritten later */
349      stream_putw(obuf, 0);
350
351      for (info = rn->info; info; info = info->next)
352        {
353          entry_count++;
354
355          /* Peer index */
356          stream_putw(obuf, info->peer->table_dump_index);
357
358          /* Originated */
359#ifdef HAVE_CLOCK_MONOTONIC
360          stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime));
361#else
362          stream_putl (obuf, info->uptime);
363#endif /* HAVE_CLOCK_MONOTONIC */
364
365          /* Dump attribute. */
366          /* Skip prefix & AFI/SAFI for MP_NLRI */
367          bgp_dump_routes_attr (obuf, info->attr, &rn->p);
368        }
369
370      /* Overwrite the entry count, now that we know the right number */
371      stream_putw_at (obuf, sizep, entry_count);
372
373      seq++;
374
375      bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
376      fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
377
378    }
379
380  fflush (bgp_dump_routes.fp);
381
382  return seq;
383}
384
385static int
386bgp_dump_interval_func (struct thread *t)
387{
388  struct bgp_dump *bgp_dump;
389  bgp_dump = THREAD_ARG (t);
390  bgp_dump->t_interval = NULL;
391
392  /* Reschedule dump even if file couldn't be opened this time... */
393  if (bgp_dump_open_file (bgp_dump) != NULL)
394    {
395      /* In case of bgp_dump_routes, we need special route dump function. */
396      if (bgp_dump->type == BGP_DUMP_ROUTES)
397	{
398	  unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0);
399#ifdef HAVE_IPV6
400	  bgp_dump_routes_func (AFI_IP6, 0, seq);
401#endif /* HAVE_IPV6 */
402	  /* Close the file now. For a RIB dump there's no point in leaving
403	   * it open until the next scheduled dump starts. */
404	  fclose(bgp_dump->fp); bgp_dump->fp = NULL;
405	}
406    }
407
408  /* if interval is set reschedule */
409  if (bgp_dump->interval > 0)
410    bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
411
412  return 0;
413}
414
415/* Dump common information. */
416static void
417bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4)
418{
419  char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
420
421  /* Source AS number and Destination AS number. */
422  if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
423    {
424      stream_putl (obuf, peer->as);
425      stream_putl (obuf, peer->local_as);
426    }
427  else
428    {
429      stream_putw (obuf, peer->as);
430      stream_putw (obuf, peer->local_as);
431    }
432
433  if (peer->su.sa.sa_family == AF_INET)
434    {
435      stream_putw (obuf, peer->ifindex);
436      stream_putw (obuf, AFI_IP);
437
438      stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
439
440      if (peer->su_local)
441	stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN);
442      else
443	stream_put (obuf, empty, IPV4_MAX_BYTELEN);
444    }
445#ifdef HAVE_IPV6
446  else if (peer->su.sa.sa_family == AF_INET6)
447    {
448      /* Interface Index and Address family. */
449      stream_putw (obuf, peer->ifindex);
450      stream_putw (obuf, AFI_IP6);
451
452      /* Source IP Address and Destination IP Address. */
453      stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
454
455      if (peer->su_local)
456	stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
457      else
458	stream_put (obuf, empty, IPV6_MAX_BYTELEN);
459    }
460#endif /* HAVE_IPV6 */
461}
462
463/* Dump BGP status change. */
464void
465bgp_dump_state (struct peer *peer, int status_old, int status_new)
466{
467  struct stream *obuf;
468
469  /* If dump file pointer is disabled return immediately. */
470  if (bgp_dump_all.fp == NULL)
471    return;
472
473  /* Make dump stream. */
474  obuf = bgp_dump_obuf;
475  stream_reset (obuf);
476
477  bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4);
478  bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/
479
480  stream_putw (obuf, status_old);
481  stream_putw (obuf, status_new);
482
483  /* Set length. */
484  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
485
486  /* Write to the stream. */
487  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp);
488  fflush (bgp_dump_all.fp);
489}
490
491static void
492bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
493		      struct stream *packet)
494{
495  struct stream *obuf;
496
497  /* If dump file pointer is disabled return immediately. */
498  if (bgp_dump->fp == NULL)
499    return;
500
501  /* Make dump stream. */
502  obuf = bgp_dump_obuf;
503  stream_reset (obuf);
504
505  /* Dump header and common part. */
506  if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
507    {
508      bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4);
509    }
510  else
511    {
512      bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE);
513    }
514  bgp_dump_common (obuf, peer, 0);
515
516  /* Packet contents. */
517  stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
518
519  /* Set length. */
520  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
521
522  /* Write to the stream. */
523  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
524  fflush (bgp_dump->fp);
525}
526
527/* Called from bgp_packet.c when BGP packet is received. */
528void
529bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
530{
531  /* bgp_dump_all. */
532  bgp_dump_packet_func (&bgp_dump_all, peer, packet);
533
534  /* bgp_dump_updates. */
535  if (type == BGP_MSG_UPDATE)
536    bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
537}
538
539static unsigned int
540bgp_dump_parse_time (const char *str)
541{
542  int i;
543  int len;
544  int seen_h;
545  int seen_m;
546  int time;
547  unsigned int total;
548
549  time = 0;
550  total = 0;
551  seen_h = 0;
552  seen_m = 0;
553  len = strlen (str);
554
555  for (i = 0; i < len; i++)
556    {
557      if (isdigit ((int) str[i]))
558	{
559	  time *= 10;
560	  time += str[i] - '0';
561	}
562      else if (str[i] == 'H' || str[i] == 'h')
563	{
564	  if (seen_h)
565	    return 0;
566	  if (seen_m)
567	    return 0;
568	  total += time * 60 *60;
569	  time = 0;
570	  seen_h = 1;
571	}
572      else if (str[i] == 'M' || str[i] == 'm')
573	{
574	  if (seen_m)
575	    return 0;
576	  total += time * 60;
577	  time = 0;
578	  seen_h = 1;
579	}
580      else
581	return 0;
582    }
583  return total + time;
584}
585
586static int
587bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump,
588              enum bgp_dump_type type, const char *path,
589              const char *interval_str)
590{
591  unsigned int interval;
592
593  if (interval_str)
594    {
595
596      /* Check interval string. */
597      interval = bgp_dump_parse_time (interval_str);
598      if (interval == 0)
599	{
600	  vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
601	  return CMD_WARNING;
602	}
603
604      /* Don't schedule duplicate dumps if the dump command is given twice */
605      if (interval == bgp_dump->interval &&
606	  type == bgp_dump->type &&
607          path && bgp_dump->filename && !strcmp (path, bgp_dump->filename))
608	{
609          return CMD_SUCCESS;
610	}
611
612      /* Set interval. */
613      bgp_dump->interval = interval;
614      if (bgp_dump->interval_str)
615	free (bgp_dump->interval_str);
616      bgp_dump->interval_str = strdup (interval_str);
617
618    }
619  else
620    {
621      interval = 0;
622    }
623
624  /* Create interval thread. */
625  bgp_dump_interval_add (bgp_dump, interval);
626
627  /* Set type. */
628  bgp_dump->type = type;
629
630  /* Set file name. */
631  if (bgp_dump->filename)
632    free (bgp_dump->filename);
633  bgp_dump->filename = strdup (path);
634
635  /* This should be called when interval is expired. */
636  bgp_dump_open_file (bgp_dump);
637
638  return CMD_SUCCESS;
639}
640
641static int
642bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
643{
644  /* Set file name. */
645  if (bgp_dump->filename)
646    {
647      free (bgp_dump->filename);
648      bgp_dump->filename = NULL;
649    }
650
651  /* This should be called when interval is expired. */
652  if (bgp_dump->fp)
653    {
654      fclose (bgp_dump->fp);
655      bgp_dump->fp = NULL;
656    }
657
658  /* Create interval thread. */
659  if (bgp_dump->t_interval)
660    {
661      thread_cancel (bgp_dump->t_interval);
662      bgp_dump->t_interval = NULL;
663    }
664
665  bgp_dump->interval = 0;
666
667  if (bgp_dump->interval_str)
668    {
669      free (bgp_dump->interval_str);
670      bgp_dump->interval_str = NULL;
671    }
672
673
674  return CMD_SUCCESS;
675}
676
677DEFUN (dump_bgp_all,
678       dump_bgp_all_cmd,
679       "dump bgp all PATH",
680       "Dump packet\n"
681       "BGP packet dump\n"
682       "Dump all BGP packets\n"
683       "Output filename\n")
684{
685  return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL);
686}
687
688DEFUN (dump_bgp_all_interval,
689       dump_bgp_all_interval_cmd,
690       "dump bgp all PATH INTERVAL",
691       "Dump packet\n"
692       "BGP packet dump\n"
693       "Dump all BGP packets\n"
694       "Output filename\n"
695       "Interval of output\n")
696{
697  return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]);
698}
699
700DEFUN (no_dump_bgp_all,
701       no_dump_bgp_all_cmd,
702       "no dump bgp all [PATH] [INTERVAL]",
703       NO_STR
704       "Dump packet\n"
705       "BGP packet dump\n"
706       "Dump all BGP packets\n")
707{
708  return bgp_dump_unset (vty, &bgp_dump_all);
709}
710
711DEFUN (dump_bgp_updates,
712       dump_bgp_updates_cmd,
713       "dump bgp updates PATH",
714       "Dump packet\n"
715       "BGP packet dump\n"
716       "Dump BGP updates only\n"
717       "Output filename\n")
718{
719  return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL);
720}
721
722DEFUN (dump_bgp_updates_interval,
723       dump_bgp_updates_interval_cmd,
724       "dump bgp updates PATH INTERVAL",
725       "Dump packet\n"
726       "BGP packet dump\n"
727       "Dump BGP updates only\n"
728       "Output filename\n"
729       "Interval of output\n")
730{
731  return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]);
732}
733
734DEFUN (no_dump_bgp_updates,
735       no_dump_bgp_updates_cmd,
736       "no dump bgp updates [PATH] [INTERVAL]",
737       NO_STR
738       "Dump packet\n"
739       "BGP packet dump\n"
740       "Dump BGP updates only\n")
741{
742  return bgp_dump_unset (vty, &bgp_dump_updates);
743}
744
745DEFUN (dump_bgp_routes,
746       dump_bgp_routes_cmd,
747       "dump bgp routes-mrt PATH",
748       "Dump packet\n"
749       "BGP packet dump\n"
750       "Dump whole BGP routing table\n"
751       "Output filename\n")
752{
753  return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL);
754}
755
756DEFUN (dump_bgp_routes_interval,
757       dump_bgp_routes_interval_cmd,
758       "dump bgp routes-mrt PATH INTERVAL",
759       "Dump packet\n"
760       "BGP packet dump\n"
761       "Dump whole BGP routing table\n"
762       "Output filename\n"
763       "Interval of output\n")
764{
765  return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]);
766}
767
768DEFUN (no_dump_bgp_routes,
769       no_dump_bgp_routes_cmd,
770       "no dump bgp routes-mrt [PATH] [INTERVAL]",
771       NO_STR
772       "Dump packet\n"
773       "BGP packet dump\n"
774       "Dump whole BGP routing table\n")
775{
776  return bgp_dump_unset (vty, &bgp_dump_routes);
777}
778
779/* BGP node structure. */
780static struct cmd_node bgp_dump_node =
781{
782  DUMP_NODE,
783  "",
784  1
785};
786
787#if 0
788char *
789config_time2str (unsigned int interval)
790{
791  static char buf[BUFSIZ];
792
793  buf[0] = '\0';
794
795  if (interval / 3600)
796    {
797      sprintf (buf, "%dh", interval / 3600);
798      interval %= 3600;
799    }
800  if (interval / 60)
801    {
802      sprintf (buf + strlen (buf), "%dm", interval /60);
803      interval %= 60;
804    }
805  if (interval)
806    {
807      sprintf (buf + strlen (buf), "%d", interval);
808    }
809  return buf;
810}
811#endif
812
813static int
814config_write_bgp_dump (struct vty *vty)
815{
816  if (bgp_dump_all.filename)
817    {
818      if (bgp_dump_all.interval_str)
819	vty_out (vty, "dump bgp all %s %s%s",
820		 bgp_dump_all.filename, bgp_dump_all.interval_str,
821		 VTY_NEWLINE);
822      else
823	vty_out (vty, "dump bgp all %s%s",
824		 bgp_dump_all.filename, VTY_NEWLINE);
825    }
826  if (bgp_dump_updates.filename)
827    {
828      if (bgp_dump_updates.interval_str)
829	vty_out (vty, "dump bgp updates %s %s%s",
830		 bgp_dump_updates.filename, bgp_dump_updates.interval_str,
831		 VTY_NEWLINE);
832      else
833	vty_out (vty, "dump bgp updates %s%s",
834		 bgp_dump_updates.filename, VTY_NEWLINE);
835    }
836  if (bgp_dump_routes.filename)
837    {
838      if (bgp_dump_routes.interval_str)
839	vty_out (vty, "dump bgp routes-mrt %s %s%s",
840		 bgp_dump_routes.filename, bgp_dump_routes.interval_str,
841		 VTY_NEWLINE);
842      else
843	vty_out (vty, "dump bgp routes-mrt %s%s",
844		 bgp_dump_routes.filename, VTY_NEWLINE);
845    }
846  return 0;
847}
848
849/* Initialize BGP packet dump functionality. */
850void
851bgp_dump_init (void)
852{
853  memset (&bgp_dump_all, 0, sizeof (struct bgp_dump));
854  memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump));
855  memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump));
856
857  bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
858                              + BGP_DUMP_HEADER_SIZE);
859
860  install_node (&bgp_dump_node, config_write_bgp_dump);
861
862  install_element (CONFIG_NODE, &dump_bgp_all_cmd);
863  install_element (CONFIG_NODE, &dump_bgp_all_interval_cmd);
864  install_element (CONFIG_NODE, &no_dump_bgp_all_cmd);
865  install_element (CONFIG_NODE, &dump_bgp_updates_cmd);
866  install_element (CONFIG_NODE, &dump_bgp_updates_interval_cmd);
867  install_element (CONFIG_NODE, &no_dump_bgp_updates_cmd);
868  install_element (CONFIG_NODE, &dump_bgp_routes_cmd);
869  install_element (CONFIG_NODE, &dump_bgp_routes_interval_cmd);
870  install_element (CONFIG_NODE, &no_dump_bgp_routes_cmd);
871}
872
873void
874bgp_dump_finish (void)
875{
876  stream_free (bgp_dump_obuf);
877  bgp_dump_obuf = NULL;
878}
879