72deee4100429b87abf690f1e4ffb2b77d29e134
[vpp.git] / vnet / vnet / ip / ip_feature_registration.c
1 /*
2  * Copyright (c) 2015 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
19 static int comma_split (u8 *s, u8 **a, u8 **b)
20 {
21   *a = s;
22
23   while (*s && *s != ',')
24     s++;
25
26   if (*s == ',')
27     *s = 0;
28   else
29     return 1;
30
31   *b = (u8 *) (s+1);
32   return 0;
33 }
34
35 clib_error_t *
36 ip_feature_init_cast (vlib_main_t * vm,
37                       ip_config_main_t * cm,
38                       vnet_config_main_t * vcm,
39                       char **feature_start_nodes,
40                       int num_feature_start_nodes,
41                       vnet_cast_t cast,
42                       int is_ip4)
43 {
44   uword * index_by_name;
45   uword * reg_by_index;
46   u8 ** node_names = 0;
47   u8 * node_name;
48   char ** these_constraints;
49   char * this_constraint_c;
50   u8 ** constraints = 0;
51   u8 * constraint_tuple;
52   u8 * this_constraint;
53   u8 ** orig, ** closure;
54   uword * p;
55   int i, j, k;
56   u8 * a_name, * b_name;
57   int a_index, b_index;
58   int n_features;
59   u32 * result = 0;
60   vnet_ip_feature_registration_t * this_reg, * first_reg = 0;
61   char ** feature_nodes = 0;
62   hash_pair_t * hp;
63   u8 ** keys_to_delete = 0;
64   ip4_main_t * im4 = &ip4_main;
65   ip6_main_t * im6 = &ip6_main;
66
67   index_by_name = hash_create_string (0, sizeof (uword));
68   reg_by_index = hash_create (0, sizeof (uword));
69
70   if (cast == VNET_IP_RX_UNICAST_FEAT)
71     {
72       if (is_ip4)
73         first_reg = im4->next_uc_feature;
74       else
75         first_reg = im6->next_uc_feature;
76     }
77   else if (cast == VNET_IP_RX_MULTICAST_FEAT)
78     {
79       if (is_ip4)
80         first_reg = im4->next_mc_feature;
81       else
82         first_reg = im6->next_mc_feature;
83     } 
84   else if (cast == VNET_IP_TX_FEAT)
85     {
86       if (is_ip4)
87         first_reg = im4->next_tx_feature;
88       else
89         first_reg = im6->next_tx_feature;
90     }
91
92   
93   this_reg = first_reg;
94
95   /* pass 1, collect feature node names, construct a before b pairs */
96   while (this_reg)
97     {
98       node_name = format (0, "%s%c", this_reg->node_name, 0);
99       hash_set (reg_by_index, vec_len(node_names), (uword) this_reg);
100
101       hash_set_mem (index_by_name, node_name, vec_len(node_names));
102
103       vec_add1 (node_names, node_name);
104
105       these_constraints = this_reg->runs_before;
106       while (these_constraints && these_constraints [0])
107         {
108           this_constraint_c = these_constraints[0];
109
110           constraint_tuple = format (0, "%s,%s%c", node_name,
111                                      this_constraint_c, 0);
112           vec_add1 (constraints, constraint_tuple);
113           these_constraints++;
114         }
115
116       these_constraints = this_reg->runs_after;
117       while (these_constraints && these_constraints [0])
118         {
119           this_constraint_c = these_constraints[0];
120
121           constraint_tuple = format (0, "%s,%s%c", 
122                                      this_constraint_c, 
123                                      node_name, 0);
124           vec_add1 (constraints, constraint_tuple);
125           these_constraints++;
126         }
127
128       this_reg = this_reg->next;
129     }
130
131   n_features = vec_len (node_names);
132   orig = clib_ptclosure_alloc (n_features);
133
134   for (i = 0; i < vec_len (constraints); i++)
135     {
136       this_constraint = constraints[i];
137
138       if (comma_split (this_constraint, &a_name, &b_name))
139         return clib_error_return (0, "comma_split failed!");
140       
141       p = hash_get_mem (index_by_name, a_name);
142       /* 
143        * Note: the next two errors mean that the xxx_FEATURE_INIT macros are
144        * b0rked. As in: if you code "A depends on B," and you forget
145        * to define a FEATURE_INIT macro for B, you lose. 
146        * Nonexistent graph nodes are tolerated.
147        */
148       if (p == 0)
149         return clib_error_return (0, "feature node '%s' not found", a_name);
150       a_index = p[0];
151
152       p = hash_get_mem (index_by_name, b_name);
153       if (p == 0)
154         return clib_error_return (0, "feature node '%s' not found", b_name);
155       b_index = p[0];
156
157       /* add a before b to the original set of constraints */
158       orig[a_index][b_index] = 1;
159       vec_free (this_constraint);
160     }
161   
162   /* Compute the positive transitive closure of the original constraints */
163   closure = clib_ptclosure (orig);
164
165   /* Compute a partial order across feature nodes, if one exists. */
166  again:
167   for (i = 0; i < n_features; i++)
168     {
169       for (j = 0; j < n_features; j++)
170         {
171           if (closure[i][j])
172             goto item_constrained;
173         }
174       /* Item i can be output */
175       vec_add1 (result, i);
176       {
177         for (k = 0; k < n_features; k++)
178           closure [k][i] = 0;
179         /* 
180          * Add a "Magic" a before a constraint. 
181          * This means we'll never output it again
182          */
183         closure [i][i] = 1;
184         goto again;
185       }
186     item_constrained:
187       ;
188     }
189
190   /* see if we got a partial order... */
191   if (vec_len (result) != n_features)
192       return clib_error_return 
193         (0, "ip%s_feature_init_cast (cast=%d), no partial order!",
194          is_ip4 ? "4" : "6", cast);
195
196   /* 
197    * We win.
198    * Bind the index variables, and output the feature node name vector
199    * using the partial order we just computed. Result is in stack
200    * order, because the entry with the fewest constraints (e.g. none)
201    * is output first, etc.
202    */
203
204   for (i = n_features-1; i >= 0; i--)
205     {
206       p = hash_get (reg_by_index, result[i]);
207       ASSERT (p != 0);
208       this_reg = (vnet_ip_feature_registration_t *)p[0];
209       *this_reg->feature_index = n_features - (i+1);
210       vec_add1 (feature_nodes, this_reg->node_name);
211     }
212   
213   /* Set up the config infrastructure */
214   vnet_config_init (vm, vcm,
215                     feature_start_nodes, 
216                     num_feature_start_nodes,
217                     feature_nodes, 
218                     vec_len(feature_nodes));
219
220   /* Save a copy for show command */
221   if (is_ip4)
222     im4->feature_nodes[cast] = feature_nodes;
223   else
224     im6->feature_nodes[cast] = feature_nodes;
225
226   /* Finally, clean up all the shit we allocated */
227   hash_foreach_pair (hp, index_by_name,
228   ({
229     vec_add1 (keys_to_delete, (u8 *)hp->key);
230   }));
231   hash_free (index_by_name);
232   for (i = 0; i < vec_len(keys_to_delete); i++)
233     vec_free (keys_to_delete[i]);
234   vec_free (keys_to_delete);
235   hash_free (reg_by_index);
236   vec_free (result);
237   clib_ptclosure_free (orig);
238   clib_ptclosure_free (closure);
239   return 0;
240 }
241
242 #define foreach_af_cast                         \
243 _(4, VNET_IP_RX_UNICAST_FEAT, "ip4 unicast")               \
244 _(4, VNET_IP_RX_MULTICAST_FEAT, "ip4 multicast")           \
245 _(4, VNET_IP_TX_FEAT, "ip4 output")                 \
246 _(6, VNET_IP_RX_UNICAST_FEAT, "ip6 unicast")               \
247 _(6, VNET_IP_RX_MULTICAST_FEAT, "ip6 multicast")                \
248 _(6, VNET_IP_TX_FEAT, "ip6 output")
249
250 static clib_error_t *
251 show_ip_features_command_fn (vlib_main_t * vm,
252                  unformat_input_t * input,
253                  vlib_cli_command_t * cmd)
254 {
255   ip4_main_t * im4 = &ip4_main;
256   ip6_main_t * im6 = &ip6_main;
257   int i;
258   char ** features;
259
260   vlib_cli_output (vm, "Available IP feature nodes");
261
262 #define _(a,c,s)                                        \
263   do {                                                  \
264     features = im##a->feature_nodes[c];                 \
265     vlib_cli_output (vm, "%s:", s);                     \
266     for (i = 0; i < vec_len(features); i++)             \
267       vlib_cli_output (vm, "  %s\n", features[i]);      \
268   } while(0);
269   foreach_af_cast;
270 #undef _
271
272   return 0;
273 }
274
275 VLIB_CLI_COMMAND (show_ip_features_command, static) = {
276   .path = "show ip features",
277   .short_help = "show ip features",
278   .function = show_ip_features_command_fn,
279 };
280
281 static clib_error_t *
282 show_ip_interface_features_command_fn (vlib_main_t * vm,
283                                        unformat_input_t * input,
284                                        vlib_cli_command_t * cmd)
285 {
286   vnet_main_t * vnm = vnet_get_main();
287   ip4_main_t * im4 = &ip4_main;
288   ip_lookup_main_t * lm4 = &im4->lookup_main;
289   ip6_main_t * im6 = &ip6_main;
290   ip_lookup_main_t * lm6 = &im6->lookup_main;
291   
292   ip_lookup_main_t * lm;
293   ip_config_main_t * cm;
294   vnet_config_main_t * vcm;
295   vnet_config_t * cfg;
296   u32 cfg_index;
297   vnet_config_feature_t * feat;
298   vlib_node_t * n;
299   u32 sw_if_index;
300   u32 node_index;
301   u32 current_config_index;
302   int i, af;
303   u32 cast;
304
305   if (! unformat (input, "%U", unformat_vnet_sw_interface, 
306                   vnm, &sw_if_index))
307     return clib_error_return (0, "Interface not specified...");
308
309   vlib_cli_output (vm, "IP feature paths configured on %U...",
310                    format_vnet_sw_if_index_name, vnm, sw_if_index);
311
312   
313   for (af = 0; af < 2; af++)
314     {
315       if (af == 0)
316         lm = lm4;
317       else
318         lm = lm6;
319
320       for (cast = VNET_IP_RX_UNICAST_FEAT; cast < VNET_N_IP_FEAT; cast++)
321         {
322           cm = lm->feature_config_mains + cast;
323           vcm = &cm->config_main;
324       
325           vlib_cli_output (vm, "\nipv%s %scast:", 
326                            (af == 0) ? "4" : "6",
327                            cast == VNET_IP_RX_UNICAST_FEAT ?
328                            "uni": "multi");
329
330           current_config_index = vec_elt (cm->config_index_by_sw_if_index, 
331                                           sw_if_index);
332
333           ASSERT(current_config_index 
334                  < vec_len (vcm->config_pool_index_by_user_index));
335           
336           cfg_index = 
337             vcm->config_pool_index_by_user_index[current_config_index];
338           cfg = pool_elt_at_index (vcm->config_pool, cfg_index);
339
340           for (i = 0; i < vec_len(cfg->features); i++)
341             {
342               feat = cfg->features + i;
343               node_index = feat->node_index;
344               n = vlib_get_node (vm, node_index);
345               vlib_cli_output (vm, "  %v", n->name);
346             }
347         }
348     }
349
350   return 0;
351 }
352
353 VLIB_CLI_COMMAND (show_ip_interface_features_command, static) = {
354   .path = "show ip interface features",
355   .short_help = "show ip interface features <intfc>",
356   .function = show_ip_interface_features_command_fn,
357 };
358
359