Coverage Report

Created: 2026-01-25 06:17

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