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