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