Files
vpp/src/plugins/abf/abf_itf_attach.c
Josh Dorsey 6903da2323 abf: exclude networks with deny rules
Type: improvement

Signed-off-by: Josh Dorsey <jdorsey@netgate.com>
Change-Id: Iee43ca9278922fc7396764b88cff1a87bcb28349
2023-01-12 02:17:37 +00:00

789 lines
19 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_itf_attach.h>
#include <vnet/fib/fib_path_list.h>
#include <plugins/acl/exports.h>
/**
* Forward declarations;
*/
extern vlib_node_registration_t abf_ip4_node;
extern vlib_node_registration_t abf_ip6_node;
/**
* FIB node registered type for the bonds
*/
static fib_node_type_t abf_itf_attach_fib_node_type;
/**
* Pool of ABF interface attachment objects
*/
abf_itf_attach_t *abf_itf_attach_pool;
/**
* A per interface vector of attached policies. used in the data-plane
*/
static u32 **abf_per_itf[FIB_PROTOCOL_MAX];
/**
* Per interface values of ACL lookup context IDs. used in the data-plane
*/
static u32 *abf_alctx_per_itf[FIB_PROTOCOL_MAX];
/**
* ABF ACL module user id returned during the initialization
*/
static u32 abf_acl_user_id;
/*
* ACL plugin method vtable
*/
static acl_plugin_methods_t acl_plugin;
/**
* A DB of attachments; key={abf_index,sw_if_index}
*/
static uword *abf_itf_attach_db;
static u64
abf_itf_attach_mk_key (u32 abf_index, u32 sw_if_index)
{
u64 key;
key = abf_index;
key = key << 32;
key |= sw_if_index;
return (key);
}
static abf_itf_attach_t *
abf_itf_attach_db_find (u32 abf_index, u32 sw_if_index)
{
uword *p;
u64 key;
key = abf_itf_attach_mk_key (abf_index, sw_if_index);
p = hash_get (abf_itf_attach_db, key);
if (NULL != p)
return (pool_elt_at_index (abf_itf_attach_pool, p[0]));
return (NULL);
}
static void
abf_itf_attach_db_add (u32 abf_index, u32 sw_if_index, abf_itf_attach_t * aia)
{
u64 key;
key = abf_itf_attach_mk_key (abf_index, sw_if_index);
hash_set (abf_itf_attach_db, key, aia - abf_itf_attach_pool);
}
static void
abf_itf_attach_db_del (u32 abf_index, u32 sw_if_index)
{
u64 key;
key = abf_itf_attach_mk_key (abf_index, sw_if_index);
hash_unset (abf_itf_attach_db, key);
}
static void
abf_itf_attach_stack (abf_itf_attach_t * aia)
{
/*
* stack the DPO on the forwarding contributed by the path-list
*/
dpo_id_t via_dpo = DPO_INVALID;
abf_policy_t *ap;
ap = abf_policy_get (aia->aia_abf);
fib_path_list_contribute_forwarding (ap->ap_pl,
(FIB_PROTOCOL_IP4 == aia->aia_proto ?
FIB_FORW_CHAIN_TYPE_UNICAST_IP4 :
FIB_FORW_CHAIN_TYPE_UNICAST_IP6),
FIB_PATH_LIST_FWD_FLAG_COLLAPSE,
&via_dpo);
dpo_stack_from_node ((FIB_PROTOCOL_IP4 == aia->aia_proto ?
abf_ip4_node.index :
abf_ip6_node.index), &aia->aia_dpo, &via_dpo);
dpo_reset (&via_dpo);
}
static int
abf_cmp_attach_for_sort (void *v1, void *v2)
{
const abf_itf_attach_t *aia1;
const abf_itf_attach_t *aia2;
aia1 = abf_itf_attach_get (*(u32 *) v1);
aia2 = abf_itf_attach_get (*(u32 *) v2);
return (aia1->aia_prio - aia2->aia_prio);
}
void
abf_setup_acl_lc (fib_protocol_t fproto, u32 sw_if_index)
{
u32 *acl_vec = 0;
u32 *aiai;
abf_itf_attach_t *aia;
if (~0 == abf_alctx_per_itf[fproto][sw_if_index])
return;
vec_foreach (aiai, abf_per_itf[fproto][sw_if_index])
{
aia = abf_itf_attach_get (*aiai);
vec_add1 (acl_vec, aia->aia_acl);
}
acl_plugin.set_acl_vec_for_context (abf_alctx_per_itf[fproto][sw_if_index],
acl_vec);
vec_free (acl_vec);
}
int
abf_itf_attach (fib_protocol_t fproto,
u32 policy_id, u32 priority, u32 sw_if_index)
{
abf_itf_attach_t *aia;
abf_policy_t *ap;
u32 api, aiai;
api = abf_policy_find (policy_id);
ASSERT (INDEX_INVALID != api);
ap = abf_policy_get (api);
/*
* check this is not a duplicate
*/
aia = abf_itf_attach_db_find (policy_id, sw_if_index);
if (NULL != aia)
return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
/*
* construct a new attachment object
*/
pool_get (abf_itf_attach_pool, aia);
fib_node_init (&aia->aia_node, abf_itf_attach_fib_node_type);
aia->aia_prio = priority;
aia->aia_proto = fproto;
aia->aia_acl = ap->ap_acl;
aia->aia_abf = api;
aia->aia_sw_if_index = sw_if_index;
aiai = aia - abf_itf_attach_pool;
abf_itf_attach_db_add (policy_id, sw_if_index, aia);
/*
* stack the DPO on the forwarding contributed by the path-list
*/
abf_itf_attach_stack (aia);
/*
* Insert the policy on the interfaces list.
*/
vec_validate_init_empty (abf_per_itf[fproto], sw_if_index, NULL);
vec_add1 (abf_per_itf[fproto][sw_if_index], aia - abf_itf_attach_pool);
if (1 == vec_len (abf_per_itf[fproto][sw_if_index]))
{
/*
* when enabling the first ABF policy on the interface
* we need to enable the interface input feature
*/
vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
"ip4-unicast" :
"ip6-unicast"),
(FIB_PROTOCOL_IP4 == fproto ?
"abf-input-ip4" :
"abf-input-ip6"),
sw_if_index, 1, NULL, 0);
/* if this is the first ABF policy, we need to acquire an ACL lookup context */
vec_validate_init_empty (abf_alctx_per_itf[fproto], sw_if_index, ~0);
abf_alctx_per_itf[fproto][sw_if_index] =
acl_plugin.get_lookup_context_index (abf_acl_user_id, sw_if_index, 0);
}
else
{
vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
abf_cmp_attach_for_sort);
}
/* Prepare and set the list of ACLs for lookup within the context */
abf_setup_acl_lc (fproto, sw_if_index);
/*
* become a child of the ABF policy so we are notified when
* its forwarding changes.
*/
aia->aia_sibling = fib_node_child_add (abf_policy_fib_node_type,
api,
abf_itf_attach_fib_node_type, aiai);
return (0);
}
int
abf_itf_detach (fib_protocol_t fproto, u32 policy_id, u32 sw_if_index)
{
abf_itf_attach_t *aia;
u32 index;
/*
* check this is a valid attachment
*/
aia = abf_itf_attach_db_find (policy_id, sw_if_index);
if (NULL == aia)
return (VNET_API_ERROR_NO_SUCH_ENTRY);
/*
* first remove from the interface's vector
*/
ASSERT (abf_per_itf[fproto]);
ASSERT (abf_per_itf[fproto][sw_if_index]);
index = vec_search (abf_per_itf[fproto][sw_if_index],
aia - abf_itf_attach_pool);
ASSERT (index != ~0);
vec_del1 (abf_per_itf[fproto][sw_if_index], index);
if (0 == vec_len (abf_per_itf[fproto][sw_if_index]))
{
/*
* when deleting the last ABF policy on the interface
* we need to disable the interface input feature
*/
vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
"ip4-unicast" :
"ip6-unicast"),
(FIB_PROTOCOL_IP4 == fproto ?
"abf-input-ip4" :
"abf-input-ip6"),
sw_if_index, 0, NULL, 0);
/* Return the lookup context, invalidate its id in our records */
acl_plugin.put_lookup_context_index (abf_alctx_per_itf[fproto]
[sw_if_index]);
abf_alctx_per_itf[fproto][sw_if_index] = ~0;
}
else
{
vec_sort_with_function (abf_per_itf[fproto][sw_if_index],
abf_cmp_attach_for_sort);
}
/* Prepare and set the list of ACLs for lookup within the context */
abf_setup_acl_lc (fproto, sw_if_index);
/*
* remove the dependency on the policy
*/
fib_node_child_remove (abf_policy_fib_node_type,
aia->aia_abf, aia->aia_sibling);
/*
* remove the attachment from the DB
*/
abf_itf_attach_db_del (policy_id, sw_if_index);
/*
* release our locks on FIB forwarding data
*/
dpo_reset (&aia->aia_dpo);
/*
* return the object
*/
pool_put (abf_itf_attach_pool, aia);
return (0);
}
static u8 *
format_abf_intf_attach (u8 * s, va_list * args)
{
abf_itf_attach_t *aia = va_arg (*args, abf_itf_attach_t *);
abf_policy_t *ap;
ap = abf_policy_get (aia->aia_abf);
s = format (s, "abf-interface-attach: policy:%d priority:%d",
ap->ap_id, aia->aia_prio);
s = format (s, "\n %U", format_dpo_id, &aia->aia_dpo, 2);
return (s);
}
static clib_error_t *
abf_itf_attach_cmd (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
u32 policy_id, sw_if_index;
fib_protocol_t fproto;
u32 is_del, priority;
vnet_main_t *vnm;
is_del = 0;
sw_if_index = policy_id = ~0;
vnm = vnet_get_main ();
fproto = FIB_PROTOCOL_MAX;
priority = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "del"))
is_del = 1;
else if (unformat (input, "add"))
is_del = 0;
else if (unformat (input, "ip4"))
fproto = FIB_PROTOCOL_IP4;
else if (unformat (input, "ip6"))
fproto = FIB_PROTOCOL_IP6;
else if (unformat (input, "policy %d", &policy_id))
;
else if (unformat (input, "priority %d", &priority))
;
else if (unformat (input, "%U",
unformat_vnet_sw_interface, vnm, &sw_if_index))
;
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
if (~0 == policy_id)
{
return (clib_error_return (0, "invalid policy ID:%d", policy_id));
}
if (~0 == sw_if_index)
{
return (clib_error_return (0, "invalid interface name"));
}
if (FIB_PROTOCOL_MAX == fproto)
{
return (clib_error_return (0, "Specify either ip4 or ip6"));
}
if (~0 == abf_policy_find (policy_id))
return (clib_error_return (0, "invalid policy ID:%d", policy_id));
if (is_del)
abf_itf_detach (fproto, policy_id, sw_if_index);
else
abf_itf_attach (fproto, policy_id, priority, sw_if_index);
return (NULL);
}
/* *INDENT-OFF* */
/**
* Attach an ABF policy to an interface.
*/
VLIB_CLI_COMMAND (abf_itf_attach_cmd_node, static) = {
.path = "abf attach",
.function = abf_itf_attach_cmd,
.short_help = "abf attach <ip4|ip6> [del] policy <value> <interface>",
// this is not MP safe
};
/* *INDENT-ON* */
static clib_error_t *
abf_show_attach_cmd (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
const abf_itf_attach_t *aia;
u32 sw_if_index, *aiai;
fib_protocol_t fproto;
vnet_main_t *vnm;
sw_if_index = ~0;
vnm = vnet_get_main ();
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "%U",
unformat_vnet_sw_interface, vnm, &sw_if_index))
;
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
if (~0 == sw_if_index)
{
vlib_cli_output (vm, "specify an interface");
}
/* *INDENT-OFF* */
FOR_EACH_FIB_IP_PROTOCOL(fproto)
{
if (sw_if_index < vec_len(abf_per_itf[fproto]))
{
if (vec_len(abf_per_itf[fproto][sw_if_index]))
vlib_cli_output(vm, "%U:", format_fib_protocol, fproto);
vec_foreach(aiai, abf_per_itf[fproto][sw_if_index])
{
aia = pool_elt_at_index(abf_itf_attach_pool, *aiai);
vlib_cli_output(vm, " %U", format_abf_intf_attach, aia);
}
}
}
/* *INDENT-ON* */
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (abf_show_attach_cmd_node, static) = {
.path = "show abf attach",
.function = abf_show_attach_cmd,
.short_help = "show abf attach <interface>",
.is_mp_safe = 1,
};
/* *INDENT-ON* */
void
abf_itf_attach_walk (abf_itf_attach_walk_cb_t cb, void *ctx)
{
u32 aii;
/* *INDENT-OFF* */
pool_foreach_index (aii, abf_itf_attach_pool)
{
if (!cb(aii, ctx))
break;
}
/* *INDENT-ON* */
}
typedef enum abf_next_t_
{
ABF_NEXT_DROP,
ABF_N_NEXT,
} abf_next_t;
typedef struct abf_input_trace_t_
{
abf_next_t next;
index_t index;
} abf_input_trace_t;
typedef enum
{
#define abf_error(n,s) ABF_ERROR_##n,
#include "abf_error.def"
#undef abf_error
ABF_N_ERROR,
} abf_error_t;
always_inline uword
abf_input_inline (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame, fib_protocol_t fproto)
{
u32 n_left_from, *from, *to_next, next_index, matches, misses;
from = vlib_frame_vector_args (frame);
n_left_from = frame->n_vectors;
next_index = node->cached_next_index;
matches = misses = 0;
while (n_left_from > 0)
{
u32 n_left_to_next;
vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
while (n_left_from > 0 && n_left_to_next > 0)
{
const u32 *attachments0;
const abf_itf_attach_t *aia0;
abf_next_t next0 = ABF_NEXT_DROP;
vlib_buffer_t *b0;
u32 bi0, sw_if_index0;
fa_5tuple_opaque_t fa_5tuple0;
u32 match_acl_index = ~0;
u32 match_acl_pos = ~0;
u32 match_rule_index = ~0;
u32 trace_bitmap = 0;
u32 lc_index;
u8 action;
bi0 = from[0];
to_next[0] = bi0;
from += 1;
to_next += 1;
n_left_from -= 1;
n_left_to_next -= 1;
b0 = vlib_get_buffer (vm, bi0);
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
ASSERT (vec_len (abf_per_itf[fproto]) > sw_if_index0);
attachments0 = abf_per_itf[fproto][sw_if_index0];
ASSERT (vec_len (abf_alctx_per_itf[fproto]) > sw_if_index0);
/*
* check if any of the policies attached to this interface matches.
*/
lc_index = abf_alctx_per_itf[fproto][sw_if_index0];
/*
A non-inline version looks like this:
acl_plugin.fill_5tuple (lc_index, b0, (FIB_PROTOCOL_IP6 == fproto),
1, 0, &fa_5tuple0);
if (acl_plugin.match_5tuple
(lc_index, &fa_5tuple0, (FIB_PROTOCOL_IP6 == fproto), &action,
&match_acl_pos, &match_acl_index, &match_rule_index,
&trace_bitmap))
. . .
*/
acl_plugin_fill_5tuple_inline (acl_plugin.p_acl_main, lc_index, b0,
(FIB_PROTOCOL_IP6 == fproto), 1, 0,
&fa_5tuple0);
if (acl_plugin_match_5tuple_inline (
acl_plugin.p_acl_main, lc_index, &fa_5tuple0,
(FIB_PROTOCOL_IP6 == fproto), &action, &match_acl_pos,
&match_acl_index, &match_rule_index, &trace_bitmap) &&
action > 0)
{
/*
* match:
* follow the DPO chain
*/
aia0 = abf_itf_attach_get (attachments0[match_acl_pos]);
next0 = aia0->aia_dpo.dpoi_next_node;
vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
aia0->aia_dpo.dpoi_index;
matches++;
}
else
{
/*
* miss:
* move on down the feature arc
*/
vnet_feature_next (&next0, b0);
misses++;
}
if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
{
abf_input_trace_t *tr;
tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
tr->next = next0;
tr->index = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
}
/* verify speculative enqueue, maybe switch current next frame */
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next, bi0,
next0);
}
vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}
vlib_node_increment_counter (vm,
(fproto = FIB_PROTOCOL_IP6 ?
abf_ip4_node.index :
abf_ip6_node.index),
ABF_ERROR_MATCHED, matches);
vlib_node_increment_counter (vm,
(fproto = FIB_PROTOCOL_IP6 ?
abf_ip4_node.index :
abf_ip6_node.index),
ABF_ERROR_MISSED, misses);
return frame->n_vectors;
}
static uword
abf_input_ip4 (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
}
static uword
abf_input_ip6 (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
return abf_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
}
static u8 *
format_abf_input_trace (u8 * s, va_list * args)
{
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
abf_input_trace_t *t = va_arg (*args, abf_input_trace_t *);
s = format (s, " next %d index %d", t->next, t->index);
return s;
}
static char *abf_error_strings[] = {
#define abf_error(n,s) s,
#include "abf_error.def"
#undef abf_error
};
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (abf_ip4_node) =
{
.function = abf_input_ip4,
.name = "abf-input-ip4",
.vector_size = sizeof (u32),
.format_trace = format_abf_input_trace,
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = ABF_N_ERROR,
.error_strings = abf_error_strings,
.n_next_nodes = ABF_N_NEXT,
.next_nodes =
{
[ABF_NEXT_DROP] = "error-drop",
}
};
VLIB_REGISTER_NODE (abf_ip6_node) =
{
.function = abf_input_ip6,
.name = "abf-input-ip6",
.vector_size = sizeof (u32),
.format_trace = format_abf_input_trace,
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = 0,
.n_next_nodes = ABF_N_NEXT,
.next_nodes =
{
[ABF_NEXT_DROP] = "error-drop",
}
};
VNET_FEATURE_INIT (abf_ip4_feat, static) =
{
.arc_name = "ip4-unicast",
.node_name = "abf-input-ip4",
.runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
};
VNET_FEATURE_INIT (abf_ip6_feat, static) =
{
.arc_name = "ip6-unicast",
.node_name = "abf-input-ip6",
.runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
};
/* *INDENT-ON* */
static fib_node_t *
abf_itf_attach_get_node (fib_node_index_t index)
{
abf_itf_attach_t *aia = abf_itf_attach_get (index);
return (&(aia->aia_node));
}
static abf_itf_attach_t *
abf_itf_attach_get_from_node (fib_node_t * node)
{
return ((abf_itf_attach_t *) (((char *) node) -
STRUCT_OFFSET_OF (abf_itf_attach_t,
aia_node)));
}
static void
abf_itf_attach_last_lock_gone (fib_node_t * node)
{
/*
* ABF interface attachments are leaves on the graph.
* we do not manage locks from children.
*/
}
/*
* abf_itf_attach_back_walk_notify
*
* A back walk has reached this BIER fmask
*/
static fib_node_back_walk_rc_t
abf_itf_attach_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_itf_attach_t *aia = abf_itf_attach_get_from_node (node);
abf_itf_attach_stack (aia);
return (FIB_NODE_BACK_WALK_CONTINUE);
}
/*
* The BIER fmask's graph node virtual function table
*/
static const fib_node_vft_t abf_itf_attach_vft = {
.fnv_get = abf_itf_attach_get_node,
.fnv_last_lock = abf_itf_attach_last_lock_gone,
.fnv_back_walk = abf_itf_attach_back_walk_notify,
};
static clib_error_t *
abf_itf_bond_init (vlib_main_t * vm)
{
abf_itf_attach_fib_node_type =
fib_node_register_new_type ("abf-attach", &abf_itf_attach_vft);
clib_error_t *acl_init_res = acl_plugin_exports_init (&acl_plugin);
if (acl_init_res)
return (acl_init_res);
abf_acl_user_id =
acl_plugin.register_user_module ("ABF plugin", "sw_if_index", NULL);
return (NULL);
}
/* *INDENT-OFF* */
VLIB_INIT_FUNCTION (abf_itf_bond_init) =
{
.runs_after = VLIB_INITS("acl_init"),
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/