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