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/session_lookup.c | |
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/session_lookup.c')
-rw-r--r-- | src/vnet/session/session_lookup.c | 370 |
1 files changed, 362 insertions, 8 deletions
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) { |