1/* BGP-4 dump routine 2 Copyright (C) 1999 Kunihiro Ishiguro 3 4This file is part of GNU Zebra. 5 6GNU Zebra is free software; you can redistribute it and/or modify it 7under the terms of the GNU General Public License as published by the 8Free Software Foundation; either version 2, or (at your option) any 9later version. 10 11GNU Zebra is distributed in the hope that it will be useful, but 12WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with GNU Zebra; see the file COPYING. If not, write to the Free 18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 1902111-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" 29#include "bgpd/bgp_table.h" 30 31#include "bgpd/bgpd.h" 32#include "bgpd/bgp_route.h" 33#include "bgpd/bgp_attr.h" 34#include "bgpd/bgp_dump.h" 35 36enum bgp_dump_type 37{ 38 BGP_DUMP_ALL, 39 BGP_DUMP_UPDATES, 40 BGP_DUMP_ROUTES 41}; 42 43enum MRT_MSG_TYPES { 44 MSG_NULL, 45 MSG_START, /* sender is starting up */ 46 MSG_DIE, /* receiver should shut down */ 47 MSG_I_AM_DEAD, /* sender is shutting down */ 48 MSG_PEER_DOWN, /* sender's peer is down */ 49 MSG_PROTOCOL_BGP, /* msg is a BGP packet */ 50 MSG_PROTOCOL_RIP, /* msg is a RIP packet */ 51 MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */ 52 MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */ 53 MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */ 54 MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */ 55 MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */ 56 MSG_TABLE_DUMP /* routing table dump */ 57}; 58 59struct bgp_dump 60{ 61 enum bgp_dump_type type; 62 63 char *filename; 64 65 FILE *fp; 66 67 unsigned int interval; 68 69 char *interval_str; 70 71 struct thread *t_interval; 72}; 73 74/* BGP packet dump output buffer. */ 75struct stream *bgp_dump_obuf; 76 77/* BGP dump strucuture for 'dump bgp all' */ 78struct bgp_dump bgp_dump_all; 79 80/* BGP dump structure for 'dump bgp updates' */ 81struct bgp_dump bgp_dump_updates; 82 83/* BGP dump structure for 'dump bgp routes' */ 84struct bgp_dump bgp_dump_routes; 85 86/* Dump whole BGP table is very heavy process. */ 87struct thread *t_bgp_dump_routes; 88 89/* Some define for BGP packet dump. */ 90FILE * 91bgp_dump_open_file (struct bgp_dump *bgp_dump) 92{ 93 int ret; 94 time_t clock; 95 struct tm *tm; 96 char fullpath[MAXPATHLEN]; 97 char realpath[MAXPATHLEN]; 98 99 time (&clock); 100 tm = localtime (&clock); 101 102 if (bgp_dump->filename[0] != DIRECTORY_SEP) 103 { 104 sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename); 105 ret = strftime (realpath, MAXPATHLEN, fullpath, tm); 106 } 107 else 108 ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm); 109 110 if (ret == 0) 111 { 112 zlog_warn ("bgp_dump_open_file: strftime error"); 113 return NULL; 114 } 115 116 if (bgp_dump->fp) 117 fclose (bgp_dump->fp); 118 119 120 bgp_dump->fp = fopen (realpath, "w"); 121 122 if (bgp_dump->fp == NULL) 123 return NULL; 124 125 return bgp_dump->fp; 126} 127 128int 129bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval) 130{ 131 int bgp_dump_interval_func (struct thread *); 132 133 bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func, 134 bgp_dump, interval); 135 return 0; 136} 137 138/* Dump common header. */ 139void 140bgp_dump_header (struct stream *obuf, int type, int subtype) 141{ 142 time_t now; 143 144 /* Set header. */ 145 time (&now); 146 147 /* Put dump packet header. */ 148 stream_putl (obuf, now); 149 stream_putw (obuf, type); 150 stream_putw (obuf, subtype); 151 152 stream_putl (obuf, 0); /* len */ 153} 154 155void 156bgp_dump_set_size (struct stream *s, int type) 157{ 158 stream_putl_at (s, 8, stream_get_putp (s) - BGP_DUMP_HEADER_SIZE); 159} 160 161void 162bgp_dump_routes_entry (struct prefix *p, struct bgp_info *info, int afi, 163 int type, unsigned int seq) 164{ 165 struct stream *obuf; 166 struct attr *attr; 167 struct peer *peer; 168 int plen; 169 int safi = 0; 170 171 /* Make dump stream. */ 172 obuf = bgp_dump_obuf; 173 stream_reset (obuf); 174 175 attr = info->attr; 176 peer = info->peer; 177 178 /* We support MRT's old format. */ 179 if (type == MSG_TABLE_DUMP) 180 { 181 bgp_dump_header (obuf, MSG_TABLE_DUMP, afi); 182 stream_putw (obuf, 0); /* View # */ 183 stream_putw (obuf, seq); /* Sequence number. */ 184 } 185 else 186 { 187 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY); 188 189 stream_putl (obuf, info->uptime); /* Time Last Change */ 190 stream_putw (obuf, afi); /* Address Family */ 191 stream_putc (obuf, safi); /* SAFI */ 192 } 193 194 if (afi == AFI_IP) 195 { 196 if (type == MSG_TABLE_DUMP) 197 { 198 /* Prefix */ 199 stream_put_in_addr (obuf, &p->u.prefix4); 200 stream_putc (obuf, p->prefixlen); 201 202 /* Status */ 203 stream_putc (obuf, 1); 204 205 /* Originated */ 206 stream_putl (obuf, info->uptime); 207 208 /* Peer's IP address */ 209 stream_put_in_addr (obuf, &peer->su.sin.sin_addr); 210 211 /* Peer's AS number. */ 212 stream_putw (obuf, peer->as); 213 214 /* Dump attribute. */ 215 bgp_dump_routes_attr (obuf, attr); 216 } 217 else 218 { 219 /* Next-Hop-Len */ 220 stream_putc (obuf, IPV4_MAX_BYTELEN); 221 stream_put_in_addr (obuf, &attr->nexthop); 222 stream_putc (obuf, p->prefixlen); 223 plen = PSIZE (p->prefixlen); 224 stream_put (obuf, &p->u.prefix4, plen); 225 bgp_dump_routes_attr (obuf, attr); 226 } 227 } 228#ifdef HAVE_IPV6 229 else if (afi == AFI_IP6) 230 { 231 if (type == MSG_TABLE_DUMP) 232 { 233 /* Prefix */ 234 stream_write (obuf, (u_char *)&p->u.prefix6, IPV6_MAX_BYTELEN); 235 stream_putc (obuf, p->prefixlen); 236 237 /* Status */ 238 stream_putc (obuf, 1); 239 240 /* Originated */ 241 stream_putl (obuf, info->uptime); 242 243 /* Peer's IP address */ 244 stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, 245 IPV6_MAX_BYTELEN); 246 247 /* Peer's AS number. */ 248 stream_putw (obuf, peer->as); 249 250 /* Dump attribute. */ 251 bgp_dump_routes_attr (obuf, attr); 252 } 253 else 254 { 255 ; 256 } 257 } 258#endif /* HAVE_IPV6 */ 259 260 /* Set length. */ 261 bgp_dump_set_size (obuf, type); 262 263 fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump_routes.fp); 264 fflush (bgp_dump_routes.fp); 265} 266 267/* Runs under child process. */ 268void 269bgp_dump_routes_func (int afi) 270{ 271 struct stream *obuf; 272 struct bgp_node *rn; 273 struct bgp_info *info; 274 struct bgp *bgp; 275 struct bgp_table *table; 276 unsigned int seq = 0; 277 278 obuf = bgp_dump_obuf; 279 280 bgp = bgp_get_default (); 281 if (!bgp) 282 return; 283 284 if (bgp_dump_routes.fp == NULL) 285 return; 286 287 /* Walk down each BGP route. */ 288 table = bgp->rib[afi][SAFI_UNICAST]; 289 290 for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) 291 for (info = rn->info; info; info = info->next) 292 bgp_dump_routes_entry (&rn->p, info, afi, MSG_TABLE_DUMP, seq++); 293} 294 295int 296bgp_dump_interval_func (struct thread *t) 297{ 298 struct bgp_dump *bgp_dump; 299 300 bgp_dump = THREAD_ARG (t); 301 bgp_dump->t_interval = NULL; 302 303 if (bgp_dump_open_file (bgp_dump) == NULL) 304 return 0; 305 306 /* In case of bgp_dump_routes, we need special route dump function. */ 307 if (bgp_dump->type == BGP_DUMP_ROUTES) 308 { 309 bgp_dump_routes_func (AFI_IP); 310 bgp_dump_routes_func (AFI_IP6); 311 } 312 313 bgp_dump_interval_add (bgp_dump, bgp_dump->interval); 314 315 return 0; 316} 317 318/* Dump common information. */ 319void 320bgp_dump_common (struct stream *obuf, struct peer *peer) 321{ 322 char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 323 324 /* Source AS number and Destination AS number. */ 325 stream_putw (obuf, peer->as); 326 stream_putw (obuf, peer->local_as); 327 328 if (peer->afc[AFI_IP][SAFI_UNICAST]) 329 { 330 stream_putw (obuf, peer->ifindex); 331 stream_putw (obuf, AFI_IP); 332 333 stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN); 334 335 if (peer->su_local) 336 stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN); 337 else 338 stream_put (obuf, empty, IPV4_MAX_BYTELEN); 339 } 340#ifdef HAVE_IPV6 341 else if (peer->afc[AFI_IP6][SAFI_UNICAST]) 342 { 343 /* Interface Index and Address family. */ 344 stream_putw (obuf, peer->ifindex); 345 stream_putw (obuf, AFI_IP6); 346 347 /* Source IP Address and Destination IP Address. */ 348 stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); 349 350 if (peer->su_local) 351 stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN); 352 else 353 stream_put (obuf, empty, IPV6_MAX_BYTELEN); 354 } 355#endif /* HAVE_IPV6 */ 356} 357 358/* Dump BGP status change. */ 359void 360bgp_dump_state (struct peer *peer, int status_old, int status_new) 361{ 362 struct stream *obuf; 363 364 /* If dump file pointer is disabled return immediately. */ 365 if (bgp_dump_all.fp == NULL) 366 return; 367 368 /* Make dump stream. */ 369 obuf = bgp_dump_obuf; 370 stream_reset (obuf); 371 372 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE); 373 bgp_dump_common (obuf, peer); 374 375 stream_putw (obuf, status_old); 376 stream_putw (obuf, status_new); 377 378 /* Set length. */ 379 bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); 380 381 /* Write to the stream. */ 382 fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump_all.fp); 383 fflush (bgp_dump_all.fp); 384} 385 386void 387bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, 388 struct stream *packet) 389{ 390 struct stream *obuf; 391 392 /* If dump file pointer is disabled return immediately. */ 393 if (bgp_dump->fp == NULL) 394 return; 395 396 /* Make dump stream. */ 397 obuf = bgp_dump_obuf; 398 stream_reset (obuf); 399 400 /* Dump header and common part. */ 401 bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE); 402 bgp_dump_common (obuf, peer); 403 404 /* Packet contents. */ 405 stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet)); 406 407 /* Set length. */ 408 bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); 409 410 /* Write to the stream. */ 411 fwrite (STREAM_DATA (obuf), stream_get_putp (obuf), 1, bgp_dump->fp); 412 fflush (bgp_dump->fp); 413} 414 415/* Called from bgp_packet.c when BGP packet is received. */ 416void 417bgp_dump_packet (struct peer *peer, int type, struct stream *packet) 418{ 419 /* bgp_dump_all. */ 420 bgp_dump_packet_func (&bgp_dump_all, peer, packet); 421 422 /* bgp_dump_updates. */ 423 if (type == BGP_MSG_UPDATE) 424 bgp_dump_packet_func (&bgp_dump_updates, peer, packet); 425} 426 427unsigned int 428bgp_dump_parse_time (char *str) 429{ 430 int i; 431 int len; 432 int seen_h; 433 int seen_m; 434 int time; 435 unsigned int total; 436 437 time = 0; 438 total = 0; 439 seen_h = 0; 440 seen_m = 0; 441 len = strlen (str); 442 443 for (i = 0; i < len; i++) 444 { 445 if (isdigit ((int) str[i])) 446 { 447 time *= 10; 448 time += str[i] - '0'; 449 } 450 else if (str[i] == 'H' || str[i] == 'h') 451 { 452 if (seen_h) 453 return 0; 454 if (seen_m) 455 return 0; 456 total += time * 60 *60; 457 time = 0; 458 seen_h = 1; 459 } 460 else if (str[i] == 'M' || str[i] == 'm') 461 { 462 if (seen_m) 463 return 0; 464 total += time * 60; 465 time = 0; 466 seen_h = 1; 467 } 468 else 469 return 0; 470 } 471 return total + time; 472} 473 474int 475bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, int type, 476 char *path, char *interval_str) 477{ 478 if (interval_str) 479 { 480 unsigned int interval; 481 482 /* Check interval string. */ 483 interval = bgp_dump_parse_time (interval_str); 484 if (interval == 0) 485 { 486 vty_out (vty, "Malformed interval string%s", VTY_NEWLINE); 487 return CMD_WARNING; 488 } 489 /* Set interval. */ 490 bgp_dump->interval = interval; 491 if (bgp_dump->interval_str) 492 free (bgp_dump->interval_str); 493 bgp_dump->interval_str = strdup (interval_str); 494 495 /* Create interval thread. */ 496 bgp_dump_interval_add (bgp_dump, interval); 497 } 498 499 /* Set type. */ 500 bgp_dump->type = type; 501 502 /* Set file name. */ 503 if (bgp_dump->filename) 504 free (bgp_dump->filename); 505 bgp_dump->filename = strdup (path); 506 507 /* This should be called when interval is expired. */ 508 bgp_dump_open_file (bgp_dump); 509 510 return CMD_SUCCESS; 511} 512 513int 514bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump) 515{ 516 /* Set file name. */ 517 if (bgp_dump->filename) 518 { 519 free (bgp_dump->filename); 520 bgp_dump->filename = NULL; 521 } 522 523 /* This should be called when interval is expired. */ 524 if (bgp_dump->fp) 525 { 526 fclose (bgp_dump->fp); 527 bgp_dump->fp = NULL; 528 } 529 530 /* Create interval thread. */ 531 if (bgp_dump->t_interval) 532 { 533 thread_cancel (bgp_dump->t_interval); 534 bgp_dump->t_interval = NULL; 535 } 536 537 bgp_dump->interval = 0; 538 539 if (bgp_dump->interval_str) 540 { 541 free (bgp_dump->interval_str); 542 bgp_dump->interval_str = NULL; 543 } 544 545 546 return CMD_SUCCESS; 547} 548 549DEFUN (dump_bgp_all, 550 dump_bgp_all_cmd, 551 "dump bgp all PATH", 552 "Dump packet\n" 553 "BGP packet dump\n" 554 "Dump all BGP packets\n" 555 "Output filename\n") 556{ 557 return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL); 558} 559 560DEFUN (dump_bgp_all_interval, 561 dump_bgp_all_interval_cmd, 562 "dump bgp all PATH INTERVAL", 563 "Dump packet\n" 564 "BGP packet dump\n" 565 "Dump all BGP packets\n" 566 "Output filename\n" 567 "Interval of output\n") 568{ 569 return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]); 570} 571 572DEFUN (no_dump_bgp_all, 573 no_dump_bgp_all_cmd, 574 "no dump bgp all [PATH] [INTERVAL]", 575 NO_STR 576 "Dump packet\n" 577 "BGP packet dump\n" 578 "Dump all BGP packets\n") 579{ 580 return bgp_dump_unset (vty, &bgp_dump_all); 581} 582 583DEFUN (dump_bgp_updates, 584 dump_bgp_updates_cmd, 585 "dump bgp updates PATH", 586 "Dump packet\n" 587 "BGP packet dump\n" 588 "Dump BGP updates only\n" 589 "Output filename\n") 590{ 591 return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL); 592} 593 594DEFUN (dump_bgp_updates_interval, 595 dump_bgp_updates_interval_cmd, 596 "dump bgp updates PATH INTERVAL", 597 "Dump packet\n" 598 "BGP packet dump\n" 599 "Dump BGP updates only\n" 600 "Output filename\n" 601 "Interval of output\n") 602{ 603 return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]); 604} 605 606DEFUN (no_dump_bgp_updates, 607 no_dump_bgp_updates_cmd, 608 "no dump bgp updates [PATH] [INTERVAL]", 609 NO_STR 610 "Dump packet\n" 611 "BGP packet dump\n" 612 "Dump BGP updates only\n") 613{ 614 return bgp_dump_unset (vty, &bgp_dump_updates); 615} 616 617DEFUN (dump_bgp_routes, 618 dump_bgp_routes_cmd, 619 "dump bgp routes-mrt PATH", 620 "Dump packet\n" 621 "BGP packet dump\n" 622 "Dump whole BGP routing table\n" 623 "Output filename\n") 624{ 625 return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL); 626} 627 628DEFUN (dump_bgp_routes_interval, 629 dump_bgp_routes_interval_cmd, 630 "dump bgp routes-mrt PATH INTERVAL", 631 "Dump packet\n" 632 "BGP packet dump\n" 633 "Dump whole BGP routing table\n" 634 "Output filename\n" 635 "Interval of output\n") 636{ 637 return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]); 638} 639 640DEFUN (no_dump_bgp_routes, 641 no_dump_bgp_routes_cmd, 642 "no dump bgp routes-mrt [PATH] [INTERVAL]", 643 NO_STR 644 "Dump packet\n" 645 "BGP packet dump\n" 646 "Dump whole BGP routing table\n") 647{ 648 return bgp_dump_unset (vty, &bgp_dump_routes); 649} 650 651/* BGP node structure. */ 652struct cmd_node bgp_dump_node = 653{ 654 DUMP_NODE, 655 "", 656}; 657 658#if 0 659char * 660config_time2str (unsigned int interval) 661{ 662 static char buf[BUFSIZ]; 663 664 buf[0] = '\0'; 665 666 if (interval / 3600) 667 { 668 sprintf (buf, "%dh", interval / 3600); 669 interval %= 3600; 670 } 671 if (interval / 60) 672 { 673 sprintf (buf + strlen (buf), "%dm", interval /60); 674 interval %= 60; 675 } 676 if (interval) 677 { 678 sprintf (buf + strlen (buf), "%d", interval); 679 } 680 return buf; 681} 682#endif 683 684int 685config_write_bgp_dump (struct vty *vty) 686{ 687 if (bgp_dump_all.filename) 688 { 689 if (bgp_dump_all.interval_str) 690 vty_out (vty, "dump bgp all %s %s%s", 691 bgp_dump_all.filename, bgp_dump_all.interval_str, 692 VTY_NEWLINE); 693 else 694 vty_out (vty, "dump bgp all %s%s", 695 bgp_dump_all.filename, VTY_NEWLINE); 696 } 697 if (bgp_dump_updates.filename) 698 { 699 if (bgp_dump_updates.interval_str) 700 vty_out (vty, "dump bgp updates %s %s%s", 701 bgp_dump_updates.filename, bgp_dump_updates.interval_str, 702 VTY_NEWLINE); 703 else 704 vty_out (vty, "dump bgp updates %s%s", 705 bgp_dump_updates.filename, VTY_NEWLINE); 706 } 707 if (bgp_dump_routes.filename) 708 { 709 if (bgp_dump_routes.interval_str) 710 vty_out (vty, "dump bgp routes-mrt %s %s%s", 711 bgp_dump_routes.filename, bgp_dump_routes.interval_str, 712 VTY_NEWLINE); 713 else 714 vty_out (vty, "dump bgp routes-mrt %s%s", 715 bgp_dump_routes.filename, VTY_NEWLINE); 716 } 717 return 0; 718} 719 720/* Initialize BGP packet dump functionality. */ 721void 722bgp_dump_init () 723{ 724 memset (&bgp_dump_all, 0, sizeof (struct bgp_dump)); 725 memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump)); 726 memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump)); 727 728 bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_HEADER_SIZE); 729 730 install_node (&bgp_dump_node, config_write_bgp_dump); 731 732 install_element (CONFIG_NODE, &dump_bgp_all_cmd); 733 install_element (CONFIG_NODE, &dump_bgp_all_interval_cmd); 734 install_element (CONFIG_NODE, &no_dump_bgp_all_cmd); 735 install_element (CONFIG_NODE, &dump_bgp_updates_cmd); 736 install_element (CONFIG_NODE, &dump_bgp_updates_interval_cmd); 737 install_element (CONFIG_NODE, &no_dump_bgp_updates_cmd); 738 install_element (CONFIG_NODE, &dump_bgp_routes_cmd); 739 install_element (CONFIG_NODE, &dump_bgp_routes_interval_cmd); 740 install_element (CONFIG_NODE, &no_dump_bgp_routes_cmd); 741} 742