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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <plugins/abf/abf_itf_attach.h>
17 #include <vnet/fib/fib_path_list.h>
18 #include <plugins/acl/exports.h>
21 * Forward declarations;
23 extern vlib_node_registration_t abf_ip4_node;
24 extern vlib_node_registration_t abf_ip6_node;
27 * FIB node registered type for the bonds
29 static fib_node_type_t abf_itf_attach_fib_node_type;
32 * Pool of ABF interface attachment objects
34 abf_itf_attach_t *abf_itf_attach_pool;
37 * A per interface vector of attached policies. used in the data-plane
39 static u32 **abf_per_itf[FIB_PROTOCOL_MAX];
42 * Per interface values of ACL lookup context IDs. used in the data-plane
44 static u32 *abf_alctx_per_itf[FIB_PROTOCOL_MAX];
47 * ABF ACL module user id returned during the initialization
49 static u32 abf_acl_user_id;
51 * ACL plugin method vtable
54 static acl_plugin_methods_t acl_plugin;
57 * A DB of attachments; key={abf_index,sw_if_index}
59 static uword *abf_itf_attach_db;
62 abf_itf_attach_mk_key (u32 abf_index, u32 sw_if_index)
73 static abf_itf_attach_t *
74 abf_itf_attach_db_find (u32 abf_index, u32 sw_if_index)
79 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
81 p = hash_get (abf_itf_attach_db, key);
84 return (pool_elt_at_index (abf_itf_attach_pool, p[0]));
90 abf_itf_attach_db_add (u32 abf_index, u32 sw_if_index, abf_itf_attach_t * aia)
94 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
96 hash_set (abf_itf_attach_db, key, aia - abf_itf_attach_pool);
100 abf_itf_attach_db_del (u32 abf_index, u32 sw_if_index)
104 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
106 hash_unset (abf_itf_attach_db, key);
110 abf_itf_attach_stack (abf_itf_attach_t * aia)
113 * stack the DPO on the forwarding contributed by the path-list
115 dpo_id_t via_dpo = DPO_INVALID;
118 ap = abf_policy_get (aia->aia_abf);
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,
127 dpo_stack_from_node ((FIB_PROTOCOL_IP4 == aia->aia_proto ?
129 abf_ip6_node.index), &aia->aia_dpo, &via_dpo);
130 dpo_reset (&via_dpo);
134 abf_cmp_attach_for_sort (void *v1, void *v2)
136 const abf_itf_attach_t *aia1;
137 const abf_itf_attach_t *aia2;
139 aia1 = abf_itf_attach_get (*(u32 *) v1);
140 aia2 = abf_itf_attach_get (*(u32 *) v2);
142 return (aia1->aia_prio - aia2->aia_prio);
146 abf_setup_acl_lc (fib_protocol_t fproto, u32 sw_if_index)
150 abf_itf_attach_t *aia;
152 if (~0 == abf_alctx_per_itf[fproto][sw_if_index])
155 vec_foreach (aiai, abf_per_itf[fproto][sw_if_index])
157 aia = abf_itf_attach_get (*aiai);
158 vec_add1 (acl_vec, aia->aia_acl);
160 acl_plugin.set_acl_vec_for_context (abf_alctx_per_itf[fproto][sw_if_index],
166 abf_itf_attach (fib_protocol_t fproto,
167 u32 policy_id, u32 priority, u32 sw_if_index)
169 abf_itf_attach_t *aia;
173 api = abf_policy_find (policy_id);
175 ASSERT (INDEX_INVALID != api);
176 ap = abf_policy_get (api);
179 * check this is not a duplicate
181 aia = abf_itf_attach_db_find (policy_id, sw_if_index);
184 return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
187 * construct a new attachment object
189 pool_get (abf_itf_attach_pool, aia);
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;
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);
201 * stack the DPO on the forwarding contributed by the path-list
203 abf_itf_attach_stack (aia);
206 * Insert the policy on the interfaces list.
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]))
213 * when enabling the first ABF policy on the interface
214 * we need to enable the interface input feature
216 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
219 (FIB_PROTOCOL_IP4 == fproto ?
222 sw_if_index, 1, NULL, 0);
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);
231 vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
232 abf_cmp_attach_for_sort);
235 /* Prepare and set the list of ACLs for lookup within the context */
236 abf_setup_acl_lc (fproto, sw_if_index);
239 * become a child of the ABF policy so we are notified when
240 * its forwarding changes.
242 aia->aia_sibling = fib_node_child_add (abf_policy_fib_node_type,
244 abf_itf_attach_fib_node_type, aiai);
250 abf_itf_detach (fib_protocol_t fproto, u32 policy_id, u32 sw_if_index)
252 abf_itf_attach_t *aia;
256 * check this is a valid attachment
258 aia = abf_itf_attach_db_find (policy_id, sw_if_index);
261 return (VNET_API_ERROR_NO_SUCH_ENTRY);
264 * first remove from the interface's vector
266 ASSERT (abf_per_itf[fproto]);
267 ASSERT (abf_per_itf[fproto][sw_if_index]);
269 index = vec_search (abf_per_itf[fproto][sw_if_index],
270 aia - abf_itf_attach_pool);
272 ASSERT (index != ~0);
273 vec_del1 (abf_per_itf[fproto][sw_if_index], index);
275 if (0 == vec_len (abf_per_itf[fproto][sw_if_index]))
278 * when deleting the last ABF policy on the interface
279 * we need to disable the interface input feature
281 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
284 (FIB_PROTOCOL_IP4 == fproto ?
287 sw_if_index, 0, NULL, 0);
289 /* Return the lookup context, invalidate its id in our records */
290 acl_plugin.put_lookup_context_index (abf_alctx_per_itf[fproto]
292 abf_alctx_per_itf[fproto][sw_if_index] = ~0;
296 vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
297 abf_cmp_attach_for_sort);
300 /* Prepare and set the list of ACLs for lookup within the context */
301 abf_setup_acl_lc (fproto, sw_if_index);
304 * remove the dependency on the policy
306 fib_node_child_remove (abf_policy_fib_node_type,
307 aia->aia_abf, aia->aia_sibling);
310 * remove the attachment from the DB
312 abf_itf_attach_db_del (policy_id, sw_if_index);
315 * release our locks on FIB forwarding data
317 dpo_reset (&aia->aia_dpo);
322 pool_put (abf_itf_attach_pool, aia);
328 format_abf_intf_attach (u8 * s, va_list * args)
330 abf_itf_attach_t *aia = va_arg (*args, abf_itf_attach_t *);
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);
341 static clib_error_t *
342 abf_itf_attach_cmd (vlib_main_t * vm,
343 unformat_input_t * input, vlib_cli_command_t * cmd)
345 u32 policy_id, sw_if_index;
346 fib_protocol_t fproto;
347 u32 is_del, priority;
351 sw_if_index = policy_id = ~0;
352 vnm = vnet_get_main ();
353 fproto = FIB_PROTOCOL_MAX;
356 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
358 if (unformat (input, "del"))
360 else if (unformat (input, "add"))
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))
368 else if (unformat (input, "priority %d", &priority))
370 else if (unformat (input, "%U",
371 unformat_vnet_sw_interface, vnm, &sw_if_index))
374 return (clib_error_return (0, "unknown input '%U'",
375 format_unformat_error, input));
380 return (clib_error_return (0, "invalid policy ID:%d", policy_id));
382 if (~0 == sw_if_index)
384 return (clib_error_return (0, "invalid interface name"));
386 if (FIB_PROTOCOL_MAX == fproto)
388 return (clib_error_return (0, "Specify either ip4 or ip6"));
391 if (~0 == abf_policy_find (policy_id))
392 return (clib_error_return (0, "invalid policy ID:%d", policy_id));
395 abf_itf_detach (fproto, policy_id, sw_if_index);
397 abf_itf_attach (fproto, policy_id, priority, sw_if_index);
404 * Attach an ABF policy to an interface.
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
414 static clib_error_t *
415 abf_show_attach_cmd (vlib_main_t * vm,
416 unformat_input_t * input, vlib_cli_command_t * cmd)
418 const abf_itf_attach_t *aia;
419 u32 sw_if_index, *aiai;
420 fib_protocol_t fproto;
424 vnm = vnet_get_main ();
426 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
428 if (unformat (input, "%U",
429 unformat_vnet_sw_interface, vnm, &sw_if_index))
432 return (clib_error_return (0, "unknown input '%U'",
433 format_unformat_error, input));
436 if (~0 == sw_if_index)
438 vlib_cli_output (vm, "specify an interface");
442 FOR_EACH_FIB_IP_PROTOCOL(fproto)
444 if (sw_if_index < vec_len(abf_per_itf[fproto]))
446 if (vec_len(abf_per_itf[fproto][sw_if_index]))
447 vlib_cli_output(vm, "%U:", format_fib_protocol, fproto);
449 vec_foreach(aiai, abf_per_itf[fproto][sw_if_index])
451 aia = pool_elt_at_index(abf_itf_attach_pool, *aiai);
452 vlib_cli_output(vm, " %U", format_abf_intf_attach, aia);
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>",
470 abf_itf_attach_walk (abf_itf_attach_walk_cb_t cb, void *ctx)
475 pool_foreach_index(aii, abf_itf_attach_pool,
483 typedef enum abf_next_t_
489 typedef struct abf_input_trace_t_
497 #define abf_error(n,s) ABF_ERROR_##n,
498 #include "abf_error.def"
504 abf_input_inline (vlib_main_t * vm,
505 vlib_node_runtime_t * node,
506 vlib_frame_t * frame, fib_protocol_t fproto)
508 u32 n_left_from, *from, *to_next, next_index, matches, misses;
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;
515 while (n_left_from > 0)
519 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
521 while (n_left_from > 0 && n_left_to_next > 0)
523 const u32 *attachments0;
524 const abf_itf_attach_t *aia0;
525 abf_next_t next0 = ABF_NEXT_DROP;
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;
543 b0 = vlib_get_buffer (vm, bi0);
544 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
546 ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
547 attachments0 = abf_per_itf[fproto][sw_if_index0];
549 ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
551 * check if any of the policies attached to this interface matches.
553 lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
556 A non-inline version looks like this:
558 acl_plugin.fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
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,
566 acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b0,
567 (FIB_PROTOCOL_IP6 == fproto), 1, 0,
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))
577 * follow the DPO chain
579 aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
581 next0 = aia0->aia_dpo.dpoi_next_node;
582 vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
583 aia0->aia_dpo.dpoi_index;
590 * move on down the feature arc
592 vnet_feature_next (&next0, b0);
596 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
598 abf_input_trace_t *tr;
600 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
602 tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
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,
611 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
614 vlib_node_increment_counter (vm,
615 (fproto = FIB_PROTOCOL_IP6 ?
618 ABF_ERROR_MATCHED, matches);
619 vlib_node_increment_counter (vm,
620 (fproto = FIB_PROTOCOL_IP6 ?
623 ABF_ERROR_MISSED, misses);
625 return frame->n_vectors;
629 abf_input_ip4 (vlib_main_t * vm,
630 vlib_node_runtime_t * node, vlib_frame_t * frame)
632 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
636 abf_input_ip6 (vlib_main_t * vm,
637 vlib_node_runtime_t * node, vlib_frame_t * frame)
639 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
643 format_abf_input_trace (u8 * s, va_list * args)
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 *);
649 s = format (s, " next %d index %d", t->next, t->index);
653 static char *abf_error_strings[] = {
654 #define abf_error(n,s) s,
655 #include "abf_error.def"
660 VLIB_REGISTER_NODE (abf_ip4_node) =
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,
672 [ABF_NEXT_DROP] = "error-drop",
676 VLIB_REGISTER_NODE (abf_ip6_node) =
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,
684 .n_next_nodes = ABF_N_NEXT,
688 [ABF_NEXT_DROP] = "error-drop",
692 VNET_FEATURE_INIT (abf_ip4_feat, static) =
694 .arc_name = "ip4-unicast",
695 .node_name = "abf-input-ip4",
696 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
699 VNET_FEATURE_INIT (abf_ip6_feat, static) =
701 .arc_name = "ip6-unicast",
702 .node_name = "abf-input-ip6",
703 .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
708 abf_itf_attach_get_node (fib_node_index_t index)
710 abf_itf_attach_t *aia = abf_itf_attach_get (index);
711 return (&(aia->aia_node));
714 static abf_itf_attach_t *
715 abf_itf_attach_get_from_node (fib_node_t * node)
717 return ((abf_itf_attach_t *) (((char *) node) -
718 STRUCT_OFFSET_OF (abf_itf_attach_t,
723 abf_itf_attach_last_lock_gone (fib_node_t * node)
726 * ABF interface attachments are leaves on the graph.
727 * we do not manage locks from children.
732 * abf_itf_attach_back_walk_notify
734 * A back walk has reached this BIER fmask
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)
741 * re-stack the fmask on the n-eos of the via
743 abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
745 abf_itf_attach_stack (aia);
747 return (FIB_NODE_BACK_WALK_CONTINUE);
751 * The BIER fmask's graph node virtual function table
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,
759 static clib_error_t *
760 abf_itf_bond_init (vlib_main_t * vm)
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);
766 return (acl_init_res);
769 acl_plugin.register_user_module ("ABF plugin", "sw_if_index", NULL);
775 VLIB_INIT_FUNCTION (abf_itf_bond_init) =
777 .runs_after = VLIB_INITS("acl_init"),
782 * fd.io coding-style-patch-verification: ON
785 * eval: (c-set-style "gnu")