ACL based forwarding
[vpp.git] / src / plugins / abf / abf_itf_attach.c
1 /*
2  * Copyright (c) 2017 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 <plugins/abf/abf_itf_attach.h>
17 #include <vnet/fib/fib_path_list.h>
18 #include <plugins/acl/exports.h>
19
20 /**
21  * Forward declarations;
22  */
23 extern vlib_node_registration_t abf_ip4_node;
24 extern vlib_node_registration_t abf_ip6_node;
25
26 /**
27  * FIB node registered type for the bonds
28  */
29 static fib_node_type_t abf_itf_attach_fib_node_type;
30
31 /**
32  * Pool of ABF interface attachment objects
33  */
34 abf_itf_attach_t *abf_itf_attach_pool;
35
36 /**
37  * A per interface vector of attached policies. used in the data-plane
38  */
39 static u32 **abf_per_itf[FIB_PROTOCOL_MAX];
40
41 /**
42  * Per interface values of ACL lookup context IDs. used in the data-plane
43  */
44 static u32 *abf_alctx_per_itf[FIB_PROTOCOL_MAX];
45
46 /**
47  * ABF ACL module user id returned during the initialization
48  */
49 static u32 abf_acl_user_id;
50
51 /**
52  * A DB of attachments; key={abf_index,sw_if_index}
53  */
54 static uword *abf_itf_attach_db;
55
56 static u64
57 abf_itf_attach_mk_key (u32 abf_index, u32 sw_if_index)
58 {
59   u64 key;
60
61   key = abf_index;
62   key = key << 32;
63   key |= sw_if_index;
64
65   return (key);
66 }
67
68 static abf_itf_attach_t *
69 abf_itf_attach_db_find (u32 abf_index, u32 sw_if_index)
70 {
71   uword *p;
72   u64 key;
73
74   key = abf_itf_attach_mk_key (abf_index, sw_if_index);
75
76   p = hash_get (abf_itf_attach_db, key);
77
78   if (NULL != p)
79     return (pool_elt_at_index (abf_itf_attach_pool, p[0]));
80
81   return (NULL);
82 }
83
84 static void
85 abf_itf_attach_db_add (u32 abf_index, u32 sw_if_index, abf_itf_attach_t * aia)
86 {
87   u64 key;
88
89   key = abf_itf_attach_mk_key (abf_index, sw_if_index);
90
91   hash_set (abf_itf_attach_db, key, aia - abf_itf_attach_pool);
92 }
93
94 static void
95 abf_itf_attach_db_del (u32 abf_index, u32 sw_if_index)
96 {
97   u64 key;
98
99   key = abf_itf_attach_mk_key (abf_index, sw_if_index);
100
101   hash_unset (abf_itf_attach_db, key);
102 }
103
104 static void
105 abf_itf_attach_stack (abf_itf_attach_t * aia)
106 {
107   /*
108    * stack the DPO on the forwarding contributed by the path-list
109    */
110   dpo_id_t via_dpo = DPO_INVALID;
111   abf_policy_t *ap;
112
113   ap = abf_policy_get (aia->aia_abf);
114
115   fib_path_list_contribute_forwarding (ap->ap_pl,
116                                        (FIB_PROTOCOL_IP4 == aia->aia_proto ?
117                                         FIB_FORW_CHAIN_TYPE_UNICAST_IP4 :
118                                         FIB_FORW_CHAIN_TYPE_UNICAST_IP6),
119                                        FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
120                                        &via_dpo);
121
122   dpo_stack_from_node ((FIB_PROTOCOL_IP4 == aia->aia_proto ?
123                         abf_ip4_node.index :
124                         abf_ip6_node.index), &aia->aia_dpo, &via_dpo);
125   dpo_reset (&via_dpo);
126 }
127
128 static int
129 abf_cmp_attach_for_sort (void *v1, void *v2)
130 {
131   const abf_itf_attach_t *aia1;
132   const abf_itf_attach_t *aia2;
133
134   aia1 = abf_itf_attach_get (*(u32 *) v1);
135   aia2 = abf_itf_attach_get (*(u32 *) v2);
136
137   return (aia1->aia_prio - aia2->aia_prio);
138 }
139
140 void
141 abf_setup_acl_lc (fib_protocol_t fproto, u32 sw_if_index)
142 {
143   u32 *acl_vec = 0;
144   u32 *aiai;
145   abf_itf_attach_t *aia;
146
147   if (~0 == abf_alctx_per_itf[fproto][sw_if_index])
148     return;
149
150   vec_foreach (aiai, abf_per_itf[fproto][sw_if_index])
151   {
152     aia = abf_itf_attach_get (*aiai);
153     vec_add1 (acl_vec, aia->aia_acl);
154   }
155   acl_plugin_set_acl_vec_for_context (abf_alctx_per_itf[fproto][sw_if_index],
156                                       acl_vec);
157   vec_free (acl_vec);
158 }
159
160 int
161 abf_itf_attach (fib_protocol_t fproto,
162                 u32 policy_id, u32 priority, u32 sw_if_index)
163 {
164   abf_itf_attach_t *aia;
165   abf_policy_t *ap;
166   u32 api, aiai;
167
168   api = abf_policy_find (policy_id);
169
170   ASSERT (INDEX_INVALID != api);
171   ap = abf_policy_get (api);
172
173   /*
174    * check this is not a duplicate
175    */
176   aia = abf_itf_attach_db_find (policy_id, sw_if_index);
177
178   if (NULL != aia)
179     return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
180
181   /*
182    * construt a new attachemnt object
183    */
184   pool_get (abf_itf_attach_pool, aia);
185
186   fib_node_init (&aia->aia_node, abf_itf_attach_fib_node_type);
187   aia->aia_prio = priority;
188   aia->aia_proto = fproto;
189   aia->aia_acl = ap->ap_acl;
190   aia->aia_abf = api;
191   aia->aia_sw_if_index = sw_if_index;
192   aiai = aia - abf_itf_attach_pool;
193   abf_itf_attach_db_add (policy_id, sw_if_index, aia);
194
195   /*
196    * stack the DPO on the forwarding contributed by the path-list
197    */
198   abf_itf_attach_stack (aia);
199
200   /*
201    * Insert the policy on the interfaces list.
202    */
203   vec_validate_init_empty (abf_per_itf[fproto], sw_if_index, NULL);
204   vec_add1 (abf_per_itf[fproto][sw_if_index], aia - abf_itf_attach_pool);
205   if (1 == vec_len (abf_per_itf[fproto][sw_if_index]))
206     {
207       /*
208        * when enabling the first ABF polciy on the interface
209        * we need to enable the interface input feature
210        */
211       vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
212                                     "ip4-unicast" :
213                                     "ip6-unicast"),
214                                    (FIB_PROTOCOL_IP4 == fproto ?
215                                     "abf-input-ip4" :
216                                     "abf-input-ip6"),
217                                    sw_if_index, 1, NULL, 0);
218
219       /* if this is the first ABF policy, we need to acquire an ACL lookup context */
220       vec_validate_init_empty (abf_alctx_per_itf[fproto], sw_if_index, ~0);
221       abf_alctx_per_itf[fproto][sw_if_index] =
222         acl_plugin_get_lookup_context_index (abf_acl_user_id, sw_if_index, 0);
223     }
224   else
225     {
226       vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
227                               abf_cmp_attach_for_sort);
228     }
229
230   /* Prepare and set the list of ACLs for lookup within the context */
231   abf_setup_acl_lc (fproto, sw_if_index);
232
233   /*
234    * become a child of the ABF poilcy so we are notified when
235    * its forwarding changes.
236    */
237   aia->aia_sibling = fib_node_child_add (abf_policy_fib_node_type,
238                                          api,
239                                          abf_itf_attach_fib_node_type, aiai);
240
241   return (0);
242 }
243
244 int
245 abf_itf_detach (fib_protocol_t fproto, u32 policy_id, u32 sw_if_index)
246 {
247   abf_itf_attach_t *aia;
248   u32 index;
249
250   /*
251    * check this is a valid attahment
252    */
253   aia = abf_itf_attach_db_find (policy_id, sw_if_index);
254
255   if (NULL == aia)
256     return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
257
258   /*
259    * first remove from the interface's vecotr
260    */
261   ASSERT (abf_per_itf[fproto]);
262   ASSERT (abf_per_itf[fproto][sw_if_index]);
263
264   index = vec_search (abf_per_itf[fproto][sw_if_index],
265                       aia - abf_itf_attach_pool);
266
267   ASSERT (index != ~0);
268   vec_del1 (abf_per_itf[fproto][sw_if_index], index);
269
270   if (0 == vec_len (abf_per_itf[fproto][sw_if_index]))
271     {
272       /*
273        * when deleting the last ABF polciy on the interface
274        * we need to disable the interface input feature
275        */
276       vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
277                                     "ip4-unicast" :
278                                     "ip6-unicast"),
279                                    (FIB_PROTOCOL_IP4 == fproto ?
280                                     "abf-input-ip4" :
281                                     "abf-input-ip6"),
282                                    sw_if_index, 0, NULL, 0);
283
284       /* Return the lookup context, invalidate its id in our records */
285       acl_plugin_put_lookup_context_index (abf_alctx_per_itf[fproto]
286                                            [sw_if_index]);
287       abf_alctx_per_itf[fproto][sw_if_index] = ~0;
288     }
289   else
290     {
291       vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
292                               abf_cmp_attach_for_sort);
293     }
294
295   /* Prepare and set the list of ACLs for lookup within the context */
296   abf_setup_acl_lc (fproto, sw_if_index);
297
298   /*
299    * remove the dependency on the policy
300    */
301   fib_node_child_remove (abf_policy_fib_node_type,
302                          aia->aia_abf, aia->aia_sibling);
303
304   /*
305    * remove the attahcment from the DB
306    */
307   abf_itf_attach_db_del (policy_id, sw_if_index);
308
309   /*
310    * release our locks on FIB forwarding data
311    */
312   dpo_reset (&aia->aia_dpo);
313
314   /*
315    * return the object
316    */
317   pool_put (abf_itf_attach_pool, aia);
318
319   return (0);
320 }
321
322 static u8 *
323 format_abf_intf_attach (u8 * s, va_list * args)
324 {
325   abf_itf_attach_t *aia = va_arg (*args, abf_itf_attach_t *);
326   abf_policy_t *ap;
327
328   ap = abf_policy_get (aia->aia_abf);
329   s = format (s, "abf-interface-attach: policy:%d prioity:%d",
330               ap->ap_id, aia->aia_prio);
331   s = format (s, "\n  %U", format_dpo_id, &aia->aia_dpo, 2);
332
333   return (s);
334 }
335
336 static clib_error_t *
337 abf_itf_attach_cmd (vlib_main_t * vm,
338                     unformat_input_t * input, vlib_cli_command_t * cmd)
339 {
340   u32 policy_id, sw_if_index;
341   fib_protocol_t fproto;
342   u32 is_del, priority;
343   vnet_main_t *vnm;
344
345   is_del = 0;
346   sw_if_index = policy_id = ~0;
347   vnm = vnet_get_main ();
348   fproto = FIB_PROTOCOL_MAX;
349   priority = 0;
350
351   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
352     {
353       if (unformat (input, "del"))
354         is_del = 1;
355       else if (unformat (input, "add"))
356         is_del = 0;
357       else if (unformat (input, "ip4"))
358         fproto = FIB_PROTOCOL_IP4;
359       else if (unformat (input, "ip6"))
360         fproto = FIB_PROTOCOL_IP6;
361       else if (unformat (input, "policy %d", &policy_id))
362         ;
363       else if (unformat (input, "priority %d", &priority))
364         ;
365       else if (unformat (input, "%U",
366                          unformat_vnet_sw_interface, vnm, &sw_if_index))
367         ;
368       else
369         return (clib_error_return (0, "unknown input '%U'",
370                                    format_unformat_error, input));
371     }
372
373   if (~0 == policy_id)
374     {
375       return (clib_error_return (0, "invalid policy ID:%d", policy_id));
376     }
377   if (~0 == sw_if_index)
378     {
379       return (clib_error_return (0, "invalid interface name"));
380     }
381   if (FIB_PROTOCOL_MAX == fproto)
382     {
383       return (clib_error_return (0, "Specify either ip4 or ip6"));
384     }
385
386   if (~0 == abf_policy_find (policy_id))
387     return (clib_error_return (0, "invalid policy ID:%d", policy_id));
388
389   if (is_del)
390     abf_itf_detach (fproto, policy_id, sw_if_index);
391   else
392     abf_itf_attach (fproto, policy_id, priority, sw_if_index);
393
394   return (NULL);
395 }
396
397 /* *INDENT-OFF* */
398 /**
399  * Attach an ABF policy to an interface.
400  */
401 VLIB_CLI_COMMAND (abf_itf_attach_cmd_node, static) = {
402   .path = "abf attach",
403   .function = abf_itf_attach_cmd,
404   .short_help = "abf attach <ip4|ip6> [del] policy <value> <interface>",
405   // this is not MP safe
406 };
407 /* *INDENT-ON* */
408
409 static clib_error_t *
410 abf_show_attach_cmd (vlib_main_t * vm,
411                      unformat_input_t * input, vlib_cli_command_t * cmd)
412 {
413   const abf_itf_attach_t *aia;
414   u32 sw_if_index, *aiai;
415   fib_protocol_t fproto;
416   vnet_main_t *vnm;
417
418   sw_if_index = ~0;
419   vnm = vnet_get_main ();
420
421   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
422     {
423       if (unformat (input, "%U",
424                     unformat_vnet_sw_interface, vnm, &sw_if_index))
425         ;
426       else
427         return (clib_error_return (0, "unknown input '%U'",
428                                    format_unformat_error, input));
429     }
430
431   if (~0 == sw_if_index)
432     {
433       vlib_cli_output (vm, "specify an interface");
434     }
435
436   /* *INDENT-OFF* */
437   FOR_EACH_FIB_IP_PROTOCOL(fproto)
438   {
439     if (sw_if_index < vec_len(abf_per_itf[fproto]))
440       {
441         if (vec_len(abf_per_itf[fproto][sw_if_index]))
442           vlib_cli_output(vm, "%U:", format_fib_protocol, fproto);
443
444         vec_foreach(aiai, abf_per_itf[fproto][sw_if_index])
445           {
446             aia = pool_elt_at_index(abf_itf_attach_pool, *aiai);
447             vlib_cli_output(vm, " %U", format_abf_intf_attach, aia);
448           }
449       }
450   }
451   /* *INDENT-ON* */
452   return (NULL);
453 }
454
455 /* *INDENT-OFF* */
456 VLIB_CLI_COMMAND (abf_show_attach_cmd_node, static) = {
457   .path = "show abf attach",
458   .function = abf_show_attach_cmd,
459   .short_help = "show abf attach <interface>",
460   .is_mp_safe = 1,
461 };
462 /* *INDENT-ON* */
463
464 void
465 abf_itf_attach_walk (abf_itf_attach_walk_cb_t cb, void *ctx)
466 {
467   u32 aii;
468
469   /* *INDENT-OFF* */
470   pool_foreach_index(aii, abf_itf_attach_pool,
471   ({
472     if (!cb(aii, ctx))
473       break;
474   }));
475   /* *INDENT-ON* */
476 }
477
478 typedef enum abf_next_t_
479 {
480   ABF_NEXT_DROP,
481   ABF_N_NEXT,
482 } abf_next_t;
483
484 typedef struct abf_input_trace_t_
485 {
486   abf_next_t next;
487   index_t index;
488 } abf_input_trace_t;
489
490 typedef enum
491 {
492 #define abf_error(n,s) ABF_ERROR_##n,
493 #include "abf_error.def"
494 #undef abf_error
495   ABF_N_ERROR,
496 } abf_error_t;
497
498 always_inline uword
499 abf_input_inline (vlib_main_t * vm,
500                   vlib_node_runtime_t * node,
501                   vlib_frame_t * frame, fib_protocol_t fproto)
502 {
503   u32 n_left_from, *from, *to_next, next_index, matches;
504
505   from = vlib_frame_vector_args (frame);
506   n_left_from = frame->n_vectors;
507   next_index = node->cached_next_index;
508   matches = 0;
509
510   while (n_left_from > 0)
511     {
512       u32 n_left_to_next;
513
514       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
515
516       while (n_left_from > 0 && n_left_to_next > 0)
517         {
518           const u32 *attachments0;
519           const abf_itf_attach_t *aia0;
520           abf_next_t next0 = ABF_NEXT_DROP;
521           vlib_buffer_t *b0;
522           u32 bi0, sw_if_index0;
523           fa_5tuple_opaque_t fa_5tuple0;
524           u32 match_acl_index = ~0;
525           u32 match_acl_pos = ~0;
526           u32 match_rule_index = ~0;
527           u32 trace_bitmap = 0;
528           u8 action;
529
530           bi0 = from[0];
531           to_next[0] = bi0;
532           from += 1;
533           to_next += 1;
534           n_left_from -= 1;
535           n_left_to_next -= 1;
536
537           b0 = vlib_get_buffer (vm, bi0);
538           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
539
540           ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
541           attachments0 = abf_per_itf[fproto][sw_if_index0];
542
543           ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
544           /*
545            * check if any of the policies attached to this interface matches.
546            */
547           u32 lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
548
549           acl_plugin_fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
550                                   1, 0, &fa_5tuple0);
551
552           if (acl_plugin_match_5tuple
553               (lc_index, &fa_5tuple0, (FIB_PROTOCOL_IP6 == fproto), &action,
554                &match_acl_pos, &match_acl_index, &match_rule_index,
555                &trace_bitmap))
556             {
557               /*
558                * match:
559                *  follow the DPO chain
560                */
561               aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
562
563               next0 = aia0->aia_dpo.dpoi_next_node;
564               vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
565                 aia0->aia_dpo.dpoi_index;
566               matches++;
567             }
568           else
569             {
570               /*
571                * miss:
572                *  move on down the feature arc
573                */
574               vnet_feature_next (sw_if_index0, &next0, b0);
575             }
576
577           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
578             {
579               abf_input_trace_t *tr;
580
581               tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
582               tr->next = next0;
583               tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
584             }
585
586           /* verify speculative enqueue, maybe switch current next frame */
587           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
588                                            to_next, n_left_to_next, bi0,
589                                            next0);
590         }
591
592       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
593     }
594
595   vlib_node_increment_counter (vm,
596                                (fproto = FIB_PROTOCOL_IP6 ?
597                                 abf_ip4_node.index :
598                                 abf_ip6_node.index),
599                                ABF_ERROR_MATCHED, matches);
600
601   return frame->n_vectors;
602 }
603
604 static uword
605 abf_input_ip4 (vlib_main_t * vm,
606                vlib_node_runtime_t * node, vlib_frame_t * frame)
607 {
608   return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
609 }
610
611 static uword
612 abf_input_ip6 (vlib_main_t * vm,
613                vlib_node_runtime_t * node, vlib_frame_t * frame)
614 {
615   return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
616 }
617
618 static u8 *
619 format_abf_input_trace (u8 * s, va_list * args)
620 {
621   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
622   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
623   abf_input_trace_t *t = va_arg (*args, abf_input_trace_t *);
624
625   s = format (s, " next %d index %d", t->next, t->index);
626   return s;
627 }
628
629 static char *abf_error_strings[] = {
630 #define abf_error(n,s) s,
631 #include "abf_error.def"
632 #undef abf_error
633 };
634
635 /* *INDENT-OFF* */
636 VLIB_REGISTER_NODE (abf_ip4_node) =
637 {
638   .function = abf_input_ip4,
639   .name = "abf-input-ip4",
640   .vector_size = sizeof (u32),
641   .format_trace = format_abf_input_trace,
642   .type = VLIB_NODE_TYPE_INTERNAL,
643   .n_errors = ABF_N_ERROR,
644   .error_strings = abf_error_strings,
645   .n_next_nodes = ABF_N_NEXT,
646   .next_nodes =
647   {
648     [ABF_NEXT_DROP] = "error-drop",
649   }
650 };
651
652 VLIB_REGISTER_NODE (abf_ip6_node) =
653 {
654   .function = abf_input_ip6,
655   .name = "abf-input-ip6",
656   .vector_size = sizeof (u32),
657   .format_trace = format_abf_input_trace,
658   .type = VLIB_NODE_TYPE_INTERNAL,
659   .n_errors = 0,
660   .n_next_nodes = ABF_N_NEXT,
661
662   .next_nodes =
663   {
664     [ABF_NEXT_DROP] = "error-drop",
665   }
666 };
667
668 VNET_FEATURE_INIT (abf_ip4_feat, static) =
669 {
670   .arc_name = "ip4-unicast",
671   .node_name = "abf-input-ip4",
672   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
673 };
674
675 VNET_FEATURE_INIT (abf_ip6_feat, static) =
676 {
677   .arc_name = "ip6-unicast",
678   .node_name = "abf-input-ip6",
679   .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
680 };
681 /* *INDENT-ON* */
682
683 static fib_node_t *
684 abf_itf_attach_get_node (fib_node_index_t index)
685 {
686   abf_itf_attach_t *aia = abf_itf_attach_get (index);
687   return (&(aia->aia_node));
688 }
689
690 static abf_itf_attach_t *
691 abf_itf_attach_get_from_node (fib_node_t * node)
692 {
693   return ((abf_itf_attach_t *) (((char *) node) -
694                                 STRUCT_OFFSET_OF (abf_itf_attach_t,
695                                                   aia_node)));
696 }
697
698 static void
699 abf_itf_attach_last_lock_gone (fib_node_t * node)
700 {
701   /*
702    * ABF interface attachments are leaves on the graph.
703    * we do not manage locks from children.
704    */
705 }
706
707 /*
708  * abf_itf_attach_back_walk_notify
709  *
710  * A back walk has reached this BIER fmask
711  */
712 static fib_node_back_walk_rc_t
713 abf_itf_attach_back_walk_notify (fib_node_t * node,
714                                  fib_node_back_walk_ctx_t * ctx)
715 {
716   /*
717    * re-stack the fmask on the n-eos of the via
718    */
719   abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
720
721   abf_itf_attach_stack (aia);
722
723   return (FIB_NODE_BACK_WALK_CONTINUE);
724 }
725
726 /*
727  * The BIER fmask's graph node virtual function table
728  */
729 static const fib_node_vft_t abf_itf_attach_vft = {
730   .fnv_get = abf_itf_attach_get_node,
731   .fnv_last_lock = abf_itf_attach_last_lock_gone,
732   .fnv_back_walk = abf_itf_attach_back_walk_notify,
733 };
734
735 static clib_error_t *
736 abf_itf_bond_init (vlib_main_t * vm)
737 {
738   abf_itf_attach_fib_node_type =
739     fib_node_register_new_type (&abf_itf_attach_vft);
740   clib_error_t *acl_init_res = acl_plugin_exports_init ();
741   if (acl_init_res)
742     return (acl_init_res);
743
744   abf_acl_user_id =
745     acl_plugin_register_user_module ("abp plugin", "sw_if_index", NULL);
746
747   return (NULL);
748 }
749
750 VLIB_INIT_FUNCTION (abf_itf_bond_init);
751
752 /*
753  * fd.io coding-style-patch-verification: ON
754  *
755  * Local Variables:
756  * eval: (c-set-style "gnu")
757  * End:
758  */