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