aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/abf/abf_policy.c
diff options
context:
space:
mode:
authorAndrew Yourtchenko <ayourtch@gmail.com>2017-11-17 14:38:18 +0100
committerDamjan Marion <dmarion.lists@gmail.com>2018-04-17 18:25:05 +0000
commit669d07dc016757b856e1014a415996cf9f0ebc58 (patch)
treebd86de6e168fd66563f3f81aa971403c0409bbe9 /src/plugins/abf/abf_policy.c
parent2926eca95138577be8d88eb8d6a442d93f182309 (diff)
ACL based forwarding
A poor man's flow switching or policy based rounting. An ACL is used to match packets and is associated with a [set of] forwarding paths that determine how to forward matched packets - collectively this association is a 'policy'. Policies are then 'attached', in a priority order, to an interface when thaey are encountered as an input feature. If a packet matches no policies it is forwarded normally in the IP FIB. This commit is used to test the "ACL-as-a-service" functionality, which currently compiles, and the existing traffic ACL tests pass in both hash and linear modes. Change-Id: I0b274ec9f2e645352fa898b43eb54c457e195964 Signed-off-by: Neale Ranns <nranns@cisco.com> Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com> Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/plugins/abf/abf_policy.c')
-rw-r--r--src/plugins/abf/abf_policy.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/src/plugins/abf/abf_policy.c b/src/plugins/abf/abf_policy.c
new file mode 100644
index 00000000000..a0c4ac8adb3
--- /dev/null
+++ b/src/plugins/abf/abf_policy.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/abf/abf_policy.h>
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <vnet/fib/fib_path_list.h>
+#include <vnet/fib/fib_walk.h>
+
+/**
+ * FIB node type the attachment is registered
+ */
+fib_node_type_t abf_policy_fib_node_type;
+
+/**
+ * Pool of ABF objects
+ */
+static abf_policy_t *abf_policy_pool;
+
+/**
+ * DB of ABF policy objects
+ * - policy ID to index conversion.
+ */
+static uword *abf_policy_db;
+
+
+abf_policy_t *
+abf_policy_get (u32 index)
+{
+ return (pool_elt_at_index (abf_policy_pool, index));
+}
+
+static u32
+abf_policy_get_index (const abf_policy_t * abf)
+{
+ return (abf - abf_policy_pool);
+}
+
+static abf_policy_t *
+abf_policy_find_i (u32 policy_id)
+{
+ u32 api;
+
+ api = abf_policy_find (policy_id);
+
+ if (INDEX_INVALID != api)
+ return (abf_policy_get (api));
+
+ return (NULL);
+}
+
+u32
+abf_policy_find (u32 policy_id)
+{
+ uword *p;
+
+ p = hash_get (abf_policy_db, policy_id);
+
+ if (NULL != p)
+ return (p[0]);
+
+ return (INDEX_INVALID);
+}
+
+
+void
+abf_policy_update (u32 policy_id,
+ u32 acl_index, const fib_route_path_t * rpaths)
+{
+ abf_policy_t *ap;
+ u32 api;
+
+ api = abf_policy_find (policy_id);
+
+ if (INDEX_INVALID == api)
+ {
+ /*
+ * create a new policy
+ */
+ pool_get (abf_policy_pool, ap);
+
+ api = ap - abf_policy_pool;
+ fib_node_init (&ap->ap_node, abf_policy_fib_node_type);
+ ap->ap_acl = acl_index;
+ ap->ap_id = policy_id;
+ ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
+ FIB_PATH_LIST_FLAG_NO_URPF), rpaths);
+
+ /*
+ * become a child of the path list so we get poked when
+ * the forwarding changes.
+ */
+ ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
+ abf_policy_fib_node_type,
+ api);
+
+ /*
+ * add this new policy to the DB
+ */
+ hash_set (abf_policy_db, policy_id, api);
+
+ /*
+ * take a lock on behalf of the CLI/API creation
+ */
+ fib_node_lock (&ap->ap_node);
+ }
+ else
+ {
+ /*
+ * update an existing policy.
+ * - add the path to the path-list and swap our ancestory
+ * - backwalk to poke all attachments to update
+ */
+ fib_node_index_t old_pl;
+
+ ap = abf_policy_get (api);
+ old_pl = ap->ap_pl;
+
+ if (FIB_NODE_INDEX_INVALID != old_pl)
+ {
+ ap->ap_pl = fib_path_list_copy_and_path_add (old_pl,
+ (FIB_PATH_LIST_FLAG_SHARED
+ |
+ FIB_PATH_LIST_FLAG_NO_URPF),
+ rpaths);
+ fib_path_list_child_remove (old_pl, ap->ap_sibling);
+ }
+ else
+ {
+ ap->ap_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
+ FIB_PATH_LIST_FLAG_NO_URPF),
+ rpaths);
+ }
+
+ ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
+ abf_policy_fib_node_type,
+ api);
+
+ fib_node_back_walk_ctx_t ctx = {
+ .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
+ };
+
+ fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
+ }
+}
+
+static void
+abf_policy_destroy (abf_policy_t * ap)
+{
+ /*
+ * this ABF should not be a sibling on the path list, since
+ * that was removed when the API config went
+ */
+ ASSERT (ap->ap_sibling == ~0);
+ ASSERT (ap->ap_pl == FIB_NODE_INDEX_INVALID);
+
+ hash_unset (abf_policy_db, ap->ap_id);
+ pool_put (abf_policy_pool, ap);
+}
+
+int
+abf_policy_delete (u32 policy_id, const fib_route_path_t * rpaths)
+{
+ abf_policy_t *ap;
+ u32 api;
+
+ api = abf_policy_find (policy_id);
+
+ if (INDEX_INVALID == api)
+ {
+ /*
+ * no such policy
+ */
+ return (-1);
+ }
+ else
+ {
+ /*
+ * update an existing policy.
+ * - add the path to the path-list and swap our ancestory
+ * - backwalk to poke all attachments to update
+ */
+ fib_node_index_t old_pl;
+
+ ap = abf_policy_get (api);
+ old_pl = ap->ap_pl;
+
+ ap->ap_pl =
+ fib_path_list_copy_and_path_remove (ap->ap_pl,
+ (FIB_PATH_LIST_FLAG_SHARED |
+ FIB_PATH_LIST_FLAG_NO_URPF),
+ rpaths);
+
+ fib_path_list_child_remove (old_pl, ap->ap_sibling);
+ ap->ap_sibling = ~0;
+
+ if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
+ {
+ /*
+ * no more paths on this policy. It's toast
+ * remove the CLI/API's lock
+ */
+ fib_node_unlock (&ap->ap_node);
+ }
+ else
+ {
+ ap->ap_sibling = fib_path_list_child_add (ap->ap_pl,
+ abf_policy_fib_node_type,
+ api);
+
+ fib_node_back_walk_ctx_t ctx = {
+ .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
+ };
+
+ fib_walk_sync (abf_policy_fib_node_type, api, &ctx);
+ }
+ }
+
+ return (0);
+}
+
+static clib_error_t *
+abf_policy_cmd (vlib_main_t * vm,
+ unformat_input_t * main_input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u32 acl_index, policy_id;
+ fib_route_path_t *rpaths = NULL, rpath;
+ u32 is_del;
+
+ is_del = 0;
+ acl_index = INDEX_INVALID;
+ policy_id = INDEX_INVALID;
+
+ /* Get a line of input. */
+ if (!unformat_user (main_input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "acl %d", &acl_index))
+ ;
+ else if (unformat (line_input, "id %d", &policy_id))
+ ;
+ else if (unformat (line_input, "del"))
+ is_del = 1;
+ else if (unformat (line_input, "add"))
+ is_del = 0;
+ else if (unformat (line_input, "via %U",
+ unformat_fib_route_path, &rpath))
+ vec_add1 (rpaths, rpath);
+ else
+ return (clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input));
+ }
+
+ if (INDEX_INVALID == policy_id)
+ {
+ vlib_cli_output (vm, "Specify a Policy ID");
+ return 0;
+ }
+
+ if (!is_del)
+ {
+ if (INDEX_INVALID == acl_index)
+ {
+ vlib_cli_output (vm, "ACL index must be set");
+ return 0;
+ }
+
+ abf_policy_update (policy_id, acl_index, rpaths);
+ }
+ else
+ {
+ abf_policy_delete (policy_id, rpaths);
+ }
+
+ unformat_free (line_input);
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+/**
+ * Create an ABF policy.
+ */
+VLIB_CLI_COMMAND (abf_policy_cmd_node, static) = {
+ .path = "abf policy",
+ .function = abf_policy_cmd,
+ .short_help = "abf policy [add|del] id <index> acl <index> via ...",
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+static u8 *
+format_abf (u8 * s, va_list * args)
+{
+ abf_policy_t *ap = va_arg (*args, abf_policy_t *);
+
+ s = format (s, "abf:[%d]: policy:%d acl:%d",
+ ap - abf_policy_pool, ap->ap_id, ap->ap_acl);
+ s = format (s, "\n ");
+ if (FIB_NODE_INDEX_INVALID == ap->ap_pl)
+ {
+ s = format (s, "no forwarding");
+ }
+ else
+ {
+ s = fib_path_list_format (ap->ap_pl, s);
+ }
+
+ return (s);
+}
+
+void
+abf_policy_walk (abf_policy_walk_cb_t cb, void *ctx)
+{
+ u32 api;
+
+ /* *INDENT-OFF* */
+ pool_foreach_index(api, abf_policy_pool,
+ ({
+ if (!cb(api, ctx))
+ break;
+ }));
+ /* *INDENT-ON* */
+}
+
+static clib_error_t *
+abf_show_policy_cmd (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ u32 policy_id;
+ abf_policy_t *ap;
+
+ policy_id = INDEX_INVALID;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%d", &policy_id))
+ ;
+ else
+ return (clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, input));
+ }
+
+ if (INDEX_INVALID == policy_id)
+ {
+ /* *INDENT-OFF* */
+ pool_foreach(ap, abf_policy_pool,
+ ({
+ vlib_cli_output(vm, "%U", format_abf, ap);
+ }));
+ /* *INDENT-ON* */
+ }
+ else
+ {
+ ap = abf_policy_find_i (policy_id);
+
+ if (NULL != ap)
+ vlib_cli_output (vm, "%U", format_abf, ap);
+ else
+ vlib_cli_output (vm, "Invalid policy ID:%d", policy_id);
+ }
+
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (abf_policy_show_policy_cmd_node, static) = {
+ .path = "show abf policy",
+ .function = abf_show_policy_cmd,
+ .short_help = "show abf policy <value>",
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+static fib_node_t *
+abf_policy_get_node (fib_node_index_t index)
+{
+ abf_policy_t *ap = abf_policy_get (index);
+ return (&(ap->ap_node));
+}
+
+static abf_policy_t *
+abf_policy_get_from_node (fib_node_t * node)
+{
+ return ((abf_policy_t *) (((char *) node) -
+ STRUCT_OFFSET_OF (abf_policy_t, ap_node)));
+}
+
+static void
+abf_policy_last_lock_gone (fib_node_t * node)
+{
+ abf_policy_destroy (abf_policy_get_from_node (node));
+}
+
+/*
+ * A back walk has reached this ABF policy
+ */
+static fib_node_back_walk_rc_t
+abf_policy_back_walk_notify (fib_node_t * node,
+ fib_node_back_walk_ctx_t * ctx)
+{
+ /*
+ * re-stack the fmask on the n-eos of the via
+ */
+ abf_policy_t *abf = abf_policy_get_from_node (node);
+
+ /*
+ * propagate further up the graph.
+ * we can do this synchronously since the fan out is small.
+ */
+ fib_walk_sync (abf_policy_fib_node_type, abf_policy_get_index (abf), ctx);
+
+ return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/*
+ * The BIER fmask's graph node virtual function table
+ */
+static const fib_node_vft_t abf_policy_vft = {
+ .fnv_get = abf_policy_get_node,
+ .fnv_last_lock = abf_policy_last_lock_gone,
+ .fnv_back_walk = abf_policy_back_walk_notify,
+};
+
+static clib_error_t *
+abf_policy_init (vlib_main_t * vm)
+{
+ abf_policy_fib_node_type = fib_node_register_new_type (&abf_policy_vft);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (abf_policy_init);
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+ .version = VPP_BUILD_VER,
+ .description = "ACL based Forwarding",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */