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 : }
|