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;
52 * A DB of attachments; key={abf_index,sw_if_index}
54 static uword *abf_itf_attach_db;
57 abf_itf_attach_mk_key (u32 abf_index, u32 sw_if_index)
68 static abf_itf_attach_t *
69 abf_itf_attach_db_find (u32 abf_index, u32 sw_if_index)
74 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
76 p = hash_get (abf_itf_attach_db, key);
79 return (pool_elt_at_index (abf_itf_attach_pool, p[0]));
85 abf_itf_attach_db_add (u32 abf_index, u32 sw_if_index, abf_itf_attach_t * aia)
89 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
91 hash_set (abf_itf_attach_db, key, aia - abf_itf_attach_pool);
95 abf_itf_attach_db_del (u32 abf_index, u32 sw_if_index)
99 key = abf_itf_attach_mk_key (abf_index, sw_if_index);
101 hash_unset (abf_itf_attach_db, key);
105 abf_itf_attach_stack (abf_itf_attach_t * aia)
108 * stack the DPO on the forwarding contributed by the path-list
110 dpo_id_t via_dpo = DPO_INVALID;
113 ap = abf_policy_get (aia->aia_abf);
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,
122 dpo_stack_from_node ((FIB_PROTOCOL_IP4 == aia->aia_proto ?
124 abf_ip6_node.index), &aia->aia_dpo, &via_dpo);
125 dpo_reset (&via_dpo);
129 abf_cmp_attach_for_sort (void *v1, void *v2)
131 const abf_itf_attach_t *aia1;
132 const abf_itf_attach_t *aia2;
134 aia1 = abf_itf_attach_get (*(u32 *) v1);
135 aia2 = abf_itf_attach_get (*(u32 *) v2);
137 return (aia1->aia_prio - aia2->aia_prio);
141 abf_setup_acl_lc (fib_protocol_t fproto, u32 sw_if_index)
145 abf_itf_attach_t *aia;
147 if (~0 == abf_alctx_per_itf[fproto][sw_if_index])
150 vec_foreach (aiai, abf_per_itf[fproto][sw_if_index])
152 aia = abf_itf_attach_get (*aiai);
153 vec_add1 (acl_vec, aia->aia_acl);
155 acl_plugin_set_acl_vec_for_context (abf_alctx_per_itf[fproto][sw_if_index],
161 abf_itf_attach (fib_protocol_t fproto,
162 u32 policy_id, u32 priority, u32 sw_if_index)
164 abf_itf_attach_t *aia;
168 api = abf_policy_find (policy_id);
170 ASSERT (INDEX_INVALID != api);
171 ap = abf_policy_get (api);
174 * check this is not a duplicate
176 aia = abf_itf_attach_db_find (policy_id, sw_if_index);
179 return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
182 * construt a new attachemnt object
184 pool_get (abf_itf_attach_pool, aia);
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;
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);
196 * stack the DPO on the forwarding contributed by the path-list
198 abf_itf_attach_stack (aia);
201 * Insert the policy on the interfaces list.
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]))
208 * when enabling the first ABF polciy on the interface
209 * we need to enable the interface input feature
211 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
214 (FIB_PROTOCOL_IP4 == fproto ?
217 sw_if_index, 1, NULL, 0);
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);
226 vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
227 abf_cmp_attach_for_sort);
230 /* Prepare and set the list of ACLs for lookup within the context */
231 abf_setup_acl_lc (fproto, sw_if_index);
234 * become a child of the ABF poilcy so we are notified when
235 * its forwarding changes.
237 aia->aia_sibling = fib_node_child_add (abf_policy_fib_node_type,
239 abf_itf_attach_fib_node_type, aiai);
245 abf_itf_detach (fib_protocol_t fproto, u32 policy_id, u32 sw_if_index)
247 abf_itf_attach_t *aia;
251 * check this is a valid attahment
253 aia = abf_itf_attach_db_find (policy_id, sw_if_index);
256 return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
259 * first remove from the interface's vecotr
261 ASSERT (abf_per_itf[fproto]);
262 ASSERT (abf_per_itf[fproto][sw_if_index]);
264 index = vec_search (abf_per_itf[fproto][sw_if_index],
265 aia - abf_itf_attach_pool);
267 ASSERT (index != ~0);
268 vec_del1 (abf_per_itf[fproto][sw_if_index], index);
270 if (0 == vec_len (abf_per_itf[fproto][sw_if_index]))
273 * when deleting the last ABF polciy on the interface
274 * we need to disable the interface input feature
276 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
279 (FIB_PROTOCOL_IP4 == fproto ?
282 sw_if_index, 0, NULL, 0);
284 /* Return the lookup context, invalidate its id in our records */
285 acl_plugin_put_lookup_context_index (abf_alctx_per_itf[fproto]
287 abf_alctx_per_itf[fproto][sw_if_index] = ~0;
291 vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
292 abf_cmp_attach_for_sort);
295 /* Prepare and set the list of ACLs for lookup within the context */
296 abf_setup_acl_lc (fproto, sw_if_index);
299 * remove the dependency on the policy
301 fib_node_child_remove (abf_policy_fib_node_type,
302 aia->aia_abf, aia->aia_sibling);
305 * remove the attahcment from the DB
307 abf_itf_attach_db_del (policy_id, sw_if_index);
310 * release our locks on FIB forwarding data
312 dpo_reset (&aia->aia_dpo);
317 pool_put (abf_itf_attach_pool, aia);
323 format_abf_intf_attach (u8 * s, va_list * args)
325 abf_itf_attach_t *aia = va_arg (*args, abf_itf_attach_t *);
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);
336 static clib_error_t *
337 abf_itf_attach_cmd (vlib_main_t * vm,
338 unformat_input_t * input, vlib_cli_command_t * cmd)
340 u32 policy_id, sw_if_index;
341 fib_protocol_t fproto;
342 u32 is_del, priority;
346 sw_if_index = policy_id = ~0;
347 vnm = vnet_get_main ();
348 fproto = FIB_PROTOCOL_MAX;
351 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
353 if (unformat (input, "del"))
355 else if (unformat (input, "add"))
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))
363 else if (unformat (input, "priority %d", &priority))
365 else if (unformat (input, "%U",
366 unformat_vnet_sw_interface, vnm, &sw_if_index))
369 return (clib_error_return (0, "unknown input '%U'",
370 format_unformat_error, input));
375 return (clib_error_return (0, "invalid policy ID:%d", policy_id));
377 if (~0 == sw_if_index)
379 return (clib_error_return (0, "invalid interface name"));
381 if (FIB_PROTOCOL_MAX == fproto)
383 return (clib_error_return (0, "Specify either ip4 or ip6"));
386 if (~0 == abf_policy_find (policy_id))
387 return (clib_error_return (0, "invalid policy ID:%d", policy_id));
390 abf_itf_detach (fproto, policy_id, sw_if_index);
392 abf_itf_attach (fproto, policy_id, priority, sw_if_index);
399 * Attach an ABF policy to an interface.
401 VLIB_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
409 static clib_error_t *
410 abf_show_attach_cmd (vlib_main_t * vm,
411 unformat_input_t * input, vlib_cli_command_t * cmd)
413 const abf_itf_attach_t *aia;
414 u32 sw_if_index, *aiai;
415 fib_protocol_t fproto;
419 vnm = vnet_get_main ();
421 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
423 if (unformat (input, "%U",
424 unformat_vnet_sw_interface, vnm, &sw_if_index))
427 return (clib_error_return (0, "unknown input '%U'",
428 format_unformat_error, input));
431 if (~0 == sw_if_index)
433 vlib_cli_output (vm, "specify an interface");
437 FOR_EACH_FIB_IP_PROTOCOL(fproto)
439 if (sw_if_index < vec_len(abf_per_itf[fproto]))
441 if (vec_len(abf_per_itf[fproto][sw_if_index]))
442 vlib_cli_output(vm, "%U:", format_fib_protocol, fproto);
444 vec_foreach(aiai, abf_per_itf[fproto][sw_if_index])
446 aia = pool_elt_at_index(abf_itf_attach_pool, *aiai);
447 vlib_cli_output(vm, " %U", format_abf_intf_attach, aia);
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>",
465 abf_itf_attach_walk (abf_itf_attach_walk_cb_t cb, void *ctx)
470 pool_foreach_index(aii, abf_itf_attach_pool,
478 typedef enum abf_next_t_
484 typedef struct abf_input_trace_t_
492 #define abf_error(n,s) ABF_ERROR_##n,
493 #include "abf_error.def"
499 abf_input_inline (vlib_main_t * vm,
500 vlib_node_runtime_t * node,
501 vlib_frame_t * frame, fib_protocol_t fproto)
503 u32 n_left_from, *from, *to_next, next_index, matches;
505 from = vlib_frame_vector_args (frame);
506 n_left_from = frame->n_vectors;
507 next_index = node->cached_next_index;
510 while (n_left_from > 0)
514 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
516 while (n_left_from > 0 && n_left_to_next > 0)
518 const u32 *attachments0;
519 const abf_itf_attach_t *aia0;
520 abf_next_t next0 = ABF_NEXT_DROP;
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;
537 b0 = vlib_get_buffer (vm, bi0);
538 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
540 ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
541 attachments0 = abf_per_itf[fproto][sw_if_index0];
543 ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
545 * check if any of the policies attached to this interface matches.
547 u32 lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
549 acl_plugin_fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
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,
559 * follow the DPO chain
561 aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
563 next0 = aia0->aia_dpo.dpoi_next_node;
564 vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
565 aia0->aia_dpo.dpoi_index;
572 * move on down the feature arc
574 vnet_feature_next (sw_if_index0, &next0, b0);
577 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
579 abf_input_trace_t *tr;
581 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
583 tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
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,
592 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
595 vlib_node_increment_counter (vm,
596 (fproto = FIB_PROTOCOL_IP6 ?
599 ABF_ERROR_MATCHED, matches);
601 return frame->n_vectors;
605 abf_input_ip4 (vlib_main_t * vm,
606 vlib_node_runtime_t * node, vlib_frame_t * frame)
608 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
612 abf_input_ip6 (vlib_main_t * vm,
613 vlib_node_runtime_t * node, vlib_frame_t * frame)
615 return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
619 format_abf_input_trace (u8 * s, va_list * args)
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 *);
625 s = format (s, " next %d index %d", t->next, t->index);
629 static char *abf_error_strings[] = {
630 #define abf_error(n,s) s,
631 #include "abf_error.def"
636 VLIB_REGISTER_NODE (abf_ip4_node) =
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,
648 [ABF_NEXT_DROP] = "error-drop",
652 VLIB_REGISTER_NODE (abf_ip6_node) =
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,
660 .n_next_nodes = ABF_N_NEXT,
664 [ABF_NEXT_DROP] = "error-drop",
668 VNET_FEATURE_INIT (abf_ip4_feat, static) =
670 .arc_name = "ip4-unicast",
671 .node_name = "abf-input-ip4",
672 .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
675 VNET_FEATURE_INIT (abf_ip6_feat, static) =
677 .arc_name = "ip6-unicast",
678 .node_name = "abf-input-ip6",
679 .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
684 abf_itf_attach_get_node (fib_node_index_t index)
686 abf_itf_attach_t *aia = abf_itf_attach_get (index);
687 return (&(aia->aia_node));
690 static abf_itf_attach_t *
691 abf_itf_attach_get_from_node (fib_node_t * node)
693 return ((abf_itf_attach_t *) (((char *) node) -
694 STRUCT_OFFSET_OF (abf_itf_attach_t,
699 abf_itf_attach_last_lock_gone (fib_node_t * node)
702 * ABF interface attachments are leaves on the graph.
703 * we do not manage locks from children.
708 * abf_itf_attach_back_walk_notify
710 * A back walk has reached this BIER fmask
712 static fib_node_back_walk_rc_t
713 abf_itf_attach_back_walk_notify (fib_node_t * node,
714 fib_node_back_walk_ctx_t * ctx)
717 * re-stack the fmask on the n-eos of the via
719 abf_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
721 abf_itf_attach_stack (aia);
723 return (FIB_NODE_BACK_WALK_CONTINUE);
727 * The BIER fmask's graph node virtual function table
729 static 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,
735 static clib_error_t *
736 abf_itf_bond_init (vlib_main_t * vm)
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 ();
742 return (acl_init_res);
745 acl_plugin_register_user_module ("abp plugin", "sw_if_index", NULL);
750 VLIB_INIT_FUNCTION (abf_itf_bond_init);
753 * fd.io coding-style-patch-verification: ON
756 * eval: (c-set-style "gnu")