abf_itf_attach.c revision 669d07dc
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/**
52 * A DB of attachments; key={abf_index,sw_if_index}
53 */
54static uword *abf_itf_attach_db;
55
56static u64
57abf_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
68static abf_itf_attach_t *
69abf_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
84static void
85abf_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
94static void
95abf_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
104static void
105abf_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
128static int
129abf_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
140void
141abf_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
160int
161abf_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
244int
245abf_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
322static u8 *
323format_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
336static clib_error_t *
337abf_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 */
401VLIB_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
409static clib_error_t *
410abf_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* */
456VLIB_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
464void
465abf_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
478typedef enum abf_next_t_
479{
480  ABF_NEXT_DROP,
481  ABF_N_NEXT,
482} abf_next_t;
483
484typedef struct abf_input_trace_t_
485{
486  abf_next_t next;
487  index_t index;
488} abf_input_trace_t;
489
490typedef 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
498always_inline uword
499abf_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
604static uword
605abf_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
611static uword
612abf_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
618static u8 *
619format_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
629static 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* */
636VLIB_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
652VLIB_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
668VNET_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
675VNET_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
683static fib_node_t *
684abf_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
690static abf_itf_attach_t *
691abf_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
698static void
699abf_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 */
712static fib_node_back_walk_rc_t
713abf_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 */
729static 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
735static clib_error_t *
736abf_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
750VLIB_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 */
759