feature: recover "show ip features" command
[vpp.git] / vnet / vnet / feature / registration.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/vnet.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/mpls/mpls.h>
19
20 /**
21  * @file
22  * @brief Feature Subgraph Ordering.
23
24     Dynamically compute feature subgraph ordering by performing a
25     topological sort across a set of "feature A before feature B" and
26     "feature C after feature B" constraints.
27
28     Use the topological sort result to set up vnet_config_main_t's for
29     use at runtime.
30
31     Feature subgraph arcs are simple enough. They start at specific
32     fixed nodes, and end at specific fixed nodes.  In between, a
33     per-interface current feature configuration dictates which
34     additional nodes each packet visits. Each so-called feature node
35     can [of course] drop any specific packet.
36
37     See ip4_forward.c, ip6_forward.c in this directory to see the
38     current rx-unicast, rx-multicast, and tx feature subgraph arc
39     definitions.
40
41     Let's say that we wish to add a new feature to the ip4 unicast
42     feature subgraph arc, which needs to run before @c ip4-lookup.  In
43     either base code or a plugin,
44     <CODE><PRE>
45     \#include <vnet/feature/feature.h>
46     </PRE></CODE>
47
48     and add the new feature as shown:
49
50     <CODE><PRE>
51     VNET_FEATURE_INIT (ip4_lookup, static) =
52     {
53       .arch_name = "ip4-unicast",
54       .node_name = "my-ip4-unicast-feature",
55       .runs_before = VLIB_FEATURES ("ip4-lookup", 0)
56     };
57     </PRE></CODE>
58
59     Here's the standard coding pattern to enable / disable
60     @c my-ip4-unicast-feature on an interface:
61
62     <CODE><PRE>
63
64     sw_if_index = <interface-handle>
65     vnet_feature_enable_disable ("ip4-unicast", "my-ip4-unicast-feature",
66                                  sw_if_index, 1 );
67     </PRE></CODE>
68
69     Here's how to obtain the correct next node index in packet
70     processing code, aka in the implementation of @c my-ip4-unicast-feature:
71
72     <CODE><PRE>
73     vnet_feature_main_t *fm = &feature_main;
74     vnet_feature_config_main_t * cm = &fm->feature_config_mains[VNET_FEAT_IP4_UNICAST];
75
76     Call @c vnet_get_config_data to set next0, and to advance
77     @c b0->current_config_index:
78
79     config_data0 = vnet_get_config_data (&cm->config_main,
80                                          &b0->current_config_index,
81                                          &next0,
82                                          0 / * sizeof config data * /);
83     </PRE></CODE>
84
85     Nodes are free to drop or otherwise redirect packets. Packets
86     which "pass" should be enqueued via the next0 arc computed by
87     vnet_get_config_data.
88 */
89
90 static const char *vnet_cast_names[] = VNET_CAST_NAMES;
91
92 static int
93 comma_split (u8 * s, u8 ** a, u8 ** b)
94 {
95   *a = s;
96
97   while (*s && *s != ',')
98     s++;
99
100   if (*s == ',')
101     *s = 0;
102   else
103     return 1;
104
105   *b = (u8 *) (s + 1);
106   return 0;
107 }
108
109 /**
110  * @brief Initialize a feature graph arc
111  * @param vm vlib main structure pointer
112  * @param vcm vnet config main structure pointer
113  * @param feature_start_nodes names of start-nodes which use this
114  *        feature graph arc
115  * @param num_feature_start_nodes number of start-nodes
116  * @param first_reg first element in
117  *        [an __attribute__((constructor)) function built, or
118  *        otherwise created] singly-linked list of feature registrations
119  * @param [out] in_feature_nodes returned vector of
120  *        topologically-sorted feature node names, for use in
121  *        show commands
122  * @returns 0 on success, otherwise an error message. Errors
123  *        are fatal since they invariably involve mistyped node-names, or
124  *        genuinely missing node-names
125  */
126 clib_error_t *
127 vnet_feature_arc_init (vlib_main_t * vm,
128                        vnet_config_main_t * vcm,
129                        char **feature_start_nodes,
130                        int num_feature_start_nodes,
131                        vnet_feature_registration_t * first_reg,
132                        char ***in_feature_nodes)
133 {
134   uword *index_by_name;
135   uword *reg_by_index;
136   u8 **node_names = 0;
137   u8 *node_name;
138   char **these_constraints;
139   char *this_constraint_c;
140   u8 **constraints = 0;
141   u8 *constraint_tuple;
142   u8 *this_constraint;
143   u8 **orig, **closure;
144   uword *p;
145   int i, j, k;
146   u8 *a_name, *b_name;
147   int a_index, b_index;
148   int n_features;
149   u32 *result = 0;
150   vnet_feature_registration_t *this_reg = 0;
151   char **feature_nodes = 0;
152   hash_pair_t *hp;
153   u8 **keys_to_delete = 0;
154
155   index_by_name = hash_create_string (0, sizeof (uword));
156   reg_by_index = hash_create (0, sizeof (uword));
157
158   this_reg = first_reg;
159
160   /* pass 1, collect feature node names, construct a before b pairs */
161   while (this_reg)
162     {
163       node_name = format (0, "%s%c", this_reg->node_name, 0);
164       hash_set (reg_by_index, vec_len (node_names), (uword) this_reg);
165
166       hash_set_mem (index_by_name, node_name, vec_len (node_names));
167
168       vec_add1 (node_names, node_name);
169
170       these_constraints = this_reg->runs_before;
171       while (these_constraints && these_constraints[0])
172         {
173           this_constraint_c = these_constraints[0];
174
175           constraint_tuple = format (0, "%s,%s%c", node_name,
176                                      this_constraint_c, 0);
177           vec_add1 (constraints, constraint_tuple);
178           these_constraints++;
179         }
180
181       these_constraints = this_reg->runs_after;
182       while (these_constraints && these_constraints[0])
183         {
184           this_constraint_c = these_constraints[0];
185
186           constraint_tuple = format (0, "%s,%s%c",
187                                      this_constraint_c, node_name, 0);
188           vec_add1 (constraints, constraint_tuple);
189           these_constraints++;
190         }
191
192       this_reg = this_reg->next;
193     }
194
195   n_features = vec_len (node_names);
196   orig = clib_ptclosure_alloc (n_features);
197
198   for (i = 0; i < vec_len (constraints); i++)
199     {
200       this_constraint = constraints[i];
201
202       if (comma_split (this_constraint, &a_name, &b_name))
203         return clib_error_return (0, "comma_split failed!");
204
205       p = hash_get_mem (index_by_name, a_name);
206       /*
207        * Note: the next two errors mean that the xxx_FEATURE_INIT macros are
208        * b0rked. As in: if you code "A depends on B," and you forget
209        * to define a FEATURE_INIT macro for B, you lose.
210        * Nonexistent graph nodes are tolerated.
211        */
212       if (p == 0)
213         return clib_error_return (0, "feature node '%s' not found", a_name);
214       a_index = p[0];
215
216       p = hash_get_mem (index_by_name, b_name);
217       if (p == 0)
218         return clib_error_return (0, "feature node '%s' not found", b_name);
219       b_index = p[0];
220
221       /* add a before b to the original set of constraints */
222       orig[a_index][b_index] = 1;
223       vec_free (this_constraint);
224     }
225
226   /* Compute the positive transitive closure of the original constraints */
227   closure = clib_ptclosure (orig);
228
229   /* Compute a partial order across feature nodes, if one exists. */
230 again:
231   for (i = 0; i < n_features; i++)
232     {
233       for (j = 0; j < n_features; j++)
234         {
235           if (closure[i][j])
236             goto item_constrained;
237         }
238       /* Item i can be output */
239       vec_add1 (result, i);
240       {
241         for (k = 0; k < n_features; k++)
242           closure[k][i] = 0;
243         /*
244          * Add a "Magic" a before a constraint.
245          * This means we'll never output it again
246          */
247         closure[i][i] = 1;
248         goto again;
249       }
250     item_constrained:
251       ;
252     }
253
254   /* see if we got a partial order... */
255   if (vec_len (result) != n_features)
256     return clib_error_return (0, "%d feature_init_cast no partial order!");
257
258   /*
259    * We win.
260    * Bind the index variables, and output the feature node name vector
261    * using the partial order we just computed. Result is in stack
262    * order, because the entry with the fewest constraints (e.g. none)
263    * is output first, etc.
264    */
265
266   for (i = n_features - 1; i >= 0; i--)
267     {
268       p = hash_get (reg_by_index, result[i]);
269       ASSERT (p != 0);
270       this_reg = (vnet_feature_registration_t *) p[0];
271       if (this_reg->feature_index)
272         *this_reg->feature_index = n_features - (i + 1);
273       this_reg->feature_index_u32 = n_features - (i + 1);
274       vec_add1 (feature_nodes, this_reg->node_name);
275     }
276
277   /* Set up the config infrastructure */
278   vnet_config_init (vm, vcm,
279                     feature_start_nodes,
280                     num_feature_start_nodes,
281                     feature_nodes, vec_len (feature_nodes));
282
283   /* Save a copy for show command */
284   *in_feature_nodes = feature_nodes;
285
286   /* Finally, clean up all the shit we allocated */
287   /* *INDENT-OFF* */
288   hash_foreach_pair (hp, index_by_name,
289   ({
290     vec_add1 (keys_to_delete, (u8 *)hp->key);
291   }));
292   /* *INDENT-ON* */
293   hash_free (index_by_name);
294   for (i = 0; i < vec_len (keys_to_delete); i++)
295     vec_free (keys_to_delete[i]);
296   vec_free (keys_to_delete);
297   hash_free (reg_by_index);
298   vec_free (result);
299   clib_ptclosure_free (orig);
300   clib_ptclosure_free (closure);
301   return 0;
302 }
303
304 #define foreach_af_cast                                 \
305 _(4, VNET_IP_RX_UNICAST_FEAT, "ip4 unicast")            \
306 _(4, VNET_IP_RX_MULTICAST_FEAT, "ip4 multicast")        \
307 _(4, VNET_IP_TX_FEAT, "ip4 output")                     \
308 _(6, VNET_IP_RX_UNICAST_FEAT, "ip6 unicast")            \
309 _(6, VNET_IP_RX_MULTICAST_FEAT, "ip6 multicast")        \
310 _(6, VNET_IP_TX_FEAT, "ip6 output")
311
312 /** Display the set of available ip features.
313     Useful for verifying that expected features are present
314 */
315
316 static clib_error_t *
317 show_ip_features_command_fn (vlib_main_t * vm,
318                              unformat_input_t * input,
319                              vlib_cli_command_t * cmd)
320 {
321   ip4_main_t *im4 = &ip4_main;
322   ip6_main_t *im6 = &ip6_main;
323   int i;
324   char **features;
325
326   vlib_cli_output (vm, "Available IP feature nodes");
327
328 #define _(a,c,s)                                        \
329   do {                                                  \
330     features = im##a->feature_nodes[c];                 \
331     vlib_cli_output (vm, "%s:", s);                     \
332     for (i = 0; i < vec_len(features); i++)             \
333       vlib_cli_output (vm, "  %s\n", features[i]);      \
334   } while(0);
335   foreach_af_cast;
336 #undef _
337
338   return 0;
339 }
340
341 /*?
342  * This command is used to display the set of available IP features.
343  * This can be useful for verifying that expected features are present.
344  *
345  * @cliexpar
346  * Example of how to display the set of available IP features:
347  * @cliexstart{show ip features}
348  * Available IP feature nodes
349  * ip4 unicast:
350  *   ip4-inacl
351  *   ip4-source-check-via-rx
352  *   ip4-source-check-via-any
353  *   ip4-source-and-port-range-check-rx
354  *   ip4-policer-classify
355  *   ipsec-input-ip4
356  *   vpath-input-ip4
357  *   snat-in2out
358  *   snat-out2in
359  *   ip4-lookup
360  * ip4 multicast:
361  *   vpath-input-ip4
362  *   ip4-lookup-multicast
363  * ip4 output:
364  *   ip4-source-and-port-range-check-tx
365  *   interface-output
366  * ip6 unicast:
367  *   ip6-inacl
368  *   ip6-policer-classify
369  *   ipsec-input-ip6
370  *   l2tp-decap
371  *   vpath-input-ip6
372  *   sir-to-ila
373  *   ip6-lookup
374  * ip6 multicast:
375  *   vpath-input-ip6
376  *   ip6-lookup
377  * ip6 output:
378  *   interface-output
379  * @cliexend
380 ?*/
381 /* *INDENT-OFF* */
382 VLIB_CLI_COMMAND (show_ip_features_command, static) = {
383   .path = "show ip features",
384   .short_help = "show ip features",
385   .function = show_ip_features_command_fn,
386 };
387 /* *INDENT-ON* */
388
389 /** Display the set of IP features configured on a specific interface
390  */
391
392 void
393 ip_interface_features_show (vlib_main_t * vm,
394                             const char *pname,
395                             vnet_feature_config_main_t * cm, u32 sw_if_index)
396 {
397   u32 node_index, current_config_index;
398   vnet_cast_t cast;
399   vnet_config_main_t *vcm;
400   vnet_config_t *cfg;
401   u32 cfg_index;
402   vnet_config_feature_t *feat;
403   vlib_node_t *n;
404   int i;
405
406   vlib_cli_output (vm, "%s feature paths configured on %U...",
407                    pname, format_vnet_sw_if_index_name,
408                    vnet_get_main (), sw_if_index);
409
410   for (cast = VNET_IP_RX_UNICAST_FEAT; cast < VNET_N_IP_FEAT; cast++)
411     {
412       vcm = &(cm[cast].config_main);
413
414       vlib_cli_output (vm, "\n%s %s:", pname, vnet_cast_names[cast]);
415
416       if (NULL == cm[cast].config_index_by_sw_if_index ||
417           vec_len (cm[cast].config_index_by_sw_if_index) < sw_if_index)
418         {
419           vlib_cli_output (vm, "none configured");
420           continue;
421         }
422
423       current_config_index = vec_elt (cm[cast].config_index_by_sw_if_index,
424                                       sw_if_index);
425
426       ASSERT (current_config_index
427               < vec_len (vcm->config_pool_index_by_user_index));
428
429       cfg_index = vcm->config_pool_index_by_user_index[current_config_index];
430       cfg = pool_elt_at_index (vcm->config_pool, cfg_index);
431
432       for (i = 0; i < vec_len (cfg->features); i++)
433         {
434           feat = cfg->features + i;
435           node_index = feat->node_index;
436           n = vlib_get_node (vm, node_index);
437           vlib_cli_output (vm, "  %v", n->name);
438         }
439     }
440 }
441
442 static clib_error_t *
443 show_ip_interface_features_command_fn (vlib_main_t * vm,
444                                        unformat_input_t * input,
445                                        vlib_cli_command_t * cmd)
446 {
447   vnet_main_t *vnm = vnet_get_main ();
448   ip4_main_t *im4 = &ip4_main;
449   ip_lookup_main_t *lm4 = &im4->lookup_main;
450   ip6_main_t *im6 = &ip6_main;
451   ip_lookup_main_t *lm6 = &im6->lookup_main;
452
453   ip_lookup_main_t *lm;
454   u32 sw_if_index, af;
455
456   if (!unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
457     return clib_error_return (0, "Interface not specified...");
458
459   vlib_cli_output (vm, "IP feature paths configured on %U...",
460                    format_vnet_sw_if_index_name, vnm, sw_if_index);
461
462   for (af = 0; af < 2; af++)
463     {
464       if (af == 0)
465         lm = lm4;
466       else
467         lm = lm6;
468
469       ip_interface_features_show (vm, (af == 0) ? "ip4" : "ip6",
470                                   lm->feature_config_mains, sw_if_index);
471     }
472
473   return 0;
474 }
475
476 /*?
477  * This command is used to display the set of IP features configured
478  * on a specific interface
479  *
480  * @cliexpar
481  * Example of how to display the set of available IP features on an interface:
482  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
483  * IP feature paths configured on GigabitEthernet2/0/0...
484  * ipv4 unicast:
485  *   ip4-lookup
486  * ipv4 multicast:
487  *   ip4-lookup-multicast
488  * ipv4 multicast:
489  *   interface-output
490  * ipv6 unicast:
491  *   ip6-lookup
492  * ipv6 multicast:
493  *   ip6-lookup
494  * ipv6 multicast:
495  *   interface-output
496  * @cliexend
497 ?*/
498 /* *INDENT-OFF* */
499 VLIB_CLI_COMMAND (show_ip_interface_features_command, static) = {
500   .path = "show ip interface features",
501   .short_help = "show ip interface features <interface>",
502   .function = show_ip_interface_features_command_fn,
503 };
504 /* *INDENT-ON* */
505
506 /*
507  * fd.io coding-style-patch-verification: ON
508  *
509  * Local Variables:
510  * eval: (c-set-style "gnu")
511  * End:
512  */