Reorganize source tree to use single autotools instance
[vpp.git] / src / 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
83 static int
84 comma_split (u8 * s, u8 ** a, u8 ** b)
85 {
86   *a = s;
87
88   while (*s && *s != ',')
89     s++;
90
91   if (*s == ',')
92     *s = 0;
93   else
94     return 1;
95
96   *b = (u8 *) (s + 1);
97   return 0;
98 }
99
100 /**
101  * @brief Initialize a feature graph arc
102  * @param vm vlib main structure pointer
103  * @param vcm vnet config main structure pointer
104  * @param feature_start_nodes names of start-nodes which use this
105  *        feature graph arc
106  * @param num_feature_start_nodes number of start-nodes
107  * @param first_reg first element in
108  *        [an __attribute__((constructor)) function built, or
109  *        otherwise created] singly-linked list of feature registrations
110  * @param [out] in_feature_nodes returned vector of
111  *        topologically-sorted feature node names, for use in
112  *        show commands
113  * @returns 0 on success, otherwise an error message. Errors
114  *        are fatal since they invariably involve mistyped node-names, or
115  *        genuinely missing node-names
116  */
117 clib_error_t *
118 vnet_feature_arc_init (vlib_main_t * vm,
119                        vnet_config_main_t * vcm,
120                        char **feature_start_nodes,
121                        int num_feature_start_nodes,
122                        vnet_feature_registration_t * first_reg,
123                        char ***in_feature_nodes)
124 {
125   uword *index_by_name;
126   uword *reg_by_index;
127   u8 **node_names = 0;
128   u8 *node_name;
129   char **these_constraints;
130   char *this_constraint_c;
131   u8 **constraints = 0;
132   u8 *constraint_tuple;
133   u8 *this_constraint;
134   u8 **orig, **closure;
135   uword *p;
136   int i, j, k;
137   u8 *a_name, *b_name;
138   int a_index, b_index;
139   int n_features;
140   u32 *result = 0;
141   vnet_feature_registration_t *this_reg = 0;
142   char **feature_nodes = 0;
143   hash_pair_t *hp;
144   u8 **keys_to_delete = 0;
145
146   index_by_name = hash_create_string (0, sizeof (uword));
147   reg_by_index = hash_create (0, sizeof (uword));
148
149   this_reg = first_reg;
150
151   /* pass 1, collect feature node names, construct a before b pairs */
152   while (this_reg)
153     {
154       node_name = format (0, "%s%c", this_reg->node_name, 0);
155       hash_set (reg_by_index, vec_len (node_names), (uword) this_reg);
156
157       hash_set_mem (index_by_name, node_name, vec_len (node_names));
158
159       vec_add1 (node_names, node_name);
160
161       these_constraints = this_reg->runs_before;
162       while (these_constraints && these_constraints[0])
163         {
164           this_constraint_c = these_constraints[0];
165
166           constraint_tuple = format (0, "%s,%s%c", node_name,
167                                      this_constraint_c, 0);
168           vec_add1 (constraints, constraint_tuple);
169           these_constraints++;
170         }
171
172       these_constraints = this_reg->runs_after;
173       while (these_constraints && these_constraints[0])
174         {
175           this_constraint_c = these_constraints[0];
176
177           constraint_tuple = format (0, "%s,%s%c",
178                                      this_constraint_c, node_name, 0);
179           vec_add1 (constraints, constraint_tuple);
180           these_constraints++;
181         }
182
183       this_reg = this_reg->next;
184     }
185
186   n_features = vec_len (node_names);
187   orig = clib_ptclosure_alloc (n_features);
188
189   for (i = 0; i < vec_len (constraints); i++)
190     {
191       this_constraint = constraints[i];
192
193       if (comma_split (this_constraint, &a_name, &b_name))
194         return clib_error_return (0, "comma_split failed!");
195
196       p = hash_get_mem (index_by_name, a_name);
197       /*
198        * Note: the next two errors mean that something is
199        * b0rked. As in: if you code "A depends on B," and you forget
200        * to define a FEATURE_INIT macro for B, you lose.
201        * Nonexistent graph nodes are tolerated.
202        */
203       if (p == 0)
204         return clib_error_return (0, "feature node '%s' not found", a_name);
205       a_index = p[0];
206
207       p = hash_get_mem (index_by_name, b_name);
208       if (p == 0)
209         return clib_error_return (0, "feature node '%s' not found", b_name);
210       b_index = p[0];
211
212       /* add a before b to the original set of constraints */
213       orig[a_index][b_index] = 1;
214       vec_free (this_constraint);
215     }
216
217   /* Compute the positive transitive closure of the original constraints */
218   closure = clib_ptclosure (orig);
219
220   /* Compute a partial order across feature nodes, if one exists. */
221 again:
222   for (i = 0; i < n_features; i++)
223     {
224       for (j = 0; j < n_features; j++)
225         {
226           if (closure[i][j])
227             goto item_constrained;
228         }
229       /* Item i can be output */
230       vec_add1 (result, i);
231       {
232         for (k = 0; k < n_features; k++)
233           closure[k][i] = 0;
234         /*
235          * Add a "Magic" a before a constraint.
236          * This means we'll never output it again
237          */
238         closure[i][i] = 1;
239         goto again;
240       }
241     item_constrained:
242       ;
243     }
244
245   /* see if we got a partial order... */
246   if (vec_len (result) != n_features)
247     return clib_error_return (0, "%d feature_init_cast no partial order!");
248
249   /*
250    * We win.
251    * Bind the index variables, and output the feature node name vector
252    * using the partial order we just computed. Result is in stack
253    * order, because the entry with the fewest constraints (e.g. none)
254    * is output first, etc.
255    */
256
257   for (i = n_features - 1; i >= 0; i--)
258     {
259       p = hash_get (reg_by_index, result[i]);
260       ASSERT (p != 0);
261       this_reg = (vnet_feature_registration_t *) p[0];
262       if (this_reg->feature_index_ptr)
263         *this_reg->feature_index_ptr = n_features - (i + 1);
264       this_reg->feature_index = n_features - (i + 1);
265       vec_add1 (feature_nodes, this_reg->node_name);
266     }
267
268   /* Set up the config infrastructure */
269   vnet_config_init (vm, vcm,
270                     feature_start_nodes,
271                     num_feature_start_nodes,
272                     feature_nodes, vec_len (feature_nodes));
273
274   /* Save a copy for show command */
275   *in_feature_nodes = feature_nodes;
276
277   /* Finally, clean up all the shit we allocated */
278   /* *INDENT-OFF* */
279   hash_foreach_pair (hp, index_by_name,
280   ({
281     vec_add1 (keys_to_delete, (u8 *)hp->key);
282   }));
283   /* *INDENT-ON* */
284   hash_free (index_by_name);
285   for (i = 0; i < vec_len (keys_to_delete); i++)
286     vec_free (keys_to_delete[i]);
287   vec_free (keys_to_delete);
288   hash_free (reg_by_index);
289   vec_free (result);
290   clib_ptclosure_free (orig);
291   clib_ptclosure_free (closure);
292   return 0;
293 }
294
295 /*
296  * fd.io coding-style-patch-verification: ON
297  *
298  * Local Variables:
299  * eval: (c-set-style "gnu")
300  * End:
301  */