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