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