Add adjacency counters to the stats segment
[vpp.git] / src / vnet / adj / adj.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/adj/adj.h>
17 #include <vnet/adj/adj_internal.h>
18 #include <vnet/adj/adj_glean.h>
19 #include <vnet/adj/adj_midchain.h>
20 #include <vnet/adj/adj_mcast.h>
21 #include <vnet/adj/adj_delegate.h>
22 #include <vnet/fib/fib_node_list.h>
23
24 /* Adjacency packet/byte counters indexed by adjacency index. */
25 vlib_combined_counter_main_t adjacency_counters = {
26     .name = "adjacency",
27     .stat_segment_name = "/net/adjacency",
28 };
29
30 /*
31  * the single adj pool
32  */
33 ip_adjacency_t *adj_pool;
34
35 /**
36  * @brief Global Config for enabling per-adjacency counters.
37  * By default these are disabled.
38  */
39 int adj_per_adj_counters;
40
41 const ip46_address_t ADJ_BCAST_ADDR = {
42     .ip6 = {
43         .as_u64[0] = 0xffffffffffffffff,
44         .as_u64[1] = 0xffffffffffffffff,
45     },
46 };
47
48 always_inline void
49 adj_poison (ip_adjacency_t * adj)
50 {
51     if (CLIB_DEBUG > 0)
52     {
53         memset (adj, 0xfe, sizeof (adj[0]));
54     }
55 }
56
57 ip_adjacency_t *
58 adj_alloc (fib_protocol_t proto)
59 {
60     ip_adjacency_t *adj;
61
62     pool_get_aligned(adj_pool, adj, CLIB_CACHE_LINE_BYTES);
63
64     adj_poison(adj);
65
66     /* Make sure certain fields are always initialized. */
67     /* Validate adjacency counters. */
68     vlib_validate_combined_counter(&adjacency_counters,
69                                    adj_get_index(adj));
70     vlib_zero_combined_counter(&adjacency_counters,
71                                adj_get_index(adj));
72     fib_node_init(&adj->ia_node,
73                   FIB_NODE_TYPE_ADJ);
74
75     adj->ia_nh_proto = proto;
76     adj->ia_flags = 0;
77     adj->rewrite_header.sw_if_index = ~0;
78     adj->rewrite_header.flags = 0;
79     adj->lookup_next_index = 0;
80     adj->ia_delegates = NULL;
81
82     /* lest it become a midchain in the future */
83     memset(&adj->sub_type.midchain.next_dpo, 0,
84            sizeof(adj->sub_type.midchain.next_dpo));
85
86     return (adj);
87 }
88
89 static int
90 adj_index_is_special (adj_index_t adj_index)
91 {
92     if (ADJ_INDEX_INVALID == adj_index)
93         return (!0);
94
95     return (0);
96 }
97
98 /**
99  * @brief Pretty print helper function for formatting specific adjacencies.
100  * @param s - input string to format
101  * @param args - other args passed to format function such as:
102  *                 - vnet_main_t
103  *                 - ip_lookup_main_t
104  *                 - adj_index
105  */
106 u8 *
107 format_ip_adjacency (u8 * s, va_list * args)
108 {
109     format_ip_adjacency_flags_t fiaf;
110     ip_adjacency_t * adj;
111     u32 adj_index;
112
113     adj_index = va_arg (*args, u32);
114     fiaf = va_arg (*args, format_ip_adjacency_flags_t);
115     adj = adj_get(adj_index);
116   
117     switch (adj->lookup_next_index)
118     {
119     case IP_LOOKUP_NEXT_REWRITE:
120         s = format (s, "%U", format_adj_nbr, adj_index, 0);
121         break;
122     case IP_LOOKUP_NEXT_ARP:
123         s = format (s, "%U", format_adj_nbr_incomplete, adj_index, 0);
124         break;
125     case IP_LOOKUP_NEXT_GLEAN:
126         s = format (s, "%U", format_adj_glean, adj_index, 0);
127         break;
128     case IP_LOOKUP_NEXT_MIDCHAIN:
129         s = format (s, "%U", format_adj_midchain, adj_index, 2);
130         break;
131     case IP_LOOKUP_NEXT_MCAST:
132         s = format (s, "%U", format_adj_mcast, adj_index, 0);
133         break;
134     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
135         s = format (s, "%U", format_adj_mcast_midchain, adj_index, 0);
136         break;
137     default:
138         break;
139     }
140
141     if (fiaf & FORMAT_IP_ADJACENCY_DETAIL)
142     {
143         vlib_counter_t counts;
144
145         vlib_get_combined_counter(&adjacency_counters, adj_index, &counts);
146         s = format (s, "\n   counts:[%Ld:%Ld]", counts.packets, counts.bytes);
147         s = format (s, "\n   locks:%d", adj->ia_node.fn_locks);
148         s = format(s, "\n delegates:\n  ");
149         adj_delegate_format(s, adj);
150
151         s = format(s, "\n children:");
152         if (fib_node_list_get_size(adj->ia_node.fn_children))
153         {
154             s = format(s, "\n  ");
155             s = fib_node_children_format(adj->ia_node.fn_children, s);
156         }
157     }
158
159     return s;
160 }
161
162 /*
163  * adj_last_lock_gone
164  *
165  * last lock/reference to the adj has gone, we no longer need it.
166  */
167 static void
168 adj_last_lock_gone (ip_adjacency_t *adj)
169 {
170     vlib_main_t * vm = vlib_get_main();
171
172     ASSERT(0 == fib_node_list_get_size(adj->ia_node.fn_children));
173     ADJ_DBG(adj, "last-lock-gone");
174
175     adj_delegate_adj_deleted(adj);
176
177     vlib_worker_thread_barrier_sync (vm);
178
179     switch (adj->lookup_next_index)
180     {
181     case IP_LOOKUP_NEXT_MIDCHAIN:
182         dpo_reset(&adj->sub_type.midchain.next_dpo);
183         /* FALL THROUGH */
184     case IP_LOOKUP_NEXT_ARP:
185     case IP_LOOKUP_NEXT_REWRITE:
186     case IP_LOOKUP_NEXT_BCAST:
187         /*
188          * complete and incomplete nbr adjs
189          */
190         adj_nbr_remove(adj_get_index(adj),
191                        adj->ia_nh_proto,
192                        adj->ia_link,
193                        &adj->sub_type.nbr.next_hop,
194                        adj->rewrite_header.sw_if_index);
195         break;
196     case IP_LOOKUP_NEXT_GLEAN:
197         adj_glean_remove(adj->ia_nh_proto,
198                          adj->rewrite_header.sw_if_index);
199         break;
200     case IP_LOOKUP_NEXT_MCAST:
201     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
202         adj_mcast_remove(adj->ia_nh_proto,
203                          adj->rewrite_header.sw_if_index);
204         break;
205     case IP_LOOKUP_NEXT_DROP:
206     case IP_LOOKUP_NEXT_PUNT:
207     case IP_LOOKUP_NEXT_LOCAL:
208     case IP_LOOKUP_NEXT_ICMP_ERROR:
209     case IP_LOOKUP_N_NEXT:
210         /*
211          * type not stored in any DB from which we need to remove it
212          */
213         break;
214     }
215
216     vlib_worker_thread_barrier_release(vm);
217
218     fib_node_deinit(&adj->ia_node);
219     ASSERT(0 == vec_len(adj->ia_delegates));
220     vec_free(adj->ia_delegates);
221     pool_put(adj_pool, adj);
222 }
223
224 u32
225 adj_dpo_get_urpf (const dpo_id_t *dpo)
226 {
227     ip_adjacency_t *adj;
228
229     adj = adj_get(dpo->dpoi_index);
230
231     return (adj->rewrite_header.sw_if_index);
232 }
233
234 void
235 adj_lock (adj_index_t adj_index)
236 {
237     ip_adjacency_t *adj;
238
239     if (adj_index_is_special(adj_index))
240     {
241         return;
242     }
243
244     adj = adj_get(adj_index);
245     ASSERT(adj);
246
247     ADJ_DBG(adj, "lock");
248     fib_node_lock(&adj->ia_node);
249 }
250
251 void
252 adj_unlock (adj_index_t adj_index)
253 {
254     ip_adjacency_t *adj;
255
256     if (adj_index_is_special(adj_index))
257     {
258         return;
259     }
260
261     adj = adj_get(adj_index);
262     ASSERT(adj);
263
264     ADJ_DBG(adj, "unlock");
265     ASSERT(adj);
266
267     fib_node_unlock(&adj->ia_node);
268 }
269
270 u32
271 adj_child_add (adj_index_t adj_index,
272                fib_node_type_t child_type,
273                fib_node_index_t child_index)
274 {
275     ASSERT(ADJ_INDEX_INVALID != adj_index);
276     if (adj_index_is_special(adj_index))
277     {
278         return (~0);
279     }
280
281     return (fib_node_child_add(FIB_NODE_TYPE_ADJ,
282                                adj_index,
283                                child_type,
284                                child_index));
285 }
286
287 void
288 adj_child_remove (adj_index_t adj_index,
289                   u32 sibling_index)
290 {
291     if (adj_index_is_special(adj_index))
292     {
293         return;
294     }
295
296     fib_node_child_remove(FIB_NODE_TYPE_ADJ,
297                           adj_index,
298                           sibling_index);
299 }
300
301 /*
302  * Context for the walk to update the cached feture flags.
303  */
304 typedef struct adj_feature_update_t_
305 {
306     u8 arc;
307     u8 enable;
308 } adj_feature_update_ctx_t;
309
310 static adj_walk_rc_t
311 adj_feature_update_walk_cb (adj_index_t ai,
312                             void *arg)
313 {
314     adj_feature_update_ctx_t *ctx = arg;
315     ip_adjacency_t *adj;
316
317     adj = adj_get(ai);
318
319     /*
320      * this ugly mess matches the feature arc that is changing with affected
321      * adjacencies
322      */
323     if (((ctx->arc == ip6_main.lookup_main.output_feature_arc_index) &&
324          (VNET_LINK_IP6 == adj->ia_link)) ||
325         ((ctx->arc == ip4_main.lookup_main.output_feature_arc_index) &&
326          (VNET_LINK_IP4 == adj->ia_link)) ||
327         ((ctx->arc == mpls_main.output_feature_arc_index) &&
328          (VNET_LINK_MPLS == adj->ia_link)))
329     {
330         if (ctx->enable)
331             adj->rewrite_header.flags |= VNET_REWRITE_HAS_FEATURES;
332         else
333             adj->rewrite_header.flags &= ~VNET_REWRITE_HAS_FEATURES;
334     }
335     return (ADJ_WALK_RC_CONTINUE);
336 }
337
338 void
339 adj_feature_update (u32 sw_if_index,
340                     u8 arc_index,
341                     u8 is_enable)
342 {
343     /*
344      * Walk all the adjacencies on the interface to update the cached
345      * 'has-features' flag
346      */
347     adj_feature_update_ctx_t ctx = {
348         .arc = arc_index,
349         .enable = is_enable,
350     };
351     adj_walk (sw_if_index, adj_feature_update_walk_cb, &ctx);
352 }
353
354 static adj_walk_rc_t
355 adj_mtu_update_walk_cb (adj_index_t ai,
356                         void *arg)
357 {
358     ip_adjacency_t *adj;
359
360     adj = adj_get(ai);
361
362     vnet_rewrite_update_mtu (vnet_get_main(), adj->ia_link,
363                              &adj->rewrite_header);
364
365     return (ADJ_WALK_RC_CONTINUE);
366 }
367
368 static void
369 adj_mtu_update (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
370 {
371   adj_walk (sw_if_index, adj_mtu_update_walk_cb, NULL);
372 }
373
374 VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION(adj_mtu_update);
375
376 /**
377  * @brief Walk the Adjacencies on a given interface
378  */
379 void
380 adj_walk (u32 sw_if_index,
381           adj_walk_cb_t cb,
382           void *ctx)
383 {
384     /*
385      * walk all the neighbor adjacencies
386      */
387     fib_protocol_t proto;
388
389     FOR_EACH_FIB_IP_PROTOCOL(proto)
390     {
391         adj_nbr_walk(sw_if_index, proto, cb, ctx);
392         adj_mcast_walk(sw_if_index, proto, cb, ctx);
393     }
394 }
395
396 /**
397  * @brief Return the link type of the adjacency
398  */
399 vnet_link_t
400 adj_get_link_type (adj_index_t ai)
401 {
402     const ip_adjacency_t *adj;
403
404     adj = adj_get(ai);
405
406     return (adj->ia_link); 
407 }
408
409 /**
410  * @brief Return the sw interface index of the adjacency.
411  */
412 u32
413 adj_get_sw_if_index (adj_index_t ai)
414 {
415     const ip_adjacency_t *adj;
416
417     adj = adj_get(ai);
418
419     return (adj->rewrite_header.sw_if_index);
420 }
421
422 /**
423  * @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding
424  * 0 is down, !0 is up.
425  */
426 int
427 adj_is_up (adj_index_t ai)
428 {
429     return (adj_bfd_is_up(ai));
430 }
431
432 /**
433  * @brief Return the rewrite string of the adjacency
434  */
435 const u8*
436 adj_get_rewrite (adj_index_t ai)
437 {
438     vnet_rewrite_header_t *rw;
439     ip_adjacency_t *adj;
440
441     adj = adj_get(ai);
442     rw = &adj->rewrite_header;
443
444     ASSERT (rw->data_bytes != 0xfefe);
445
446     return (rw->data - rw->data_bytes);
447 }
448
449 static fib_node_t *
450 adj_get_node (fib_node_index_t index)
451 {
452     ip_adjacency_t *adj;
453
454     adj = adj_get(index);
455
456     return (&adj->ia_node);
457 }
458
459 #define ADJ_FROM_NODE(_node)                                            \
460     ((ip_adjacency_t*)((char*)_node - STRUCT_OFFSET_OF(ip_adjacency_t, ia_node)))
461
462 static void
463 adj_node_last_lock_gone (fib_node_t *node)
464 {
465     adj_last_lock_gone(ADJ_FROM_NODE(node));
466 }
467
468 static fib_node_back_walk_rc_t
469 adj_back_walk_notify (fib_node_t *node,
470                       fib_node_back_walk_ctx_t *ctx)
471 {
472     /*
473      * Que pasa. yo soj en el final!
474      */
475     ASSERT(0);
476
477     return (FIB_NODE_BACK_WALK_CONTINUE);
478 }
479
480 /*
481  * Adjacency's graph node virtual function table
482  */
483 static const fib_node_vft_t adj_vft = {
484     .fnv_get = adj_get_node,
485     .fnv_last_lock = adj_node_last_lock_gone,
486     .fnv_back_walk = adj_back_walk_notify,
487 };
488
489 static clib_error_t *
490 adj_module_init (vlib_main_t * vm)
491 {
492     fib_node_register_type(FIB_NODE_TYPE_ADJ, &adj_vft);
493
494     adj_nbr_module_init();
495     adj_glean_module_init();
496     adj_midchain_module_init();
497     adj_mcast_module_init();
498
499     return (NULL);
500 }
501
502 VLIB_INIT_FUNCTION (adj_module_init);
503
504 static clib_error_t *
505 adj_show (vlib_main_t * vm,
506           unformat_input_t * input,
507           vlib_cli_command_t * cmd)
508 {
509     adj_index_t ai = ADJ_INDEX_INVALID;
510     u32 sw_if_index = ~0;
511     int summary = 0;
512
513     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
514     {
515         if (unformat (input, "%d", &ai))
516             ;
517         else if (unformat (input, "sum"))
518             summary = 1;
519         else if (unformat (input, "summary"))
520             summary = 1;
521         else if (unformat (input, "%U",
522                            unformat_vnet_sw_interface, vnet_get_main(),
523                            &sw_if_index))
524             ;
525         else
526             break;
527     }
528
529     if (summary)
530     {
531         vlib_cli_output (vm, "Number of adjacencies: %d", pool_elts(adj_pool));
532         vlib_cli_output (vm, "Per-adjacency counters: %s",
533                          (adj_are_counters_enabled() ?
534                           "enabled":
535                           "disabled"));
536     }
537     else
538     {
539         if (ADJ_INDEX_INVALID != ai)
540         {
541             if (pool_is_free_index(adj_pool, ai))
542             {
543                 vlib_cli_output (vm, "adjacency %d invalid", ai);
544                 return 0;
545             }
546
547             vlib_cli_output (vm, "[@%d] %U",
548                              ai,
549                              format_ip_adjacency,  ai,
550                              FORMAT_IP_ADJACENCY_DETAIL);
551         }
552         else
553         {
554             /* *INDENT-OFF* */
555             pool_foreach_index(ai, adj_pool,
556             ({
557                 if (~0 != sw_if_index &&
558                     sw_if_index != adj_get_sw_if_index(ai))
559                 {
560                 }
561                 else
562                 {
563                     vlib_cli_output (vm, "[@%d] %U",
564                                      ai,
565                                      format_ip_adjacency, ai,
566                                      FORMAT_IP_ADJACENCY_NONE);
567                 }
568             }));
569             /* *INDENT-ON* */
570         }
571     }
572     return 0;
573 }
574
575 /*?
576  * Show all adjacencies.
577  * @cliexpar
578  * @cliexstart{sh adj}
579  * [@0]
580  * [@1]  glean: loop0
581  * [@2] ipv4 via 1.0.0.2 loop0: IP4: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
582  * [@3] mpls via 1.0.0.2 loop0: MPLS: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
583  * [@4] ipv4 via 1.0.0.3 loop0: IP4: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
584  * [@5] mpls via 1.0.0.3 loop0: MPLS: 00:00:22:aa:bb:cc -> 00:00:11:aa:bb:cc
585  * @cliexend
586  ?*/
587 VLIB_CLI_COMMAND (adj_show_command, static) = {
588     .path = "show adj",
589     .short_help = "show adj [<adj_index>] [interface] [summary]",
590     .function = adj_show,
591 };
592
593 /**
594  * @brief CLI invoked function to enable/disable per-adj counters
595  */
596 static clib_error_t *
597 adj_cli_counters_set (vlib_main_t * vm,
598                       unformat_input_t * input,
599                       vlib_cli_command_t * cmd)
600 {
601     clib_error_t *error = NULL;
602     int enable = ~0;
603
604     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
605     {
606         if (unformat (input, "enable"))
607             enable = 1;
608         else if (unformat (input, "disable"))
609             enable = 0;
610         else
611             break;
612     }
613
614     if (enable != ~0)
615     {
616         /* user requested something sensible */
617         adj_per_adj_counters = enable;
618     }
619     else
620     {
621         error = clib_error_return (0, "specify 'enable' or 'disable'");
622     }
623
624     return (error);
625 }
626
627 /*?
628  * Enabe/disble per-adjacency counters. This is optional because it comes with
629  * a non-negligible performance cost.
630  ?*/
631 VLIB_CLI_COMMAND (adj_cli_counters_set_command, static) = {
632     .path = "adjacency counters",
633     .short_help = "adjacency counters [enable|disable]",
634     .function = adj_cli_counters_set,
635 };