2 Copyright (C) 1999 Kunihiro Ishiguro
4 This file is part of GNU Zebra.
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
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.
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
25 #include "sockunion.h"
32 #include "bgpd/bgp_table.h"
34 #include "bgpd/bgpd.h"
35 #include "bgpd/bgp_route.h"
36 #include "bgpd/bgp_attr.h"
37 #include "bgpd/bgp_dump.h"
48 MSG_START
, /* sender is starting up */
49 MSG_DIE
, /* receiver should shut down */
50 MSG_I_AM_DEAD
, /* sender is shutting down */
51 MSG_PEER_DOWN
, /* sender's peer is down */
52 MSG_PROTOCOL_BGP
, /* msg is a BGP packet */
53 MSG_PROTOCOL_RIP
, /* msg is a RIP packet */
54 MSG_PROTOCOL_IDRP
, /* msg is an IDRP packet */
55 MSG_PROTOCOL_RIPNG
, /* msg is a RIPNG packet */
56 MSG_PROTOCOL_BGP4PLUS
, /* msg is a BGP4+ packet */
57 MSG_PROTOCOL_BGP4PLUS_01
, /* msg is a BGP4+ (draft 01) packet */
58 MSG_PROTOCOL_OSPF
, /* msg is an OSPF packet */
59 MSG_TABLE_DUMP
, /* routing table dump */
60 MSG_TABLE_DUMP_V2
/* routing table dump, version 2 */
63 static int bgp_dump_interval_func (struct thread
*);
67 enum bgp_dump_type type
;
73 unsigned int interval
;
77 struct thread
*t_interval
;
80 /* BGP packet dump output buffer. */
81 struct stream
*bgp_dump_obuf
;
83 /* BGP dump strucuture for 'dump bgp all' */
84 struct bgp_dump bgp_dump_all
;
86 /* BGP dump structure for 'dump bgp updates' */
87 struct bgp_dump bgp_dump_updates
;
89 /* BGP dump structure for 'dump bgp routes' */
90 struct bgp_dump bgp_dump_routes
;
92 /* Dump whole BGP table is very heavy process. */
93 struct thread
*t_bgp_dump_routes
;
95 /* Some define for BGP packet dump. */
97 bgp_dump_open_file (struct bgp_dump
*bgp_dump
)
102 char fullpath
[MAXPATHLEN
];
103 char realpath
[MAXPATHLEN
];
107 tm
= localtime (&clock
);
109 if (bgp_dump
->filename
[0] != DIRECTORY_SEP
)
111 sprintf (fullpath
, "%s/%s", vty_get_cwd (), bgp_dump
->filename
);
112 ret
= strftime (realpath
, MAXPATHLEN
, fullpath
, tm
);
115 ret
= strftime (realpath
, MAXPATHLEN
, bgp_dump
->filename
, tm
);
119 zlog_warn ("bgp_dump_open_file: strftime error");
124 fclose (bgp_dump
->fp
);
127 oldumask
= umask(0777 & ~LOGFILE_MASK
);
128 bgp_dump
->fp
= fopen (realpath
, "w");
130 if (bgp_dump
->fp
== NULL
)
132 zlog_warn ("bgp_dump_open_file: %s: %s", realpath
, strerror (errno
));
142 bgp_dump_interval_add (struct bgp_dump
*bgp_dump
, int interval
)
150 /* Periodic dump every interval seconds */
151 if ((interval
< 86400) && ((86400 % interval
) == 0))
153 /* Dump at predictable times: if a day has a whole number of
154 * intervals, dump every interval seconds starting from midnight
158 secs_into_day
= tm
->tm_sec
+ 60*tm
->tm_min
+ 60*60*tm
->tm_hour
;
159 interval
= interval
- secs_into_day
% interval
; /* always > 0 */
161 bgp_dump
->t_interval
= thread_add_timer (master
, bgp_dump_interval_func
,
166 /* One-off dump: execute immediately, don't affect any scheduled dumps */
167 bgp_dump
->t_interval
= thread_add_event (master
, bgp_dump_interval_func
,
174 /* Dump common header. */
176 bgp_dump_header (struct stream
*obuf
, int type
, int subtype
)
183 /* Put dump packet header. */
184 stream_putl (obuf
, now
);
185 stream_putw (obuf
, type
);
186 stream_putw (obuf
, subtype
);
188 stream_putl (obuf
, 0); /* len */
192 bgp_dump_set_size (struct stream
*s
, int type
)
194 stream_putl_at (s
, 8, stream_get_endp (s
) - BGP_DUMP_HEADER_SIZE
);
198 bgp_dump_routes_index_table(struct bgp
*bgp
)
201 struct listnode
*node
;
205 obuf
= bgp_dump_obuf
;
209 bgp_dump_header (obuf
, MSG_TABLE_DUMP_V2
, TABLE_DUMP_V2_PEER_INDEX_TABLE
);
211 /* Collector BGP ID */
212 stream_put_in_addr (obuf
, &bgp
->router_id
);
217 stream_putw (obuf
, strlen(bgp
->name
));
218 stream_put(obuf
, bgp
->name
, strlen(bgp
->name
));
222 stream_putw(obuf
, 0);
226 stream_putw (obuf
, listcount(bgp
->peer
));
228 /* Walk down all peers */
229 for(ALL_LIST_ELEMENTS_RO (bgp
->peer
, node
, peer
))
233 if (sockunion_family(&peer
->su
) == AF_INET
)
235 stream_putc (obuf
, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP
);
238 else if (sockunion_family(&peer
->su
) == AF_INET6
)
240 stream_putc (obuf
, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6
);
242 #endif /* HAVE_IPV6 */
245 stream_put_in_addr (obuf
, &peer
->remote_id
);
247 /* Peer's IP address */
248 if (sockunion_family(&peer
->su
) == AF_INET
)
250 stream_put_in_addr (obuf
, &peer
->su
.sin
.sin_addr
);
253 else if (sockunion_family(&peer
->su
) == AF_INET6
)
255 stream_write (obuf
, (u_char
*)&peer
->su
.sin6
.sin6_addr
,
258 #endif /* HAVE_IPV6 */
260 /* Peer's AS number. */
261 /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */
262 stream_putl (obuf
, peer
->as
);
264 /* Store the peer number for this peer */
265 peer
->table_dump_index
= peerno
;
269 bgp_dump_set_size(obuf
, MSG_TABLE_DUMP_V2
);
271 fwrite (STREAM_DATA (obuf
), stream_get_endp (obuf
), 1, bgp_dump_routes
.fp
);
272 fflush (bgp_dump_routes
.fp
);
276 /* Runs under child process. */
278 bgp_dump_routes_func (int afi
, int first_run
, unsigned int seq
)
281 struct bgp_info
*info
;
284 struct bgp_table
*table
;
286 bgp
= bgp_get_default ();
290 if (bgp_dump_routes
.fp
== NULL
)
293 /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
294 so this should only be done on the first call to bgp_dump_routes_func.
295 ( this function will be called once for ipv4 and once for ipv6 ) */
297 bgp_dump_routes_index_table(bgp
);
299 obuf
= bgp_dump_obuf
;
302 /* Walk down each BGP route. */
303 table
= bgp
->rib
[afi
][SAFI_UNICAST
];
305 for (rn
= bgp_table_top (table
); rn
; rn
= bgp_route_next (rn
))
315 bgp_dump_header (obuf
, MSG_TABLE_DUMP_V2
, TABLE_DUMP_V2_RIB_IPV4_UNICAST
);
318 else if (afi
== AFI_IP6
)
320 bgp_dump_header (obuf
, MSG_TABLE_DUMP_V2
, TABLE_DUMP_V2_RIB_IPV6_UNICAST
);
322 #endif /* HAVE_IPV6 */
324 /* Sequence number */
325 stream_putl(obuf
, seq
);
328 stream_putc (obuf
, rn
->p
.prefixlen
);
333 /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
334 stream_write(obuf
, (u_char
*)&rn
->p
.u
.prefix4
, (rn
->p
.prefixlen
+7)/8);
337 else if (afi
== AFI_IP6
)
339 /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
340 stream_write (obuf
, (u_char
*)&rn
->p
.u
.prefix6
, (rn
->p
.prefixlen
+7)/8);
342 #endif /* HAVE_IPV6 */
344 /* Save where we are now, so we can overwride the entry count later */
345 int sizep
= stream_get_endp(obuf
);
348 uint16_t entry_count
= 0;
350 /* Entry count, note that this is overwritten later */
351 stream_putw(obuf
, 0);
353 for (info
= rn
->info
; info
; info
= info
->next
)
358 stream_putw(obuf
, info
->peer
->table_dump_index
);
361 #ifdef HAVE_CLOCK_MONOTONIC
362 stream_putl (obuf
, time(NULL
) - (bgp_clock() - info
->uptime
));
364 stream_putl (obuf
, info
->uptime
);
365 #endif /* HAVE_CLOCK_MONOTONIC */
367 /* Dump attribute. */
368 /* Skip prefix & AFI/SAFI for MP_NLRI */
369 bgp_dump_routes_attr (obuf
, info
->attr
, &rn
->p
);
372 /* Overwrite the entry count, now that we know the right number */
373 stream_putw_at (obuf
, sizep
, entry_count
);
377 bgp_dump_set_size(obuf
, MSG_TABLE_DUMP_V2
);
378 fwrite (STREAM_DATA (obuf
), stream_get_endp (obuf
), 1, bgp_dump_routes
.fp
);
382 fflush (bgp_dump_routes
.fp
);
388 bgp_dump_interval_func (struct thread
*t
)
390 struct bgp_dump
*bgp_dump
;
391 bgp_dump
= THREAD_ARG (t
);
392 bgp_dump
->t_interval
= NULL
;
394 /* Reschedule dump even if file couldn't be opened this time... */
395 if (bgp_dump_open_file (bgp_dump
) != NULL
)
397 /* In case of bgp_dump_routes, we need special route dump function. */
398 if (bgp_dump
->type
== BGP_DUMP_ROUTES
)
400 unsigned int seq
= bgp_dump_routes_func (AFI_IP
, 1, 0);
402 bgp_dump_routes_func (AFI_IP6
, 0, seq
);
403 #endif /* HAVE_IPV6 */
404 /* Close the file now. For a RIB dump there's no point in leaving
405 * it open until the next scheduled dump starts. */
406 fclose(bgp_dump
->fp
); bgp_dump
->fp
= NULL
;
410 /* if interval is set reschedule */
411 if (bgp_dump
->interval
> 0)
412 bgp_dump_interval_add (bgp_dump
, bgp_dump
->interval
);
417 /* Dump common information. */
419 bgp_dump_common (struct stream
*obuf
, struct peer
*peer
, int forceas4
)
421 char empty
[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
423 /* Source AS number and Destination AS number. */
424 if (forceas4
|| CHECK_FLAG (peer
->cap
, PEER_CAP_AS4_RCV
) )
426 stream_putl (obuf
, peer
->as
);
427 stream_putl (obuf
, peer
->local_as
);
431 stream_putw (obuf
, peer
->as
);
432 stream_putw (obuf
, peer
->local_as
);
435 if (peer
->su
.sa
.sa_family
== AF_INET
)
437 stream_putw (obuf
, peer
->ifindex
);
438 stream_putw (obuf
, AFI_IP
);
440 stream_put (obuf
, &peer
->su
.sin
.sin_addr
, IPV4_MAX_BYTELEN
);
443 stream_put (obuf
, &peer
->su_local
->sin
.sin_addr
, IPV4_MAX_BYTELEN
);
445 stream_put (obuf
, empty
, IPV4_MAX_BYTELEN
);
448 else if (peer
->su
.sa
.sa_family
== AF_INET6
)
450 /* Interface Index and Address family. */
451 stream_putw (obuf
, peer
->ifindex
);
452 stream_putw (obuf
, AFI_IP6
);
454 /* Source IP Address and Destination IP Address. */
455 stream_put (obuf
, &peer
->su
.sin6
.sin6_addr
, IPV6_MAX_BYTELEN
);
458 stream_put (obuf
, &peer
->su_local
->sin6
.sin6_addr
, IPV6_MAX_BYTELEN
);
460 stream_put (obuf
, empty
, IPV6_MAX_BYTELEN
);
462 #endif /* HAVE_IPV6 */
465 /* Dump BGP status change. */
467 bgp_dump_state (struct peer
*peer
, int status_old
, int status_new
)
471 /* If dump file pointer is disabled return immediately. */
472 if (bgp_dump_all
.fp
== NULL
)
475 /* Make dump stream. */
476 obuf
= bgp_dump_obuf
;
479 bgp_dump_header (obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_STATE_CHANGE_AS4
);
480 bgp_dump_common (obuf
, peer
, 1);/* force this in as4speak*/
482 stream_putw (obuf
, status_old
);
483 stream_putw (obuf
, status_new
);
486 bgp_dump_set_size (obuf
, MSG_PROTOCOL_BGP4MP
);
488 /* Write to the stream. */
489 fwrite (STREAM_DATA (obuf
), stream_get_endp (obuf
), 1, bgp_dump_all
.fp
);
490 fflush (bgp_dump_all
.fp
);
494 bgp_dump_packet_func (struct bgp_dump
*bgp_dump
, struct peer
*peer
,
495 struct stream
*packet
)
499 /* If dump file pointer is disabled return immediately. */
500 if (bgp_dump
->fp
== NULL
)
503 /* Make dump stream. */
504 obuf
= bgp_dump_obuf
;
507 /* Dump header and common part. */
508 if (CHECK_FLAG (peer
->cap
, PEER_CAP_AS4_RCV
) )
510 bgp_dump_header (obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_MESSAGE_AS4
);
514 bgp_dump_header (obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_MESSAGE
);
516 bgp_dump_common (obuf
, peer
, 0);
518 /* Packet contents. */
519 stream_put (obuf
, STREAM_DATA (packet
), stream_get_endp (packet
));
522 bgp_dump_set_size (obuf
, MSG_PROTOCOL_BGP4MP
);
524 /* Write to the stream. */
525 fwrite (STREAM_DATA (obuf
), stream_get_endp (obuf
), 1, bgp_dump
->fp
);
526 fflush (bgp_dump
->fp
);
529 /* Called from bgp_packet.c when BGP packet is received. */
531 bgp_dump_packet (struct peer
*peer
, int type
, struct stream
*packet
)
534 bgp_dump_packet_func (&bgp_dump_all
, peer
, packet
);
536 /* bgp_dump_updates. */
537 if (type
== BGP_MSG_UPDATE
)
538 bgp_dump_packet_func (&bgp_dump_updates
, peer
, packet
);
542 bgp_dump_parse_time (const char *str
)
557 for (i
= 0; i
< len
; i
++)
559 if (isdigit ((int) str
[i
]))
562 time
+= str
[i
] - '0';
564 else if (str
[i
] == 'H' || str
[i
] == 'h')
570 total
+= time
* 60 *60;
574 else if (str
[i
] == 'M' || str
[i
] == 'm')
589 bgp_dump_set (struct vty
*vty
, struct bgp_dump
*bgp_dump
,
590 enum bgp_dump_type type
, const char *path
,
591 const char *interval_str
)
593 unsigned int interval
;
598 /* Check interval string. */
599 interval
= bgp_dump_parse_time (interval_str
);
602 vty_out (vty
, "Malformed interval string%s", VTY_NEWLINE
);
606 /* Don't schedule duplicate dumps if the dump command is given twice */
607 if (interval
== bgp_dump
->interval
&&
608 type
== bgp_dump
->type
&&
609 path
&& bgp_dump
->filename
&& !strcmp (path
, bgp_dump
->filename
))
615 bgp_dump
->interval
= interval
;
616 if (bgp_dump
->interval_str
)
617 free (bgp_dump
->interval_str
);
618 bgp_dump
->interval_str
= strdup (interval_str
);
626 /* Create interval thread. */
627 bgp_dump_interval_add (bgp_dump
, interval
);
630 bgp_dump
->type
= type
;
633 if (bgp_dump
->filename
)
634 free (bgp_dump
->filename
);
635 bgp_dump
->filename
= strdup (path
);
637 /* This should be called when interval is expired. */
638 bgp_dump_open_file (bgp_dump
);
644 bgp_dump_unset (struct vty
*vty
, struct bgp_dump
*bgp_dump
)
647 if (bgp_dump
->filename
)
649 free (bgp_dump
->filename
);
650 bgp_dump
->filename
= NULL
;
653 /* This should be called when interval is expired. */
656 fclose (bgp_dump
->fp
);
660 /* Create interval thread. */
661 if (bgp_dump
->t_interval
)
663 thread_cancel (bgp_dump
->t_interval
);
664 bgp_dump
->t_interval
= NULL
;
667 bgp_dump
->interval
= 0;
669 if (bgp_dump
->interval_str
)
671 free (bgp_dump
->interval_str
);
672 bgp_dump
->interval_str
= NULL
;
684 "Dump all BGP packets\n"
687 return bgp_dump_set (vty
, &bgp_dump_all
, BGP_DUMP_ALL
, argv
[0], NULL
);
690 DEFUN (dump_bgp_all_interval
,
691 dump_bgp_all_interval_cmd
,
692 "dump bgp all PATH INTERVAL",
695 "Dump all BGP packets\n"
697 "Interval of output\n")
699 return bgp_dump_set (vty
, &bgp_dump_all
, BGP_DUMP_ALL
, argv
[0], argv
[1]);
702 DEFUN (no_dump_bgp_all
,
704 "no dump bgp all [PATH] [INTERVAL]",
708 "Dump all BGP packets\n")
710 return bgp_dump_unset (vty
, &bgp_dump_all
);
713 DEFUN (dump_bgp_updates
,
714 dump_bgp_updates_cmd
,
715 "dump bgp updates PATH",
718 "Dump BGP updates only\n"
721 return bgp_dump_set (vty
, &bgp_dump_updates
, BGP_DUMP_UPDATES
, argv
[0], NULL
);
724 DEFUN (dump_bgp_updates_interval
,
725 dump_bgp_updates_interval_cmd
,
726 "dump bgp updates PATH INTERVAL",
729 "Dump BGP updates only\n"
731 "Interval of output\n")
733 return bgp_dump_set (vty
, &bgp_dump_updates
, BGP_DUMP_UPDATES
, argv
[0], argv
[1]);
736 DEFUN (no_dump_bgp_updates
,
737 no_dump_bgp_updates_cmd
,
738 "no dump bgp updates [PATH] [INTERVAL]",
742 "Dump BGP updates only\n")
744 return bgp_dump_unset (vty
, &bgp_dump_updates
);
747 DEFUN (dump_bgp_routes
,
749 "dump bgp routes-mrt PATH",
752 "Dump whole BGP routing table\n"
755 return bgp_dump_set (vty
, &bgp_dump_routes
, BGP_DUMP_ROUTES
, argv
[0], NULL
);
758 DEFUN (dump_bgp_routes_interval
,
759 dump_bgp_routes_interval_cmd
,
760 "dump bgp routes-mrt PATH INTERVAL",
763 "Dump whole BGP routing table\n"
765 "Interval of output\n")
767 return bgp_dump_set (vty
, &bgp_dump_routes
, BGP_DUMP_ROUTES
, argv
[0], argv
[1]);
770 DEFUN (no_dump_bgp_routes
,
771 no_dump_bgp_routes_cmd
,
772 "no dump bgp routes-mrt [PATH] [INTERVAL]",
776 "Dump whole BGP routing table\n")
778 return bgp_dump_unset (vty
, &bgp_dump_routes
);
781 /* BGP node structure. */
782 static struct cmd_node bgp_dump_node
=
791 config_time2str (unsigned int interval
)
793 static char buf
[BUFSIZ
];
799 sprintf (buf
, "%dh", interval
/ 3600);
804 sprintf (buf
+ strlen (buf
), "%dm", interval
/60);
809 sprintf (buf
+ strlen (buf
), "%d", interval
);
816 config_write_bgp_dump (struct vty
*vty
)
818 if (bgp_dump_all
.filename
)
820 if (bgp_dump_all
.interval_str
)
821 vty_out (vty
, "dump bgp all %s %s%s",
822 bgp_dump_all
.filename
, bgp_dump_all
.interval_str
,
825 vty_out (vty
, "dump bgp all %s%s",
826 bgp_dump_all
.filename
, VTY_NEWLINE
);
828 if (bgp_dump_updates
.filename
)
830 if (bgp_dump_updates
.interval_str
)
831 vty_out (vty
, "dump bgp updates %s %s%s",
832 bgp_dump_updates
.filename
, bgp_dump_updates
.interval_str
,
835 vty_out (vty
, "dump bgp updates %s%s",
836 bgp_dump_updates
.filename
, VTY_NEWLINE
);
838 if (bgp_dump_routes
.filename
)
840 if (bgp_dump_routes
.interval_str
)
841 vty_out (vty
, "dump bgp routes-mrt %s %s%s",
842 bgp_dump_routes
.filename
, bgp_dump_routes
.interval_str
,
845 vty_out (vty
, "dump bgp routes-mrt %s%s",
846 bgp_dump_routes
.filename
, VTY_NEWLINE
);
851 /* Initialize BGP packet dump functionality. */
855 memset (&bgp_dump_all
, 0, sizeof (struct bgp_dump
));
856 memset (&bgp_dump_updates
, 0, sizeof (struct bgp_dump
));
857 memset (&bgp_dump_routes
, 0, sizeof (struct bgp_dump
));
859 bgp_dump_obuf
= stream_new (BGP_MAX_PACKET_SIZE
+ BGP_DUMP_MSG_HEADER
860 + BGP_DUMP_HEADER_SIZE
);
862 install_node (&bgp_dump_node
, config_write_bgp_dump
);
864 install_element (CONFIG_NODE
, &dump_bgp_all_cmd
);
865 install_element (CONFIG_NODE
, &dump_bgp_all_interval_cmd
);
866 install_element (CONFIG_NODE
, &no_dump_bgp_all_cmd
);
867 install_element (CONFIG_NODE
, &dump_bgp_updates_cmd
);
868 install_element (CONFIG_NODE
, &dump_bgp_updates_interval_cmd
);
869 install_element (CONFIG_NODE
, &no_dump_bgp_updates_cmd
);
870 install_element (CONFIG_NODE
, &dump_bgp_routes_cmd
);
871 install_element (CONFIG_NODE
, &dump_bgp_routes_interval_cmd
);
872 install_element (CONFIG_NODE
, &no_dump_bgp_routes_cmd
);
876 bgp_dump_finish (void)
878 stream_free (bgp_dump_obuf
);
879 bgp_dump_obuf
= NULL
;