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"
33 #include "bgpd/bgpd.h"
34 #include "bgpd/bgp_route.h"
35 #include "bgpd/bgp_attr.h"
36 #include "bgpd/bgp_dump.h"
47 static const struct bgp_dump_type_map {
48 enum bgp_dump_type type;
50 } bgp_dump_type_map[] =
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"},
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 */
79 enum bgp_dump_type type;
85 unsigned int interval;
89 struct thread *t_interval;
92 static int bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump);
93 static int bgp_dump_interval_func (struct thread *);
95 /* BGP packet dump output buffer. */
96 struct stream *bgp_dump_obuf;
98 /* BGP dump strucuture for 'dump bgp all' */
99 struct bgp_dump bgp_dump_all;
101 /* BGP dump structure for 'dump bgp updates' */
102 struct bgp_dump bgp_dump_updates;
104 /* BGP dump structure for 'dump bgp routes' */
105 struct bgp_dump bgp_dump_routes;
108 bgp_dump_open_file (struct bgp_dump *bgp_dump)
113 char fullpath[MAXPATHLEN];
114 char realpath[MAXPATHLEN];
118 tm = localtime (&clock);
120 if (bgp_dump->filename[0] != DIRECTORY_SEP)
122 sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename);
123 ret = strftime (realpath, MAXPATHLEN, fullpath, tm);
126 ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm);
130 zlog_warn ("bgp_dump_open_file: strftime error");
135 fclose (bgp_dump->fp);
138 oldumask = umask(0777 & ~LOGFILE_MASK);
139 bgp_dump->fp = fopen (realpath, "w");
141 if (bgp_dump->fp == NULL)
143 zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno));
153 bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval)
161 /* Periodic dump every interval seconds */
162 if ((interval < 86400) && ((86400 % interval) == 0))
164 /* Dump at predictable times: if a day has a whole number of
165 * intervals, dump every interval seconds starting from midnight
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 */
172 bgp_dump->t_interval = thread_add_timer (bm->master, bgp_dump_interval_func,
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,
185 /* Dump common header. */
187 bgp_dump_header (struct stream *obuf, int type, int subtype, int dump_type)
189 struct timeval clock;
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;
197 gettimeofday(&clock, NULL);
200 msecs = clock.tv_usec;
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 */
208 /* Adding microseconds for the MRT Extended Header */
209 if (type == MSG_PROTOCOL_BGP4MP_ET)
210 stream_putl (obuf, msecs);
214 bgp_dump_set_size (struct stream *s, int type)
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)
221 stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE);
225 bgp_dump_routes_index_table(struct bgp *bgp)
228 struct listnode *node;
232 obuf = bgp_dump_obuf;
236 bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE,
239 /* Collector BGP ID */
240 stream_put_in_addr (obuf, &bgp->router_id);
245 stream_putw (obuf, strlen(bgp->name));
246 stream_put(obuf, bgp->name, strlen(bgp->name));
250 stream_putw(obuf, 0);
253 /* Peer count ( plus one extra internal peer ) */
254 stream_putw (obuf, listcount(bgp->peer) + 1);
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);
264 stream_putl (obuf, 0);
266 /* Walk down all peers */
267 for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer))
271 if (sockunion_family(&peer->su) == AF_INET)
273 stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
275 else if (sockunion_family(&peer->su) == AF_INET6)
277 stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
281 stream_put_in_addr (obuf, &peer->remote_id);
283 /* Peer's IP address */
284 if (sockunion_family(&peer->su) == AF_INET)
286 stream_put_in_addr (obuf, &peer->su.sin.sin_addr);
288 else if (sockunion_family(&peer->su) == AF_INET6)
290 stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr,
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);
298 /* Store the peer number for this peer */
299 peer->table_dump_index = peerno;
303 bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
305 fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
306 fflush (bgp_dump_routes.fp);
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)
318 obuf = bgp_dump_obuf;
323 bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST,
325 else if (afi == AFI_IP6)
326 bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST,
329 /* Sequence number */
330 stream_putl (obuf, seq);
333 stream_putc (obuf, rn->p.prefixlen);
338 /* We'll dump only the useful bits (those not 0), but have to
340 stream_write (obuf, (u_char *) &rn->p.u.prefix4,
341 (rn->p.prefixlen + 7) / 8);
343 else if (afi == AFI_IP6)
345 /* We'll dump only the useful bits (those not 0), but have to
347 stream_write (obuf, (u_char *) &rn->p.u.prefix6,
348 (rn->p.prefixlen + 7) / 8);
351 /* Save where we are now, so we can overwride the entry count later */
352 sizep = stream_get_endp (obuf);
355 uint16_t entry_count = 0;
357 /* Entry count, note that this is overwritten later */
358 stream_putw (obuf, 0);
360 endp = stream_get_endp (obuf);
361 for (; info; info = info->next)
366 stream_putw (obuf, info->peer->table_dump_index);
369 #ifdef HAVE_CLOCK_MONOTONIC
370 stream_putl (obuf, time (NULL) - (bgp_clock () - info->uptime));
372 stream_putl (obuf, info->uptime);
373 #endif /* HAVE_CLOCK_MONOTONIC */
375 /* Dump attribute. */
376 /* Skip prefix & AFI/SAFI for MP_NLRI */
377 bgp_dump_routes_attr (obuf, info->attr, &rn->p);
379 cur_endp = stream_get_endp (obuf);
380 if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
381 + BGP_DUMP_HEADER_SIZE)
383 stream_set_endp (obuf, endp);
391 /* Overwrite the entry count, now that we know the right number */
392 stream_putw_at (obuf, sizep, entry_count);
394 bgp_dump_set_size (obuf, MSG_TABLE_DUMP_V2);
395 fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
400 /* Runs under child process. */
402 bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
404 struct bgp_info *info;
407 struct bgp_table *table;
409 bgp = bgp_get_default ();
413 if (bgp_dump_routes.fp == NULL)
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 ) */
420 bgp_dump_routes_index_table(bgp);
422 /* Walk down each BGP route. */
423 table = bgp->rib[afi][SAFI_UNICAST];
425 for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
430 info = bgp_dump_route_node_record(afi, rn, info, seq);
435 fflush (bgp_dump_routes.fp);
441 bgp_dump_interval_func (struct thread *t)
443 struct bgp_dump *bgp_dump;
444 bgp_dump = THREAD_ARG (t);
445 bgp_dump->t_interval = NULL;
447 /* Reschedule dump even if file couldn't be opened this time... */
448 if (bgp_dump_open_file (bgp_dump) != NULL)
450 /* In case of bgp_dump_routes, we need special route dump function. */
451 if (bgp_dump->type == BGP_DUMP_ROUTES)
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;
461 /* if interval is set reschedule */
462 if (bgp_dump->interval > 0)
463 bgp_dump_interval_add (bgp_dump, bgp_dump->interval);
468 /* Dump common information. */
470 bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4)
472 char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
474 /* Source AS number and Destination AS number. */
475 if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
477 stream_putl (obuf, peer->as);
478 stream_putl (obuf, peer->local_as);
482 stream_putw (obuf, peer->as);
483 stream_putw (obuf, peer->local_as);
486 if (peer->su.sa.sa_family == AF_INET)
488 stream_putw (obuf, peer->ifindex);
489 stream_putw (obuf, AFI_IP);
491 stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
494 stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN);
496 stream_put (obuf, empty, IPV4_MAX_BYTELEN);
498 else if (peer->su.sa.sa_family == AF_INET6)
500 /* Interface Index and Address family. */
501 stream_putw (obuf, peer->ifindex);
502 stream_putw (obuf, AFI_IP6);
504 /* Source IP Address and Destination IP Address. */
505 stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
508 stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
510 stream_put (obuf, empty, IPV6_MAX_BYTELEN);
514 /* Dump BGP status change. */
516 bgp_dump_state (struct peer *peer, int status_old, int status_new)
520 /* If dump file pointer is disabled return immediately. */
521 if (bgp_dump_all.fp == NULL)
524 /* Make dump stream. */
525 obuf = bgp_dump_obuf;
528 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4,
530 bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/
532 stream_putw (obuf, status_old);
533 stream_putw (obuf, status_new);
536 bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
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);
544 bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer,
545 struct stream *packet)
549 /* If dump file pointer is disabled return immediately. */
550 if (bgp_dump->fp == NULL)
553 /* Make dump stream. */
554 obuf = bgp_dump_obuf;
557 /* Dump header and common part. */
558 if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) )
560 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4,
565 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
568 bgp_dump_common (obuf, peer, 0);
570 /* Packet contents. */
571 stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet));
574 bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP);
576 /* Write to the stream. */
577 fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp);
578 fflush (bgp_dump->fp);
581 /* Called from bgp_packet.c when BGP packet is received. */
583 bgp_dump_packet (struct peer *peer, int type, struct stream *packet)
586 bgp_dump_packet_func (&bgp_dump_all, peer, packet);
588 /* bgp_dump_updates. */
589 if (type == BGP_MSG_UPDATE)
590 bgp_dump_packet_func (&bgp_dump_updates, peer, packet);
594 bgp_dump_parse_time (const char *str)
609 for (i = 0; i < len; i++)
611 if (isdigit ((int) str[i]))
614 time += str[i] - '0';
616 else if (str[i] == 'H' || str[i] == 'h')
622 total += time * 60 *60;
626 else if (str[i] == 'M' || str[i] == 'm')
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)
645 unsigned int interval;
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)
653 if (bgp_dump->interval_str &&
654 strcmp(bgp_dump->interval_str, interval_str) == 0)
659 if (!bgp_dump->interval_str)
664 /* Removing previous config */
665 bgp_dump_unset(vty, bgp_dump);
669 /* Check interval string. */
670 interval = bgp_dump_parse_time (interval_str);
673 vty_out (vty, "Malformed interval string%s", VTY_NEWLINE);
677 /* Setting interval string */
678 bgp_dump->interval_str = strdup (interval_str);
686 bgp_dump->type = type;
689 bgp_dump->interval = interval;
692 bgp_dump->filename = strdup (path);
694 /* Create interval thread. */
695 bgp_dump_interval_add (bgp_dump, interval);
697 /* This should be called when interval is expired. */
698 bgp_dump_open_file (bgp_dump);
704 bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump)
706 /* Removing file name. */
707 if (bgp_dump->filename)
709 free (bgp_dump->filename);
710 bgp_dump->filename = NULL;
716 fclose (bgp_dump->fp);
720 /* Removing interval thread. */
721 if (bgp_dump->t_interval)
723 thread_cancel (bgp_dump->t_interval);
724 bgp_dump->t_interval = NULL;
727 bgp_dump->interval = 0;
729 /* Removing interval string. */
730 if (bgp_dump->interval_str)
732 free (bgp_dump->interval_str);
733 bgp_dump->interval_str = NULL;
741 "dump bgp (all|all-et|updates|updates-et|routes-mrt) PATH [INTERVAL]",
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"
748 "Interval of output\n")
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;
755 for (map = bgp_dump_type_map; map->str; map++)
756 if (strcmp(argv[0], map->str) == 0)
757 bgp_dump_type = map->type;
759 switch (bgp_dump_type)
762 case BGP_DUMP_ALL_ET:
763 bgp_dump_struct = &bgp_dump_all;
765 case BGP_DUMP_UPDATES:
766 case BGP_DUMP_UPDATES_ET:
767 bgp_dump_struct = &bgp_dump_updates;
769 case BGP_DUMP_ROUTES:
771 bgp_dump_struct = &bgp_dump_routes;
775 /* When an interval is given */
779 return bgp_dump_set (vty, bgp_dump_struct, bgp_dump_type,
783 DEFUN (no_dump_bgp_all,
785 "no dump bgp (all|updates|routes-mrt) [PATH] [INTERVAL]",
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")
793 return bgp_dump_unset (vty, &bgp_dump_all);
796 /* BGP node structure. */
797 static struct cmd_node bgp_dump_node =
806 config_time2str (unsigned int interval)
808 static char buf[BUFSIZ];
814 sprintf (buf, "%dh", interval / 3600);
819 sprintf (buf + strlen (buf), "%dm", interval /60);
824 sprintf (buf + strlen (buf), "%d", interval);
831 config_write_bgp_dump (struct vty *vty)
833 if (bgp_dump_all.filename)
835 const char *type_str = "all";
836 if (bgp_dump_all.type == BGP_DUMP_ALL_ET)
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,
844 vty_out (vty, "dump bgp %s %s%s", type_str,
845 bgp_dump_all.filename, VTY_NEWLINE);
847 if (bgp_dump_updates.filename)
849 const char *type_str = "updates";
850 if (bgp_dump_updates.type == BGP_DUMP_UPDATES_ET)
851 type_str = "updates-et";
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,
858 vty_out (vty, "dump bgp updates %s%s",
859 bgp_dump_updates.filename, VTY_NEWLINE);
861 if (bgp_dump_routes.filename)
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,
871 /* Initialize BGP packet dump functionality. */
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));
879 bgp_dump_obuf = stream_new ((BGP_MAX_PACKET_SIZE << 1)
880 + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
882 install_node (&bgp_dump_node, config_write_bgp_dump);
884 install_element (CONFIG_NODE, &dump_bgp_all_cmd);
885 install_element (CONFIG_NODE, &no_dump_bgp_all_cmd);
889 bgp_dump_finish (void)
891 stream_free (bgp_dump_obuf);
892 bgp_dump_obuf = NULL;