Annotation of embedaddon/quagga/zebra/zebra_fpm.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Main implementation file for interface to Forwarding Plane Manager.
! 3: *
! 4: * Copyright (C) 2012 by Open Source Routing.
! 5: * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
! 6: *
! 7: * This file is part of GNU Zebra.
! 8: *
! 9: * GNU Zebra is free software; you can redistribute it and/or modify it
! 10: * under the terms of the GNU General Public License as published by the
! 11: * Free Software Foundation; either version 2, or (at your option) any
! 12: * later version.
! 13: *
! 14: * GNU Zebra is distributed in the hope that it will be useful, but
! 15: * WITHOUT ANY WARRANTY; without even the implied warranty of
! 16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 17: * General Public License for more details.
! 18: *
! 19: * You should have received a copy of the GNU General Public License
! 20: * along with GNU Zebra; see the file COPYING. If not, write to the Free
! 21: * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
! 22: * 02111-1307, USA.
! 23: */
! 24:
! 25: #include <zebra.h>
! 26:
! 27: #include "log.h"
! 28: #include "stream.h"
! 29: #include "thread.h"
! 30: #include "network.h"
! 31: #include "command.h"
! 32:
! 33: #include "zebra/rib.h"
! 34:
! 35: #include "fpm/fpm.h"
! 36: #include "zebra_fpm.h"
! 37: #include "zebra_fpm_private.h"
! 38:
! 39: /*
! 40: * Interval at which we attempt to connect to the FPM.
! 41: */
! 42: #define ZFPM_CONNECT_RETRY_IVL 5
! 43:
! 44: /*
! 45: * Sizes of outgoing and incoming stream buffers for writing/reading
! 46: * FPM messages.
! 47: */
! 48: #define ZFPM_OBUF_SIZE (2 * FPM_MAX_MSG_LEN)
! 49: #define ZFPM_IBUF_SIZE (FPM_MAX_MSG_LEN)
! 50:
! 51: /*
! 52: * The maximum number of times the FPM socket write callback can call
! 53: * 'write' before it yields.
! 54: */
! 55: #define ZFPM_MAX_WRITES_PER_RUN 10
! 56:
! 57: /*
! 58: * Interval over which we collect statistics.
! 59: */
! 60: #define ZFPM_STATS_IVL_SECS 10
! 61:
! 62: /*
! 63: * Structure that holds state for iterating over all route_node
! 64: * structures that are candidates for being communicated to the FPM.
! 65: */
! 66: typedef struct zfpm_rnodes_iter_t_
! 67: {
! 68: rib_tables_iter_t tables_iter;
! 69: route_table_iter_t iter;
! 70: } zfpm_rnodes_iter_t;
! 71:
! 72: /*
! 73: * Statistics.
! 74: */
! 75: typedef struct zfpm_stats_t_ {
! 76: unsigned long connect_calls;
! 77: unsigned long connect_no_sock;
! 78:
! 79: unsigned long read_cb_calls;
! 80:
! 81: unsigned long write_cb_calls;
! 82: unsigned long write_calls;
! 83: unsigned long partial_writes;
! 84: unsigned long max_writes_hit;
! 85: unsigned long t_write_yields;
! 86:
! 87: unsigned long nop_deletes_skipped;
! 88: unsigned long route_adds;
! 89: unsigned long route_dels;
! 90:
! 91: unsigned long updates_triggered;
! 92: unsigned long redundant_triggers;
! 93: unsigned long non_fpm_table_triggers;
! 94:
! 95: unsigned long dests_del_after_update;
! 96:
! 97: unsigned long t_conn_down_starts;
! 98: unsigned long t_conn_down_dests_processed;
! 99: unsigned long t_conn_down_yields;
! 100: unsigned long t_conn_down_finishes;
! 101:
! 102: unsigned long t_conn_up_starts;
! 103: unsigned long t_conn_up_dests_processed;
! 104: unsigned long t_conn_up_yields;
! 105: unsigned long t_conn_up_aborts;
! 106: unsigned long t_conn_up_finishes;
! 107:
! 108: } zfpm_stats_t;
! 109:
! 110: /*
! 111: * States for the FPM state machine.
! 112: */
! 113: typedef enum {
! 114:
! 115: /*
! 116: * In this state we are not yet ready to connect to the FPM. This
! 117: * can happen when this module is disabled, or if we're cleaning up
! 118: * after a connection has gone down.
! 119: */
! 120: ZFPM_STATE_IDLE,
! 121:
! 122: /*
! 123: * Ready to talk to the FPM and periodically trying to connect to
! 124: * it.
! 125: */
! 126: ZFPM_STATE_ACTIVE,
! 127:
! 128: /*
! 129: * In the middle of bringing up a TCP connection. Specifically,
! 130: * waiting for a connect() call to complete asynchronously.
! 131: */
! 132: ZFPM_STATE_CONNECTING,
! 133:
! 134: /*
! 135: * TCP connection to the FPM is up.
! 136: */
! 137: ZFPM_STATE_ESTABLISHED
! 138:
! 139: } zfpm_state_t;
! 140:
! 141: /*
! 142: * Globals.
! 143: */
! 144: typedef struct zfpm_glob_t_
! 145: {
! 146:
! 147: /*
! 148: * True if the FPM module has been enabled.
! 149: */
! 150: int enabled;
! 151:
! 152: struct thread_master *master;
! 153:
! 154: zfpm_state_t state;
! 155:
! 156: /*
! 157: * Port on which the FPM is running.
! 158: */
! 159: int fpm_port;
! 160:
! 161: /*
! 162: * List of rib_dest_t structures to be processed
! 163: */
! 164: TAILQ_HEAD (zfpm_dest_q, rib_dest_t_) dest_q;
! 165:
! 166: /*
! 167: * Stream socket to the FPM.
! 168: */
! 169: int sock;
! 170:
! 171: /*
! 172: * Buffers for messages to/from the FPM.
! 173: */
! 174: struct stream *obuf;
! 175: struct stream *ibuf;
! 176:
! 177: /*
! 178: * Threads for I/O.
! 179: */
! 180: struct thread *t_connect;
! 181: struct thread *t_write;
! 182: struct thread *t_read;
! 183:
! 184: /*
! 185: * Thread to clean up after the TCP connection to the FPM goes down
! 186: * and the state that belongs to it.
! 187: */
! 188: struct thread *t_conn_down;
! 189:
! 190: struct {
! 191: zfpm_rnodes_iter_t iter;
! 192: } t_conn_down_state;
! 193:
! 194: /*
! 195: * Thread to take actions once the TCP conn to the FPM comes up, and
! 196: * the state that belongs to it.
! 197: */
! 198: struct thread *t_conn_up;
! 199:
! 200: struct {
! 201: zfpm_rnodes_iter_t iter;
! 202: } t_conn_up_state;
! 203:
! 204: unsigned long connect_calls;
! 205: time_t last_connect_call_time;
! 206:
! 207: /*
! 208: * Stats from the start of the current statistics interval up to
! 209: * now. These are the counters we typically update in the code.
! 210: */
! 211: zfpm_stats_t stats;
! 212:
! 213: /*
! 214: * Statistics that were gathered in the last collection interval.
! 215: */
! 216: zfpm_stats_t last_ivl_stats;
! 217:
! 218: /*
! 219: * Cumulative stats from the last clear to the start of the current
! 220: * statistics interval.
! 221: */
! 222: zfpm_stats_t cumulative_stats;
! 223:
! 224: /*
! 225: * Stats interval timer.
! 226: */
! 227: struct thread *t_stats;
! 228:
! 229: /*
! 230: * If non-zero, the last time when statistics were cleared.
! 231: */
! 232: time_t last_stats_clear_time;
! 233:
! 234: } zfpm_glob_t;
! 235:
! 236: static zfpm_glob_t zfpm_glob_space;
! 237: static zfpm_glob_t *zfpm_g = &zfpm_glob_space;
! 238:
! 239: static int zfpm_read_cb (struct thread *thread);
! 240: static int zfpm_write_cb (struct thread *thread);
! 241:
! 242: static void zfpm_set_state (zfpm_state_t state, const char *reason);
! 243: static void zfpm_start_connect_timer (const char *reason);
! 244: static void zfpm_start_stats_timer (void);
! 245:
! 246: /*
! 247: * zfpm_thread_should_yield
! 248: */
! 249: static inline int
! 250: zfpm_thread_should_yield (struct thread *t)
! 251: {
! 252: return thread_should_yield (t);
! 253: }
! 254:
! 255: /*
! 256: * zfpm_state_to_str
! 257: */
! 258: static const char *
! 259: zfpm_state_to_str (zfpm_state_t state)
! 260: {
! 261: switch (state)
! 262: {
! 263:
! 264: case ZFPM_STATE_IDLE:
! 265: return "idle";
! 266:
! 267: case ZFPM_STATE_ACTIVE:
! 268: return "active";
! 269:
! 270: case ZFPM_STATE_CONNECTING:
! 271: return "connecting";
! 272:
! 273: case ZFPM_STATE_ESTABLISHED:
! 274: return "established";
! 275:
! 276: default:
! 277: return "unknown";
! 278: }
! 279: }
! 280:
! 281: /*
! 282: * zfpm_get_time
! 283: */
! 284: static time_t
! 285: zfpm_get_time (void)
! 286: {
! 287: struct timeval tv;
! 288:
! 289: if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0)
! 290: zlog_warn ("FPM: quagga_gettime failed!!");
! 291:
! 292: return tv.tv_sec;
! 293: }
! 294:
! 295: /*
! 296: * zfpm_get_elapsed_time
! 297: *
! 298: * Returns the time elapsed (in seconds) since the given time.
! 299: */
! 300: static time_t
! 301: zfpm_get_elapsed_time (time_t reference)
! 302: {
! 303: time_t now;
! 304:
! 305: now = zfpm_get_time ();
! 306:
! 307: if (now < reference)
! 308: {
! 309: assert (0);
! 310: return 0;
! 311: }
! 312:
! 313: return now - reference;
! 314: }
! 315:
! 316: /*
! 317: * zfpm_is_table_for_fpm
! 318: *
! 319: * Returns TRUE if the the given table is to be communicated to the
! 320: * FPM.
! 321: */
! 322: static inline int
! 323: zfpm_is_table_for_fpm (struct route_table *table)
! 324: {
! 325: rib_table_info_t *info;
! 326:
! 327: info = rib_table_info (table);
! 328:
! 329: /*
! 330: * We only send the unicast tables in the main instance to the FPM
! 331: * at this point.
! 332: */
! 333: if (info->vrf->id != 0)
! 334: return 0;
! 335:
! 336: if (info->safi != SAFI_UNICAST)
! 337: return 0;
! 338:
! 339: return 1;
! 340: }
! 341:
! 342: /*
! 343: * zfpm_rnodes_iter_init
! 344: */
! 345: static inline void
! 346: zfpm_rnodes_iter_init (zfpm_rnodes_iter_t *iter)
! 347: {
! 348: memset (iter, 0, sizeof (*iter));
! 349: rib_tables_iter_init (&iter->tables_iter);
! 350:
! 351: /*
! 352: * This is a hack, but it makes implementing 'next' easier by
! 353: * ensuring that route_table_iter_next() will return NULL the first
! 354: * time we call it.
! 355: */
! 356: route_table_iter_init (&iter->iter, NULL);
! 357: route_table_iter_cleanup (&iter->iter);
! 358: }
! 359:
! 360: /*
! 361: * zfpm_rnodes_iter_next
! 362: */
! 363: static inline struct route_node *
! 364: zfpm_rnodes_iter_next (zfpm_rnodes_iter_t *iter)
! 365: {
! 366: struct route_node *rn;
! 367: struct route_table *table;
! 368:
! 369: while (1)
! 370: {
! 371: rn = route_table_iter_next (&iter->iter);
! 372: if (rn)
! 373: return rn;
! 374:
! 375: /*
! 376: * We've made our way through this table, go to the next one.
! 377: */
! 378: route_table_iter_cleanup (&iter->iter);
! 379:
! 380: while ((table = rib_tables_iter_next (&iter->tables_iter)))
! 381: {
! 382: if (zfpm_is_table_for_fpm (table))
! 383: break;
! 384: }
! 385:
! 386: if (!table)
! 387: return NULL;
! 388:
! 389: route_table_iter_init (&iter->iter, table);
! 390: }
! 391:
! 392: return NULL;
! 393: }
! 394:
! 395: /*
! 396: * zfpm_rnodes_iter_pause
! 397: */
! 398: static inline void
! 399: zfpm_rnodes_iter_pause (zfpm_rnodes_iter_t *iter)
! 400: {
! 401: route_table_iter_pause (&iter->iter);
! 402: }
! 403:
! 404: /*
! 405: * zfpm_rnodes_iter_cleanup
! 406: */
! 407: static inline void
! 408: zfpm_rnodes_iter_cleanup (zfpm_rnodes_iter_t *iter)
! 409: {
! 410: route_table_iter_cleanup (&iter->iter);
! 411: rib_tables_iter_cleanup (&iter->tables_iter);
! 412: }
! 413:
! 414: /*
! 415: * zfpm_stats_init
! 416: *
! 417: * Initialize a statistics block.
! 418: */
! 419: static inline void
! 420: zfpm_stats_init (zfpm_stats_t *stats)
! 421: {
! 422: memset (stats, 0, sizeof (*stats));
! 423: }
! 424:
! 425: /*
! 426: * zfpm_stats_reset
! 427: */
! 428: static inline void
! 429: zfpm_stats_reset (zfpm_stats_t *stats)
! 430: {
! 431: zfpm_stats_init (stats);
! 432: }
! 433:
! 434: /*
! 435: * zfpm_stats_copy
! 436: */
! 437: static inline void
! 438: zfpm_stats_copy (const zfpm_stats_t *src, zfpm_stats_t *dest)
! 439: {
! 440: memcpy (dest, src, sizeof (*dest));
! 441: }
! 442:
! 443: /*
! 444: * zfpm_stats_compose
! 445: *
! 446: * Total up the statistics in two stats structures ('s1 and 's2') and
! 447: * return the result in the third argument, 'result'. Note that the
! 448: * pointer 'result' may be the same as 's1' or 's2'.
! 449: *
! 450: * For simplicity, the implementation below assumes that the stats
! 451: * structure is composed entirely of counters. This can easily be
! 452: * changed when necessary.
! 453: */
! 454: static void
! 455: zfpm_stats_compose (const zfpm_stats_t *s1, const zfpm_stats_t *s2,
! 456: zfpm_stats_t *result)
! 457: {
! 458: const unsigned long *p1, *p2;
! 459: unsigned long *result_p;
! 460: int i, num_counters;
! 461:
! 462: p1 = (const unsigned long *) s1;
! 463: p2 = (const unsigned long *) s2;
! 464: result_p = (unsigned long *) result;
! 465:
! 466: num_counters = (sizeof (zfpm_stats_t) / sizeof (unsigned long));
! 467:
! 468: for (i = 0; i < num_counters; i++)
! 469: {
! 470: result_p[i] = p1[i] + p2[i];
! 471: }
! 472: }
! 473:
! 474: /*
! 475: * zfpm_read_on
! 476: */
! 477: static inline void
! 478: zfpm_read_on (void)
! 479: {
! 480: assert (!zfpm_g->t_read);
! 481: assert (zfpm_g->sock >= 0);
! 482:
! 483: THREAD_READ_ON (zfpm_g->master, zfpm_g->t_read, zfpm_read_cb, 0,
! 484: zfpm_g->sock);
! 485: }
! 486:
! 487: /*
! 488: * zfpm_write_on
! 489: */
! 490: static inline void
! 491: zfpm_write_on (void)
! 492: {
! 493: assert (!zfpm_g->t_write);
! 494: assert (zfpm_g->sock >= 0);
! 495:
! 496: THREAD_WRITE_ON (zfpm_g->master, zfpm_g->t_write, zfpm_write_cb, 0,
! 497: zfpm_g->sock);
! 498: }
! 499:
! 500: /*
! 501: * zfpm_read_off
! 502: */
! 503: static inline void
! 504: zfpm_read_off (void)
! 505: {
! 506: THREAD_READ_OFF (zfpm_g->t_read);
! 507: }
! 508:
! 509: /*
! 510: * zfpm_write_off
! 511: */
! 512: static inline void
! 513: zfpm_write_off (void)
! 514: {
! 515: THREAD_WRITE_OFF (zfpm_g->t_write);
! 516: }
! 517:
! 518: /*
! 519: * zfpm_conn_up_thread_cb
! 520: *
! 521: * Callback for actions to be taken when the connection to the FPM
! 522: * comes up.
! 523: */
! 524: static int
! 525: zfpm_conn_up_thread_cb (struct thread *thread)
! 526: {
! 527: struct route_node *rnode;
! 528: zfpm_rnodes_iter_t *iter;
! 529: rib_dest_t *dest;
! 530:
! 531: assert (zfpm_g->t_conn_up);
! 532: zfpm_g->t_conn_up = NULL;
! 533:
! 534: iter = &zfpm_g->t_conn_up_state.iter;
! 535:
! 536: if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
! 537: {
! 538: zfpm_debug ("Connection not up anymore, conn_up thread aborting");
! 539: zfpm_g->stats.t_conn_up_aborts++;
! 540: goto done;
! 541: }
! 542:
! 543: while ((rnode = zfpm_rnodes_iter_next (iter)))
! 544: {
! 545: dest = rib_dest_from_rnode (rnode);
! 546:
! 547: if (dest)
! 548: {
! 549: zfpm_g->stats.t_conn_up_dests_processed++;
! 550: zfpm_trigger_update (rnode, NULL);
! 551: }
! 552:
! 553: /*
! 554: * Yield if need be.
! 555: */
! 556: if (!zfpm_thread_should_yield (thread))
! 557: continue;
! 558:
! 559: zfpm_g->stats.t_conn_up_yields++;
! 560: zfpm_rnodes_iter_pause (iter);
! 561: zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
! 562: zfpm_conn_up_thread_cb,
! 563: 0, 0);
! 564: return 0;
! 565: }
! 566:
! 567: zfpm_g->stats.t_conn_up_finishes++;
! 568:
! 569: done:
! 570: zfpm_rnodes_iter_cleanup (iter);
! 571: return 0;
! 572: }
! 573:
! 574: /*
! 575: * zfpm_connection_up
! 576: *
! 577: * Called when the connection to the FPM comes up.
! 578: */
! 579: static void
! 580: zfpm_connection_up (const char *detail)
! 581: {
! 582: assert (zfpm_g->sock >= 0);
! 583: zfpm_read_on ();
! 584: zfpm_write_on ();
! 585: zfpm_set_state (ZFPM_STATE_ESTABLISHED, detail);
! 586:
! 587: /*
! 588: * Start thread to push existing routes to the FPM.
! 589: */
! 590: assert (!zfpm_g->t_conn_up);
! 591:
! 592: zfpm_rnodes_iter_init (&zfpm_g->t_conn_up_state.iter);
! 593:
! 594: zfpm_debug ("Starting conn_up thread");
! 595: zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
! 596: zfpm_conn_up_thread_cb, 0, 0);
! 597: zfpm_g->stats.t_conn_up_starts++;
! 598: }
! 599:
! 600: /*
! 601: * zfpm_connect_check
! 602: *
! 603: * Check if an asynchronous connect() to the FPM is complete.
! 604: */
! 605: static void
! 606: zfpm_connect_check ()
! 607: {
! 608: int status;
! 609: socklen_t slen;
! 610: int ret;
! 611:
! 612: zfpm_read_off ();
! 613: zfpm_write_off ();
! 614:
! 615: slen = sizeof (status);
! 616: ret = getsockopt (zfpm_g->sock, SOL_SOCKET, SO_ERROR, (void *) &status,
! 617: &slen);
! 618:
! 619: if (ret >= 0 && status == 0)
! 620: {
! 621: zfpm_connection_up ("async connect complete");
! 622: return;
! 623: }
! 624:
! 625: /*
! 626: * getsockopt() failed or indicated an error on the socket.
! 627: */
! 628: close (zfpm_g->sock);
! 629: zfpm_g->sock = -1;
! 630:
! 631: zfpm_start_connect_timer ("getsockopt() after async connect failed");
! 632: return;
! 633: }
! 634:
! 635: /*
! 636: * zfpm_conn_down_thread_cb
! 637: *
! 638: * Callback that is invoked to clean up state after the TCP connection
! 639: * to the FPM goes down.
! 640: */
! 641: static int
! 642: zfpm_conn_down_thread_cb (struct thread *thread)
! 643: {
! 644: struct route_node *rnode;
! 645: zfpm_rnodes_iter_t *iter;
! 646: rib_dest_t *dest;
! 647:
! 648: assert (zfpm_g->state == ZFPM_STATE_IDLE);
! 649:
! 650: assert (zfpm_g->t_conn_down);
! 651: zfpm_g->t_conn_down = NULL;
! 652:
! 653: iter = &zfpm_g->t_conn_down_state.iter;
! 654:
! 655: while ((rnode = zfpm_rnodes_iter_next (iter)))
! 656: {
! 657: dest = rib_dest_from_rnode (rnode);
! 658:
! 659: if (dest)
! 660: {
! 661: if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM))
! 662: {
! 663: TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
! 664: }
! 665:
! 666: UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
! 667: UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
! 668:
! 669: zfpm_g->stats.t_conn_down_dests_processed++;
! 670:
! 671: /*
! 672: * Check if the dest should be deleted.
! 673: */
! 674: rib_gc_dest(rnode);
! 675: }
! 676:
! 677: /*
! 678: * Yield if need be.
! 679: */
! 680: if (!zfpm_thread_should_yield (thread))
! 681: continue;
! 682:
! 683: zfpm_g->stats.t_conn_down_yields++;
! 684: zfpm_rnodes_iter_pause (iter);
! 685: zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
! 686: zfpm_conn_down_thread_cb,
! 687: 0, 0);
! 688: return 0;
! 689: }
! 690:
! 691: zfpm_g->stats.t_conn_down_finishes++;
! 692: zfpm_rnodes_iter_cleanup (iter);
! 693:
! 694: /*
! 695: * Start the process of connecting to the FPM again.
! 696: */
! 697: zfpm_start_connect_timer ("cleanup complete");
! 698: return 0;
! 699: }
! 700:
! 701: /*
! 702: * zfpm_connection_down
! 703: *
! 704: * Called when the connection to the FPM has gone down.
! 705: */
! 706: static void
! 707: zfpm_connection_down (const char *detail)
! 708: {
! 709: if (!detail)
! 710: detail = "unknown";
! 711:
! 712: assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
! 713:
! 714: zlog_info ("connection to the FPM has gone down: %s", detail);
! 715:
! 716: zfpm_read_off ();
! 717: zfpm_write_off ();
! 718:
! 719: stream_reset (zfpm_g->ibuf);
! 720: stream_reset (zfpm_g->obuf);
! 721:
! 722: if (zfpm_g->sock >= 0) {
! 723: close (zfpm_g->sock);
! 724: zfpm_g->sock = -1;
! 725: }
! 726:
! 727: /*
! 728: * Start thread to clean up state after the connection goes down.
! 729: */
! 730: assert (!zfpm_g->t_conn_down);
! 731: zfpm_debug ("Starting conn_down thread");
! 732: zfpm_rnodes_iter_init (&zfpm_g->t_conn_down_state.iter);
! 733: zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
! 734: zfpm_conn_down_thread_cb, 0, 0);
! 735: zfpm_g->stats.t_conn_down_starts++;
! 736:
! 737: zfpm_set_state (ZFPM_STATE_IDLE, detail);
! 738: }
! 739:
! 740: /*
! 741: * zfpm_read_cb
! 742: */
! 743: static int
! 744: zfpm_read_cb (struct thread *thread)
! 745: {
! 746: size_t already;
! 747: struct stream *ibuf;
! 748: uint16_t msg_len;
! 749: fpm_msg_hdr_t *hdr;
! 750:
! 751: zfpm_g->stats.read_cb_calls++;
! 752: assert (zfpm_g->t_read);
! 753: zfpm_g->t_read = NULL;
! 754:
! 755: /*
! 756: * Check if async connect is now done.
! 757: */
! 758: if (zfpm_g->state == ZFPM_STATE_CONNECTING)
! 759: {
! 760: zfpm_connect_check();
! 761: return 0;
! 762: }
! 763:
! 764: assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
! 765: assert (zfpm_g->sock >= 0);
! 766:
! 767: ibuf = zfpm_g->ibuf;
! 768:
! 769: already = stream_get_endp (ibuf);
! 770: if (already < FPM_MSG_HDR_LEN)
! 771: {
! 772: ssize_t nbyte;
! 773:
! 774: nbyte = stream_read_try (ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already);
! 775: if (nbyte == 0 || nbyte == -1)
! 776: {
! 777: zfpm_connection_down ("closed socket in read");
! 778: return 0;
! 779: }
! 780:
! 781: if (nbyte != (ssize_t) (FPM_MSG_HDR_LEN - already))
! 782: goto done;
! 783:
! 784: already = FPM_MSG_HDR_LEN;
! 785: }
! 786:
! 787: stream_set_getp (ibuf, 0);
! 788:
! 789: hdr = (fpm_msg_hdr_t *) stream_pnt (ibuf);
! 790:
! 791: if (!fpm_msg_hdr_ok (hdr))
! 792: {
! 793: zfpm_connection_down ("invalid message header");
! 794: return 0;
! 795: }
! 796:
! 797: msg_len = fpm_msg_len (hdr);
! 798:
! 799: /*
! 800: * Read out the rest of the packet.
! 801: */
! 802: if (already < msg_len)
! 803: {
! 804: ssize_t nbyte;
! 805:
! 806: nbyte = stream_read_try (ibuf, zfpm_g->sock, msg_len - already);
! 807:
! 808: if (nbyte == 0 || nbyte == -1)
! 809: {
! 810: zfpm_connection_down ("failed to read message");
! 811: return 0;
! 812: }
! 813:
! 814: if (nbyte != (ssize_t) (msg_len - already))
! 815: goto done;
! 816: }
! 817:
! 818: zfpm_debug ("Read out a full fpm message");
! 819:
! 820: /*
! 821: * Just throw it away for now.
! 822: */
! 823: stream_reset (ibuf);
! 824:
! 825: done:
! 826: zfpm_read_on ();
! 827: return 0;
! 828: }
! 829:
! 830: /*
! 831: * zfpm_writes_pending
! 832: *
! 833: * Returns TRUE if we may have something to write to the FPM.
! 834: */
! 835: static int
! 836: zfpm_writes_pending (void)
! 837: {
! 838:
! 839: /*
! 840: * Check if there is any data in the outbound buffer that has not
! 841: * been written to the socket yet.
! 842: */
! 843: if (stream_get_endp (zfpm_g->obuf) - stream_get_getp (zfpm_g->obuf))
! 844: return 1;
! 845:
! 846: /*
! 847: * Check if there are any prefixes on the outbound queue.
! 848: */
! 849: if (!TAILQ_EMPTY (&zfpm_g->dest_q))
! 850: return 1;
! 851:
! 852: return 0;
! 853: }
! 854:
! 855: /*
! 856: * zfpm_encode_route
! 857: *
! 858: * Encode a message to the FPM with information about the given route.
! 859: *
! 860: * Returns the number of bytes written to the buffer. 0 or a negative
! 861: * value indicates an error.
! 862: */
! 863: static inline int
! 864: zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf,
! 865: size_t in_buf_len)
! 866: {
! 867: #ifndef HAVE_NETLINK
! 868: return 0;
! 869: #else
! 870:
! 871: int cmd;
! 872:
! 873: cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE;
! 874:
! 875: return zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len);
! 876:
! 877: #endif /* HAVE_NETLINK */
! 878: }
! 879:
! 880: /*
! 881: * zfpm_route_for_update
! 882: *
! 883: * Returns the rib that is to be sent to the FPM for a given dest.
! 884: */
! 885: static struct rib *
! 886: zfpm_route_for_update (rib_dest_t *dest)
! 887: {
! 888: struct rib *rib;
! 889:
! 890: RIB_DEST_FOREACH_ROUTE (dest, rib)
! 891: {
! 892: if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
! 893: continue;
! 894:
! 895: return rib;
! 896: }
! 897:
! 898: /*
! 899: * We have no route for this destination.
! 900: */
! 901: return NULL;
! 902: }
! 903:
! 904: /*
! 905: * zfpm_build_updates
! 906: *
! 907: * Process the outgoing queue and write messages to the outbound
! 908: * buffer.
! 909: */
! 910: static void
! 911: zfpm_build_updates (void)
! 912: {
! 913: struct stream *s;
! 914: rib_dest_t *dest;
! 915: unsigned char *buf, *data, *buf_end;
! 916: size_t msg_len;
! 917: size_t data_len;
! 918: fpm_msg_hdr_t *hdr;
! 919: struct rib *rib;
! 920: int is_add, write_msg;
! 921:
! 922: s = zfpm_g->obuf;
! 923:
! 924: assert (stream_empty (s));
! 925:
! 926: do {
! 927:
! 928: /*
! 929: * Make sure there is enough space to write another message.
! 930: */
! 931: if (STREAM_WRITEABLE (s) < FPM_MAX_MSG_LEN)
! 932: break;
! 933:
! 934: buf = STREAM_DATA (s) + stream_get_endp (s);
! 935: buf_end = buf + STREAM_WRITEABLE (s);
! 936:
! 937: dest = TAILQ_FIRST (&zfpm_g->dest_q);
! 938: if (!dest)
! 939: break;
! 940:
! 941: assert (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM));
! 942:
! 943: hdr = (fpm_msg_hdr_t *) buf;
! 944: hdr->version = FPM_PROTO_VERSION;
! 945: hdr->msg_type = FPM_MSG_TYPE_NETLINK;
! 946:
! 947: data = fpm_msg_data (hdr);
! 948:
! 949: rib = zfpm_route_for_update (dest);
! 950: is_add = rib ? 1 : 0;
! 951:
! 952: write_msg = 1;
! 953:
! 954: /*
! 955: * If this is a route deletion, and we have not sent the route to
! 956: * the FPM previously, skip it.
! 957: */
! 958: if (!is_add && !CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
! 959: {
! 960: write_msg = 0;
! 961: zfpm_g->stats.nop_deletes_skipped++;
! 962: }
! 963:
! 964: if (write_msg) {
! 965: data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data);
! 966:
! 967: assert (data_len);
! 968: if (data_len)
! 969: {
! 970: msg_len = fpm_data_len_to_msg_len (data_len);
! 971: hdr->msg_len = htons (msg_len);
! 972: stream_forward_endp (s, msg_len);
! 973:
! 974: if (is_add)
! 975: zfpm_g->stats.route_adds++;
! 976: else
! 977: zfpm_g->stats.route_dels++;
! 978: }
! 979: }
! 980:
! 981: /*
! 982: * Remove the dest from the queue, and reset the flag.
! 983: */
! 984: UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
! 985: TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
! 986:
! 987: if (is_add)
! 988: {
! 989: SET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
! 990: }
! 991: else
! 992: {
! 993: UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
! 994: }
! 995:
! 996: /*
! 997: * Delete the destination if necessary.
! 998: */
! 999: if (rib_gc_dest (dest->rnode))
! 1000: zfpm_g->stats.dests_del_after_update++;
! 1001:
! 1002: } while (1);
! 1003:
! 1004: }
! 1005:
! 1006: /*
! 1007: * zfpm_write_cb
! 1008: */
! 1009: static int
! 1010: zfpm_write_cb (struct thread *thread)
! 1011: {
! 1012: struct stream *s;
! 1013: int num_writes;
! 1014:
! 1015: zfpm_g->stats.write_cb_calls++;
! 1016: assert (zfpm_g->t_write);
! 1017: zfpm_g->t_write = NULL;
! 1018:
! 1019: /*
! 1020: * Check if async connect is now done.
! 1021: */
! 1022: if (zfpm_g->state == ZFPM_STATE_CONNECTING)
! 1023: {
! 1024: zfpm_connect_check ();
! 1025: return 0;
! 1026: }
! 1027:
! 1028: assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
! 1029: assert (zfpm_g->sock >= 0);
! 1030:
! 1031: num_writes = 0;
! 1032:
! 1033: do
! 1034: {
! 1035: int bytes_to_write, bytes_written;
! 1036:
! 1037: s = zfpm_g->obuf;
! 1038:
! 1039: /*
! 1040: * If the stream is empty, try fill it up with data.
! 1041: */
! 1042: if (stream_empty (s))
! 1043: {
! 1044: zfpm_build_updates ();
! 1045: }
! 1046:
! 1047: bytes_to_write = stream_get_endp (s) - stream_get_getp (s);
! 1048: if (!bytes_to_write)
! 1049: break;
! 1050:
! 1051: bytes_written = write (zfpm_g->sock, STREAM_PNT (s), bytes_to_write);
! 1052: zfpm_g->stats.write_calls++;
! 1053: num_writes++;
! 1054:
! 1055: if (bytes_written < 0)
! 1056: {
! 1057: if (ERRNO_IO_RETRY (errno))
! 1058: break;
! 1059:
! 1060: zfpm_connection_down ("failed to write to socket");
! 1061: return 0;
! 1062: }
! 1063:
! 1064: if (bytes_written != bytes_to_write)
! 1065: {
! 1066:
! 1067: /*
! 1068: * Partial write.
! 1069: */
! 1070: stream_forward_getp (s, bytes_written);
! 1071: zfpm_g->stats.partial_writes++;
! 1072: break;
! 1073: }
! 1074:
! 1075: /*
! 1076: * We've written out the entire contents of the stream.
! 1077: */
! 1078: stream_reset (s);
! 1079:
! 1080: if (num_writes >= ZFPM_MAX_WRITES_PER_RUN)
! 1081: {
! 1082: zfpm_g->stats.max_writes_hit++;
! 1083: break;
! 1084: }
! 1085:
! 1086: if (zfpm_thread_should_yield (thread))
! 1087: {
! 1088: zfpm_g->stats.t_write_yields++;
! 1089: break;
! 1090: }
! 1091: } while (1);
! 1092:
! 1093: if (zfpm_writes_pending ())
! 1094: zfpm_write_on ();
! 1095:
! 1096: return 0;
! 1097: }
! 1098:
! 1099: /*
! 1100: * zfpm_connect_cb
! 1101: */
! 1102: static int
! 1103: zfpm_connect_cb (struct thread *t)
! 1104: {
! 1105: int sock, ret;
! 1106: struct sockaddr_in serv;
! 1107:
! 1108: assert (zfpm_g->t_connect);
! 1109: zfpm_g->t_connect = NULL;
! 1110: assert (zfpm_g->state == ZFPM_STATE_ACTIVE);
! 1111:
! 1112: sock = socket (AF_INET, SOCK_STREAM, 0);
! 1113: if (sock < 0)
! 1114: {
! 1115: zfpm_debug ("Failed to create socket for connect(): %s", strerror(errno));
! 1116: zfpm_g->stats.connect_no_sock++;
! 1117: return 0;
! 1118: }
! 1119:
! 1120: set_nonblocking(sock);
! 1121:
! 1122: /* Make server socket. */
! 1123: memset (&serv, 0, sizeof (serv));
! 1124: serv.sin_family = AF_INET;
! 1125: serv.sin_port = htons (zfpm_g->fpm_port);
! 1126: #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
! 1127: serv.sin_len = sizeof (struct sockaddr_in);
! 1128: #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
! 1129: serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
! 1130:
! 1131: /*
! 1132: * Connect to the FPM.
! 1133: */
! 1134: zfpm_g->connect_calls++;
! 1135: zfpm_g->stats.connect_calls++;
! 1136: zfpm_g->last_connect_call_time = zfpm_get_time ();
! 1137:
! 1138: ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv));
! 1139: if (ret >= 0)
! 1140: {
! 1141: zfpm_g->sock = sock;
! 1142: zfpm_connection_up ("connect succeeded");
! 1143: return 1;
! 1144: }
! 1145:
! 1146: if (errno == EINPROGRESS)
! 1147: {
! 1148: zfpm_g->sock = sock;
! 1149: zfpm_read_on ();
! 1150: zfpm_write_on ();
! 1151: zfpm_set_state (ZFPM_STATE_CONNECTING, "async connect in progress");
! 1152: return 0;
! 1153: }
! 1154:
! 1155: zlog_info ("can't connect to FPM %d: %s", sock, safe_strerror (errno));
! 1156: close (sock);
! 1157:
! 1158: /*
! 1159: * Restart timer for retrying connection.
! 1160: */
! 1161: zfpm_start_connect_timer ("connect() failed");
! 1162: return 0;
! 1163: }
! 1164:
! 1165: /*
! 1166: * zfpm_set_state
! 1167: *
! 1168: * Move state machine into the given state.
! 1169: */
! 1170: static void
! 1171: zfpm_set_state (zfpm_state_t state, const char *reason)
! 1172: {
! 1173: zfpm_state_t cur_state = zfpm_g->state;
! 1174:
! 1175: if (!reason)
! 1176: reason = "Unknown";
! 1177:
! 1178: if (state == cur_state)
! 1179: return;
! 1180:
! 1181: zfpm_debug("beginning state transition %s -> %s. Reason: %s",
! 1182: zfpm_state_to_str (cur_state), zfpm_state_to_str (state),
! 1183: reason);
! 1184:
! 1185: switch (state) {
! 1186:
! 1187: case ZFPM_STATE_IDLE:
! 1188: assert (cur_state == ZFPM_STATE_ESTABLISHED);
! 1189: break;
! 1190:
! 1191: case ZFPM_STATE_ACTIVE:
! 1192: assert (cur_state == ZFPM_STATE_IDLE ||
! 1193: cur_state == ZFPM_STATE_CONNECTING);
! 1194: assert (zfpm_g->t_connect);
! 1195: break;
! 1196:
! 1197: case ZFPM_STATE_CONNECTING:
! 1198: assert (zfpm_g->sock);
! 1199: assert (cur_state == ZFPM_STATE_ACTIVE);
! 1200: assert (zfpm_g->t_read);
! 1201: assert (zfpm_g->t_write);
! 1202: break;
! 1203:
! 1204: case ZFPM_STATE_ESTABLISHED:
! 1205: assert (cur_state == ZFPM_STATE_ACTIVE ||
! 1206: cur_state == ZFPM_STATE_CONNECTING);
! 1207: assert (zfpm_g->sock);
! 1208: assert (zfpm_g->t_read);
! 1209: assert (zfpm_g->t_write);
! 1210: break;
! 1211: }
! 1212:
! 1213: zfpm_g->state = state;
! 1214: }
! 1215:
! 1216: /*
! 1217: * zfpm_calc_connect_delay
! 1218: *
! 1219: * Returns the number of seconds after which we should attempt to
! 1220: * reconnect to the FPM.
! 1221: */
! 1222: static long
! 1223: zfpm_calc_connect_delay (void)
! 1224: {
! 1225: time_t elapsed;
! 1226:
! 1227: /*
! 1228: * Return 0 if this is our first attempt to connect.
! 1229: */
! 1230: if (zfpm_g->connect_calls == 0)
! 1231: {
! 1232: return 0;
! 1233: }
! 1234:
! 1235: elapsed = zfpm_get_elapsed_time (zfpm_g->last_connect_call_time);
! 1236:
! 1237: if (elapsed > ZFPM_CONNECT_RETRY_IVL) {
! 1238: return 0;
! 1239: }
! 1240:
! 1241: return ZFPM_CONNECT_RETRY_IVL - elapsed;
! 1242: }
! 1243:
! 1244: /*
! 1245: * zfpm_start_connect_timer
! 1246: */
! 1247: static void
! 1248: zfpm_start_connect_timer (const char *reason)
! 1249: {
! 1250: long delay_secs;
! 1251:
! 1252: assert (!zfpm_g->t_connect);
! 1253: assert (zfpm_g->sock < 0);
! 1254:
! 1255: assert(zfpm_g->state == ZFPM_STATE_IDLE ||
! 1256: zfpm_g->state == ZFPM_STATE_ACTIVE ||
! 1257: zfpm_g->state == ZFPM_STATE_CONNECTING);
! 1258:
! 1259: delay_secs = zfpm_calc_connect_delay();
! 1260: zfpm_debug ("scheduling connect in %ld seconds", delay_secs);
! 1261:
! 1262: THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_connect, zfpm_connect_cb, 0,
! 1263: delay_secs);
! 1264: zfpm_set_state (ZFPM_STATE_ACTIVE, reason);
! 1265: }
! 1266:
! 1267: /*
! 1268: * zfpm_is_enabled
! 1269: *
! 1270: * Returns TRUE if the zebra FPM module has been enabled.
! 1271: */
! 1272: static inline int
! 1273: zfpm_is_enabled (void)
! 1274: {
! 1275: return zfpm_g->enabled;
! 1276: }
! 1277:
! 1278: /*
! 1279: * zfpm_conn_is_up
! 1280: *
! 1281: * Returns TRUE if the connection to the FPM is up.
! 1282: */
! 1283: static inline int
! 1284: zfpm_conn_is_up (void)
! 1285: {
! 1286: if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
! 1287: return 0;
! 1288:
! 1289: assert (zfpm_g->sock >= 0);
! 1290:
! 1291: return 1;
! 1292: }
! 1293:
! 1294: /*
! 1295: * zfpm_trigger_update
! 1296: *
! 1297: * The zebra code invokes this function to indicate that we should
! 1298: * send an update to the FPM about the given route_node.
! 1299: */
! 1300: void
! 1301: zfpm_trigger_update (struct route_node *rn, const char *reason)
! 1302: {
! 1303: rib_dest_t *dest;
! 1304: char buf[INET6_ADDRSTRLEN];
! 1305:
! 1306: /*
! 1307: * Ignore if the connection is down. We will update the FPM about
! 1308: * all destinations once the connection comes up.
! 1309: */
! 1310: if (!zfpm_conn_is_up ())
! 1311: return;
! 1312:
! 1313: dest = rib_dest_from_rnode (rn);
! 1314:
! 1315: /*
! 1316: * Ignore the trigger if the dest is not in a table that we would
! 1317: * send to the FPM.
! 1318: */
! 1319: if (!zfpm_is_table_for_fpm (rib_dest_table (dest)))
! 1320: {
! 1321: zfpm_g->stats.non_fpm_table_triggers++;
! 1322: return;
! 1323: }
! 1324:
! 1325: if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) {
! 1326: zfpm_g->stats.redundant_triggers++;
! 1327: return;
! 1328: }
! 1329:
! 1330: if (reason)
! 1331: {
! 1332: zfpm_debug ("%s/%d triggering update to FPM - Reason: %s",
! 1333: inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)),
! 1334: rn->p.prefixlen, reason);
! 1335: }
! 1336:
! 1337: SET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
! 1338: TAILQ_INSERT_TAIL (&zfpm_g->dest_q, dest, fpm_q_entries);
! 1339: zfpm_g->stats.updates_triggered++;
! 1340:
! 1341: /*
! 1342: * Make sure that writes are enabled.
! 1343: */
! 1344: if (zfpm_g->t_write)
! 1345: return;
! 1346:
! 1347: zfpm_write_on ();
! 1348: }
! 1349:
! 1350: /*
! 1351: * zfpm_stats_timer_cb
! 1352: */
! 1353: static int
! 1354: zfpm_stats_timer_cb (struct thread *t)
! 1355: {
! 1356: assert (zfpm_g->t_stats);
! 1357: zfpm_g->t_stats = NULL;
! 1358:
! 1359: /*
! 1360: * Remember the stats collected in the last interval for display
! 1361: * purposes.
! 1362: */
! 1363: zfpm_stats_copy (&zfpm_g->stats, &zfpm_g->last_ivl_stats);
! 1364:
! 1365: /*
! 1366: * Add the current set of stats into the cumulative statistics.
! 1367: */
! 1368: zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
! 1369: &zfpm_g->cumulative_stats);
! 1370:
! 1371: /*
! 1372: * Start collecting stats afresh over the next interval.
! 1373: */
! 1374: zfpm_stats_reset (&zfpm_g->stats);
! 1375:
! 1376: zfpm_start_stats_timer ();
! 1377:
! 1378: return 0;
! 1379: }
! 1380:
! 1381: /*
! 1382: * zfpm_stop_stats_timer
! 1383: */
! 1384: static void
! 1385: zfpm_stop_stats_timer (void)
! 1386: {
! 1387: if (!zfpm_g->t_stats)
! 1388: return;
! 1389:
! 1390: zfpm_debug ("Stopping existing stats timer");
! 1391: THREAD_TIMER_OFF (zfpm_g->t_stats);
! 1392: }
! 1393:
! 1394: /*
! 1395: * zfpm_start_stats_timer
! 1396: */
! 1397: void
! 1398: zfpm_start_stats_timer (void)
! 1399: {
! 1400: assert (!zfpm_g->t_stats);
! 1401:
! 1402: THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_stats, zfpm_stats_timer_cb, 0,
! 1403: ZFPM_STATS_IVL_SECS);
! 1404: }
! 1405:
! 1406: /*
! 1407: * Helper macro for zfpm_show_stats() below.
! 1408: */
! 1409: #define ZFPM_SHOW_STAT(counter) \
! 1410: do { \
! 1411: vty_out (vty, "%-40s %10lu %16lu%s", #counter, total_stats.counter, \
! 1412: zfpm_g->last_ivl_stats.counter, VTY_NEWLINE); \
! 1413: } while (0)
! 1414:
! 1415: /*
! 1416: * zfpm_show_stats
! 1417: */
! 1418: static void
! 1419: zfpm_show_stats (struct vty *vty)
! 1420: {
! 1421: zfpm_stats_t total_stats;
! 1422: time_t elapsed;
! 1423:
! 1424: vty_out (vty, "%s%-40s %10s Last %2d secs%s%s", VTY_NEWLINE, "Counter",
! 1425: "Total", ZFPM_STATS_IVL_SECS, VTY_NEWLINE, VTY_NEWLINE);
! 1426:
! 1427: /*
! 1428: * Compute the total stats up to this instant.
! 1429: */
! 1430: zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
! 1431: &total_stats);
! 1432:
! 1433: ZFPM_SHOW_STAT (connect_calls);
! 1434: ZFPM_SHOW_STAT (connect_no_sock);
! 1435: ZFPM_SHOW_STAT (read_cb_calls);
! 1436: ZFPM_SHOW_STAT (write_cb_calls);
! 1437: ZFPM_SHOW_STAT (write_calls);
! 1438: ZFPM_SHOW_STAT (partial_writes);
! 1439: ZFPM_SHOW_STAT (max_writes_hit);
! 1440: ZFPM_SHOW_STAT (t_write_yields);
! 1441: ZFPM_SHOW_STAT (nop_deletes_skipped);
! 1442: ZFPM_SHOW_STAT (route_adds);
! 1443: ZFPM_SHOW_STAT (route_dels);
! 1444: ZFPM_SHOW_STAT (updates_triggered);
! 1445: ZFPM_SHOW_STAT (non_fpm_table_triggers);
! 1446: ZFPM_SHOW_STAT (redundant_triggers);
! 1447: ZFPM_SHOW_STAT (dests_del_after_update);
! 1448: ZFPM_SHOW_STAT (t_conn_down_starts);
! 1449: ZFPM_SHOW_STAT (t_conn_down_dests_processed);
! 1450: ZFPM_SHOW_STAT (t_conn_down_yields);
! 1451: ZFPM_SHOW_STAT (t_conn_down_finishes);
! 1452: ZFPM_SHOW_STAT (t_conn_up_starts);
! 1453: ZFPM_SHOW_STAT (t_conn_up_dests_processed);
! 1454: ZFPM_SHOW_STAT (t_conn_up_yields);
! 1455: ZFPM_SHOW_STAT (t_conn_up_aborts);
! 1456: ZFPM_SHOW_STAT (t_conn_up_finishes);
! 1457:
! 1458: if (!zfpm_g->last_stats_clear_time)
! 1459: return;
! 1460:
! 1461: elapsed = zfpm_get_elapsed_time (zfpm_g->last_stats_clear_time);
! 1462:
! 1463: vty_out (vty, "%sStats were cleared %lu seconds ago%s", VTY_NEWLINE,
! 1464: (unsigned long) elapsed, VTY_NEWLINE);
! 1465: }
! 1466:
! 1467: /*
! 1468: * zfpm_clear_stats
! 1469: */
! 1470: static void
! 1471: zfpm_clear_stats (struct vty *vty)
! 1472: {
! 1473: if (!zfpm_is_enabled ())
! 1474: {
! 1475: vty_out (vty, "The FPM module is not enabled...%s", VTY_NEWLINE);
! 1476: return;
! 1477: }
! 1478:
! 1479: zfpm_stats_reset (&zfpm_g->stats);
! 1480: zfpm_stats_reset (&zfpm_g->last_ivl_stats);
! 1481: zfpm_stats_reset (&zfpm_g->cumulative_stats);
! 1482:
! 1483: zfpm_stop_stats_timer ();
! 1484: zfpm_start_stats_timer ();
! 1485:
! 1486: zfpm_g->last_stats_clear_time = zfpm_get_time();
! 1487:
! 1488: vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE);
! 1489: }
! 1490:
! 1491: /*
! 1492: * show_zebra_fpm_stats
! 1493: */
! 1494: DEFUN (show_zebra_fpm_stats,
! 1495: show_zebra_fpm_stats_cmd,
! 1496: "show zebra fpm stats",
! 1497: SHOW_STR
! 1498: "Zebra information\n"
! 1499: "Forwarding Path Manager information\n"
! 1500: "Statistics\n")
! 1501: {
! 1502: zfpm_show_stats (vty);
! 1503: return CMD_SUCCESS;
! 1504: }
! 1505:
! 1506: /*
! 1507: * clear_zebra_fpm_stats
! 1508: */
! 1509: DEFUN (clear_zebra_fpm_stats,
! 1510: clear_zebra_fpm_stats_cmd,
! 1511: "clear zebra fpm stats",
! 1512: CLEAR_STR
! 1513: "Zebra information\n"
! 1514: "Clear Forwarding Path Manager information\n"
! 1515: "Statistics\n")
! 1516: {
! 1517: zfpm_clear_stats (vty);
! 1518: return CMD_SUCCESS;
! 1519: }
! 1520:
! 1521: /**
! 1522: * zfpm_init
! 1523: *
! 1524: * One-time initialization of the Zebra FPM module.
! 1525: *
! 1526: * @param[in] port port at which FPM is running.
! 1527: * @param[in] enable TRUE if the zebra FPM module should be enabled
! 1528: *
! 1529: * Returns TRUE on success.
! 1530: */
! 1531: int
! 1532: zfpm_init (struct thread_master *master, int enable, uint16_t port)
! 1533: {
! 1534: static int initialized = 0;
! 1535:
! 1536: if (initialized) {
! 1537: return 1;
! 1538: }
! 1539:
! 1540: initialized = 1;
! 1541:
! 1542: memset (zfpm_g, 0, sizeof (*zfpm_g));
! 1543: zfpm_g->master = master;
! 1544: TAILQ_INIT(&zfpm_g->dest_q);
! 1545: zfpm_g->sock = -1;
! 1546: zfpm_g->state = ZFPM_STATE_IDLE;
! 1547:
! 1548: /*
! 1549: * Netlink must currently be available for the Zebra-FPM interface
! 1550: * to be enabled.
! 1551: */
! 1552: #ifndef HAVE_NETLINK
! 1553: enable = 0;
! 1554: #endif
! 1555:
! 1556: zfpm_g->enabled = enable;
! 1557:
! 1558: zfpm_stats_init (&zfpm_g->stats);
! 1559: zfpm_stats_init (&zfpm_g->last_ivl_stats);
! 1560: zfpm_stats_init (&zfpm_g->cumulative_stats);
! 1561:
! 1562: install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd);
! 1563: install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd);
! 1564:
! 1565: if (!enable) {
! 1566: return 1;
! 1567: }
! 1568:
! 1569: if (!port)
! 1570: port = FPM_DEFAULT_PORT;
! 1571:
! 1572: zfpm_g->fpm_port = port;
! 1573:
! 1574: zfpm_g->obuf = stream_new (ZFPM_OBUF_SIZE);
! 1575: zfpm_g->ibuf = stream_new (ZFPM_IBUF_SIZE);
! 1576:
! 1577: zfpm_start_stats_timer ();
! 1578: zfpm_start_connect_timer ("initialized");
! 1579:
! 1580: return 1;
! 1581: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>