LCOV - code coverage report
Current view: top level - zebra - zebra_fpm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 128 490 26.1 %
Date: 2015-11-19 Functions: 17 43 39.5 %

          Line data    Source code
       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           0 : zfpm_thread_should_yield (struct thread *t)
     251             : {
     252           0 :   return thread_should_yield (t);
     253             : }
     254             : 
     255             : /*
     256             :  * zfpm_state_to_str
     257             :  */
     258             : static const char *
     259           0 : zfpm_state_to_str (zfpm_state_t state)
     260             : {
     261           0 :   switch (state)
     262             :     {
     263             : 
     264             :     case ZFPM_STATE_IDLE:
     265           0 :       return "idle";
     266             : 
     267             :     case ZFPM_STATE_ACTIVE:
     268           0 :       return "active";
     269             : 
     270             :     case ZFPM_STATE_CONNECTING:
     271           0 :       return "connecting";
     272             : 
     273             :     case ZFPM_STATE_ESTABLISHED:
     274           0 :       return "established";
     275             : 
     276             :     default:
     277           0 :       return "unknown";
     278             :     }
     279             : }
     280             : 
     281             : /*
     282             :  * zfpm_get_time
     283             :  */
     284             : static time_t
     285          90 : zfpm_get_time (void)
     286             : {
     287             :   struct timeval tv;
     288             : 
     289          90 :   if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0)
     290           0 :     zlog_warn ("FPM: quagga_gettime failed!!");
     291             : 
     292          90 :   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          45 : zfpm_get_elapsed_time (time_t reference)
     302             : {
     303             :   time_t now;
     304             : 
     305          45 :   now = zfpm_get_time ();
     306             : 
     307          45 :   if (now < reference)
     308             :     {
     309           0 :       assert (0);
     310             :       return 0;
     311             :     }
     312             : 
     313          45 :   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           0 : zfpm_is_table_for_fpm (struct route_table *table)
     324             : {
     325             :   rib_table_info_t *info;
     326             : 
     327           0 :   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           0 :   if (info->vrf->id != 0)
     334           0 :     return 0;
     335             : 
     336           0 :   if (info->safi != SAFI_UNICAST)
     337           0 :     return 0;
     338             : 
     339           0 :   return 1;
     340             : }
     341             : 
     342             : /*
     343             :  * zfpm_rnodes_iter_init
     344             :  */
     345             : static inline void
     346           0 : zfpm_rnodes_iter_init (zfpm_rnodes_iter_t *iter)
     347             : {
     348           0 :   memset (iter, 0, sizeof (*iter));
     349           0 :   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           0 :   route_table_iter_init (&iter->iter, NULL);
     357           0 :   route_table_iter_cleanup (&iter->iter);
     358           0 : }
     359             : 
     360             : /*
     361             :  * zfpm_rnodes_iter_next
     362             :  */
     363             : static inline struct route_node *
     364           0 : 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           0 :       rn = route_table_iter_next (&iter->iter);
     372           0 :       if (rn)
     373           0 :         return rn;
     374             : 
     375             :       /*
     376             :        * We've made our way through this table, go to the next one.
     377             :        */
     378           0 :       route_table_iter_cleanup (&iter->iter);
     379             : 
     380           0 :       while ((table = rib_tables_iter_next (&iter->tables_iter)))
     381             :         {
     382           0 :           if (zfpm_is_table_for_fpm (table))
     383           0 :             break;
     384             :         }
     385             : 
     386           0 :       if (!table)
     387           0 :         return NULL;
     388             : 
     389           0 :       route_table_iter_init (&iter->iter, table);
     390           0 :     }
     391             : 
     392             :   return NULL;
     393             : }
     394             : 
     395             : /*
     396             :  * zfpm_rnodes_iter_pause
     397             :  */
     398             : static inline void
     399           0 : zfpm_rnodes_iter_pause (zfpm_rnodes_iter_t *iter)
     400             : {
     401           0 :   route_table_iter_pause (&iter->iter);
     402           0 : }
     403             : 
     404             : /*
     405             :  * zfpm_rnodes_iter_cleanup
     406             :  */
     407             : static inline void
     408           0 : zfpm_rnodes_iter_cleanup (zfpm_rnodes_iter_t *iter)
     409             : {
     410           0 :   route_table_iter_cleanup (&iter->iter);
     411           0 :   rib_tables_iter_cleanup (&iter->tables_iter);
     412           0 : }
     413             : 
     414             : /*
     415             :  * zfpm_stats_init
     416             :  *
     417             :  * Initialize a statistics block.
     418             :  */
     419             : static inline void
     420         135 : zfpm_stats_init (zfpm_stats_t *stats)
     421             : {
     422         135 :   memset (stats, 0, sizeof (*stats));
     423         135 : }
     424             : 
     425             : /*
     426             :  * zfpm_stats_reset
     427             :  */
     428             : static inline void
     429           0 : zfpm_stats_reset (zfpm_stats_t *stats)
     430             : {
     431           0 :   zfpm_stats_init (stats);
     432           0 : }
     433             : 
     434             : /*
     435             :  * zfpm_stats_copy
     436             :  */
     437             : static inline void
     438           0 : zfpm_stats_copy (const zfpm_stats_t *src, zfpm_stats_t *dest)
     439             : {
     440           0 :   memcpy (dest, src, sizeof (*dest));
     441           0 : }
     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           0 : 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           0 :   p1 = (const unsigned long *) s1;
     463           0 :   p2 = (const unsigned long *) s2;
     464           0 :   result_p = (unsigned long *) result;
     465             : 
     466           0 :   num_counters = (sizeof (zfpm_stats_t) / sizeof (unsigned long));
     467             : 
     468           0 :   for (i = 0; i < num_counters; i++)
     469             :     {
     470           0 :       result_p[i] = p1[i] + p2[i];
     471             :     }
     472           0 : }
     473             : 
     474             : /*
     475             :  * zfpm_read_on
     476             :  */
     477             : static inline void
     478          45 : zfpm_read_on (void)
     479             : {
     480          45 :   assert (!zfpm_g->t_read);
     481          45 :   assert (zfpm_g->sock >= 0);
     482             : 
     483          45 :   THREAD_READ_ON (zfpm_g->master, zfpm_g->t_read, zfpm_read_cb, 0,
     484             :                   zfpm_g->sock);
     485          45 : }
     486             : 
     487             : /*
     488             :  * zfpm_write_on
     489             :  */
     490             : static inline void
     491          45 : zfpm_write_on (void)
     492             : {
     493          45 :   assert (!zfpm_g->t_write);
     494          45 :   assert (zfpm_g->sock >= 0);
     495             : 
     496          45 :   THREAD_WRITE_ON (zfpm_g->master, zfpm_g->t_write, zfpm_write_cb, 0,
     497             :                    zfpm_g->sock);
     498          45 : }
     499             : 
     500             : /*
     501             :  * zfpm_read_off
     502             :  */
     503             : static inline void
     504          45 : zfpm_read_off (void)
     505             : {
     506          45 :   THREAD_READ_OFF (zfpm_g->t_read);
     507          45 : }
     508             : 
     509             : /*
     510             :  * zfpm_write_off
     511             :  */
     512             : static inline void
     513          45 : zfpm_write_off (void)
     514             : {
     515          45 :   THREAD_WRITE_OFF (zfpm_g->t_write);
     516          45 : }
     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           0 : 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           0 :   assert (zfpm_g->t_conn_up);
     532           0 :   zfpm_g->t_conn_up = NULL;
     533             : 
     534           0 :   iter = &zfpm_g->t_conn_up_state.iter;
     535             : 
     536           0 :   if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
     537             :     {
     538           0 :       zfpm_debug ("Connection not up anymore, conn_up thread aborting");
     539           0 :       zfpm_g->stats.t_conn_up_aborts++;
     540           0 :       goto done;
     541             :     }
     542             : 
     543           0 :   while ((rnode = zfpm_rnodes_iter_next (iter)))
     544             :     {
     545           0 :       dest = rib_dest_from_rnode (rnode);
     546             : 
     547           0 :       if (dest)
     548             :         {
     549           0 :           zfpm_g->stats.t_conn_up_dests_processed++;
     550           0 :           zfpm_trigger_update (rnode, NULL);
     551             :         }
     552             : 
     553             :       /*
     554             :        * Yield if need be.
     555             :        */
     556           0 :       if (!zfpm_thread_should_yield (thread))
     557           0 :         continue;
     558             : 
     559           0 :       zfpm_g->stats.t_conn_up_yields++;
     560           0 :       zfpm_rnodes_iter_pause (iter);
     561           0 :       zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
     562             :                                                  zfpm_conn_up_thread_cb,
     563             :                                                  0, 0);
     564           0 :       return 0;
     565             :     }
     566             : 
     567           0 :   zfpm_g->stats.t_conn_up_finishes++;
     568             : 
     569             :  done:
     570           0 :   zfpm_rnodes_iter_cleanup (iter);
     571           0 :   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           0 : zfpm_connection_up (const char *detail)
     581             : {
     582           0 :   assert (zfpm_g->sock >= 0);
     583           0 :   zfpm_read_on ();
     584           0 :   zfpm_write_on ();
     585           0 :   zfpm_set_state (ZFPM_STATE_ESTABLISHED, detail);
     586             : 
     587             :   /*
     588             :    * Start thread to push existing routes to the FPM.
     589             :    */
     590           0 :   assert (!zfpm_g->t_conn_up);
     591             : 
     592           0 :   zfpm_rnodes_iter_init (&zfpm_g->t_conn_up_state.iter);
     593             : 
     594           0 :   zfpm_debug ("Starting conn_up thread");
     595           0 :   zfpm_g->t_conn_up = thread_add_background (zfpm_g->master,
     596             :                                              zfpm_conn_up_thread_cb, 0, 0);
     597           0 :   zfpm_g->stats.t_conn_up_starts++;
     598           0 : }
     599             : 
     600             : /*
     601             :  * zfpm_connect_check
     602             :  *
     603             :  * Check if an asynchronous connect() to the FPM is complete.
     604             :  */
     605             : static void
     606          45 : zfpm_connect_check ()
     607             : {
     608             :   int status;
     609             :   socklen_t slen;
     610             :   int ret;
     611             : 
     612          45 :   zfpm_read_off ();
     613          45 :   zfpm_write_off ();
     614             : 
     615          45 :   slen = sizeof (status);
     616          45 :   ret = getsockopt (zfpm_g->sock, SOL_SOCKET, SO_ERROR, (void *) &status,
     617             :                     &slen);
     618             : 
     619          45 :   if (ret >= 0 && status == 0)
     620             :     {
     621           0 :       zfpm_connection_up ("async connect complete");
     622           0 :       return;
     623             :     }
     624             : 
     625             :   /*
     626             :    * getsockopt() failed or indicated an error on the socket.
     627             :    */
     628          45 :   close (zfpm_g->sock);
     629          45 :   zfpm_g->sock = -1;
     630             : 
     631          45 :   zfpm_start_connect_timer ("getsockopt() after async connect failed");
     632          45 :   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           0 : 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           0 :   assert (zfpm_g->state == ZFPM_STATE_IDLE);
     649             : 
     650           0 :   assert (zfpm_g->t_conn_down);
     651           0 :   zfpm_g->t_conn_down = NULL;
     652             : 
     653           0 :   iter = &zfpm_g->t_conn_down_state.iter;
     654             : 
     655           0 :   while ((rnode = zfpm_rnodes_iter_next (iter)))
     656             :     {
     657           0 :       dest = rib_dest_from_rnode (rnode);
     658             : 
     659           0 :       if (dest)
     660             :         {
     661           0 :           if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM))
     662             :             {
     663           0 :               TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
     664             :             }
     665             : 
     666           0 :           UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
     667           0 :           UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
     668             : 
     669           0 :           zfpm_g->stats.t_conn_down_dests_processed++;
     670             : 
     671             :           /*
     672             :            * Check if the dest should be deleted.
     673             :            */
     674           0 :           rib_gc_dest(rnode);
     675             :         }
     676             : 
     677             :       /*
     678             :        * Yield if need be.
     679             :        */
     680           0 :       if (!zfpm_thread_should_yield (thread))
     681           0 :         continue;
     682             : 
     683           0 :       zfpm_g->stats.t_conn_down_yields++;
     684           0 :       zfpm_rnodes_iter_pause (iter);
     685           0 :       zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
     686             :                                                    zfpm_conn_down_thread_cb,
     687             :                                                    0, 0);
     688           0 :       return 0;
     689             :     }
     690             : 
     691           0 :   zfpm_g->stats.t_conn_down_finishes++;
     692           0 :   zfpm_rnodes_iter_cleanup (iter);
     693             : 
     694             :   /*
     695             :    * Start the process of connecting to the FPM again.
     696             :    */
     697           0 :   zfpm_start_connect_timer ("cleanup complete");
     698           0 :   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           0 : zfpm_connection_down (const char *detail)
     708             : {
     709           0 :   if (!detail)
     710           0 :     detail = "unknown";
     711             : 
     712           0 :   assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
     713             : 
     714           0 :   zlog_info ("connection to the FPM has gone down: %s", detail);
     715             : 
     716           0 :   zfpm_read_off ();
     717           0 :   zfpm_write_off ();
     718             : 
     719           0 :   stream_reset (zfpm_g->ibuf);
     720           0 :   stream_reset (zfpm_g->obuf);
     721             : 
     722           0 :   if (zfpm_g->sock >= 0) {
     723           0 :     close (zfpm_g->sock);
     724           0 :     zfpm_g->sock = -1;
     725             :   }
     726             : 
     727             :   /*
     728             :    * Start thread to clean up state after the connection goes down.
     729             :    */
     730           0 :   assert (!zfpm_g->t_conn_down);
     731           0 :   zfpm_debug ("Starting conn_down thread");
     732           0 :   zfpm_rnodes_iter_init (&zfpm_g->t_conn_down_state.iter);
     733           0 :   zfpm_g->t_conn_down = thread_add_background (zfpm_g->master,
     734             :                                                zfpm_conn_down_thread_cb, 0, 0);
     735           0 :   zfpm_g->stats.t_conn_down_starts++;
     736             : 
     737           0 :   zfpm_set_state (ZFPM_STATE_IDLE, detail);
     738           0 : }
     739             : 
     740             : /*
     741             :  * zfpm_read_cb
     742             :  */
     743             : static int
     744          45 : 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          45 :   zfpm_g->stats.read_cb_calls++;
     752          45 :   assert (zfpm_g->t_read);
     753          45 :   zfpm_g->t_read = NULL;
     754             : 
     755             :   /*
     756             :    * Check if async connect is now done.
     757             :    */
     758          45 :   if (zfpm_g->state == ZFPM_STATE_CONNECTING)
     759             :     {
     760          45 :       zfpm_connect_check();
     761          45 :       return 0;
     762             :     }
     763             : 
     764           0 :   assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
     765           0 :   assert (zfpm_g->sock >= 0);
     766             : 
     767           0 :   ibuf = zfpm_g->ibuf;
     768             : 
     769           0 :   already = stream_get_endp (ibuf);
     770           0 :   if (already < FPM_MSG_HDR_LEN)
     771             :     {
     772             :       ssize_t nbyte;
     773             : 
     774           0 :       nbyte = stream_read_try (ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already);
     775           0 :       if (nbyte == 0 || nbyte == -1)
     776             :         {
     777           0 :           zfpm_connection_down ("closed socket in read");
     778           0 :           return 0;
     779             :         }
     780             : 
     781           0 :       if (nbyte != (ssize_t) (FPM_MSG_HDR_LEN - already))
     782           0 :         goto done;
     783             : 
     784           0 :       already = FPM_MSG_HDR_LEN;
     785             :     }
     786             : 
     787           0 :   stream_set_getp (ibuf, 0);
     788             : 
     789           0 :   hdr = (fpm_msg_hdr_t *) stream_pnt (ibuf);
     790             : 
     791           0 :   if (!fpm_msg_hdr_ok (hdr))
     792             :     {
     793           0 :       zfpm_connection_down ("invalid message header");
     794           0 :       return 0;
     795             :     }
     796             : 
     797           0 :   msg_len = fpm_msg_len (hdr);
     798             : 
     799             :   /*
     800             :    * Read out the rest of the packet.
     801             :    */
     802           0 :   if (already < msg_len)
     803             :     {
     804             :       ssize_t nbyte;
     805             : 
     806           0 :       nbyte = stream_read_try (ibuf, zfpm_g->sock, msg_len - already);
     807             : 
     808           0 :       if (nbyte == 0 || nbyte == -1)
     809             :         {
     810           0 :           zfpm_connection_down ("failed to read message");
     811           0 :           return 0;
     812             :         }
     813             : 
     814           0 :       if (nbyte != (ssize_t) (msg_len - already))
     815           0 :         goto done;
     816             :     }
     817             : 
     818           0 :   zfpm_debug ("Read out a full fpm message");
     819             : 
     820             :   /*
     821             :    * Just throw it away for now.
     822             :    */
     823           0 :   stream_reset (ibuf);
     824             : 
     825             :  done:
     826           0 :   zfpm_read_on ();
     827           0 :   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           0 : 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           0 :   if (stream_get_endp (zfpm_g->obuf) - stream_get_getp (zfpm_g->obuf))
     844           0 :     return 1;
     845             : 
     846             :   /*
     847             :    * Check if there are any prefixes on the outbound queue.
     848             :    */
     849           0 :   if (!TAILQ_EMPTY (&zfpm_g->dest_q))
     850           0 :     return 1;
     851             : 
     852           0 :   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           0 : 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           0 :   cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE;
     874             : 
     875           0 :   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           0 : zfpm_route_for_update (rib_dest_t *dest)
     887             : {
     888             :   struct rib *rib;
     889             : 
     890           0 :   RIB_DEST_FOREACH_ROUTE (dest, rib)
     891             :     {
     892           0 :       if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
     893           0 :         continue;
     894             : 
     895           0 :       return rib;
     896             :     }
     897             : 
     898             :   /*
     899             :    * We have no route for this destination.
     900             :    */
     901           0 :   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           0 : 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           0 :   s = zfpm_g->obuf;
     923             : 
     924           0 :   assert (stream_empty (s));
     925             : 
     926             :   do {
     927             : 
     928             :     /*
     929             :      * Make sure there is enough space to write another message.
     930             :      */
     931           0 :     if (STREAM_WRITEABLE (s) < FPM_MAX_MSG_LEN)
     932           0 :       break;
     933             : 
     934           0 :     buf = STREAM_DATA (s) + stream_get_endp (s);
     935           0 :     buf_end = buf + STREAM_WRITEABLE (s);
     936             : 
     937           0 :     dest = TAILQ_FIRST (&zfpm_g->dest_q);
     938           0 :     if (!dest)
     939           0 :       break;
     940             : 
     941           0 :     assert (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM));
     942             : 
     943           0 :     hdr = (fpm_msg_hdr_t *) buf;
     944           0 :     hdr->version = FPM_PROTO_VERSION;
     945           0 :     hdr->msg_type = FPM_MSG_TYPE_NETLINK;
     946             : 
     947           0 :     data = fpm_msg_data (hdr);
     948             : 
     949           0 :     rib = zfpm_route_for_update (dest);
     950           0 :     is_add = rib ? 1 : 0;
     951             : 
     952           0 :     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           0 :     if (!is_add && !CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
     959             :       {
     960           0 :         write_msg = 0;
     961           0 :         zfpm_g->stats.nop_deletes_skipped++;
     962             :       }
     963             : 
     964           0 :     if (write_msg) {
     965           0 :       data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data);
     966             : 
     967           0 :       assert (data_len);
     968           0 :       if (data_len)
     969             :         {
     970           0 :           msg_len = fpm_data_len_to_msg_len (data_len);
     971           0 :           hdr->msg_len = htons (msg_len);
     972           0 :           stream_forward_endp (s, msg_len);
     973             : 
     974           0 :           if (is_add)
     975           0 :             zfpm_g->stats.route_adds++;
     976             :           else
     977           0 :             zfpm_g->stats.route_dels++;
     978             :         }
     979             :     }
     980             : 
     981             :     /*
     982             :      * Remove the dest from the queue, and reset the flag.
     983             :      */
     984           0 :     UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
     985           0 :     TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries);
     986             : 
     987           0 :     if (is_add)
     988             :       {
     989           0 :         SET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
     990             :       }
     991             :     else
     992             :       {
     993           0 :         UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM);
     994             :       }
     995             : 
     996             :     /*
     997             :      * Delete the destination if necessary.
     998             :      */
     999           0 :     if (rib_gc_dest (dest->rnode))
    1000           0 :       zfpm_g->stats.dests_del_after_update++;
    1001             : 
    1002           0 :   } while (1);
    1003             : 
    1004           0 : }
    1005             : 
    1006             : /*
    1007             :  * zfpm_write_cb
    1008             :  */
    1009             : static int
    1010           0 : zfpm_write_cb (struct thread *thread)
    1011             : {
    1012             :   struct stream *s;
    1013             :   int num_writes;
    1014             : 
    1015           0 :   zfpm_g->stats.write_cb_calls++;
    1016           0 :   assert (zfpm_g->t_write);
    1017           0 :   zfpm_g->t_write = NULL;
    1018             : 
    1019             :   /*
    1020             :    * Check if async connect is now done.
    1021             :    */
    1022           0 :   if (zfpm_g->state == ZFPM_STATE_CONNECTING)
    1023             :     {
    1024           0 :       zfpm_connect_check ();
    1025           0 :       return 0;
    1026             :     }
    1027             : 
    1028           0 :   assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED);
    1029           0 :   assert (zfpm_g->sock >= 0);
    1030             : 
    1031           0 :   num_writes = 0;
    1032             : 
    1033             :   do
    1034             :     {
    1035             :       int bytes_to_write, bytes_written;
    1036             : 
    1037           0 :       s = zfpm_g->obuf;
    1038             : 
    1039             :       /*
    1040             :        * If the stream is empty, try fill it up with data.
    1041             :        */
    1042           0 :       if (stream_empty (s))
    1043             :         {
    1044           0 :           zfpm_build_updates ();
    1045             :         }
    1046             : 
    1047           0 :       bytes_to_write = stream_get_endp (s) - stream_get_getp (s);
    1048           0 :       if (!bytes_to_write)
    1049           0 :         break;
    1050             : 
    1051           0 :       bytes_written = write (zfpm_g->sock, STREAM_PNT (s), bytes_to_write);
    1052           0 :       zfpm_g->stats.write_calls++;
    1053           0 :       num_writes++;
    1054             : 
    1055           0 :       if (bytes_written < 0)
    1056             :         {
    1057           0 :           if (ERRNO_IO_RETRY (errno))
    1058             :             break;
    1059             : 
    1060           0 :           zfpm_connection_down ("failed to write to socket");
    1061           0 :           return 0;
    1062             :         }
    1063             : 
    1064           0 :       if (bytes_written != bytes_to_write)
    1065             :         {
    1066             : 
    1067             :           /*
    1068             :            * Partial write.
    1069             :            */
    1070           0 :           stream_forward_getp (s, bytes_written);
    1071           0 :           zfpm_g->stats.partial_writes++;
    1072           0 :           break;
    1073             :         }
    1074             : 
    1075             :       /*
    1076             :        * We've written out the entire contents of the stream.
    1077             :        */
    1078           0 :       stream_reset (s);
    1079             : 
    1080           0 :       if (num_writes >= ZFPM_MAX_WRITES_PER_RUN)
    1081             :         {
    1082           0 :           zfpm_g->stats.max_writes_hit++;
    1083           0 :           break;
    1084             :         }
    1085             : 
    1086           0 :       if (zfpm_thread_should_yield (thread))
    1087             :         {
    1088           0 :           zfpm_g->stats.t_write_yields++;
    1089           0 :           break;
    1090             :         }
    1091           0 :     } while (1);
    1092             : 
    1093           0 :   if (zfpm_writes_pending ())
    1094           0 :       zfpm_write_on ();
    1095             : 
    1096           0 :   return 0;
    1097             : }
    1098             : 
    1099             : /*
    1100             :  * zfpm_connect_cb
    1101             :  */
    1102             : static int
    1103          45 : zfpm_connect_cb (struct thread *t)
    1104             : {
    1105             :   int sock, ret;
    1106             :   struct sockaddr_in serv;
    1107             : 
    1108          45 :   assert (zfpm_g->t_connect);
    1109          45 :   zfpm_g->t_connect = NULL;
    1110          45 :   assert (zfpm_g->state == ZFPM_STATE_ACTIVE);
    1111             : 
    1112          45 :   sock = socket (AF_INET, SOCK_STREAM, 0);
    1113          45 :   if (sock < 0)
    1114             :     {
    1115           0 :       zfpm_debug ("Failed to create socket for connect(): %s", strerror(errno));
    1116           0 :       zfpm_g->stats.connect_no_sock++;
    1117           0 :       return 0;
    1118             :     }
    1119             : 
    1120          45 :   set_nonblocking(sock);
    1121             : 
    1122             :   /* Make server socket. */
    1123          45 :   memset (&serv, 0, sizeof (serv));
    1124          45 :   serv.sin_family = AF_INET;
    1125          45 :   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          45 :   serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    1130             : 
    1131             :   /*
    1132             :    * Connect to the FPM.
    1133             :    */
    1134          45 :   zfpm_g->connect_calls++;
    1135          45 :   zfpm_g->stats.connect_calls++;
    1136          45 :   zfpm_g->last_connect_call_time = zfpm_get_time ();
    1137             : 
    1138          45 :   ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv));
    1139          45 :   if (ret >= 0)
    1140             :     {
    1141           0 :       zfpm_g->sock = sock;
    1142           0 :       zfpm_connection_up ("connect succeeded");
    1143           0 :       return 1;
    1144             :     }
    1145             : 
    1146          45 :   if (errno == EINPROGRESS)
    1147             :     {
    1148          45 :       zfpm_g->sock = sock;
    1149          45 :       zfpm_read_on ();
    1150          45 :       zfpm_write_on ();
    1151          45 :       zfpm_set_state (ZFPM_STATE_CONNECTING, "async connect in progress");
    1152          45 :       return 0;
    1153             :     }
    1154             : 
    1155           0 :   zlog_info ("can't connect to FPM %d: %s", sock, safe_strerror (errno));
    1156           0 :   close (sock);
    1157             : 
    1158             :   /*
    1159             :    * Restart timer for retrying connection.
    1160             :    */
    1161           0 :   zfpm_start_connect_timer ("connect() failed");
    1162           0 :   return 0;
    1163             : }
    1164             : 
    1165             : /*
    1166             :  * zfpm_set_state
    1167             :  *
    1168             :  * Move state machine into the given state.
    1169             :  */
    1170             : static void
    1171         135 : zfpm_set_state (zfpm_state_t state, const char *reason)
    1172             : {
    1173         135 :   zfpm_state_t cur_state = zfpm_g->state;
    1174             : 
    1175         135 :   if (!reason)
    1176           0 :     reason = "Unknown";
    1177             : 
    1178         135 :   if (state == cur_state)
    1179           0 :     return;
    1180             : 
    1181         135 :   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         135 :   switch (state) {
    1186             : 
    1187             :   case ZFPM_STATE_IDLE:
    1188           0 :     assert (cur_state == ZFPM_STATE_ESTABLISHED);
    1189           0 :     break;
    1190             : 
    1191             :   case ZFPM_STATE_ACTIVE:
    1192          90 :      assert (cur_state == ZFPM_STATE_IDLE ||
    1193             :              cur_state == ZFPM_STATE_CONNECTING);
    1194          90 :     assert (zfpm_g->t_connect);
    1195          90 :     break;
    1196             : 
    1197             :   case ZFPM_STATE_CONNECTING:
    1198          45 :     assert (zfpm_g->sock);
    1199          45 :     assert (cur_state == ZFPM_STATE_ACTIVE);
    1200          45 :     assert (zfpm_g->t_read);
    1201          45 :     assert (zfpm_g->t_write);
    1202          45 :     break;
    1203             : 
    1204             :   case ZFPM_STATE_ESTABLISHED:
    1205           0 :     assert (cur_state == ZFPM_STATE_ACTIVE ||
    1206             :             cur_state == ZFPM_STATE_CONNECTING);
    1207           0 :     assert (zfpm_g->sock);
    1208           0 :     assert (zfpm_g->t_read);
    1209           0 :     assert (zfpm_g->t_write);
    1210           0 :     break;
    1211             :   }
    1212             : 
    1213         135 :   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          90 : 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          90 :   if (zfpm_g->connect_calls == 0)
    1231             :     {
    1232          45 :       return 0;
    1233             :     }
    1234             : 
    1235          45 :   elapsed = zfpm_get_elapsed_time (zfpm_g->last_connect_call_time);
    1236             : 
    1237          45 :   if (elapsed > ZFPM_CONNECT_RETRY_IVL) {
    1238           0 :     return 0;
    1239             :   }
    1240             : 
    1241          45 :   return ZFPM_CONNECT_RETRY_IVL - elapsed;
    1242             : }
    1243             : 
    1244             : /*
    1245             :  * zfpm_start_connect_timer
    1246             :  */
    1247             : static void
    1248          90 : zfpm_start_connect_timer (const char *reason)
    1249             : {
    1250             :   long delay_secs;
    1251             : 
    1252          90 :   assert (!zfpm_g->t_connect);
    1253          90 :   assert (zfpm_g->sock < 0);
    1254             : 
    1255          90 :   assert(zfpm_g->state == ZFPM_STATE_IDLE ||
    1256             :          zfpm_g->state == ZFPM_STATE_ACTIVE ||
    1257             :          zfpm_g->state == ZFPM_STATE_CONNECTING);
    1258             : 
    1259          90 :   delay_secs = zfpm_calc_connect_delay();
    1260          90 :   zfpm_debug ("scheduling connect in %ld seconds", delay_secs);
    1261             : 
    1262          90 :   THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_connect, zfpm_connect_cb, 0,
    1263             :                    delay_secs);
    1264          90 :   zfpm_set_state (ZFPM_STATE_ACTIVE, reason);
    1265          90 : }
    1266             : 
    1267             : /*
    1268             :  * zfpm_is_enabled
    1269             :  *
    1270             :  * Returns TRUE if the zebra FPM module has been enabled.
    1271             :  */
    1272             : static inline int
    1273           0 : zfpm_is_enabled (void)
    1274             : {
    1275           0 :   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         925 : zfpm_conn_is_up (void)
    1285             : {
    1286         925 :   if (zfpm_g->state != ZFPM_STATE_ESTABLISHED)
    1287         925 :     return 0;
    1288             : 
    1289           0 :   assert (zfpm_g->sock >= 0);
    1290             : 
    1291           0 :   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         925 : 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         925 :   if (!zfpm_conn_is_up ())
    1311        1850 :     return;
    1312             : 
    1313           0 :   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           0 :   if (!zfpm_is_table_for_fpm (rib_dest_table (dest)))
    1320             :     {
    1321           0 :       zfpm_g->stats.non_fpm_table_triggers++;
    1322           0 :       return;
    1323             :     }
    1324             : 
    1325           0 :   if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) {
    1326           0 :     zfpm_g->stats.redundant_triggers++;
    1327           0 :     return;
    1328             :   }
    1329             : 
    1330           0 :   if (reason)
    1331             :     {
    1332           0 :       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           0 :   SET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM);
    1338           0 :   TAILQ_INSERT_TAIL (&zfpm_g->dest_q, dest, fpm_q_entries);
    1339           0 :   zfpm_g->stats.updates_triggered++;
    1340             : 
    1341             :   /*
    1342             :    * Make sure that writes are enabled.
    1343             :    */
    1344           0 :   if (zfpm_g->t_write)
    1345           0 :     return;
    1346             : 
    1347           0 :   zfpm_write_on ();
    1348             : }
    1349             : 
    1350             : /*
    1351             :  * zfpm_stats_timer_cb
    1352             :  */
    1353             : static int
    1354           0 : zfpm_stats_timer_cb (struct thread *t)
    1355             : {
    1356           0 :   assert (zfpm_g->t_stats);
    1357           0 :   zfpm_g->t_stats = NULL;
    1358             : 
    1359             :   /*
    1360             :    * Remember the stats collected in the last interval for display
    1361             :    * purposes.
    1362             :    */
    1363           0 :   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           0 :   zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
    1369           0 :                       &zfpm_g->cumulative_stats);
    1370             : 
    1371             :   /*
    1372             :    * Start collecting stats afresh over the next interval.
    1373             :    */
    1374           0 :   zfpm_stats_reset (&zfpm_g->stats);
    1375             : 
    1376           0 :   zfpm_start_stats_timer ();
    1377             : 
    1378           0 :   return 0;
    1379             : }
    1380             : 
    1381             : /*
    1382             :  * zfpm_stop_stats_timer
    1383             :  */
    1384             : static void
    1385           0 : zfpm_stop_stats_timer (void)
    1386             : {
    1387           0 :   if (!zfpm_g->t_stats)
    1388           0 :     return;
    1389             : 
    1390           0 :   zfpm_debug ("Stopping existing stats timer");
    1391           0 :   THREAD_TIMER_OFF (zfpm_g->t_stats);
    1392             : }
    1393             : 
    1394             : /*
    1395             :  * zfpm_start_stats_timer
    1396             :  */
    1397             : void
    1398          45 : zfpm_start_stats_timer (void)
    1399             : {
    1400          45 :   assert (!zfpm_g->t_stats);
    1401             : 
    1402          45 :   THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_stats, zfpm_stats_timer_cb, 0,
    1403             :                    ZFPM_STATS_IVL_SECS);
    1404          45 : }
    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           0 : zfpm_show_stats (struct vty *vty)
    1420             : {
    1421             :   zfpm_stats_t total_stats;
    1422             :   time_t elapsed;
    1423             : 
    1424           0 :   vty_out (vty, "%s%-40s %10s     Last %2d secs%s%s", VTY_NEWLINE, "Counter",
    1425           0 :            "Total", ZFPM_STATS_IVL_SECS, VTY_NEWLINE, VTY_NEWLINE);
    1426             : 
    1427             :   /*
    1428             :    * Compute the total stats up to this instant.
    1429             :    */
    1430           0 :   zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats,
    1431             :                       &total_stats);
    1432             : 
    1433           0 :   ZFPM_SHOW_STAT (connect_calls);
    1434           0 :   ZFPM_SHOW_STAT (connect_no_sock);
    1435           0 :   ZFPM_SHOW_STAT (read_cb_calls);
    1436           0 :   ZFPM_SHOW_STAT (write_cb_calls);
    1437           0 :   ZFPM_SHOW_STAT (write_calls);
    1438           0 :   ZFPM_SHOW_STAT (partial_writes);
    1439           0 :   ZFPM_SHOW_STAT (max_writes_hit);
    1440           0 :   ZFPM_SHOW_STAT (t_write_yields);
    1441           0 :   ZFPM_SHOW_STAT (nop_deletes_skipped);
    1442           0 :   ZFPM_SHOW_STAT (route_adds);
    1443           0 :   ZFPM_SHOW_STAT (route_dels);
    1444           0 :   ZFPM_SHOW_STAT (updates_triggered);
    1445           0 :   ZFPM_SHOW_STAT (non_fpm_table_triggers);
    1446           0 :   ZFPM_SHOW_STAT (redundant_triggers);
    1447           0 :   ZFPM_SHOW_STAT (dests_del_after_update);
    1448           0 :   ZFPM_SHOW_STAT (t_conn_down_starts);
    1449           0 :   ZFPM_SHOW_STAT (t_conn_down_dests_processed);
    1450           0 :   ZFPM_SHOW_STAT (t_conn_down_yields);
    1451           0 :   ZFPM_SHOW_STAT (t_conn_down_finishes);
    1452           0 :   ZFPM_SHOW_STAT (t_conn_up_starts);
    1453           0 :   ZFPM_SHOW_STAT (t_conn_up_dests_processed);
    1454           0 :   ZFPM_SHOW_STAT (t_conn_up_yields);
    1455           0 :   ZFPM_SHOW_STAT (t_conn_up_aborts);
    1456           0 :   ZFPM_SHOW_STAT (t_conn_up_finishes);
    1457             : 
    1458           0 :   if (!zfpm_g->last_stats_clear_time)
    1459           0 :     return;
    1460             : 
    1461           0 :   elapsed = zfpm_get_elapsed_time (zfpm_g->last_stats_clear_time);
    1462             : 
    1463           0 :   vty_out (vty, "%sStats were cleared %lu seconds ago%s", VTY_NEWLINE,
    1464           0 :            (unsigned long) elapsed, VTY_NEWLINE);
    1465             : }
    1466             : 
    1467             : /*
    1468             :  * zfpm_clear_stats
    1469             :  */
    1470             : static void
    1471           0 : zfpm_clear_stats (struct vty *vty)
    1472             : {
    1473           0 :   if (!zfpm_is_enabled ())
    1474             :     {
    1475           0 :       vty_out (vty, "The FPM module is not enabled...%s", VTY_NEWLINE);
    1476           0 :       return;
    1477             :     }
    1478             : 
    1479           0 :   zfpm_stats_reset (&zfpm_g->stats);
    1480           0 :   zfpm_stats_reset (&zfpm_g->last_ivl_stats);
    1481           0 :   zfpm_stats_reset (&zfpm_g->cumulative_stats);
    1482             : 
    1483           0 :   zfpm_stop_stats_timer ();
    1484           0 :   zfpm_start_stats_timer ();
    1485             : 
    1486           0 :   zfpm_g->last_stats_clear_time = zfpm_get_time();
    1487             : 
    1488           0 :   vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE);
    1489             : }
    1490             : 
    1491             : /*
    1492             :  * show_zebra_fpm_stats
    1493             :  */
    1494           0 : 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           0 :   zfpm_show_stats (vty);
    1503           0 :   return CMD_SUCCESS;
    1504             : }
    1505             : 
    1506             : /*
    1507             :  * clear_zebra_fpm_stats
    1508             :  */
    1509           0 : 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           0 :   zfpm_clear_stats (vty);
    1518           0 :   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          45 : zfpm_init (struct thread_master *master, int enable, uint16_t port)
    1533             : {
    1534             :   static int initialized = 0;
    1535             : 
    1536          45 :   if (initialized) {
    1537           0 :     return 1;
    1538             :   }
    1539             : 
    1540          45 :   initialized = 1;
    1541             : 
    1542          45 :   memset (zfpm_g, 0, sizeof (*zfpm_g));
    1543          45 :   zfpm_g->master = master;
    1544          45 :   TAILQ_INIT(&zfpm_g->dest_q);
    1545          45 :   zfpm_g->sock = -1;
    1546          45 :   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          45 :   zfpm_g->enabled = enable;
    1557             : 
    1558          45 :   zfpm_stats_init (&zfpm_g->stats);
    1559          45 :   zfpm_stats_init (&zfpm_g->last_ivl_stats);
    1560          45 :   zfpm_stats_init (&zfpm_g->cumulative_stats);
    1561             : 
    1562          45 :   install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd);
    1563          45 :   install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd);
    1564             : 
    1565          45 :   if (!enable) {
    1566           0 :     return 1;
    1567             :   }
    1568             : 
    1569          45 :   if (!port)
    1570          45 :     port = FPM_DEFAULT_PORT;
    1571             : 
    1572          45 :   zfpm_g->fpm_port = port;
    1573             : 
    1574          45 :   zfpm_g->obuf = stream_new (ZFPM_OBUF_SIZE);
    1575          45 :   zfpm_g->ibuf = stream_new (ZFPM_IBUF_SIZE);
    1576             : 
    1577          45 :   zfpm_start_stats_timer ();
    1578          45 :   zfpm_start_connect_timer ("initialized");
    1579             : 
    1580          45 :   return 1;
    1581             : }

Generated by: LCOV version 1.10