diff options
Diffstat (limited to 'src/plugins/acl')
-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 |
13 files changed, 2117 insertions, 1148 deletions
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 |