]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_dump.c
*: manual SPDX License ID conversions
[mirror_frr.git] / bgpd / bgp_dump.c
CommitLineData
718e3744 1/* BGP-4 dump routine
896014f4
DL
2 * Copyright (C) 1999 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
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
9 * later version.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
718e3744 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"
0b2aa3a0 29#include "linklist.h"
3f9c7369 30#include "queue.h"
6e919709 31#include "memory.h"
039f3a34 32#include "filter.h"
3f9c7369 33
718e3744 34#include "bgpd/bgp_table.h"
718e3744 35#include "bgpd/bgpd.h"
36#include "bgpd/bgp_route.h"
37#include "bgpd/bgp_attr.h"
38#include "bgpd/bgp_dump.h"
7077f45c 39#include "bgpd/bgp_errors.h"
584470fb 40#include "bgpd/bgp_packet.h"
6b0655a2 41
d62a17ae 42enum bgp_dump_type {
43 BGP_DUMP_ALL,
44 BGP_DUMP_ALL_ET,
45 BGP_DUMP_UPDATES,
46 BGP_DUMP_UPDATES_ET,
47 BGP_DUMP_ROUTES
718e3744 48};
49
4db5d90a 50static const struct bgp_dump_type_map {
d62a17ae 51 enum bgp_dump_type type;
52 const char *str;
53} bgp_dump_type_map[] = {
54 {BGP_DUMP_ALL, "all"}, {BGP_DUMP_ALL_ET, "all-et"},
55 {BGP_DUMP_UPDATES, "updates"}, {BGP_DUMP_UPDATES_ET, "updates-et"},
56 {BGP_DUMP_ROUTES, "routes-mrt"}, {0, NULL},
57};
4db5d90a 58
718e3744 59enum MRT_MSG_TYPES {
d62a17ae 60 MSG_NULL,
61 MSG_START, /* sender is starting up */
62 MSG_DIE, /* receiver should shut down */
63 MSG_I_AM_DEAD, /* sender is shutting down */
64 MSG_PEER_DOWN, /* sender's peer is down */
65 MSG_PROTOCOL_BGP, /* msg is a BGP packet */
66 MSG_PROTOCOL_RIP, /* msg is a RIP packet */
67 MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */
68 MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */
69 MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */
70 MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
71 MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */
72 MSG_TABLE_DUMP, /* routing table dump */
73 MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */
718e3744 74};
75
d62a17ae 76struct bgp_dump {
77 enum bgp_dump_type type;
718e3744 78
d62a17ae 79 char *filename;
718e3744 80
d62a17ae 81 FILE *fp;
718e3744 82
d62a17ae 83 unsigned int interval;
718e3744 84
d62a17ae 85 char *interval_str;
718e3744 86
d62a17ae 87 struct thread *t_interval;
718e3744 88};
89
d62a17ae 90static int bgp_dump_unset(struct bgp_dump *bgp_dump);
cc9f21da 91static void bgp_dump_interval_func(struct thread *);
4db5d90a 92
718e3744 93/* BGP packet dump output buffer. */
94struct stream *bgp_dump_obuf;
95
96/* BGP dump strucuture for 'dump bgp all' */
97struct bgp_dump bgp_dump_all;
98
99/* BGP dump structure for 'dump bgp updates' */
100struct bgp_dump bgp_dump_updates;
101
102/* BGP dump structure for 'dump bgp routes' */
103struct bgp_dump bgp_dump_routes;
104
d62a17ae 105static FILE *bgp_dump_open_file(struct bgp_dump *bgp_dump)
718e3744 106{
d62a17ae 107 int ret;
108 time_t clock;
a2700b50 109 struct tm tm;
d62a17ae 110 char fullpath[MAXPATHLEN];
111 char realpath[MAXPATHLEN];
112 mode_t oldumask;
113
114 time(&clock);
a2700b50 115 localtime_r(&clock, &tm);
d62a17ae 116
117 if (bgp_dump->filename[0] != DIRECTORY_SEP) {
772270f3
QY
118 snprintf(fullpath, sizeof(fullpath), "%s/%s", vty_get_cwd(),
119 bgp_dump->filename);
c84e5187
DL
120#pragma GCC diagnostic push
121#pragma GCC diagnostic ignored "-Wformat-nonliteral"
122 /* user supplied date/time format string */
a2700b50 123 ret = strftime(realpath, MAXPATHLEN, fullpath, &tm);
d62a17ae 124 } else
a2700b50 125 ret = strftime(realpath, MAXPATHLEN, bgp_dump->filename, &tm);
c84e5187 126#pragma GCC diagnostic pop
d62a17ae 127
128 if (ret == 0) {
a10c2872 129 flog_warn(EC_BGP_DUMP, "%s: strftime error", __func__);
d62a17ae 130 return NULL;
131 }
718e3744 132
d62a17ae 133 if (bgp_dump->fp)
134 fclose(bgp_dump->fp);
718e3744 135
136
d62a17ae 137 oldumask = umask(0777 & ~LOGFILE_MASK);
138 bgp_dump->fp = fopen(realpath, "w");
718e3744 139
d62a17ae 140 if (bgp_dump->fp == NULL) {
a10c2872 141 flog_warn(EC_BGP_DUMP, "%s: %s: %s", __func__, realpath,
d62a17ae 142 strerror(errno));
143 umask(oldumask);
144 return NULL;
145 }
146 umask(oldumask);
718e3744 147
d62a17ae 148 return bgp_dump->fp;
718e3744 149}
150
d62a17ae 151static int bgp_dump_interval_add(struct bgp_dump *bgp_dump, int interval)
718e3744 152{
d62a17ae 153 int secs_into_day;
154 time_t t;
a2700b50 155 struct tm tm;
d62a17ae 156
157 if (interval > 0) {
158 /* Periodic dump every interval seconds */
159 if ((interval < 86400) && ((86400 % interval) == 0)) {
160 /* Dump at predictable times: if a day has a whole
161 * number of
162 * intervals, dump every interval seconds starting from
163 * midnight
164 */
165 (void)time(&t);
a2700b50
MS
166 localtime_r(&t, &tm);
167 secs_into_day = tm.tm_sec + 60 * tm.tm_min
168 + 60 * 60 * tm.tm_hour;
d62a17ae 169 interval = interval
170 - secs_into_day % interval; /* always > 0 */
171 }
d62a17ae 172 thread_add_timer(bm->master, bgp_dump_interval_func, bgp_dump,
173 interval, &bgp_dump->t_interval);
174 } else {
175 /* One-off dump: execute immediately, don't affect any scheduled
176 * dumps */
d62a17ae 177 thread_add_event(bm->master, bgp_dump_interval_func, bgp_dump,
178 0, &bgp_dump->t_interval);
9834cd0f 179 }
fba3d22b 180
d62a17ae 181 return 0;
718e3744 182}
183
184/* Dump common header. */
d62a17ae 185static void bgp_dump_header(struct stream *obuf, int type, int subtype,
186 int dump_type)
718e3744 187{
d62a17ae 188 struct timeval clock;
189 long msecs;
190 time_t secs;
4db5d90a 191
d62a17ae 192 if ((dump_type == BGP_DUMP_ALL_ET || dump_type == BGP_DUMP_UPDATES_ET)
193 && type == MSG_PROTOCOL_BGP4MP)
194 type = MSG_PROTOCOL_BGP4MP_ET;
4db5d90a 195
d62a17ae 196 gettimeofday(&clock, NULL);
718e3744 197
d62a17ae 198 secs = clock.tv_sec;
199 msecs = clock.tv_usec;
718e3744 200
d62a17ae 201 /* Put dump packet header. */
202 stream_putl(obuf, secs);
203 stream_putw(obuf, type);
204 stream_putw(obuf, subtype);
205 stream_putl(obuf, 0); /* len */
4db5d90a 206
d62a17ae 207 /* Adding microseconds for the MRT Extended Header */
208 if (type == MSG_PROTOCOL_BGP4MP_ET)
209 stream_putl(obuf, msecs);
718e3744 210}
211
d62a17ae 212static void bgp_dump_set_size(struct stream *s, int type)
718e3744 213{
d62a17ae 214 /*
215 * The BGP_DUMP_HEADER_SIZE stay at 12 event when ET:
216 * "The Microsecond Timestamp is included in the computation
217 * of the Length field value." (RFC6396 2011)
218 */
219 stream_putl_at(s, 8, stream_get_endp(s) - BGP_DUMP_HEADER_SIZE);
718e3744 220}
221
d62a17ae 222static void bgp_dump_routes_index_table(struct bgp *bgp)
718e3744 223{
d62a17ae 224 struct peer *peer;
225 struct listnode *node;
226 uint16_t peerno = 1;
227 struct stream *obuf;
228
229 obuf = bgp_dump_obuf;
230 stream_reset(obuf);
231
232 /* MRT header */
233 bgp_dump_header(obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE,
234 BGP_DUMP_ROUTES);
235
236 /* Collector BGP ID */
237 stream_put_in_addr(obuf, &bgp->router_id);
238
239 /* View name */
8c1a4c10
DS
240 if (bgp->name_pretty) {
241 stream_putw(obuf, strlen(bgp->name_pretty));
242 stream_put(obuf, bgp->name_pretty, strlen(bgp->name_pretty));
d62a17ae 243 } else {
244 stream_putw(obuf, 0);
245 }
718e3744 246
d62a17ae 247 /* Peer count ( plus one extra internal peer ) */
248 stream_putw(obuf, listcount(bgp->peer) + 1);
249
250 /* Populate fake peer at index 0, for locally originated routes */
251 /* Peer type (IPv4) */
9d303b37
DL
252 stream_putc(obuf,
253 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
254 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
d62a17ae 255 /* Peer BGP ID (0.0.0.0) */
256 stream_putl(obuf, 0);
257 /* Peer IP address (0.0.0.0) */
258 stream_putl(obuf, 0);
259 /* Peer ASN (0) */
260 stream_putl(obuf, 0);
261
262 /* Walk down all peers */
263 for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
264
265 /* Peer's type */
266 if (sockunion_family(&peer->su) == AF_INET) {
267 stream_putc(
268 obuf,
269 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
270 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
271 } else if (sockunion_family(&peer->su) == AF_INET6) {
272 stream_putc(
273 obuf,
274 TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
275 + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
276 }
277
278 /* Peer's BGP ID */
279 stream_put_in_addr(obuf, &peer->remote_id);
280
281 /* Peer's IP address */
282 if (sockunion_family(&peer->su) == AF_INET) {
283 stream_put_in_addr(obuf, &peer->su.sin.sin_addr);
284 } else if (sockunion_family(&peer->su) == AF_INET6) {
d7c0a89a 285 stream_write(obuf, (uint8_t *)&peer->su.sin6.sin6_addr,
d62a17ae 286 IPV6_MAX_BYTELEN);
287 }
288
289 /* Peer's AS number. */
290 /* Note that, as this is an AS4 compliant quagga, the RIB is
291 * always AS4 */
292 stream_putl(obuf, peer->as);
293
294 /* Store the peer number for this peer */
295 peer->table_dump_index = peerno;
296 peerno++;
297 }
718e3744 298
d62a17ae 299 bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
718e3744 300
d62a17ae 301 fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_routes.fp);
302 fflush(bgp_dump_routes.fp);
718e3744 303}
304
4b7e6066 305static struct bgp_path_info *
9bcb3eef 306bgp_dump_route_node_record(int afi, struct bgp_dest *dest,
40381db7 307 struct bgp_path_info *path, unsigned int seq)
246556b5 308{
d62a17ae 309 struct stream *obuf;
310 size_t sizep;
311 size_t endp;
be92fc9f 312 bool addpath_capable;
9bcb3eef 313 const struct prefix *p = bgp_dest_get_prefix(dest);
d62a17ae 314
315 obuf = bgp_dump_obuf;
316 stream_reset(obuf);
317
be92fc9f 318 addpath_capable = bgp_addpath_encode_rx(path->peer, afi, SAFI_UNICAST);
1073f44d 319
d62a17ae 320 /* MRT header */
be92fc9f 321 if (afi == AFI_IP && addpath_capable)
1073f44d
DT
322 bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
323 TABLE_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH,
324 BGP_DUMP_ROUTES);
325 else if (afi == AFI_IP)
d62a17ae 326 bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
327 TABLE_DUMP_V2_RIB_IPV4_UNICAST,
328 BGP_DUMP_ROUTES);
be92fc9f 329 else if (afi == AFI_IP6 && addpath_capable)
1073f44d
DT
330 bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
331 TABLE_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH,
332 BGP_DUMP_ROUTES);
d62a17ae 333 else if (afi == AFI_IP6)
334 bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
335 TABLE_DUMP_V2_RIB_IPV6_UNICAST,
336 BGP_DUMP_ROUTES);
337
338 /* Sequence number */
339 stream_putl(obuf, seq);
340
341 /* Prefix length */
b54892e0 342 stream_putc(obuf, p->prefixlen);
d62a17ae 343
344 /* Prefix */
345 if (afi == AFI_IP) {
346 /* We'll dump only the useful bits (those not 0), but have to
347 * align on 8 bits */
b54892e0
DS
348 stream_write(obuf, (uint8_t *)&p->u.prefix4,
349 (p->prefixlen + 7) / 8);
d62a17ae 350 } else if (afi == AFI_IP6) {
351 /* We'll dump only the useful bits (those not 0), but have to
352 * align on 8 bits */
b54892e0
DS
353 stream_write(obuf, (uint8_t *)&p->u.prefix6,
354 (p->prefixlen + 7) / 8);
d62a17ae 355 }
246556b5 356
d62a17ae 357 /* Save where we are now, so we can overwride the entry count later */
358 sizep = stream_get_endp(obuf);
246556b5 359
d62a17ae 360 /* Entry count */
361 uint16_t entry_count = 0;
246556b5 362
d62a17ae 363 /* Entry count, note that this is overwritten later */
364 stream_putw(obuf, 0);
246556b5 365
d62a17ae 366 endp = stream_get_endp(obuf);
40381db7 367 for (; path; path = path->next) {
d62a17ae 368 size_t cur_endp;
246556b5 369
d62a17ae 370 /* Peer index */
40381db7 371 stream_putw(obuf, path->peer->table_dump_index);
246556b5 372
d62a17ae 373 /* Originated */
083ec940 374 stream_putl(obuf, time(NULL) - (monotime(NULL) - path->uptime));
246556b5 375
1073f44d 376 /*Path Identifier*/
be92fc9f 377 if (addpath_capable) {
1073f44d
DT
378 stream_putl(obuf, path->addpath_rx_id);
379 }
380
d62a17ae 381 /* Dump attribute. */
382 /* Skip prefix & AFI/SAFI for MP_NLRI */
97a52c82 383 bgp_dump_routes_attr(obuf, path, p);
246556b5 384
d62a17ae 385 cur_endp = stream_get_endp(obuf);
556beacf
QY
386 if (cur_endp > BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE
387 + BGP_DUMP_MSG_HEADER
d62a17ae 388 + BGP_DUMP_HEADER_SIZE) {
389 stream_set_endp(obuf, endp);
390 break;
391 }
246556b5 392
d62a17ae 393 entry_count++;
394 endp = cur_endp;
395 }
246556b5 396
d62a17ae 397 /* Overwrite the entry count, now that we know the right number */
398 stream_putw_at(obuf, sizep, entry_count);
246556b5 399
d62a17ae 400 bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
401 fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_routes.fp);
246556b5 402
40381db7 403 return path;
246556b5
EU
404}
405
406
718e3744 407/* Runs under child process. */
d62a17ae 408static unsigned int bgp_dump_routes_func(int afi, int first_run,
409 unsigned int seq)
718e3744 410{
40381db7 411 struct bgp_path_info *path;
9bcb3eef 412 struct bgp_dest *dest;
d62a17ae 413 struct bgp *bgp;
414 struct bgp_table *table;
415
416 bgp = bgp_get_default();
417 if (!bgp)
418 return seq;
419
420 if (bgp_dump_routes.fp == NULL)
421 return seq;
422
423 /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
424 so this should only be done on the first call to
425 bgp_dump_routes_func.
426 ( this function will be called once for ipv4 and once for ipv6 ) */
427 if (first_run)
428 bgp_dump_routes_index_table(bgp);
429
430 /* Walk down each BGP route. */
431 table = bgp->rib[afi][SAFI_UNICAST];
432
9bcb3eef
DS
433 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
434 path = bgp_dest_get_bgp_path_info(dest);
40381db7 435 while (path) {
9bcb3eef 436 path = bgp_dump_route_node_record(afi, dest, path, seq);
d62a17ae 437 seq++;
438 }
439 }
0b2aa3a0 440
d62a17ae 441 fflush(bgp_dump_routes.fp);
0b2aa3a0 442
d62a17ae 443 return seq;
718e3744 444}
445
cc9f21da 446static void bgp_dump_interval_func(struct thread *t)
718e3744 447{
d62a17ae 448 struct bgp_dump *bgp_dump;
449 bgp_dump = THREAD_ARG(t);
d62a17ae 450
451 /* Reschedule dump even if file couldn't be opened this time... */
452 if (bgp_dump_open_file(bgp_dump) != NULL) {
453 /* In case of bgp_dump_routes, we need special route dump
454 * function. */
455 if (bgp_dump->type == BGP_DUMP_ROUTES) {
456 unsigned int seq = bgp_dump_routes_func(AFI_IP, 1, 0);
457 bgp_dump_routes_func(AFI_IP6, 0, seq);
458 /* Close the file now. For a RIB dump there's no point
459 * in leaving
460 * it open until the next scheduled dump starts. */
461 fclose(bgp_dump->fp);
462 bgp_dump->fp = NULL;
463 }
9834cd0f 464 }
718e3744 465
d62a17ae 466 /* if interval is set reschedule */
467 if (bgp_dump->interval > 0)
468 bgp_dump_interval_add(bgp_dump, bgp_dump->interval);
718e3744 469}
470
471/* Dump common information. */
d62a17ae 472static void bgp_dump_common(struct stream *obuf, struct peer *peer,
473 int forceas4)
718e3744 474{
d62a17ae 475 char empty[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
476
477 /* Source AS number and Destination AS number. */
478 if (forceas4 || CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) {
479 stream_putl(obuf, peer->as);
480 stream_putl(obuf, peer->local_as);
481 } else {
482 stream_putw(obuf, peer->as);
483 stream_putw(obuf, peer->local_as);
484 }
718e3744 485
d62a17ae 486 if (peer->su.sa.sa_family == AF_INET) {
194a4f2c 487 stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
d62a17ae 488 stream_putw(obuf, AFI_IP);
489
490 stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
491
492 if (peer->su_local)
493 stream_put(obuf, &peer->su_local->sin.sin_addr,
494 IPV4_MAX_BYTELEN);
495 else
496 stream_put(obuf, empty, IPV4_MAX_BYTELEN);
497 } else if (peer->su.sa.sa_family == AF_INET6) {
498 /* Interface Index and Address family. */
194a4f2c 499 stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
d62a17ae 500 stream_putw(obuf, AFI_IP6);
501
502 /* Source IP Address and Destination IP Address. */
503 stream_put(obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
504
505 if (peer->su_local)
506 stream_put(obuf, &peer->su_local->sin6.sin6_addr,
507 IPV6_MAX_BYTELEN);
508 else
509 stream_put(obuf, empty, IPV6_MAX_BYTELEN);
510 }
718e3744 511}
512
513/* Dump BGP status change. */
7d8d0eab 514int bgp_dump_state(struct peer *peer)
718e3744 515{
d62a17ae 516 struct stream *obuf;
718e3744 517
d62a17ae 518 /* If dump file pointer is disabled return immediately. */
519 if (bgp_dump_all.fp == NULL)
7d8d0eab 520 return 0;
718e3744 521
d62a17ae 522 /* Make dump stream. */
523 obuf = bgp_dump_obuf;
524 stream_reset(obuf);
718e3744 525
d62a17ae 526 bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4,
527 bgp_dump_all.type);
528 bgp_dump_common(obuf, peer, 1); /* force this in as4speak*/
718e3744 529
7d8d0eab
MKS
530 stream_putw(obuf, peer->ostatus);
531 stream_putw(obuf, peer->status);
718e3744 532
d62a17ae 533 /* Set length. */
534 bgp_dump_set_size(obuf, MSG_PROTOCOL_BGP4MP);
718e3744 535
d62a17ae 536 /* Write to the stream. */
537 fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_all.fp);
538 fflush(bgp_dump_all.fp);
7d8d0eab 539 return 0;
718e3744 540}
541
d62a17ae 542static void bgp_dump_packet_func(struct bgp_dump *bgp_dump, struct peer *peer,
543 struct stream *packet)
718e3744 544{
d62a17ae 545 struct stream *obuf;
be92fc9f 546 bool addpath_capable = false;
d62a17ae 547 /* If dump file pointer is disabled return immediately. */
548 if (bgp_dump->fp == NULL)
549 return;
1073f44d 550 if (peer->su.sa.sa_family == AF_INET) {
be92fc9f 551 addpath_capable =
1073f44d
DT
552 bgp_addpath_encode_rx(peer, AFI_IP, SAFI_UNICAST);
553 } else if (peer->su.sa.sa_family == AF_INET6) {
be92fc9f 554 addpath_capable =
1073f44d
DT
555 bgp_addpath_encode_rx(peer, AFI_IP6, SAFI_UNICAST);
556 }
d62a17ae 557
558 /* Make dump stream. */
559 obuf = bgp_dump_obuf;
560 stream_reset(obuf);
561
562 /* Dump header and common part. */
be92fc9f 563 if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && addpath_capable) {
1073f44d
DT
564 bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP,
565 BGP4MP_MESSAGE_AS4_ADDPATH, bgp_dump->type);
566 } else if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) {
d62a17ae 567 bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4,
568 bgp_dump->type);
be92fc9f 569 } else if (addpath_capable) {
1073f44d
DT
570 bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP,
571 BGP4MP_MESSAGE_ADDPATH, bgp_dump->type);
d62a17ae 572 } else {
573 bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
574 bgp_dump->type);
575 }
576 bgp_dump_common(obuf, peer, 0);
718e3744 577
d62a17ae 578 /* Packet contents. */
579 stream_put(obuf, STREAM_DATA(packet), stream_get_endp(packet));
718e3744 580
d62a17ae 581 /* Set length. */
582 bgp_dump_set_size(obuf, MSG_PROTOCOL_BGP4MP);
718e3744 583
d62a17ae 584 /* Write to the stream. */
585 fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump->fp);
586 fflush(bgp_dump->fp);
718e3744 587}
588
589/* Called from bgp_packet.c when BGP packet is received. */
584470fb
DL
590static int bgp_dump_packet(struct peer *peer, uint8_t type, bgp_size_t size,
591 struct stream *packet)
718e3744 592{
d62a17ae 593 /* bgp_dump_all. */
594 bgp_dump_packet_func(&bgp_dump_all, peer, packet);
718e3744 595
d62a17ae 596 /* bgp_dump_updates. */
597 if (type == BGP_MSG_UPDATE)
598 bgp_dump_packet_func(&bgp_dump_updates, peer, packet);
584470fb 599 return 0;
718e3744 600}
6b0655a2 601
d62a17ae 602static unsigned int bgp_dump_parse_time(const char *str)
718e3744 603{
d62a17ae 604 int i;
605 int len;
606 int seen_h;
607 int seen_m;
608 int time;
609 unsigned int total;
610
611 time = 0;
612 total = 0;
613 seen_h = 0;
614 seen_m = 0;
615 len = strlen(str);
616
617 for (i = 0; i < len; i++) {
fefa5e0f 618 if (isdigit((unsigned char)str[i])) {
d62a17ae 619 time *= 10;
620 time += str[i] - '0';
621 } else if (str[i] == 'H' || str[i] == 'h') {
622 if (seen_h)
623 return 0;
624 if (seen_m)
625 return 0;
626 total += time * 60 * 60;
627 time = 0;
628 seen_h = 1;
629 } else if (str[i] == 'M' || str[i] == 'm') {
630 if (seen_m)
631 return 0;
632 total += time * 60;
633 time = 0;
634 seen_m = 1;
635 } else
636 return 0;
718e3744 637 }
d62a17ae 638 return total + time;
718e3744 639}
640
d62a17ae 641static int 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)
718e3744 644{
d62a17ae 645 unsigned int interval;
646
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) {
650 if (interval_str) {
651 if (bgp_dump->interval_str
652 && strcmp(bgp_dump->interval_str, interval_str)
653 == 0)
654 return CMD_SUCCESS;
655 } else {
656 if (!bgp_dump->interval_str)
657 return CMD_SUCCESS;
658 }
718e3744 659 }
45ad592e 660
d62a17ae 661 /* Removing previous config */
662 bgp_dump_unset(bgp_dump);
663
664 if (interval_str) {
665 /* Check interval string. */
666 interval = bgp_dump_parse_time(interval_str);
667 if (interval == 0) {
668 vty_out(vty, "Malformed interval string\n");
669 return CMD_WARNING_CONFIG_FAILED;
670 }
671
672 /* Setting interval string */
673 bgp_dump->interval_str =
674 XSTRDUP(MTYPE_BGP_DUMP_STR, interval_str);
675 } else {
676 interval = 0;
677 }
718e3744 678
d62a17ae 679 /* Set type. */
680 bgp_dump->type = type;
718e3744 681
d62a17ae 682 /* Set interval */
683 bgp_dump->interval = interval;
4db5d90a 684
d62a17ae 685 /* Set file name. */
686 bgp_dump->filename = XSTRDUP(MTYPE_BGP_DUMP_STR, path);
4db5d90a 687
d62a17ae 688 /* Create interval thread. */
689 bgp_dump_interval_add(bgp_dump, interval);
718e3744 690
d62a17ae 691 /* This should be called when interval is expired. */
692 bgp_dump_open_file(bgp_dump);
718e3744 693
d62a17ae 694 return CMD_SUCCESS;
718e3744 695}
696
d62a17ae 697static int bgp_dump_unset(struct bgp_dump *bgp_dump)
718e3744 698{
d62a17ae 699 /* Removing file name. */
e1b36e13 700 XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename);
718e3744 701
d62a17ae 702 /* Closing file. */
703 if (bgp_dump->fp) {
704 fclose(bgp_dump->fp);
705 bgp_dump->fp = NULL;
706 }
718e3744 707
b3d6bc6e 708 /* Removing interval event. */
c3aaa89a 709 THREAD_OFF(bgp_dump->t_interval);
718e3744 710
d62a17ae 711 bgp_dump->interval = 0;
718e3744 712
d62a17ae 713 /* Removing interval string. */
e1b36e13 714 XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str);
d62a17ae 715
716 return CMD_SUCCESS;
718e3744 717}
718
719DEFUN (dump_bgp_all,
720 dump_bgp_all_cmd,
6147e2c6 721 "dump bgp <all|all-et|updates|updates-et|routes-mrt> PATH [INTERVAL]",
718e3744 722 "Dump packet\n"
723 "BGP packet dump\n"
cbac2b1b
EU
724 "Dump all BGP packets\nDump all BGP packets (Extended Timestamp Header)\n"
725 "Dump BGP updates only\nDump BGP updates only (Extended Timestamp Header)\n"
4db5d90a 726 "Dump whole BGP routing table\n"
718e3744 727 "Output filename\n"
728 "Interval of output\n")
729{
d62a17ae 730 int idx_dump_routes = 2;
731 int idx_path = 3;
732 int idx_interval = 4;
733 int bgp_dump_type = 0;
734 const char *interval = NULL;
735 struct bgp_dump *bgp_dump_struct = NULL;
736 const struct bgp_dump_type_map *map = NULL;
737
738 for (map = bgp_dump_type_map; map->str; map++)
739 if (strmatch(argv[idx_dump_routes]->text, map->str))
740 bgp_dump_type = map->type;
741
742 switch (bgp_dump_type) {
743 case BGP_DUMP_ALL:
744 case BGP_DUMP_ALL_ET:
745 bgp_dump_struct = &bgp_dump_all;
746 break;
747 case BGP_DUMP_UPDATES:
748 case BGP_DUMP_UPDATES_ET:
749 bgp_dump_struct = &bgp_dump_updates;
750 break;
751 case BGP_DUMP_ROUTES:
752 default:
753 bgp_dump_struct = &bgp_dump_routes;
754 break;
755 }
718e3744 756
d62a17ae 757 /* When an interval is given */
758 if (argc == idx_interval + 1)
759 interval = argv[idx_interval]->arg;
718e3744 760
d62a17ae 761 return bgp_dump_set(vty, bgp_dump_struct, bgp_dump_type,
762 argv[idx_path]->arg, interval);
718e3744 763}
764
4db5d90a
AF
765DEFUN (no_dump_bgp_all,
766 no_dump_bgp_all_cmd,
37bc45eb 767 "no dump bgp <all|all-et|updates|updates-et|routes-mrt> [PATH [INTERVAL]]",
718e3744 768 NO_STR
4db5d90a
AF
769 "Stop dump packet\n"
770 "Stop BGP packet dump\n"
ca492402
DS
771 "Stop dump process all\n"
772 "Stop dump process all-et\n"
773 "Stop dump process updates\n"
774 "Stop dump process updates-et\n"
3a2d747c
QY
775 "Stop dump process route-mrt\n"
776 "Output filename\n"
777 "Interval of output\n")
718e3744 778{
d62a17ae 779 int idx_dump_routes = 3;
780 int bgp_dump_type = 0;
781 const struct bgp_dump_type_map *map = NULL;
782 struct bgp_dump *bgp_dump_struct = NULL;
783
784 for (map = bgp_dump_type_map; map->str; map++)
785 if (strmatch(argv[idx_dump_routes]->text, map->str))
786 bgp_dump_type = map->type;
787
788 switch (bgp_dump_type) {
789 case BGP_DUMP_ALL:
790 case BGP_DUMP_ALL_ET:
791 bgp_dump_struct = &bgp_dump_all;
792 break;
793 case BGP_DUMP_UPDATES:
794 case BGP_DUMP_UPDATES_ET:
795 bgp_dump_struct = &bgp_dump_updates;
796 break;
797 case BGP_DUMP_ROUTES:
798 default:
799 bgp_dump_struct = &bgp_dump_routes;
800 break;
801 }
ca492402 802
d62a17ae 803 return bgp_dump_unset(bgp_dump_struct);
718e3744 804}
805
612c2c15 806static int config_write_bgp_dump(struct vty *vty);
718e3744 807/* BGP node structure. */
62b346ee 808static struct cmd_node bgp_dump_node = {
f4b8291f 809 .name = "dump",
62b346ee
DL
810 .node = DUMP_NODE,
811 .prompt = "",
612c2c15 812 .config_write = config_write_bgp_dump,
62b346ee 813};
718e3744 814
d62a17ae 815static int config_write_bgp_dump(struct vty *vty)
718e3744 816{
d62a17ae 817 if (bgp_dump_all.filename) {
818 const char *type_str = "all";
819 if (bgp_dump_all.type == BGP_DUMP_ALL_ET)
820 type_str = "all-et";
821
822 if (bgp_dump_all.interval_str)
823 vty_out(vty, "dump bgp %s %s %s\n", type_str,
824 bgp_dump_all.filename,
825 bgp_dump_all.interval_str);
826 else
827 vty_out(vty, "dump bgp %s %s\n", type_str,
828 bgp_dump_all.filename);
829 }
830 if (bgp_dump_updates.filename) {
831 const char *type_str = "updates";
832 if (bgp_dump_updates.type == BGP_DUMP_UPDATES_ET)
833 type_str = "updates-et";
834
835 if (bgp_dump_updates.interval_str)
836 vty_out(vty, "dump bgp %s %s %s\n", type_str,
837 bgp_dump_updates.filename,
838 bgp_dump_updates.interval_str);
839 else
840 vty_out(vty, "dump bgp %s %s\n", type_str,
841 bgp_dump_updates.filename);
842 }
843 if (bgp_dump_routes.filename) {
844 if (bgp_dump_routes.interval_str)
845 vty_out(vty, "dump bgp routes-mrt %s %s\n",
846 bgp_dump_routes.filename,
847 bgp_dump_routes.interval_str);
848 else
849 vty_out(vty, "dump bgp routes-mrt %s\n",
850 bgp_dump_routes.filename);
851 }
852 return 0;
718e3744 853}
6b0655a2 854
718e3744 855/* Initialize BGP packet dump functionality. */
d62a17ae 856void bgp_dump_init(void)
718e3744 857{
6006b807
DA
858 memset(&bgp_dump_all, 0, sizeof(bgp_dump_all));
859 memset(&bgp_dump_updates, 0, sizeof(bgp_dump_updates));
860 memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes));
718e3744 861
d62a17ae 862 bgp_dump_obuf =
556beacf
QY
863 stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE * 2)
864 + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
718e3744 865
612c2c15 866 install_node(&bgp_dump_node);
718e3744 867
d62a17ae 868 install_element(CONFIG_NODE, &dump_bgp_all_cmd);
869 install_element(CONFIG_NODE, &no_dump_bgp_all_cmd);
584470fb
DL
870
871 hook_register(bgp_packet_dump, bgp_dump_packet);
7d8d0eab 872 hook_register(peer_status_changed, bgp_dump_state);
718e3744 873}
228da428 874
d62a17ae 875void bgp_dump_finish(void)
228da428 876{
d62a17ae 877 bgp_dump_unset(&bgp_dump_all);
878 bgp_dump_unset(&bgp_dump_updates);
879 bgp_dump_unset(&bgp_dump_routes);
b8438f6d 880
d62a17ae 881 stream_free(bgp_dump_obuf);
882 bgp_dump_obuf = NULL;
376d7c50 883 hook_unregister(bgp_packet_dump, bgp_dump_packet);
7d8d0eab 884 hook_unregister(peer_status_changed, bgp_dump_state);
228da428 885}