http: fix client parse error handling
[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
18 vnet_feature_main_t feature_main;
19
20 typedef struct vnet_feature_upd_registration_t_
21 {
22   vnet_feature_update_cb_t cb;
23   void *data;
24 } vnet_feature_upd_registration_t;
25
26 static vnet_feature_upd_registration_t *regs;
27
28 void
29 vnet_feature_register (vnet_feature_update_cb_t cb, void *data)
30 {
31   vnet_feature_upd_registration_t *reg;
32
33   vec_add2 (regs, reg, 1);
34
35   reg->cb = cb;
36   reg->data = data;
37 }
38
39 static void
40 vnet_feature_reg_invoke (u32 sw_if_index, u8 arc_index, u8 is_enable)
41 {
42   vnet_feature_upd_registration_t *reg;
43
44   vec_foreach (reg, regs)
45     reg->cb (sw_if_index, arc_index, is_enable, reg->data);
46 }
47
48
49 static clib_error_t *
50 vnet_feature_init (vlib_main_t * vm)
51 {
52   vnet_feature_main_t *fm = &feature_main;
53   vnet_feature_registration_t *freg;
54   vnet_feature_arc_registration_t *areg;
55   vnet_feature_constraint_registration_t *creg;
56   u32 arc_index = 0;
57
58   fm->arc_index_by_name = hash_create_string (0, sizeof (uword));
59   areg = fm->next_arc;
60
61   /* process feature arc registrations */
62   while (areg)
63     {
64       char *s;
65       int i = 0;
66       areg->feature_arc_index = arc_index;
67       if (areg->arc_index_ptr)
68         *areg->arc_index_ptr = arc_index;
69       hash_set_mem (fm->arc_index_by_name, areg->arc_name,
70                     pointer_to_uword (areg));
71
72       /* process start nodes */
73       while ((s = areg->start_nodes[i]))
74         {
75           i++;
76         }
77       areg->n_start_nodes = i;
78
79       /* next */
80       areg = areg->next;
81       arc_index++;
82     }
83
84   vec_validate (fm->next_feature_by_arc, arc_index - 1);
85   vec_validate (fm->feature_nodes, arc_index - 1);
86   vec_validate (fm->feature_config_mains, arc_index - 1);
87   vec_validate (fm->next_feature_by_name, arc_index - 1);
88   vec_validate (fm->sw_if_index_has_features, arc_index - 1);
89   vec_validate (fm->feature_count_by_sw_if_index, arc_index - 1);
90   vec_validate (fm->next_constraint_by_arc, arc_index - 1);
91
92   freg = fm->next_feature;
93   while (freg)
94     {
95       vnet_feature_registration_t *next;
96       uword *p = hash_get_mem (fm->arc_index_by_name, freg->arc_name);
97       if (p == 0)
98         {
99           /* Don't start vpp with broken features arcs */
100           clib_warning ("Unknown feature arc '%s'", freg->arc_name);
101           os_exit (1);
102         }
103
104       areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
105       arc_index = areg->feature_arc_index;
106
107       next = freg->next;
108       freg->next_in_arc = fm->next_feature_by_arc[arc_index];
109       fm->next_feature_by_arc[arc_index] = freg;
110
111       /* next */
112       freg = next;
113     }
114
115   /* Move bulk constraints to the constraint by arc lists */
116   creg = fm->next_constraint;
117   while (creg)
118     {
119       vnet_feature_constraint_registration_t *next;
120       uword *p = hash_get_mem (fm->arc_index_by_name, creg->arc_name);
121       if (p == 0)
122         {
123           /* Don't start vpp with broken features arcs */
124           clib_warning ("Unknown feature arc '%s'", creg->arc_name);
125           os_exit (1);
126         }
127
128       areg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
129       arc_index = areg->feature_arc_index;
130
131       next = creg->next;
132       creg->next_in_arc = fm->next_constraint_by_arc[arc_index];
133       fm->next_constraint_by_arc[arc_index] = creg;
134
135       /* next */
136       creg = next;
137     }
138
139
140   areg = fm->next_arc;
141   while (areg)
142     {
143       clib_error_t *error;
144       vnet_feature_config_main_t *cm;
145       vnet_config_main_t *vcm;
146       char **features_in_order, *last_feature;
147
148       arc_index = areg->feature_arc_index;
149       cm = &fm->feature_config_mains[arc_index];
150       vcm = &cm->config_main;
151       if ((error = vnet_feature_arc_init
152            (vm, vcm, areg->start_nodes, areg->n_start_nodes,
153             areg->last_in_arc,
154             fm->next_feature_by_arc[arc_index],
155             fm->next_constraint_by_arc[arc_index],
156             &fm->feature_nodes[arc_index])))
157         {
158           clib_error_report (error);
159           os_exit (1);
160         }
161
162       features_in_order = fm->feature_nodes[arc_index];
163
164       /* If specified, verify that the last node in the arc is actually last */
165       if (areg->last_in_arc && vec_len (features_in_order) > 0)
166         {
167           last_feature = features_in_order[vec_len (features_in_order) - 1];
168           if (strncmp (areg->last_in_arc, last_feature,
169                        strlen (areg->last_in_arc)))
170             clib_warning
171               ("WARNING: %s arc: last node is %s, but expected %s!",
172                areg->arc_name, last_feature, areg->last_in_arc);
173         }
174
175       fm->next_feature_by_name[arc_index] =
176         hash_create_string (0, sizeof (uword));
177       freg = fm->next_feature_by_arc[arc_index];
178
179       while (freg)
180         {
181           hash_set_mem (fm->next_feature_by_name[arc_index],
182                         freg->node_name, pointer_to_uword (freg));
183           freg = freg->next_in_arc;
184         }
185
186       /* next */
187       areg = areg->next;
188       arc_index++;
189     }
190
191   return 0;
192 }
193
194 VLIB_INIT_FUNCTION (vnet_feature_init);
195
196 u8
197 vnet_get_feature_arc_index (const char *s)
198 {
199   vnet_feature_main_t *fm = &feature_main;
200   vnet_feature_arc_registration_t *reg;
201   uword *p;
202
203   p = hash_get_mem (fm->arc_index_by_name, s);
204   if (p == 0)
205     return ~0;
206
207   reg = uword_to_pointer (p[0], vnet_feature_arc_registration_t *);
208   return reg->feature_arc_index;
209 }
210
211 vnet_feature_registration_t *
212 vnet_get_feature_reg (const char *arc_name, const char *node_name)
213 {
214   u8 arc_index;
215
216   arc_index = vnet_get_feature_arc_index (arc_name);
217   if (arc_index == (u8) ~ 0)
218     return 0;
219
220   vnet_feature_main_t *fm = &feature_main;
221   vnet_feature_registration_t *reg;
222   uword *p;
223
224   p = hash_get_mem (fm->next_feature_by_name[arc_index], node_name);
225   if (p == 0)
226     return 0;
227
228   reg = uword_to_pointer (p[0], vnet_feature_registration_t *);
229   return reg;
230 }
231
232 u32
233 vnet_get_feature_index (u8 arc, const char *s)
234 {
235   vnet_feature_main_t *fm = &feature_main;
236   vnet_feature_registration_t *reg;
237   uword *p;
238
239   if (s == 0)
240     return ~0;
241
242   p = hash_get_mem (fm->next_feature_by_name[arc], s);
243   if (p == 0)
244     return ~0;
245
246   reg = uword_to_pointer (p[0], vnet_feature_registration_t *);
247   return reg->feature_index;
248 }
249
250 int
251 vnet_feature_enable_disable_with_index (u8 arc_index, u32 feature_index,
252                                         u32 sw_if_index, int enable_disable,
253                                         void *feature_config,
254                                         u32 n_feature_config_bytes)
255 {
256   vnet_feature_main_t *fm = &feature_main;
257   vnet_feature_config_main_t *cm;
258   i16 feature_count;
259   u32 ci;
260
261   if (arc_index == (u8) ~ 0)
262     return VNET_API_ERROR_INVALID_VALUE;
263
264   if (feature_index == ~0)
265     return VNET_API_ERROR_INVALID_VALUE_2;
266
267   cm = &fm->feature_config_mains[arc_index];
268   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
269   ci = cm->config_index_by_sw_if_index[sw_if_index];
270
271   vec_validate (fm->feature_count_by_sw_if_index[arc_index], sw_if_index);
272   feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index];
273
274   if (!enable_disable && feature_count < 1)
275     return 0;
276
277   ci = (enable_disable
278         ? vnet_config_add_feature
279         : vnet_config_del_feature)
280     (vlib_get_main (), &cm->config_main, ci, feature_index, feature_config,
281      n_feature_config_bytes);
282   if (ci == ~0)
283     {
284       return 0;
285     }
286   cm->config_index_by_sw_if_index[sw_if_index] = ci;
287
288   /* update feature count */
289   enable_disable = (enable_disable > 0);
290   feature_count += enable_disable ? 1 : -1;
291   ASSERT (feature_count >= 0);
292
293   fm->sw_if_index_has_features[arc_index] =
294     clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index,
295                      (feature_count > 0));
296   fm->feature_count_by_sw_if_index[arc_index][sw_if_index] = feature_count;
297
298   vnet_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0));
299
300   return 0;
301 }
302
303 int
304 vnet_feature_enable_disable (const char *arc_name, const char *node_name,
305                              u32 sw_if_index, int enable_disable,
306                              void *feature_config, u32 n_feature_config_bytes)
307 {
308   u32 feature_index;
309   u8 arc_index;
310
311   arc_index = vnet_get_feature_arc_index (arc_name);
312
313   if (arc_index == (u8) ~ 0)
314     return VNET_API_ERROR_INVALID_VALUE;
315
316   feature_index = vnet_get_feature_index (arc_index, node_name);
317
318   return vnet_feature_enable_disable_with_index (arc_index, feature_index,
319                                                  sw_if_index, enable_disable,
320                                                  feature_config,
321                                                  n_feature_config_bytes);
322 }
323
324 int
325 vnet_feature_is_enabled (const char *arc_name, const char *feature_node_name,
326                          u32 sw_if_index)
327 {
328   vnet_feature_main_t *fm = &feature_main;
329   vnet_feature_config_main_t *cm;
330   vnet_config_main_t *ccm;
331   vnet_config_t *current_config;
332   vnet_config_feature_t *f;
333   u32 feature_index;
334   u32 ci;
335   u8 arc_index;
336   u32 *p;
337
338   arc_index = vnet_get_feature_arc_index (arc_name);
339
340   /* No such arc? */
341   if (arc_index == (u8) ~ 0)
342     return VNET_API_ERROR_INVALID_VALUE;
343
344   feature_index = vnet_get_feature_index (arc_index, feature_node_name);
345
346   /* No such feature? */
347   if (feature_index == (u32) ~ 0)
348     return VNET_API_ERROR_INVALID_VALUE_2;
349
350   cm = &fm->feature_config_mains[arc_index];
351
352   if (sw_if_index < vec_len (cm->config_index_by_sw_if_index))
353     ci = vec_elt (cm->config_index_by_sw_if_index, sw_if_index);
354   else
355     /* sw_if_index out of range, certainly not enabled */
356     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
357
358   /* No features were ever configured? */
359   if (ci == ~0)
360     return 0;
361
362   ccm = &cm->config_main;
363
364   p = heap_elt_at_index (ccm->config_string_heap, ci);
365
366   current_config = pool_elt_at_index (ccm->config_pool, p[-1]);
367
368   /* Find feature with the required index */
369   vec_foreach (f, current_config->features)
370   {
371     if (f->feature_index == feature_index)
372       /* Feature was enabled */
373       return 1;
374   }
375   /* feature wasn't enabled */
376   return 0;
377 }
378
379 u32
380 vnet_feature_get_end_node (u8 arc_index, u32 sw_if_index)
381 {
382   vnet_feature_main_t *fm = &feature_main;
383   vnet_feature_config_main_t *cm;
384   u32 ci;
385
386   if (arc_index == (u8) ~0)
387     return VNET_API_ERROR_INVALID_VALUE;
388
389   cm = &fm->feature_config_mains[arc_index];
390   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
391   ci = cm->config_index_by_sw_if_index[sw_if_index];
392
393   return (vnet_config_get_end_node (vlib_get_main (), &cm->config_main, ci));
394 }
395
396 u32
397 vnet_feature_reset_end_node (u8 arc_index, u32 sw_if_index)
398 {
399   vnet_feature_main_t *fm = &feature_main;
400   vnet_feature_config_main_t *cm;
401   u32 ci;
402
403   cm = &fm->feature_config_mains[arc_index];
404   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
405   ci = cm->config_index_by_sw_if_index[sw_if_index];
406
407   ci = vnet_config_reset_end_node (vlib_get_main (), &cm->config_main, ci);
408
409   if (ci != ~0)
410     cm->config_index_by_sw_if_index[sw_if_index] = ci;
411
412   i16 feature_count;
413
414   if (NULL == fm->feature_count_by_sw_if_index ||
415       vec_len (fm->feature_count_by_sw_if_index) <= arc_index ||
416       vec_len (fm->feature_count_by_sw_if_index[arc_index]) <= sw_if_index)
417     feature_count = 0;
418   else
419     feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index];
420
421   vnet_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0));
422
423   return ci;
424 }
425
426 u32
427 vnet_feature_modify_end_node (u8 arc_index,
428                               u32 sw_if_index, u32 end_node_index)
429 {
430   vnet_feature_main_t *fm = &feature_main;
431   vnet_feature_config_main_t *cm;
432   u32 ci;
433
434   if (arc_index == (u8) ~ 0)
435     return VNET_API_ERROR_INVALID_VALUE;
436
437   if (end_node_index == ~0)
438     return VNET_API_ERROR_INVALID_VALUE_2;
439
440   cm = &fm->feature_config_mains[arc_index];
441   vec_validate_init_empty (cm->config_index_by_sw_if_index, sw_if_index, ~0);
442   ci = cm->config_index_by_sw_if_index[sw_if_index];
443
444   ci = vnet_config_modify_end_node (vlib_get_main (), &cm->config_main,
445                                     ci, end_node_index);
446
447   if (ci != ~0)
448     cm->config_index_by_sw_if_index[sw_if_index] = ci;
449
450   i16 feature_count;
451
452   if (NULL == fm->feature_count_by_sw_if_index ||
453       vec_len (fm->feature_count_by_sw_if_index) <= arc_index ||
454       vec_len (fm->feature_count_by_sw_if_index[arc_index]) <= sw_if_index)
455     feature_count = 0;
456   else
457     feature_count = fm->feature_count_by_sw_if_index[arc_index][sw_if_index];
458
459   vnet_feature_reg_invoke (sw_if_index, arc_index, (feature_count > 0));
460
461   return ci;
462 }
463
464 static int
465 feature_cmp (void *a1, void *a2)
466 {
467   vnet_feature_registration_t *reg1 = a1;
468   vnet_feature_registration_t *reg2 = a2;
469
470   return (int) reg1->feature_index - reg2->feature_index;
471 }
472
473 /** Display the set of available driver features.
474     Useful for verifying that expected features are present
475 */
476
477 static clib_error_t *
478 show_features_command_fn (vlib_main_t * vm,
479                           unformat_input_t * input, vlib_cli_command_t * cmd)
480 {
481   vnet_feature_main_t *fm = &feature_main;
482   vnet_feature_arc_registration_t *areg;
483   vnet_feature_registration_t *freg;
484   vnet_feature_registration_t *feature_regs = 0;
485   int verbose = 0;
486
487   if (unformat (input, "verbose"))
488     verbose = 1;
489
490   vlib_cli_output (vm, "Available feature paths");
491
492   areg = fm->next_arc;
493   while (areg)
494     {
495       if (verbose)
496         vlib_cli_output (vm, "[%2d] %s:", areg->feature_arc_index,
497                          areg->arc_name);
498       else
499         vlib_cli_output (vm, "%s:", areg->arc_name);
500
501       freg = fm->next_feature_by_arc[areg->feature_arc_index];
502       while (freg)
503         {
504           vec_add1 (feature_regs, freg[0]);
505           freg = freg->next_in_arc;
506         }
507
508       vec_sort_with_function (feature_regs, feature_cmp);
509
510       vec_foreach (freg, feature_regs)
511       {
512         if (verbose)
513           vlib_cli_output (vm, "  [%2d]: %s\n", freg->feature_index,
514                            freg->node_name);
515         else
516           vlib_cli_output (vm, "  %s\n", freg->node_name);
517       }
518       vec_reset_length (feature_regs);
519       /* next */
520       areg = areg->next;
521     }
522   vec_free (feature_regs);
523
524   return 0;
525 }
526
527 /*?
528  * Display the set of available driver features
529  *
530  * @cliexpar
531  * Example:
532  * @cliexcmd{show features [verbose]}
533  * @cliexend
534  * @endparblock
535 ?*/
536 VLIB_CLI_COMMAND (show_features_command, static) = {
537   .path = "show features",
538   .short_help = "show features [verbose]",
539   .function = show_features_command_fn,
540 };
541
542 /** Display the set of driver features configured on a specific interface
543   * Called by "show interface" handler
544  */
545
546 void
547 vnet_interface_features_show (vlib_main_t * vm, u32 sw_if_index, int verbose)
548 {
549   vnet_feature_main_t *fm = &feature_main;
550   u32 node_index, current_config_index;
551   u16 feature_arc;
552   vnet_feature_config_main_t *cm = fm->feature_config_mains;
553   vnet_feature_arc_registration_t *areg;
554   vnet_config_main_t *vcm;
555   vnet_config_t *cfg;
556   u32 cfg_index;
557   vnet_config_feature_t *feat;
558   vlib_node_t *n;
559   int i;
560
561   vlib_cli_output (vm, "Feature paths configured on %U...",
562                    format_vnet_sw_if_index_name,
563                    vnet_get_main (), sw_if_index);
564
565   areg = fm->next_arc;
566   while (areg)
567     {
568       feature_arc = areg->feature_arc_index;
569       vcm = &(cm[feature_arc].config_main);
570
571       vlib_cli_output (vm, "\n%s:", areg->arc_name);
572       areg = areg->next;
573
574       if (!vnet_have_features (feature_arc, sw_if_index))
575         {
576           vlib_cli_output (vm, "  none configured");
577           continue;
578         }
579
580       current_config_index =
581         vec_elt (cm[feature_arc].config_index_by_sw_if_index, sw_if_index);
582       cfg_index =
583         vec_elt (vcm->config_pool_index_by_user_index, current_config_index);
584       cfg = pool_elt_at_index (vcm->config_pool, cfg_index);
585
586       for (i = 0; i < vec_len (cfg->features); i++)
587         {
588           feat = cfg->features + i;
589           node_index = feat->node_index;
590           n = vlib_get_node (vm, node_index);
591           if (verbose)
592             vlib_cli_output (vm, "  [%2d] %v", feat->feature_index, n->name);
593           else
594             vlib_cli_output (vm, "  %v", n->name);
595         }
596       if (verbose)
597         {
598           n =
599             vlib_get_node (vm,
600                            vcm->end_node_indices_by_user_index
601                            [current_config_index]);
602           vlib_cli_output (vm, "  [end] %v", n->name);
603         }
604     }
605 }
606
607 static clib_error_t *
608 set_interface_features_command_fn (vlib_main_t * vm,
609                                    unformat_input_t * input,
610                                    vlib_cli_command_t * cmd)
611 {
612   vnet_main_t *vnm = vnet_get_main ();
613   unformat_input_t _line_input, *line_input = &_line_input;
614   clib_error_t *error = 0;
615
616   u8 *arc_name = 0;
617   u8 *feature_name = 0;
618   u32 sw_if_index = ~0;
619   u8 enable = 1;
620
621   /* Get a line of input. */
622   if (!unformat_user (input, unformat_line_input, line_input))
623     return 0;
624
625   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
626     {
627       if (unformat
628           (line_input, "%U %s arc %s", unformat_vnet_sw_interface, vnm,
629            &sw_if_index, &feature_name, &arc_name))
630         ;
631       else if (unformat (line_input, "disable"))
632         enable = 0;
633       else
634         {
635           error = unformat_parse_error (line_input);
636           goto done;
637         }
638     }
639   if (!feature_name || !arc_name)
640     {
641       error = clib_error_return (0, "Both feature name and arc required...");
642       goto done;
643     }
644
645   if (sw_if_index == ~0)
646     {
647       error = clib_error_return (0, "Interface not specified...");
648       goto done;
649     }
650
651   vec_add1 (arc_name, 0);
652   vec_add1 (feature_name, 0);
653
654   u8 arc_index;
655
656   arc_index = vnet_get_feature_arc_index ((const char *) arc_name);
657
658   if (arc_index == (u8) ~ 0)
659     {
660       error =
661         clib_error_return (0, "Unknown arc name (%s)... ",
662                            (const char *) arc_name);
663       goto done;
664     }
665
666   vnet_feature_registration_t *reg;
667   reg =
668     vnet_get_feature_reg ((const char *) arc_name,
669                           (const char *) feature_name);
670   if (reg == 0)
671     {
672       error =
673         clib_error_return (0,
674                            "Feature (%s) not registered to arc (%s)... See 'show features verbose' for valid feature/arc combinations. ",
675                            feature_name, arc_name);
676       goto done;
677     }
678   if (reg->enable_disable_cb)
679     error = reg->enable_disable_cb (sw_if_index, enable);
680   if (!error)
681     vnet_feature_enable_disable ((const char *) arc_name,
682                                  (const char *) feature_name, sw_if_index,
683                                  enable, 0, 0);
684
685 done:
686   vec_free (feature_name);
687   vec_free (arc_name);
688   unformat_free (line_input);
689   return error;
690 }
691
692 /*?
693  * Set feature for given interface
694  *
695  * @cliexpar
696  * Example:
697  * @cliexcmd{set interface feature GigabitEthernet2/0/0 ip4_flow_classify arc ip4_unicast}
698  * @cliexend
699  * @endparblock
700 ?*/
701 VLIB_CLI_COMMAND (set_interface_feature_command, static) = {
702   .path = "set interface feature",
703   .short_help = "set interface feature <intfc> <feature_name> arc <arc_name> "
704       "[disable]",
705   .function = set_interface_features_command_fn,
706 };
707
708 static clib_error_t *
709 vnet_feature_add_del_sw_interface (vnet_main_t * vnm, u32 sw_if_index,
710                                    u32 is_add)
711 {
712   vnet_feature_main_t *fm = &feature_main;
713   const vnet_feature_arc_registration_t *far;
714
715   if (is_add)
716     return 0;
717
718   /*
719    * remove all enabled features from an interface on deletion
720    */
721   for (far = fm->next_arc; far != 0; far = far->next)
722     {
723       const u8 arc_index = far->feature_arc_index;
724       vnet_feature_config_main_t *cm =
725         vec_elt_at_index (fm->feature_config_mains, arc_index);
726       const u32 ci =
727         vec_len (cm->config_index_by_sw_if_index) <=
728         sw_if_index ? ~0 : vec_elt (cm->config_index_by_sw_if_index,
729                                     sw_if_index);
730
731       if (~0 == ci)
732         continue;
733
734       fm->sw_if_index_has_features[arc_index] =
735         clib_bitmap_set (fm->sw_if_index_has_features[arc_index], sw_if_index,
736                          0);
737
738       vnet_feature_reg_invoke (sw_if_index, arc_index, 0);
739
740       if (vec_len (fm->feature_count_by_sw_if_index[arc_index]) > sw_if_index)
741         vec_elt (fm->feature_count_by_sw_if_index[arc_index], sw_if_index) =
742           0;
743
744       vec_elt (cm->config_index_by_sw_if_index, sw_if_index) = ~0;
745       vnet_config_del (&cm->config_main, ci);
746     }
747
748   return 0;
749 }
750
751 VNET_SW_INTERFACE_ADD_DEL_FUNCTION_PRIO (vnet_feature_add_del_sw_interface,
752                                          VNET_ITF_FUNC_PRIORITY_HIGH);
753
754 /*
755  * fd.io coding-style-patch-verification: ON
756  *
757  * Local Variables:
758  * eval: (c-set-style "gnu")
759  * End:
760  */