udp: fix csum computation when offload disabled
[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 /**
403  * Attach an ABF policy to an interface.
404  */
405 VLIB_CLI_COMMAND (abf_itf_attach_cmd_node, static) = {
406   .path = "abf attach",
407   .function = abf_itf_attach_cmd,
408   .short_help = "abf attach <ip4|ip6> [del] policy <value> <interface>",
409   // this is not MP safe
410 };
411
412 static clib_error_t *
413 abf_show_attach_cmd (vlib_main_t * vm,
414                      unformat_input_t * input, vlib_cli_command_t * cmd)
415 {
416   const abf_itf_attach_t *aia;
417   u32 sw_if_index, *aiai;
418   fib_protocol_t fproto;
419   vnet_main_t *vnm;
420
421   sw_if_index = ~0;
422   vnm = vnet_get_main ();
423
424   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
425     {
426       if (unformat (input, "%U",
427                     unformat_vnet_sw_interface, vnm, &sw_if_index))
428         ;
429       else
430         return (clib_error_return (0, "unknown input '%U'",
431                                    format_unformat_error, input));
432     }
433
434   if (~0 == sw_if_index)
435     {
436       vlib_cli_output (vm, "specify an interface");
437     }
438
439   FOR_EACH_FIB_IP_PROTOCOL(fproto)
440   {
441     if (sw_if_index < vec_len(abf_per_itf[fproto]))
442       {
443         if (vec_len(abf_per_itf[fproto][sw_if_index]))
444           vlib_cli_output(vm, "%U:", format_fib_protocol, fproto);
445
446         vec_foreach(aiai, abf_per_itf[fproto][sw_if_index])
447           {
448             aia = pool_elt_at_index(abf_itf_attach_pool, *aiai);
449             vlib_cli_output(vm, " %U", format_abf_intf_attach, aia);
450           }
451       }
452   }
453   return (NULL);
454 }
455
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
463 void
464 abf_itf_attach_walk (abf_itf_attach_walk_cb_t cb, void *ctx)
465 {
466   u32 aii;
467
468   pool_foreach_index (aii, abf_itf_attach_pool)
469    {
470     if (!cb(aii, ctx))
471       break;
472   }
473 }
474
475 typedef enum abf_next_t_
476 {
477   ABF_NEXT_DROP,
478   ABF_N_NEXT,
479 } abf_next_t;
480
481 typedef struct abf_input_trace_t_
482 {
483   abf_next_t next;
484   index_t index;
485 } abf_input_trace_t;
486
487 typedef enum
488 {
489 #define abf_error(n,s) ABF_ERROR_##n,
490 #include "abf_error.def"
491 #undef abf_error
492   ABF_N_ERROR,
493 } abf_error_t;
494
495 always_inline uword
496 abf_input_inline (vlib_main_t * vm,
497                   vlib_node_runtime_t * node,
498                   vlib_frame_t * frame, fib_protocol_t fproto)
499 {
500   u32 n_left_from, *from, *to_next, next_index, matches, misses;
501
502   from = vlib_frame_vector_args (frame);
503   n_left_from = frame->n_vectors;
504   next_index = node->cached_next_index;
505   matches = misses = 0;
506
507   while (n_left_from > 0)
508     {
509       u32 n_left_to_next;
510
511       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
512
513       while (n_left_from > 0 && n_left_to_next > 0)
514         {
515           const u32 *attachments0;
516           const abf_itf_attach_t *aia0;
517           abf_next_t next0 = ABF_NEXT_DROP;
518           vlib_buffer_t *b0;
519           u32 bi0, sw_if_index0;
520           fa_5tuple_opaque_t fa_5tuple0;
521           u32 match_acl_index = ~0;
522           u32 match_acl_pos = ~0;
523           u32 match_rule_index = ~0;
524           u32 trace_bitmap = 0;
525           u32 lc_index;
526           u8 action;
527
528           bi0 = from[0];
529           to_next[0] = bi0;
530           from += 1;
531           to_next += 1;
532           n_left_from -= 1;
533           n_left_to_next -= 1;
534
535           b0 = vlib_get_buffer (vm, bi0);
536           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
537
538           ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
539           attachments0 = abf_per_itf[fproto][sw_if_index0];
540
541           ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
542           /*
543            * check if any of the policies attached to this interface matches.
544            */
545           lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
546
547           /*
548              A non-inline version looks like this:
549
550              acl_plugin.fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
551              1, 0, &fa_5tuple0);
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           acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b0,
559                                          (FIB_PROTOCOL_IP6 == fproto), 1, 0,
560                                          &fa_5tuple0);
561
562           if (acl_plugin_match_5tuple_inline (
563                 acl_plugin.p_acl_main, lc_index, &fa_5tuple0,
564                 (FIB_PROTOCOL_IP6 == fproto), &action, &match_acl_pos,
565                 &match_acl_index, &match_rule_index, &trace_bitmap) &&
566               action > 0)
567             {
568               /*
569                * match:
570                *  follow the DPO chain
571                */
572               aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
573
574               next0 = aia0->aia_dpo.dpoi_next_node;
575               vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
576                 aia0->aia_dpo.dpoi_index;
577               matches++;
578             }
579           else
580             {
581               /*
582                * miss:
583                *  move on down the feature arc
584                */
585               vnet_feature_next (&next0, b0);
586               misses++;
587             }
588
589           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
590             {
591               abf_input_trace_t *tr;
592
593               tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
594               tr->next = next0;
595               tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
596             }
597
598           /* verify speculative enqueue, maybe switch current next frame */
599           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
600                                            to_next, n_left_to_next, bi0,
601                                            next0);
602         }
603
604       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
605     }
606
607   vlib_node_increment_counter (vm,
608                                (fproto = FIB_PROTOCOL_IP6 ?
609                                 abf_ip4_node.index :
610                                 abf_ip6_node.index),
611                                ABF_ERROR_MATCHED, matches);
612   vlib_node_increment_counter (vm,
613                                (fproto = FIB_PROTOCOL_IP6 ?
614                                 abf_ip4_node.index :
615                                 abf_ip6_node.index),
616                                ABF_ERROR_MISSED, misses);
617
618   return frame->n_vectors;
619 }
620
621 static uword
622 abf_input_ip4 (vlib_main_t * vm,
623                vlib_node_runtime_t * node, vlib_frame_t * frame)
624 {
625   return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
626 }
627
628 static uword
629 abf_input_ip6 (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_IP6);
633 }
634
635 static u8 *
636 format_abf_input_trace (u8 * s, va_list * args)
637 {
638   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
639   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
640   abf_input_trace_t *t = va_arg (*args, abf_input_trace_t *);
641
642   s = format (s, " next %d index %d", t->next, t->index);
643   return s;
644 }
645
646 static char *abf_error_strings[] = {
647 #define abf_error(n,s) s,
648 #include "abf_error.def"
649 #undef abf_error
650 };
651
652 VLIB_REGISTER_NODE (abf_ip4_node) =
653 {
654   .function = abf_input_ip4,
655   .name = "abf-input-ip4",
656   .vector_size = sizeof (u32),
657   .format_trace = format_abf_input_trace,
658   .type = VLIB_NODE_TYPE_INTERNAL,
659   .n_errors = ABF_N_ERROR,
660   .error_strings = abf_error_strings,
661   .n_next_nodes = ABF_N_NEXT,
662   .next_nodes =
663   {
664     [ABF_NEXT_DROP] = "error-drop",
665   }
666 };
667
668 VLIB_REGISTER_NODE (abf_ip6_node) =
669 {
670   .function = abf_input_ip6,
671   .name = "abf-input-ip6",
672   .vector_size = sizeof (u32),
673   .format_trace = format_abf_input_trace,
674   .type = VLIB_NODE_TYPE_INTERNAL,
675   .n_errors = 0,
676   .n_next_nodes = ABF_N_NEXT,
677
678   .next_nodes =
679   {
680     [ABF_NEXT_DROP] = "error-drop",
681   }
682 };
683
684 VNET_FEATURE_INIT (abf_ip4_feat, static) =
685 {
686   .arc_name = "ip4-unicast",
687   .node_name = "abf-input-ip4",
688   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
689 };
690
691 VNET_FEATURE_INIT (abf_ip6_feat, static) =
692 {
693   .arc_name = "ip6-unicast",
694   .node_name = "abf-input-ip6",
695   .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
696 };
697
698 static fib_node_t *
699 abf_itf_attach_get_node (fib_node_index_t index)
700 {
701   abf_itf_attach_t *aia = abf_itf_attach_get (index);
702   return (&(aia->aia_node));
703 }
704
705 static abf_itf_attach_t *
706 abf_itf_attach_get_from_node (fib_node_t * node)
707 {
708   return ((abf_itf_attach_t *) (((char *) node) -
709                                 STRUCT_OFFSET_OF (abf_itf_attach_t,
710                                                   aia_node)));
711 }
712
713 static void
714 abf_itf_attach_last_lock_gone (fib_node_t * node)
715 {
716   /*
717    * ABF interface attachments are leaves on the graph.
718    * we do not manage locks from children.
719    */
720 }
721
722 /*
723  * abf_itf_attach_back_walk_notify
724  *
725  * A back walk has reached this BIER fmask
726  */
727 static fib_node_back_walk_rc_t
728 abf_itf_attach_back_walk_notify (fib_node_t * node,
729                                  fib_node_back_walk_ctx_t * ctx)
730 {
731   /*
732    * re-stack the fmask on the n-eos of the via
733    */
734   abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
735
736   abf_itf_attach_stack (aia);
737
738   return (FIB_NODE_BACK_WALK_CONTINUE);
739 }
740
741 /*
742  * The BIER fmask's graph node virtual function table
743  */
744 static const fib_node_vft_t abf_itf_attach_vft = {
745   .fnv_get = abf_itf_attach_get_node,
746   .fnv_last_lock = abf_itf_attach_last_lock_gone,
747   .fnv_back_walk = abf_itf_attach_back_walk_notify,
748 };
749
750 static clib_error_t *
751 abf_itf_bond_init (vlib_main_t * vm)
752 {
753   abf_itf_attach_fib_node_type =
754     fib_node_register_new_type ("abf-attach", &abf_itf_attach_vft);
755   clib_error_t *acl_init_res = acl_plugin_exports_init (&acl_plugin);
756   if (acl_init_res)
757     return (acl_init_res);
758
759   abf_acl_user_id =
760     acl_plugin.register_user_module ("ABF plugin", "sw_if_index", NULL);
761
762   return (NULL);
763 }
764
765 VLIB_INIT_FUNCTION (abf_itf_bond_init) =
766 {
767   .runs_after = VLIB_INITS("acl_init"),
768 };
769
770 /*
771  * fd.io coding-style-patch-verification: ON
772  *
773  * Local Variables:
774  * eval: (c-set-style "gnu")
775  * End:
776  */