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