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