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