8717ea908a9de8d56b295a779dda60d32ddeb010
[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 extern l2_in_out_feat_arc_main_t l2_in_out_feat_arc_main;
132
133 #ifndef CLIB_MARCH_VARIANT
134 l2_in_out_feat_arc_main_t l2_in_out_feat_arc_main;
135 #endif /* CLIB_MARCH_VARIANT */
136
137 #define get_u16(addr) ( *((u16 *)(addr)) )
138 #define L2_FEAT_ARC_VEC_SIZE 2
139
140 static_always_inline void
141 buffer_prefetch_xN (int vector_sz, vlib_buffer_t ** b)
142 {
143   int ii;
144   for (ii = 0; ii < vector_sz; ii++)
145     CLIB_PREFETCH (b[ii], CLIB_CACHE_LINE_BYTES, STORE);
146 }
147
148 static_always_inline void
149 get_sw_if_index_xN (int vector_sz, int is_output, vlib_buffer_t ** b,
150                     u32 * out_sw_if_index)
151 {
152   int ii;
153   for (ii = 0; ii < vector_sz; ii++)
154     if (is_output)
155       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_TX];
156     else
157       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_RX];
158 }
159
160 static_always_inline void
161 get_ethertype_xN (int vector_sz, int is_output, vlib_buffer_t ** b,
162                   u16 * out_ethertype)
163 {
164   int ii;
165   for (ii = 0; ii < vector_sz; ii++)
166     {
167       ethernet_header_t *h0 = vlib_buffer_get_current (b[ii]);
168       u8 *l3h0 = (u8 *) h0 + vnet_buffer (b[ii])->l2.l2_len;
169       out_ethertype[ii] = clib_net_to_host_u16 (get_u16 (l3h0 - 2));
170     }
171 }
172
173
174 static_always_inline void
175 set_next_in_arc_head_xN (int vector_sz, int is_output, u32 * next_nodes,
176                          vlib_buffer_t ** b, u32 * sw_if_index,
177                          u16 * ethertype, u8 ip4_arc, u8 ip6_arc,
178                          u8 nonip_arc, u16 * out_next)
179 {
180   int ii;
181   for (ii = 0; ii < vector_sz; ii++)
182     {
183       u32 next_index = 0;
184       u8 feature_arc;
185       switch (ethertype[ii])
186         {
187         case ETHERNET_TYPE_IP4:
188           feature_arc = ip4_arc;
189           break;
190         case ETHERNET_TYPE_IP6:
191           feature_arc = ip6_arc;
192           break;
193         default:
194           feature_arc = nonip_arc;
195         }
196       if (PREDICT_TRUE (vnet_have_features (feature_arc, sw_if_index[ii])))
197         vnet_feature_arc_start (feature_arc,
198                                 sw_if_index[ii], &next_index, b[ii]);
199       else
200         next_index =
201           vnet_l2_feature_next (b[ii], next_nodes,
202                                 is_output ? L2OUTPUT_FEAT_OUTPUT_FEAT_ARC :
203                                 L2INPUT_FEAT_INPUT_FEAT_ARC);
204
205       out_next[ii] = next_index;
206     }
207 }
208
209 static_always_inline void
210 set_next_in_arc_tail_xN (int vector_sz, int is_output, u32 * next_nodes,
211                          vlib_buffer_t ** b, u16 * out_next)
212 {
213   int ii;
214   for (ii = 0; ii < vector_sz; ii++)
215     {
216       out_next[ii] =
217         vnet_l2_feature_next (b[ii], next_nodes,
218                               is_output ? L2OUTPUT_FEAT_OUTPUT_FEAT_ARC :
219                               L2INPUT_FEAT_INPUT_FEAT_ARC);
220     }
221
222 }
223
224
225 static_always_inline void
226 maybe_trace_xN (int vector_sz, int arc_head, vlib_main_t * vm,
227                 vlib_node_runtime_t * node, vlib_buffer_t ** b,
228                 u32 * sw_if_index, u16 * ethertype, u16 * next)
229 {
230   int ii;
231   for (ii = 0; ii < vector_sz; ii++)
232     if (PREDICT_FALSE (b[ii]->flags & VLIB_BUFFER_IS_TRACED))
233       {
234         l2_in_out_feat_arc_trace_t *t =
235           vlib_add_trace (vm, node, b[ii], sizeof (*t));
236         t->arc_head = arc_head;
237         t->sw_if_index = arc_head ? sw_if_index[ii] : ~0;
238         t->feature_bitmap = vnet_buffer (b[ii])->l2.feature_bitmap;
239         t->ethertype = arc_head ? ethertype[ii] : 0;
240         t->next_index = next[ii];
241       }
242 }
243
244 always_inline uword
245 l2_in_out_feat_arc_node_fn (vlib_main_t * vm,
246                             vlib_node_runtime_t * node, vlib_frame_t * frame,
247                             int is_output, vlib_node_registration_t * fa_node,
248                             int arc_head, int do_trace)
249 {
250   u32 n_left, *from;
251   u16 nexts[VLIB_FRAME_SIZE], *next;
252   u16 ethertypes[VLIB_FRAME_SIZE], *ethertype;
253   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
254   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index;
255   l2_in_out_feat_arc_main_t *fam = &l2_in_out_feat_arc_main;
256
257   u8 ip4_arc_index = fam->ip4_feat_arc_index[is_output];
258   u8 ip6_arc_index = fam->ip6_feat_arc_index[is_output];
259   u8 nonip_arc_index = fam->nonip_feat_arc_index[is_output];
260   u32 *next_node_indices = fam->feat_next_node_index[is_output];
261
262   from = vlib_frame_vector_args (frame);
263   vlib_get_buffers (vm, from, bufs, frame->n_vectors);
264   /* set the initial values for the current buffer the next pointers */
265   b = bufs;
266   next = nexts;
267   ethertype = ethertypes;
268   sw_if_index = sw_if_indices;
269   n_left = frame->n_vectors;
270
271   CLIB_PREFETCH (next_node_indices, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
272
273   while (n_left > 3 * L2_FEAT_ARC_VEC_SIZE)
274     {
275       const int vec_sz = L2_FEAT_ARC_VEC_SIZE;
276       /* prefetch next N buffers */
277       buffer_prefetch_xN (vec_sz, b + 2 * vec_sz);
278
279       if (arc_head)
280         {
281           get_sw_if_index_xN (vec_sz, is_output, b, sw_if_index);
282           get_ethertype_xN (vec_sz, is_output, b, ethertype);
283           set_next_in_arc_head_xN (vec_sz, is_output, next_node_indices, b,
284                                    sw_if_index, ethertype, ip4_arc_index,
285                                    ip6_arc_index, nonip_arc_index, next);
286         }
287       else
288         {
289           set_next_in_arc_tail_xN (vec_sz, is_output, next_node_indices, b,
290                                    next);
291         }
292       if (do_trace)
293         maybe_trace_xN (vec_sz, arc_head, vm, node, b, sw_if_index, ethertype,
294                         next);
295
296       next += vec_sz;
297       b += vec_sz;
298       sw_if_index += vec_sz;
299       ethertype += vec_sz;
300
301       n_left -= vec_sz;
302     }
303
304   while (n_left > 0)
305     {
306       const int vec_sz = 1;
307
308       if (arc_head)
309         {
310           get_sw_if_index_xN (vec_sz, is_output, b, sw_if_index);
311           get_ethertype_xN (vec_sz, is_output, b, ethertype);
312           set_next_in_arc_head_xN (vec_sz, is_output, next_node_indices, b,
313                                    sw_if_index, ethertype, ip4_arc_index,
314                                    ip6_arc_index, nonip_arc_index, next);
315         }
316       else
317         {
318           set_next_in_arc_tail_xN (vec_sz, is_output, next_node_indices, b,
319                                    next);
320         }
321       if (do_trace)
322         maybe_trace_xN (vec_sz, arc_head, vm, node, b, sw_if_index, ethertype,
323                         next);
324
325       next += vec_sz;
326       b += vec_sz;
327       sw_if_index += vec_sz;
328       ethertype += vec_sz;
329
330       n_left -= vec_sz;
331     }
332
333   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
334
335   return frame->n_vectors;
336 }
337
338 VLIB_NODE_FN (l2_in_feat_arc_node) (vlib_main_t * vm,
339                                     vlib_node_runtime_t * node,
340                                     vlib_frame_t * frame)
341 {
342   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
343     return l2_in_out_feat_arc_node_fn (vm, node, frame,
344                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
345                                        &l2_in_feat_arc_node, 1, 1);
346   else
347     return l2_in_out_feat_arc_node_fn (vm, node, frame,
348                                        IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP,
349                                        &l2_in_feat_arc_node, 1, 0);
350 }
351
352 VLIB_NODE_FN (l2_out_feat_arc_node) (vlib_main_t * vm,
353                                      vlib_node_runtime_t * node,
354                                      vlib_frame_t * frame)
355 {
356   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
357     return l2_in_out_feat_arc_node_fn (vm, node, frame,
358                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
359                                        &l2_out_feat_arc_node, 1, 1);
360   else
361     return l2_in_out_feat_arc_node_fn (vm, node, frame,
362                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
363                                        &l2_out_feat_arc_node, 1, 0);
364 }
365
366 VLIB_NODE_FN (l2_in_feat_arc_end_node) (vlib_main_t * vm,
367                                         vlib_node_runtime_t * node,
368                                         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 VLIB_NODE_FN (l2_out_feat_arc_end_node) (vlib_main_t * vm,
381                                          vlib_node_runtime_t * node,
382                                          vlib_frame_t * frame)
383 {
384   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
385     return l2_in_out_feat_arc_node_fn (vm, node, frame,
386                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
387                                        &l2_out_feat_arc_end_node, 0, 1);
388   else
389     return l2_in_out_feat_arc_node_fn (vm, node, frame,
390                                        IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP,
391                                        &l2_out_feat_arc_end_node, 0, 0);
392 }
393
394
395 #ifndef CLIB_MARCH_VARIANT
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 #endif /* CLIB_MARCH_VARIANT */
408
409 /* *INDENT-OFF* */
410 VNET_FEATURE_ARC_INIT (l2_in_ip4_arc, static) =
411 {
412   .arc_name  = "l2-input-ip4",
413   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
414   .arc_index_ptr = &l2_in_out_feat_arc_main.ip4_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
415 };
416
417 VNET_FEATURE_ARC_INIT (l2_out_ip4_arc, static) =
418 {
419   .arc_name  = "l2-output-ip4",
420   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
421   .arc_index_ptr = &l2_in_out_feat_arc_main.ip4_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
422 };
423
424 VNET_FEATURE_ARC_INIT (l2_out_ip6_arc, static) =
425 {
426   .arc_name  = "l2-input-ip6",
427   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
428   .arc_index_ptr = &l2_in_out_feat_arc_main.ip6_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
429 };
430 VNET_FEATURE_ARC_INIT (l2_in_ip6_arc, static) =
431 {
432   .arc_name  = "l2-output-ip6",
433   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
434   .arc_index_ptr = &l2_in_out_feat_arc_main.ip6_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
435 };
436
437 VNET_FEATURE_ARC_INIT (l2_out_nonip_arc, static) =
438 {
439   .arc_name  = "l2-input-nonip",
440   .start_nodes = VNET_FEATURES ("l2-input-feat-arc"),
441   .arc_index_ptr = &l2_in_out_feat_arc_main.nonip_feat_arc_index[IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP],
442 };
443 VNET_FEATURE_ARC_INIT (l2_in_nonip_arc, static) =
444 {
445   .arc_name  = "l2-output-nonip",
446   .start_nodes = VNET_FEATURES ("l2-output-feat-arc"),
447   .arc_index_ptr = &l2_in_out_feat_arc_main.nonip_feat_arc_index[IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP],
448 };
449
450
451 /* *INDENT-ON* */
452
453
454 /* *INDENT-OFF* */
455 VLIB_REGISTER_NODE (l2_in_feat_arc_node) = {
456   .name = "l2-input-feat-arc",
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) = {
467   .name = "l2-output-feat-arc",
468   .vector_size = sizeof (u32),
469   .format_trace = format_l2_out_feat_arc_trace,
470   .type = VLIB_NODE_TYPE_INTERNAL,
471
472   .n_errors = ARRAY_LEN(l2_out_feat_arc_error_strings),
473   .error_strings = l2_out_feat_arc_error_strings,
474
475 };
476
477 VLIB_REGISTER_NODE (l2_in_feat_arc_end_node) = {
478   .name = "l2-input-feat-arc-end",
479   .vector_size = sizeof (u32),
480   .format_trace = format_l2_in_feat_arc_trace,
481   .sibling_of = "l2-input-feat-arc",
482 };
483
484 VLIB_REGISTER_NODE (l2_out_feat_arc_end_node) = {
485   .name = "l2-output-feat-arc-end",
486   .vector_size = sizeof (u32),
487   .format_trace = format_l2_out_feat_arc_trace,
488   .sibling_of = "l2-output-feat-arc",
489 };
490
491 VNET_FEATURE_INIT (l2_in_ip4_arc_end, static) =
492 {
493   .arc_name = "l2-input-ip4",
494   .node_name = "l2-input-feat-arc-end",
495   .runs_before = 0,     /* not before any other features */
496 };
497
498 VNET_FEATURE_INIT (l2_out_ip4_arc_end, static) =
499 {
500   .arc_name = "l2-output-ip4",
501   .node_name = "l2-output-feat-arc-end",
502   .runs_before = 0,     /* not before any other features */
503 };
504
505 VNET_FEATURE_INIT (l2_in_ip6_arc_end, static) =
506 {
507   .arc_name = "l2-input-ip6",
508   .node_name = "l2-input-feat-arc-end",
509   .runs_before = 0,     /* not before any other features */
510 };
511
512
513 VNET_FEATURE_INIT (l2_out_ip6_arc_end, static) =
514 {
515   .arc_name = "l2-output-ip6",
516   .node_name = "l2-output-feat-arc-end",
517   .runs_before = 0,     /* not before any other features */
518 };
519
520 VNET_FEATURE_INIT (l2_in_nonip_arc_end, static) =
521 {
522   .arc_name = "l2-input-nonip",
523   .node_name = "l2-input-feat-arc-end",
524   .runs_before = 0,     /* not before any other features */
525 };
526
527
528 VNET_FEATURE_INIT (l2_out_nonip_arc_end, static) =
529 {
530   .arc_name = "l2-output-nonip",
531   .node_name = "l2-output-feat-arc-end",
532   .runs_before = 0,     /* not before any other features */
533 };
534 /* *INDENT-ON* */
535
536
537 #ifndef CLIB_MARCH_VARIANT
538 clib_error_t *
539 l2_in_out_feat_arc_init (vlib_main_t * vm)
540 {
541   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
542
543   mp->vlib_main = vm;
544   mp->vnet_main = vnet_get_main ();
545
546   /* Initialize the feature next-node indexes */
547   feat_bitmap_init_next_nodes (vm,
548                                l2_in_feat_arc_end_node.index,
549                                L2INPUT_N_FEAT,
550                                l2input_get_feat_names (),
551                                mp->feat_next_node_index
552                                [IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP]);
553   feat_bitmap_init_next_nodes (vm, l2_out_feat_arc_end_node.index,
554                                L2OUTPUT_N_FEAT, l2output_get_feat_names (),
555                                mp->feat_next_node_index
556                                [IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP]);
557   return 0;
558 }
559
560
561 static int
562 l2_has_features (u32 sw_if_index, int is_output)
563 {
564   int has_features = 0;
565   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
566   has_features +=
567     vnet_have_features (mp->ip4_feat_arc_index[is_output], sw_if_index);
568   has_features +=
569     vnet_have_features (mp->ip6_feat_arc_index[is_output], sw_if_index);
570   has_features +=
571     vnet_have_features (mp->nonip_feat_arc_index[is_output], sw_if_index);
572   return has_features > 0;
573 }
574
575 static int
576 l2_is_output_arc (u8 arc_index)
577 {
578   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
579   int idx = IN_OUT_FEAT_ARC_OUTPUT_TABLE_GROUP;
580   return (mp->ip4_feat_arc_index[idx] == arc_index
581           || mp->ip6_feat_arc_index[idx] == arc_index
582           || mp->nonip_feat_arc_index[idx] == arc_index);
583 }
584
585 static int
586 l2_is_input_arc (u8 arc_index)
587 {
588   l2_in_out_feat_arc_main_t *mp = &l2_in_out_feat_arc_main;
589   int idx = IN_OUT_FEAT_ARC_INPUT_TABLE_GROUP;
590   return (mp->ip4_feat_arc_index[idx] == arc_index
591           || mp->ip6_feat_arc_index[idx] == arc_index
592           || mp->nonip_feat_arc_index[idx] == arc_index);
593 }
594
595 int
596 vnet_l2_feature_enable_disable (const char *arc_name, const char *node_name,
597                                 u32 sw_if_index, int enable_disable,
598                                 void *feature_config,
599                                 u32 n_feature_config_bytes)
600 {
601   u8 arc_index = vnet_get_feature_arc_index (arc_name);
602   if (arc_index == (u8) ~ 0)
603     return VNET_API_ERROR_INVALID_VALUE;
604
605   /* check the state before we tried to enable/disable */
606   int had_features = vnet_have_features (arc_index, sw_if_index);
607
608   int ret = vnet_feature_enable_disable (arc_name, node_name, sw_if_index,
609                                          enable_disable, feature_config,
610                                          n_feature_config_bytes);
611   if (ret)
612     return ret;
613
614   int has_features = vnet_have_features (arc_index, sw_if_index);
615
616   if (had_features != has_features)
617     {
618       if (l2_is_output_arc (arc_index))
619         {
620           vnet_l2_in_out_feat_arc_enable_disable (sw_if_index, 1,
621                                                   l2_has_features
622                                                   (sw_if_index, 1));
623         }
624       if (l2_is_input_arc (arc_index))
625         {
626           vnet_l2_in_out_feat_arc_enable_disable (sw_if_index, 0,
627                                                   l2_has_features
628                                                   (sw_if_index, 0));
629         }
630     }
631   return 0;
632 }
633
634
635 VLIB_INIT_FUNCTION (l2_in_out_feat_arc_init);
636 #endif /* CLIB_MARCH_VARIANT */
637
638 /*
639  * fd.io coding-style-patch-verification: ON
640  *
641  * Local Variables:
642  * eval: (c-set-style "gnu")
643  * End:
644  */