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 "bgpd/bgp_table.h"
30
31#include "bgpd/bgpd.h"
32#include "bgpd/bgp_route.h"
33#include "bgpd/bgp_attr.h"
34#include "bgpd/bgp_dump.h"
35
36enum bgp_dump_type
37{
38  BGP_DUMP_ALL,
39  BGP_DUMP_UPDATES,
40  BGP_DUMP_ROUTES
41};
42
43enum MRT_MSG_TYPES {
44   MSG_NULL,
45   MSG_START,                   /* sender is starting up */
46   MSG_DIE,                     /* receiver should shut down */
47   MSG_I_AM_DEAD,               /* sender is shutting down */
48   MSG_PEER_DOWN,               /* sender's peer is down */
49   MSG_PROTOCOL_BGP,            /* msg is a BGP packet */
50   MSG_PROTOCOL_RIP,            /* msg is a RIP packet */
51   MSG_PROTOCOL_IDRP,           /* msg is an IDRP packet */
52   MSG_PROTOCOL_RIPNG,          /* msg is a RIPNG packet */
53   MSG_PROTOCOL_BGP4PLUS,       /* msg is a BGP4+ packet */
54   MSG_PROTOCOL_BGP4PLUS_01,    /* msg is a BGP4+ (draft 01) packet */
55   MSG_PROTOCOL_OSPF,           /* msg is an OSPF packet */
56   MSG_TABLE_DUMP               /* routing table dump */
57};
58
59struct bgp_dump
60{
61  enum bgp_dump_type type;
62
63  char *filename;
64
65  FILE *fp;
66
67  unsigned int interval;
68
69  char *interval_str;
70
71  struct thread *t_interval;
72};
73
74/* BGP packet dump output buffer. */
75struct stream *bgp_dump_obuf;
76
77/* BGP dump strucuture for 'dump bgp all' */
78struct bgp_dump bgp_dump_all;
79
80/* BGP dump structure for 'dump bgp updates' */
81struct bgp_dump bgp_dump_updates;
82
83/* BGP dump structure for 'dump bgp routes' */
84struct bgp_dump bgp_dump_routes;
85
86/* Dump whole BGP table is very heavy process.  */
87struct thread *t_bgp_dump_routes;
88
89/* Some define for BGP packet dump. */
90FILE *
91bgp_dump_open_file (struct bgp_dump *bgp_dump)
92{
93  int ret;
94  time_t clock;
95  struct tm *tm;
96  char fullpath[MAXPATHLEN];
97  char realpath[MAXPATHLEN];
98
99  time (&clock);
100  tm = localtime (&clock);
101
102  if (bgp_dump->filename[0] != DIRECTORY_SEP)
103    {
104      sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
105      ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
106    }
107  else
108    ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
109
110  if (ret == 0)
111    {
112      zlog_warn ("bgp_dump_open_file: strftime error");
113      return NULL;
114    }
115
116  if (bgp_dump->fp)
117    fclose (bgp_dump->fp);
118
119
120  bgp_dump->fp = fopen (realpath, "w");
121
122  if (bgp_dump->fp == NULL)
123    return NULL;
124
125  return bgp_dump->fp;
126}
127
128int
129bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
130{
131  int bgp_dump_interval_func (struct thread *);
132
133  bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func,
134					   bgp_dump, interval);
135  return 0;
136}
137
138/* Dump common header. */
139void
140bgp_dump_header (struct stream *obuf, int type, int subtype)
141{
142  time_t now;
143
144  /* Set header. */
145  time (&now);
146
147  /* Put dump packet header. */
148  stream_putl (obuf, now);
149  stream_putw (obuf, type);
150  stream_putw (obuf, subtype);
151
152  stream_putl (obuf, 0);	/* len */
153}
154
155void
156bgp_dump_set_size (struct stream *s, int type)
157{
158  stream_putl_at (s, 8, stream_get_putp (s) - BGP_DUMP_HEADER_SIZE);
159}
160
161void
162bgp_dump_routes_entry (struct prefix *p, struct bgp_info *info, int afi,
163		       int type, unsigned int seq)
164{
165  struct stream *obuf;
166  struct attr *attr;
167  struct peer *peer;
168  int plen;
169  int safi = 0;
170
171  /* Make dump stream. */
172  obuf = bgp_dump_obuf;
173  stream_reset (obuf);
174
175  attr = info->attr;
176  peer = info->peer;
177
178  /* We support MRT's old format. */
179  if (type == MSG_TABLE_DUMP)
180    {
181      bgp_dump_header (obuf, MSG_TABLE_DUMP, afi);
182      stream_putw (obuf, 0);	/* View # */
183      stream_putw (obuf, seq);	/* Sequence number. */
184    }
185  else
186    {
187      bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY);
188
189      stream_putl (obuf, info->uptime); /* Time Last Change */
190      stream_putw (obuf, afi);	/* Address Family */
191      stream_putc (obuf, safi);	/* SAFI */
192    }
193
194  if (afi == AFI_IP)
195    {
196      if (type == MSG_TABLE_DUMP)
197	{
198	  /* Prefix */
199	  stream_put_in_addr (obuf, &p->u.prefix4);
200	  stream_putc (obuf, p->prefixlen);
201
202	  /* Status */
203	  stream_putc (obuf, 1);
204
205	  /* Originated */
206	  stream_putl (obuf, info->uptime);
207
208	  /* Peer's IP address */
209	  stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
210
211	  /* Peer's AS number. */
212	  stream_putw (obuf, peer->as);
213
214	  /* Dump attribute. */
215	  bgp_dump_routes_attr (obuf, attr);
216	}
217      else
218	{
219	  /* Next-Hop-Len */
220	  stream_putc (obuf, IPV4_MAX_BYTELEN);
221	  stream_put_in_addr (obuf, &attr->nexthop);
222	  stream_putc (obuf, p->prefixlen);
223	  plen = PSIZE (p->prefixlen);
224	  stream_put (obuf, &p->u.prefix4, plen);
225	  bgp_dump_routes_attr (obuf, attr);
226	}
227    }
228#ifdef HAVE_IPV6
229  else if (afi == AFI_IP6)
230    {
231      if (type == MSG_TABLE_DUMP)
232	{
233	  /* Prefix */
234	  stream_write (obuf, (u_char *)&p->u.prefix6, IPV6_MAX_BYTELEN);
235	  stream_putc (obuf, p->prefixlen);
236
237	  /* Status */
238	  stream_putc (obuf, 1);
239
240	  /* Originated */
241	  stream_putl (obuf, info->uptime);
242
243	  /* Peer's IP address */
244	  stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
245			IPV6_MAX_BYTELEN);
246
247	  /* Peer's AS number. */
248	  stream_putw (obuf, peer->as);
249
250	  /* Dump attribute. */
251	  bgp_dump_routes_attr (obuf, attr);
252	}
253      else
254	{
255	  ;
256	}
257    }
258#endif /* HAVE_IPV6 */
259
260  /* Set length. */
261  bgp_dump_set_size (obuf, type);
262
263  fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump_routes.fp);
264  fflush (bgp_dump_routes.fp);
265}
266
267/* Runs under child process. */
268void
269bgp_dump_routes_func (int afi)
270{
271  struct stream *obuf;
272  struct bgp_node *rn;
273  struct bgp_info *info;
274  struct bgp *bgp;
275  struct bgp_table *table;
276  unsigned int seq = 0;
277
278  obuf = bgp_dump_obuf;
279
280  bgp = bgp_get_default ();
281  if (!bgp)
282    return;
283
284  if (bgp_dump_routes.fp == NULL)
285    return;
286
287  /* Walk down each BGP route. */
288  table = bgp->rib[afi][SAFI_UNICAST];
289
290  for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
291    for (info = rn->info; info; info = info->next)
292      bgp_dump_routes_entry (&rn->p, info, afi, MSG_TABLE_DUMP, seq++);
293}
294
295int
296bgp_dump_interval_func (struct thread *t)
297{
298  struct bgp_dump *bgp_dump;
299
300  bgp_dump = THREAD_ARG (t);
301  bgp_dump->t_interval = NULL;
302
303  if (bgp_dump_open_file (bgp_dump) == NULL)
304    return 0;
305
306  /* In case of bgp_dump_routes, we need special route dump function. */
307  if (bgp_dump->type == BGP_DUMP_ROUTES)
308    {
309      bgp_dump_routes_func (AFI_IP);
310      bgp_dump_routes_func (AFI_IP6);
311    }
312
313  bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
314
315  return 0;
316}
317
318/* Dump common information. */
319void
320bgp_dump_common (struct stream *obuf, struct peer *peer)
321{
322  char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
323
324  /* Source AS number and Destination AS number. */
325  stream_putw (obuf, peer->as);
326  stream_putw (obuf, peer->local_as);
327
328  if (peer->afc[AFI_IP][SAFI_UNICAST])
329    {
330      stream_putw (obuf, peer->ifindex);
331      stream_putw (obuf, AFI_IP);
332
333      stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
334
335      if (peer->su_local)
336	stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN);
337      else
338	stream_put (obuf, empty, IPV4_MAX_BYTELEN);
339    }
340#ifdef HAVE_IPV6
341  else if (peer->afc[AFI_IP6][SAFI_UNICAST])
342    {
343      /* Interface Index and Address family. */
344      stream_putw (obuf, peer->ifindex);
345      stream_putw (obuf, AFI_IP6);
346
347      /* Source IP Address and Destination IP Address. */
348      stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
349
350      if (peer->su_local)
351	stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
352      else
353	stream_put (obuf, empty, IPV6_MAX_BYTELEN);
354    }
355#endif /* HAVE_IPV6 */
356}
357
358/* Dump BGP status change. */
359void
360bgp_dump_state (struct peer *peer, int status_old, int status_new)
361{
362  struct stream *obuf;
363
364  /* If dump file pointer is disabled return immediately. */
365  if (bgp_dump_all.fp == NULL)
366    return;
367
368  /* Make dump stream. */
369  obuf = bgp_dump_obuf;
370  stream_reset (obuf);
371
372  bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE);
373  bgp_dump_common (obuf, peer);
374
375  stream_putw (obuf, status_old);
376  stream_putw (obuf, status_new);
377
378  /* Set length. */
379  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
380
381  /* Write to the stream. */
382  fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump_all.fp);
383  fflush (bgp_dump_all.fp);
384}
385
386void
387bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
388		      struct stream *packet)
389{
390  struct stream *obuf;
391
392  /* If dump file pointer is disabled return immediately. */
393  if (bgp_dump->fp == NULL)
394    return;
395
396  /* Make dump stream. */
397  obuf = bgp_dump_obuf;
398  stream_reset (obuf);
399
400  /* Dump header and common part. */
401  bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE);
402  bgp_dump_common (obuf, peer);
403
404  /* Packet contents. */
405  stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
406
407  /* Set length. */
408  bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
409
410  /* Write to the stream. */
411  fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump->fp);
412  fflush (bgp_dump->fp);
413}
414
415/* Called from bgp_packet.c when BGP packet is received. */
416void
417bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
418{
419  /* bgp_dump_all. */
420  bgp_dump_packet_func (&bgp_dump_all, peer, packet);
421
422  /* bgp_dump_updates. */
423  if (type == BGP_MSG_UPDATE)
424    bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
425}
426
427unsigned int
428bgp_dump_parse_time (char *str)
429{
430  int i;
431  int len;
432  int seen_h;
433  int seen_m;
434  int time;
435  unsigned int total;
436
437  time = 0;
438  total = 0;
439  seen_h = 0;
440  seen_m = 0;
441  len = strlen (str);
442
443  for (i = 0; i < len; i++)
444    {
445      if (isdigit ((int) str[i]))
446	{
447	  time *= 10;
448	  time += str[i] - '0';
449	}
450      else if (str[i] == 'H' || str[i] == 'h')
451	{
452	  if (seen_h)
453	    return 0;
454	  if (seen_m)
455	    return 0;
456	  total += time * 60 *60;
457	  time = 0;
458	  seen_h = 1;
459	}
460      else if (str[i] == 'M' || str[i] == 'm')
461	{
462	  if (seen_m)
463	    return 0;
464	  total += time * 60;
465	  time = 0;
466	  seen_h = 1;
467	}
468      else
469	return 0;
470    }
471  return total + time;
472}
473
474int
475bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, int type,
476	      char *path, char *interval_str)
477{
478  if (interval_str)
479    {
480      unsigned int interval;
481
482      /* Check interval string. */
483      interval = bgp_dump_parse_time (interval_str);
484      if (interval == 0)
485	{
486	  vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
487	  return CMD_WARNING;
488	}
489      /* Set interval. */
490      bgp_dump->interval = interval;
491      if (bgp_dump->interval_str)
492	free (bgp_dump->interval_str);
493      bgp_dump->interval_str = strdup (interval_str);
494
495      /* Create interval thread. */
496      bgp_dump_interval_add (bgp_dump, interval);
497    }
498
499  /* Set type. */
500  bgp_dump->type = type;
501
502  /* Set file name. */
503  if (bgp_dump->filename)
504    free (bgp_dump->filename);
505  bgp_dump->filename = strdup (path);
506
507  /* This should be called when interval is expired. */
508  bgp_dump_open_file (bgp_dump);
509
510  return CMD_SUCCESS;
511}
512
513int
514bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
515{
516  /* Set file name. */
517  if (bgp_dump->filename)
518    {
519      free (bgp_dump->filename);
520      bgp_dump->filename = NULL;
521    }
522
523  /* This should be called when interval is expired. */
524  if (bgp_dump->fp)
525    {
526      fclose (bgp_dump->fp);
527      bgp_dump->fp = NULL;
528    }
529
530  /* Create interval thread. */
531  if (bgp_dump->t_interval)
532    {
533      thread_cancel (bgp_dump->t_interval);
534      bgp_dump->t_interval = NULL;
535    }
536
537  bgp_dump->interval = 0;
538
539  if (bgp_dump->interval_str)
540    {
541      free (bgp_dump->interval_str);
542      bgp_dump->interval_str = NULL;
543    }
544
545
546  return CMD_SUCCESS;
547}
548
549DEFUN (dump_bgp_all,
550       dump_bgp_all_cmd,
551       "dump bgp all PATH",
552       "Dump packet\n"
553       "BGP packet dump\n"
554       "Dump all BGP packets\n"
555       "Output filename\n")
556{
557  return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL);
558}
559
560DEFUN (dump_bgp_all_interval,
561       dump_bgp_all_interval_cmd,
562       "dump bgp all PATH INTERVAL",
563       "Dump packet\n"
564       "BGP packet dump\n"
565       "Dump all BGP packets\n"
566       "Output filename\n"
567       "Interval of output\n")
568{
569  return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]);
570}
571
572DEFUN (no_dump_bgp_all,
573       no_dump_bgp_all_cmd,
574       "no dump bgp all [PATH] [INTERVAL]",
575       NO_STR
576       "Dump packet\n"
577       "BGP packet dump\n"
578       "Dump all BGP packets\n")
579{
580  return bgp_dump_unset (vty, &bgp_dump_all);
581}
582
583DEFUN (dump_bgp_updates,
584       dump_bgp_updates_cmd,
585       "dump bgp updates PATH",
586       "Dump packet\n"
587       "BGP packet dump\n"
588       "Dump BGP updates only\n"
589       "Output filename\n")
590{
591  return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL);
592}
593
594DEFUN (dump_bgp_updates_interval,
595       dump_bgp_updates_interval_cmd,
596       "dump bgp updates PATH INTERVAL",
597       "Dump packet\n"
598       "BGP packet dump\n"
599       "Dump BGP updates only\n"
600       "Output filename\n"
601       "Interval of output\n")
602{
603  return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]);
604}
605
606DEFUN (no_dump_bgp_updates,
607       no_dump_bgp_updates_cmd,
608       "no dump bgp updates [PATH] [INTERVAL]",
609       NO_STR
610       "Dump packet\n"
611       "BGP packet dump\n"
612       "Dump BGP updates only\n")
613{
614  return bgp_dump_unset (vty, &bgp_dump_updates);
615}
616
617DEFUN (dump_bgp_routes,
618       dump_bgp_routes_cmd,
619       "dump bgp routes-mrt PATH",
620       "Dump packet\n"
621       "BGP packet dump\n"
622       "Dump whole BGP routing table\n"
623       "Output filename\n")
624{
625  return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL);
626}
627
628DEFUN (dump_bgp_routes_interval,
629       dump_bgp_routes_interval_cmd,
630       "dump bgp routes-mrt PATH INTERVAL",
631       "Dump packet\n"
632       "BGP packet dump\n"
633       "Dump whole BGP routing table\n"
634       "Output filename\n"
635       "Interval of output\n")
636{
637  return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]);
638}
639
640DEFUN (no_dump_bgp_routes,
641       no_dump_bgp_routes_cmd,
642       "no dump bgp routes-mrt [PATH] [INTERVAL]",
643       NO_STR
644       "Dump packet\n"
645       "BGP packet dump\n"
646       "Dump whole BGP routing table\n")
647{
648  return bgp_dump_unset (vty, &bgp_dump_routes);
649}
650
651/* BGP node structure. */
652struct cmd_node bgp_dump_node =
653{
654  DUMP_NODE,
655  "",
656};
657
658#if 0
659char *
660config_time2str (unsigned int interval)
661{
662  static char buf[BUFSIZ];
663
664  buf[0] = '\0';
665
666  if (interval / 3600)
667    {
668      sprintf (buf, "%dh", interval / 3600);
669      interval %= 3600;
670    }
671  if (interval / 60)
672    {
673      sprintf (buf + strlen (buf), "%dm", interval /60);
674      interval %= 60;
675    }
676  if (interval)
677    {
678      sprintf (buf + strlen (buf), "%d", interval);
679    }
680  return buf;
681}
682#endif
683
684int
685config_write_bgp_dump (struct vty *vty)
686{
687  if (bgp_dump_all.filename)
688    {
689      if (bgp_dump_all.interval_str)
690	vty_out (vty, "dump bgp all %s %s%s",
691		 bgp_dump_all.filename, bgp_dump_all.interval_str,
692		 VTY_NEWLINE);
693      else
694	vty_out (vty, "dump bgp all %s%s",
695		 bgp_dump_all.filename, VTY_NEWLINE);
696    }
697  if (bgp_dump_updates.filename)
698    {
699      if (bgp_dump_updates.interval_str)
700	vty_out (vty, "dump bgp updates %s %s%s",
701		 bgp_dump_updates.filename, bgp_dump_updates.interval_str,
702		 VTY_NEWLINE);
703      else
704	vty_out (vty, "dump bgp updates %s%s",
705		 bgp_dump_updates.filename, VTY_NEWLINE);
706    }
707  if (bgp_dump_routes.filename)
708    {
709      if (bgp_dump_routes.interval_str)
710	vty_out (vty, "dump bgp routes-mrt %s %s%s",
711		 bgp_dump_routes.filename, bgp_dump_routes.interval_str,
712		 VTY_NEWLINE);
713      else
714	vty_out (vty, "dump bgp routes-mrt %s%s",
715		 bgp_dump_routes.filename, VTY_NEWLINE);
716    }
717  return 0;
718}
719
720/* Initialize BGP packet dump functionality. */
721void
722bgp_dump_init ()
723{
724  memset (&bgp_dump_all, 0, sizeof (struct bgp_dump));
725  memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump));
726  memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump));
727
728  bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_HEADER_SIZE);
729
730  install_node (&bgp_dump_node, config_write_bgp_dump);
731
732  install_element (CONFIG_NODE, &dump_bgp_all_cmd);
733  install_element (CONFIG_NODE, &dump_bgp_all_interval_cmd);
734  install_element (CONFIG_NODE, &no_dump_bgp_all_cmd);
735  install_element (CONFIG_NODE, &dump_bgp_updates_cmd);
736  install_element (CONFIG_NODE, &dump_bgp_updates_interval_cmd);
737  install_element (CONFIG_NODE, &no_dump_bgp_updates_cmd);
738  install_element (CONFIG_NODE, &dump_bgp_routes_cmd);
739  install_element (CONFIG_NODE, &dump_bgp_routes_interval_cmd);
740  install_element (CONFIG_NODE, &no_dump_bgp_routes_cmd);
741}
742