blob: fca51edc7c2cdc2b0d6530b98530d57bcd53fbba [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* 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}