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