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