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