vlib: autogenerate <node> before <last-in-arc> constraints
[vpp.git] / src / vnet / feature / feature.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/feature/feature.h>
17 #include <vnet/adj/adj.h>
18
19 vnet_feature_main_t feature_main;
20
21 static clib_error_t *
22 vnet_feature_init (vlib_main_t * vm)
23 {
24   vnet_feature_main_t *fm = &feature_main;
25   vnet_feature_registration_t *freg;
26   vnet_feature_arc_registration_t *areg;
27   vnet_feature_constraint_registration_t *creg;
28   u32 arc_index = 0;
29
30   fm->arc_index_by_name = hash_create_string (0, sizeof (uword));
31   areg = fm->next_arc;
32
33   /* process feature arc registrations */
34   while (areg)
35     {
36       char *s;
37       int i = 0;
38       areg->feature_arc_index = arc_index;
39       if (areg->arc_index_ptr)
40         *areg->arc_index_ptr = arc_index;
41       hash_set_mem (fm->arc_index_by_name, areg->arc_name,
42                     pointer_to_uword (areg));
43
44       /* process start nodes */
45       while ((s = areg->start_nodes[i]))
46         {
47           i++;
48         }
49       areg->n_start_nodes = i;
50
51       /* next */
52       areg = areg->next;
53       arc_index++;
54     }
55
56   vec_validate (fm->next_feature_by_arc, arc_index - 1);
57   vec_validate (fm->feature_nodes, arc_index - 1);
58   vec_validate (fm->feature_config_mains, arc_index - 1);
59   vec_validate (fm->next_feature_by_name, arc_index - 1);
60   vec_validate (fm->sw_if_index_has_features, arc_index - 1);
61   vec_validate (fm->feature_count_by_sw_if_index, arc_index - 1);
62   vec_validate (fm->next_constraint_by_arc, arc_index - 1);
63
64   freg = fm->next_feature;
65   while (freg)
66     {
67       vnet_feature_registration_t *next;
68       uword *p = hash_get_mem (fm->arc_index_by_name, freg->arc_name);
69       if (p == 0)
70         {
71           /* Don't start vpp with broken features arcs */
72           clib_warning ("Unknown feature arc '%s'", freg->arc_name);
73           os_exit (1);
74         }
75
76       areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
77       arc_index = areg->feature_arc_index;
78
79       next = freg->next;
80       freg->next_in_arc = fm->next_feature_by_arc[arc_index];
81       fm->next_feature_by_arc[arc_index] = freg;
82
83       /* next */
84       freg = next;
85     }
86
87   /* Move bulk constraints to the constraint by arc lists */
88   creg = fm->next_constraint;
89   while (creg)
90     {
91       vnet_feature_constraint_registration_t *next;
92       uword *p = hash_get_mem (fm->arc_index_by_name, creg->arc_name);
93       if (p == 0)
94         {
95           /* Don't start vpp with broken features arcs */
96           clib_warning ("Unknown feature arc '%s'", creg->arc_name);
97           os_exit (1);
98         }
99
100       areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
101       arc_index = areg->feature_arc_index;
102
103       next = creg->next;
104       creg->next_in_arc = fm->next_constraint_by_arc[arc_index];
105       fm->next_constraint_by_arc[arc_index] = creg;
106
107       /* next */
108       creg = next;
109     }
110
111
112   areg = fm->next_arc;
113   while (areg)
114     {
115       clib_error_t *error;
116       vnet_feature_config_main_t *cm;
117       vnet_config_main_t *vcm;
118       char **features_in_order, *last_feature;
119
120       arc_index = areg->feature_arc_index;
121       cm = &fm->feature_config_mains[arc_index];
122       vcm = &cm->config_main;
123       if ((error = vnet_feature_arc_init
124            (vm, vcm, areg->start_nodes, areg->n_start_nodes,
125             areg->last_in_arc,
126             fm->next_feature_by_arc[arc_index],
127             fm->next_constraint_by_arc[arc_index],
128             &fm->feature_nodes[arc_index])))
129         {
130           clib_error_report (error);
131           os_exit (1);
132         }
133
134       features_in_order = fm->feature_nodes[arc_index];
135
136       /* If specified, verify that the last node in the arc is actually last */
137       if (areg->last_in_arc && vec_len (features_in_order) > 0)
138         {
139           last_feature = features_in_order[vec_len (features_in_order) - 1];
140           if (strncmp (areg->last_in_arc, last_feature,
141                        strlen (areg->last_in_arc)))
142             clib_warning
143               ("WARNING: %s arc: last node is %s, but expected %s!",
144                areg->arc_name, last_feature, areg->last_in_arc);
145         }
146
147       fm->next_feature_by_name[arc_index] =
148         hash_create_string (0, sizeof (uword));
149       freg = fm->next_feature_by_arc[arc_index];
150
151       while (freg)
152         {
153           hash_set_mem (fm->next_feature_by_name[arc_index],
154                         freg->node_name, pointer_to_uword (freg));
155           freg = freg->next_in_arc;
156         }
157
158       /* next */
159       areg = areg->next;
160       arc_index++;
161     }
162
163   return 0;
164 }
165
166 VLIB_INIT_FUNCTION (vnet_feature_init);
167
168 u8
169 vnet_get_feature_arc_index (const char *s)
170 {
171   vnet_feature_main_t *fm = &feature_main;
172   vnet_feature_arc_registration_t *reg;
173   uword *p;
174
175   p = hash_get_mem (fm->arc_index_by_name, s);
176   if (p == 0)
177     return ~0;
178
179   reg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
180   return reg->feature_arc_index;
181 }
182
183 vnet_feature_registration_t *
184 vnet_get_feature_reg (const char *arc_name, const char *node_name)
185 {
186   u8 arc_index;
187
188   arc_index = vnet_get_feature_arc_index (arc_name);
189   if (arc_index == (u8) ~ 0)
190     return 0;
191
192   vnet_feature_main_t *fm = &feature_main;
193   vnet_feature_registration_t *reg;
194   uword *p;
195
196   p = hash_get_mem (fm->next_feature_by_name[arc_index], node_name);
197   if (p == 0)
198     return 0;
199
200   reg = uword_to_pointer (p[0], vnet_feature_registration_t *);
201   return reg;
202 }
203
204 u32
205 vnet_get_feature_index (u8 arc, const char *s)
206 {
207   vnet_feature_main_t *fm = &feature_main;
208   vnet_feature_registration_t *reg;
209   uword *p;
210
211   if (s == 0)
212     return ~0;
213
214   p = hash_get_mem (fm->next_feature_by_name[arc], s);
215   if (p == 0)
216     return ~0;
217
218   reg = uword_to_pointer (p[0], vnet_feature_registration_t *);
219   return reg->feature_index;
220 }
221
222 int
223 vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index,
224                                         u32 sw_if_index, int enable_disable,
225                                         void *feature_config,
226                                         u32 n_feature_config_bytes)
227 {
228   vnet_feature_main_t *fm = &feature_main;
229   vnet_feature_config_main_t *cm;
230   i16 feature_count;
231   u32 ci;
232
233   if (arc_index == (u8) ~ 0)
234     return VNET_API_ERROR_INVALID_VALUE;
235
236   if (feature_index == ~0)
237     return VNET_API_ERROR_INVALID_VALUE_2;
238
239   cm = &fm->feature_config_mains[arc_index];
240   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
241   ci = cm->config_index_by_sw_if_index[sw_if_index];
242
243   vec_validate (fm->feature_count_by_sw_if_index[arc_index], sw_if_index);
244   feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index];
245
246   if (!enable_disable && feature_count < 1)
247     return 0;
248
249   ci = (enable_disable
250         ? vnet_config_add_feature
251         : vnet_config_del_feature)
252     (vlib_get_main (), &cm->config_main, ci, feature_index, feature_config,
253      n_feature_config_bytes);
254   if (ci == ~0)
255     {
256       return 0;
257     }
258   cm->config_index_by_sw_if_index[sw_if_index] = ci;
259
260   /* update feature count */
261   enable_disable = (enable_disable > 0);
262   feature_count += enable_disable ? 1 : -1;
263   ASSERT (feature_count >= 0);
264
265   fm->sw_if_index_has_features[arc_index] =
266     clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index,
267                      (feature_count > 0));
268   adj_feature_update (sw_if_index, arc_index, (feature_count > 0));
269
270   fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count;
271   return 0;
272 }
273
274 int
275 vnet_feature_enable_disable (const char *arc_name, const char *node_name,
276                              u32 sw_if_index, int enable_disable,
277                              void *feature_config, u32 n_feature_config_bytes)
278 {
279   u32 feature_index;
280   u8 arc_index;
281
282   arc_index = vnet_get_feature_arc_index (arc_name);
283
284   if (arc_index == (u8) ~ 0)
285     return VNET_API_ERROR_INVALID_VALUE;
286
287   feature_index = vnet_get_feature_index (arc_index, node_name);
288
289   return vnet_feature_enable_disable_with_index (arc_index, feature_index,
290                                                  sw_if_index, enable_disable,
291                                                  feature_config,
292                                                  n_feature_config_bytes);
293 }
294
295 static int
296 feature_cmp (void *a1, void *a2)
297 {
298   vnet_feature_registration_t *reg1 = a1;
299   vnet_feature_registration_t *reg2 = a2;
300
301   return (int) reg1->feature_index - reg2->feature_index;
302 }
303
304 /** Display the set of available driver features.
305     Useful for verifying that expected features are present
306 */
307
308 static clib_error_t *
309 show_features_command_fn (vlib_main_t * vm,
310                           unformat_input_t * input, vlib_cli_command_t * cmd)
311 {
312   vnet_feature_main_t *fm = &feature_main;
313   vnet_feature_arc_registration_t *areg;
314   vnet_feature_registration_t *freg;
315   vnet_feature_registration_t *feature_regs = 0;
316   int verbose = 0;
317
318   if (unformat (input, "verbose"))
319     verbose = 1;
320
321   vlib_cli_output (vm, "Available feature paths");
322
323   areg = fm->next_arc;
324   while (areg)
325     {
326       if (verbose)
327         vlib_cli_output (vm, "[%2d] %s:", areg->feature_arc_index,
328                          areg->arc_name);
329       else
330         vlib_cli_output (vm, "%s:", areg->arc_name);
331
332       freg = fm->next_feature_by_arc[areg->feature_arc_index];
333       while (freg)
334         {
335           vec_add1 (feature_regs, freg[0]);
336           freg = freg->next_in_arc;
337         }
338
339       vec_sort_with_function (feature_regs, feature_cmp);
340
341       vec_foreach (freg, feature_regs)
342       {
343         if (verbose)
344           vlib_cli_output (vm, "  [%2d]: %s\n", freg->feature_index,
345                            freg->node_name);
346         else
347           vlib_cli_output (vm, "  %s\n", freg->node_name);
348       }
349       vec_reset_length (feature_regs);
350       /* next */
351       areg = areg->next;
352     }
353   vec_free (feature_regs);
354
355   return 0;
356 }
357
358 /*?
359  * Display the set of available driver features
360  *
361  * @cliexpar
362  * Example:
363  * @cliexcmd{show features [verbose]}
364  * @cliexend
365  * @endparblock
366 ?*/
367 /* *INDENT-OFF* */
368 VLIB_CLI_COMMAND (show_features_command, static) = {
369   .path = "show features",
370   .short_help = "show features",
371   .function = show_features_command_fn,
372 };
373 /* *INDENT-ON* */
374
375 /** Display the set of driver features configured on a specific interface
376   * Called by "show interface" handler
377  */
378
379 void
380 vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index, int verbose)
381 {
382   vnet_feature_main_t *fm = &feature_main;
383   u32 node_index, current_config_index;
384   u16 feature_arc;
385   vnet_feature_config_main_t *cm = fm->feature_config_mains;
386   vnet_feature_arc_registration_t *areg;
387   vnet_config_main_t *vcm;
388   vnet_config_t *cfg;
389   u32 cfg_index;
390   vnet_config_feature_t *feat;
391   vlib_node_t *n;
392   int i;
393
394   vlib_cli_output (vm, "Feature paths configured on %U...",
395                    format_vnet_sw_if_index_name,
396                    vnet_get_main (), sw_if_index);
397
398   areg = fm->next_arc;
399   while (areg)
400     {
401       feature_arc = areg->feature_arc_index;
402       vcm = &(cm[feature_arc].config_main);
403
404       vlib_cli_output (vm, "\n%s:", areg->arc_name);
405       areg = areg->next;
406
407       if (NULL == cm[feature_arc].config_index_by_sw_if_index ||
408           vec_len (cm[feature_arc].config_index_by_sw_if_index) <=
409           sw_if_index)
410         {
411           vlib_cli_output (vm, "  none configured");
412           continue;
413         }
414
415       current_config_index =
416         vec_elt (cm[feature_arc].config_index_by_sw_if_index, sw_if_index);
417
418       if (current_config_index == ~0)
419         {
420           vlib_cli_output (vm, "  none configured");
421           continue;
422         }
423
424       ASSERT (current_config_index
425               < vec_len (vcm->config_pool_index_by_user_index));
426
427       cfg_index = vcm->config_pool_index_by_user_index[current_config_index];
428       cfg = pool_elt_at_index (vcm->config_pool, cfg_index);
429
430       for (i = 0; i < vec_len (cfg->features); i++)
431         {
432           feat = cfg->features + i;
433           node_index = feat->node_index;
434           n = vlib_get_node (vm, node_index);
435           if (verbose)
436             vlib_cli_output (vm, "  [%2d] %v", feat->feature_index, n->name);
437           else
438             vlib_cli_output (vm, "  %v", n->name);
439         }
440     }
441 }
442
443 static clib_error_t *
444 set_interface_features_command_fn (vlib_main_t * vm,
445                                    unformat_input_t * input,
446                                    vlib_cli_command_t * cmd)
447 {
448   vnet_main_t *vnm = vnet_get_main ();
449   unformat_input_t _line_input, *line_input = &_line_input;
450   clib_error_t *error = 0;
451
452   u8 *arc_name = 0;
453   u8 *feature_name = 0;
454   u32 sw_if_index = ~0;
455   u8 enable = 1;
456
457   /* Get a line of input. */
458   if (!unformat_user (input, unformat_line_input, line_input))
459     return 0;
460
461   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
462     {
463       if (unformat
464           (line_input, "%U %v", unformat_vnet_sw_interface, vnm, &sw_if_index,
465            &feature_name))
466         ;
467       else if (unformat (line_input, "arc %v", &arc_name))
468         ;
469       else if (unformat (line_input, "disable"))
470         enable = 0;
471       else
472         {
473           if (feature_name && arc_name)
474             break;
475           error = unformat_parse_error (line_input);
476           goto done;
477         }
478     }
479
480   if (sw_if_index == ~0)
481     {
482       error = clib_error_return (0, "Interface not specified...");
483       goto done;
484     }
485
486   vec_add1 (arc_name, 0);
487   vec_add1 (feature_name, 0);
488
489   vnet_feature_registration_t *reg;
490   reg =
491     vnet_get_feature_reg ((const char *) arc_name,
492                           (const char *) feature_name);
493   if (reg == 0)
494     {
495       error = clib_error_return (0, "Unknown feature...");
496       goto done;
497     }
498   if (reg->enable_disable_cb)
499     error = reg->enable_disable_cb (sw_if_index, enable);
500   if (!error)
501     vnet_feature_enable_disable ((const char *) arc_name,
502                                  (const char *) feature_name, sw_if_index,
503                                  enable, 0, 0);
504
505 done:
506   vec_free (feature_name);
507   vec_free (arc_name);
508   unformat_free (line_input);
509   return error;
510 }
511
512 /*?
513  * Set feature for given interface
514  *
515  * @cliexpar
516  * Example:
517  * @cliexcmd{set interface feature GigabitEthernet2/0/0 ip4_flow_classify arc ip4_unicast}
518  * @cliexend
519  * @endparblock
520 ?*/
521 /* *INDENT-OFF* */
522 VLIB_CLI_COMMAND (set_interface_feature_command, static) = {
523   .path = "set interface feature",
524   .short_help = "set interface feature <intfc> <feature_name> arc <arc_name> "
525       "[disable]",
526   .function = set_interface_features_command_fn,
527 };
528 /* *INDENT-ON* */
529
530 /*
531  * fd.io coding-style-patch-verification: ON
532  *
533  * Local Variables:
534  * eval: (c-set-style "gnu")
535  * End:
536  */