diff options
author | Florin Coras <fcoras@cisco.com> | 2017-10-17 00:03:13 -0700 |
---|---|---|
committer | Dave Barach <openvpp@barachs.net> | 2017-10-28 19:56:39 +0000 |
commit | 1c7104514cd40d2377caca36cf40c13b791bc5aa (patch) | |
tree | 2b95bb11dd8658e826ad8cb3fe4d399adbab7e01 /src/vnet/session | |
parent | ae5a02f8235b9a243df09b42e932ae5f238e366b (diff) |
session: rules tables
This introduces 5-tuple lookup tables that may be used to implement
custom session layer actions at connection establishment time (session
layer perspective).
The rules table build mask-match-action lookup trees that for a given
5-tuple key return the action for the first longest match. If rules
overlap, ordering is established by tuple longest match with the
following descending priority: remote ip, local ip, remote port, local
port.
At this time, the only match action supported is to forward packets to
the application identified by the action.
Change-Id: Icbade6fac720fa3979820d50cd7d6137f8b635c3
Signed-off-by: Florin Coras <fcoras@cisco.com>
Diffstat (limited to 'src/vnet/session')
-rw-r--r-- | src/vnet/session/application.c | 23 | ||||
-rw-r--r-- | src/vnet/session/application.h | 3 | ||||
-rw-r--r-- | src/vnet/session/application_namespace.c | 13 | ||||
-rw-r--r-- | src/vnet/session/application_namespace.h | 9 | ||||
-rw-r--r-- | src/vnet/session/mma_16.h | 33 | ||||
-rw-r--r-- | src/vnet/session/mma_40.h | 33 | ||||
-rw-r--r-- | src/vnet/session/mma_template.c | 265 | ||||
-rw-r--r-- | src/vnet/session/mma_template.h | 91 | ||||
-rw-r--r-- | src/vnet/session/session.api | 38 | ||||
-rwxr-xr-x | src/vnet/session/session_api.c | 40 | ||||
-rw-r--r-- | src/vnet/session/session_lookup.c | 370 | ||||
-rw-r--r-- | src/vnet/session/session_lookup.h | 26 | ||||
-rw-r--r-- | src/vnet/session/session_rules_table.c | 429 | ||||
-rw-r--r-- | src/vnet/session/session_rules_table.h | 107 | ||||
-rw-r--r-- | src/vnet/session/session_table.c | 2 | ||||
-rw-r--r-- | src/vnet/session/session_table.h | 6 | ||||
-rw-r--r-- | src/vnet/session/session_test.c | 462 | ||||
-rw-r--r-- | src/vnet/session/transport.c | 32 | ||||
-rw-r--r-- | src/vnet/session/transport.h | 3 |
19 files changed, 1974 insertions, 11 deletions
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c index c6fd1197304..df68ce01597 100644 --- a/src/vnet/session/application.c +++ b/src/vnet/session/application.c @@ -482,6 +482,27 @@ application_has_global_scope (application_t * app) return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; } +u32 +application_n_listeners (application_t * app) +{ + return hash_elts (app->listeners_table); +} + +stream_session_t * +application_first_listener (application_t * app) +{ + u64 handle; + u32 sm_index; + + /* *INDENT-OFF* */ + hash_foreach (handle, sm_index, app->listeners_table, ({ + return listen_session_get_from_handle (handle); + })); + /* *INDENT-ON* */ + + return 0; +} + u8 * format_application_listener (u8 * s, va_list * args) { @@ -651,7 +672,7 @@ show_app_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_output (vm, "%U", format_application_listener, 0 /* header */ , 0, 0, verbose); - /* *INDENT-OFF* */ + /* *INDENT-OFF* */ pool_foreach (app, app_pool, ({ /* App's listener sessions */ diff --git a/src/vnet/session/application.h b/src/vnet/session/application.h index 00a5554dd23..b05e1e17b4f 100644 --- a/src/vnet/session/application.h +++ b/src/vnet/session/application.h @@ -132,7 +132,8 @@ u8 *application_name_from_index (u32 app_index); u8 application_has_local_scope (application_t * app); u8 application_has_global_scope (application_t * app); - +u32 application_n_listeners (application_t * app); +stream_session_t *application_first_listener (application_t * app); #endif /* SRC_VNET_SESSION_APPLICATION_H_ */ /* diff --git a/src/vnet/session/application_namespace.c b/src/vnet/session/application_namespace.c index f66dc27ee4e..326b0c0146f 100644 --- a/src/vnet/session/application_namespace.c +++ b/src/vnet/session/application_namespace.c @@ -137,6 +137,19 @@ app_namespace_id_from_index (u32 index) return app_namespace_id (app_ns); } +u32 +app_namespace_get_fib_index (app_namespace_t * app_ns, u8 fib_proto) +{ + return fib_proto == FIB_PROTOCOL_IP4 ? + app_ns->ip4_fib_index : app_ns->ip6_fib_index; +} + +session_table_t * +app_namespace_get_local_table (app_namespace_t * app_ns) +{ + return session_table_get (app_ns->local_table_index); +} + void app_namespaces_init (void) { diff --git a/src/vnet/session/application_namespace.h b/src/vnet/session/application_namespace.h index da3f6017961..6eb9d53c864 100644 --- a/src/vnet/session/application_namespace.h +++ b/src/vnet/session/application_namespace.h @@ -14,6 +14,7 @@ */ #include <vnet/vnet.h> +#include <vnet/session/session_table.h> #ifndef SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ #define SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ @@ -71,6 +72,14 @@ u32 app_namespace_index_from_id (const u8 * ns_id); void app_namespaces_init (void); clib_error_t *vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t * a); +u32 app_namespace_get_fib_index (app_namespace_t * app_ns, u8 fib_proto); +session_table_t *app_namespace_get_local_table (app_namespace_t * app_ns); + +always_inline app_namespace_t * +app_namespace_get_default (void) +{ + return app_namespace_get (0); +} #endif /* SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ */ diff --git a/src/vnet/session/mma_16.h b/src/vnet/session/mma_16.h new file mode 100644 index 00000000000..3e2e84d4a8b --- /dev/null +++ b/src/vnet/session/mma_16.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#undef MMA_RT_TYPE +#define MMA_RT_TYPE 16 + +#ifndef SRC_VNET_SESSION_MMA_TABLE_16_H_ +#define SRC_VNET_SESSION_MMA_TABLE_16_H_ + +#undef SRC_VNET_SESSION_MMA_TEMPLATE_H_ +#include <vnet/session/mma_template.h> + +#endif /* SRC_VNET_SESSION_MMA_TABLE_16_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/mma_40.h b/src/vnet/session/mma_40.h new file mode 100644 index 00000000000..773b7f08cca --- /dev/null +++ b/src/vnet/session/mma_40.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef MMA_RT_TYPE +#define MMA_RT_TYPE 40 + +#ifndef SRC_VNET_SESSION_MMA_TABLE_40_H_ +#define SRC_VNET_SESSION_MMA_TABLE_40_H_ + +#undef SRC_VNET_SESSION_MMA_TEMPLATE_H_ +#include <vnet/session/mma_template.h> + +#endif /* SRC_VNET_SESSION_MMA_TABLE_40_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/mma_template.c b/src/vnet/session/mma_template.c new file mode 100644 index 00000000000..81333a73ee5 --- /dev/null +++ b/src/vnet/session/mma_template.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include <vppinfra/error.h> + +u8 RT (rule_is_exact_match) (RTT (mma_rule) * key, RTT (mma_rule) * r) +{ + int i; + + for (i = 0; i < ARRAY_LEN (key->match.as_u64); i++) + { + if (key->match.as_u64[i] != r->match.as_u64[i]) + return 0; + } + for (i = 0; i < ARRAY_LEN (key->mask.as_u64); i++) + { + if (key->mask.as_u64[i] != r->mask.as_u64[i]) + return 0; + } + return 1; +} + +u8 +RT (rule_is_match_for_key) (RTT (mma_mask_or_match) * key, RTT (mma_rule) * r) +{ + RTT (mma_mask_or_match) _tmp_key, *tkp = &_tmp_key; + int i; + + *tkp = *key; + for (i = 0; i < ARRAY_LEN (tkp->as_u64); i++) + tkp->as_u64[i] &= r->mask.as_u64[i]; + for (i = 0; i < ARRAY_LEN (tkp->as_u64); i++) + { + if (tkp->as_u64[i] != r->match.as_u64[i]) + return 0; + } + return 1; +} + +RTT (mma_rule) * RT (mma_rules_table_rule_alloc) (RTT (mma_rules_table) * srt) +{ + RTT (mma_rule) * rule; + pool_get (srt->rules, rule); + memset (rule, 0, sizeof (*rule)); + return rule; +} + +RTT (mma_rule) * +RT (mma_rule_free) (RTT (mma_rules_table) * srt, RTT (mma_rule) * rule) +{ + pool_put (srt->rules, rule); + memset (rule, 0xfa, sizeof (*rule)); + return rule; +} + +RTT (mma_rule) * +RT (mma_rules_table_get_rule) (RTT (mma_rules_table) * srt, u32 srt_index) +{ + if (!pool_is_free_index (srt->rules, srt_index)) + return (srt->rules + srt_index); + return 0; +} + +u32 +RT (mma_rules_table_rule_index) (RTT (mma_rules_table) * srt, + RTT (mma_rule) * sr) +{ + ASSERT (sr); + return (sr - srt->rules); +} + +/** + * Lookup key in table + * + * This should be optimized .. eventually + */ +u32 +RT (mma_rules_table_lookup) (RTT (mma_rules_table) * srt, + RTT (mma_mask_or_match) * key, u32 rule_index) +{ + RTT (mma_rule) * rp; + u32 rv; + int i; + + ASSERT (rule_index != SESSION_RULES_TABLE_INVALID_INDEX); + rp = RT (mma_rules_table_get_rule) (srt, rule_index); + ASSERT (rp); + + if (!RT (rule_is_match_for_key) (key, rp)) + return ~0; + for (i = 0; i < vec_len (rp->next_indices); i++) + { + rv = RT (mma_rules_table_lookup) (srt, key, rp->next_indices[i]); + if (rv != ~0) + return (rv); + } + return (rp->action_index); +} + +u32 +RT (mma_rules_table_lookup_rule) (RTT (mma_rules_table) * srt, + RTT (mma_mask_or_match) * key, + u32 rule_index) +{ + RTT (mma_rule) * rp; + u32 rv; + int i; + + ASSERT (rule_index != SESSION_RULES_TABLE_INVALID_INDEX); + rp = RT (mma_rules_table_get_rule) (srt, rule_index); + ASSERT (rp); + + if (!RT (rule_is_match_for_key) (key, rp)) + return ~0; + for (i = 0; i < vec_len (rp->next_indices); i++) + { + rv = RT (mma_rules_table_lookup_rule) (srt, key, rp->next_indices[i]); + if (rv != ~0) + return (rv); + } + return rule_index; +} + +static +RTT (mma_rules_table) * +RTT (sort_srt); + + int RT (mma_sort_indices) (void *e1, void *e2) +{ + u32 *ri1 = e1, *ri2 = e2; + RTT (mma_rule) * rule1, *rule2; + rule1 = RT (mma_rules_table_get_rule) (RTT (sort_srt), *ri1); + rule2 = RT (mma_rules_table_get_rule) (RTT (sort_srt), *ri2); + return RTT (sort_srt)->rule_cmp_fn (rule1, rule2); +} + +void RT (mma_sort) (RTT (mma_rules_table) * srt, u32 * next_indices) +{ + RTT (sort_srt) = srt; + vec_sort_with_function (next_indices, RT (mma_sort_indices)); +} + +int +RT (mma_rules_table_add_rule) (RTT (mma_rules_table) * srt, + RTT (mma_rule) * rule) +{ + u32 parent_index, i, *next_indices = 0, added = 0, rule_index; + RTT (mma_rule) * parent, *child; + + rule_index = RT (mma_rules_table_rule_index) (srt, rule); + parent_index = RT (mma_rules_table_lookup_rule) (srt, &rule->match, + srt->root_index); + parent = RT (mma_rules_table_get_rule) (srt, parent_index); + if (RT (rule_is_exact_match) (rule, parent)) + { + parent->action_index = rule->action_index; + RT (mma_rule_free) (srt, rule); + return -1; + } + + if (vec_len (parent->next_indices) == 0) + { + vec_add1 (parent->next_indices, rule_index); + return 0; + } + + /* Check if new rule is parent of some of the existing children */ + for (i = 0; i < vec_len (parent->next_indices); i++) + { + child = RT (mma_rules_table_get_rule) (srt, parent->next_indices[i]); + if (RT (rule_is_match_for_key) (&child->match, rule)) + { + vec_add1 (rule->next_indices, parent->next_indices[i]); + if (!added) + { + vec_add1 (next_indices, rule_index); + added = 1; + } + } + else + { + if (!added && srt->rule_cmp_fn (rule, child) < 0) + { + vec_add1 (next_indices, rule_index); + added = 1; + } + vec_add1 (next_indices, parent->next_indices[i]); + } + } + if (!added) + vec_add1 (next_indices, rule_index); + vec_free (parent->next_indices); + parent->next_indices = next_indices; + return 0; +} + +int +RT (mma_rules_table_del_rule) (RTT (mma_rules_table) * srt, + RTT (mma_rule) * rule, u32 rule_index) +{ + RTT (mma_rule) * rp; + u32 rv; + int i; + + ASSERT (rule_index != SESSION_RULES_TABLE_INVALID_INDEX); + rp = RT (mma_rules_table_get_rule) (srt, rule_index); + + if (!RT (rule_is_match_for_key) (&rule->match, rp)) + return ~0; + if (RT (rule_is_exact_match) (rule, rp)) + return 1; + for (i = 0; i < vec_len (rp->next_indices); i++) + { + rv = RT (mma_rules_table_del_rule) (srt, rule, rp->next_indices[i]); + if (rv == 1) + { + RTT (mma_rule) * child; + u32 *next_indices = 0, *new_elts, left_to_add; + child = RT (mma_rules_table_get_rule) (srt, rp->next_indices[i]); + ASSERT (RT (rule_is_exact_match) (rule, child)); + + if (i != 0) + { + vec_add2 (next_indices, new_elts, i); + clib_memcpy (new_elts, rp->next_indices, i * sizeof (u32)); + } + if (vec_len (child->next_indices)) + vec_append (next_indices, child->next_indices); + left_to_add = vec_len (rp->next_indices) - i - 1; + if (left_to_add) + { + vec_add2 (next_indices, new_elts, left_to_add); + clib_memcpy (new_elts, &rp->next_indices[i + 1], + left_to_add * sizeof (u32)); + } + RT (mma_rule_free) (srt, child); + vec_free (rp->next_indices); + rp->next_indices = next_indices; + return 0; + } + else if (rv == 0) + return rv; + } + return ~0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/mma_template.h b/src/vnet/session/mma_template.h new file mode 100644 index 00000000000..8b6fd75e69c --- /dev/null +++ b/src/vnet/session/mma_template.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef SRC_VNET_SESSION_MMA_TEMPLATE_H_ +#define SRC_VNET_SESSION_MMA_TEMPLATE_H_ + +#include <vppinfra/pool.h> + +#ifndef MMA_RT_TYPE +#error MMA_RT_TYPE not defined +#endif + +#define _rt(a,b) a##_##b +#define __rt(a, b) _rt(a,b) +#define RT(a) __rt(a, MMA_RT_TYPE) + +#define _rtt(a,b) a##_##b##_t +#define __rtt(a, b) _rtt(a,b) +#define RTT(a) __rtt(a, MMA_RT_TYPE) + +#define SESSION_RULES_TABLE_INVALID_INDEX ((u32)~0) + +typedef struct +{ + u64 as_u64[MMA_RT_TYPE / 8]; +} RTT (mma_mask_or_match); + +typedef struct +{ + u32 action_index; + u32 *next_indices; + /* *INDENT-OFF* */ + RTT (mma_mask_or_match) mask; + RTT (mma_mask_or_match) match; + RTT (mma_mask_or_match) max_match; + /* *INDENT-ON* */ +} RTT (mma_rule); + +typedef int (*RTT (rule_cmp_fn)) (RTT (mma_rule) * rule1, + RTT (mma_rule) * rule2); +typedef struct +{ + /** Root for rules tree */ + u32 root_index; + + /** Rules pool */ + RTT (mma_rule) * rules; + + RTT (rule_cmp_fn) rule_cmp_fn; +} RTT (mma_rules_table); + +u32 +RT (mma_table_lookup) (RTT (mma_rules_table) * srt, + RTT (mma_mask_or_match) * key, u32 rule_index); +u32 +RT (mma_table_lookup_rule) (RTT (mma_rules_table) * srt, + RTT (mma_mask_or_match) * key, u32 rule_index); +int +RT (mma_table_add_rule) (RTT (mma_rules_table) * srt, RTT (mma_rule) * rule); +int +RT (mma_table_del_rule) (RTT (mma_rules_table) * srt, + RTT (mma_rule) * rule, u32 rule_index); +RTT (mma_rule) * +RT (mma_rules_table_rule_alloc) (RTT (mma_rules_table) * srt); +RTT (mma_rule) * +RT (session_rule_free) (RTT (mma_rules_table) * srt, RTT (mma_rule) * rule); +RTT (mma_rule) * +RT (mma_table_get_rule) (RTT (mma_rules_table) * srt, u32 srt_index); +u32 +RT (mma_table_rule_index) (RTT (mma_rules_table) * srt, RTT (mma_rule) * sr); +#endif /* SRC_VNET_SESSION_MMA_TEMPLATE_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/session.api b/src/vnet/session/session.api index fee98c2c8ff..e6f3b02005d 100644 --- a/src/vnet/session/session.api +++ b/src/vnet/session/session.api @@ -359,6 +359,44 @@ autoreply define app_namespace_add_del { u8 namespace_id[64]; }; +/** \brief add/del session rule + @param client_index - opaque cookie to identify the sender + client to vpp direction only + @param context - sender context, to match reply w/ request + @param transport_proto - transport protocol (0 - tcp 1 - udp) + @param is_ip4 - flag to indicate if ip addresses are ip4 or 6 + @param lcl_ip - local ip + @param lcl_plen - local prefix length + @param rmt_ip - remote ip + @param rmt_ple - remote prefix length + @param lcl_port - local port + @param rmt_port - remote port + @param action_index - the only action defined now is forward to + application with index action_index + @param is_add - flag to indicate if add or del + @param appns_index - application namespace where rule is to be applied + to + @param scope - flag that indicates scope of the rule: global or local. + If 0, default is global, 1 is global 2 is local, 3 is + both +*/ +autoreply define session_rule_add_del { + u32 client_index; + u32 context; + u8 transport_proto; + u8 is_ip4; + u8 lcl_ip[16]; + u8 lcl_plen; + u8 rmt_ip[16]; + u8 rmt_plen; + u16 lcl_port; + u16 rmt_port; + u32 action_index; + u8 is_add; + u32 appns_index; + u8 scope; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/session/session_api.c b/src/vnet/session/session_api.c index 181312cc952..32ef34b0952 100755 --- a/src/vnet/session/session_api.c +++ b/src/vnet/session/session_api.c @@ -16,9 +16,10 @@ #include <vnet/vnet.h> #include <vlibmemory/api.h> #include <vnet/session/application.h> +#include <vnet/session/application_interface.h> +#include <vnet/session/session_rules_table.h> #include <vnet/vnet_msg_enum.h> -#include "application_interface.h" #define vl_typedefs /* define message structures */ #include <vnet/vnet_all_api_h.h> @@ -52,6 +53,7 @@ _(UNBIND_SOCK, unbind_sock) \ _(CONNECT_SOCK, connect_sock) \ _(SESSION_ENABLE_DISABLE, session_enable_disable) \ _(APP_NAMESPACE_ADD_DEL, app_namespace_add_del) \ +_(SESSION_RULE_ADD_DEL, session_rule_add_del) \ static int send_add_segment_callback (u32 api_client_index, const u8 * segment_name, @@ -778,6 +780,42 @@ done: REPLY_MACRO (VL_API_APP_NAMESPACE_ADD_DEL_REPLY); } +static void +vl_api_session_rule_add_del_t_handler (vl_api_session_rule_add_del_t * mp) +{ + vl_api_session_rule_add_del_reply_t *rmp; + session_rule_add_del_args_t args; + session_rule_table_add_del_args_t *table_args = &args.table_args; + clib_error_t *error; + u8 fib_proto; + int rv = 0; + + fib_proto = mp->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; + + table_args->lcl.fp_len = mp->lcl_plen; + table_args->lcl.fp_proto = fib_proto; + table_args->rmt.fp_len = mp->rmt_plen; + table_args->rmt.fp_proto = fib_proto; + table_args->lcl_port = clib_net_to_host_u16 (mp->lcl_port); + table_args->rmt_port = clib_net_to_host_u16 (mp->rmt_port); + table_args->action_index = clib_net_to_host_u32 (mp->action_index); + table_args->is_add = mp->is_add; + args.appns_index = clib_net_to_host_u32 (mp->appns_index); + args.scope = mp->scope; + + memset (&table_args->lcl.fp_addr, 0, sizeof (table_args->lcl.fp_addr)); + memset (&table_args->rmt.fp_addr, 0, sizeof (table_args->rmt.fp_addr)); + ip_set (&table_args->lcl.fp_addr, mp->lcl_ip, mp->is_ip4); + ip_set (&table_args->rmt.fp_addr, mp->rmt_ip, mp->is_ip4); + error = vnet_session_rule_add_del (&args); + if (error) + { + rv = clib_error_get_code (error); + clib_error_report (error); + } + REPLY_MACRO (VL_API_SESSION_RULE_ADD_DEL_REPLY); +} + static clib_error_t * application_reaper_cb (u32 client_index) { diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c index 740c5a6d533..2168c61257c 100644 --- a/src/vnet/session/session_lookup.c +++ b/src/vnet/session/session_lookup.c @@ -339,12 +339,56 @@ session_lookup_del_session (stream_session_t * s) return session_lookup_del_connection (ts); } +static stream_session_t * +session_lookup_app_listen_session (u32 app_index) +{ + application_t *app; + app = application_get (app_index); + if (!app) + return 0; + + if (application_n_listeners (app) != 1) + { + clib_warning ("there should be one and only one listener %d", + hash_elts (app->listeners_table)); + return 0; + } + + return application_first_listener (app); +} + +stream_session_t * +session_lookup_rules_table4 (session_rules_table_t * srt, u8 proto, + ip4_address_t * lcl, u16 lcl_port, + ip4_address_t * rmt, u16 rmt_port) +{ + u32 action_index; + action_index = session_rules_table_lookup4 (srt, proto, lcl, rmt, lcl_port, + rmt_port); + /* Nothing sophisticated for now, action index is app index */ + return session_lookup_app_listen_session (action_index); +} + +stream_session_t * +session_lookup_rules_table6 (session_rules_table_t * srt, u8 proto, + ip6_address_t * lcl, u16 lcl_port, + ip6_address_t * rmt, u16 rmt_port) +{ + u32 action_index; + action_index = session_rules_table_lookup6 (srt, proto, lcl, rmt, lcl_port, + rmt_port); + return session_lookup_app_listen_session (action_index); +} + u64 session_lookup_session_endpoint (u32 table_index, session_endpoint_t * sep) { session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + ip4_address_t lcl4; + ip6_address_t lcl6; + u32 si; int rv; st = session_table_get (table_index); @@ -357,6 +401,13 @@ session_lookup_session_endpoint (u32 table_index, session_endpoint_t * sep) rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) return kv4.value; + + memset (&lcl4, 0, sizeof (lcl4)); + si = + session_rules_table_lookup4 (&st->session_rules, sep->transport_proto, + &lcl4, &sep->ip.ip4, 0, sep->port); + if (si != SESSION_RULES_TABLE_INVALID_INDEX) + return si; } else { @@ -365,6 +416,13 @@ session_lookup_session_endpoint (u32 table_index, session_endpoint_t * sep) rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) return kv6.value; + + memset (&lcl6, 0, sizeof (lcl6)); + si = + session_rules_table_lookup6 (&st->session_rules, sep->transport_proto, + &lcl6, &sep->ip.ip6, 0, sep->port); + if (si != SESSION_RULES_TABLE_INVALID_INDEX) + return si; } return SESSION_INVALID_HANDLE; } @@ -375,6 +433,8 @@ session_lookup_global_session_endpoint (session_endpoint_t * sep) session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + ip4_address_t lcl4; + ip6_address_t lcl6; u8 fib_proto; u32 table_index; int rv; @@ -391,6 +451,10 @@ session_lookup_global_session_endpoint (session_endpoint_t * sep) rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) return session_get_from_handle (kv4.value); + memset (&lcl4, 0, sizeof (lcl4)); + return session_lookup_rules_table4 (&st->session_rules, + sep->transport_proto, &lcl4, 0, + &sep->ip.ip4, sep->port); } else { @@ -399,8 +463,11 @@ session_lookup_global_session_endpoint (session_endpoint_t * sep) rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) return session_get_from_handle (kv6.value); + memset (&lcl6, 0, sizeof (lcl6)); + return session_lookup_rules_table6 (&st->session_rules, + sep->transport_proto, &lcl6, 0, + &sep->ip.ip6, sep->port); } - return 0; } u32 @@ -410,6 +477,9 @@ session_lookup_local_session_endpoint (u32 table_index, session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + ip4_address_t lcl4; + ip6_address_t lcl6; + u32 si; int rv; st = session_table_get (table_index); @@ -431,6 +501,13 @@ session_lookup_local_session_endpoint (u32 table_index, rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) return (u32) kv4.value; + + memset (&lcl4, 0, sizeof (lcl4)); + si = + session_rules_table_lookup4 (&st->session_rules, sep->transport_proto, + &lcl4, &sep->ip.ip4, 0, sep->port); + if (si != SESSION_RULES_TABLE_INVALID_INDEX) + return si; } else { @@ -447,6 +524,13 @@ session_lookup_local_session_endpoint (u32 table_index, rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) return (u32) kv6.value; + + memset (&lcl6, 0, sizeof (lcl6)); + si = + session_rules_table_lookup6 (&st->session_rules, sep->transport_proto, + &lcl6, &sep->ip.ip6, 0, sep->port); + if (si != SESSION_RULES_TABLE_INVALID_INDEX) + return si; } return SESSION_INVALID_INDEX; } @@ -625,6 +709,30 @@ session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4) return 0; } +transport_connection_t * +session_lookup_rules_table_connection4 (session_rules_table_t * srt, u8 proto, + ip4_address_t * lcl, u16 lcl_port, + ip4_address_t * rmt, u16 rmt_port) +{ + stream_session_t *s; + s = session_lookup_rules_table4 (srt, proto, lcl, lcl_port, rmt, rmt_port); + if (s) + return tp_vfts[s->session_type].get_listener (s->connection_index); + return 0; +} + +transport_connection_t * +session_lookup_rules_table_connection6 (session_rules_table_t * srt, u8 proto, + ip6_address_t * lcl, u16 lcl_port, + ip6_address_t * rmt, u16 rmt_port) +{ + stream_session_t *s; + s = session_lookup_rules_table6 (srt, proto, lcl, lcl_port, rmt, rmt_port); + if (s) + return tp_vfts[s->session_type].get_listener (s->connection_index); + return 0; +} + /** * Lookup connection with ip4 and transport layer information * @@ -637,6 +745,7 @@ session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4) * - Try to find a fully-formed or local source wildcarded (listener bound to * all interfaces) listener session * - Try to find a half-open connection + * - Try session rules table * - return 0 * * @param fib_index index of fib wherein the connection was received @@ -679,14 +788,18 @@ session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl, if (s) return tp_vfts[s->session_type].get_listener (s->connection_index); - /* Finally, try half-open connections */ + /* Try half-open connections */ rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4); if (rv == 0) { u32 sst = session_type_from_proto_and_ip (proto, 1); return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF); } - return 0; + + /* Check the session rules table */ + return session_lookup_rules_table_connection4 (&st->session_rules, proto, + lcl, lcl_port, rmt, + rmt_port); } /** @@ -741,7 +854,10 @@ session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl, u32 sst = session_type_from_proto_and_ip (proto, 1); return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF); } - return 0; + /* Check the session rules table */ + return session_lookup_rules_table_connection4 (&st->session_rules, proto, + lcl, lcl_port, rmt, + rmt_port); } /** @@ -779,7 +895,8 @@ session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt, /* If nothing is found, check if any listener is available */ if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto))) return s; - return 0; + return session_lookup_rules_table4 (&st->session_rules, proto, lcl, + lcl_port, rmt, rmt_port); } /** @@ -794,6 +911,7 @@ session_lookup_safe4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt, * - Try to find a fully-formed or local source wildcarded (listener bound to * all interfaces) listener session * - Try to find a half-open connection + * - Try session rules table * - return 0 * * @param fib_index index of the fib wherein the connection was received @@ -843,7 +961,9 @@ session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl, return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF); } - return 0; + return session_lookup_rules_table_connection6 (&st->session_rules, proto, + lcl, lcl_port, rmt, + rmt_port); } /** @@ -898,7 +1018,9 @@ session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl, return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF); } - return 0; + return session_lookup_rules_table_connection6 (&st->session_rules, proto, + lcl, lcl_port, rmt, + rmt_port); } /** @@ -935,7 +1057,8 @@ session_lookup_safe6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt, /* If nothing is found, check if any listener is available */ if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto))) return s; - return 0; + return session_lookup_rules_table6 (&st->session_rules, proto, lcl, + lcl_port, rmt, rmt_port); } u64 @@ -969,6 +1092,39 @@ session_lookup_local_listener_parse_handle (u64 handle, return 0; } +clib_error_t * +vnet_session_rule_add_del (session_rule_add_del_args_t * args) +{ + app_namespace_t *app_ns = app_namespace_get (args->appns_index); + session_table_t *st; + u32 fib_index; + u8 fib_proto; + clib_error_t *error; + + if (!app_ns) + return clib_error_return_code (0, VNET_API_ERROR_APP_INVALID_NS, 0, + "invalid app ns"); + if (args->scope > 3) + return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0, + "invalid scope"); + if ((args->scope & SESSION_RULE_SCOPE_GLOBAL) || args->scope == 0) + { + fib_proto = args->table_args.rmt.fp_proto; + fib_index = app_namespace_get_fib_index (app_ns, fib_proto); + st = session_table_get_for_fib_index (fib_proto, fib_index); + if ((error = session_rules_table_add_del (&st->session_rules, + &args->table_args))) + return error; + } + if (args->scope & SESSION_RULE_SCOPE_LOCAL) + { + st = app_namespace_get_local_table (app_ns); + error = + session_rules_table_add_del (&st->session_rules, &args->table_args); + } + return error; +} + u8 * format_ip4_session_lookup_kvp (u8 * s, va_list * args) { @@ -1039,6 +1195,204 @@ session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table, } } +static clib_error_t * +session_rule_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen, rmt_plen; + u32 appns_index, scope = 0; + ip46_address_t lcl_ip, rmt_ip; + u8 is_ip4 = 1, conn_set = 0; + u8 fib_proto, is_add = 1, *ns_id = 0; + app_namespace_t *app_ns; + + memset (&lcl_ip, 0, sizeof (lcl_ip)); + memset (&rmt_ip, 0, sizeof (rmt_ip)); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_add = 0; + else if (unformat (input, "add")) + ; + else if (unformat (input, "appns %_%v%_", &ns_id)) + ; + else if (unformat (input, "scope global")) + scope = SESSION_RULE_SCOPE_GLOBAL; + else if (unformat (input, "scope local")) + scope = SESSION_RULE_SCOPE_LOCAL; + else if (unformat (input, "scope all")) + scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL; + else if (unformat (input, "proto %U", unformat_transport_proto, &proto)) + ; + else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address, + &lcl_ip.ip4, &lcl_plen, &lcl_port, + unformat_ip4_address, &rmt_ip.ip4, &rmt_plen, + &rmt_port)) + { + is_ip4 = 1; + conn_set = 1; + } + else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address, + &lcl_ip.ip6, &lcl_plen, &lcl_port, + unformat_ip6_address, &rmt_ip.ip6, &rmt_plen, + &rmt_port)) + { + is_ip4 = 0; + conn_set = 1; + } + else if (unformat (input, "action %d", &action)) + ; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + if (proto == ~0 || !conn_set || action == ~0) + return clib_error_return (0, "proto, connection and action must be set"); + + if (ns_id) + { + app_ns = app_namespace_get_from_id (ns_id); + if (!app_ns) + return clib_error_return (0, "namespace %v does not exist", ns_id); + } + else + { + app_ns = app_namespace_get_default (); + } + appns_index = app_namespace_index (app_ns); + + fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; + session_rule_add_del_args_t args = { + .table_args.lcl.fp_addr = lcl_ip, + .table_args.lcl.fp_len = lcl_plen, + .table_args.lcl.fp_proto = fib_proto, + .table_args.rmt.fp_addr = rmt_ip, + .table_args.rmt.fp_len = rmt_plen, + .table_args.rmt.fp_proto = fib_proto, + .table_args.lcl_port = lcl_port, + .table_args.rmt_port = rmt_port, + .table_args.action_index = action, + .table_args.is_add = is_add, + .appns_index = appns_index, + .scope = scope, + }; + return vnet_session_rule_add_del (&args); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (session_rule_command, static) = +{ + .path = "session rule", + .short_help = "session rule [add|del] appns <ns_id> proto <proto> " + "<lcl-ip/plen> <lcl-port> <rmt-ip/plen> <rmt-port> action <action>", + .function = session_rule_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_session_rules_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u32 transport_proto = ~0, lcl_port, rmt_port, lcl_plen, rmt_plen; + u32 fib_index, scope = 0; + ip46_address_t lcl_ip, rmt_ip; + u8 is_ip4 = 1, show_one = 0; + app_namespace_t *app_ns; + session_table_t *st; + u8 *ns_id = 0, fib_proto; + + memset (&lcl_ip, 0, sizeof (lcl_ip)); + memset (&rmt_ip, 0, sizeof (rmt_ip)); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_transport_proto, &transport_proto)) + ; + else if (unformat (input, "appns %_%v%_", &ns_id)) + ; + else if (unformat (input, "scope global")) + scope = 1; + else if (unformat (input, "scope local")) + scope = 2; + else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip4_address, + &lcl_ip.ip4, &lcl_plen, &lcl_port, + unformat_ip4_address, &rmt_ip.ip4, &rmt_plen, + &rmt_port)) + { + is_ip4 = 1; + show_one = 1; + } + else if (unformat (input, "%U/%d %d %U/%d %d", unformat_ip6_address, + &lcl_ip.ip6, &lcl_plen, &lcl_port, + unformat_ip6_address, &rmt_ip.ip6, &rmt_plen, + &rmt_port)) + { + is_ip4 = 0; + show_one = 1; + } + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + if (transport_proto == ~0) + { + vlib_cli_output (vm, "transport proto must be set"); + return 0; + } + + if (ns_id) + { + app_ns = app_namespace_get_from_id (ns_id); + if (!app_ns) + { + vlib_cli_output (vm, "appns %v doesn't exist", ns_id); + return 0; + } + } + else + { + app_ns = app_namespace_get_default (); + } + + if (scope == 1 || scope == 0) + { + fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; + fib_index = is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index; + st = session_table_get_for_fib_index (fib_proto, fib_index); + } + else + { + st = app_namespace_get_local_table (app_ns); + } + + if (show_one) + { + session_rules_table_show_rule (vm, &st->session_rules, transport_proto, + &lcl_ip, lcl_port, &rmt_ip, rmt_port, + is_ip4); + return 0; + } + + session_rules_table_cli_dump (vm, &st->session_rules, FIB_PROTOCOL_IP4, + transport_proto); + session_rules_table_cli_dump (vm, &st->session_rules, FIB_PROTOCOL_IP6, + transport_proto); + + vec_free (ns_id); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_session_rules_command, static) = +{ + .path = "show session rules", + .short_help = "show session rules [appns <id> proto <proto> <lcl-ip/plen>" + " <lcl-port> <rmt-ip/plen> <rmt-port>]", + .function = show_session_rules_command_fn, +}; +/* *INDENT-ON* */ + void session_lookup_init (void) { diff --git a/src/vnet/session/session_lookup.h b/src/vnet/session/session_lookup.h index 449f8f4e2d2..46af302d12c 100644 --- a/src/vnet/session/session_lookup.h +++ b/src/vnet/session/session_lookup.h @@ -85,6 +85,32 @@ int session_lookup_local_listener_parse_handle (u64 handle, void session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table, u8 type, u8 is_local); + +enum _session_rule_scope +{ + SESSION_RULE_SCOPE_GLOBAL = 1, + SESSION_RULE_SCOPE_LOCAL = 2, +} session_rule_scope_e; + +typedef struct _session_rule_add_del_args +{ + /** + * Actual arguments to adding the rule to a session rules table + */ + session_rule_table_add_del_args_t table_args; + /** + * Application namespace where rule should be applied. If 0, + * default namespace is used. + */ + u32 appns_index; + /** + * Rule scope flag. + */ + u8 scope; +} session_rule_add_del_args_t; + +clib_error_t *vnet_session_rule_add_del (session_rule_add_del_args_t * args); + void session_lookup_init (void); #endif /* SRC_VNET_SESSION_SESSION_LOOKUP_H_ */ diff --git a/src/vnet/session/session_rules_table.c b/src/vnet/session/session_rules_table.c new file mode 100644 index 00000000000..fb433114cf2 --- /dev/null +++ b/src/vnet/session/session_rules_table.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include <vnet/session/mma_16.h> +#include <vnet/session/mma_template.c> +#include <vnet/session/mma_40.h> +#include <vnet/session/mma_template.c> +#include <vnet/session/session_rules_table.h> +#include <vnet/session/transport.h> + +static void +fib_pref_normalize (fib_prefix_t * pref) +{ + if (pref->fp_proto == FIB_PROTOCOL_IP4) + ip4_address_normalize (&pref->fp_addr.ip4, pref->fp_len); + else + ip6_address_normalize (&pref->fp_addr.ip6, pref->fp_len); +} + +u8 * +format_session_rule4 (u8 * s, va_list * args) +{ + mma_rules_table_16_t *srt = va_arg (*args, mma_rules_table_16_t *); + mma_rule_16_t *sr = va_arg (*args, mma_rule_16_t *); + session_mask_or_match_4_t *mask, *match; + int i; + + match = (session_mask_or_match_4_t *) & sr->match; + mask = (session_mask_or_match_4_t *) & sr->mask; + + s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d", + mma_rules_table_rule_index_16 (srt, sr), format_ip4_address, + &match->lcl_ip, + ip4_mask_to_preflen (&mask->lcl_ip), + match->lcl_port, format_ip4_address, &match->rmt_ip, + ip4_mask_to_preflen (&mask->rmt_ip), + match->rmt_port, sr->action_index); + if (vec_len (sr->next_indices)) + { + s = format (s, "\n children: "); + for (i = 0; i < vec_len (sr->next_indices); i++) + s = format (s, "%d ", sr->next_indices[i]); + } + return s; +} + +u8 * +format_session_rule6 (u8 * s, va_list * args) +{ + mma_rules_table_40_t *srt = va_arg (*args, mma_rules_table_40_t *); + mma_rule_40_t *sr = va_arg (*args, mma_rule_40_t *); + session_mask_or_match_6_t *mask, *match; + int i; + + match = (session_mask_or_match_6_t *) & sr->match; + mask = (session_mask_or_match_6_t *) & sr->mask; + + s = format (s, "[%d] rule: %U/%d %d %U/%d %d action: %d", + mma_rules_table_rule_index_40 (srt, sr), format_ip6_address, + &match->lcl_ip, ip6_mask_to_preflen (&mask->lcl_ip), + match->lcl_port, format_ip6_address, &match->rmt_ip, + ip6_mask_to_preflen (&mask->rmt_ip), match->rmt_port, + sr->action_index); + if (vec_len (sr->next_indices)) + { + s = format (s, "\n children: "); + for (i = 0; i < vec_len (sr->next_indices); i++) + s = format (s, "%d ", sr->next_indices[i]); + } + return s; +} + +void * +session_rules_table_get (session_rules_table_t * srt, u8 transport_proto, + u8 fib_proto) +{ + if (fib_proto == FIB_PROTOCOL_IP4) + return &srt->session_rules_tables_16[transport_proto]; + else if (fib_proto == FIB_PROTOCOL_IP6) + return &srt->session_rules_tables_40[transport_proto]; + return 0; +} + +int +rule_cmp_16 (mma_rule_16_t * rule1, mma_rule_16_t * rule2) +{ + session_mask_or_match_4_t *m1, *m2; + + m1 = (session_mask_or_match_4_t *) & rule1->max_match; + m2 = (session_mask_or_match_4_t *) & rule2->max_match; + if (m1->rmt_ip.as_u32 != m2->rmt_ip.as_u32) + return (m1->rmt_ip.as_u32 < m2->rmt_ip.as_u32 ? -1 : 1); + if (m1->lcl_ip.as_u32 != m2->lcl_ip.as_u32) + return (m1->lcl_ip.as_u32 < m2->lcl_ip.as_u32 ? -1 : 1); + if (m1->rmt_port != m2->rmt_port) + return (m1->rmt_port < m2->rmt_port ? -1 : 1); + if (m1->lcl_port != m2->lcl_port) + return (m1->lcl_port < m2->lcl_port ? -1 : 1); + return 0; +} + +int +rule_cmp_40 (mma_rule_40_t * rule1, mma_rule_40_t * rule2) +{ + session_mask_or_match_6_t *r1, *r2; + r1 = (session_mask_or_match_6_t *) & rule1->max_match; + r2 = (session_mask_or_match_6_t *) & rule2->max_match; + if (r1->rmt_ip.as_u64[0] != r2->rmt_ip.as_u64[0]) + return (r1->rmt_ip.as_u64[0] < r2->rmt_ip.as_u64[0] ? -1 : 1); + if (r1->rmt_ip.as_u64[1] != r2->rmt_ip.as_u64[1]) + return (r1->rmt_ip.as_u64[1] < r2->rmt_ip.as_u64[1] ? -1 : 1); + if (r1->lcl_ip.as_u64[0] != r2->lcl_ip.as_u64[0]) + return (r1->lcl_ip.as_u64[0] < r2->lcl_ip.as_u64[0] ? -1 : 1); + if (r1->lcl_ip.as_u64[1] != r2->lcl_ip.as_u64[1]) + return (r1->lcl_ip.as_u64[1] < r2->lcl_ip.as_u64[1]) ? -1 : 1; + if (r1->rmt_port != r2->rmt_port) + return (r1->rmt_port < r2->rmt_port ? -1 : 1); + if (r1->lcl_port != r2->lcl_port) + return (r1->lcl_port < r2->lcl_port ? -1 : 1); + return 0; +} + +void +session_rules_table_init_rule_16 (mma_rule_16_t * rule, + fib_prefix_t * lcl, u16 lcl_port, + fib_prefix_t * rmt, u16 rmt_port) +{ + session_mask_or_match_4_t *match, *mask, *max_match; + fib_pref_normalize (lcl); + fib_pref_normalize (rmt); + match = (session_mask_or_match_4_t *) & rule->match; + match->lcl_ip.as_u32 = lcl->fp_addr.ip4.as_u32; + match->rmt_ip.as_u32 = rmt->fp_addr.ip4.as_u32; + match->lcl_port = lcl_port; + match->rmt_port = rmt_port; + mask = (session_mask_or_match_4_t *) & rule->mask; + ip4_preflen_to_mask (lcl->fp_len, &mask->lcl_ip); + ip4_preflen_to_mask (rmt->fp_len, &mask->rmt_ip); + mask->lcl_port = lcl_port == 0 ? 0 : (u16) ~ 0; + mask->rmt_port = rmt_port == 0 ? 0 : (u16) ~ 0; + max_match = (session_mask_or_match_4_t *) & rule->max_match; + ip4_prefix_max_address_host_order (&rmt->fp_addr.ip4, rmt->fp_len, + &max_match->rmt_ip); + ip4_prefix_max_address_host_order (&lcl->fp_addr.ip4, lcl->fp_len, + &max_match->lcl_ip); + max_match->lcl_port = lcl_port == 0 ? (u16) ~ 0 : lcl_port; + max_match->rmt_port = rmt_port == 0 ? (u16) ~ 0 : rmt_port; +} + +void +session_rules_table_init_rule_40 (mma_rule_40_t * rule, + fib_prefix_t * lcl, u16 lcl_port, + fib_prefix_t * rmt, u16 rmt_port) +{ + session_mask_or_match_6_t *match, *mask, *max_match; + fib_pref_normalize (lcl); + fib_pref_normalize (rmt); + match = (session_mask_or_match_6_t *) & rule->match; + clib_memcpy (&match->lcl_ip, &lcl->fp_addr.ip6, sizeof (match->lcl_ip)); + clib_memcpy (&match->rmt_ip, &rmt->fp_addr.ip6, sizeof (match->rmt_ip)); + match->lcl_port = lcl_port; + match->rmt_port = rmt_port; + mask = (session_mask_or_match_6_t *) & rule->mask; + ip6_preflen_to_mask (lcl->fp_len, &mask->lcl_ip); + ip6_preflen_to_mask (rmt->fp_len, &mask->rmt_ip); + mask->lcl_port = lcl_port == 0 ? 0 : (u16) ~ 0; + mask->rmt_port = rmt_port == 0 ? 0 : (u16) ~ 0; + max_match = (session_mask_or_match_6_t *) & rule->max_match; + ip6_prefix_max_address_host_order (&rmt->fp_addr.ip6, rmt->fp_len, + &max_match->rmt_ip); + ip6_prefix_max_address_host_order (&lcl->fp_addr.ip6, lcl->fp_len, + &max_match->lcl_ip); + max_match->lcl_port = lcl_port == 0 ? (u16) ~ 0 : lcl_port; + max_match->rmt_port = rmt_port == 0 ? (u16) ~ 0 : rmt_port; +} + +mma_rule_16_t * +session_rules_table_alloc_rule_16 (mma_rules_table_16_t * srt, + fib_prefix_t * lcl, u16 lcl_port, + fib_prefix_t * rmt, u16 rmt_port) +{ + mma_rule_16_t *rule = 0; + rule = mma_rules_table_rule_alloc_16 (srt); + session_rules_table_init_rule_16 (rule, lcl, lcl_port, rmt, rmt_port); + return rule; +} + +mma_rule_40_t * +session_rules_table_alloc_rule_40 (mma_rules_table_40_t * srt, + fib_prefix_t * lcl, u16 lcl_port, + fib_prefix_t * rmt, u16 rmt_port) +{ + mma_rule_40_t *rule; + rule = mma_rules_table_rule_alloc_40 (srt); + session_rules_table_init_rule_40 (rule, lcl, lcl_port, rmt, rmt_port); + return rule; +} + +clib_error_t * +session_rules_table_add_del (session_rules_table_t * srt, + session_rule_table_add_del_args_t * args) +{ + u8 fib_proto = args->rmt.fp_proto; + + if (args->transport_proto != TRANSPORT_PROTO_TCP + && args->transport_proto != TRANSPORT_PROTO_UDP) + return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0, + "invalid transport proto"); + + if (fib_proto == FIB_PROTOCOL_IP4) + { + mma_rules_table_16_t *srt4; + srt4 = &srt->session_rules_tables_16[args->transport_proto]; + if (args->is_add) + { + mma_rule_16_t *rule; + rule = session_rules_table_alloc_rule_16 (srt4, &args->lcl, + args->lcl_port, + &args->rmt, + args->rmt_port); + rule->action_index = args->action_index; + mma_rules_table_add_rule_16 (srt4, rule); + } + else + { + mma_rule_16_t rule; + memset (&rule, 0, sizeof (rule)); + session_rules_table_init_rule_16 (&rule, &args->lcl, args->lcl_port, + &args->rmt, args->rmt_port); + mma_rules_table_del_rule_16 (srt4, &rule, srt4->root_index); + } + } + else if (fib_proto == FIB_PROTOCOL_IP6) + { + mma_rules_table_40_t *srt6; + mma_rule_40_t *rule; + srt6 = &srt->session_rules_tables_40[args->transport_proto]; + if (args->is_add) + { + rule = session_rules_table_alloc_rule_40 (srt6, &args->lcl, + args->lcl_port, + &args->rmt, + args->rmt_port); + rule->action_index = args->action_index; + mma_rules_table_add_rule_40 (srt6, rule); + } + else + { + mma_rule_40_t rule; + memset (&rule, 0, sizeof (rule)); + session_rules_table_init_rule_40 (&rule, &args->lcl, args->lcl_port, + &args->rmt, args->rmt_port); + mma_rules_table_del_rule_40 (srt6, &rule, srt6->root_index); + } + } + else + return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE_2, 0, + "invalid fib proto"); + return 0; +} + +u32 +session_rules_table_lookup4 (session_rules_table_t * srt, u8 transport_proto, + ip4_address_t * lcl_ip, ip4_address_t * rmt_ip, + u16 lcl_port, u16 rmt_port) +{ + mma_rules_table_16_t *srt4 = &srt->session_rules_tables_16[transport_proto]; + session_mask_or_match_4_t key = { + .lcl_ip.as_u32 = lcl_ip->as_u32, + .rmt_ip.as_u32 = rmt_ip->as_u32, + .lcl_port = lcl_port, + .rmt_port = rmt_port, + }; + return mma_rules_table_lookup_16 (srt4, + (mma_mask_or_match_16_t *) & key, + srt4->root_index); +} + +u32 +session_rules_table_lookup6 (session_rules_table_t * srt, u8 transport_proto, + ip6_address_t * lcl_ip, ip6_address_t * rmt_ip, + u16 lcl_port, u16 rmt_port) +{ + mma_rules_table_40_t *srt6 = &srt->session_rules_tables_40[transport_proto]; + session_mask_or_match_6_t key = { + .lcl_port = lcl_port, + .rmt_port = rmt_port, + }; + clib_memcpy (&key.lcl_ip, &lcl_ip, sizeof (&lcl_ip)); + clib_memcpy (&key.rmt_ip, &rmt_ip, sizeof (&rmt_ip)); + return mma_rules_table_lookup_40 (srt6, + (mma_mask_or_match_40_t *) & key, + srt6->root_index); +} + +void +session_rules_table_init (session_rules_table_t * srt) +{ + mma_rules_table_16_t *srt4; + mma_rules_table_40_t *srt6; + mma_rule_16_t *rule4; + mma_rule_40_t *rule6; + fib_prefix_t null_prefix; + int i; + + memset (&null_prefix, 0, sizeof (null_prefix)); + + for (i = 0; i < TRANSPORT_N_PROTO; i++) + { + srt4 = &srt->session_rules_tables_16[i]; + rule4 = session_rules_table_alloc_rule_16 (srt4, &null_prefix, 0, + &null_prefix, 0); + rule4->action_index = SESSION_RULES_TABLE_INVALID_INDEX; + srt4->root_index = mma_rules_table_rule_index_16 (srt4, rule4); + srt4->rule_cmp_fn = rule_cmp_16; + } + + for (i = 0; i < TRANSPORT_N_PROTO; i++) + { + srt6 = &srt->session_rules_tables_40[i];; + rule6 = session_rules_table_alloc_rule_40 (srt6, &null_prefix, 0, + &null_prefix, 0); + rule6->action_index = SESSION_RULES_TABLE_INVALID_INDEX; + srt6->root_index = mma_rules_table_rule_index_40 (srt6, rule6); + srt6->rule_cmp_fn = rule_cmp_40; + } +} + +void +session_rules_table_show_rule (vlib_main_t * vm, session_rules_table_t * srt, + u8 transport_proto, ip46_address_t * lcl_ip, + u16 lcl_port, ip46_address_t * rmt_ip, + u16 rmt_port, u8 is_ip4) +{ + mma_rules_table_16_t *srt4; + mma_rules_table_40_t *srt6; + mma_rule_16_t *sr4; + mma_rule_40_t *sr6; + u32 ri; + + if (is_ip4) + { + srt4 = session_rules_table_get (srt, transport_proto, FIB_PROTOCOL_IP4); + session_mask_or_match_4_t key = { + .lcl_ip.as_u32 = lcl_ip->ip4.as_u32, + .rmt_ip.as_u32 = rmt_ip->ip4.as_u32, + .lcl_port = lcl_port, + .rmt_port = rmt_port, + }; + ri = + mma_rules_table_lookup_rule_16 (srt4, + (mma_mask_or_match_16_t *) & key, + srt4->root_index); + sr4 = mma_rules_table_get_rule_16 (srt4, ri); + vlib_cli_output (vm, "%U", format_session_rule4, srt4, sr4); + } + else + { + srt6 = session_rules_table_get (srt, transport_proto, FIB_PROTOCOL_IP6); + session_mask_or_match_6_t key = { + .lcl_port = lcl_port, + .rmt_port = rmt_port, + }; + clib_memcpy (&key.lcl_ip, &lcl_ip->ip6, sizeof (&lcl_ip->ip6)); + clib_memcpy (&key.rmt_ip, &rmt_ip->ip6, sizeof (&rmt_ip->ip6)); + ri = + mma_rules_table_lookup_rule_40 (srt6, + (mma_mask_or_match_40_t *) & + key, srt6->root_index); + sr6 = mma_rules_table_get_rule_40 (srt6, ri); + vlib_cli_output (vm, "%U", format_session_rule6, srt6, sr6); + } +} + +void +session_rules_table_cli_dump (vlib_main_t * vm, session_rules_table_t * srt, + u8 fib_proto, u8 transport_proto) +{ + if (fib_proto == FIB_PROTOCOL_IP4) + { + mma_rules_table_16_t *srt4; + mma_rule_16_t *sr4; + srt4 = &srt->session_rules_tables_16[transport_proto]; + vlib_cli_output (vm, "%U IP4 rules table", format_transport_proto, + transport_proto); + + /* *INDENT-OFF* */ + pool_foreach(sr4, srt4->rules, ({ + vlib_cli_output (vm, "%U", format_session_rule4, srt4, sr4); + })); + /* *INDENT-ON* */ + + } + else if (fib_proto == FIB_PROTOCOL_IP6) + { + mma_rules_table_40_t *srt6; + mma_rule_40_t *sr6; + srt6 = &srt->session_rules_tables_40[transport_proto]; + vlib_cli_output (vm, "\n%U IP6 rules table", format_transport_proto, + transport_proto); + + /* *INDENT-OFF* */ + pool_foreach(sr6, srt6->rules, ({ + vlib_cli_output (vm, "%U", format_session_rule6, srt6, sr6); + })); + /* *INDENT-ON* */ + + } +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/session_rules_table.h b/src/vnet/session/session_rules_table.h new file mode 100644 index 00000000000..e9d573a3b04 --- /dev/null +++ b/src/vnet/session/session_rules_table.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_VNET_SESSION_SESSION_RULES_TABLE_H_ +#define SRC_VNET_SESSION_SESSION_RULES_TABLE_H_ + +#include <vnet/vnet.h> +#include <vnet/fib/fib.h> +#include <vnet/session/transport.h> +#include <vnet/session/mma_16.h> +#include <vnet/session/mma_40.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + union + { + struct + { + ip4_address_t rmt_ip; + ip4_address_t lcl_ip; + u16 rmt_port; + u16 lcl_port; + }; + u64 as_u64[2]; + }; +}) session_mask_or_match_4_t; + +typedef CLIB_PACKED (struct +{ + union + { + struct + { + ip6_address_t rmt_ip; + ip6_address_t lcl_ip; + u16 rmt_port; + u16 lcl_port; + }; + u64 as_u64[5]; + }; +}) session_mask_or_match_6_t; +/* *INDENT-ON* */ + +typedef struct _session_rules_table_add_del_args +{ + u8 transport_proto; + fib_prefix_t lcl; + fib_prefix_t rmt; + u16 lcl_port; + u16 rmt_port; + u32 action_index; + u8 is_add; +} session_rule_table_add_del_args_t; + +typedef struct _session_rules_table_t +{ + /** + * Per fib proto and transport proto session rules tables + */ + mma_rules_table_16_t session_rules_tables_16[TRANSPORT_N_PROTO]; + mma_rules_table_40_t session_rules_tables_40[TRANSPORT_N_PROTO]; +} session_rules_table_t; + +void *session_rules_table_get (session_rules_table_t * srt, + u8 transport_proto, u8 fib_proto); +u32 session_rules_table_lookup4 (session_rules_table_t * srt, + u8 transport_proto, ip4_address_t * lcl_ip, + ip4_address_t * rmt_ip, u16 lcl_port, + u16 rmt_port); +u32 session_rules_table_lookup6 (session_rules_table_t * srt, + u8 transport_proto, ip6_address_t * lcl_ip, + ip6_address_t * rmt_ip, u16 lcl_port, + u16 rmt_port); +void session_rules_table_cli_dump (vlib_main_t * vm, + session_rules_table_t * srt, u8 fib_proto, + u8 transport_proto); +void session_rules_table_show_rule (vlib_main_t * vm, + session_rules_table_t * srt, + u8 transport_proto, + ip46_address_t * lcl_ip, u16 lcl_port, + ip46_address_t * rmt_ip, u16 rmt_port, + u8 is_ip4); +clib_error_t *session_rules_table_add_del (session_rules_table_t * srt, + session_rule_table_add_del_args_t * + args); +void session_rules_table_init (session_rules_table_t * srt); +#endif /* SRC_VNET_SESSION_SESSION_RULES_TABLE_H_ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/session_table.c b/src/vnet/session/session_table.c index 04c0c816ab0..cd8502aa13d 100644 --- a/src/vnet/session/session_table.c +++ b/src/vnet/session/session_table.c @@ -87,6 +87,8 @@ session_table_init (session_table_t * slt) clib_bihash_init_48_8 (&slt->v6_half_open_hash, "v6 half-open table", configured_v6_halfopen_table_buckets, configured_v6_halfopen_table_memory); + + session_rules_table_init (&slt->session_rules); } typedef struct _ip4_session_table_walk_ctx_t diff --git a/src/vnet/session/session_table.h b/src/vnet/session/session_table.h index 5e0564043f3..6588a43aa14 100644 --- a/src/vnet/session/session_table.h +++ b/src/vnet/session/session_table.h @@ -18,6 +18,7 @@ #include <vppinfra/bihash_16_8.h> #include <vppinfra/bihash_48_8.h> +#include <vnet/session/session_rules_table.h> typedef struct _session_lookup_table { @@ -32,6 +33,11 @@ typedef struct _session_lookup_table */ clib_bihash_16_8_t v4_half_open_hash; clib_bihash_48_8_t v6_half_open_hash; + + /** + * Per fib proto and transport proto session rules tables + */ + session_rules_table_t session_rules; } session_table_t; #define SESSION_TABLE_INVALID_INDEX ((u32)~0) diff --git a/src/vnet/session/session_test.c b/src/vnet/session/session_test.c index 986837cbf24..bdd4f052adc 100644 --- a/src/vnet/session/session_test.c +++ b/src/vnet/session/session_test.c @@ -17,6 +17,7 @@ #include <vnet/session/application_interface.h> #include <vnet/session/application.h> #include <vnet/session/session.h> +#include <vnet/session/session_rules_table.h> #define SESSION_TEST_I(_cond, _comment, _args...) \ ({ \ @@ -438,6 +439,463 @@ session_test_namespace (vlib_main_t * vm, unformat_input_t * input) return 0; } +static int +session_test_rule_table (vlib_main_t * vm, unformat_input_t * input) +{ + session_rules_table_t _srt, *srt = &_srt; + u16 lcl_port = 1234, rmt_port = 4321; + u32 action_index = 1, res; + ip4_address_t lcl_lkup, rmt_lkup; + clib_error_t *error; + int verbose = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "verbose")) + verbose = 1; + else + { + vlib_cli_output (vm, "parse error: '%U'", format_unformat_error, + input); + return -1; + } + } + + memset (srt, 0, sizeof (*srt)); + session_rules_table_init (srt); + + ip4_address_t lcl_ip = { + .as_u32 = clib_host_to_net_u32 (0x01020304), + }; + ip4_address_t rmt_ip = { + .as_u32 = clib_host_to_net_u32 (0x05060708), + }; + ip4_address_t lcl_ip2 = { + .as_u32 = clib_host_to_net_u32 (0x02020202), + }; + ip4_address_t rmt_ip2 = { + .as_u32 = clib_host_to_net_u32 (0x06060606), + }; + ip4_address_t lcl_ip3 = { + .as_u32 = clib_host_to_net_u32 (0x03030303), + }; + ip4_address_t rmt_ip3 = { + .as_u32 = clib_host_to_net_u32 (0x07070707), + }; + fib_prefix_t lcl_pref = { + .fp_addr.ip4.as_u32 = lcl_ip.as_u32, + .fp_len = 16, + .fp_proto = FIB_PROTOCOL_IP4, + }; + fib_prefix_t rmt_pref = { + .fp_addr.ip4.as_u32 = rmt_ip.as_u32, + .fp_len = 16, + .fp_proto = FIB_PROTOCOL_IP4, + }; + + session_rule_table_add_del_args_t args = { + .lcl = lcl_pref, + .rmt = rmt_pref, + .lcl_port = lcl_port, + .rmt_port = rmt_port, + .action_index = action_index++, + .is_add = 1, + }; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d", + action_index - 1); + + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 1), + "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 1: %d", + res); + + /* + * Add 1.2.3.4/24 1234 5.6.7.8/16 4321 and 1.2.3.4/24 1234 5.6.7.8/24 4321 + */ + args.lcl.fp_addr.ip4 = lcl_ip; + args.lcl.fp_len = 24; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/16 4321 action %d", + action_index - 1); + args.rmt.fp_addr.ip4 = rmt_ip; + args.rmt.fp_len = 24; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1234 5.6.7.8/24 4321 action %d", + action_index - 1); + + /* + * Add 2.2.2.2/24 1234 6.6.6.6/16 4321 and 3.3.3.3/24 1234 7.7.7.7/16 4321 + */ + args.lcl.fp_addr.ip4 = lcl_ip2; + args.lcl.fp_len = 24; + args.rmt.fp_addr.ip4 = rmt_ip2; + args.rmt.fp_len = 16; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 2.2.2.2/24 1234 6.6.6.6/16 4321 action %d", + action_index - 1); + args.lcl.fp_addr.ip4 = lcl_ip3; + args.rmt.fp_addr.ip4 = rmt_ip3; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 3.3.3.3/24 1234 7.7.7.7/16 4321 action %d", + action_index - 1); + + /* + * Add again 3.3.3.3/24 1234 7.7.7.7/16 4321 + */ + args.lcl.fp_addr.ip4 = lcl_ip3; + args.rmt.fp_addr.ip4 = rmt_ip3; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "overwrite 3.3.3.3/24 1234 7.7.7.7/16 4321 " + "action %d", action_index - 1); + + /* + * Lookup 1.2.3.4/32 1234 5.6.7.8/32 4321, 1.2.2.4/32 1234 5.6.7.9/32 4321 + * and 3.3.3.3 1234 7.7.7.7 4321 + */ + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 3), + "Lookup 1.2.3.4 1234 5.6.7.8 4321 action " "should be 3: %d", + res); + + lcl_lkup.as_u32 = clib_host_to_net_u32 (0x01020204); + rmt_lkup.as_u32 = clib_host_to_net_u32 (0x05060709); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_lkup, + &rmt_lkup, lcl_port, rmt_port); + SESSION_TEST ((res == 1), + "Lookup 1.2.2.4 1234 5.6.7.9 4321, action " "should be 1: %d", + res); + + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip3, &rmt_ip3, + lcl_port, rmt_port); + SESSION_TEST ((res == 6), + "Lookup 3.3.3.3 1234 7.7.7.7 4321, action " + "should be 6 (updated): %d", res); + + /* + * Add 1.2.3.4/24 * 5.6.7.8/24 * + * Lookup 1.2.3.4 1234 5.6.7.8 4321 and 1.2.3.4 1235 5.6.7.8 4321 + */ + args.lcl.fp_addr.ip4 = lcl_ip; + args.rmt.fp_addr.ip4 = rmt_ip; + args.lcl.fp_len = 24; + args.rmt.fp_len = 24; + args.lcl_port = 0; + args.rmt_port = 0; + args.action_index = action_index++; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/24 * 5.6.7.8/24 * action %d", + action_index - 1); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 7), + "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should" + " be 7 (lpm dst): %d", res); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port + 1, rmt_port); + SESSION_TEST ((res == 7), + "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 7: %d", + res); + + /* + * Del 1.2.3.4/24 * 5.6.7.8/24 * + * Add 1.2.3.4/16 * 5.6.7.8/16 * and 1.2.3.4/24 1235 5.6.7.8/24 4321 + * Lookup 1.2.3.4 1234 5.6.7.8 4321, 1.2.3.4 1235 5.6.7.8 4321 and + * 1.2.3.4 1235 5.6.7.8 4322 + */ + args.is_add = 0; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Del 1.2.3.4/24 * 5.6.7.8/24 *"); + + args.lcl.fp_addr.ip4 = lcl_ip; + args.rmt.fp_addr.ip4 = rmt_ip; + args.lcl.fp_len = 16; + args.rmt.fp_len = 16; + args.lcl_port = 0; + args.rmt_port = 0; + args.action_index = action_index++; + args.is_add = 1; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 * action %d", + action_index - 1); + + args.lcl.fp_addr.ip4 = lcl_ip; + args.rmt.fp_addr.ip4 = rmt_ip; + args.lcl.fp_len = 24; + args.rmt.fp_len = 24; + args.lcl_port = lcl_port + 1; + args.rmt_port = rmt_port; + args.action_index = action_index++; + args.is_add = 1; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/24 1235 5.6.7.8/24 4321 action %d", + action_index - 1); + + if (verbose) + session_rules_table_cli_dump (vm, srt, FIB_PROTOCOL_IP4, + TRANSPORT_PROTO_TCP); + + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 3), + "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", + res); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port + 1, rmt_port); + SESSION_TEST ((res == 9), + "Lookup 1.2.3.4 1235 5.6.7.8 4321, action should " "be 9: %d", + res); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port + 1, rmt_port + 1); + SESSION_TEST ((res == 8), + "Lookup 1.2.3.4 1235 5.6.7.8 4322, action should " "be 8: %d", + res); + + /* + * Delete 1.2.0.0/16 1234 5.6.0.0/16 4321 and 1.2.0.0/16 * 5.6.0.0/16 * + * Lookup 1.2.3.4 1234 5.6.7.8 4321 + */ + args.lcl_port = 1234; + args.rmt_port = 4321; + args.lcl.fp_len = 16; + args.rmt.fp_len = 16; + args.is_add = 0; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Del 1.2.0.0/16 1234 5.6.0.0/16 4321"); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 3), + "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", + res); + + args.lcl_port = 0; + args.rmt_port = 0; + args.is_add = 0; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Del 1.2.0.0/16 * 5.6.0.0/16 *"); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 3), + "Lookup 1.2.3.4 1234 5.6.7.8 4321, action should " "be 3: %d", + res); + + /* + * Delete 1.2.3.4/24 1234 5.6.7.5/24 + */ + args.lcl.fp_addr.ip4 = lcl_ip; + args.rmt.fp_addr.ip4 = rmt_ip; + args.lcl.fp_len = 24; + args.rmt.fp_len = 24; + args.lcl_port = 1234; + args.rmt_port = 4321; + args.is_add = 0; + error = session_rules_table_add_del (srt, &args); + SESSION_TEST ((error == 0), "Del 1.2.3.4/24 1234 5.6.7.5/24"); + res = + session_rules_table_lookup4 (srt, TRANSPORT_PROTO_TCP, &lcl_ip, &rmt_ip, + lcl_port, rmt_port); + SESSION_TEST ((res == 2), "Action should be 2: %d", res); + + return 0; +} + +static int +session_test_rules (vlib_main_t * vm, unformat_input_t * input) +{ + session_endpoint_t server_sep = SESSION_ENDPOINT_NULL; + u64 options[SESSION_OPTIONS_N_OPTIONS]; + u16 lcl_port = 1234, rmt_port = 4321; + u32 server_index, app_index; + u32 dummy_server_api_index = ~0; + transport_connection_t *tc; + u32 dummy_port = 1111; + clib_error_t *error = 0; + u8 segment_name[128]; + stream_session_t *listener, *s; + app_namespace_t *default_ns = app_namespace_get_default (); + u32 local_ns_index = default_ns->local_table_index; + + server_sep.is_ip4 = 1; + server_sep.port = dummy_port; + memset (options, 0, sizeof (options)); + + vnet_app_attach_args_t attach_args = { + .api_client_index = ~0, + .options = options, + .namespace_id = 0, + .session_cb_vft = &dummy_session_cbs, + .segment_name = segment_name, + }; + + vnet_bind_args_t bind_args = { + .sep = server_sep, + .app_index = 0, + }; + + /* + * Attach server with global and local default scope + */ + options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_BUILTIN_APP; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ACCEPT_REDIRECT; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + attach_args.namespace_id = 0; + attach_args.api_client_index = dummy_server_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "server attached"); + server_index = attach_args.app_index; + + bind_args.app_index = server_index; + error = vnet_bind (&bind_args); + SESSION_TEST ((error == 0), "server bound to %U/%d", format_ip46_address, + &server_sep.ip, 1, server_sep.port); + listener = listen_session_get_from_handle (bind_args.handle); + ip4_address_t lcl_ip = { + .as_u32 = clib_host_to_net_u32 (0x01020304), + }; + ip4_address_t rmt_ip = { + .as_u32 = clib_host_to_net_u32 (0x05060708), + }; + fib_prefix_t lcl_pref = { + .fp_addr.ip4.as_u32 = lcl_ip.as_u32, + .fp_len = 16, + .fp_proto = FIB_PROTOCOL_IP4, + }; + fib_prefix_t rmt_pref = { + .fp_addr.ip4.as_u32 = rmt_ip.as_u32, + .fp_len = 16, + .fp_proto = FIB_PROTOCOL_IP4, + }; + + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc == 0), "optimized lookup should not work (port)"); + + /* + * Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action server_index + */ + session_rule_add_del_args_t args = { + .table_args.lcl = lcl_pref, + .table_args.rmt = rmt_pref, + .table_args.lcl_port = lcl_port, + .table_args.rmt_port = rmt_port, + .table_args.action_index = server_index, + .table_args.is_add = 1, + .appns_index = 0, + }; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/16 1234 5.6.7.8/16 4321 action %d", + args.table_args.action_index); + + tc = session_lookup_connection4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port, rmt_port, + TRANSPORT_PROTO_TCP); + SESSION_TEST ((tc->c_index == listener->connection_index), + "optimized lookup should return the listener"); + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc->c_index == listener->connection_index), + "lookup should return the listener"); + s = session_lookup_safe4 (0, &lcl_pref.fp_addr.ip4, &rmt_pref.fp_addr.ip4, + lcl_port, rmt_port, TRANSPORT_PROTO_TCP); + SESSION_TEST ((s->connection_index == listener->connection_index), + "safe lookup should return the listener"); + session_endpoint_t sep = { + .ip = rmt_pref.fp_addr, + .is_ip4 = 1, + .port = rmt_port, + .transport_proto = TRANSPORT_PROTO_TCP, + }; + app_index = session_lookup_local_session_endpoint (local_ns_index, &sep); + SESSION_TEST ((app_index != server_index), "local session endpoint lookup " + "should not work (global scope)"); + + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port + 1, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc == 0), + "optimized lookup for wrong lcl port + 1 should not" " work"); + + /* + * Add 1.2.3.4/16 * 5.6.7.8/16 4321 + */ + args.table_args.lcl_port = 0; + args.scope = SESSION_RULE_SCOPE_LOCAL | SESSION_RULE_SCOPE_GLOBAL; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Add 1.2.3.4/16 * 5.6.7.8/16 4321 action %d", + args.table_args.action_index); + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port + 1, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc->c_index == listener->connection_index), + "optimized lookup for lcl port + 1 should work"); + app_index = session_lookup_local_session_endpoint (local_ns_index, &sep); + SESSION_TEST ((app_index != server_index), "local session endpoint lookup " + "should not work (constrained lcl ip)"); + + /* + * Add local scope rule for 0/0 * 5.6.7.8/16 4321 action server_index + */ + args.table_args.lcl.fp_len = 0; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Add * * 5.6.7.8/16 4321 action %d", + args.table_args.action_index); + app_index = session_lookup_local_session_endpoint (local_ns_index, &sep); + SESSION_TEST ((app_index == server_index), "local session endpoint lookup " + "should work"); + + /* + * Delete 0/0 * 5.6.7.8/16 4321, 1.2.3.4/16 * 5.6.7.8/16 4321 and + * 1.2.3.4/16 1234 5.6.7.8/16 4321 + */ + args.table_args.is_add = 0; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Del 0/0 * 5.6.7.8/16 4321"); + app_index = session_lookup_local_session_endpoint (local_ns_index, &sep); + SESSION_TEST ((app_index != server_index), "local session endpoint lookup " + "should not work (removed)"); + + args.table_args.is_add = 0; + args.table_args.lcl = lcl_pref; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Del 1.2.3.4/16 * 5.6.7.8/16 4321"); + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port + 1, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc == 0), "optimized lookup for lcl port + 1 should not " + "work (del)"); + + args.table_args.is_add = 0; + args.table_args.lcl_port = 1234; + error = vnet_session_rule_add_del (&args); + SESSION_TEST ((error == 0), "Del 1.2.3.4/16 1234 5.6.7.8/16 4321"); + tc = session_lookup_connection_wt4 (0, &lcl_pref.fp_addr.ip4, + &rmt_pref.fp_addr.ip4, lcl_port, + rmt_port, TRANSPORT_PROTO_TCP, 0); + SESSION_TEST ((tc == 0), "optimized lookup should not work (del)"); + return 0; +} + static clib_error_t * session_test (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd_arg) @@ -452,6 +910,10 @@ session_test (vlib_main_t * vm, { res = session_test_namespace (vm, input); } + else if (unformat (input, "rules-table")) + res = session_test_rule_table (vm, input); + else if (unformat (input, "rules")) + res = session_test_rules (vm, input); else break; } diff --git a/src/vnet/session/transport.c b/src/vnet/session/transport.c index fc722e45668..c18cf15974e 100644 --- a/src/vnet/session/transport.c +++ b/src/vnet/session/transport.c @@ -42,6 +42,38 @@ static transport_endpoint_t *local_endpoints; */ static clib_spinlock_t local_endpoints_lock; +u8 * +format_transport_proto (u8 * s, va_list * args) +{ + u32 transport_proto = va_arg (*args, u32); + switch (transport_proto) + { + case TRANSPORT_PROTO_TCP: + s = format (s, "TCP"); + break; + case TRANSPORT_PROTO_UDP: + s = format (s, "UDP"); + break; + } + return s; +} + +uword +unformat_transport_proto (unformat_input_t * input, va_list * args) +{ + u32 *proto = va_arg (*args, u32 *); + if (unformat (input, "tcp")) + *proto = TRANSPORT_PROTO_TCP; + else if (unformat (input, "TCP")) + *proto = TRANSPORT_PROTO_TCP; + else if (unformat (input, "udp")) + *proto = TRANSPORT_PROTO_UDP; + else if (unformat (input, "UDP")) + *proto = TRANSPORT_PROTO_UDP; + else + return 0; + return 1; +} u32 transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto, diff --git a/src/vnet/session/transport.h b/src/vnet/session/transport.h index f2cc80bb23a..822dc8317f1 100644 --- a/src/vnet/session/transport.h +++ b/src/vnet/session/transport.h @@ -73,6 +73,9 @@ typedef enum _transport_proto TRANSPORT_N_PROTO } transport_proto_t; +u8 *format_transport_proto (u8 * s, va_list * args); +uword unformat_transport_proto (unformat_input_t * input, va_list * args); + #define foreach_transport_connection_fields \ _(u32, sw_if_index) /**< interface endpoint is associated with */ \ _(ip46_address_t, ip) /**< ip address */ \ |