BD ARP entry use common API types
[vpp.git] / src / vnet / l2 / l2_in_out_feat_arc.c
1 /*
2  * l2_in_out_feat_arc.c : layer 2 input/output acl processing
3  *
4  * Copyright (c) 2013,2018 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/pg/pg.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/ethernet/packet.h>
23 #include <vnet/ip/ip_packet.h>
24 #include <vnet/ip/ip4_packet.h>
25 #include <vnet/ip/ip6_packet.h>
26 #include <vlib/cli.h>
27 #include <vnet/l2/l2_input.h>
28 #include <vnet/l2/l2_output.h>
29 #include <vnet/l2/feat_bitmap.h>
30 #include <vnet/l2/l2_in_out_feat_arc.h>
31
32 #include <vppinfra/error.h>
33 #include <vppinfra/hash.h>
34 #include <vppinfra/cache.h>
35
36
37 typedef struct
38 {
39
40   /* Next nodes for each feature */
41   u32 feat_next_node_index[IN_OUT_FEAT_ARC_N_TABLE_GROUPS][32];
42   u8 ip4_feat_arc_index[IN_OUT_FEAT_ARC_N_TABLE_GROUPS];
43   u8 ip6_feat_arc_index[IN_OUT_FEAT_ARC_N_TABLE_GROUPS];
44   u8 nonip_feat_arc_index[IN_OUT_FEAT_ARC_N_TABLE_GROUPS];
45   u32 next_slot[IN_OUT_FEAT_ARC_N_TABLE_GROUPS];
46
47   /* convenience variables */
48   vlib_main_t *vlib_main;
49   vnet_main_t *vnet_main;
50 } l2_in_out_feat_arc_main_t __attribute__ ((aligned (CLIB_CACHE_LINE_BYTES)));
51
52 typedef struct
53 {
54   u32 sw_if_index;
55   u32 next_index;
56   u32 feature_bitmap;
57   u16 ethertype;
58   u8 arc_head;
59 } l2_in_out_feat_arc_trace_t;
60
61 /* packet trace format function */
62 static u8 *
63 format_l2_in_out_feat_arc_trace (u8 * s, u32 is_output, va_list * args)
64 {
65   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
66   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
67   l2_in_out_feat_arc_trace_t *t =
68     va_arg (*args, l2_in_out_feat_arc_trace_t *);
69
70   s =
71     format (s,
72             "%s: head %d feature_bitmap %x ethertype %x sw_if_index %d, next_index %d",
73             is_output ? "OUT-FEAT-ARC" : "IN-FEAT-ARC", t->arc_head,
74             t->feature_bitmap, t->ethertype, t->sw_if_index, t->next_index);
75   return s;
76 }
77
78 static u8 *
79 format_l2_in_feat_arc_trace (u8 * s, va_list * args)
80 {
81   return format_l2_in_out_feat_arc_trace (s,
82                                           IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
83                                           args);
84 }
85
86 static u8 *
87 format_l2_out_feat_arc_trace (u8 * s, va_list * args)
88 {
89   return format_l2_in_out_feat_arc_trace (s,
90                                           IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
91                                           args);
92 }
93
94
95 #define foreach_l2_in_feat_arc_error                   \
96 _(DEFAULT, "in default")                         \
97
98
99 #define foreach_l2_out_feat_arc_error                   \
100 _(DEFAULT, "out default")                         \
101
102
103 typedef enum
104 {
105 #define _(sym,str) L2_IN_FEAT_ARC_ERROR_##sym,
106   foreach_l2_in_feat_arc_error
107 #undef _
108     L2_IN_FEAT_ARC_N_ERROR,
109 } l2_in_feat_arc_error_t;
110
111 static char *l2_in_feat_arc_error_strings[] = {
112 #define _(sym,string) string,
113   foreach_l2_in_feat_arc_error
114 #undef _
115 };
116
117 typedef enum
118 {
119 #define _(sym,str) L2_OUT_FEAT_ARC_ERROR_##sym,
120   foreach_l2_out_feat_arc_error
121 #undef _
122     L2_OUT_FEAT_ARC_N_ERROR,
123 } l2_out_feat_arc_error_t;
124
125 static char *l2_out_feat_arc_error_strings[] = {
126 #define _(sym,string) string,
127   foreach_l2_out_feat_arc_error
128 #undef _
129 };
130
131
132 l2_in_out_feat_arc_main_t l2_in_out_feat_arc_main;
133
134 #define get_u16(addr) ( *((u16 *)(addr)) )
135 #define L2_FEAT_ARC_VEC_SIZE 2
136
137 static_always_inline void
138 buffer_prefetch_xN (int vector_sz, vlib_buffer_t ** b)
139 {
140   int ii;
141   for (ii = 0; ii < vector_sz; ii++)
142     CLIB_PREFETCH (b[ii], CLIB_CACHE_LINE_BYTES, STORE);
143 }
144
145 static_always_inline void
146 get_sw_if_index_xN (int vector_sz, int is_output, vlib_buffer_t ** b,
147                     u32 * out_sw_if_index)
148 {
149   int ii;
150   for (ii = 0; ii < vector_sz; ii++)
151     if (is_output)
152       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_TX];
153     else
154       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_RX];
155 }
156
157 static_always_inline void
158 get_ethertype_xN (int vector_sz, int is_output, vlib_buffer_t ** b,
159                   u16 * out_ethertype)
160 {
161   int ii;
162   for (ii = 0; ii < vector_sz; ii++)
163     {
164       ethernet_header_t *h0 = vlib_buffer_get_current (b[ii]);
165       u8 *l3h0 = (u8 *) h0 + vnet_buffer (b[ii])->l2.l2_len;
166       out_ethertype[ii] = clib_net_to_host_u16 (get_u16 (l3h0 - 2));
167     }
168 }
169
170
171 static_always_inline void
172 set_next_in_arc_head_xN (int vector_sz, int is_output, u32 * next_nodes,
173                          vlib_buffer_t ** b, u32 * sw_if_index,
174                          u16 * ethertype, u8 ip4_arc, u8 ip6_arc,
175                          u8 nonip_arc, u16 * out_next)
176 {
177   int ii;
178   for (ii = 0; ii < vector_sz; ii++)
179     {
180       u32 next_index = 0;
181       u8 feature_arc;
182       switch (ethertype[ii])
183         {
184         case ETHERNET_TYPE_IP4:
185           feature_arc = ip4_arc;
186           break;
187         case ETHERNET_TYPE_IP6:
188           feature_arc = ip6_arc;
189           break;
190         default:
191           feature_arc = nonip_arc;
192         }
193       if (PREDICT_TRUE (vnet_have_features (feature_arc, sw_if_index[ii])))
194         vnet_feature_arc_start (feature_arc,
195                                 sw_if_index[ii], &next_index, b[ii]);
196       else
197         next_index =
198           vnet_l2_feature_next (b[ii], next_nodes,
199                                 is_output ? L2OUTPUT_FEAT_OUTPUT_FEAT_ARC :
200                                 L2INPUT_FEAT_INPUT_FEAT_ARC);
201
202       out_next[ii] = next_index;
203     }
204 }
205
206 static_always_inline void
207 set_next_in_arc_tail_xN (int vector_sz, int is_output, u32 * next_nodes,
208                          vlib_buffer_t ** b, u16 * out_next)
209 {
210   int ii;
211   for (ii = 0; ii < vector_sz; ii++)
212     {
213       out_next[ii] =
214         vnet_l2_feature_next (b[ii], next_nodes,
215                               is_output ? L2OUTPUT_FEAT_OUTPUT_FEAT_ARC :
216                               L2INPUT_FEAT_INPUT_FEAT_ARC);
217     }
218
219 }
220
221
222 static_always_inline void
223 maybe_trace_xN (int vector_sz, int arc_head, vlib_main_t * vm,
224                 vlib_node_runtime_t * node, vlib_buffer_t ** b,
225                 u32 * sw_if_index, u16 * ethertype, u16 * next)
226 {
227   int ii;
228   for (ii = 0; ii < vector_sz; ii++)
229     if (PREDICT_FALSE (b[ii]->flags & VLIB_BUFFER_IS_TRACED))
230       {
231         l2_in_out_feat_arc_trace_t *t =
232           vlib_add_trace (vm, node, b[ii], sizeof (*t));
233         t->arc_head = arc_head;
234         t->sw_if_index = arc_head ? sw_if_index[ii] : ~0;
235         t->feature_bitmap = vnet_buffer (b[ii])->l2.feature_bitmap;
236         t->ethertype = arc_head ? ethertype[ii] : 0;
237         t->next_index = next[ii];
238       }
239 }
240
241 always_inline uword
242 l2_in_out_feat_arc_node_fn (vlib_main_t * vm,
243                             vlib_node_runtime_t * node, vlib_frame_t * frame,
244                             int is_output, vlib_node_registration_t * fa_node,
245                             int arc_head, int do_trace)
246 {
247   u32 n_left, *from;
248   u16 nexts[VLIB_FRAME_SIZE], *next;
249   u16 ethertypes[VLIB_FRAME_SIZE], *ethertype;
250   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
251   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index;
252   l2_in_out_feat_arc_main_t *fam = &l2_in_out_feat_arc_main;
253
254   u8 ip4_arc_index = fam->ip4_feat_arc_index[is_output];
255   u8 ip6_arc_index = fam->ip6_feat_arc_index[is_output];
256   u8 nonip_arc_index = fam->nonip_feat_arc_index[is_output];
257   u32 *next_node_indices = fam->feat_next_node_index[is_output];
258
259   from = vlib_frame_vector_args (frame);
260   vlib_get_buffers (vm, from, bufs, frame->n_vectors);
261   /* set the initial values for the current buffer the next pointers */
262   b = bufs;
263   next = nexts;
264   ethertype = ethertypes;
265   sw_if_index = sw_if_indices;
266   n_left = frame->n_vectors;
267
268   CLIB_PREFETCH (next_node_indices, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
269
270   while (n_left > 3 * L2_FEAT_ARC_VEC_SIZE)
271     {
272       const int vec_sz = L2_FEAT_ARC_VEC_SIZE;
273       /* prefetch next N buffers */
274       buffer_prefetch_xN (vec_sz, b + 2 * vec_sz);
275
276       if (arc_head)
277         {
278           get_sw_if_index_xN (vec_sz, is_output, b, sw_if_index);
279           get_ethertype_xN (vec_sz, is_output, b, ethertype);
280           set_next_in_arc_head_xN (vec_sz, is_output, next_node_indices, b,
281                                    sw_if_index, ethertype, ip4_arc_index,
282                                    ip6_arc_index, nonip_arc_index, next);
283         }
284       else
285         {
286           set_next_in_arc_tail_xN (vec_sz, is_output, next_node_indices, b,
287                                    next);
288         }
289       if (do_trace)
290         maybe_trace_xN (vec_sz, arc_head, vm, node, b, sw_if_index, ethertype,
291                         next);
292
293       next += vec_sz;
294       b += vec_sz;
295       sw_if_index += vec_sz;
296       ethertype += vec_sz;
297
298       n_left -= vec_sz;
299     }
300
301   while (n_left > 0)
302     {
303       const int vec_sz = 1;
304
305       if (arc_head)
306         {
307           get_sw_if_index_xN (vec_sz, is_output, b, sw_if_index);
308           get_ethertype_xN (vec_sz, is_output, b, ethertype);
309           set_next_in_arc_head_xN (vec_sz, is_output, next_node_indices, b,
310                                    sw_if_index, ethertype, ip4_arc_index,
311                                    ip6_arc_index, nonip_arc_index, next);
312         }
313       else
314         {
315           set_next_in_arc_tail_xN (vec_sz, is_output, next_node_indices, b,
316                                    next);
317         }
318       if (do_trace)
319         maybe_trace_xN (vec_sz, arc_head, vm, node, b, sw_if_index, ethertype,
320                         next);
321
322       next += vec_sz;
323       b += vec_sz;
324       sw_if_index += vec_sz;
325       ethertype += vec_sz;
326
327       n_left -= vec_sz;
328     }
329
330   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
331
332   return frame->n_vectors;
333 }
334
335 static vlib_node_registration_t l2_in_feat_arc_node;
336 static uword
337 l2_in_feat_arc_node_fn (vlib_main_t * vm,
338                         vlib_node_runtime_t * node, vlib_frame_t * frame)
339 {
340   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
341     return l2_in_out_feat_arc_node_fn (vm, node, frame,
342                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
343                                        &l2_in_feat_arc_node, 1, 1);
344   else
345     return l2_in_out_feat_arc_node_fn (vm, node, frame,
346                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
347                                        &l2_in_feat_arc_node, 1, 0);
348 }
349
350 static vlib_node_registration_t l2_out_feat_arc_node;
351 static uword
352 l2_out_feat_arc_node_fn (vlib_main_t * vm,
353                          vlib_node_runtime_t * node, vlib_frame_t * frame)
354 {
355   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
356     return l2_in_out_feat_arc_node_fn (vm, node, frame,
357                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
358                                        &l2_out_feat_arc_node, 1, 1);
359   else
360     return l2_in_out_feat_arc_node_fn (vm, node, frame,
361                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
362                                        &l2_out_feat_arc_node, 1, 0);
363 }
364
365 static vlib_node_registration_t l2_in_feat_arc_end_node;
366 static uword
367 l2_in_feat_arc_end_node_fn (vlib_main_t * vm,
368                             vlib_node_runtime_t * node, vlib_frame_t * frame)
369 {
370   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
371     return l2_in_out_feat_arc_node_fn (vm, node, frame,
372                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
373                                        &l2_in_feat_arc_end_node, 0, 1);
374   else
375     return l2_in_out_feat_arc_node_fn (vm, node, frame,
376                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
377                                        &l2_in_feat_arc_end_node, 0, 0);
378 }
379
380 static vlib_node_registration_t l2_out_feat_arc_end_node;
381 static uword
382 l2_out_feat_arc_end_node_fn (vlib_main_t * vm,
383                              vlib_node_runtime_t * node, vlib_frame_t * frame)
384 {
385   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
386     return l2_in_out_feat_arc_node_fn (vm, node, frame,
387                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
388                                        &l2_out_feat_arc_end_node, 0, 1);
389   else
390     return l2_in_out_feat_arc_node_fn (vm, node, frame,
391                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
392                                        &l2_out_feat_arc_end_node, 0, 0);
393 }
394
395
396 void
397 vnet_l2_in_out_feat_arc_enable_disable (u32 sw_if_index, int is_output,
398                                         int enable_disable)
399 {
400   if (is_output)
401     l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_OUTPUT_FEAT_ARC,
402                                  (u32) enable_disable);
403   else
404     l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_INPUT_FEAT_ARC,
405                                 (u32) enable_disable);
406 }
407
408 /* *INDENT-OFF* */
409 VNET_FEATURE_ARC_INIT (l2_in_ip4_arc, static) =
410 {
411   .arc_name  = "l2-input-ip4",
412   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
413   .arc_index_ptr = &l2_in_out_feat_arc_main.ip4_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
414 };
415
416 VNET_FEATURE_ARC_INIT (l2_out_ip4_arc, static) =
417 {
418   .arc_name  = "l2-output-ip4",
419   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
420   .arc_index_ptr = &l2_in_out_feat_arc_main.ip4_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
421 };
422
423 VNET_FEATURE_ARC_INIT (l2_out_ip6_arc, static) =
424 {
425   .arc_name  = "l2-input-ip6",
426   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
427   .arc_index_ptr = &l2_in_out_feat_arc_main.ip6_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
428 };
429 VNET_FEATURE_ARC_INIT (l2_in_ip6_arc, static) =
430 {
431   .arc_name  = "l2-output-ip6",
432   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
433   .arc_index_ptr = &l2_in_out_feat_arc_main.ip6_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
434 };
435
436 VNET_FEATURE_ARC_INIT (l2_out_nonip_arc, static) =
437 {
438   .arc_name  = "l2-input-nonip",
439   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
440   .arc_index_ptr = &l2_in_out_feat_arc_main.nonip_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
441 };
442 VNET_FEATURE_ARC_INIT (l2_in_nonip_arc, static) =
443 {
444   .arc_name  = "l2-output-nonip",
445   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
446   .arc_index_ptr = &l2_in_out_feat_arc_main.nonip_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
447 };
448
449
450 /* *INDENT-ON* */
451
452
453 /* *INDENT-OFF* */
454 VLIB_REGISTER_NODE (l2_in_feat_arc_node,static) = {
455   .name = "l2-input-feat-arc",
456   .function = l2_in_feat_arc_node_fn,
457   .vector_size = sizeof (u32),
458   .format_trace = format_l2_in_feat_arc_trace,
459   .type = VLIB_NODE_TYPE_INTERNAL,
460
461   .n_errors = ARRAY_LEN(l2_in_feat_arc_error_strings),
462   .error_strings = l2_in_feat_arc_error_strings,
463
464 };
465
466 VLIB_REGISTER_NODE (l2_out_feat_arc_node,static) = {
467   .name = "l2-output-feat-arc",
468   .function = l2_out_feat_arc_node_fn,
469   .vector_size = sizeof (u32),
470   .format_trace = format_l2_out_feat_arc_trace,
471   .type = VLIB_NODE_TYPE_INTERNAL,
472
473   .n_errors = ARRAY_LEN(l2_out_feat_arc_error_strings),
474   .error_strings = l2_out_feat_arc_error_strings,
475
476 };
477
478 VLIB_REGISTER_NODE (l2_in_feat_arc_end_node,static) = {
479   .name = "l2-input-feat-arc-end",
480   .function = l2_in_feat_arc_end_node_fn,
481   .vector_size = sizeof (u32),
482   .format_trace = format_l2_in_feat_arc_trace,
483   .sibling_of = "l2-input-feat-arc",
484 };
485
486 VLIB_REGISTER_NODE (l2_out_feat_arc_end_node,static) = {
487   .name = "l2-output-feat-arc-end",
488   .function = l2_out_feat_arc_end_node_fn,
489   .vector_size = sizeof (u32),
490   .format_trace = format_l2_out_feat_arc_trace,
491   .sibling_of = "l2-output-feat-arc",
492 };
493
494 VNET_FEATURE_INIT (l2_in_ip4_arc_end, static) =
495 {
496   .arc_name = "l2-input-ip4",
497   .node_name = "l2-input-feat-arc-end",
498   .runs_before = 0,     /* not before any other features */
499 };
500
501 VNET_FEATURE_INIT (l2_out_ip4_arc_end, static) =
502 {
503   .arc_name = "l2-output-ip4",
504   .node_name = "l2-output-feat-arc-end",
505   .runs_before = 0,     /* not before any other features */
506 };
507
508 VNET_FEATURE_INIT (l2_in_ip6_arc_end, static) =
509 {
510   .arc_name = "l2-input-ip6",
511   .node_name = "l2-input-feat-arc-end",
512   .runs_before = 0,     /* not before any other features */
513 };
514
515
516 VNET_FEATURE_INIT (l2_out_ip6_arc_end, static) =
517 {
518   .arc_name = "l2-output-ip6",
519   .node_name = "l2-output-feat-arc-end",
520   .runs_before = 0,     /* not before any other features */
521 };
522
523 VNET_FEATURE_INIT (l2_in_nonip_arc_end, static) =
524 {
525   .arc_name = "l2-input-nonip",
526   .node_name = "l2-input-feat-arc-end",
527   .runs_before = 0,     /* not before any other features */
528 };
529
530
531 VNET_FEATURE_INIT (l2_out_nonip_arc_end, static) =
532 {
533   .arc_name = "l2-output-nonip",
534   .node_name = "l2-output-feat-arc-end",
535   .runs_before = 0,     /* not before any other features */
536 };
537
538
539 VLIB_NODE_FUNCTION_MULTIARCH (l2_in_feat_arc_node, l2_in_feat_arc_node_fn)
540 VLIB_NODE_FUNCTION_MULTIARCH (l2_out_feat_arc_node, l2_out_feat_arc_node_fn)
541 VLIB_NODE_FUNCTION_MULTIARCH (l2_in_feat_arc_end_node, l2_in_feat_arc_end_node_fn)
542 VLIB_NODE_FUNCTION_MULTIARCH (l2_out_feat_arc_end_node, l2_out_feat_arc_end_node_fn)
543
544 /* *INDENT-ON* */
545
546
547 clib_error_t *
548 l2_in_out_feat_arc_init (vlib_main_t * vm)
549 {
550   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
551
552   mp->vlib_main = vm;
553   mp->vnet_main = vnet_get_main ();
554
555   /* Initialize the feature next-node indexes */
556   feat_bitmap_init_next_nodes (vm,
557                                l2_in_feat_arc_end_node.index,
558                                L2INPUT_N_FEAT,
559                                l2input_get_feat_names (),
560                                mp->feat_next_node_index
561                                [IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP]);
562   feat_bitmap_init_next_nodes (vm, l2_out_feat_arc_end_node.index,
563                                L2OUTPUT_N_FEAT, l2output_get_feat_names (),
564                                mp->feat_next_node_index
565                                [IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP]);
566   return 0;
567 }
568
569
570 static int
571 l2_has_features (u32 sw_if_index, int is_output)
572 {
573   int has_features = 0;
574   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
575   has_features +=
576     vnet_have_features (mp->ip4_feat_arc_index[is_output], sw_if_index);
577   has_features +=
578     vnet_have_features (mp->ip6_feat_arc_index[is_output], sw_if_index);
579   has_features +=
580     vnet_have_features (mp->nonip_feat_arc_index[is_output], sw_if_index);
581   return has_features > 0;
582 }
583
584 static int
585 l2_is_output_arc (u8 arc_index)
586 {
587   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
588   int idx = IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP;
589   return (mp->ip4_feat_arc_index[idx] == arc_index
590           || mp->ip6_feat_arc_index[idx] == arc_index
591           || mp->nonip_feat_arc_index[idx] == arc_index);
592 }
593
594 static int
595 l2_is_input_arc (u8 arc_index)
596 {
597   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
598   int idx = IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP;
599   return (mp->ip4_feat_arc_index[idx] == arc_index
600           || mp->ip6_feat_arc_index[idx] == arc_index
601           || mp->nonip_feat_arc_index[idx] == arc_index);
602 }
603
604 int
605 vnet_l2_feature_enable_disable (const char *arc_name, const char *node_name,
606                                 u32 sw_if_index, int enable_disable,
607                                 void *feature_config,
608                                 u32 n_feature_config_bytes)
609 {
610   u8 arc_index = vnet_get_feature_arc_index (arc_name);
611   if (arc_index == (u8) ~ 0)
612     return VNET_API_ERROR_INVALID_VALUE;
613
614   /* check the state before we tried to enable/disable */
615   int had_features = vnet_have_features (arc_index, sw_if_index);
616
617   int ret = vnet_feature_enable_disable (arc_name, node_name, sw_if_index,
618                                          enable_disable, feature_config,
619                                          n_feature_config_bytes);
620   if (ret)
621     return ret;
622
623   int has_features = vnet_have_features (arc_index, sw_if_index);
624
625   if (had_features != has_features)
626     {
627       if (l2_is_output_arc (arc_index))
628         {
629           vnet_l2_in_out_feat_arc_enable_disable (sw_if_index, 1,
630                                                   l2_has_features
631                                                   (sw_if_index, 1));
632         }
633       if (l2_is_input_arc (arc_index))
634         {
635           vnet_l2_in_out_feat_arc_enable_disable (sw_if_index, 0,
636                                                   l2_has_features
637                                                   (sw_if_index, 0));
638         }
639     }
640   return 0;
641 }
642
643
644 VLIB_INIT_FUNCTION (l2_in_out_feat_arc_init);
645
646 /*
647  * fd.io coding-style-patch-verification: ON
648  *
649  * Local Variables:
650  * eval: (c-set-style "gnu")
651  * End:
652  */