diff options
author | Andrew Yourtchenko <ayourtch@gmail.com> | 2018-03-21 19:53:39 +0100 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2018-03-22 20:50:09 +0000 |
commit | 94f9a6de3f706243d138e05b63fef1d5c8174f6c (patch) | |
tree | 5e50ccf062229f3d60b9ec656c6ca53cae0c5455 /src | |
parent | 8ced6e1a53d6a828debe7dc2396c7eb7d6866e4a (diff) |
acl-plugin: implement ACL lookup contexts for "ACL as a service" use by other plugins
This code implements the functionality required for other plugins wishing
to perform ACL lookups in the contexts of their choice, rather than only
in the context of the interface in/out.
The lookups are the stateless ACLs - there is no concept of "direction"
within the context, hence no concept of "connection" either.
The plugins need to include the
The file acl_lookup_context.md has more info.
Change-Id: I91ba97428cc92b24d1517e808dc2fd8e56ea2f8d
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/acl.am | 1 | ||||
-rw-r--r-- | src/plugins/acl/acl.c | 883 | ||||
-rw-r--r-- | src/plugins/acl/acl.h | 24 | ||||
-rw-r--r-- | src/plugins/acl/acl_lookup_context.md | 113 | ||||
-rw-r--r-- | src/plugins/acl/elog_acl_trace.h | 137 | ||||
-rw-r--r-- | src/plugins/acl/exports.h | 31 | ||||
-rw-r--r-- | src/plugins/acl/fa_node.c | 480 | ||||
-rw-r--r-- | src/plugins/acl/fa_node.h | 11 | ||||
-rw-r--r-- | src/plugins/acl/hash_lookup.c | 451 | ||||
-rw-r--r-- | src/plugins/acl/hash_lookup.h | 29 | ||||
-rw-r--r-- | src/plugins/acl/hash_lookup_types.h | 11 | ||||
-rw-r--r-- | src/plugins/acl/lookup_context.c | 304 | ||||
-rw-r--r-- | src/plugins/acl/lookup_context.h | 60 | ||||
-rw-r--r-- | src/plugins/acl/public_inlines.h | 731 | ||||
-rw-r--r-- | src/vnet/api_errno.h | 3 |
15 files changed, 2120 insertions, 1149 deletions
diff --git a/src/plugins/acl.am b/src/plugins/acl.am index 0a414481dbf..b67191ec938 100644 --- a/src/plugins/acl.am +++ b/src/plugins/acl.am @@ -17,6 +17,7 @@ vppplugins_LTLIBRARIES += acl_plugin.la acl_plugin_la_SOURCES = \ acl/acl.c \ acl/hash_lookup.c \ + acl/lookup_context.c \ acl/fa_node.c \ acl/l2sess.h \ acl/manual_fns.h \ diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index 8543ac6eef2..314380bcf8f 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -51,7 +51,7 @@ #undef vl_api_version #include "fa_node.h" -#include "hash_lookup.h" +#include "public_inlines.h" acl_main_t acl_main; @@ -105,6 +105,16 @@ format_vec16 (u8 * s, va_list * va) +u8 +acl_plugin_acl_exists (u32 acl_index) +{ + acl_main_t *am = &acl_main; + + if (pool_is_free_index (am->acls, acl_index)) + return 0; + + return 1; +} static void * acl_set_heap (acl_main_t * am) @@ -191,6 +201,77 @@ vl_api_acl_plugin_control_ping_t_handler (vl_api_acl_plugin_control_ping_t * /* *INDENT-ON* */ } +static void +print_clib_warning_and_reset (vlib_main_t * vm, u8 * out0) +{ + clib_warning ("%v", out0); + vec_reset_length (out0); +} + +static void +print_cli_and_reset (vlib_main_t * vm, u8 * out0) +{ + vlib_cli_output (vm, "%v", out0); + vec_reset_length (out0); +} + +typedef void (*acl_vector_print_func_t) (vlib_main_t * vm, u8 * out0); + +static void +acl_print_acl_x (acl_vector_print_func_t vpr, vlib_main_t * vm, + acl_main_t * am, int acl_index) +{ + acl_rule_t *r; + u8 *out0 = format (0, "acl-index %u count %u tag {%s}\n", acl_index, + am->acls[acl_index].count, am->acls[acl_index].tag); + int j; + vpr (vm, out0); + for (j = 0; j < am->acls[acl_index].count; j++) + { + r = &am->acls[acl_index].rules[j]; + out0 = format (out0, " %4d: %s ", j, r->is_ipv6 ? "ipv6" : "ipv4"); + out0 = format_acl_action (out0, r->is_permit); + out0 = format (out0, " src %U/%d", format_ip46_address, &r->src, + r->is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, + r->src_prefixlen); + out0 = + format (out0, " dst %U/%d", format_ip46_address, &r->dst, + r->is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, r->dst_prefixlen); + out0 = format (out0, " proto %d", r->proto); + out0 = format (out0, " sport %d", r->src_port_or_type_first); + if (r->src_port_or_type_first != r->src_port_or_type_last) + { + out0 = format (out0, "-%d", r->src_port_or_type_last); + } + out0 = format (out0, " dport %d", r->dst_port_or_code_first); + if (r->dst_port_or_code_first != r->dst_port_or_code_last) + { + out0 = format (out0, "-%d", r->dst_port_or_code_last); + } + if (r->tcp_flags_mask || r->tcp_flags_value) + { + out0 = + format (out0, " tcpflags %d mask %d", r->tcp_flags_value, + r->tcp_flags_mask); + } + out0 = format (out0, "\n"); + vpr (vm, out0); + } +} + +static void +acl_print_acl (vlib_main_t * vm, acl_main_t * am, int acl_index) +{ + acl_print_acl_x (print_cli_and_reset, vm, am, acl_index); +} + +static void +warning_acl_print_acl (vlib_main_t * vm, acl_main_t * am, int acl_index) +{ + acl_print_acl_x (print_clib_warning_and_reset, vm, am, acl_index); +} + + static int acl_add_list (u32 count, vl_api_acl_rule_t rules[], u32 * acl_list_index, u8 * tag) @@ -201,6 +282,10 @@ acl_add_list (u32 count, vl_api_acl_rule_t rules[], acl_rule_t *acl_new_rules = 0; int i; + if (am->trace_acl > 255) + clib_warning ("API dbg: acl_add_list index %d tag %s", *acl_list_index, + tag); + if (*acl_list_index != ~0) { /* They supplied some number, let's see if this ACL exists */ @@ -264,7 +349,6 @@ acl_add_list (u32 count, vl_api_acl_rule_t rules[], else { a = am->acls + *acl_list_index; - hash_acl_delete (am, *acl_list_index); /* Get rid of the old rules */ if (a->rules) vec_free (a->rules); @@ -272,82 +356,54 @@ acl_add_list (u32 count, vl_api_acl_rule_t rules[], a->rules = acl_new_rules; a->count = count; memcpy (a->tag, tag, sizeof (a->tag)); - hash_acl_add (am, *acl_list_index); + if (am->trace_acl > 255) + warning_acl_print_acl (am->vlib_main, am, *acl_list_index); + /* notify the lookup contexts about the ACL changes */ + acl_plugin_lookup_context_notify_acl_change (*acl_list_index); clib_mem_set_heap (oldheap); return 0; } static int +acl_is_used_by (u32 acl_index, u32 ** foo_index_vec_by_acl) +{ + if (acl_index < vec_len (foo_index_vec_by_acl)) + { + if (vec_len (vec_elt (foo_index_vec_by_acl, acl_index)) > 0) + { + /* ACL is applied somewhere. */ + return 1; + } + } + return 0; +} + +static int acl_del_list (u32 acl_list_index) { acl_main_t *am = &acl_main; acl_list_t *a; - int i, ii; if (pool_is_free_index (am->acls, acl_list_index)) { return VNET_API_ERROR_NO_SUCH_ENTRY; } - - if (acl_list_index < vec_len (am->input_sw_if_index_vec_by_acl)) - { - if (vec_len (vec_elt (am->input_sw_if_index_vec_by_acl, acl_list_index)) - > 0) - { - /* ACL is applied somewhere inbound. Refuse to delete */ - return VNET_API_ERROR_ACL_IN_USE_INBOUND; - } - } - if (acl_list_index < vec_len (am->output_sw_if_index_vec_by_acl)) - { - if (vec_len - (vec_elt (am->output_sw_if_index_vec_by_acl, acl_list_index)) > 0) - { - /* ACL is applied somewhere outbound. Refuse to delete */ - return VNET_API_ERROR_ACL_IN_USE_OUTBOUND; - } - } + if (acl_is_used_by (acl_list_index, am->input_sw_if_index_vec_by_acl)) + return VNET_API_ERROR_ACL_IN_USE_INBOUND; + if (acl_is_used_by (acl_list_index, am->output_sw_if_index_vec_by_acl)) + return VNET_API_ERROR_ACL_IN_USE_OUTBOUND; + /* lookup contexts cover other cases, not just inbound/oubound, so check that */ + if (acl_is_used_by (acl_list_index, am->lc_index_vec_by_acl)) + return VNET_API_ERROR_ACL_IN_USE_BY_LOOKUP_CONTEXT; void *oldheap = acl_set_heap (am); - /* delete any references to the ACL */ - for (i = 0; i < vec_len (am->output_acl_vec_by_sw_if_index); i++) - { - for (ii = 0; ii < vec_len (am->output_acl_vec_by_sw_if_index[i]); - /* see body */ ) - { - if (acl_list_index == am->output_acl_vec_by_sw_if_index[i][ii]) - { - vec_del1 (am->output_acl_vec_by_sw_if_index[i], ii); - } - else - { - ii++; - } - } - } - for (i = 0; i < vec_len (am->input_acl_vec_by_sw_if_index); i++) - { - for (ii = 0; ii < vec_len (am->input_acl_vec_by_sw_if_index[i]); - /* see body */ ) - { - if (acl_list_index == am->input_acl_vec_by_sw_if_index[i][ii]) - { - vec_del1 (am->input_acl_vec_by_sw_if_index[i], ii); - } - else - { - ii++; - } - } - } - /* delete the hash table data */ - hash_acl_delete (am, acl_list_index); /* now we can delete the ACL itself */ a = pool_elt_at_index (am->acls, acl_list_index); if (a->rules) vec_free (a->rules); - pool_put (am->acls, a); + /* acl_list_index is now free, notify the lookup contexts */ + acl_plugin_lookup_context_notify_acl_change (acl_list_index); clib_mem_set_heap (oldheap); return 0; } @@ -954,10 +1010,11 @@ acl_hook_l2_output_classify (acl_main_t * am, u32 sw_if_index) rv = vnet_l2_output_classify_set_tables (sw_if_index, ip4_table_index, ip6_table_index, dot1q_table_index); - +/* clib_warning ("ACL enabling on interface sw_if_index %d, setting tables to the following: ip4: %d ip6: %d\n", sw_if_index, ip4_table_index, ip6_table_index); +*/ if (rv) { acl_classify_add_del_table_tiny (cm, ip6_5tuple_mask, @@ -1000,17 +1057,32 @@ done: return rv; } -int +static void +acl_clear_sessions (acl_main_t * am, u32 sw_if_index) +{ + void *oldheap = clib_mem_set_heap (am->vlib_main->heap_base); + vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index, + ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX, + sw_if_index); + clib_mem_set_heap (oldheap); +} + + +static int acl_interface_in_enable_disable (acl_main_t * am, u32 sw_if_index, int enable_disable) { - int rv; + int rv = 0; /* Utterly wrong? */ if (pool_is_free_index (am->vnet_main->interface_main.sw_interfaces, sw_if_index)) return VNET_API_ERROR_INVALID_SW_IF_INDEX; + if (clib_bitmap_get (am->in_acl_on_sw_if_index, sw_if_index) == + enable_disable) + return 0; + acl_fa_enable_disable (sw_if_index, 1, enable_disable); if (enable_disable) @@ -1022,10 +1094,13 @@ acl_interface_in_enable_disable (acl_main_t * am, u32 sw_if_index, rv = acl_unhook_l2_input_classify (am, sw_if_index); } + am->in_acl_on_sw_if_index = + clib_bitmap_set (am->in_acl_on_sw_if_index, sw_if_index, enable_disable); + return rv; } -int +static int acl_interface_out_enable_disable (acl_main_t * am, u32 sw_if_index, int enable_disable) { @@ -1036,6 +1111,10 @@ acl_interface_out_enable_disable (acl_main_t * am, u32 sw_if_index, sw_if_index)) return VNET_API_ERROR_INVALID_SW_IF_INDEX; + if (clib_bitmap_get (am->out_acl_on_sw_if_index, sw_if_index) == + enable_disable) + return 0; + acl_fa_enable_disable (sw_if_index, 0, enable_disable); if (enable_disable) @@ -1047,231 +1126,168 @@ acl_interface_out_enable_disable (acl_main_t * am, u32 sw_if_index, rv = acl_unhook_l2_output_classify (am, sw_if_index); } + am->out_acl_on_sw_if_index = + clib_bitmap_set (am->out_acl_on_sw_if_index, sw_if_index, enable_disable); + return rv; } static int +acl_interface_inout_enable_disable (acl_main_t * am, u32 sw_if_index, + int is_input, int enable_disable) +{ + if (is_input) + return acl_interface_in_enable_disable (am, sw_if_index, enable_disable); + else + return acl_interface_out_enable_disable (am, sw_if_index, enable_disable); +} + +static int acl_is_not_defined (acl_main_t * am, u32 acl_list_index) { return (pool_is_free_index (am->acls, acl_list_index)); } - static int -acl_interface_add_inout_acl (u32 sw_if_index, u8 is_input, u32 acl_list_index) -{ - acl_main_t *am = &acl_main; - if (acl_is_not_defined (am, acl_list_index)) - { - /* ACL is not defined. Can not apply */ - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - void *oldheap = acl_set_heap (am); +acl_interface_set_inout_acl_list (acl_main_t * am, u32 sw_if_index, + u8 is_input, u32 * vec_acl_list_index, + int *may_clear_sessions) +{ + u32 *pacln; + uword *seen_acl_bitmap = 0; + uword *old_seen_acl_bitmap = 0; + uword *change_acl_bitmap = 0; + int acln; + int rv = 0; - if (is_input) - { - vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); - u32 index = vec_search (am->input_acl_vec_by_sw_if_index[sw_if_index], - acl_list_index); - if (index < vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index])) - { - clib_warning - ("ACL %d is already applied inbound on sw_if_index %d (index %d)", - acl_list_index, sw_if_index, index); - /* the entry is already there */ - clib_mem_set_heap (oldheap); - return VNET_API_ERROR_ACL_IN_USE_INBOUND; - } - /* if there was no ACL applied before, enable the ACL processing */ - if (vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]) == 0) - { - acl_interface_in_enable_disable (am, sw_if_index, 1); - } - vec_add (am->input_acl_vec_by_sw_if_index[sw_if_index], &acl_list_index, - 1); - vec_validate (am->input_sw_if_index_vec_by_acl, acl_list_index); - vec_add (am->input_sw_if_index_vec_by_acl[acl_list_index], &sw_if_index, - 1); - } - else - { - vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); + if (am->trace_acl > 255) + clib_warning + ("API dbg: acl_interface_set_inout_acl_list: sw_if_index %d is_input %d acl_vec: [%U]", + sw_if_index, is_input, format_vec32, vec_acl_list_index, "%d"); - u32 index = vec_search (am->output_acl_vec_by_sw_if_index[sw_if_index], - acl_list_index); - if (index < vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index])) - { - clib_warning - ("ACL %d is already applied outbound on sw_if_index %d (index %d)", - acl_list_index, sw_if_index, index); - /* the entry is already there */ - clib_mem_set_heap (oldheap); - return VNET_API_ERROR_ACL_IN_USE_OUTBOUND; - } - /* if there was no ACL applied before, enable the ACL processing */ - if (vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]) == 0) - { - acl_interface_out_enable_disable (am, sw_if_index, 1); - } - vec_add (am->output_acl_vec_by_sw_if_index[sw_if_index], - &acl_list_index, 1); - vec_validate (am->output_sw_if_index_vec_by_acl, acl_list_index); - vec_add (am->output_sw_if_index_vec_by_acl[acl_list_index], - &sw_if_index, 1); + vec_foreach (pacln, vec_acl_list_index) + { + if (acl_is_not_defined (am, *pacln)) + { + /* ACL is not defined. Can not apply */ + clib_warning ("ERROR: ACL %d not defined", *pacln); + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto done; + } + if (clib_bitmap_get (seen_acl_bitmap, *pacln)) + { + /* ACL being applied twice within the list. error. */ + clib_warning ("ERROR: ACL %d being applied twice", *pacln); + rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS; + goto done; + } + seen_acl_bitmap = clib_bitmap_set (seen_acl_bitmap, *pacln, 1); + } + + + u32 **pinout_lc_index_by_sw_if_index = + is_input ? &am-> + input_lc_index_by_sw_if_index : &am->output_lc_index_by_sw_if_index; + + u32 ***pinout_acl_vec_by_sw_if_index = + is_input ? &am-> + input_acl_vec_by_sw_if_index : &am->output_acl_vec_by_sw_if_index; + + u32 ***pinout_sw_if_index_vec_by_acl = + is_input ? &am-> + input_sw_if_index_vec_by_acl : &am->output_sw_if_index_vec_by_acl; + + vec_validate ((*pinout_acl_vec_by_sw_if_index), sw_if_index); + + clib_bitmap_validate (old_seen_acl_bitmap, 1); + + vec_foreach (pacln, (*pinout_acl_vec_by_sw_if_index)[sw_if_index]) + { + old_seen_acl_bitmap = clib_bitmap_set (old_seen_acl_bitmap, *pacln, 1); + } + change_acl_bitmap = + clib_bitmap_dup_xor (old_seen_acl_bitmap, seen_acl_bitmap); + + if (am->trace_acl > 255) + clib_warning ("bitmaps: old seen %U new seen %U changed %U", + format_bitmap_hex, old_seen_acl_bitmap, format_bitmap_hex, + seen_acl_bitmap, format_bitmap_hex, change_acl_bitmap); + +/* *INDENT-OFF* */ + clib_bitmap_foreach(acln, change_acl_bitmap, ({ + if (clib_bitmap_get(old_seen_acl_bitmap, acln)) { + /* ACL is being removed. */ + if (acln < vec_len((*pinout_sw_if_index_vec_by_acl))) { + int index = vec_search((*pinout_sw_if_index_vec_by_acl)[acln], sw_if_index); + vec_del1((*pinout_sw_if_index_vec_by_acl)[acln], index); + } + } else { + /* ACL is being added. */ + vec_validate((*pinout_sw_if_index_vec_by_acl), acln); + vec_add1((*pinout_sw_if_index_vec_by_acl)[acln], sw_if_index); } - clib_mem_set_heap (oldheap); - return 0; -} + })); +/* *INDENT-ON* */ + vec_free ((*pinout_acl_vec_by_sw_if_index)[sw_if_index]); + (*pinout_acl_vec_by_sw_if_index)[sw_if_index] = + vec_dup (vec_acl_list_index); -static int -acl_interface_del_inout_acl (u32 sw_if_index, u8 is_input, u32 acl_list_index) -{ - acl_main_t *am = &acl_main; - int i; - int rv = VNET_API_ERROR_NO_SUCH_ENTRY; - void *oldheap = acl_set_heap (am); - if (is_input) + /* if no commonalities between the ACL# - then we should definitely clear the sessions */ + if (may_clear_sessions && *may_clear_sessions + && !clib_bitmap_is_zero (change_acl_bitmap)) { - vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); - for (i = 0; i < vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]); - i++) - { - if (acl_list_index == - am->input_acl_vec_by_sw_if_index[sw_if_index][i]) - { - vec_del1 (am->input_acl_vec_by_sw_if_index[sw_if_index], i); - rv = 0; - break; - } - } - - if (acl_list_index < vec_len (am->input_sw_if_index_vec_by_acl)) - { - u32 index = - vec_search (am->input_sw_if_index_vec_by_acl[acl_list_index], - sw_if_index); - if (index < - vec_len (am->input_sw_if_index_vec_by_acl[acl_list_index])) - { - hash_acl_unapply (am, sw_if_index, is_input, acl_list_index); - vec_del1 (am->input_sw_if_index_vec_by_acl[acl_list_index], - index); - } - } + acl_clear_sessions (am, sw_if_index); + *may_clear_sessions = 0; + } - /* If there is no more ACLs applied on an interface, disable ACL processing */ - if (0 == vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index])) + /* + * prepare or delete the lookup context if necessary, and if context exists, set ACL list + */ + vec_validate_init_empty ((*pinout_lc_index_by_sw_if_index), sw_if_index, + ~0); + if (vec_len (vec_acl_list_index) > 0) + { + u32 lc_index = (*pinout_lc_index_by_sw_if_index)[sw_if_index]; + if (~0 == lc_index) { - acl_interface_in_enable_disable (am, sw_if_index, 0); + lc_index = + acl_plugin_get_lookup_context_index (am->interface_acl_user_id, + sw_if_index, is_input); + ASSERT (lc_index >= 0); + (*pinout_lc_index_by_sw_if_index)[sw_if_index] = lc_index; } + acl_plugin_set_acl_vec_for_context (lc_index, vec_acl_list_index); } else { - vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); - for (i = 0; - i < vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]); i++) + if (~0 != (*pinout_lc_index_by_sw_if_index)[sw_if_index]) { - if (acl_list_index == - am->output_acl_vec_by_sw_if_index[sw_if_index][i]) - { - vec_del1 (am->output_acl_vec_by_sw_if_index[sw_if_index], i); - rv = 0; - break; - } + acl_plugin_put_lookup_context_index ((*pinout_lc_index_by_sw_if_index)[sw_if_index]); + (*pinout_lc_index_by_sw_if_index)[sw_if_index] = ~0; } + } - if (acl_list_index < vec_len (am->output_sw_if_index_vec_by_acl)) - { - u32 index = - vec_search (am->output_sw_if_index_vec_by_acl[acl_list_index], - sw_if_index); - if (index < - vec_len (am->output_sw_if_index_vec_by_acl[acl_list_index])) - { - hash_acl_unapply (am, sw_if_index, is_input, acl_list_index); - vec_del1 (am->output_sw_if_index_vec_by_acl[acl_list_index], - index); - } - } + /* ensure ACL processing is enabled/disabled as needed */ + acl_interface_inout_enable_disable (am, sw_if_index, is_input, + vec_len (vec_acl_list_index) > 0); - /* If there is no more ACLs applied on an interface, disable ACL processing */ - if (0 == vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index])) - { - acl_interface_out_enable_disable (am, sw_if_index, 0); - } - } - clib_mem_set_heap (oldheap); +done: + clib_bitmap_free (change_acl_bitmap); + clib_bitmap_free (seen_acl_bitmap); + clib_bitmap_free (old_seen_acl_bitmap); return rv; } static void -acl_interface_reset_inout_acls (u32 sw_if_index, u8 is_input) +acl_interface_reset_inout_acls (u32 sw_if_index, u8 is_input, + int *may_clear_sessions) { acl_main_t *am = &acl_main; - int i; void *oldheap = acl_set_heap (am); - if (is_input) - { - vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); - if (vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]) > 0) - { - acl_interface_in_enable_disable (am, sw_if_index, 0); - } - - for (i = vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]) - 1; - i >= 0; i--) - { - u32 acl_list_index = - am->input_acl_vec_by_sw_if_index[sw_if_index][i]; - hash_acl_unapply (am, sw_if_index, is_input, acl_list_index); - if (acl_list_index < vec_len (am->input_sw_if_index_vec_by_acl)) - { - u32 index = - vec_search (am->input_sw_if_index_vec_by_acl[acl_list_index], - sw_if_index); - if (index < - vec_len (am->input_sw_if_index_vec_by_acl[acl_list_index])) - { - vec_del1 (am->input_sw_if_index_vec_by_acl[acl_list_index], - index); - } - } - } - - vec_reset_length (am->input_acl_vec_by_sw_if_index[sw_if_index]); - } - else - { - vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); - if (vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]) > 0) - { - acl_interface_out_enable_disable (am, sw_if_index, 0); - } - - for (i = vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]) - 1; - i >= 0; i--) - { - u32 acl_list_index = - am->output_acl_vec_by_sw_if_index[sw_if_index][i]; - hash_acl_unapply (am, sw_if_index, is_input, acl_list_index); - if (acl_list_index < vec_len (am->output_sw_if_index_vec_by_acl)) - { - u32 index = - vec_search (am->output_sw_if_index_vec_by_acl[acl_list_index], - sw_if_index); - if (index < - vec_len (am->output_sw_if_index_vec_by_acl[acl_list_index])) - { - vec_del1 (am->output_sw_if_index_vec_by_acl[acl_list_index], - index); - } - } - } - - vec_reset_length (am->output_acl_vec_by_sw_if_index[sw_if_index]); - } + acl_interface_set_inout_acl_list (am, sw_if_index, is_input, 0, + may_clear_sessions); clib_mem_set_heap (oldheap); } @@ -1279,23 +1295,61 @@ static int acl_interface_add_del_inout_acl (u32 sw_if_index, u8 is_add, u8 is_input, u32 acl_list_index) { - int rv = VNET_API_ERROR_NO_SUCH_ENTRY; + acl_main_t *am = &acl_main; + u32 *acl_vec = 0; + int may_clear_sessions = 1; + + int error_already_applied = is_input ? VNET_API_ERROR_ACL_IN_USE_INBOUND + : VNET_API_ERROR_ACL_IN_USE_OUTBOUND; + + u32 ***pinout_acl_vec_by_sw_if_index = + is_input ? &am-> + input_acl_vec_by_sw_if_index : &am->output_acl_vec_by_sw_if_index; + int rv = 0; + void *oldheap = acl_set_heap (am); + if (is_add) { - rv = - acl_interface_add_inout_acl (sw_if_index, is_input, acl_list_index); - if (rv == 0) + vec_validate ((*pinout_acl_vec_by_sw_if_index), sw_if_index); + u32 index = vec_search ((*pinout_acl_vec_by_sw_if_index)[sw_if_index], + acl_list_index); + + if (~0 != index) { - hash_acl_apply (am, sw_if_index, is_input, acl_list_index); + rv = error_already_applied; + goto done; } + + acl_vec = vec_dup ((*pinout_acl_vec_by_sw_if_index)[sw_if_index]); + vec_add1 (acl_vec, acl_list_index); } else { - hash_acl_unapply (am, sw_if_index, is_input, acl_list_index); - rv = - acl_interface_del_inout_acl (sw_if_index, is_input, acl_list_index); + if (sw_if_index > vec_len (*pinout_acl_vec_by_sw_if_index)) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto done; + } + + u32 index = vec_search ((*pinout_acl_vec_by_sw_if_index)[sw_if_index], + acl_list_index); + + if (~0 == index) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto done; + } + + acl_vec = vec_dup ((*pinout_acl_vec_by_sw_if_index)[sw_if_index]); + vec_del1 (acl_vec, index); } + + rv = acl_interface_set_inout_acl_list (am, sw_if_index, is_input, acl_vec, + &may_clear_sessions); +done: + vec_free (acl_vec); + clib_mem_set_heap (oldheap); return rv; } @@ -2331,9 +2385,7 @@ static void rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; else { - acl_interface_reset_inout_acls (sw_if_index, 0); - acl_interface_reset_inout_acls (sw_if_index, 1); - + int may_clear_sessions = 1; for (i = 0; i < mp->count; i++) { if (acl_is_not_defined (am, ntohl (mp->acls[i]))) @@ -2344,12 +2396,26 @@ static void } if (0 == rv) { + void *oldheap = acl_set_heap (am); + + u32 *in_acl_vec = 0; + u32 *out_acl_vec = 0; for (i = 0; i < mp->count; i++) - { - acl_interface_add_del_inout_acl (sw_if_index, 1, - (i < mp->n_input), - ntohl (mp->acls[i])); - } + if (i < mp->n_input) + vec_add1 (in_acl_vec, clib_net_to_host_u32 (mp->acls[i])); + else + vec_add1 (out_acl_vec, clib_net_to_host_u32 (mp->acls[i])); + + rv = + acl_interface_set_inout_acl_list (am, sw_if_index, 0, out_acl_vec, + &may_clear_sessions); + rv = rv + || acl_interface_set_inout_acl_list (am, sw_if_index, 1, + in_acl_vec, + &may_clear_sessions); + vec_free (in_acl_vec); + vec_free (out_acl_vec); + clib_mem_set_heap (oldheap); } } @@ -2469,6 +2535,8 @@ send_acl_interface_list_details (acl_main_t * am, vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); + clib_mem_set_heap (oldheap); + n_input = vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]); n_output = vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]); count = n_input + n_output; @@ -2495,7 +2563,6 @@ send_acl_interface_list_details (acl_main_t * am, mp->acls[n_input + i] = htonl (am->output_acl_vec_by_sw_if_index[sw_if_index][i]); } - clib_mem_set_heap (oldheap); vl_api_send_msg (reg, (u8 *) mp); } @@ -3046,13 +3113,14 @@ acl_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) } if (0 == is_add) { + int may_clear_sessions = 1; vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index, ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX, sw_if_index); /* also unapply any ACLs in case the users did not do so. */ macip_acl_interface_del_acl (am, sw_if_index); - acl_interface_reset_inout_acls (sw_if_index, 0); - acl_interface_reset_inout_acls (sw_if_index, 1); + acl_interface_reset_inout_acls (sw_if_index, 0, &may_clear_sessions); + acl_interface_reset_inout_acls (sw_if_index, 1, &may_clear_sessions); } return 0; } @@ -3090,6 +3158,21 @@ acl_set_aclplugin_fn (vlib_main_t * vm, am->l4_match_nonfirst_fragment = (val != 0); goto done; } + if (unformat (input, "event-trace")) + { + if (!unformat (input, "%u", &val)) + { + error = clib_error_return (0, + "expecting trace level, got `%U`", + format_unformat_error, input); + goto done; + } + else + { + am->trace_acl = val; + goto done; + } + } if (unformat (input, "heap")) { if (unformat (input, "main")) @@ -3103,9 +3186,9 @@ acl_set_aclplugin_fn (vlib_main_t * vm, else if (unformat (input, "hash")) { if (unformat (input, "validate %u", &val)) - acl_plugin_hash_acl_set_validate_heap (am, val); + acl_plugin_hash_acl_set_validate_heap (val); else if (unformat (input, "trace %u", &val)) - acl_plugin_hash_acl_set_trace_heap (am, val); + acl_plugin_hash_acl_set_trace_heap (val); goto done; } goto done; @@ -3345,50 +3428,6 @@ acl_show_aclplugin_macip_interface_fn (vlib_main_t * vm, return error; } -#define PRINT_AND_RESET(vm, out0) do { vlib_cli_output(vm, "%v", out0); vec_reset_length(out0); } while(0) -static void -acl_print_acl (vlib_main_t * vm, acl_main_t * am, int acl_index) -{ - acl_rule_t *r; - u8 *out0 = format (0, "acl-index %u count %u tag {%s}\n", acl_index, - am->acls[acl_index].count, am->acls[acl_index].tag); - int j; - PRINT_AND_RESET (vm, out0); - for (j = 0; j < am->acls[acl_index].count; j++) - { - r = &am->acls[acl_index].rules[j]; - out0 = format (out0, " %4d: %s ", j, r->is_ipv6 ? "ipv6" : "ipv4"); - out0 = format_acl_action (out0, r->is_permit); - out0 = format (out0, " src %U/%d", format_ip46_address, &r->src, - r->is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, - r->src_prefixlen); - out0 = - format (out0, " dst %U/%d", format_ip46_address, &r->dst, - r->is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, r->dst_prefixlen); - out0 = format (out0, " proto %d", r->proto); - out0 = format (out0, " sport %d", r->src_port_or_type_first); - if (r->src_port_or_type_first != r->src_port_or_type_last) - { - out0 = format (out0, "-%d", r->src_port_or_type_last); - } - out0 = format (out0, " dport %d", r->dst_port_or_code_first); - if (r->dst_port_or_code_first != r->dst_port_or_code_last) - { - out0 = format (out0, "-%d", r->dst_port_or_code_last); - } - if (r->tcp_flags_mask || r->tcp_flags_value) - { - out0 = - format (out0, " tcpflags %d mask %d", r->tcp_flags_value, - r->tcp_flags_mask); - } - out0 = format (out0, "\n"); - PRINT_AND_RESET (vm, out0); - } -} - -#undef PRINT_AND_RESET - static void acl_plugin_show_acl (acl_main_t * am, u32 acl_index) { @@ -3420,6 +3459,11 @@ acl_plugin_show_acl (acl_main_t * am, u32 acl_index) format_vec32, am->output_sw_if_index_vec_by_acl[i], "%d"); } + if (i < vec_len (am->lc_index_vec_by_acl)) + { + vlib_cli_output (vm, " used in lookup context index: %U\n", + format_vec32, am->lc_index_vec_by_acl[i], "%d"); + } } } @@ -3437,8 +3481,38 @@ acl_show_aclplugin_acl_fn (vlib_main_t * vm, return error; } +static clib_error_t * +acl_show_aclplugin_lookup_context_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + + u32 lc_index = ~0; + (void) unformat (input, "index %u", &lc_index); + + acl_plugin_show_lookup_context (lc_index); + return error; +} + +static clib_error_t * +acl_show_aclplugin_lookup_user_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + clib_error_t *error = 0; + + u32 lc_index = ~0; + (void) unformat (input, "index %u", &lc_index); + + acl_plugin_show_lookup_user (lc_index); + return error; +} + + static void -acl_plugin_show_interface (acl_main_t * am, u32 sw_if_index, int show_acl) +acl_plugin_show_interface (acl_main_t * am, u32 sw_if_index, int show_acl, + int detail) { vlib_main_t *vm = am->vlib_main; u32 swi; @@ -3496,6 +3570,16 @@ acl_plugin_show_interface (acl_main_t * am, u32 sw_if_index, int show_acl) vlib_cli_output (vm, "\n"); } } + if (detail && (swi < vec_len (am->input_lc_index_by_sw_if_index))) + { + vlib_cli_output (vm, " input lookup context index: %d", + am->input_lc_index_by_sw_if_index[swi]); + } + if (detail && (swi < vec_len (am->output_lc_index_by_sw_if_index))) + { + vlib_cli_output (vm, " output lookup context index: %d", + am->output_lc_index_by_sw_if_index[swi]); + } } } @@ -3531,8 +3615,9 @@ acl_show_aclplugin_interface_fn (vlib_main_t * vm, u32 sw_if_index = ~0; (void) unformat (input, "sw_if_index %u", &sw_if_index); int show_acl = unformat (input, "acl"); + int detail = unformat (input, "detail"); - acl_plugin_show_interface (am, sw_if_index, show_acl); + acl_plugin_show_interface (am, sw_if_index, show_acl, detail); return error; } @@ -3721,148 +3806,12 @@ acl_show_aclplugin_sessions_fn (vlib_main_t * vm, return error; } -static void -acl_plugin_show_tables_mask_type (acl_main_t * am) -{ - vlib_main_t *vm = am->vlib_main; - ace_mask_type_entry_t *mte; - - vlib_cli_output (vm, "Mask-type entries:"); - /* *INDENT-OFF* */ - pool_foreach(mte, am->ace_mask_type_pool, - ({ - vlib_cli_output(vm, " %3d: %016llx %016llx %016llx %016llx %016llx %016llx refcount %d", - mte - am->ace_mask_type_pool, - mte->mask.kv.key[0], mte->mask.kv.key[1], mte->mask.kv.key[2], - mte->mask.kv.key[3], mte->mask.kv.key[4], mte->mask.kv.value, mte->refcount); - })); - /* *INDENT-ON* */ -} - -static void -acl_plugin_show_tables_acl_hash_info (acl_main_t * am, u32 acl_index) -{ - vlib_main_t *vm = am->vlib_main; - u32 i, j; - u64 *m; - vlib_cli_output (vm, "Mask-ready ACL representations\n"); - for (i = 0; i < vec_len (am->hash_acl_infos); i++) - { - if ((acl_index != ~0) && (acl_index != i)) - { - continue; - } - hash_acl_info_t *ha = &am->hash_acl_infos[i]; - vlib_cli_output (vm, "acl-index %u bitmask-ready layout\n", i); - vlib_cli_output (vm, " applied inbound on sw_if_index list: %U\n", - format_vec32, ha->inbound_sw_if_index_list, "%d"); - vlib_cli_output (vm, " applied outbound on sw_if_index list: %U\n", - format_vec32, ha->outbound_sw_if_index_list, "%d"); - vlib_cli_output (vm, " mask type index bitmap: %U\n", - format_bitmap_hex, ha->mask_type_index_bitmap); - for (j = 0; j < vec_len (ha->rules); j++) - { - hash_ace_info_t *pa = &ha->rules[j]; - m = (u64 *) & pa->match; - vlib_cli_output (vm, - " %4d: %016llx %016llx %016llx %016llx %016llx %016llx mask index %d acl %d rule %d action %d src/dst portrange not ^2: %d,%d\n", - j, m[0], m[1], m[2], m[3], m[4], m[5], - pa->mask_type_index, pa->acl_index, pa->ace_index, - pa->action, pa->src_portrange_not_powerof2, - pa->dst_portrange_not_powerof2); - } - } -} - -static void -acl_plugin_print_pae (vlib_main_t * vm, int j, applied_hash_ace_entry_t * pae) -{ - vlib_cli_output (vm, - " %4d: acl %d rule %d action %d bitmask-ready rule %d next %d prev %d tail %d hitcount %lld", - j, pae->acl_index, pae->ace_index, pae->action, - pae->hash_ace_info_index, pae->next_applied_entry_index, - pae->prev_applied_entry_index, - pae->tail_applied_entry_index, pae->hitcount); -} - -static void -acl_plugin_show_tables_applied_info (acl_main_t * am, u32 sw_if_index) -{ - vlib_main_t *vm = am->vlib_main; - u32 swi, j; - vlib_cli_output (vm, "Applied lookup entries for interfaces"); - - for (swi = 0; - (swi < vec_len (am->input_applied_hash_acl_info_by_sw_if_index)) - || (swi < vec_len (am->output_applied_hash_acl_info_by_sw_if_index)) - || (swi < vec_len (am->input_hash_entry_vec_by_sw_if_index)) - || (swi < vec_len (am->output_hash_entry_vec_by_sw_if_index)); swi++) - { - if ((sw_if_index != ~0) && (sw_if_index != swi)) - { - continue; - } - vlib_cli_output (vm, "sw_if_index %d:", swi); - if (swi < vec_len (am->input_applied_hash_acl_info_by_sw_if_index)) - { - applied_hash_acl_info_t *pal = - &am->input_applied_hash_acl_info_by_sw_if_index[swi]; - vlib_cli_output (vm, " input lookup mask_type_index_bitmap: %U", - format_bitmap_hex, pal->mask_type_index_bitmap); - vlib_cli_output (vm, " input applied acls: %U", format_vec32, - pal->applied_acls, "%d"); - } - if (swi < vec_len (am->input_hash_entry_vec_by_sw_if_index)) - { - vlib_cli_output (vm, " input lookup applied entries:"); - for (j = 0; - j < vec_len (am->input_hash_entry_vec_by_sw_if_index[swi]); - j++) - { - acl_plugin_print_pae (vm, j, - &am->input_hash_entry_vec_by_sw_if_index - [swi][j]); - } - } - - if (swi < vec_len (am->output_applied_hash_acl_info_by_sw_if_index)) - { - applied_hash_acl_info_t *pal = - &am->output_applied_hash_acl_info_by_sw_if_index[swi]; - vlib_cli_output (vm, " output lookup mask_type_index_bitmap: %U", - format_bitmap_hex, pal->mask_type_index_bitmap); - vlib_cli_output (vm, " output applied acls: %U", format_vec32, - pal->applied_acls, "%d"); - } - if (swi < vec_len (am->output_hash_entry_vec_by_sw_if_index)) - { - vlib_cli_output (vm, " output lookup applied entries:"); - for (j = 0; - j < vec_len (am->output_hash_entry_vec_by_sw_if_index[swi]); - j++) - { - acl_plugin_print_pae (vm, j, - &am->output_hash_entry_vec_by_sw_if_index - [swi][j]); - } - } - } -} - -static void -acl_plugin_show_tables_bihash (acl_main_t * am, u32 show_bihash_verbose) -{ - vlib_main_t *vm = am->vlib_main; - show_hash_acl_hash (vm, am, show_bihash_verbose); -} - static clib_error_t * acl_show_aclplugin_tables_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { clib_error_t *error = 0; - acl_main_t *am = &acl_main; u32 acl_index = ~0; u32 sw_if_index = ~0; @@ -3905,13 +3854,13 @@ acl_show_aclplugin_tables_fn (vlib_main_t * vm, show_bihash = 1; } if (show_mask_type) - acl_plugin_show_tables_mask_type (am); + acl_plugin_show_tables_mask_type (); if (show_acl_hash_info) - acl_plugin_show_tables_acl_hash_info (am, acl_index); + acl_plugin_show_tables_acl_hash_info (acl_index); if (show_applied_info) - acl_plugin_show_tables_applied_info (am, sw_if_index); + acl_plugin_show_tables_applied_info (sw_if_index); if (show_bihash) - acl_plugin_show_tables_bihash (am, show_bihash_verbose); + acl_plugin_show_tables_bihash (show_bihash_verbose); return error; } @@ -3940,6 +3889,18 @@ VLIB_CLI_COMMAND (aclplugin_show_acl_command, static) = { .function = acl_show_aclplugin_acl_fn, }; +VLIB_CLI_COMMAND (aclplugin_show_lookup_context_command, static) = { + .path = "show acl-plugin lookup context", + .short_help = "show acl-plugin lookup context [index N]", + .function = acl_show_aclplugin_lookup_context_fn, +}; + +VLIB_CLI_COMMAND (aclplugin_show_lookup_user_command, static) = { + .path = "show acl-plugin lookup user", + .short_help = "show acl-plugin lookup user [index N]", + .function = acl_show_aclplugin_lookup_user_fn, +}; + VLIB_CLI_COMMAND (aclplugin_show_decode_5tuple_command, static) = { .path = "show acl-plugin decode 5tuple", .short_help = "show acl-plugin decode 5tuple XXXX XXXX XXXX XXXX XXXX XXXX", @@ -4113,6 +4074,10 @@ acl_init (vlib_main_t * vm) /* use the new fancy hash-based matching */ am->use_hash_acl_matching = 1; + am->interface_acl_user_id = + acl_plugin_register_user_module ("interface ACL", "sw_if_index", + "is_input"); + return error; } diff --git a/src/plugins/acl/acl.h b/src/plugins/acl/acl.h index 555358c4f66..9d66b7f1721 100644 --- a/src/plugins/acl/acl.h +++ b/src/plugins/acl/acl.h @@ -29,6 +29,7 @@ #include "fa_node.h" #include "hash_lookup_types.h" +#include "lookup_context.h" #define ACL_PLUGIN_VERSION_MAJOR 1 #define ACL_PLUGIN_VERSION_MINOR 3 @@ -140,6 +141,11 @@ typedef struct { /* API message ID base */ u16 msg_id_base; + /* The pool of users of ACL lookup contexts */ + acl_lookup_context_user_t *acl_users; + /* The pool of ACL lookup contexts */ + acl_lookup_context_t *acl_lookup_contexts; + acl_list_t *acls; /* Pool of ACLs */ hash_acl_info_t *hash_acl_infos; /* corresponding hash matching housekeeping info */ clib_bihash_48_8_t acl_lookup_hash; /* ACL lookup hash table. */ @@ -150,10 +156,20 @@ typedef struct { void *hash_lookup_mheap; u32 hash_lookup_mheap_size; int acl_lookup_hash_initialized; +/* applied_hash_ace_entry_t **input_hash_entry_vec_by_sw_if_index; applied_hash_ace_entry_t **output_hash_entry_vec_by_sw_if_index; applied_hash_acl_info_t *input_applied_hash_acl_info_by_sw_if_index; applied_hash_acl_info_t *output_applied_hash_acl_info_by_sw_if_index; +*/ + applied_hash_ace_entry_t **hash_entry_vec_by_lc_index; + applied_hash_acl_info_t *applied_hash_acl_info_by_lc_index; + + /* Corresponding lookup context indices for in/out lookups per sw_if_index */ + u32 *input_lc_index_by_sw_if_index; + u32 *output_lc_index_by_sw_if_index; + /* context user id for interface ACLs */ + u32 interface_acl_user_id; macip_acl_list_t *macip_acls; /* Pool of MAC-IP ACLs */ @@ -165,6 +181,13 @@ typedef struct { u32 **input_sw_if_index_vec_by_acl; u32 **output_sw_if_index_vec_by_acl; + /* bitmaps 1=sw_if_index has in/out ACL processing enabled */ + uword *in_acl_on_sw_if_index; + uword *out_acl_on_sw_if_index; + + /* lookup contexts where a given ACL is used */ + u32 **lc_index_vec_by_acl; + /* Total count of interface+direction pairs enabled */ u32 fa_total_enabled_count; @@ -239,6 +262,7 @@ typedef struct { u64 fa_conn_table_max_entries; int trace_sessions; + int trace_acl; /* * If the cleaner has to delete more than this number diff --git a/src/plugins/acl/acl_lookup_context.md b/src/plugins/acl/acl_lookup_context.md new file mode 100644 index 00000000000..c049aae5f19 --- /dev/null +++ b/src/plugins/acl/acl_lookup_context.md @@ -0,0 +1,113 @@ +Lookup contexts aka "ACL as a service" {#acl_lookup_context} +====================================== + +The initial implementation of the ACL plugin had tightly tied the policy (L3-L4) ACLs +to ingress/egress processing on an interface. + +However, some uses outside of pure traffic control have appeared, for example, +ACL-based forwarding, etc. Also, improved algorithms of the ACL lookup +could benefit of the more abstract representation, not coupled to the interfaces. + +This describes a way to accomodate these use cases by generalizing the ACL +lookups into "ACL lookup contexts", not tied to specific interfaces, usable +by other portions of the code by utilizing the exports.h header file, +which provides the necessary interface. + + +Why "lookup contexts" and not "match me an ACL#" ? +================================================ + +The first reason is the logical grouping of multiple ACLs. + +The interface matching code currently allows for matching multiple ACLs +in a 'first-match' fashion. Some other use cases also fall into a similar +pattern: they attemt to match a sequence of ACLs, and the first matched ACL +determines what the outcome is, e.g. where to forward traffic. Thus, +a match never happens on an ACL in isolation, but always on a group of +ACLs. + +The second reason is potential optimizations in matching. + +A naive match on series of ACLs each represented as a vector of ACEs +does not care about the API level - it could be "match one ACL", or +"match the set of ACLs" - there will be just a simple loop iterating over +the ACLs to match, returning the first match. Be it in the ACL code or +in the user code. + +However, for more involved lookup methods, providing a more high-level +interface of matching over the entire group of ACLs allows for future +improvements in the algorithms, delivered at once to all the users +of the API. + +What is a "lookup context" ? +============================ + +An ACL lookup context is an entity that groups the set of ACL#s +together for the purposes of a first-match lookup, and may store +additional internal information needed to optimize the lookups +for that particular vector of ACLs. + +Using ACL contexts in your code +=============================== + +In order to use the ACL lookup contexts, you need to include +plugins/acl/exports.h into your code. This header includes +all the necessary dependencies required, as well as +the actual "meat" include file containing the necessary +definitions - plugins/acl/public_inlines.h + +As you probably will invoke this code from another plugin, +the non-inline function calls are implemented via function pointers, +which you need to initialize by calling acl_plugin_exports_init(), which, +if everything succeeds, returns 0 - else it will return clib_error_t with +more information about what went wrong. + +When you have initialized the symbols, you also need to register yourself +as a user of the ACL lookups - this allows to track the ACL lookup context +ownership, as well as make the debug show outputs more user friendly. + +To do that, call acl_plugin_register_user_module(caller_module_string, val1_label, val2_label) - +and record the returned value. This will bethe first parameter that you pass to create a new +lookup context. The passed strings must be static, and are used as descriptions for the ACL +contexts themselves, as well as labels for up to two user-supplied u32 labels, used to +differentiate the lookup contexts for the debugging purposes. + +Creating a new context is done by calling acl_plugin_get_lookup_context_index(user_id, val1, val2). +The first argument is your "user" ID obtained in a registration call earlier, the other two +arguments are u32s with semantics that you designate. They are used purely for debugging purposes +in the "show acl lookup context" command. + +To set the vector of ACL numbers to be looked up within the context, use the function +acl_plugin_set_acl_vec_for_context(lc_index, acl_list). The first parameter specifies the context +that you have created, the second parameter is a vector of u32s, each u32 being the index of the ACL +which we should be looking up within this context. The comand is idempotent, i.e. +it unapplies the previously applied list of ACLs, and then sets the new list of ACLs. + +Subsequent ACL updates for the already applied ACLs will cause the re-application +on an as-needed basis. Note, that the ACL application is potentially a relatively costly operation, +so it is only expected that these changes will be done in the control plane, NOT in the datapath. + +The matching within the context is done using two functions - acl_plugin_fill_5tuple() and +acl_plugin_match_5tuple() and their corresponding inline versions, named acl_plugin_fill_5tuple_inline() +and acl_plugin_match_5tuple_inline(). The inline and non-inline versions have the equivalent functionality, +in that the non-inline version calls the inline version. These two variants are provided +for debugging/maintenance reasons. + +When you no longer need a particular context, you can return the allocated resources by calling +acl_plugin_put_lookup_context_index() to mark it as free. The lookup structured associated with +the vector of ACLs set for the lookup are cleaned up automatically. However, the ACLs themselves +are not deleted and are available for subsequent reuse by other lookup contexts if needed. + +Debug CLIs +========== + +To see the state of the ACL lookup contexts, you can issue "show acl-plugin lookup user" to see +all of the users which registered for the usage of the ACL plugin lookup contexts, +and "show acl-plugin lookup context" to show the actual contexts created. You will notice +that the latter command uses the values supplied during the module registration in order to +make the output more friendly. + +The "show acl-plugin acl" and "show acl-plugin interface" commands have also acquired the +notion of lookup context, but there it is used from the client perspective, since +with this change the interface ACL lookup itself is a user of ACL lookup contexts. + diff --git a/src/plugins/acl/elog_acl_trace.h b/src/plugins/acl/elog_acl_trace.h new file mode 100644 index 00000000000..0c4f68f7b0f --- /dev/null +++ b/src/plugins/acl/elog_acl_trace.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef _ELOG_ACL_TRACE_H_ +#define _ELOG_ACL_TRACE_H_ + + +/* use like: elog_acl_cond_trace_X1(am, (x < 0), "foobar: %d", "i4", int32_value); */ + +#define elog_acl_cond_trace_X1(am, trace_cond, acl_elog_trace_format_label, acl_elog_trace_format_args, acl_elog_val1) \ +do { \ + if (trace_cond) { \ + CLIB_UNUSED(struct { u8 available_space[18 - sizeof(acl_elog_val1)]; } *static_check); \ + u16 thread_index = os_get_thread_index (); \ + vlib_worker_thread_t * w = vlib_worker_threads + thread_index; \ + ELOG_TYPE_DECLARE (e) = \ + { \ + .format = "(%02d) " acl_elog_trace_format_label, \ + .format_args = "i2" acl_elog_trace_format_args, \ + }; \ + CLIB_PACKED(struct \ + { \ + u16 thread; \ + typeof(acl_elog_val1) val1; \ + }) *ed; \ + ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track); \ + ed->thread = thread_index; \ + ed->val1 = acl_elog_val1; \ + } \ +} while (0) + + +/* use like: elog_acl_cond_trace_X2(am, (x<0), "foobar: %d some u64: %lu", "i4i8", int32_value, int64_value); */ + +#define elog_acl_cond_trace_X2(am, trace_cond, acl_elog_trace_format_label, acl_elog_trace_format_args, \ + acl_elog_val1, acl_elog_val2) \ +do { \ + if (trace_cond) { \ + CLIB_UNUSED(struct { u8 available_space[18 - sizeof(acl_elog_val1) - sizeof(acl_elog_val2)]; } *static_check); \ + u16 thread_index = os_get_thread_index (); \ + vlib_worker_thread_t * w = vlib_worker_threads + thread_index; \ + ELOG_TYPE_DECLARE (e) = \ + { \ + .format = "(%02d) " acl_elog_trace_format_label, \ + .format_args = "i2" acl_elog_trace_format_args, \ + }; \ + CLIB_PACKED(struct \ + { \ + u16 thread; \ + typeof(acl_elog_val1) val1; \ + typeof(acl_elog_val2) val2; \ + }) *ed; \ + ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track); \ + ed->thread = thread_index; \ + ed->val1 = acl_elog_val1; \ + ed->val2 = acl_elog_val2; \ + } \ +} while (0) + + +/* use like: elog_acl_cond_trace_X3(am, (x<0), "foobar: %d some u64 %lu baz: %d", "i4i8i4", int32_value, u64_value, int_value); */ + +#define elog_acl_cond_trace_X3(am, trace_cond, acl_elog_trace_format_label, acl_elog_trace_format_args, acl_elog_val1, \ + acl_elog_val2, acl_elog_val3) \ +do { \ + if (trace_cond) { \ + CLIB_UNUSED(struct { u8 available_space[18 - sizeof(acl_elog_val1) - sizeof(acl_elog_val2) \ + - sizeof(acl_elog_val3)]; } *static_check); \ + u16 thread_index = os_get_thread_index (); \ + vlib_worker_thread_t * w = vlib_worker_threads + thread_index; \ + ELOG_TYPE_DECLARE (e) = \ + { \ + .format = "(%02d) " acl_elog_trace_format_label, \ + .format_args = "i2" acl_elog_trace_format_args, \ + }; \ + CLIB_PACKED(struct \ + { \ + u16 thread; \ + typeof(acl_elog_val1) val1; \ + typeof(acl_elog_val2) val2; \ + typeof(acl_elog_val3) val3; \ + }) *ed; \ + ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track); \ + ed->thread = thread_index; \ + ed->val1 = acl_elog_val1; \ + ed->val2 = acl_elog_val2; \ + ed->val3 = acl_elog_val3; \ + } \ +} while (0) + + +/* use like: elog_acl_cond_trace_X4(am, (x<0), "foobar: %d some int %d baz: %d bar: %d", "i4i4i4i4", int32_value, int32_value2, int_value, int_value); */ + +#define elog_acl_cond_trace_X4(am, trace_cond, acl_elog_trace_format_label, acl_elog_trace_format_args, acl_elog_val1, \ + acl_elog_val2, acl_elog_val3, acl_elog_val4) \ +do { \ + if (trace_cond) { \ + CLIB_UNUSED(struct { u8 available_space[18 - sizeof(acl_elog_val1) - sizeof(acl_elog_val2) \ + - sizeof(acl_elog_val3) -sizeof(acl_elog_val4)]; } *static_check); \ + u16 thread_index = os_get_thread_index (); \ + vlib_worker_thread_t * w = vlib_worker_threads + thread_index; \ + ELOG_TYPE_DECLARE (e) = \ + { \ + .format = "(%02d) " acl_elog_trace_format_label, \ + .format_args = "i2" acl_elog_trace_format_args, \ + }; \ + CLIB_PACKED(struct \ + { \ + u16 thread; \ + typeof(acl_elog_val1) val1; \ + typeof(acl_elog_val2) val2; \ + typeof(acl_elog_val3) val3; \ + typeof(acl_elog_val4) val4; \ + }) *ed; \ + ed = ELOG_TRACK_DATA (&vlib_global_main.elog_main, e, w->elog_track); \ + ed->thread = thread_index; \ + ed->val1 = acl_elog_val1; \ + ed->val2 = acl_elog_val2; \ + ed->val3 = acl_elog_val3; \ + ed->val4 = acl_elog_val4; \ + } \ +} while (0) + + +#endif diff --git a/src/plugins/acl/exports.h b/src/plugins/acl/exports.h new file mode 100644 index 00000000000..d904ad3bbae --- /dev/null +++ b/src/plugins/acl/exports.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 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. + */ +#ifndef included_acl_exports_h +#define included_acl_exports_h + +/* + * This file contains the declarations for external consumption, + * along with the necessary dependent includes. + */ + +#define ACL_PLUGIN_EXTERNAL_EXPORTS + +#include <vlib/unix/plugin.h> + +#include <plugins/acl/acl.h> +#include <plugins/acl/fa_node.h> +#include <plugins/acl/public_inlines.h> + +#endif /* included_acl_exports_h */ diff --git a/src/plugins/acl/fa_node.c b/src/plugins/acl/fa_node.c index c8e3d2d3a3e..d29576a4bce 100644 --- a/src/plugins/acl/fa_node.c +++ b/src/plugins/acl/fa_node.c @@ -19,20 +19,25 @@ #include <vnet/vnet.h> #include <vnet/pg/pg.h> #include <vppinfra/error.h> + + #include <acl/acl.h> -#include <vppinfra/bihash_40_8.h> +#include <vnet/ip/icmp46_packet.h> + +#include <plugins/acl/fa_node.h> +#include <plugins/acl/acl.h> +#include <plugins/acl/lookup_context.h> +#include <plugins/acl/public_inlines.h> +#include <vppinfra/bihash_40_8.h> #include <vppinfra/bihash_template.h> #include <vppinfra/bihash_template.c> -#include <vnet/ip/icmp46_packet.h> - -#include "fa_node.h" -#include "hash_lookup.h" typedef struct { u32 next_index; u32 sw_if_index; + u32 lc_index; u32 match_acl_in_index; u32 match_rule_index; u64 packet_info[6]; @@ -76,10 +81,9 @@ format_fa_5tuple (u8 * s, va_list * args) { fa_5tuple_t *p5t = va_arg (*args, fa_5tuple_t *); - return format(s, "%s sw_if_index %d (lsb16 %d) l3 %s%s %U -> %U" + return format(s, "lc_index %d (lsb16 of sw_if_index %d) l3 %s%s %U -> %U" " l4 proto %d l4_valid %d port %d -> %d tcp flags (%s) %02x rsvd %x", - p5t->pkt.is_input ? "input" : "output", - p5t->pkt.sw_if_index, p5t->l4.lsb_of_sw_if_index, p5t->pkt.is_ip6 ? "ip6" : "ip4", + p5t->pkt.lc_index, p5t->l4.lsb_of_sw_if_index, p5t->pkt.is_ip6 ? "ip6" : "ip4", p5t->pkt.is_nonfirst_fragment ? " non-initial fragment" : "", format_ip46_address, &p5t->addr[0], p5t->pkt.is_ip6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, format_ip46_address, &p5t->addr[1], p5t->pkt.is_ip6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4, @@ -106,9 +110,9 @@ format_acl_fa_trace (u8 * s, va_list * args) s = format (s, - "acl-plugin: sw_if_index %d, next index %d, action: %d, match: acl %d rule %d trace_bits %08x\n" + "acl-plugin: lc_index: %d, sw_if_index %d, next index %d, action: %d, match: acl %d rule %d trace_bits %08x\n" " pkt info %016llx %016llx %016llx %016llx %016llx %016llx", - t->sw_if_index, t->next_index, t->action, t->match_acl_in_index, + t->lc_index, t->sw_if_index, t->next_index, t->action, t->match_acl_in_index, t->match_rule_index, t->trace_bitmap, t->packet_info[0], t->packet_info[1], t->packet_info[2], t->packet_info[3], t->packet_info[4], t->packet_info[5]); @@ -144,420 +148,6 @@ static char *acl_fa_error_strings[] = { }; /* *INDENT-ON* */ -static void * -get_ptr_to_offset (vlib_buffer_t * b0, int offset) -{ - u8 *p = vlib_buffer_get_current (b0) + offset; - return p; -} - - -static int -fa_acl_match_addr (ip46_address_t * addr1, ip46_address_t * addr2, - int prefixlen, int is_ip6) -{ - if (prefixlen == 0) - { - /* match any always succeeds */ - return 1; - } - if (is_ip6) - { - if (memcmp (addr1, addr2, prefixlen / 8)) - { - /* If the starting full bytes do not match, no point in bittwidling the thumbs further */ - return 0; - } - if (prefixlen % 8) - { - u8 b1 = *((u8 *) addr1 + 1 + prefixlen / 8); - u8 b2 = *((u8 *) addr2 + 1 + prefixlen / 8); - u8 mask0 = (0xff - ((1 << (8 - (prefixlen % 8))) - 1)); - return (b1 & mask0) == b2; - } - else - { - /* The prefix fits into integer number of bytes, so nothing left to do */ - return 1; - } - } - else - { - uint32_t a1 = ntohl (addr1->ip4.as_u32); - uint32_t a2 = ntohl (addr2->ip4.as_u32); - uint32_t mask0 = 0xffffffff - ((1 << (32 - prefixlen)) - 1); - return (a1 & mask0) == a2; - } -} - -static int -fa_acl_match_port (u16 port, u16 port_first, u16 port_last, int is_ip6) -{ - return ((port >= port_first) && (port <= port_last)); -} - -int -single_acl_match_5tuple (acl_main_t * am, u32 acl_index, fa_5tuple_t * pkt_5tuple, - int is_ip6, u8 * r_action, u32 * r_acl_match_p, - u32 * r_rule_match_p, u32 * trace_bitmap) -{ - int i; - acl_list_t *a; - acl_rule_t *r; - - if (pool_is_free_index (am->acls, acl_index)) - { - if (r_acl_match_p) - *r_acl_match_p = acl_index; - if (r_rule_match_p) - *r_rule_match_p = -1; - /* the ACL does not exist but is used for policy. Block traffic. */ - return 0; - } - a = am->acls + acl_index; - for (i = 0; i < a->count; i++) - { - r = a->rules + i; - if (is_ip6 != r->is_ipv6) - { - continue; - } - if (!fa_acl_match_addr - (&pkt_5tuple->addr[1], &r->dst, r->dst_prefixlen, is_ip6)) - continue; - -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d pkt dst addr %U match rule addr %U/%d", - acl_index, i, format_ip46_address, &pkt_5tuple->addr[1], - r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, format_ip46_address, - &r->dst, r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, - r->dst_prefixlen); -#endif - - if (!fa_acl_match_addr - (&pkt_5tuple->addr[0], &r->src, r->src_prefixlen, is_ip6)) - continue; - -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d pkt src addr %U match rule addr %U/%d", - acl_index, i, format_ip46_address, &pkt_5tuple->addr[0], - r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, format_ip46_address, - &r->src, r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, - r->src_prefixlen); - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d trying to match pkt proto %d with rule %d", - acl_index, i, pkt_5tuple->l4.proto, r->proto); -#endif - if (r->proto) - { - if (pkt_5tuple->l4.proto != r->proto) - continue; - - if (PREDICT_FALSE (pkt_5tuple->pkt.is_nonfirst_fragment && - am->l4_match_nonfirst_fragment)) - { - /* non-initial fragment with frag match configured - match this rule */ - *trace_bitmap |= 0x80000000; - *r_action = r->is_permit; - if (r_acl_match_p) - *r_acl_match_p = acl_index; - if (r_rule_match_p) - *r_rule_match_p = i; - return 1; - } - - /* A sanity check just to ensure we are about to match the ports extracted from the packet */ - if (PREDICT_FALSE (!pkt_5tuple->pkt.l4_valid)) - continue; - -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d pkt proto %d match rule %d", - acl_index, i, pkt_5tuple->l4.proto, r->proto); -#endif - - if (!fa_acl_match_port - (pkt_5tuple->l4.port[0], r->src_port_or_type_first, - r->src_port_or_type_last, is_ip6)) - continue; - -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d pkt sport %d match rule [%d..%d]", - acl_index, i, pkt_5tuple->l4.port[0], r->src_port_or_type_first, - r->src_port_or_type_last); -#endif - - if (!fa_acl_match_port - (pkt_5tuple->l4.port[1], r->dst_port_or_code_first, - r->dst_port_or_code_last, is_ip6)) - continue; - -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning - ("ACL_FA_NODE_DBG acl %d rule %d pkt dport %d match rule [%d..%d]", - acl_index, i, pkt_5tuple->l4.port[1], r->dst_port_or_code_first, - r->dst_port_or_code_last); -#endif - if (pkt_5tuple->pkt.tcp_flags_valid - && ((pkt_5tuple->pkt.tcp_flags & r->tcp_flags_mask) != - r->tcp_flags_value)) - continue; - } - /* everything matches! */ -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_DBG acl %d rule %d FULL-MATCH, action %d", - acl_index, i, r->is_permit); -#endif - *r_action = r->is_permit; - if (r_acl_match_p) - *r_acl_match_p = acl_index; - if (r_rule_match_p) - *r_rule_match_p = i; - return 1; - } - return 0; -} - -static u8 -linear_multi_acl_match_5tuple (u32 sw_if_index, fa_5tuple_t * pkt_5tuple, int is_l2, - int is_ip6, int is_input, u32 * acl_match_p, - u32 * rule_match_p, u32 * trace_bitmap) -{ - acl_main_t *am = &acl_main; - int i; - u32 *acl_vector; - u8 action = 0; - - if (is_input) - { - vec_validate (am->input_acl_vec_by_sw_if_index, sw_if_index); - acl_vector = am->input_acl_vec_by_sw_if_index[sw_if_index]; - } - else - { - vec_validate (am->output_acl_vec_by_sw_if_index, sw_if_index); - acl_vector = am->output_acl_vec_by_sw_if_index[sw_if_index]; - } - for (i = 0; i < vec_len (acl_vector); i++) - { -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_DBG: Trying to match ACL: %d", - acl_vector[i]); -#endif - if (single_acl_match_5tuple - (am, acl_vector[i], pkt_5tuple, is_ip6, &action, - acl_match_p, rule_match_p, trace_bitmap)) - { - return action; - } - } - if (vec_len (acl_vector) > 0) - { - /* If there are ACLs and none matched, deny by default */ - return 0; - } -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_DBG: No ACL on sw_if_index %d", sw_if_index); -#endif - /* Deny by default. If there are no ACLs defined we should not be here. */ - return 0; -} - -static u8 -multi_acl_match_5tuple (u32 sw_if_index, fa_5tuple_t * pkt_5tuple, int is_l2, - int is_ip6, int is_input, u32 * acl_match_p, - u32 * rule_match_p, u32 * trace_bitmap) -{ - acl_main_t *am = &acl_main; - if (am->use_hash_acl_matching) { - return hash_multi_acl_match_5tuple(sw_if_index, pkt_5tuple, is_l2, is_ip6, - is_input, acl_match_p, rule_match_p, trace_bitmap); - } else { - return linear_multi_acl_match_5tuple(sw_if_index, pkt_5tuple, is_l2, is_ip6, - is_input, acl_match_p, rule_match_p, trace_bitmap); - } -} - -static int -offset_within_packet (vlib_buffer_t * b0, int offset) -{ - /* For the purposes of this code, "within" means we have at least 8 bytes after it */ - return (offset <= (b0->current_length - 8)); -} - -static void -acl_fill_5tuple (acl_main_t * am, vlib_buffer_t * b0, int is_ip6, - int is_input, int is_l2_path, fa_5tuple_t * p5tuple_pkt) -{ - int l3_offset; - int l4_offset; - u16 ports[2]; - u16 proto; - - if (is_l2_path) - { - l3_offset = ethernet_buffer_header_size(b0); - } - else - { - if (is_input) - l3_offset = 0; - else - l3_offset = vnet_buffer(b0)->ip.save_rewrite_length; - } - - /* key[0..3] contains src/dst address and is cleared/set below */ - /* Remainder of the key and per-packet non-key data */ - p5tuple_pkt->kv.key[4] = 0; - p5tuple_pkt->kv.value = 0; - - if (is_ip6) - { - clib_memcpy (&p5tuple_pkt->addr, - get_ptr_to_offset (b0, - offsetof (ip6_header_t, - src_address) + l3_offset), - sizeof (p5tuple_pkt->addr)); - proto = - *(u8 *) get_ptr_to_offset (b0, - offsetof (ip6_header_t, - protocol) + l3_offset); - l4_offset = l3_offset + sizeof (ip6_header_t); -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_DBG: proto: %d, l4_offset: %d", proto, - l4_offset); -#endif - /* IP6 EH handling is here, increment l4_offset if needs to, update the proto */ - int need_skip_eh = clib_bitmap_get (am->fa_ipv6_known_eh_bitmap, proto); - if (PREDICT_FALSE (need_skip_eh)) - { - while (need_skip_eh && offset_within_packet (b0, l4_offset)) - { - /* Fragment header needs special handling */ - if (PREDICT_FALSE(ACL_EH_FRAGMENT == proto)) - { - proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); - u16 frag_offset; - clib_memcpy (&frag_offset, get_ptr_to_offset (b0, 2 + l4_offset), sizeof(frag_offset)); - frag_offset = ntohs(frag_offset) >> 3; - if (frag_offset) - { - p5tuple_pkt->pkt.is_nonfirst_fragment = 1; - /* invalidate L4 offset so we don't try to find L4 info */ - l4_offset += b0->current_length; - } - else - { - /* First fragment: skip the frag header and move on. */ - l4_offset += 8; - } - } - else - { - u8 nwords = *(u8 *) get_ptr_to_offset (b0, 1 + l4_offset); - proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); - l4_offset += 8 * (1 + (u16) nwords); - } -#ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_DBG: new proto: %d, new offset: %d", - proto, l4_offset); -#endif - need_skip_eh = - clib_bitmap_get (am->fa_ipv6_known_eh_bitmap, proto); - } - } - } - else - { - p5tuple_pkt->kv.key[0] = 0; - p5tuple_pkt->kv.key[1] = 0; - p5tuple_pkt->kv.key[2] = 0; - p5tuple_pkt->kv.key[3] = 0; - clib_memcpy (&p5tuple_pkt->addr[0].ip4, - get_ptr_to_offset (b0, - offsetof (ip4_header_t, - src_address) + l3_offset), - sizeof (p5tuple_pkt->addr[0].ip4)); - clib_memcpy (&p5tuple_pkt->addr[1].ip4, - get_ptr_to_offset (b0, - offsetof (ip4_header_t, - dst_address) + l3_offset), - sizeof (p5tuple_pkt->addr[1].ip4)); - proto = - *(u8 *) get_ptr_to_offset (b0, - offsetof (ip4_header_t, - protocol) + l3_offset); - l4_offset = l3_offset + sizeof (ip4_header_t); - u16 flags_and_fragment_offset; - clib_memcpy (&flags_and_fragment_offset, - get_ptr_to_offset (b0, - offsetof (ip4_header_t, - flags_and_fragment_offset)) + l3_offset, - sizeof(flags_and_fragment_offset)); - flags_and_fragment_offset = ntohs (flags_and_fragment_offset); - - /* non-initial fragments have non-zero offset */ - if ((PREDICT_FALSE(0xfff & flags_and_fragment_offset))) - { - p5tuple_pkt->pkt.is_nonfirst_fragment = 1; - /* invalidate L4 offset so we don't try to find L4 info */ - l4_offset += b0->current_length; - } - - } - p5tuple_pkt->l4.proto = proto; - if (PREDICT_TRUE (offset_within_packet (b0, l4_offset))) - { - p5tuple_pkt->pkt.l4_valid = 1; - if (icmp_protos[is_ip6] == proto) - { - /* type */ - p5tuple_pkt->l4.port[0] = - *(u8 *) get_ptr_to_offset (b0, - l4_offset + offsetof (icmp46_header_t, - type)); - /* code */ - p5tuple_pkt->l4.port[1] = - *(u8 *) get_ptr_to_offset (b0, - l4_offset + offsetof (icmp46_header_t, - code)); - } - else if ((IPPROTO_TCP == proto) || (IPPROTO_UDP == proto)) - { - clib_memcpy (&ports, - get_ptr_to_offset (b0, - l4_offset + offsetof (tcp_header_t, - src_port)), - sizeof (ports)); - p5tuple_pkt->l4.port[0] = ntohs (ports[0]); - p5tuple_pkt->l4.port[1] = ntohs (ports[1]); - - p5tuple_pkt->pkt.tcp_flags = - *(u8 *) get_ptr_to_offset (b0, - l4_offset + offsetof (tcp_header_t, - flags)); - p5tuple_pkt->pkt.tcp_flags_valid = (proto == IPPROTO_TCP); - } - /* - * FIXME: rather than the above conditional, here could - * be a nice generic mechanism to extract two L4 values: - * - * have a per-protocol array of 4 elements like this: - * u8 offset; to take the byte from, off L4 header - * u8 mask; to mask it with, before storing - * - * this way we can describe UDP, TCP and ICMP[46] semantics, - * and add a sort of FPM-type behavior for other protocols. - * - * Of course, is it faster ? and is it needed ? - * - */ - } -} - static int acl_fa_ifc_has_sessions (acl_main_t * am, int sw_if_index0) { @@ -774,6 +364,10 @@ acl_fa_conn_list_add_session (acl_main_t * am, fa_full_session_id_t sess_id, u64 ASSERT(prev_sess->thread_index == sess->thread_index); } pw->fa_conn_list_tail[list_id] = sess_id.session_index; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning("FA-SESSION-DEBUG: add session id %d on thread %d sw_if_index %d", sess_id.session_index, thread_index, sess->sw_if_index); +#endif pw->serviced_sw_if_index_bitmap = clib_bitmap_set(pw->serviced_sw_if_index_bitmap, sess->sw_if_index, 1); if (~0 == pw->fa_conn_list_head[list_id]) { @@ -942,8 +536,8 @@ acl_fa_check_idle_sessions(acl_main_t *am, u16 thread_index, u64 now) if ((now < sess_timeout_time) && (0 == clib_bitmap_get(pw->pending_clear_sw_if_index_bitmap, sw_if_index))) { #ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_CLEAN: Restarting timer for session %d", - (int) session_index); + clib_warning ("ACL_FA_NODE_CLEAN: Restarting timer for session %d, sw_if_index %d", + (int) fsid.session_index, sess->sw_if_index); #endif /* There was activity on the session, so the idle timeout has not passed. Enqueue for another time period. */ @@ -954,8 +548,8 @@ acl_fa_check_idle_sessions(acl_main_t *am, u16 thread_index, u64 now) else { #ifdef FA_NODE_VERBOSE_DEBUG - clib_warning ("ACL_FA_NODE_CLEAN: Deleting session %d", - (int) session_index); + clib_warning ("ACL_FA_NODE_CLEAN: Deleting session %d, sw_if_index %d", + (int) fsid.session_index, sess->sw_if_index); #endif acl_fa_delete_session (am, sw_if_index, fsid); pw->cnt_deleted_sessions++; @@ -1044,9 +638,7 @@ static int acl_fa_find_session (acl_main_t * am, u32 sw_if_index0, fa_5tuple_t * p5tuple, clib_bihash_kv_40_8_t * pvalue_sess) { - return (BV (clib_bihash_search) - (&am->fa_sessions_hash, &p5tuple->kv, - pvalue_sess) == 0); + return (clib_bihash_search_40_8 (&am->fa_sessions_hash, &p5tuple->kv, pvalue_sess) == 0); } @@ -1090,8 +682,10 @@ acl_fa_node_fn (vlib_main_t * vm, u32 next0 = 0; u8 action = 0; u32 sw_if_index0; + u32 lc_index0; int acl_check_needed = 1; u32 match_acl_in_index = ~0; + u32 match_acl_pos = ~0; u32 match_rule_index = ~0; u8 error0 = 0; u32 valid_new_sess; @@ -1111,26 +705,30 @@ acl_fa_node_fn (vlib_main_t * vm, else sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + if (is_input) + lc_index0 = am->input_lc_index_by_sw_if_index[sw_if_index0]; + else + lc_index0 = am->output_lc_index_by_sw_if_index[sw_if_index0]; /* * Extract the L3/L4 matching info into a 5-tuple structure, * then create a session key whose layout is independent on forward or reverse * direction of the packet. */ - acl_fill_5tuple (am, b0, is_ip6, is_input, is_l2_path, &fa_5tuple); + acl_plugin_fill_5tuple_inline (lc_index0, b0, is_ip6, is_input, is_l2_path, (fa_5tuple_opaque_t *)&fa_5tuple); fa_5tuple.l4.lsb_of_sw_if_index = sw_if_index0 & 0xffff; + fa_5tuple.pkt.lc_index = lc_index0; valid_new_sess = acl_make_5tuple_session_key (am, is_input, is_ip6, sw_if_index0, &fa_5tuple, &kv_sess); - fa_5tuple.pkt.sw_if_index = sw_if_index0; fa_5tuple.pkt.is_ip6 = is_ip6; - fa_5tuple.pkt.is_input = is_input; + // XXDEL fa_5tuple.pkt.is_input = is_input; fa_5tuple.pkt.mask_type_index_lsb = ~0; #ifdef FA_NODE_VERBOSE_DEBUG clib_warning - ("ACL_FA_NODE_DBG: session 5-tuple %016llx %016llx %016llx %016llx %016llx : %016llx", + ("ACL_FA_NODE_DBG: session 5-tuple %016llx %016llx %016llx %016llx %016llx %016llx", kv_sess.kv.key[0], kv_sess.kv.key[1], kv_sess.kv.key[2], kv_sess.kv.key[3], kv_sess.kv.key[4], kv_sess.kv.value); clib_warning - ("ACL_FA_NODE_DBG: packet 5-tuple %016llx %016llx %016llx %016llx %016llx : %016llx", + ("ACL_FA_NODE_DBG: packet 5-tuple %016llx %016llx %016llx %016llx %016llx %016llx", fa_5tuple.kv.key[0], fa_5tuple.kv.key[1], fa_5tuple.kv.key[2], fa_5tuple.kv.key[3], fa_5tuple.kv.key[4], fa_5tuple.kv.value); #endif @@ -1189,9 +787,9 @@ acl_fa_node_fn (vlib_main_t * vm, if (acl_check_needed) { - action = - multi_acl_match_5tuple (sw_if_index0, &fa_5tuple, is_l2_path, - is_ip6, is_input, &match_acl_in_index, + action = 0; /* deny by default */ + acl_plugin_match_5tuple_inline (lc_index0, (fa_5tuple_opaque_t *)&fa_5tuple, + is_ip6, &action, &match_acl_pos, &match_acl_in_index, &match_rule_index, &trace_bitmap); error0 = action; if (1 == action) @@ -1236,12 +834,16 @@ acl_fa_node_fn (vlib_main_t * vm, else vnet_feature_next (sw_if_index0, &next0, b0); } +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning("ACL_FA_NODE_DBG: sw_if_index %d lc_index %d action %d acl_index %d rule_index %d", sw_if_index0, lc_index0, action, match_acl_in_index, match_rule_index); +#endif if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { acl_fa_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; + t->lc_index = lc_index0; t->next_index = next0; t->match_acl_in_index = match_acl_in_index; t->match_rule_index = match_rule_index; diff --git a/src/plugins/acl/fa_node.h b/src/plugins/acl/fa_node.h index 7ef558e17ea..dc4f87f0eb1 100644 --- a/src/plugins/acl/fa_node.h +++ b/src/plugins/acl/fa_node.h @@ -4,6 +4,8 @@ #include <stddef.h> #include <vppinfra/bihash_40_8.h> +// #define FA_NODE_VERBOSE_DEBUG 3 + #define TCP_FLAG_FIN 0x01 #define TCP_FLAG_SYN 0x02 #define TCP_FLAG_RST 0x04 @@ -22,15 +24,14 @@ typedef union { u64 as_u64; struct { - u32 sw_if_index; + u32 lc_index; u16 mask_type_index_lsb; u8 tcp_flags; u8 tcp_flags_valid:1; - u8 is_input:1; u8 l4_valid:1; u8 is_nonfirst_fragment:1; u8 is_ip6:1; - u8 flags_reserved:3; + u8 flags_reserved:4; }; } fa_packet_info_t; @@ -53,6 +54,10 @@ typedef union { clib_bihash_kv_40_8_t kv; } fa_5tuple_t; +typedef struct { + u8 opaque[sizeof(fa_5tuple_t)]; +} fa_5tuple_opaque_t; + typedef struct { fa_5tuple_t info; /* (5+1)*8 = 48 bytes */ diff --git a/src/plugins/acl/hash_lookup.c b/src/plugins/acl/hash_lookup.c index 2262402d52f..ad55054c3e3 100644 --- a/src/plugins/acl/hash_lookup.c +++ b/src/plugins/acl/hash_lookup.c @@ -33,126 +33,17 @@ #include "hash_lookup_private.h" -static inline applied_hash_ace_entry_t **get_applied_hash_aces(acl_main_t *am, int is_input, u32 sw_if_index) +always_inline applied_hash_ace_entry_t **get_applied_hash_aces(acl_main_t *am, u32 lc_index) { - applied_hash_ace_entry_t **applied_hash_aces = is_input ? vec_elt_at_index(am->input_hash_entry_vec_by_sw_if_index, sw_if_index) + applied_hash_ace_entry_t **applied_hash_aces = vec_elt_at_index(am->hash_entry_vec_by_lc_index, lc_index); + +/*is_input ? vec_elt_at_index(am->input_hash_entry_vec_by_sw_if_index, sw_if_index) : vec_elt_at_index(am->output_hash_entry_vec_by_sw_if_index, sw_if_index); +*/ return applied_hash_aces; } - -/* - * This returns true if there is indeed a match on the portranges. - * With all these levels of indirections, this is not going to be very fast, - * so, best use the individual ports or wildcard ports for performance. - */ -static int -match_portranges(acl_main_t *am, fa_5tuple_t *match, u32 index) -{ - - applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, match->pkt.is_input, match->pkt.sw_if_index); - applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), index); - - acl_rule_t *r = &(am->acls[pae->acl_index].rules[pae->ace_index]); - DBG("PORTMATCH: %d <= %d <= %d && %d <= %d <= %d ?", - r->src_port_or_type_first, match->l4.port[0], r->src_port_or_type_last, - r->dst_port_or_code_first, match->l4.port[1], r->dst_port_or_code_last); - - return ( ((r->src_port_or_type_first <= match->l4.port[0]) && r->src_port_or_type_last >= match->l4.port[0]) && - ((r->dst_port_or_code_first <= match->l4.port[1]) && r->dst_port_or_code_last >= match->l4.port[1]) ); -} - -static u32 -multi_acl_match_get_applied_ace_index(acl_main_t *am, fa_5tuple_t *match) -{ - clib_bihash_kv_48_8_t kv; - clib_bihash_kv_48_8_t result; - fa_5tuple_t *kv_key = (fa_5tuple_t *)kv.key; - hash_acl_lookup_value_t *result_val = (hash_acl_lookup_value_t *)&result.value; - u64 *pmatch = (u64 *)match; - u64 *pmask; - u64 *pkey; - int mask_type_index; - u32 curr_match_index = ~0; - - u32 sw_if_index = match->pkt.sw_if_index; - u8 is_input = match->pkt.is_input; - applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index); - applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index : - &am->output_applied_hash_acl_info_by_sw_if_index; - - DBG("TRYING TO MATCH: %016llx %016llx %016llx %016llx %016llx %016llx", - pmatch[0], pmatch[1], pmatch[2], pmatch[3], pmatch[4], pmatch[5]); - - for(mask_type_index=0; mask_type_index < pool_len(am->ace_mask_type_pool); mask_type_index++) { - if (!clib_bitmap_get(vec_elt_at_index((*applied_hash_acls), sw_if_index)->mask_type_index_bitmap, mask_type_index)) { - /* This bit is not set. Avoid trying to match */ - continue; - } - ace_mask_type_entry_t *mte = vec_elt_at_index(am->ace_mask_type_pool, mask_type_index); - pmatch = (u64 *)match; - pmask = (u64 *)&mte->mask; - pkey = (u64 *)kv.key; - /* - * unrolling the below loop results in a noticeable performance increase. - int i; - for(i=0; i<6; i++) { - kv.key[i] = pmatch[i] & pmask[i]; - } - */ - - *pkey++ = *pmatch++ & *pmask++; - *pkey++ = *pmatch++ & *pmask++; - *pkey++ = *pmatch++ & *pmask++; - *pkey++ = *pmatch++ & *pmask++; - *pkey++ = *pmatch++ & *pmask++; - *pkey++ = *pmatch++ & *pmask++; - - kv_key->pkt.mask_type_index_lsb = mask_type_index; - DBG(" KEY %3d: %016llx %016llx %016llx %016llx %016llx %016llx", mask_type_index, - kv.key[0], kv.key[1], kv.key[2], kv.key[3], kv.key[4], kv.key[5]); - int res = BV (clib_bihash_search) (&am->acl_lookup_hash, &kv, &result); - if (res == 0) { - DBG("ACL-MATCH! result_val: %016llx", result_val->as_u64); - if (result_val->applied_entry_index < curr_match_index) { - if (PREDICT_FALSE(result_val->need_portrange_check)) { - /* - * This is going to be slow, since we can have multiple superset - * entries for narrow-ish portranges, e.g.: - * 0..42 100..400, 230..60000, - * so we need to walk linearly and check if they match. - */ - - u32 curr_index = result_val->applied_entry_index; - while ((curr_index != ~0) && !match_portranges(am, match, curr_index)) { - /* while no match and there are more entries, walk... */ - applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces),curr_index); - DBG("entry %d did not portmatch, advancing to %d", curr_index, pae->next_applied_entry_index); - curr_index = pae->next_applied_entry_index; - } - if (curr_index < curr_match_index) { - DBG("The index %d is the new candidate in portrange matches.", curr_index); - curr_match_index = curr_index; - } else { - DBG("Curr portmatch index %d is too big vs. current matched one %d", curr_index, curr_match_index); - } - } else { - /* The usual path is here. Found an entry in front of the current candiate - so it's a new one */ - DBG("This match is the new candidate"); - curr_match_index = result_val->applied_entry_index; - if (!result_val->shadowed) { - /* new result is known to not be shadowed, so no point to look up further */ - break; - } - } - } - } - } - DBG("MATCH-RESULT: %d", curr_match_index); - return curr_match_index; -} - static void hashtable_add_del(acl_main_t *am, clib_bihash_kv_48_8_t *kv, int is_add) { @@ -165,7 +56,7 @@ hashtable_add_del(acl_main_t *am, clib_bihash_kv_48_8_t *kv, int is_add) static void fill_applied_hash_ace_kv(acl_main_t *am, applied_hash_ace_entry_t **applied_hash_aces, - u32 sw_if_index, u8 is_input, + u32 lc_index, u32 new_index, clib_bihash_kv_48_8_t *kv) { fa_5tuple_t *kv_key = (fa_5tuple_t *)kv->key; @@ -175,8 +66,7 @@ fill_applied_hash_ace_kv(acl_main_t *am, memcpy(kv_key, &(vec_elt_at_index(ha->rules, pae->hash_ace_info_index)->match), sizeof(*kv_key)); /* initialize the sw_if_index and direction */ - kv_key->pkt.sw_if_index = sw_if_index; - kv_key->pkt.is_input = is_input; + kv_key->pkt.lc_index = lc_index; kv_val->as_u64 = 0; kv_val->applied_entry_index = new_index; kv_val->need_portrange_check = vec_elt_at_index(ha->rules, pae->hash_ace_info_index)->src_portrange_not_powerof2 || @@ -187,13 +77,13 @@ fill_applied_hash_ace_kv(acl_main_t *am, static void add_del_hashtable_entry(acl_main_t *am, - u32 sw_if_index, u8 is_input, + u32 lc_index, applied_hash_ace_entry_t **applied_hash_aces, u32 index, int is_add) { clib_bihash_kv_48_8_t kv; - fill_applied_hash_ace_kv(am, applied_hash_aces, sw_if_index, is_input, index, &kv); + fill_applied_hash_ace_kv(am, applied_hash_aces, lc_index, index, &kv); hashtable_add_del(am, &kv, is_add); } @@ -201,16 +91,16 @@ add_del_hashtable_entry(acl_main_t *am, static void activate_applied_ace_hash_entry(acl_main_t *am, - u32 sw_if_index, u8 is_input, + u32 lc_index, applied_hash_ace_entry_t **applied_hash_aces, u32 new_index) { clib_bihash_kv_48_8_t kv; ASSERT(new_index != ~0); applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), new_index); - DBG("activate_applied_ace_hash_entry sw_if_index %d is_input %d new_index %d", sw_if_index, is_input, new_index); + DBG("activate_applied_ace_hash_entry lc_index %d new_index %d", lc_index, new_index); - fill_applied_hash_ace_kv(am, applied_hash_aces, sw_if_index, is_input, new_index, &kv); + fill_applied_hash_ace_kv(am, applied_hash_aces, lc_index, new_index, &kv); DBG("APPLY ADD KY: %016llx %016llx %016llx %016llx %016llx %016llx", kv.key[0], kv.key[1], kv.key[2], @@ -272,8 +162,9 @@ hash_acl_set_heap(acl_main_t *am) } void -acl_plugin_hash_acl_set_validate_heap(acl_main_t *am, int on) +acl_plugin_hash_acl_set_validate_heap(int on) { + acl_main_t *am = &acl_main; clib_mem_set_heap(hash_acl_set_heap(am)); mheap_t *h = mheap_header (am->hash_lookup_mheap); if (on) { @@ -287,8 +178,9 @@ acl_plugin_hash_acl_set_validate_heap(acl_main_t *am, int on) } void -acl_plugin_hash_acl_set_trace_heap(acl_main_t *am, int on) +acl_plugin_hash_acl_set_trace_heap(int on) { + acl_main_t *am = &acl_main; clib_mem_set_heap(hash_acl_set_heap(am)); mheap_t *h = mheap_header (am->hash_lookup_mheap); if (on) { @@ -299,11 +191,11 @@ acl_plugin_hash_acl_set_trace_heap(acl_main_t *am, int on) } void -hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) +hash_acl_apply(acl_main_t *am, u32 lc_index, int acl_index, u32 acl_position) { int i; - DBG0("HASH ACL apply: sw_if_index %d is_input %d acl %d", sw_if_index, is_input, acl_index); + DBG0("HASH ACL apply: lc_index %d acl %d", lc_index, acl_index); if (!am->acl_lookup_hash_initialized) { BV (clib_bihash_init) (&am->acl_lookup_hash, "ACL plugin rule lookup bihash", am->hash_lookup_hash_buckets, am->hash_lookup_hash_memory); @@ -311,42 +203,36 @@ hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) } void *oldheap = hash_acl_set_heap(am); - if (is_input) { - vec_validate(am->input_hash_entry_vec_by_sw_if_index, sw_if_index); - } else { - vec_validate(am->output_hash_entry_vec_by_sw_if_index, sw_if_index); - } + vec_validate(am->hash_entry_vec_by_lc_index, lc_index); vec_validate(am->hash_acl_infos, acl_index); - applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index); + applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, lc_index); hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index); - u32 **hash_acl_applied_sw_if_index = is_input ? &ha->inbound_sw_if_index_list - : &ha->outbound_sw_if_index_list; + u32 **hash_acl_applied_lc_index = &ha->lc_index_list; int base_offset = vec_len(*applied_hash_aces); /* Update the bitmap of the mask types with which the lookup - needs to happen for the ACLs applied to this sw_if_index */ - applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index : - &am->output_applied_hash_acl_info_by_sw_if_index; - vec_validate((*applied_hash_acls), sw_if_index); - applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index); + needs to happen for the ACLs applied to this lc_index */ + applied_hash_acl_info_t **applied_hash_acls = &am->applied_hash_acl_info_by_lc_index; + vec_validate((*applied_hash_acls), lc_index); + applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), lc_index); /* ensure the list of applied hash acls is initialized and add this acl# to it */ u32 index = vec_search(pal->applied_acls, acl_index); if (index != ~0) { - clib_warning("BUG: trying to apply twice acl_index %d on sw_if_index %d is_input %d", - acl_index, sw_if_index, is_input); + clib_warning("BUG: trying to apply twice acl_index %d on lc_index %d, according to lc", + acl_index, lc_index); goto done; } vec_add1(pal->applied_acls, acl_index); - u32 index2 = vec_search((*hash_acl_applied_sw_if_index), sw_if_index); + u32 index2 = vec_search((*hash_acl_applied_lc_index), lc_index); if (index2 != ~0) { - clib_warning("BUG: trying to apply twice acl_index %d on (sw_if_index %d) is_input %d", - acl_index, sw_if_index, is_input); + clib_warning("BUG: trying to apply twice acl_index %d on lc_index %d, according to hash h-acl info", + acl_index, lc_index); goto done; } - vec_add1((*hash_acl_applied_sw_if_index), sw_if_index); + vec_add1((*hash_acl_applied_lc_index), lc_index); pal->mask_type_index_bitmap = clib_bitmap_or(pal->mask_type_index_bitmap, ha->mask_type_index_bitmap); @@ -369,6 +255,7 @@ hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), new_index); pae->acl_index = acl_index; pae->ace_index = ha->rules[i].ace_index; + pae->acl_position = acl_position; pae->action = ha->rules[i].action; pae->hitcount = 0; pae->hash_ace_info_index = i; @@ -376,7 +263,7 @@ hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) pae->next_applied_entry_index = ~0; pae->prev_applied_entry_index = ~0; pae->tail_applied_entry_index = ~0; - activate_applied_ace_hash_entry(am, sw_if_index, is_input, applied_hash_aces, new_index); + activate_applied_ace_hash_entry(am, lc_index, applied_hash_aces, new_index); } applied_hash_entries_analyze(am, applied_hash_aces); done: @@ -403,7 +290,7 @@ find_head_applied_ace_index(applied_hash_ace_entry_t **applied_hash_aces, u32 cu static void move_applied_ace_hash_entry(acl_main_t *am, - u32 sw_if_index, u8 is_input, + u32 lc_index, applied_hash_ace_entry_t **applied_hash_aces, u32 old_index, u32 new_index) { @@ -421,7 +308,7 @@ move_applied_ace_hash_entry(acl_main_t *am, prev_pae->next_applied_entry_index = new_index; } else { /* first entry - so the hash points to it, update */ - add_del_hashtable_entry(am, sw_if_index, is_input, + add_del_hashtable_entry(am, lc_index, applied_hash_aces, new_index, 1); ASSERT(pae->tail_applied_entry_index != ~0); } @@ -448,12 +335,12 @@ move_applied_ace_hash_entry(acl_main_t *am, static void deactivate_applied_ace_hash_entry(acl_main_t *am, - u32 sw_if_index, u8 is_input, + u32 lc_index, applied_hash_ace_entry_t **applied_hash_aces, u32 old_index) { applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), old_index); - DBG("UNAPPLY DEACTIVATE: sw_if_index %d is_input %d, applied index %d", sw_if_index, is_input, old_index); + DBG("UNAPPLY DEACTIVATE: lc_index %d applied index %d", lc_index, old_index); if (pae->prev_applied_entry_index != ~0) { DBG("UNAPPLY = index %d has prev_applied_entry_index %d", old_index, pae->prev_applied_entry_index); @@ -483,11 +370,11 @@ deactivate_applied_ace_hash_entry(acl_main_t *am, DBG("Resetting the hash table entry from %d to %d, setting tail index to %d", old_index, pae->next_applied_entry_index, pae->tail_applied_entry_index); /* unlink from the next element */ next_pae->prev_applied_entry_index = ~0; - add_del_hashtable_entry(am, sw_if_index, is_input, + add_del_hashtable_entry(am, lc_index, applied_hash_aces, pae->next_applied_entry_index, 1); } else { /* no next entry, so just delete the entry in the hash table */ - add_del_hashtable_entry(am, sw_if_index, is_input, + add_del_hashtable_entry(am, lc_index, applied_hash_aces, old_index, 0); } } @@ -499,13 +386,15 @@ deactivate_applied_ace_hash_entry(acl_main_t *am, static void -hash_acl_build_applied_lookup_bitmap(acl_main_t *am, u32 sw_if_index, u8 is_input) +hash_acl_build_applied_lookup_bitmap(acl_main_t *am, u32 lc_index) { int i; uword *new_lookup_bitmap = 0; - applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index - : &am->output_applied_hash_acl_info_by_sw_if_index; - applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index); + + applied_hash_acl_info_t **applied_hash_acls = &am->applied_hash_acl_info_by_lc_index; + vec_validate((*applied_hash_acls), lc_index); + applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), lc_index); + for(i=0; i < vec_len(pal->applied_acls); i++) { u32 a_acl_index = *vec_elt_at_index((pal->applied_acls), i); hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, a_acl_index); @@ -520,37 +409,35 @@ hash_acl_build_applied_lookup_bitmap(acl_main_t *am, u32 sw_if_index, u8 is_inpu } void -hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) +hash_acl_unapply(acl_main_t *am, u32 lc_index, int acl_index) { int i; - DBG0("HASH ACL unapply: sw_if_index %d is_input %d acl %d", sw_if_index, is_input, acl_index); - applied_hash_acl_info_t **applied_hash_acls = is_input ? &am->input_applied_hash_acl_info_by_sw_if_index - : &am->output_applied_hash_acl_info_by_sw_if_index; - applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), sw_if_index); + DBG0("HASH ACL unapply: lc_index %d acl %d", lc_index, acl_index); + applied_hash_acl_info_t **applied_hash_acls = &am->applied_hash_acl_info_by_lc_index; + applied_hash_acl_info_t *pal = vec_elt_at_index((*applied_hash_acls), lc_index); hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index); - u32 **hash_acl_applied_sw_if_index = is_input ? &ha->inbound_sw_if_index_list - : &ha->outbound_sw_if_index_list; + u32 **hash_acl_applied_lc_index = &ha->lc_index_list; /* remove this acl# from the list of applied hash acls */ u32 index = vec_search(pal->applied_acls, acl_index); if (index == ~0) { - clib_warning("BUG: trying to unapply unapplied acl_index %d on sw_if_index %d is_input %d", - acl_index, sw_if_index, is_input); + clib_warning("BUG: trying to unapply unapplied acl_index %d on lc_index %d, according to lc", + acl_index, lc_index); return; } vec_del1(pal->applied_acls, index); - u32 index2 = vec_search((*hash_acl_applied_sw_if_index), sw_if_index); + u32 index2 = vec_search((*hash_acl_applied_lc_index), lc_index); if (index2 == ~0) { - clib_warning("BUG: trying to unapply twice acl_index %d on (sw_if_index %d) is_input %d", - acl_index, sw_if_index, is_input); + clib_warning("BUG: trying to unapply twice acl_index %d on lc_index %d, according to h-acl info", + acl_index, lc_index); return; } - vec_del1((*hash_acl_applied_sw_if_index), index2); + vec_del1((*hash_acl_applied_lc_index), index2); - applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index); + applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, lc_index); for(i=0; i < vec_len((*applied_hash_aces)); i++) { if (vec_elt_at_index(*applied_hash_aces,i)->acl_index == acl_index) { @@ -559,7 +446,7 @@ hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) } } if (vec_len((*applied_hash_aces)) <= i) { - DBG("Did not find applied ACL#%d at sw_if_index %d", acl_index, sw_if_index); + DBG("Did not find applied ACL#%d at lc_index %d", acl_index, lc_index); /* we went all the way without finding any entries. Probably a list was empty. */ return; } @@ -571,14 +458,14 @@ hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) DBG("base_offset: %d, tail_offset: %d, tail_len: %d", base_offset, tail_offset, tail_len); for(i=0; i < vec_len(ha->rules); i ++) { - deactivate_applied_ace_hash_entry(am, sw_if_index, is_input, + deactivate_applied_ace_hash_entry(am, lc_index, applied_hash_aces, base_offset + i); } for(i=0; i < tail_len; i ++) { /* move the entry at tail offset to base offset */ /* that is, from (tail_offset+i) -> (base_offset+i) */ - DBG("UNAPPLY MOVE: sw_if_index %d is_input %d, applied index %d ->", sw_if_index, is_input, tail_offset+i, base_offset + i); - move_applied_ace_hash_entry(am, sw_if_index, is_input, applied_hash_aces, tail_offset + i, base_offset + i); + DBG("UNAPPLY MOVE: lc_index %d, applied index %d -> %d", lc_index, tail_offset+i, base_offset + i); + move_applied_ace_hash_entry(am, lc_index, applied_hash_aces, tail_offset + i, base_offset + i); } /* trim the end of the vector */ _vec_len((*applied_hash_aces)) -= vec_len(ha->rules); @@ -586,7 +473,7 @@ hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) applied_hash_entries_analyze(am, applied_hash_aces); /* After deletion we might not need some of the mask-types anymore... */ - hash_acl_build_applied_lookup_bitmap(am, sw_if_index, is_input); + hash_acl_build_applied_lookup_bitmap(am, lc_index); clib_mem_set_heap (oldheap); } @@ -600,24 +487,26 @@ hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) */ void -hash_acl_reapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index) +hash_acl_reapply(acl_main_t *am, u32 lc_index, int acl_index) { - u32 **applied_acls = is_input ? vec_elt_at_index(am->input_acl_vec_by_sw_if_index, sw_if_index) - : vec_elt_at_index(am->output_acl_vec_by_sw_if_index, sw_if_index); + acl_lookup_context_t *acontext = pool_elt_at_index(am->acl_lookup_contexts, lc_index); + u32 **applied_acls = &acontext->acl_indices; int i; int start_index = vec_search((*applied_acls), acl_index); + + DBG0("Start index for acl %d in lc_index %d is %d", acl_index, lc_index, start_index); /* * This function is called after we find out the sw_if_index where ACL is applied. * If the by-sw_if_index vector does not have the ACL#, then it's a bug. */ ASSERT(start_index < vec_len(*applied_acls)); - /* unapply all the ACLs till the current one */ + /* unapply all the ACLs at the tail side, up to the current one */ for(i = vec_len(*applied_acls) - 1; i > start_index; i--) { - hash_acl_unapply(am, sw_if_index, is_input, *vec_elt_at_index(*applied_acls, i)); + hash_acl_unapply(am, lc_index, *vec_elt_at_index(*applied_acls, i)); } for(i = start_index; i < vec_len(*applied_acls); i++) { - hash_acl_apply(am, sw_if_index, is_input, *vec_elt_at_index(*applied_acls, i)); + hash_acl_apply(am, lc_index, *vec_elt_at_index(*applied_acls, i), i); } } @@ -667,9 +556,8 @@ make_mask_and_match_from_rule(fa_5tuple_t *mask, acl_rule_t *r, hash_ace_info_t memset(&hi->match, 0, sizeof(hi->match)); hi->action = r->is_permit; - /* we will need to be matching based on sw_if_index, direction, and mask_type_index when applied */ - mask->pkt.sw_if_index = ~0; - mask->pkt.is_input = 1; + /* we will need to be matching based on lc_index and mask_type_index when applied */ + mask->pkt.lc_index = ~0; /* we will assign the match of mask_type_index later when we find it*/ mask->pkt.mask_type_index_lsb = ~0; @@ -766,6 +654,15 @@ release_mask_type_index(acl_main_t *am, u32 mask_type_index) } } +int hash_acl_exists(acl_main_t *am, int acl_index) +{ + if (acl_index >= vec_len(am->hash_acl_infos)) + return 0; + + hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index); + return ha->hash_acl_exists; +} + void hash_acl_add(acl_main_t *am, int acl_index) { void *oldheap = hash_acl_set_heap(am); @@ -775,6 +672,7 @@ void hash_acl_add(acl_main_t *am, int acl_index) vec_validate(am->hash_acl_infos, acl_index); hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index); memset(ha, 0, sizeof(*ha)); + ha->hash_acl_exists = 1; /* walk the newly added ACL entries and ensure that for each of them there is a mask type, increment a reference count for that mask type */ @@ -808,16 +706,10 @@ void hash_acl_add(acl_main_t *am, int acl_index) * if an ACL is applied somewhere, fill the corresponding lookup data structures. * We need to take care if the ACL is not the last one in the vector of ACLs applied to the interface. */ - if (acl_index < vec_len(am->input_sw_if_index_vec_by_acl)) { - u32 *sw_if_index; - vec_foreach(sw_if_index, am->input_sw_if_index_vec_by_acl[acl_index]) { - hash_acl_reapply(am, *sw_if_index, 1, acl_index); - } - } - if (acl_index < vec_len(am->output_sw_if_index_vec_by_acl)) { - u32 *sw_if_index; - vec_foreach(sw_if_index, am->output_sw_if_index_vec_by_acl[acl_index]) { - hash_acl_reapply(am, *sw_if_index, 0, acl_index); + if (acl_index < vec_len(am->lc_index_vec_by_acl)) { + u32 *lc_index; + vec_foreach(lc_index, am->lc_index_vec_by_acl[acl_index]) { + hash_acl_reapply(am, *lc_index, acl_index); } } clib_mem_set_heap (oldheap); @@ -841,18 +733,14 @@ void hash_acl_delete(acl_main_t *am, int acl_index) * has to be handled. */ hash_acl_info_t *ha = vec_elt_at_index(am->hash_acl_infos, acl_index); - u32 *interface_list_copy = 0; + u32 *lc_list_copy = 0; { - u32 *sw_if_index; - interface_list_copy = vec_dup(ha->inbound_sw_if_index_list); - vec_foreach(sw_if_index, interface_list_copy) { - hash_acl_unapply(am, *sw_if_index, 1, acl_index); - } - vec_free(interface_list_copy); - interface_list_copy = vec_dup(ha->outbound_sw_if_index_list); - vec_foreach(sw_if_index, interface_list_copy) { - hash_acl_unapply(am, *sw_if_index, 0, acl_index); + u32 *lc_index; + lc_list_copy = vec_dup(ha->lc_index_list); + vec_foreach(lc_index, lc_list_copy) { + hash_acl_unapply(am, *lc_index, acl_index); } + vec_free(lc_list_copy); } /* walk the mask types for the ACL about-to-be-deleted, and decrease @@ -862,32 +750,153 @@ void hash_acl_delete(acl_main_t *am, int acl_index) release_mask_type_index(am, ha->rules[i].mask_type_index); } clib_bitmap_free(ha->mask_type_index_bitmap); + ha->hash_acl_exists = 0; vec_free(ha->rules); clib_mem_set_heap (oldheap); } -u8 -hash_multi_acl_match_5tuple (u32 sw_if_index, fa_5tuple_t * pkt_5tuple, int is_l2, - int is_ip6, int is_input, u32 * acl_match_p, - u32 * rule_match_p, u32 * trace_bitmap) + +void +show_hash_acl_hash (vlib_main_t * vm, acl_main_t *am, u32 verbose) +{ + vlib_cli_output(vm, "\nACL lookup hash table:\n%U\n", + BV (format_bihash), &am->acl_lookup_hash, verbose); +} + +void +acl_plugin_show_tables_mask_type (void) { acl_main_t *am = &acl_main; - applied_hash_ace_entry_t **applied_hash_aces = get_applied_hash_aces(am, is_input, sw_if_index); - u32 match_index = multi_acl_match_get_applied_ace_index(am, pkt_5tuple); - if (match_index < vec_len((*applied_hash_aces))) { - applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), match_index); - pae->hitcount++; - *acl_match_p = pae->acl_index; - *rule_match_p = pae->ace_index; - return pae->action; - } - return 0; + vlib_main_t *vm = am->vlib_main; + ace_mask_type_entry_t *mte; + + vlib_cli_output (vm, "Mask-type entries:"); + /* *INDENT-OFF* */ + pool_foreach(mte, am->ace_mask_type_pool, + ({ + vlib_cli_output(vm, " %3d: %016llx %016llx %016llx %016llx %016llx %016llx refcount %d", + mte - am->ace_mask_type_pool, + mte->mask.kv.key[0], mte->mask.kv.key[1], mte->mask.kv.key[2], + mte->mask.kv.key[3], mte->mask.kv.key[4], mte->mask.kv.value, mte->refcount); + })); + /* *INDENT-ON* */ } +void +acl_plugin_show_tables_acl_hash_info (u32 acl_index) +{ + acl_main_t *am = &acl_main; + vlib_main_t *vm = am->vlib_main; + u32 i, j; + u64 *m; + vlib_cli_output (vm, "Mask-ready ACL representations\n"); + for (i = 0; i < vec_len (am->hash_acl_infos); i++) + { + if ((acl_index != ~0) && (acl_index != i)) + { + continue; + } + hash_acl_info_t *ha = &am->hash_acl_infos[i]; + vlib_cli_output (vm, "acl-index %u bitmask-ready layout\n", i); + vlib_cli_output (vm, " applied lc_index list: %U\n", + format_vec32, ha->lc_index_list, "%d"); + vlib_cli_output (vm, " mask type index bitmap: %U\n", + format_bitmap_hex, ha->mask_type_index_bitmap); + for (j = 0; j < vec_len (ha->rules); j++) + { + hash_ace_info_t *pa = &ha->rules[j]; + m = (u64 *) & pa->match; + vlib_cli_output (vm, + " %4d: %016llx %016llx %016llx %016llx %016llx %016llx mask index %d acl %d rule %d action %d src/dst portrange not ^2: %d,%d\n", + j, m[0], m[1], m[2], m[3], m[4], m[5], + pa->mask_type_index, pa->acl_index, pa->ace_index, + pa->action, pa->src_portrange_not_powerof2, + pa->dst_portrange_not_powerof2); + } + } +} void -show_hash_acl_hash (vlib_main_t * vm, acl_main_t *am, u32 verbose) +acl_plugin_print_pae (vlib_main_t * vm, int j, applied_hash_ace_entry_t * pae) { - vlib_cli_output(vm, "\nACL lookup hash table:\n%U\n", - BV (format_bihash), &am->acl_lookup_hash, verbose); + vlib_cli_output (vm, + " %4d: acl %d rule %d action %d bitmask-ready rule %d next %d prev %d tail %d hitcount %lld", + j, pae->acl_index, pae->ace_index, pae->action, + pae->hash_ace_info_index, pae->next_applied_entry_index, + pae->prev_applied_entry_index, + pae->tail_applied_entry_index, pae->hitcount); } + +void +acl_plugin_show_tables_applied_info (u32 sw_if_index) +{ + acl_main_t *am = &acl_main; + vlib_main_t *vm = am->vlib_main; + u32 swi; //, j; + vlib_cli_output (vm, "Applied lookup entries for interfaces"); + + for (swi = 0; + (swi < vec_len (am->input_lc_index_by_sw_if_index)) + || (swi < vec_len (am->output_lc_index_by_sw_if_index)); swi++) + { + if ((sw_if_index != ~0) && (sw_if_index != swi)) + { + continue; + } +/* + vlib_cli_output (vm, "sw_if_index %d:", swi); + if (swi < vec_len (am->input_applied_hash_acl_info_by_sw_if_index)) + { + applied_hash_acl_info_t *pal = + &am->input_applied_hash_acl_info_by_sw_if_index[swi]; + vlib_cli_output (vm, " input lookup mask_type_index_bitmap: %U", + format_bitmap_hex, pal->mask_type_index_bitmap); + vlib_cli_output (vm, " input applied acls: %U", format_vec32, + pal->applied_acls, "%d"); + } + if (swi < vec_len (am->input_hash_entry_vec_by_sw_if_index)) + { + vlib_cli_output (vm, " input lookup applied entries:"); + for (j = 0; + j < vec_len (am->input_hash_entry_vec_by_sw_if_index[swi]); + j++) + { + acl_plugin_print_pae (vm, j, + &am->input_hash_entry_vec_by_sw_if_index + [swi][j]); + } + } + + if (swi < vec_len (am->output_applied_hash_acl_info_by_sw_if_index)) + { + applied_hash_acl_info_t *pal = + &am->output_applied_hash_acl_info_by_sw_if_index[swi]; + vlib_cli_output (vm, " output lookup mask_type_index_bitmap: %U", + format_bitmap_hex, pal->mask_type_index_bitmap); + vlib_cli_output (vm, " output applied acls: %U", format_vec32, + pal->applied_acls, "%d"); + } + if (swi < vec_len (am->output_hash_entry_vec_by_sw_if_index)) + { + vlib_cli_output (vm, " output lookup applied entries:"); + for (j = 0; + j < vec_len (am->output_hash_entry_vec_by_sw_if_index[swi]); + j++) + { + acl_plugin_print_pae (vm, j, + &am->output_hash_entry_vec_by_sw_if_index + [swi][j]); + } + } +*/ + } +} + +void +acl_plugin_show_tables_bihash (u32 show_bihash_verbose) +{ + acl_main_t *am = &acl_main; + vlib_main_t *vm = am->vlib_main; + show_hash_acl_hash (vm, am, show_bihash_verbose); +} + diff --git a/src/plugins/acl/hash_lookup.h b/src/plugins/acl/hash_lookup.h index 2d7058e80ee..e401d3cbc8b 100644 --- a/src/plugins/acl/hash_lookup.h +++ b/src/plugins/acl/hash_lookup.h @@ -19,6 +19,7 @@ #define _ACL_HASH_LOOKUP_H_ #include <stddef.h> +#include "lookup_context.h" #include "acl.h" /* @@ -26,11 +27,11 @@ * during the packet processing */ -void hash_acl_apply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index); +void hash_acl_apply(acl_main_t *am, u32 lc_index, int acl_index, u32 acl_position); -/* Remove the ACL from the packet processing lookups on a given interface */ +/* Remove the ACL from the packet processing in a given lookup context */ -void hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_index); +void hash_acl_unapply(acl_main_t *am, u32 lc_index, int acl_index); /* * Add an ACL or delete an ACL. ACL may already have been referenced elsewhere, @@ -40,25 +41,7 @@ void hash_acl_unapply(acl_main_t *am, u32 sw_if_index, u8 is_input, int acl_inde void hash_acl_add(acl_main_t *am, int acl_index); void hash_acl_delete(acl_main_t *am, int acl_index); -/* - * Do the work required to match a given 5-tuple from the packet, - * and return the action as well as populate the values pointed - * to by the *_match_p pointers and maybe trace_bitmap. - */ - -u8 -hash_multi_acl_match_5tuple (u32 sw_if_index, fa_5tuple_t * pkt_5tuple, int is_l2, - int is_ip6, int is_input, u32 * acl_match_p, - u32 * rule_match_p, u32 * trace_bitmap); - - -/* - * The debug function to show the contents of the ACL lookup hash - */ -void show_hash_acl_hash(vlib_main_t * vm, acl_main_t *am, u32 verbose); - -/* Debug functions to turn validate/trace on and off */ -void acl_plugin_hash_acl_set_validate_heap(acl_main_t *am, int on); -void acl_plugin_hash_acl_set_trace_heap(acl_main_t *am, int on); +/* return if there is already a filled-in hash acl info */ +int hash_acl_exists(acl_main_t *am, int acl_index); #endif diff --git a/src/plugins/acl/hash_lookup_types.h b/src/plugins/acl/hash_lookup_types.h index 1fa197ec978..1a20ebff8f8 100644 --- a/src/plugins/acl/hash_lookup_types.h +++ b/src/plugins/acl/hash_lookup_types.h @@ -38,10 +38,11 @@ typedef struct { typedef struct { /* The mask types present in this ACL */ uword *mask_type_index_bitmap; - /* hash ACL applied on these interfaces */ - u32 *inbound_sw_if_index_list; - u32 *outbound_sw_if_index_list; + /* hash ACL applied on these lookup contexts */ + u32 *lc_index_list; hash_ace_info_t *rules; + /* a boolean flag set when the hash acl info is initialized */ + int hash_acl_exists; } hash_acl_info_t; typedef struct { @@ -69,6 +70,10 @@ typedef struct { */ u64 hitcount; /* + * acl position in vector of ACLs within lookup context + */ + u32 acl_position; + /* * Action of this applied ACE */ u8 action; diff --git a/src/plugins/acl/lookup_context.c b/src/plugins/acl/lookup_context.c new file mode 100644 index 00000000000..a4c9647776a --- /dev/null +++ b/src/plugins/acl/lookup_context.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2018 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/acl/acl.h> +#include <plugins/acl/fa_node.h> +#include <plugins/acl/public_inlines.h> +#include <vlib/unix/plugin.h> +#include "hash_lookup.h" +#include "elog_acl_trace.h" + +/* check if a given ACL exists */ +u8 acl_plugin_acl_exists (u32 acl_index); + +static u32 get_acl_user_id(acl_main_t *am, char *user_module_name, char *val1_label, char *val2_label) +{ + acl_lookup_context_user_t *auser; + + pool_foreach (auser, am->acl_users, + ({ + if (0 == strcmp(auser->user_module_name, user_module_name)) { + return (auser - am->acl_users); + } + })); + + pool_get(am->acl_users, auser); + auser->user_module_name = user_module_name; + auser->val1_label = val1_label; + auser->val2_label = val2_label; + return (auser - am->acl_users); +} + +static int acl_user_id_valid(acl_main_t *am, u32 acl_user_id) +{ + + if (pool_is_free_index (am->acl_users, acl_user_id)) + return 0; + + return 1; +} + +static int acl_lc_index_valid(acl_main_t *am, u32 lc_index) +{ + + if (pool_is_free_index (am->acl_lookup_contexts, lc_index)) + return 0; + + return 1; +} + +/* + * If you are using ACL plugin, get this unique ID first, + * so you can identify yourself when creating the lookup contexts. + */ + +u32 acl_plugin_register_user_module (char *user_module_name, char *val1_label, char *val2_label) +{ + acl_main_t *am = &acl_main; + u32 user_id = get_acl_user_id(am, user_module_name, val1_label, val2_label); + return user_id; +} + +/* + * Allocate a new lookup context index. + * Supply the id assigned to your module during registration, + * and two values of your choice identifying instances + * of use within your module. They are useful for debugging. + * If >= 0 - context id. If < 0 - error code. + */ + +int acl_plugin_get_lookup_context_index (u32 acl_user_id, u32 val1, u32 val2) +{ + acl_main_t *am = &acl_main; + acl_lookup_context_t *acontext; + + if (!acl_user_id_valid(am, acl_user_id)) + return VNET_API_ERROR_INVALID_REGISTRATION; + + pool_get(am->acl_lookup_contexts, acontext); + acontext->acl_indices = 0; + acontext->context_user_id = acl_user_id; + acontext->user_val1 = val1; + acontext->user_val2 = val2; + + u32 new_context_id = acontext - am->acl_lookup_contexts; + vec_add1(am->acl_users[acl_user_id].lookup_contexts, new_context_id); + return new_context_id; +} + +static void +lock_acl(acl_main_t *am, u32 acl, u32 lc_index) +{ + vec_validate(am->lc_index_vec_by_acl, acl); + elog_acl_cond_trace_X2(am, (am->trace_acl), "lock acl %d in lc_index %d", "i4i4", acl, lc_index); + vec_add1(am->lc_index_vec_by_acl[acl], lc_index); +} + +static void +lock_acl_vec(u32 lc_index, u32 *acls) +{ + int i; + acl_main_t *am = &acl_main; + for(i=0; i<vec_len(acls); i++) { + lock_acl(am, acls[i], lc_index); + } +} + +static void +unlock_acl(acl_main_t *am, u32 acl, u32 lc_index) +{ + vec_validate(am->lc_index_vec_by_acl, acl); + elog_acl_cond_trace_X2(am, (am->trace_acl), "unlock acl %d in lc_index %d", "i4i4", acl, lc_index); + u32 index = vec_search(am->lc_index_vec_by_acl[acl], lc_index); + if (index != ~0) + vec_del1(am->lc_index_vec_by_acl[acl], index); + else + clib_warning("BUG: can not unlock acl %d lc_index %d", acl, lc_index); +} + +static void +unlock_acl_vec(u32 lc_index, u32 *acls) +{ + int i; + acl_main_t *am = &acl_main; + for(i=0; i<vec_len(acls); i++) + unlock_acl(am, acls[i], lc_index); +} + + +static void +apply_acl_vec(u32 lc_index, u32 *acls) +{ + int i; + acl_main_t *am = &acl_main; + + for(i=0; i<vec_len(acls); i++) + hash_acl_apply(am, lc_index, acls[i], i); +} + + +static void +unapply_acl_vec(u32 lc_index, u32 *acls) +{ + int i; + acl_main_t *am = &acl_main; + if (vec_len(acls) == 0) + return; + for(i=vec_len(acls); i > 0; i--) + hash_acl_unapply(am, lc_index, acls[i-1]); +} + +/* + * Release the lookup context index and destroy + * any asssociated data structures. + */ +void acl_plugin_put_lookup_context_index (u32 lc_index) +{ + acl_main_t *am = &acl_main; + elog_acl_cond_trace_X1(am, (am->trace_acl), "LOOKUP-CONTEXT: put-context lc_index %d", "i4", lc_index); + if (!acl_lc_index_valid(am, lc_index)) { + clib_warning("BUG: lc_index %d is not valid", lc_index); + return; + } + acl_lookup_context_t *acontext = pool_elt_at_index(am->acl_lookup_contexts, lc_index); + + u32 index = vec_search(am->acl_users[acontext->context_user_id].lookup_contexts, lc_index); + ASSERT(index != ~0); + + vec_del1(am->acl_users[acontext->context_user_id].lookup_contexts, index); + unapply_acl_vec(lc_index, acontext->acl_indices); + unlock_acl_vec(lc_index, acontext->acl_indices); + vec_free(acontext->acl_indices); + pool_put(am->acl_lookup_contexts, acontext); +} + +/* + * Prepare the sequential vector of ACL#s to lookup within a given context. + * Any existing list will be overwritten. acl_list is a vector. + */ +int acl_plugin_set_acl_vec_for_context (u32 lc_index, u32 *acl_list) +{ + acl_main_t *am = &acl_main; + acl_lookup_context_t *acontext; + if (am->trace_acl) { + u32 i; + elog_acl_cond_trace_X1(am, (1), "LOOKUP-CONTEXT: set-acl-list lc_index %d", "i4", lc_index); + for(i=0; i<vec_len(acl_list); i++) { + elog_acl_cond_trace_X2(am, (1), " acl-list[%d]: %d", "i4i4", i, acl_list[i]); + } + } + if (!acl_lc_index_valid(am, lc_index)) { + clib_warning("BUG: lc_index %d is not valid", lc_index); + return -1; + } + acontext = pool_elt_at_index(am->acl_lookup_contexts, lc_index); + u32 *old_acl_vector = acontext->acl_indices; + acontext->acl_indices = vec_dup(acl_list); + + unapply_acl_vec(lc_index, old_acl_vector); + unlock_acl_vec(lc_index, old_acl_vector); + lock_acl_vec(lc_index, acontext->acl_indices); + apply_acl_vec(lc_index, acontext->acl_indices); + + vec_free(old_acl_vector); + return 0; +} + + +void acl_plugin_lookup_context_notify_acl_change(u32 acl_num) +{ + acl_main_t *am = &acl_main; + if (acl_plugin_acl_exists(acl_num)) { + if (hash_acl_exists(am, acl_num)) { + /* this is a modification, clean up the older entries */ + hash_acl_delete(am, acl_num); + } + hash_acl_add(am, acl_num); + } else { + /* this is a deletion notification */ + hash_acl_delete(am, acl_num); + } +} + + +/* Fill the 5-tuple from the packet */ + +void acl_plugin_fill_5tuple (u32 lc_index, vlib_buffer_t * b0, int is_ip6, int is_input, + int is_l2_path, fa_5tuple_opaque_t * p5tuple_pkt) +{ + acl_plugin_fill_5tuple_inline(lc_index, b0, is_ip6, is_input, is_l2_path, p5tuple_pkt); +} + +int acl_plugin_match_5tuple (u32 lc_index, + fa_5tuple_opaque_t * pkt_5tuple, + int is_ip6, u8 * r_action, + u32 * r_acl_pos_p, + u32 * r_acl_match_p, + u32 * r_rule_match_p, + u32 * trace_bitmap) +{ + return acl_plugin_match_5tuple_inline (lc_index, pkt_5tuple, is_ip6, r_action, r_acl_pos_p, r_acl_match_p, r_rule_match_p, trace_bitmap); +} + + +void +acl_plugin_show_lookup_user (u32 user_index) +{ + acl_main_t *am = &acl_main; + vlib_main_t *vm = am->vlib_main; + acl_lookup_context_user_t *auser; + + pool_foreach (auser, am->acl_users, + ({ + u32 curr_user_index = (auser - am->acl_users); + if (user_index == ~0 || (curr_user_index == user_index)) { + vlib_cli_output (vm, "index %d:%s:%s:%s", curr_user_index, auser->user_module_name, auser->val1_label, auser->val2_label); + } + })); +} + + +void +acl_plugin_show_lookup_context (u32 lc_index) +{ + acl_main_t *am = &acl_main; + vlib_main_t *vm = am->vlib_main; + acl_lookup_context_t *acontext; + // clib_warning("LOOKUP-CONTEXT: lc_index %d acl_list [ %U ]", lc_index, format_vec32, acl_list, "%d"); + if (!am->acl_lookup_contexts) + { + vlib_cli_output(vm, "ACL lookup contexts are not initialized"); + return; + } + + pool_foreach (acontext, am->acl_lookup_contexts, + ({ + u32 curr_lc_index = (acontext - am->acl_lookup_contexts); + if ((lc_index == ~0) || (curr_lc_index == lc_index)) { + if (acl_user_id_valid(am, acontext->context_user_id)) { + acl_lookup_context_user_t *auser = pool_elt_at_index(am->acl_users, acontext->context_user_id); + vlib_cli_output (vm, "index %d:%s %s: %d %s: %d, acl_indices: %U", + curr_lc_index, auser->user_module_name, auser->val1_label, + acontext->user_val1, auser->val2_label, acontext->user_val2, + format_vec32, acontext->acl_indices, "%d"); + } else { + vlib_cli_output (vm, "index %d: user_id: %d user_val1: %d user_val2: %d, acl_indices: %U", + curr_lc_index, acontext->context_user_id, + acontext->user_val1, acontext->user_val2, + format_vec32, acontext->acl_indices, "%d"); + } + } + })); +} diff --git a/src/plugins/acl/lookup_context.h b/src/plugins/acl/lookup_context.h new file mode 100644 index 00000000000..f5888a9c6fb --- /dev/null +++ b/src/plugins/acl/lookup_context.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef included_acl_lookup_context_h +#define included_acl_lookup_context_h + +typedef struct { + /* A name of the portion of the code using the ACL infra */ + char *user_module_name; + /* text label for the first u32 user value assigned to context */ + char *val1_label; + /* text label for the second u32 user value assigned to context */ + char *val2_label; + /* vector of lookup contexts of this user */ + u32 *lookup_contexts; +} acl_lookup_context_user_t; + +typedef struct { + /* vector of acl #s within this context */ + u32 *acl_indices; + /* index of corresponding acl_lookup_context_user_t */ + u32 context_user_id; + /* per-instance user value 1 */ + u32 user_val1; + /* per-instance user value 2 */ + u32 user_val2; +} acl_lookup_context_t; + +void acl_plugin_lookup_context_notify_acl_change(u32 acl_num); + +void acl_plugin_show_lookup_context (u32 lc_index); +void acl_plugin_show_lookup_user (u32 user_index); + + +/* These are in the hash matching for now */ +void acl_plugin_show_tables_mask_type (void); +void acl_plugin_show_tables_acl_hash_info (u32 acl_index); +void acl_plugin_show_tables_applied_info (u32 sw_if_index); +void acl_plugin_show_tables_bihash (u32 show_bihash_verbose); + +/* Debug functions to turn validate/trace on and off */ +void acl_plugin_hash_acl_set_validate_heap(int on); +void acl_plugin_hash_acl_set_trace_heap(int on); + + + +#endif + diff --git a/src/plugins/acl/public_inlines.h b/src/plugins/acl/public_inlines.h new file mode 100644 index 00000000000..4878d15a95c --- /dev/null +++ b/src/plugins/acl/public_inlines.h @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef included_acl_inlines_h +#define included_acl_inlines_h + + +#include <plugins/acl/acl.h> +#include <plugins/acl/fa_node.h> +#include <plugins/acl/hash_lookup_private.h> + + +/* check if a given ACL exists */ + +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +u8 (*acl_plugin_acl_exists) (u32 acl_index); +#else +u8 acl_plugin_acl_exists (u32 acl_index); +#endif + + +/* + * If you are using ACL plugin, get this unique ID first, + * so you can identify yourself when creating the lookup contexts. + */ + +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +u32 (*acl_plugin_register_user_module) (char *caller_module_string, char *val1_label, char *val2_label); +#else +u32 acl_plugin_register_user_module (char *caller_module_string, char *val1_label, char *val2_label); +#endif + +/* + * Allocate a new lookup context index. + * Supply the id assigned to your module during registration, + * and two values of your choice identifying instances + * of use within your module. They are useful for debugging. + */ +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +int (*acl_plugin_get_lookup_context_index) (u32 acl_user_id, u32 val1, u32 val2); +#else +int acl_plugin_get_lookup_context_index (u32 acl_user_id, u32 val1, u32 val2); +#endif + +/* + * Release the lookup context index and destroy + * any asssociated data structures. + */ +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +void (*acl_plugin_put_lookup_context_index) (u32 lc_index); +#else +void acl_plugin_put_lookup_context_index (u32 lc_index); +#endif + +/* + * Prepare the sequential vector of ACL#s to lookup within a given context. + * Any existing list will be overwritten. acl_list is a vector. + */ +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +int (*acl_plugin_set_acl_vec_for_context) (u32 lc_index, u32 *acl_list); +#else +int acl_plugin_set_acl_vec_for_context (u32 lc_index, u32 *acl_list); +#endif + +/* Fill the 5-tuple from the packet */ + +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +void (*acl_plugin_fill_5tuple) (u32 lc_index, vlib_buffer_t * b0, int is_ip6, int is_input, + int is_l2_path, fa_5tuple_opaque_t * p5tuple_pkt); +#else +void acl_plugin_fill_5tuple (u32 lc_index, vlib_buffer_t * b0, int is_ip6, int is_input, + int is_l2_path, fa_5tuple_opaque_t * p5tuple_pkt); +#endif + +#ifdef ACL_PLUGIN_DEFINED_BELOW_IN_FILE +static inline +void acl_plugin_fill_5tuple_inline (u32 lc_index, vlib_buffer_t * b0, int is_ip6, int is_input, + int is_l2_path, fa_5tuple_opaque_t * p5tuple_pkt) { + /* FIXME: normally the inlined version of filling in the 5-tuple. But for now just call the non-inlined version */ + acl_plugin_fill_5tuple(lc_index, b0, is_ip6, is_input, is_l2_path, p5tuple_pkt); +} +#endif + + +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS +int (*acl_plugin_match_5tuple) (u32 lc_index, + fa_5tuple_opaque_t * pkt_5tuple, + int is_ip6, u8 * r_action, + u32 * r_acl_pos_p, + u32 * r_acl_match_p, + u32 * r_rule_match_p, + u32 * trace_bitmap); +#else +int acl_plugin_match_5tuple (u32 lc_index, + fa_5tuple_opaque_t * pkt_5tuple, + int is_ip6, u8 * r_action, + u32 * r_acl_pos_p, + u32 * r_acl_match_p, + u32 * r_rule_match_p, + u32 * trace_bitmap); +#endif + +#ifdef ACL_PLUGIN_DEFINED_BELOW_IN_FILE +static inline int +acl_plugin_match_5tuple_inline (u32 lc_index, + fa_5tuple_opaque_t * pkt_5tuple, + int is_ip6, u8 * r_action, + u32 * r_acl_pos_p, + u32 * r_acl_match_p, + u32 * r_rule_match_p, + u32 * trace_bitmap) { + return acl_plugin_match_5tuple(lc_index, pkt_5tuple, is_ip6, r_action, r_acl_pos_p, r_acl_match_p, r_rule_match_p, trace_bitmap); +} +#endif + +#ifdef ACL_PLUGIN_EXTERNAL_EXPORTS + +#define LOAD_SYMBOL_FROM_PLUGIN(p, s) \ +({ \ + s = vlib_get_plugin_symbol(p, #s); \ + if (!s) \ + return clib_error_return(0, \ + "Plugin %s and/or symbol %s not found.", p, #s); \ +}) + +#define LOAD_SYMBOL(s) LOAD_SYMBOL_FROM_PLUGIN("acl_plugin.so", s) + +static inline clib_error_t * acl_plugin_exports_init (void) +{ + LOAD_SYMBOL(acl_plugin_acl_exists); + LOAD_SYMBOL(acl_plugin_register_user_module); + LOAD_SYMBOL(acl_plugin_get_lookup_context_index); + LOAD_SYMBOL(acl_plugin_put_lookup_context_index); + LOAD_SYMBOL(acl_plugin_set_acl_vec_for_context); + LOAD_SYMBOL(acl_plugin_fill_5tuple); + LOAD_SYMBOL(acl_plugin_match_5tuple); + return 0; +} + +#endif + + + +always_inline void * +get_ptr_to_offset (vlib_buffer_t * b0, int offset) +{ + u8 *p = vlib_buffer_get_current (b0) + offset; + return p; +} + +always_inline int +offset_within_packet (vlib_buffer_t * b0, int offset) +{ + /* For the purposes of this code, "within" means we have at least 8 bytes after it */ + return (offset <= (b0->current_length - 8)); +} + +always_inline void +acl_fill_5tuple (acl_main_t * am, vlib_buffer_t * b0, int is_ip6, + int is_input, int is_l2_path, fa_5tuple_t * p5tuple_pkt) +{ + /* IP4 and IP6 protocol numbers of ICMP */ + static u8 icmp_protos_v4v6[] = { IP_PROTOCOL_ICMP, IP_PROTOCOL_ICMP6 }; + + int l3_offset; + int l4_offset; + u16 ports[2]; + u16 proto; + + if (is_l2_path) + { + l3_offset = ethernet_buffer_header_size(b0); + } + else + { + if (is_input) + l3_offset = 0; + else + l3_offset = vnet_buffer(b0)->ip.save_rewrite_length; + } + + /* key[0..3] contains src/dst address and is cleared/set below */ + /* Remainder of the key and per-packet non-key data */ + p5tuple_pkt->kv.key[4] = 0; + p5tuple_pkt->kv.value = 0; + + if (is_ip6) + { + clib_memcpy (&p5tuple_pkt->addr, + get_ptr_to_offset (b0, + offsetof (ip6_header_t, + src_address) + l3_offset), + sizeof (p5tuple_pkt->addr)); + proto = + *(u8 *) get_ptr_to_offset (b0, + offsetof (ip6_header_t, + protocol) + l3_offset); + l4_offset = l3_offset + sizeof (ip6_header_t); +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning ("ACL_FA_NODE_DBG: proto: %d, l4_offset: %d", proto, + l4_offset); +#endif + /* IP6 EH handling is here, increment l4_offset if needs to, update the proto */ + int need_skip_eh = clib_bitmap_get (am->fa_ipv6_known_eh_bitmap, proto); + if (PREDICT_FALSE (need_skip_eh)) + { + while (need_skip_eh && offset_within_packet (b0, l4_offset)) + { + /* Fragment header needs special handling */ + if (PREDICT_FALSE(ACL_EH_FRAGMENT == proto)) + { + proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); + u16 frag_offset; + clib_memcpy (&frag_offset, get_ptr_to_offset (b0, 2 + l4_offset), sizeof(frag_offset)); + frag_offset = clib_net_to_host_u16(frag_offset) >> 3; + if (frag_offset) + { + p5tuple_pkt->pkt.is_nonfirst_fragment = 1; + /* invalidate L4 offset so we don't try to find L4 info */ + l4_offset += b0->current_length; + } + else + { + /* First fragment: skip the frag header and move on. */ + l4_offset += 8; + } + } + else + { + u8 nwords = *(u8 *) get_ptr_to_offset (b0, 1 + l4_offset); + proto = *(u8 *) get_ptr_to_offset (b0, l4_offset); + l4_offset += 8 * (1 + (u16) nwords); + } +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning ("ACL_FA_NODE_DBG: new proto: %d, new offset: %d", + proto, l4_offset); +#endif + need_skip_eh = + clib_bitmap_get (am->fa_ipv6_known_eh_bitmap, proto); + } + } + } + else + { + p5tuple_pkt->kv.key[0] = 0; + p5tuple_pkt->kv.key[1] = 0; + p5tuple_pkt->kv.key[2] = 0; + p5tuple_pkt->kv.key[3] = 0; + clib_memcpy (&p5tuple_pkt->addr[0].ip4, + get_ptr_to_offset (b0, + offsetof (ip4_header_t, + src_address) + l3_offset), + sizeof (p5tuple_pkt->addr[0].ip4)); + clib_memcpy (&p5tuple_pkt->addr[1].ip4, + get_ptr_to_offset (b0, + offsetof (ip4_header_t, + dst_address) + l3_offset), + sizeof (p5tuple_pkt->addr[1].ip4)); + proto = + *(u8 *) get_ptr_to_offset (b0, + offsetof (ip4_header_t, + protocol) + l3_offset); + l4_offset = l3_offset + sizeof (ip4_header_t); + u16 flags_and_fragment_offset; + clib_memcpy (&flags_and_fragment_offset, + get_ptr_to_offset (b0, + offsetof (ip4_header_t, + flags_and_fragment_offset)) + l3_offset, + sizeof(flags_and_fragment_offset)); + flags_and_fragment_offset = clib_net_to_host_u16 (flags_and_fragment_offset); + + /* non-initial fragments have non-zero offset */ + if ((PREDICT_FALSE(0xfff & flags_and_fragment_offset))) + { + p5tuple_pkt->pkt.is_nonfirst_fragment = 1; + /* invalidate L4 offset so we don't try to find L4 info */ + l4_offset += b0->current_length; + } + + } + p5tuple_pkt->l4.proto = proto; + if (PREDICT_TRUE (offset_within_packet (b0, l4_offset))) + { + p5tuple_pkt->pkt.l4_valid = 1; + if (icmp_protos_v4v6[is_ip6] == proto) + { + /* type */ + p5tuple_pkt->l4.port[0] = + *(u8 *) get_ptr_to_offset (b0, + l4_offset + offsetof (icmp46_header_t, + type)); + /* code */ + p5tuple_pkt->l4.port[1] = + *(u8 *) get_ptr_to_offset (b0, + l4_offset + offsetof (icmp46_header_t, + code)); + } + else if ((IP_PROTOCOL_TCP == proto) || (IP_PROTOCOL_UDP == proto)) + { + clib_memcpy (&ports, + get_ptr_to_offset (b0, + l4_offset + offsetof (tcp_header_t, + src_port)), + sizeof (ports)); + p5tuple_pkt->l4.port[0] = clib_net_to_host_u16 (ports[0]); + p5tuple_pkt->l4.port[1] = clib_net_to_host_u16 (ports[1]); + + p5tuple_pkt->pkt.tcp_flags = + *(u8 *) get_ptr_to_offset (b0, + l4_offset + offsetof (tcp_header_t, + flags)); + p5tuple_pkt->pkt.tcp_flags_valid = (proto == IP_PROTOCOL_TCP); + } + /* + * FIXME: rather than the above conditional, here could + * be a nice generic mechanism to extract two L4 values: + * + * have a per-protocol array of 4 elements like this: + * u8 offset; to take the byte from, off L4 header + * u8 mask; to mask it with, before storing + * + * this way we can describe UDP, TCP and ICMP[46] semantics, + * and add a sort of FPM-type behavior for other protocols. + * + * Of course, is it faster ? and is it needed ? + * + */ + } +} + +always_inline void +acl_plugin_fill_5tuple_inline (u32 lc_index, vlib_buffer_t * b0, int is_ip6, + int is_input, int is_l2_path, fa_5tuple_opaque_t * p5tuple_pkt) +{ + acl_main_t *am = &acl_main; + acl_fill_5tuple(am, b0, is_ip6, is_input, is_l2_path, (fa_5tuple_t *)p5tuple_pkt); +} + + + +always_inline int +fa_acl_match_addr (ip46_address_t * addr1, ip46_address_t * addr2, + int prefixlen, int is_ip6) +{ + if (prefixlen == 0) + { + /* match any always succeeds */ + return 1; + } + if (is_ip6) + { + if (memcmp (addr1, addr2, prefixlen / 8)) + { + /* If the starting full bytes do not match, no point in bittwidling the thumbs further */ + return 0; + } + if (prefixlen % 8) + { + u8 b1 = *((u8 *) addr1 + 1 + prefixlen / 8); + u8 b2 = *((u8 *) addr2 + 1 + prefixlen / 8); + u8 mask0 = (0xff - ((1 << (8 - (prefixlen % 8))) - 1)); + return (b1 & mask0) == b2; + } + else + { + /* The prefix fits into integer number of bytes, so nothing left to do */ + return 1; + } + } + else + { + uint32_t a1 = clib_net_to_host_u32 (addr1->ip4.as_u32); + uint32_t a2 = clib_net_to_host_u32 (addr2->ip4.as_u32); + uint32_t mask0 = 0xffffffff - ((1 << (32 - prefixlen)) - 1); + return (a1 & mask0) == a2; + } +} + +always_inline int +fa_acl_match_port (u16 port, u16 port_first, u16 port_last, int is_ip6) +{ + return ((port >= port_first) && (port <= port_last)); +} + +always_inline int +single_acl_match_5tuple (acl_main_t * am, u32 acl_index, fa_5tuple_t * pkt_5tuple, + int is_ip6, u8 * r_action, u32 * r_acl_match_p, + u32 * r_rule_match_p, u32 * trace_bitmap) +{ + int i; + acl_list_t *a; + acl_rule_t *r; + + if (pool_is_free_index (am->acls, acl_index)) + { + if (r_acl_match_p) + *r_acl_match_p = acl_index; + if (r_rule_match_p) + *r_rule_match_p = -1; + /* the ACL does not exist but is used for policy. Block traffic. */ + return 0; + } + a = am->acls + acl_index; + for (i = 0; i < a->count; i++) + { + r = a->rules + i; +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning("ACL_FA_NODE_DBG acl %d rule %d tag %s", acl_index, i, a->tag); +#endif + if (is_ip6 != r->is_ipv6) + { + continue; + } + if (!fa_acl_match_addr + (&pkt_5tuple->addr[1], &r->dst, r->dst_prefixlen, is_ip6)) + continue; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d pkt dst addr %U match rule addr %U/%d", + acl_index, i, format_ip46_address, &pkt_5tuple->addr[1], + r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, format_ip46_address, + &r->dst, r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, + r->dst_prefixlen); +#endif + + if (!fa_acl_match_addr + (&pkt_5tuple->addr[0], &r->src, r->src_prefixlen, is_ip6)) + continue; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d pkt src addr %U match rule addr %U/%d", + acl_index, i, format_ip46_address, &pkt_5tuple->addr[0], + r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, format_ip46_address, + &r->src, r->is_ipv6 ? IP46_TYPE_IP6: IP46_TYPE_IP4, + r->src_prefixlen); + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d trying to match pkt proto %d with rule %d", + acl_index, i, pkt_5tuple->l4.proto, r->proto); +#endif + if (r->proto) + { + if (pkt_5tuple->l4.proto != r->proto) + continue; + + if (PREDICT_FALSE (pkt_5tuple->pkt.is_nonfirst_fragment && + am->l4_match_nonfirst_fragment)) + { + /* non-initial fragment with frag match configured - match this rule */ + *trace_bitmap |= 0x80000000; + *r_action = r->is_permit; + if (r_acl_match_p) + *r_acl_match_p = acl_index; + if (r_rule_match_p) + *r_rule_match_p = i; + return 1; + } + + /* A sanity check just to ensure we are about to match the ports extracted from the packet */ + if (PREDICT_FALSE (!pkt_5tuple->pkt.l4_valid)) + continue; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d pkt proto %d match rule %d", + acl_index, i, pkt_5tuple->l4.proto, r->proto); +#endif + + if (!fa_acl_match_port + (pkt_5tuple->l4.port[0], r->src_port_or_type_first, + r->src_port_or_type_last, is_ip6)) + continue; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d pkt sport %d match rule [%d..%d]", + acl_index, i, pkt_5tuple->l4.port[0], r->src_port_or_type_first, + r->src_port_or_type_last); +#endif + + if (!fa_acl_match_port + (pkt_5tuple->l4.port[1], r->dst_port_or_code_first, + r->dst_port_or_code_last, is_ip6)) + continue; + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning + ("ACL_FA_NODE_DBG acl %d rule %d pkt dport %d match rule [%d..%d]", + acl_index, i, pkt_5tuple->l4.port[1], r->dst_port_or_code_first, + r->dst_port_or_code_last); +#endif + if (pkt_5tuple->pkt.tcp_flags_valid + && ((pkt_5tuple->pkt.tcp_flags & r->tcp_flags_mask) != + r->tcp_flags_value)) + continue; + } + /* everything matches! */ +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning ("ACL_FA_NODE_DBG acl %d rule %d FULL-MATCH, action %d", + acl_index, i, r->is_permit); +#endif + *r_action = r->is_permit; + if (r_acl_match_p) + *r_acl_match_p = acl_index; + if (r_rule_match_p) + *r_rule_match_p = i; + return 1; + } + return 0; +} + +always_inline int +acl_plugin_single_acl_match_5tuple (u32 acl_index, fa_5tuple_t * pkt_5tuple, + int is_ip6, u8 * r_action, u32 * r_acl_match_p, + u32 * r_rule_match_p, u32 * trace_bitmap) +{ + acl_main_t * am = &acl_main; + return single_acl_match_5tuple(am, acl_index, pkt_5tuple, is_ip6, r_action, + r_acl_match_p, r_rule_match_p, trace_bitmap); +} + +always_inline int +linear_multi_acl_match_5tuple (u32 lc_index, fa_5tuple_t * pkt_5tuple, + int is_ip6, u8 *r_action, u32 *acl_pos_p, u32 * acl_match_p, + u32 * rule_match_p, u32 * trace_bitmap) +{ + acl_main_t *am = &acl_main; + int i; + u32 *acl_vector; + u8 action = 0; + acl_lookup_context_t *acontext = pool_elt_at_index(am->acl_lookup_contexts, lc_index); + + acl_vector = acontext->acl_indices; + + for (i = 0; i < vec_len (acl_vector); i++) + { +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning ("ACL_FA_NODE_DBG: Trying to match ACL: %d", + acl_vector[i]); +#endif + if (single_acl_match_5tuple + (am, acl_vector[i], pkt_5tuple, is_ip6, &action, + acl_match_p, rule_match_p, trace_bitmap)) + { + *r_action = action; + *acl_pos_p = i; + return 1; + } + } + if (vec_len (acl_vector) > 0) + { + return 0; + } +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning ("ACL_FA_NODE_DBG: No ACL on lc_index %d", lc_index); +#endif + /* If there are no ACLs defined we should not be here. */ + return 0; +} + + + +/* + * This returns true if there is indeed a match on the portranges. + * With all these levels of indirections, this is not going to be very fast, + * so, best use the individual ports or wildcard ports for performance. + */ +always_inline int +match_portranges(acl_main_t *am, fa_5tuple_t *match, u32 index) +{ + + applied_hash_ace_entry_t **applied_hash_aces = vec_elt_at_index(am->hash_entry_vec_by_lc_index, match->pkt.lc_index); + applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), index); + + acl_rule_t *r = &(am->acls[pae->acl_index].rules[pae->ace_index]); + +#ifdef FA_NODE_VERBOSE_DEBUG + clib_warning("PORTMATCH: %d <= %d <= %d && %d <= %d <= %d ?", + r->src_port_or_type_first, match->l4.port[0], r->src_port_or_type_last, + r->dst_port_or_code_first, match->l4.port[1], r->dst_port_or_code_last); +#endif + + return ( ((r->src_port_or_type_first <= match->l4.port[0]) && r->src_port_or_type_last >= match->l4.port[0]) && + ((r->dst_port_or_code_first <= match->l4.port[1]) && r->dst_port_or_code_last >= match->l4.port[1]) ); +} + +always_inline u32 +multi_acl_match_get_applied_ace_index(acl_main_t *am, fa_5tuple_t *match) +{ + clib_bihash_kv_48_8_t kv; + clib_bihash_kv_48_8_t result; + fa_5tuple_t *kv_key = (fa_5tuple_t *)kv.key; + hash_acl_lookup_value_t *result_val = (hash_acl_lookup_value_t *)&result.value; + u64 *pmatch = (u64 *)match; + u64 *pmask; + u64 *pkey; + int mask_type_index; + u32 curr_match_index = ~0; + + u32 lc_index = match->pkt.lc_index; + applied_hash_ace_entry_t **applied_hash_aces = vec_elt_at_index(am->hash_entry_vec_by_lc_index, match->pkt.lc_index); + applied_hash_acl_info_t **applied_hash_acls = &am->applied_hash_acl_info_by_lc_index; + + DBG("TRYING TO MATCH: %016llx %016llx %016llx %016llx %016llx %016llx", + pmatch[0], pmatch[1], pmatch[2], pmatch[3], pmatch[4], pmatch[5]); + + for(mask_type_index=0; mask_type_index < pool_len(am->ace_mask_type_pool); mask_type_index++) { + if (!clib_bitmap_get(vec_elt_at_index((*applied_hash_acls), lc_index)->mask_type_index_bitmap, mask_type_index)) { + /* This bit is not set. Avoid trying to match */ + continue; + } + ace_mask_type_entry_t *mte = vec_elt_at_index(am->ace_mask_type_pool, mask_type_index); + pmatch = (u64 *)match; + pmask = (u64 *)&mte->mask; + pkey = (u64 *)kv.key; + /* + * unrolling the below loop results in a noticeable performance increase. + int i; + for(i=0; i<6; i++) { + kv.key[i] = pmatch[i] & pmask[i]; + } + */ + + *pkey++ = *pmatch++ & *pmask++; + *pkey++ = *pmatch++ & *pmask++; + *pkey++ = *pmatch++ & *pmask++; + *pkey++ = *pmatch++ & *pmask++; + *pkey++ = *pmatch++ & *pmask++; + *pkey++ = *pmatch++ & *pmask++; + + kv_key->pkt.mask_type_index_lsb = mask_type_index; + DBG(" KEY %3d: %016llx %016llx %016llx %016llx %016llx %016llx", mask_type_index, + kv.key[0], kv.key[1], kv.key[2], kv.key[3], kv.key[4], kv.key[5]); + int res = clib_bihash_search_48_8 (&am->acl_lookup_hash, &kv, &result); + if (res == 0) { + DBG("ACL-MATCH! result_val: %016llx", result_val->as_u64); + if (result_val->applied_entry_index < curr_match_index) { + if (PREDICT_FALSE(result_val->need_portrange_check)) { + /* + * This is going to be slow, since we can have multiple superset + * entries for narrow-ish portranges, e.g.: + * 0..42 100..400, 230..60000, + * so we need to walk linearly and check if they match. + */ + + u32 curr_index = result_val->applied_entry_index; + while ((curr_index != ~0) && !match_portranges(am, match, curr_index)) { + /* while no match and there are more entries, walk... */ + applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces),curr_index); + DBG("entry %d did not portmatch, advancing to %d", curr_index, pae->next_applied_entry_index); + curr_index = pae->next_applied_entry_index; + } + if (curr_index < curr_match_index) { + DBG("The index %d is the new candidate in portrange matches.", curr_index); + curr_match_index = curr_index; + } else { + DBG("Curr portmatch index %d is too big vs. current matched one %d", curr_index, curr_match_index); + } + } else { + /* The usual path is here. Found an entry in front of the current candiate - so it's a new one */ + DBG("This match is the new candidate"); + curr_match_index = result_val->applied_entry_index; + if (!result_val->shadowed) { + /* new result is known to not be shadowed, so no point to look up further */ + break; + } + } + } + } + } + DBG("MATCH-RESULT: %d", curr_match_index); + return curr_match_index; +} + +always_inline int +hash_multi_acl_match_5tuple (u32 lc_index, fa_5tuple_t * pkt_5tuple, + int is_ip6, u8 *action, u32 *acl_pos_p, u32 * acl_match_p, + u32 * rule_match_p, u32 * trace_bitmap) +{ + acl_main_t *am = &acl_main; + applied_hash_ace_entry_t **applied_hash_aces = vec_elt_at_index(am->hash_entry_vec_by_lc_index, lc_index); + u32 match_index = multi_acl_match_get_applied_ace_index(am, pkt_5tuple); + if (match_index < vec_len((*applied_hash_aces))) { + applied_hash_ace_entry_t *pae = vec_elt_at_index((*applied_hash_aces), match_index); + pae->hitcount++; + *acl_pos_p = pae->acl_position; + *acl_match_p = pae->acl_index; + *rule_match_p = pae->ace_index; + *action = pae->action; + return 1; + } + return 0; +} + + + +always_inline int +acl_plugin_match_5tuple_inline (u32 lc_index, + fa_5tuple_opaque_t * pkt_5tuple, + int is_ip6, u8 * r_action, + u32 * r_acl_pos_p, + u32 * r_acl_match_p, + u32 * r_rule_match_p, + u32 * trace_bitmap) +{ + acl_main_t *am = &acl_main; + if (am->use_hash_acl_matching) { + return hash_multi_acl_match_5tuple(lc_index, (fa_5tuple_t *)pkt_5tuple, is_ip6, r_action, + r_acl_pos_p, r_acl_match_p, r_rule_match_p, trace_bitmap); + } else { + return linear_multi_acl_match_5tuple(lc_index, (fa_5tuple_t *)pkt_5tuple, is_ip6, r_action, + r_acl_pos_p, r_acl_match_p, r_rule_match_p, trace_bitmap); + } +} + + + +#endif diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h index f928e0d8aa4..5859bb92d3e 100644 --- a/src/vnet/api_errno.h +++ b/src/vnet/api_errno.h @@ -141,7 +141,8 @@ _(INIT_FAILED, -144, "Initialization Failed") \ _(NETLINK_ERROR, -145, "netlink error") \ _(BIER_BSL_UNSUP, -146, "BIER bit-string-length unsupported") \ _(INSTANCE_IN_USE, -147, "Instance in use") \ -_(INVALID_SESSION_ID, -148, "session ID out of range") +_(INVALID_SESSION_ID, -148, "session ID out of range") \ +_(ACL_IN_USE_BY_LOOKUP_CONTEXT, -149, "ACL in use by a lookup context") \ typedef enum { |