abf_itf_attach.c revision 22f9fb12
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 */
23extern vlib_node_registration_t abf_ip4_node;
24extern vlib_node_registration_t abf_ip6_node;
25
26/**
27 * FIB node registered type for the bonds
28 */
29static fib_node_type_t abf_itf_attach_fib_node_type;
30
31/**
32 * Pool of ABF interface attachment objects
33 */
34abf_itf_attach_t *abf_itf_attach_pool;
35
36/**
37 * A per interface vector of attached policies. used in the data-plane
38 */
39static u32 **abf_per_itf[FIB_PROTOCOL_MAX];
40
41/**
42 * Per interface values of ACL lookup context IDs. used in the data-plane
43 */
44static u32 *abf_alctx_per_itf[FIB_PROTOCOL_MAX];
45
46/**
47 * ABF ACL module user id returned during the initialization
48 */
49static u32 abf_acl_user_id;
50/*
51 * ACL plugin method vtable
52 */
53
54static acl_plugin_methods_t acl_plugin;
55
56/**
57 * A DB of attachments; key={abf_index,sw_if_index}
58 */
59static uword *abf_itf_attach_db;
60
61static u64
62abf_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
73static abf_itf_attach_t *
74abf_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
89static void
90abf_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
99static void
100abf_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
109static void
110abf_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
133static int
134abf_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
145void
146abf_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
165int
166abf_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   * construt a new attachemnt 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 polciy 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 poilcy 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
249int
250abf_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 attahment
257   */
258  aia = abf_itf_attach_db_find (policy_id, sw_if_index);
259
260  if (NULL == aia)
261    return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
262
263  /*
264   * first remove from the interface's vecotr
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 polciy 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 attahcment 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
327static u8 *
328format_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 prioity:%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
341static clib_error_t *
342abf_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 */
406VLIB_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
414static clib_error_t *
415abf_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* */
461VLIB_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
469void
470abf_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
483typedef enum abf_next_t_
484{
485  ABF_NEXT_DROP,
486  ABF_N_NEXT,
487} abf_next_t;
488
489typedef struct abf_input_trace_t_
490{
491  abf_next_t next;
492  index_t index;
493} abf_input_trace_t;
494
495typedef 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
503always_inline uword
504abf_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;
509
510  from = vlib_frame_vector_args (frame);
511  n_left_from = frame->n_vectors;
512  next_index = node->cached_next_index;
513  matches = 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	  u8 action;
534
535	  bi0 = from[0];
536	  to_next[0] = bi0;
537	  from += 1;
538	  to_next += 1;
539	  n_left_from -= 1;
540	  n_left_to_next -= 1;
541
542	  b0 = vlib_get_buffer (vm, bi0);
543	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
544
545	  ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
546	  attachments0 = abf_per_itf[fproto][sw_if_index0];
547
548	  ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
549	  /*
550	   * check if any of the policies attached to this interface matches.
551	   */
552	  u32 lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
553
554	  /*
555	     A non-inline version looks like this:
556
557	     acl_plugin.fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
558	     1, 0, &fa_5tuple0);
559	     if (acl_plugin.match_5tuple
560	     (lc_index, &fa_5tuple0, (FIB_PROTOCOL_IP6 == fproto), &action,
561	     &match_acl_pos, &match_acl_index, &match_rule_index,
562	     &trace_bitmap))
563	     . . .
564	   */
565	  acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b0,
566					 (FIB_PROTOCOL_IP6 == fproto), 1, 0,
567					 &fa_5tuple0);
568
569	  if (acl_plugin_match_5tuple_inline
570	      (acl_plugin.p_acl_main, lc_index, &fa_5tuple0,
571	       (FIB_PROTOCOL_IP6 == fproto), &action, &match_acl_pos,
572	       &match_acl_index, &match_rule_index, &trace_bitmap))
573	    {
574	      /*
575	       * match:
576	       *  follow the DPO chain
577	       */
578	      aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
579
580	      next0 = aia0->aia_dpo.dpoi_next_node;
581	      vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
582		aia0->aia_dpo.dpoi_index;
583	      matches++;
584	    }
585	  else
586	    {
587	      /*
588	       * miss:
589	       *  move on down the feature arc
590	       */
591	      vnet_feature_next (sw_if_index0, &next0, b0);
592	    }
593
594	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
595	    {
596	      abf_input_trace_t *tr;
597
598	      tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
599	      tr->next = next0;
600	      tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
601	    }
602
603	  /* verify speculative enqueue, maybe switch current next frame */
604	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
605					   to_next, n_left_to_next, bi0,
606					   next0);
607	}
608
609      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
610    }
611
612  vlib_node_increment_counter (vm,
613			       (fproto = FIB_PROTOCOL_IP6 ?
614				abf_ip4_node.index :
615				abf_ip6_node.index),
616			       ABF_ERROR_MATCHED, matches);
617
618  return frame->n_vectors;
619}
620
621static uword
622abf_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
628static uword
629abf_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
635static u8 *
636format_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
646static char *abf_error_strings[] = {
647#define abf_error(n,s) s,
648#include "abf_error.def"
649#undef abf_error
650};
651
652/* *INDENT-OFF* */
653VLIB_REGISTER_NODE (abf_ip4_node) =
654{
655  .function = abf_input_ip4,
656  .name = "abf-input-ip4",
657  .vector_size = sizeof (u32),
658  .format_trace = format_abf_input_trace,
659  .type = VLIB_NODE_TYPE_INTERNAL,
660  .n_errors = ABF_N_ERROR,
661  .error_strings = abf_error_strings,
662  .n_next_nodes = ABF_N_NEXT,
663  .next_nodes =
664  {
665    [ABF_NEXT_DROP] = "error-drop",
666  }
667};
668
669VLIB_REGISTER_NODE (abf_ip6_node) =
670{
671  .function = abf_input_ip6,
672  .name = "abf-input-ip6",
673  .vector_size = sizeof (u32),
674  .format_trace = format_abf_input_trace,
675  .type = VLIB_NODE_TYPE_INTERNAL,
676  .n_errors = 0,
677  .n_next_nodes = ABF_N_NEXT,
678
679  .next_nodes =
680  {
681    [ABF_NEXT_DROP] = "error-drop",
682  }
683};
684
685VNET_FEATURE_INIT (abf_ip4_feat, static) =
686{
687  .arc_name = "ip4-unicast",
688  .node_name = "abf-input-ip4",
689  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
690};
691
692VNET_FEATURE_INIT (abf_ip6_feat, static) =
693{
694  .arc_name = "ip6-unicast",
695  .node_name = "abf-input-ip6",
696  .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
697};
698/* *INDENT-ON* */
699
700static fib_node_t *
701abf_itf_attach_get_node (fib_node_index_t index)
702{
703  abf_itf_attach_t *aia = abf_itf_attach_get (index);
704  return (&(aia->aia_node));
705}
706
707static abf_itf_attach_t *
708abf_itf_attach_get_from_node (fib_node_t * node)
709{
710  return ((abf_itf_attach_t *) (((char *) node) -
711				STRUCT_OFFSET_OF (abf_itf_attach_t,
712						  aia_node)));
713}
714
715static void
716abf_itf_attach_last_lock_gone (fib_node_t * node)
717{
718  /*
719   * ABF interface attachments are leaves on the graph.
720   * we do not manage locks from children.
721   */
722}
723
724/*
725 * abf_itf_attach_back_walk_notify
726 *
727 * A back walk has reached this BIER fmask
728 */
729static fib_node_back_walk_rc_t
730abf_itf_attach_back_walk_notify (fib_node_t * node,
731				 fib_node_back_walk_ctx_t * ctx)
732{
733  /*
734   * re-stack the fmask on the n-eos of the via
735   */
736  abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
737
738  abf_itf_attach_stack (aia);
739
740  return (FIB_NODE_BACK_WALK_CONTINUE);
741}
742
743/*
744 * The BIER fmask's graph node virtual function table
745 */
746static const fib_node_vft_t abf_itf_attach_vft = {
747  .fnv_get = abf_itf_attach_get_node,
748  .fnv_last_lock = abf_itf_attach_last_lock_gone,
749  .fnv_back_walk = abf_itf_attach_back_walk_notify,
750};
751
752static clib_error_t *
753abf_itf_bond_init (vlib_main_t * vm)
754{
755  abf_itf_attach_fib_node_type =
756    fib_node_register_new_type (&abf_itf_attach_vft);
757  clib_error_t *acl_init_res = acl_plugin_exports_init (&acl_plugin);
758  if (acl_init_res)
759    return (acl_init_res);
760
761  abf_acl_user_id =
762    acl_plugin.register_user_module ("abp plugin", "sw_if_index", NULL);
763
764  return (NULL);
765}
766
767VLIB_INIT_FUNCTION (abf_itf_bond_init);
768
769/*
770 * fd.io coding-style-patch-verification: ON
771 *
772 * Local Variables:
773 * eval: (c-set-style "gnu")
774 * End:
775 */
776