Type: refactor Change-Id: I5235bf3e9aff58af6ba2c14e8c6529c4fc9ec86c Signed-off-by: Damjan Marion <damarion@cisco.com>
473 lines
10 KiB
C
473 lines
10 KiB
C
/*
|
|
* 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 <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);
|
|
}
|
|
|
|
|
|
int
|
|
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 ancestry
|
|
* - backwalk to poke all attachments to update
|
|
*/
|
|
fib_node_index_t old_pl;
|
|
|
|
ap = abf_policy_get (api);
|
|
old_pl = ap->ap_pl;
|
|
if (ap->ap_acl != acl_index)
|
|
{
|
|
/* Should change this error code to something more descriptive */
|
|
return (VNET_API_ERROR_INVALID_VALUE);
|
|
}
|
|
|
|
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);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
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 (VNET_API_ERROR_INVALID_VALUE);
|
|
}
|
|
|
|
/*
|
|
* update an existing policy.
|
|
* - add the path to the path-list and swap our ancestry
|
|
* - backwalk to poke all attachments to update
|
|
*/
|
|
fib_node_index_t old_pl;
|
|
|
|
ap = abf_policy_get (api);
|
|
old_pl = ap->ap_pl;
|
|
|
|
fib_path_list_lock (old_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);
|
|
}
|
|
fib_path_list_unlock (old_pl);
|
|
|
|
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;
|
|
fib_route_path_t *rpaths = NULL, rpath;
|
|
u32 acl_index, policy_id, is_del;
|
|
dpo_proto_t payload_proto;
|
|
int rv = 0;
|
|
|
|
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, &payload_proto))
|
|
vec_add1 (rpaths, rpath);
|
|
else
|
|
{
|
|
clib_error_t *err;
|
|
err = clib_error_return (0, "unknown input '%U'",
|
|
format_unformat_error, line_input);
|
|
unformat_free (line_input);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (INDEX_INVALID == policy_id)
|
|
{
|
|
vlib_cli_output (vm, "Specify a Policy ID");
|
|
goto out;
|
|
}
|
|
|
|
if (vec_len (rpaths) == 0)
|
|
{
|
|
vlib_cli_output (vm, "Hop path must not be empty");
|
|
goto out;
|
|
}
|
|
|
|
if (!is_del)
|
|
{
|
|
if (INDEX_INVALID == acl_index)
|
|
{
|
|
vlib_cli_output (vm, "ACL index must be set");
|
|
goto out;
|
|
}
|
|
|
|
rv = abf_policy_update (policy_id, acl_index, rpaths);
|
|
/* Should change this error code to something more descriptive */
|
|
if (rv == VNET_API_ERROR_INVALID_VALUE)
|
|
{
|
|
vlib_cli_output (vm,
|
|
"ACL index must match existing ACL index in policy");
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
abf_policy_delete (policy_id, rpaths);
|
|
}
|
|
|
|
out:
|
|
unformat_free (line_input);
|
|
return (NULL);
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
};
|
|
|
|
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;
|
|
|
|
pool_foreach_index (api, abf_policy_pool)
|
|
{
|
|
if (!cb(api, ctx))
|
|
break;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
pool_foreach (ap, abf_policy_pool)
|
|
{
|
|
vlib_cli_output(vm, "%U", format_abf, ap);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
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", &abf_policy_vft);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (abf_policy_init);
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|