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