1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 1999 Kunihiro Ishiguro
10 #include "sockunion.h"
19 #include "bgpd/bgp_table.h"
20 #include "bgpd/bgpd.h"
21 #include "bgpd/bgp_route.h"
22 #include "bgpd/bgp_attr.h"
23 #include "bgpd/bgp_dump.h"
24 #include "bgpd/bgp_errors.h"
25 #include "bgpd/bgp_packet.h"
35 static const struct bgp_dump_type_map
{
36 enum bgp_dump_type type
;
38 } bgp_dump_type_map
[] = {
39 {BGP_DUMP_ALL
, "all"}, {BGP_DUMP_ALL_ET
, "all-et"},
40 {BGP_DUMP_UPDATES
, "updates"}, {BGP_DUMP_UPDATES_ET
, "updates-et"},
41 {BGP_DUMP_ROUTES
, "routes-mrt"}, {0, NULL
},
46 MSG_START
, /* sender is starting up */
47 MSG_DIE
, /* receiver should shut down */
48 MSG_I_AM_DEAD
, /* sender is shutting down */
49 MSG_PEER_DOWN
, /* sender's peer is down */
50 MSG_PROTOCOL_BGP
, /* msg is a BGP packet */
51 MSG_PROTOCOL_RIP
, /* msg is a RIP packet */
52 MSG_PROTOCOL_IDRP
, /* msg is an IDRP packet */
53 MSG_PROTOCOL_RIPNG
, /* msg is a RIPNG packet */
54 MSG_PROTOCOL_BGP4PLUS
, /* msg is a BGP4+ packet */
55 MSG_PROTOCOL_BGP4PLUS_01
, /* msg is a BGP4+ (draft 01) packet */
56 MSG_PROTOCOL_OSPF
, /* msg is an OSPF packet */
57 MSG_TABLE_DUMP
, /* routing table dump */
58 MSG_TABLE_DUMP_V2
/* routing table dump, version 2 */
62 enum bgp_dump_type type
;
68 unsigned int interval
;
72 struct thread
*t_interval
;
75 static int bgp_dump_unset(struct bgp_dump
*bgp_dump
);
76 static void bgp_dump_interval_func(struct thread
*);
78 /* BGP packet dump output buffer. */
79 struct stream
*bgp_dump_obuf
;
81 /* BGP dump strucuture for 'dump bgp all' */
82 struct bgp_dump bgp_dump_all
;
84 /* BGP dump structure for 'dump bgp updates' */
85 struct bgp_dump bgp_dump_updates
;
87 /* BGP dump structure for 'dump bgp routes' */
88 struct bgp_dump bgp_dump_routes
;
90 static FILE *bgp_dump_open_file(struct bgp_dump
*bgp_dump
)
95 char fullpath
[MAXPATHLEN
];
96 char realpath
[MAXPATHLEN
];
100 localtime_r(&clock
, &tm
);
102 if (bgp_dump
->filename
[0] != DIRECTORY_SEP
) {
103 snprintf(fullpath
, sizeof(fullpath
), "%s/%s", vty_get_cwd(),
105 #pragma GCC diagnostic push
106 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
107 /* user supplied date/time format string */
108 ret
= strftime(realpath
, MAXPATHLEN
, fullpath
, &tm
);
110 ret
= strftime(realpath
, MAXPATHLEN
, bgp_dump
->filename
, &tm
);
111 #pragma GCC diagnostic pop
114 flog_warn(EC_BGP_DUMP
, "%s: strftime error", __func__
);
119 fclose(bgp_dump
->fp
);
122 oldumask
= umask(0777 & ~LOGFILE_MASK
);
123 bgp_dump
->fp
= fopen(realpath
, "w");
125 if (bgp_dump
->fp
== NULL
) {
126 flog_warn(EC_BGP_DUMP
, "%s: %s: %s", __func__
, realpath
,
136 static int bgp_dump_interval_add(struct bgp_dump
*bgp_dump
, int interval
)
143 /* Periodic dump every interval seconds */
144 if ((interval
< 86400) && ((86400 % interval
) == 0)) {
145 /* Dump at predictable times: if a day has a whole
147 * intervals, dump every interval seconds starting from
151 localtime_r(&t
, &tm
);
152 secs_into_day
= tm
.tm_sec
+ 60 * tm
.tm_min
153 + 60 * 60 * tm
.tm_hour
;
155 - secs_into_day
% interval
; /* always > 0 */
157 thread_add_timer(bm
->master
, bgp_dump_interval_func
, bgp_dump
,
158 interval
, &bgp_dump
->t_interval
);
160 /* One-off dump: execute immediately, don't affect any scheduled
162 thread_add_event(bm
->master
, bgp_dump_interval_func
, bgp_dump
,
163 0, &bgp_dump
->t_interval
);
169 /* Dump common header. */
170 static void bgp_dump_header(struct stream
*obuf
, int type
, int subtype
,
173 struct timeval clock
;
177 if ((dump_type
== BGP_DUMP_ALL_ET
|| dump_type
== BGP_DUMP_UPDATES_ET
)
178 && type
== MSG_PROTOCOL_BGP4MP
)
179 type
= MSG_PROTOCOL_BGP4MP_ET
;
181 gettimeofday(&clock
, NULL
);
184 msecs
= clock
.tv_usec
;
186 /* Put dump packet header. */
187 stream_putl(obuf
, secs
);
188 stream_putw(obuf
, type
);
189 stream_putw(obuf
, subtype
);
190 stream_putl(obuf
, 0); /* len */
192 /* Adding microseconds for the MRT Extended Header */
193 if (type
== MSG_PROTOCOL_BGP4MP_ET
)
194 stream_putl(obuf
, msecs
);
197 static void bgp_dump_set_size(struct stream
*s
, int type
)
200 * The BGP_DUMP_HEADER_SIZE stay at 12 event when ET:
201 * "The Microsecond Timestamp is included in the computation
202 * of the Length field value." (RFC6396 2011)
204 stream_putl_at(s
, 8, stream_get_endp(s
) - BGP_DUMP_HEADER_SIZE
);
207 static void bgp_dump_routes_index_table(struct bgp
*bgp
)
210 struct listnode
*node
;
214 obuf
= bgp_dump_obuf
;
218 bgp_dump_header(obuf
, MSG_TABLE_DUMP_V2
, TABLE_DUMP_V2_PEER_INDEX_TABLE
,
221 /* Collector BGP ID */
222 stream_put_in_addr(obuf
, &bgp
->router_id
);
225 if (bgp
->name_pretty
) {
226 stream_putw(obuf
, strlen(bgp
->name_pretty
));
227 stream_put(obuf
, bgp
->name_pretty
, strlen(bgp
->name_pretty
));
229 stream_putw(obuf
, 0);
232 /* Peer count ( plus one extra internal peer ) */
233 stream_putw(obuf
, listcount(bgp
->peer
) + 1);
235 /* Populate fake peer at index 0, for locally originated routes */
236 /* Peer type (IPv4) */
238 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
239 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP
);
240 /* Peer BGP ID (0.0.0.0) */
241 stream_putl(obuf
, 0);
242 /* Peer IP address (0.0.0.0) */
243 stream_putl(obuf
, 0);
245 stream_putl(obuf
, 0);
247 /* Walk down all peers */
248 for (ALL_LIST_ELEMENTS_RO(bgp
->peer
, node
, peer
)) {
251 if (sockunion_family(&peer
->su
) == AF_INET
) {
254 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
255 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP
);
256 } else if (sockunion_family(&peer
->su
) == AF_INET6
) {
259 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
260 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6
);
264 stream_put_in_addr(obuf
, &peer
->remote_id
);
266 /* Peer's IP address */
267 if (sockunion_family(&peer
->su
) == AF_INET
) {
268 stream_put_in_addr(obuf
, &peer
->su
.sin
.sin_addr
);
269 } else if (sockunion_family(&peer
->su
) == AF_INET6
) {
270 stream_write(obuf
, (uint8_t *)&peer
->su
.sin6
.sin6_addr
,
274 /* Peer's AS number. */
275 /* Note that, as this is an AS4 compliant quagga, the RIB is
277 stream_putl(obuf
, peer
->as
);
279 /* Store the peer number for this peer */
280 peer
->table_dump_index
= peerno
;
284 bgp_dump_set_size(obuf
, MSG_TABLE_DUMP_V2
);
286 fwrite(STREAM_DATA(obuf
), stream_get_endp(obuf
), 1, bgp_dump_routes
.fp
);
287 fflush(bgp_dump_routes
.fp
);
290 static struct bgp_path_info
*
291 bgp_dump_route_node_record(int afi
, struct bgp_dest
*dest
,
292 struct bgp_path_info
*path
, unsigned int seq
)
297 bool addpath_capable
;
298 const struct prefix
*p
= bgp_dest_get_prefix(dest
);
300 obuf
= bgp_dump_obuf
;
303 addpath_capable
= bgp_addpath_encode_rx(path
->peer
, afi
, SAFI_UNICAST
);
306 if (afi
== AFI_IP
&& addpath_capable
)
307 bgp_dump_header(obuf
, MSG_TABLE_DUMP_V2
,
308 TABLE_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH
,
310 else if (afi
== AFI_IP
)
311 bgp_dump_header(obuf
, MSG_TABLE_DUMP_V2
,
312 TABLE_DUMP_V2_RIB_IPV4_UNICAST
,
314 else if (afi
== AFI_IP6
&& addpath_capable
)
315 bgp_dump_header(obuf
, MSG_TABLE_DUMP_V2
,
316 TABLE_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH
,
318 else if (afi
== AFI_IP6
)
319 bgp_dump_header(obuf
, MSG_TABLE_DUMP_V2
,
320 TABLE_DUMP_V2_RIB_IPV6_UNICAST
,
323 /* Sequence number */
324 stream_putl(obuf
, seq
);
327 stream_putc(obuf
, p
->prefixlen
);
331 /* We'll dump only the useful bits (those not 0), but have to
333 stream_write(obuf
, (uint8_t *)&p
->u
.prefix4
,
334 (p
->prefixlen
+ 7) / 8);
335 } else if (afi
== AFI_IP6
) {
336 /* We'll dump only the useful bits (those not 0), but have to
338 stream_write(obuf
, (uint8_t *)&p
->u
.prefix6
,
339 (p
->prefixlen
+ 7) / 8);
342 /* Save where we are now, so we can overwride the entry count later */
343 sizep
= stream_get_endp(obuf
);
346 uint16_t entry_count
= 0;
348 /* Entry count, note that this is overwritten later */
349 stream_putw(obuf
, 0);
351 endp
= stream_get_endp(obuf
);
352 for (; path
; path
= path
->next
) {
356 stream_putw(obuf
, path
->peer
->table_dump_index
);
359 stream_putl(obuf
, time(NULL
) - (monotime(NULL
) - path
->uptime
));
362 if (addpath_capable
) {
363 stream_putl(obuf
, path
->addpath_rx_id
);
366 /* Dump attribute. */
367 /* Skip prefix & AFI/SAFI for MP_NLRI */
368 bgp_dump_routes_attr(obuf
, path
, p
);
370 cur_endp
= stream_get_endp(obuf
);
371 if (cur_endp
> BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE
372 + BGP_DUMP_MSG_HEADER
373 + BGP_DUMP_HEADER_SIZE
) {
374 stream_set_endp(obuf
, endp
);
382 /* Overwrite the entry count, now that we know the right number */
383 stream_putw_at(obuf
, sizep
, entry_count
);
385 bgp_dump_set_size(obuf
, MSG_TABLE_DUMP_V2
);
386 fwrite(STREAM_DATA(obuf
), stream_get_endp(obuf
), 1, bgp_dump_routes
.fp
);
392 /* Runs under child process. */
393 static unsigned int bgp_dump_routes_func(int afi
, int first_run
,
396 struct bgp_path_info
*path
;
397 struct bgp_dest
*dest
;
399 struct bgp_table
*table
;
401 bgp
= bgp_get_default();
405 if (bgp_dump_routes
.fp
== NULL
)
408 /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
409 so this should only be done on the first call to
410 bgp_dump_routes_func.
411 ( this function will be called once for ipv4 and once for ipv6 ) */
413 bgp_dump_routes_index_table(bgp
);
415 /* Walk down each BGP route. */
416 table
= bgp
->rib
[afi
][SAFI_UNICAST
];
418 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
419 path
= bgp_dest_get_bgp_path_info(dest
);
421 path
= bgp_dump_route_node_record(afi
, dest
, path
, seq
);
426 fflush(bgp_dump_routes
.fp
);
431 static void bgp_dump_interval_func(struct thread
*t
)
433 struct bgp_dump
*bgp_dump
;
434 bgp_dump
= THREAD_ARG(t
);
436 /* Reschedule dump even if file couldn't be opened this time... */
437 if (bgp_dump_open_file(bgp_dump
) != NULL
) {
438 /* In case of bgp_dump_routes, we need special route dump
440 if (bgp_dump
->type
== BGP_DUMP_ROUTES
) {
441 unsigned int seq
= bgp_dump_routes_func(AFI_IP
, 1, 0);
442 bgp_dump_routes_func(AFI_IP6
, 0, seq
);
443 /* Close the file now. For a RIB dump there's no point
445 * it open until the next scheduled dump starts. */
446 fclose(bgp_dump
->fp
);
451 /* if interval is set reschedule */
452 if (bgp_dump
->interval
> 0)
453 bgp_dump_interval_add(bgp_dump
, bgp_dump
->interval
);
456 /* Dump common information. */
457 static void bgp_dump_common(struct stream
*obuf
, struct peer
*peer
,
460 char empty
[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
462 /* Source AS number and Destination AS number. */
463 if (forceas4
|| CHECK_FLAG(peer
->cap
, PEER_CAP_AS4_RCV
)) {
464 stream_putl(obuf
, peer
->as
);
465 stream_putl(obuf
, peer
->local_as
);
467 stream_putw(obuf
, peer
->as
);
468 stream_putw(obuf
, peer
->local_as
);
471 if (peer
->su
.sa
.sa_family
== AF_INET
) {
472 stream_putw(obuf
, peer
->ifp
? peer
->ifp
->ifindex
: 0);
473 stream_putw(obuf
, AFI_IP
);
475 stream_put(obuf
, &peer
->su
.sin
.sin_addr
, IPV4_MAX_BYTELEN
);
478 stream_put(obuf
, &peer
->su_local
->sin
.sin_addr
,
481 stream_put(obuf
, empty
, IPV4_MAX_BYTELEN
);
482 } else if (peer
->su
.sa
.sa_family
== AF_INET6
) {
483 /* Interface Index and Address family. */
484 stream_putw(obuf
, peer
->ifp
? peer
->ifp
->ifindex
: 0);
485 stream_putw(obuf
, AFI_IP6
);
487 /* Source IP Address and Destination IP Address. */
488 stream_put(obuf
, &peer
->su
.sin6
.sin6_addr
, IPV6_MAX_BYTELEN
);
491 stream_put(obuf
, &peer
->su_local
->sin6
.sin6_addr
,
494 stream_put(obuf
, empty
, IPV6_MAX_BYTELEN
);
498 /* Dump BGP status change. */
499 int bgp_dump_state(struct peer
*peer
)
503 /* If dump file pointer is disabled return immediately. */
504 if (bgp_dump_all
.fp
== NULL
)
507 /* Make dump stream. */
508 obuf
= bgp_dump_obuf
;
511 bgp_dump_header(obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_STATE_CHANGE_AS4
,
513 bgp_dump_common(obuf
, peer
, 1); /* force this in as4speak*/
515 stream_putw(obuf
, peer
->ostatus
);
516 stream_putw(obuf
, peer
->status
);
519 bgp_dump_set_size(obuf
, MSG_PROTOCOL_BGP4MP
);
521 /* Write to the stream. */
522 fwrite(STREAM_DATA(obuf
), stream_get_endp(obuf
), 1, bgp_dump_all
.fp
);
523 fflush(bgp_dump_all
.fp
);
527 static void bgp_dump_packet_func(struct bgp_dump
*bgp_dump
, struct peer
*peer
,
528 struct stream
*packet
)
531 bool addpath_capable
= false;
532 /* If dump file pointer is disabled return immediately. */
533 if (bgp_dump
->fp
== NULL
)
535 if (peer
->su
.sa
.sa_family
== AF_INET
) {
537 bgp_addpath_encode_rx(peer
, AFI_IP
, SAFI_UNICAST
);
538 } else if (peer
->su
.sa
.sa_family
== AF_INET6
) {
540 bgp_addpath_encode_rx(peer
, AFI_IP6
, SAFI_UNICAST
);
543 /* Make dump stream. */
544 obuf
= bgp_dump_obuf
;
547 /* Dump header and common part. */
548 if (CHECK_FLAG(peer
->cap
, PEER_CAP_AS4_RCV
) && addpath_capable
) {
549 bgp_dump_header(obuf
, MSG_PROTOCOL_BGP4MP
,
550 BGP4MP_MESSAGE_AS4_ADDPATH
, bgp_dump
->type
);
551 } else if (CHECK_FLAG(peer
->cap
, PEER_CAP_AS4_RCV
)) {
552 bgp_dump_header(obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_MESSAGE_AS4
,
554 } else if (addpath_capable
) {
555 bgp_dump_header(obuf
, MSG_PROTOCOL_BGP4MP
,
556 BGP4MP_MESSAGE_ADDPATH
, bgp_dump
->type
);
558 bgp_dump_header(obuf
, MSG_PROTOCOL_BGP4MP
, BGP4MP_MESSAGE
,
561 bgp_dump_common(obuf
, peer
, 0);
563 /* Packet contents. */
564 stream_put(obuf
, STREAM_DATA(packet
), stream_get_endp(packet
));
567 bgp_dump_set_size(obuf
, MSG_PROTOCOL_BGP4MP
);
569 /* Write to the stream. */
570 fwrite(STREAM_DATA(obuf
), stream_get_endp(obuf
), 1, bgp_dump
->fp
);
571 fflush(bgp_dump
->fp
);
574 /* Called from bgp_packet.c when BGP packet is received. */
575 static int bgp_dump_packet(struct peer
*peer
, uint8_t type
, bgp_size_t size
,
576 struct stream
*packet
)
579 bgp_dump_packet_func(&bgp_dump_all
, peer
, packet
);
581 /* bgp_dump_updates. */
582 if (type
== BGP_MSG_UPDATE
)
583 bgp_dump_packet_func(&bgp_dump_updates
, peer
, packet
);
587 static unsigned int bgp_dump_parse_time(const char *str
)
602 for (i
= 0; i
< len
; i
++) {
603 if (isdigit((unsigned char)str
[i
])) {
605 time
+= str
[i
] - '0';
606 } else if (str
[i
] == 'H' || str
[i
] == 'h') {
611 total
+= time
* 60 * 60;
614 } else if (str
[i
] == 'M' || str
[i
] == 'm') {
626 static int bgp_dump_set(struct vty
*vty
, struct bgp_dump
*bgp_dump
,
627 enum bgp_dump_type type
, const char *path
,
628 const char *interval_str
)
630 unsigned int interval
;
632 /* Don't schedule duplicate dumps if the dump command is given twice */
633 if (bgp_dump
->filename
&& strcmp(path
, bgp_dump
->filename
) == 0
634 && type
== bgp_dump
->type
) {
636 if (bgp_dump
->interval_str
637 && strcmp(bgp_dump
->interval_str
, interval_str
)
641 if (!bgp_dump
->interval_str
)
646 /* Removing previous config */
647 bgp_dump_unset(bgp_dump
);
650 /* Check interval string. */
651 interval
= bgp_dump_parse_time(interval_str
);
653 vty_out(vty
, "Malformed interval string\n");
654 return CMD_WARNING_CONFIG_FAILED
;
657 /* Setting interval string */
658 bgp_dump
->interval_str
=
659 XSTRDUP(MTYPE_BGP_DUMP_STR
, interval_str
);
665 bgp_dump
->type
= type
;
668 bgp_dump
->interval
= interval
;
671 bgp_dump
->filename
= XSTRDUP(MTYPE_BGP_DUMP_STR
, path
);
673 /* Create interval thread. */
674 bgp_dump_interval_add(bgp_dump
, interval
);
676 /* This should be called when interval is expired. */
677 bgp_dump_open_file(bgp_dump
);
682 static int bgp_dump_unset(struct bgp_dump
*bgp_dump
)
684 /* Removing file name. */
685 XFREE(MTYPE_BGP_DUMP_STR
, bgp_dump
->filename
);
689 fclose(bgp_dump
->fp
);
693 /* Removing interval event. */
694 THREAD_OFF(bgp_dump
->t_interval
);
696 bgp_dump
->interval
= 0;
698 /* Removing interval string. */
699 XFREE(MTYPE_BGP_DUMP_STR
, bgp_dump
->interval_str
);
706 "dump bgp <all|all-et|updates|updates-et|routes-mrt> PATH [INTERVAL]",
709 "Dump all BGP packets\nDump all BGP packets (Extended Timestamp Header)\n"
710 "Dump BGP updates only\nDump BGP updates only (Extended Timestamp Header)\n"
711 "Dump whole BGP routing table\n"
713 "Interval of output\n")
715 int idx_dump_routes
= 2;
717 int idx_interval
= 4;
718 int bgp_dump_type
= 0;
719 const char *interval
= NULL
;
720 struct bgp_dump
*bgp_dump_struct
= NULL
;
721 const struct bgp_dump_type_map
*map
= NULL
;
723 for (map
= bgp_dump_type_map
; map
->str
; map
++)
724 if (strmatch(argv
[idx_dump_routes
]->text
, map
->str
))
725 bgp_dump_type
= map
->type
;
727 switch (bgp_dump_type
) {
729 case BGP_DUMP_ALL_ET
:
730 bgp_dump_struct
= &bgp_dump_all
;
732 case BGP_DUMP_UPDATES
:
733 case BGP_DUMP_UPDATES_ET
:
734 bgp_dump_struct
= &bgp_dump_updates
;
736 case BGP_DUMP_ROUTES
:
738 bgp_dump_struct
= &bgp_dump_routes
;
742 /* When an interval is given */
743 if (argc
== idx_interval
+ 1)
744 interval
= argv
[idx_interval
]->arg
;
746 return bgp_dump_set(vty
, bgp_dump_struct
, bgp_dump_type
,
747 argv
[idx_path
]->arg
, interval
);
750 DEFUN (no_dump_bgp_all
,
752 "no dump bgp <all|all-et|updates|updates-et|routes-mrt> [PATH [INTERVAL]]",
755 "Stop BGP packet dump\n"
756 "Stop dump process all\n"
757 "Stop dump process all-et\n"
758 "Stop dump process updates\n"
759 "Stop dump process updates-et\n"
760 "Stop dump process route-mrt\n"
762 "Interval of output\n")
764 int idx_dump_routes
= 3;
765 int bgp_dump_type
= 0;
766 const struct bgp_dump_type_map
*map
= NULL
;
767 struct bgp_dump
*bgp_dump_struct
= NULL
;
769 for (map
= bgp_dump_type_map
; map
->str
; map
++)
770 if (strmatch(argv
[idx_dump_routes
]->text
, map
->str
))
771 bgp_dump_type
= map
->type
;
773 switch (bgp_dump_type
) {
775 case BGP_DUMP_ALL_ET
:
776 bgp_dump_struct
= &bgp_dump_all
;
778 case BGP_DUMP_UPDATES
:
779 case BGP_DUMP_UPDATES_ET
:
780 bgp_dump_struct
= &bgp_dump_updates
;
782 case BGP_DUMP_ROUTES
:
784 bgp_dump_struct
= &bgp_dump_routes
;
788 return bgp_dump_unset(bgp_dump_struct
);
791 static int config_write_bgp_dump(struct vty
*vty
);
792 /* BGP node structure. */
793 static struct cmd_node bgp_dump_node
= {
797 .config_write
= config_write_bgp_dump
,
800 static int config_write_bgp_dump(struct vty
*vty
)
802 if (bgp_dump_all
.filename
) {
803 const char *type_str
= "all";
804 if (bgp_dump_all
.type
== BGP_DUMP_ALL_ET
)
807 if (bgp_dump_all
.interval_str
)
808 vty_out(vty
, "dump bgp %s %s %s\n", type_str
,
809 bgp_dump_all
.filename
,
810 bgp_dump_all
.interval_str
);
812 vty_out(vty
, "dump bgp %s %s\n", type_str
,
813 bgp_dump_all
.filename
);
815 if (bgp_dump_updates
.filename
) {
816 const char *type_str
= "updates";
817 if (bgp_dump_updates
.type
== BGP_DUMP_UPDATES_ET
)
818 type_str
= "updates-et";
820 if (bgp_dump_updates
.interval_str
)
821 vty_out(vty
, "dump bgp %s %s %s\n", type_str
,
822 bgp_dump_updates
.filename
,
823 bgp_dump_updates
.interval_str
);
825 vty_out(vty
, "dump bgp %s %s\n", type_str
,
826 bgp_dump_updates
.filename
);
828 if (bgp_dump_routes
.filename
) {
829 if (bgp_dump_routes
.interval_str
)
830 vty_out(vty
, "dump bgp routes-mrt %s %s\n",
831 bgp_dump_routes
.filename
,
832 bgp_dump_routes
.interval_str
);
834 vty_out(vty
, "dump bgp routes-mrt %s\n",
835 bgp_dump_routes
.filename
);
840 /* Initialize BGP packet dump functionality. */
841 void bgp_dump_init(void)
843 memset(&bgp_dump_all
, 0, sizeof(bgp_dump_all
));
844 memset(&bgp_dump_updates
, 0, sizeof(bgp_dump_updates
));
845 memset(&bgp_dump_routes
, 0, sizeof(bgp_dump_routes
));
848 stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE
* 2)
849 + BGP_DUMP_MSG_HEADER
+ BGP_DUMP_HEADER_SIZE
);
851 install_node(&bgp_dump_node
);
853 install_element(CONFIG_NODE
, &dump_bgp_all_cmd
);
854 install_element(CONFIG_NODE
, &no_dump_bgp_all_cmd
);
856 hook_register(bgp_packet_dump
, bgp_dump_packet
);
857 hook_register(peer_status_changed
, bgp_dump_state
);
860 void bgp_dump_finish(void)
862 bgp_dump_unset(&bgp_dump_all
);
863 bgp_dump_unset(&bgp_dump_updates
);
864 bgp_dump_unset(&bgp_dump_routes
);
866 stream_free(bgp_dump_obuf
);
867 bgp_dump_obuf
= NULL
;
868 hook_unregister(bgp_packet_dump
, bgp_dump_packet
);
869 hook_unregister(peer_status_changed
, bgp_dump_state
);