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 * construt a new attachemnt 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 polciy 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 poilcy 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 attahment
258 aia = abf_itf_attach_db_find (policy_id, sw_if_index);
261 return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
264 * first remove from the interface's vecotr
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 polciy 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 attahcment 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 prioity:%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;
510 from = vlib_frame_vector_args (frame);
511 n_left_from = frame->n_vectors;
512 next_index = node->cached_next_index;
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;
542 b0 = vlib_get_buffer (vm, bi0);
543 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
545 ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
546 attachments0 = abf_per_itf[fproto][sw_if_index0];
548 ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
550 * check if any of the policies attached to this interface matches.
552 u32 lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
555 A non-inline version looks like this:
557 acl_plugin.fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
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,
565 acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b0,
566 (FIB_PROTOCOL_IP6 == fproto), 1, 0,
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))
576 * follow the DPO chain
578 aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
580 next0 = aia0->aia_dpo.dpoi_next_node;
581 vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
582 aia0->aia_dpo.dpoi_index;
589 * move on down the feature arc
591 vnet_feature_next (sw_if_index0, &next0, b0);
594 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
596 abf_input_trace_t *tr;
598 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
600 tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
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,
609 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
612 vlib_node_increment_counter (vm,
613 (fproto = FIB_PROTOCOL_IP6 ?
616 ABF_ERROR_MATCHED, matches);
618 return frame->n_vectors;
622 abf_input_ip4 (vlib_main_t * vm,
623 vlib_node_runtime_t * node, vlib_frame_t * frame)
625 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
629 abf_input_ip6 (vlib_main_t * vm,
630 vlib_node_runtime_t * node, vlib_frame_t * frame)
632 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
636 format_abf_input_trace (u8 * s, va_list * args)
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 *);
642 s = format (s, " next %d index %d", t->next, t->index);
646 static char *abf_error_strings[] = {
647 #define abf_error(n,s) s,
648 #include "abf_error.def"
653 VLIB_REGISTER_NODE (abf_ip4_node) =
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,
665 [ABF_NEXT_DROP] = "error-drop",
669 VLIB_REGISTER_NODE (abf_ip6_node) =
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,
677 .n_next_nodes = ABF_N_NEXT,
681 [ABF_NEXT_DROP] = "error-drop",
685 VNET_FEATURE_INIT (abf_ip4_feat, static) =
687 .arc_name = "ip4-unicast",
688 .node_name = "abf-input-ip4",
689 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
692 VNET_FEATURE_INIT (abf_ip6_feat, static) =
694 .arc_name = "ip6-unicast",
695 .node_name = "abf-input-ip6",
696 .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
701 abf_itf_attach_get_node (fib_node_index_t index)
703 abf_itf_attach_t *aia = abf_itf_attach_get (index);
704 return (&(aia->aia_node));
707 static abf_itf_attach_t *
708 abf_itf_attach_get_from_node (fib_node_t * node)
710 return ((abf_itf_attach_t *) (((char *) node) -
711 STRUCT_OFFSET_OF (abf_itf_attach_t,
716 abf_itf_attach_last_lock_gone (fib_node_t * node)
719 * ABF interface attachments are leaves on the graph.
720 * we do not manage locks from children.
725 * abf_itf_attach_back_walk_notify
727 * A back walk has reached this BIER fmask
729 static fib_node_back_walk_rc_t
730 abf_itf_attach_back_walk_notify (fib_node_t * node,
731 fib_node_back_walk_ctx_t * ctx)
734 * re-stack the fmask on the n-eos of the via
736 abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
738 abf_itf_attach_stack (aia);
740 return (FIB_NODE_BACK_WALK_CONTINUE);
744 * The BIER fmask's graph node virtual function table
746 static 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,
752 static clib_error_t *
753 abf_itf_bond_init (vlib_main_t * vm)
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);
759 return (acl_init_res);
762 acl_plugin.register_user_module ("abp plugin", "sw_if_index", NULL);
767 VLIB_INIT_FUNCTION (abf_itf_bond_init);
770 * fd.io coding-style-patch-verification: ON
773 * eval: (c-set-style "gnu")