diff options
Diffstat (limited to 'src/vnet/session')
-rw-r--r-- | src/vnet/session/application.c | 158 | ||||
-rw-r--r-- | src/vnet/session/application.h | 34 | ||||
-rw-r--r-- | src/vnet/session/application_interface.c | 380 | ||||
-rw-r--r-- | src/vnet/session/application_interface.h | 36 | ||||
-rw-r--r-- | src/vnet/session/application_namespace.c | 293 | ||||
-rw-r--r-- | src/vnet/session/application_namespace.h | 83 | ||||
-rw-r--r-- | src/vnet/session/segment_manager.c | 2 | ||||
-rw-r--r-- | src/vnet/session/session.api | 51 | ||||
-rw-r--r-- | src/vnet/session/session.c | 91 | ||||
-rw-r--r-- | src/vnet/session/session.h | 41 | ||||
-rwxr-xr-x | src/vnet/session/session_api.c | 153 | ||||
-rwxr-xr-x | src/vnet/session/session_cli.c | 36 | ||||
-rw-r--r-- | src/vnet/session/session_debug.h | 17 | ||||
-rw-r--r-- | src/vnet/session/session_lookup.c | 859 | ||||
-rw-r--r-- | src/vnet/session/session_lookup.h | 126 | ||||
-rw-r--r-- | src/vnet/session/session_node.c | 6 | ||||
-rw-r--r-- | src/vnet/session/session_table.c | 124 | ||||
-rw-r--r-- | src/vnet/session/session_table.h | 61 | ||||
-rw-r--r-- | src/vnet/session/session_test.c | 473 | ||||
-rw-r--r-- | src/vnet/session/stream_session.h | 39 | ||||
-rw-r--r-- | src/vnet/session/transport.h | 29 |
21 files changed, 2462 insertions, 630 deletions
diff --git a/src/vnet/session/application.c b/src/vnet/session/application.c index 2b789c5f420..75d3cfb2e33 100644 --- a/src/vnet/session/application.c +++ b/src/vnet/session/application.c @@ -15,6 +15,7 @@ #include <vnet/session/application.h> #include <vnet/session/application_interface.h> +#include <vnet/session/application_namespace.h> #include <vnet/session/session.h> /** @@ -32,6 +33,46 @@ static uword *app_by_api_client_index; */ static u32 default_app_evt_queue_size = 128; +static u8 * +app_get_name_from_reg_index (application_t * app) +{ + u8 *app_name; + + vl_api_registration_t *regp; + regp = vl_api_client_index_to_registration (app->api_client_index); + if (!regp) + app_name = format (0, "builtin-%d%c", app->index, 0); + else + app_name = format (0, "%s%c", regp->name, 0); + + return app_name; +} + +u32 +application_session_table (application_t * app, u8 fib_proto) +{ + app_namespace_t *app_ns; + app_ns = app_namespace_get (app->ns_index); + if (!application_has_global_scope (app)) + return APP_INVALID_INDEX; + if (fib_proto == FIB_PROTOCOL_IP4) + return session_lookup_get_index_for_fib (fib_proto, + app_ns->ip4_fib_index); + else + return session_lookup_get_index_for_fib (fib_proto, + app_ns->ip6_fib_index); +} + +u32 +application_local_session_table (application_t * app) +{ + app_namespace_t *app_ns; + if (!application_has_local_scope (app)) + return APP_INVALID_INDEX; + app_ns = app_namespace_get (app->ns_index); + return app_ns->local_table_index; +} + int application_api_queue_is_full (application_t * app) { @@ -50,6 +91,21 @@ application_api_queue_is_full (application_t * app) return 0; } +/** + * Returns app name + * + * Since the name is not stored per app, we generate it on the fly. It is + * the caller's responsibility to free the vector + */ +u8 * +application_name_from_index (u32 app_index) +{ + application_t *app = application_get (app_index); + if (!app) + return 0; + return app_get_name_from_reg_index (app); +} + static void application_table_add (application_t * app) { @@ -135,7 +191,6 @@ application_del (application_t * app) segment_manager_init_del (sm); } - /* If first segment manager is used by a listener */ if (app->first_segment_manager != APP_INVALID_SEGMENT_MANAGER_INDEX && app->first_segment_manager != app->connects_seg_manager) @@ -180,7 +235,9 @@ application_init (application_t * app, u32 api_client_index, u64 * options, app_evt_queue_size = options[APP_EVT_QUEUE_SIZE] > 0 ? options[APP_EVT_QUEUE_SIZE] : default_app_evt_queue_size; - /* Setup segment manager */ + /* + * Setup segment manager + */ sm = segment_manager_new (); sm->app_index = app->index; props = &app->sm_properties; @@ -203,10 +260,19 @@ application_init (application_t * app, u32 api_client_index, u64 * options, return rv; sm->first_is_protected = 1; + /* + * Setup application + */ app->first_segment_manager = segment_manager_index (sm); app->api_client_index = api_client_index; app->flags = options[APP_OPTIONS_FLAGS]; app->cb_fns = *cb_fns; + app->ns_index = options[APP_OPTIONS_NAMESPACE]; + + /* If no scope enabled, default to global */ + if (!application_has_global_scope (app) + && !application_has_local_scope (app)) + app->flags |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; /* Allocate app event queue in the first shared-memory segment */ app->event_queue = segment_manager_alloc_queue (sm, app_evt_queue_size); @@ -223,6 +289,8 @@ application_init (application_t * app, u32 api_client_index, u64 * options, application_t * application_get (u32 index) { + if (index == APP_INVALID_INDEX) + return 0; return pool_elt_at_index (app_pool, index); } @@ -269,17 +337,19 @@ application_alloc_segment_manager (application_t * app) * it's own specific listening connection. */ int -application_start_listen (application_t * srv, session_type_t session_type, - transport_endpoint_t * tep, u64 * res) +application_start_listen (application_t * srv, session_endpoint_t * sep, + u64 * res) { segment_manager_t *sm; stream_session_t *s; u64 handle; + session_type_t sst; - s = listen_session_new (session_type); + sst = session_type_from_proto_and_ip (sep->transport_proto, sep->is_ip4); + s = listen_session_new (sst); s->app_index = srv->index; - if (stream_session_listen (s, tep)) + if (stream_session_listen (s, sep)) goto err; /* Allocate segment manager. All sessions derived out of a listen session @@ -341,15 +411,15 @@ application_stop_listen (application_t * srv, u64 handle) } int -application_open_session (application_t * app, session_type_t sst, - transport_endpoint_t * tep, u32 api_context) +application_open_session (application_t * app, session_endpoint_t * sep, + u32 api_context) { segment_manager_t *sm; transport_connection_t *tc = 0; int rv; /* Make sure we have a segment manager for connects */ - if (app->connects_seg_manager == (u32) ~ 0) + if (app->connects_seg_manager == APP_INVALID_SEGMENT_MANAGER_INDEX) { sm = application_alloc_segment_manager (app); if (sm == 0) @@ -357,7 +427,7 @@ application_open_session (application_t * app, session_type_t sst, app->connects_seg_manager = segment_manager_index (sm); } - if ((rv = stream_session_open (app->index, sst, tep, &tc))) + if ((rv = stream_session_open (app->index, sep, &tc))) return rv; /* Store api_context for when the reply comes. Not the nicest thing @@ -384,21 +454,6 @@ application_get_listen_segment_manager (application_t * app, return segment_manager_get (*smp); } -static u8 * -app_get_name_from_reg_index (application_t * app) -{ - u8 *app_name; - - vl_api_registration_t *regp; - regp = vl_api_client_index_to_registration (app->api_client_index); - if (!regp) - app_name = format (0, "builtin-%d%c", app->index, 0); - else - app_name = format (0, "%s%c", regp->name, 0); - - return app_name; -} - int application_is_proxy (application_t * app) { @@ -420,6 +475,18 @@ application_add_segment_notify (u32 app_index, u32 fifo_segment_index) seg_size); } +u8 +application_has_local_scope (application_t * app) +{ + return app->flags & APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; +} + +u8 +application_has_global_scope (application_t * app) +{ + return app->flags & APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; +} + u8 * format_application_listener (u8 * s, va_list * args) { @@ -500,7 +567,7 @@ application_format_connects (application_t * app, int verbose) session_index = fifo->master_session_index; thread_index = fifo->master_thread_index; - session = stream_session_get (session_index, thread_index); + session = session_get (session_index, thread_index); str = format (0, "%U", format_stream_session, session, verbose); if (verbose) @@ -526,27 +593,33 @@ format_application (u8 * s, va_list * args) { application_t *app = va_arg (*args, application_t *); CLIB_UNUSED (int verbose) = va_arg (*args, int); + const u8 *app_ns_name; u8 *app_name; if (app == 0) { if (verbose) - s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s", "Index", "Name", - "API Client", "Add seg size", "Rx fifo size", + s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s%-15s", "Index", "Name", + "Namespace", "API Client", "Add seg size", "Rx fifo size", "Tx fifo size"); else - s = format (s, "%-10s%-20s%-20s", "Index", "Name", "API Client"); + s = + format (s, "%-10s%-20s%-15s%-20s", "Index", "Name", "Namespace", + "API Client"); return s; } app_name = app_get_name_from_reg_index (app); + app_ns_name = app_namespace_id_from_index (app->ns_index); if (verbose) - s = format (s, "%-10d%-20s%-15d%-15d%-15d%-15d", app->index, app_name, - app->api_client_index, app->sm_properties.add_segment_size, - app->sm_properties.rx_fifo_size, - app->sm_properties.tx_fifo_size); + s = + format (s, "%-10d%-20s%-15s%-15d%-15d%-15d%-15d", app->index, app_name, + app_ns_name, app->api_client_index, + app->sm_properties.add_segment_size, + app->sm_properties.rx_fifo_size, + app->sm_properties.tx_fifo_size); else - s = format (s, "%-10d%-20s%-20d", app->index, app_name, + s = format (s, "%-10d%-20s%-15s%-20d", app->index, app_name, app_ns_name, app->api_client_index); return s; } @@ -560,10 +633,7 @@ show_app_command_fn (vlib_main_t * vm, unformat_input_t * input, int do_client = 0; int verbose = 0; - if (!session_manager_is_enabled ()) - { - clib_error_return (0, "session layer is not enabled"); - } + session_cli_return_if_not_enabled (); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -627,13 +697,11 @@ show_app_command_fn (vlib_main_t * vm, unformat_input_t * input, if (!do_server && !do_client) { vlib_cli_output (vm, "%U", format_application, 0, verbose); - pool_foreach (app, app_pool, ( - { - vlib_cli_output (vm, "%U", - format_application, app, - verbose); - } - )); + /* *INDENT-OFF* */ + pool_foreach (app, app_pool, ({ + vlib_cli_output (vm, "%U", format_application, app, verbose); + })); + /* *INDENT-ON* */ } return 0; diff --git a/src/vnet/session/application.h b/src/vnet/session/application.h index e030c376d83..00a5554dd23 100644 --- a/src/vnet/session/application.h +++ b/src/vnet/session/application.h @@ -19,7 +19,7 @@ #include <vnet/vnet.h> #include <vnet/session/session.h> #include <vnet/session/segment_manager.h> - +#include <vnet/session/application_namespace.h> typedef enum { APP_SERVER, @@ -36,7 +36,7 @@ typedef struct _stream_session_cb_vft /** Notify server of newly accepted session */ int (*session_accept_callback) (stream_session_t * new_session); - /* Connection request callback */ + /** Connection request callback */ int (*session_connected_callback) (u32 app_index, u32 opaque, stream_session_t * s, u8 code); @@ -46,10 +46,10 @@ typedef struct _stream_session_cb_vft /** Notify app that session was reset */ void (*session_reset_callback) (stream_session_t * s); - /* Direct RX callback, for built-in servers */ + /** Direct RX callback, for built-in servers */ int (*builtin_server_rx_callback) (stream_session_t * session); - /* Redirect connection to local server */ + /** Redirect connection to local server */ int (*redirect_connect_callback) (u32 api_client_index, void *mp); } session_cb_vft_t; @@ -68,6 +68,9 @@ typedef struct _application /** Binary API connection index, ~0 if internal */ u32 api_client_index; + /** Namespace the application belongs to */ + u32 ns_index; + /** Application listens for events on this svm queue */ unix_shared_memory_queue_t *event_queue; @@ -95,25 +98,24 @@ typedef struct _application segment_manager_properties_t sm_properties; } application_t; +#define APP_INVALID_INDEX ((u32)~0) +#define APP_NS_INVALID_INDEX ((u32)~0) #define APP_INVALID_SEGMENT_MANAGER_INDEX ((u32) ~0) application_t *application_new (); -int -application_init (application_t * app, u32 api_client_index, u64 * options, - session_cb_vft_t * cb_fns); +int application_init (application_t * app, u32 api_client_index, + u64 * options, session_cb_vft_t * cb_fns); void application_del (application_t * app); application_t *application_get (u32 index); application_t *application_get_if_valid (u32 index); application_t *application_lookup (u32 api_client_index); u32 application_get_index (application_t * app); -int -application_start_listen (application_t * app, session_type_t session_type, - transport_endpoint_t * tep, u64 * handle); +int application_start_listen (application_t * app, + session_endpoint_t * tep, u64 * handle); int application_stop_listen (application_t * srv, u64 handle); -int -application_open_session (application_t * app, session_type_t sst, - transport_endpoint_t * tep, u32 api_context); +int application_open_session (application_t * app, session_endpoint_t * tep, + u32 api_context); int application_api_queue_is_full (application_t * app); segment_manager_t *application_get_listen_segment_manager (application_t * @@ -124,6 +126,12 @@ segment_manager_t *application_get_connect_segment_manager (application_t * app); int application_is_proxy (application_t * app); int application_add_segment_notify (u32 app_index, u32 fifo_segment_index); +u32 application_session_table (application_t * app, u8 fib_proto); +u32 application_local_session_table (application_t * app); +u8 *application_name_from_index (u32 app_index); + +u8 application_has_local_scope (application_t * app); +u8 application_has_global_scope (application_t * app); #endif /* SRC_VNET_SESSION_APPLICATION_H_ */ diff --git a/src/vnet/session/application_interface.c b/src/vnet/session/application_interface.c index 7e7449aa161..a0dff90565a 100644 --- a/src/vnet/session/application_interface.c +++ b/src/vnet/session/application_interface.c @@ -17,45 +17,37 @@ #include <vnet/session/session.h> #include <vlibmemory/api.h> #include <vnet/dpo/load_balance.h> -#include <vnet/fib/ip4_fib.h> /** @file VPP's application/session API bind/unbind/connect/disconnect calls */ static u8 -ip_is_zero (ip46_address_t * ip46_address, u8 is_ip4) +session_endpoint_is_local (session_endpoint_t * sep) { - if (is_ip4) - return (ip46_address->ip4.as_u32 == 0); - else - return (ip46_address->as_u64[0] == 0 && ip46_address->as_u64[1] == 0); + return (ip_is_zero (&sep->ip, sep->is_ip4) + || ip_is_local_host (&sep->ip, sep->is_ip4)); } static u8 -ip_is_local (ip46_address_t * ip46_address, u8 is_ip4) +session_endpoint_is_zero (session_endpoint_t * sep) { - fib_node_index_t fei; - fib_entry_flag_t flags; - fib_prefix_t prefix; + return ip_is_zero (&sep->ip, sep->is_ip4); +} - /* Check if requester is local */ - if (is_ip4) - { - prefix.fp_len = 32; - prefix.fp_proto = FIB_PROTOCOL_IP4; - } - else +u8 +session_endpoint_in_ns (session_endpoint_t * sep) +{ + u8 is_zero = ip_is_zero (&sep->ip, sep->is_ip4); + if (!is_zero && sep->sw_if_index != ENDPOINT_INVALID_INDEX + && !ip_interface_has_address (sep->sw_if_index, &sep->ip, sep->is_ip4)) { - prefix.fp_len = 128; - prefix.fp_proto = FIB_PROTOCOL_IP6; + clib_warning ("sw_if_index %u not configured with ip %U", + sep->sw_if_index, format_ip46_address, &sep->ip, + sep->is_ip4); + return 0; } - - clib_memcpy (&prefix.fp_addr, ip46_address, sizeof (ip46_address_t)); - fei = fib_table_lookup (0, &prefix); - flags = fib_entry_get_flags (fei); - - return (flags & FIB_ENTRY_FLAG_LOCAL); + return (is_zero || ip_is_local (sep->fib_index, &sep->ip, sep->is_ip4)); } int @@ -78,76 +70,190 @@ api_parse_session_handle (u64 handle, u32 * session_index, u32 * thread_index) return 0; } -int -vnet_bind_i (u32 app_index, session_type_t sst, - transport_endpoint_t * tep, u64 * handle) +static void +session_endpoint_update_for_app (session_endpoint_t * sep, + application_t * app) +{ + app_namespace_t *app_ns; + app_ns = app_namespace_get (app->ns_index); + if (app_ns) + { + /* Ask transport and network to bind to/connect using local interface + * that "supports" app's namespace. This will fix our local connection + * endpoint. + */ + sep->sw_if_index = app_ns->sw_if_index; + sep->fib_index = + sep->is_ip4 ? app_ns->ip4_fib_index : app_ns->ip6_fib_index; + } +} + +static int +vnet_bind_i (u32 app_index, session_endpoint_t * sep, u64 * handle) { application_t *app; - stream_session_t *listener; + u32 table_index, listener_index; + int rv, have_local = 0; app = application_get_if_valid (app_index); if (!app) { - clib_warning ("app not attached"); + SESSION_DBG ("app not attached"); return VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } - listener = stream_session_lookup_listener (&tep->ip, tep->port, sst); - if (listener) + session_endpoint_update_for_app (sep, app); + if (!session_endpoint_in_ns (sep)) + return VNET_API_ERROR_INVALID_VALUE_2; + + table_index = application_session_table (app, + session_endpoint_fib_proto (sep)); + listener_index = session_lookup_session_endpoint (table_index, sep); + if (listener_index != SESSION_INVALID_INDEX) return VNET_API_ERROR_ADDRESS_IN_USE; - if (!ip_is_zero (&tep->ip, tep->is_ip4) - && !ip_is_local (&tep->ip, tep->is_ip4)) - return VNET_API_ERROR_INVALID_VALUE_2; + /* + * Add session endpoint to local session table. Only binds to "inaddr_any" + * (i.e., zero address) are added to local scope table. + */ + if (application_has_local_scope (app) && session_endpoint_is_zero (sep)) + { + table_index = application_local_session_table (app); + listener_index = session_lookup_session_endpoint (table_index, sep); + if (listener_index != SESSION_INVALID_INDEX) + return VNET_API_ERROR_ADDRESS_IN_USE; + session_lookup_add_session_endpoint (table_index, sep, app->index); + *handle = session_lookup_local_listener_make_handle (sep); + have_local = 1; + } + + if (!application_has_global_scope (app)) + return (have_local - 1); + + /* + * Add session endpoint to global session table + */ /* Setup listen path down to transport */ - return application_start_listen (app, sst, tep, handle); + rv = application_start_listen (app, sep, handle); + if (rv && have_local) + session_lookup_del_session_endpoint (table_index, sep); + return rv; } int vnet_unbind_i (u32 app_index, u64 handle) { application_t *app = application_get_if_valid (app_index); + stream_session_t *listener = 0; + u32 table_index; if (!app) { - clib_warning ("app (%d) not attached", app_index); + SESSION_DBG ("app (%d) not attached", app_index); return VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } - /* Clear the listener */ - return application_stop_listen (app, handle); + /* + * Clean up local session table. If we have a listener session use it to + * find the port and proto. If not, the handle must be a local table handle + * so parse it. + */ + + if (application_has_local_scope (app)) + { + session_endpoint_t sep = SESSION_ENDPOINT_NULL; + if (!session_lookup_local_is_handle (handle)) + listener = listen_session_get_from_handle (handle); + if (listener) + { + if (listen_session_get_local_session_endpoint (listener, &sep)) + { + clib_warning ("broken listener"); + return -1; + } + } + else + { + if (session_lookup_local_listener_parse_handle (handle, &sep)) + { + clib_warning ("can't parse handle"); + return -1; + } + } + table_index = application_local_session_table (app); + session_lookup_del_session_endpoint (table_index, &sep); + } + + /* + * Clear the global scope table of the listener + */ + if (application_has_global_scope (app)) + return application_stop_listen (app, handle); + return 0; +} + +static int +app_connect_redirect (application_t * server, void *mp) +{ + return server->cb_fns.redirect_connect_callback (server->api_client_index, + mp); } int -vnet_connect_i (u32 app_index, u32 api_context, session_type_t sst, - transport_endpoint_t * tep, void *mp) +vnet_connect_i (u32 app_index, u32 api_context, session_endpoint_t * sep, + void *mp) { - stream_session_t *listener; application_t *server, *app; + u32 table_index; + + if (session_endpoint_is_zero (sep)) + return VNET_API_ERROR_INVALID_VALUE; + + app = application_get (app_index); + session_endpoint_update_for_app (sep, app); /* - * Figure out if connecting to a local server + * First check the the local scope for locally attached destinations. + * If we have local scope, we pass *all* connects through it since we may + * have special policy rules even for non-local destinations, think proxy. */ - listener = stream_session_lookup_listener (&tep->ip, tep->port, sst); - if (listener) + if (application_has_local_scope (app)) { - server = application_get (listener->app_index); - + table_index = application_local_session_table (app); + app_index = session_lookup_local_session_endpoint (table_index, sep); + server = application_get (app_index); /* * Server is willing to have a direct fifo connection created * instead of going through the state machine, etc. */ - if (server->flags & APP_OPTIONS_FLAGS_USE_FIFO) - return server->cb_fns. - redirect_connect_callback (server->api_client_index, mp); + if (server && (server->flags & APP_OPTIONS_FLAGS_ACCEPT_REDIRECT)) + return app_connect_redirect (server, mp); } /* - * Not connecting to a local server. Create regular session + * If nothing found, check the global scope for locally attached + * destinations. Make sure first that we're allowed to. */ - app = application_get (app_index); - return application_open_session (app, sst, tep, api_context); + if (session_endpoint_is_local (sep)) + return VNET_API_ERROR_SESSION_CONNECT; + + if (!application_has_global_scope (app)) + return VNET_API_ERROR_APP_CONNECT_SCOPE; + + table_index = application_session_table (app, + session_endpoint_fib_proto (sep)); + app_index = session_lookup_session_endpoint (table_index, sep); + server = application_get (app_index); + if (server && (server->flags & APP_OPTIONS_FLAGS_ACCEPT_REDIRECT)) + return app_connect_redirect (server, mp); + + /* + * Not connecting to a local server, propagate to transport + */ + if (application_open_session (app, sep, api_context)) + return VNET_API_ERROR_SESSION_CONNECT; + return 0; } /** @@ -170,37 +276,38 @@ vnet_connect_i (u32 app_index, u32 api_context, session_type_t sst, uword unformat_vnet_uri (unformat_input_t * input, va_list * args) { - session_type_t *sst = va_arg (*args, session_type_t *); - transport_endpoint_t *tep = va_arg (*args, transport_endpoint_t *); + session_endpoint_t *sep = va_arg (*args, session_endpoint_t *); - if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &tep->ip.ip4, - &tep->port)) + if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &sep->ip.ip4, + &sep->port)) { - *sst = SESSION_TYPE_IP4_TCP; - tep->port = clib_host_to_net_u16 (tep->port); - tep->is_ip4 = 1; + sep->transport_proto = TRANSPORT_PROTO_TCP; + sep->port = clib_host_to_net_u16 (sep->port); + sep->is_ip4 = 1; return 1; } - if (unformat (input, "udp://%U/%d", unformat_ip4_address, &tep->ip.ip4, - &tep->port)) + if (unformat (input, "udp://%U/%d", unformat_ip4_address, &sep->ip.ip4, + &sep->port)) { - *sst = SESSION_TYPE_IP4_UDP; - tep->port = clib_host_to_net_u16 (tep->port); - tep->is_ip4 = 1; + sep->transport_proto = TRANSPORT_PROTO_UDP; + sep->port = clib_host_to_net_u16 (sep->port); + sep->is_ip4 = 1; return 1; } - if (unformat (input, "udp://%U/%d", unformat_ip6_address, &tep->ip.ip6, - &tep->port)) + if (unformat (input, "udp://%U/%d", unformat_ip6_address, &sep->ip.ip6, + &sep->port)) { - *sst = SESSION_TYPE_IP6_UDP; - tep->port = clib_host_to_net_u16 (tep->port); + sep->transport_proto = TRANSPORT_PROTO_UDP; + sep->port = clib_host_to_net_u16 (sep->port); + sep->is_ip4 = 0; return 1; } - if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &tep->ip.ip6, - &tep->port)) + if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &sep->ip.ip6, + &sep->port)) { - *sst = SESSION_TYPE_IP6_TCP; - tep->port = clib_host_to_net_u16 (tep->port); + sep->transport_proto = TRANSPORT_PROTO_TCP; + sep->port = clib_host_to_net_u16 (sep->port); + sep->is_ip4 = 0; return 1; } @@ -208,18 +315,16 @@ unformat_vnet_uri (unformat_input_t * input, va_list * args) } static u8 *cache_uri; -static session_type_t cache_sst; -static transport_endpoint_t *cache_tep; +static session_endpoint_t *cache_sep; int -parse_uri (char *uri, session_type_t * sst, transport_endpoint_t * tep) +parse_uri (char *uri, session_endpoint_t * sep) { unformat_input_t _input, *input = &_input; if (cache_uri && !strncmp (uri, (char *) cache_uri, vec_len (cache_uri))) { - *sst = cache_sst; - *tep = *cache_tep; + *sep = *cache_sep; return 0; } @@ -228,7 +333,7 @@ parse_uri (char *uri, session_type_t * sst, transport_endpoint_t * tep) /* Parse uri */ unformat_init_string (input, uri, strlen (uri)); - if (!unformat (input, "%U", unformat_vnet_uri, sst, tep)) + if (!unformat (input, "%U", unformat_vnet_uri, sep)) { unformat_free (input); return VNET_API_ERROR_INVALID_VALUE; @@ -237,34 +342,67 @@ parse_uri (char *uri, session_type_t * sst, transport_endpoint_t * tep) vec_free (cache_uri); cache_uri = (u8 *) uri; - cache_sst = *sst; - if (cache_tep) - clib_mem_free (cache_tep); - cache_tep = clib_mem_alloc (sizeof (*tep)); - *cache_tep = *tep; + if (cache_sep) + clib_mem_free (cache_sep); + cache_sep = clib_mem_alloc (sizeof (*sep)); + *cache_sep = *sep; return 0; } +static int +session_validate_namespace (u8 * namespace_id, u64 secret, u32 * app_ns_index) +{ + app_namespace_t *app_ns; + if (vec_len (namespace_id) == 0) + { + /* Use default namespace */ + *app_ns_index = 0; + return 0; + } + + *app_ns_index = app_namespace_index_from_id (namespace_id); + if (*app_ns_index == APP_NAMESPACE_INVALID_INDEX) + return VNET_API_ERROR_APP_INVALID_NS; + app_ns = app_namespace_get (*app_ns_index); + if (!app_ns) + return VNET_API_ERROR_APP_INVALID_NS; + if (app_ns->ns_secret != secret) + return VNET_API_ERROR_APP_WRONG_NS_SECRET; + return 0; +} + /** - * Attaches application. + * Attach application to vpp * * Allocates a vpp app, i.e., a structure that keeps back pointers * to external app and a segment manager for shared memory fifo based * communication with the external app. */ -int +clib_error_t * vnet_application_attach (vnet_app_attach_args_t * a) { application_t *app = 0; segment_manager_t *sm; u8 *seg_name; + u64 secret; + u32 app_ns_index = 0; int rv; + app = application_lookup (a->api_client_index); + if (app) + return clib_error_return_code (0, VNET_API_ERROR_APP_ALREADY_ATTACHED, + 0, "app already attached"); + + secret = a->options[APP_OPTIONS_NAMESPACE_SECRET]; + if ((rv = session_validate_namespace (a->namespace_id, secret, + &app_ns_index))) + return clib_error_return_code (0, rv, 0, "namespace validation: %d", rv); + a->options[APP_OPTIONS_NAMESPACE] = app_ns_index; app = application_new (); if ((rv = application_init (app, a->api_client_index, a->options, a->session_cb_vft))) - return rv; + return clib_error_return_code (0, rv, 0, "app init: %d", rv); a->app_event_queue_address = pointer_to_uword (app->event_queue); sm = segment_manager_get (app->first_segment_manager); @@ -278,6 +416,9 @@ vnet_application_attach (vnet_app_attach_args_t * a) return 0; } +/** + * Detach application from vpp + */ int vnet_application_detach (vnet_app_detach_args_t * a) { @@ -297,56 +438,48 @@ vnet_application_detach (vnet_app_detach_args_t * a) int vnet_bind_uri (vnet_bind_args_t * a) { - session_type_t sst = SESSION_N_TYPES; - transport_endpoint_t tep; + session_endpoint_t sep = SESSION_ENDPOINT_NULL; int rv; - memset (&tep, 0, sizeof (tep)); - rv = parse_uri (a->uri, &sst, &tep); + rv = parse_uri (a->uri, &sep); if (rv) return rv; - if ((rv = vnet_bind_i (a->app_index, sst, &tep, &a->handle))) - return rv; - - return 0; + return vnet_bind_i (a->app_index, &sep, &a->handle); } int vnet_unbind_uri (vnet_unbind_args_t * a) { - session_type_t sst = SESSION_N_TYPES; stream_session_t *listener; - transport_endpoint_t tep; + session_endpoint_t sep = SESSION_ENDPOINT_NULL; int rv; - rv = parse_uri (a->uri, &sst, &tep); + rv = parse_uri (a->uri, &sep); if (rv) return rv; - listener = stream_session_lookup_listener (&tep.ip, - clib_host_to_net_u16 (tep.port), - sst); + /* NOTE: only default table supported for uri */ + listener = session_lookup_listener (0, &sep); if (!listener) return VNET_API_ERROR_ADDRESS_NOT_IN_USE; return vnet_unbind_i (a->app_index, listen_session_get_handle (listener)); } -int +clib_error_t * vnet_connect_uri (vnet_connect_args_t * a) { - transport_endpoint_t tep; - session_type_t sst; + session_endpoint_t sep = SESSION_ENDPOINT_NULL; int rv; /* Parse uri */ - memset (&tep, 0, sizeof (tep)); - rv = parse_uri (a->uri, &sst, &tep); + rv = parse_uri (a->uri, &sep); if (rv) - return rv; - - return vnet_connect_i (a->app_index, a->api_context, sst, &tep, a->mp); + return clib_error_return_code (0, rv, 0, "app init: %d", rv); + if ((rv = vnet_connect_i (a->app_index, a->api_context, &sep, a->mp))) + return clib_error_return_code (0, rv, 0, "connect failed"); + return 0; } int @@ -355,7 +488,7 @@ vnet_disconnect_session (vnet_disconnect_args_t * a) u32 index, thread_index; stream_session_t *s; - stream_session_parse_handle (a->handle, &index, &thread_index); + session_parse_handle (a->handle, &index, &thread_index); s = stream_session_get_if_valid (index, thread_index); if (!s || s->app_index != a->app_index) @@ -369,32 +502,31 @@ vnet_disconnect_session (vnet_disconnect_args_t * a) return 0; } -int +clib_error_t * vnet_bind (vnet_bind_args_t * a) { - session_type_t sst = SESSION_N_TYPES; int rv; - - sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4); - if ((rv = vnet_bind_i (a->app_index, sst, &a->tep, &a->handle))) - return rv; - + if ((rv = vnet_bind_i (a->app_index, &a->sep, &a->handle))) + return clib_error_return_code (0, rv, 0, "bind failed"); return 0; } -int +clib_error_t * vnet_unbind (vnet_unbind_args_t * a) { - return vnet_unbind_i (a->app_index, a->handle); + int rv; + if ((rv = vnet_unbind_i (a->app_index, a->handle))) + return clib_error_return_code (0, rv, 0, "unbind failed"); + return 0; } -int +clib_error_t * vnet_connect (vnet_connect_args_t * a) { - session_type_t sst; - - sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4); - return vnet_connect_i (a->app_index, a->api_context, sst, &a->tep, a->mp); + int rv; + if ((rv = vnet_connect_i (a->app_index, a->api_context, &a->sep, a->mp))) + return clib_error_return_code (0, rv, 0, "connect failed"); + return 0; } /* diff --git a/src/vnet/session/application_interface.h b/src/vnet/session/application_interface.h index 1d63f6cc102..5e1fe8ee528 100644 --- a/src/vnet/session/application_interface.h +++ b/src/vnet/session/application_interface.h @@ -30,12 +30,12 @@ typedef struct _vnet_app_attach_args_t /** Application and segment manager options */ u64 *options; + /* Namespace id */ + u8 *namespace_id; + /** Session to application callback functions */ session_cb_vft_t *session_cb_vft; - /** Flag that indicates if app is builtin */ - u8 builtin; - /* * Results */ @@ -58,7 +58,7 @@ typedef struct _vnet_bind_args_t char *uri; struct { - transport_endpoint_t tep; + session_endpoint_t sep; transport_proto_t proto; }; }; @@ -91,7 +91,7 @@ typedef struct _vnet_connect_args char *uri; struct { - transport_endpoint_t tep; + session_endpoint_t sep; transport_proto_t proto; }; }; @@ -119,6 +119,8 @@ typedef enum APP_OPTIONS_PREALLOC_FIFO_PAIRS, APP_OPTIONS_PRIVATE_SEGMENT_COUNT, APP_OPTIONS_PRIVATE_SEGMENT_SIZE, + APP_OPTIONS_NAMESPACE, + APP_OPTIONS_NAMESPACE_SECRET, SESSION_OPTIONS_SEGMENT_SIZE, SESSION_OPTIONS_ADD_SEGMENT_SIZE, SESSION_OPTIONS_RX_FIFO_SIZE, @@ -129,10 +131,12 @@ typedef enum } app_attach_options_index_t; #define foreach_app_options_flags \ - _(USE_FIFO, "Use FIFO with redirects") \ + _(ACCEPT_REDIRECT, "Use FIFO with redirects") \ _(ADD_SEGMENT, "Add segment and signal app if needed") \ _(BUILTIN_APP, "Application is builtin") \ - _(IS_PROXY, "Application is proxying") + _(IS_PROXY, "Application is proxying") \ + _(USE_GLOBAL_SCOPE, "App can use global session scope") \ + _(USE_LOCAL_SCOPE, "App can use local session scope") typedef enum _app_options { @@ -148,25 +152,17 @@ typedef enum _app_options_flags #undef _ } app_options_flags_t; -///** Server can handle delegated connect requests from local clients */ -//#define APP_OPTIONS_FLAGS_USE_FIFO (1<<0) -// -///** Server wants vpp to add segments when out of memory for fifos */ -//#define APP_OPTIONS_FLAGS_ADD_SEGMENT (1<<1) - -#define VNET_CONNECT_REDIRECTED 123 - -int vnet_application_attach (vnet_app_attach_args_t * a); +clib_error_t *vnet_application_attach (vnet_app_attach_args_t * a); int vnet_application_detach (vnet_app_detach_args_t * a); int vnet_bind_uri (vnet_bind_args_t *); int vnet_unbind_uri (vnet_unbind_args_t * a); -int vnet_connect_uri (vnet_connect_args_t * a); +clib_error_t *vnet_connect_uri (vnet_connect_args_t * a); int vnet_disconnect_session (vnet_disconnect_args_t * a); -int vnet_bind (vnet_bind_args_t * a); -int vnet_connect (vnet_connect_args_t * a); -int vnet_unbind (vnet_unbind_args_t * a); +clib_error_t *vnet_bind (vnet_bind_args_t * a); +clib_error_t *vnet_connect (vnet_connect_args_t * a); +clib_error_t *vnet_unbind (vnet_unbind_args_t * a); int api_parse_session_handle (u64 handle, u32 * session_index, diff --git a/src/vnet/session/application_namespace.c b/src/vnet/session/application_namespace.c new file mode 100644 index 00000000000..7f90943f9cd --- /dev/null +++ b/src/vnet/session/application_namespace.c @@ -0,0 +1,293 @@ +/* + * 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/application_namespace.h> +#include <vnet/session/session_table.h> +#include <vnet/session/session.h> +#include <vnet/fib/fib_table.h> + +/** + * Hash table of application namespaces by app ns ids + */ +uword *app_namespace_lookup_table; + +/** + * Pool of application namespaces + */ +static app_namespace_t *app_namespace_pool; + +app_namespace_t * +app_namespace_get (u32 index) +{ + return pool_elt_at_index (app_namespace_pool, index); +} + +app_namespace_t * +app_namespace_get_from_id (const u8 * ns_id) +{ + u32 index = app_namespace_index_from_id (ns_id); + if (index == APP_NAMESPACE_INVALID_INDEX) + return 0; + return app_namespace_get (index); +} + +u32 +app_namespace_index (app_namespace_t * app_ns) +{ + return (app_ns - app_namespace_pool); +} + +app_namespace_t * +app_namespace_alloc (u8 * ns_id) +{ + app_namespace_t *app_ns; + pool_get (app_namespace_pool, app_ns); + memset (app_ns, 0, sizeof (*app_ns)); + app_ns->ns_id = vec_dup (ns_id); + hash_set_mem (app_namespace_lookup_table, app_ns->ns_id, + app_ns - app_namespace_pool); + return app_ns; +} + +clib_error_t * +vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t * a) +{ + app_namespace_t *app_ns; + session_table_t *st; + + if (a->is_add) + { + if (a->sw_if_index != APP_NAMESPACE_INVALID_INDEX + && !vnet_get_sw_interface_safe (vnet_get_main (), a->sw_if_index)) + return clib_error_return_code (0, VNET_API_ERROR_INVALID_SW_IF_INDEX, + 0, "sw_if_index %u doesn't exist", + a->sw_if_index); + + if (a->sw_if_index != APP_NAMESPACE_INVALID_INDEX) + { + a->ip4_fib_id = + fib_table_get_table_id_for_sw_if_index (FIB_PROTOCOL_IP4, + a->sw_if_index); + a->ip6_fib_id = + fib_table_get_table_id_for_sw_if_index (FIB_PROTOCOL_IP4, + a->sw_if_index); + } + if (a->sw_if_index == APP_NAMESPACE_INVALID_INDEX + && a->ip4_fib_id == APP_NAMESPACE_INVALID_INDEX) + return clib_error_return_code (0, VNET_API_ERROR_INVALID_VALUE, 0, + "sw_if_index or fib_id must be " + "configured"); + st = session_table_alloc (); + session_table_init (st); + + app_ns = app_namespace_get_from_id (a->ns_id); + if (!app_ns) + app_ns = app_namespace_alloc (a->ns_id); + app_ns->ns_secret = a->secret; + app_ns->sw_if_index = a->sw_if_index; + app_ns->local_table_index = session_table_index (st); + app_ns->ip4_fib_index = + fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id); + app_ns->ip6_fib_index = + fib_table_find (FIB_PROTOCOL_IP6, a->ip6_fib_id); + } + else + { + return clib_error_return_code (0, VNET_API_ERROR_UNIMPLEMENTED, 0, + "namespace deletion not supported"); + } + return 0; +} + +const u8 * +app_namespace_id (app_namespace_t * app_ns) +{ + return app_ns->ns_id; +} + +u32 +app_namespace_index_from_id (const u8 * ns_id) +{ + uword *indexp; + indexp = hash_get_mem (app_namespace_lookup_table, ns_id); + if (!indexp) + return APP_NAMESPACE_INVALID_INDEX; + return *indexp; +} + +const u8 * +app_namespace_id_from_index (u32 index) +{ + app_namespace_t *app_ns; + + app_ns = app_namespace_get (index); + return app_namespace_id (app_ns); +} + +void +app_namespaces_init (void) +{ + u8 *ns_id = format (0, "default"); + app_namespace_lookup_table = + hash_create_vec (0, sizeof (u8), sizeof (uword)); + + /* + * Allocate default namespace + */ + vnet_app_namespace_add_del_args_t a = { + .ns_id = ns_id, + .secret = 0, + .sw_if_index = APP_NAMESPACE_INVALID_INDEX, + .is_add = 1 + }; + vnet_app_namespace_add_del (&a); + vec_free (ns_id); +} + +static clib_error_t * +app_ns_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u8 is_add = 0, *ns_id = 0, secret_set = 0, sw_if_index_set = 0; + u32 sw_if_index, fib_id = APP_NAMESPACE_INVALID_INDEX; + u64 secret; + clib_error_t *error = 0; + + session_cli_return_if_not_enabled (); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "add")) + is_add = 1; + else if (unformat (input, "id %_%v%_", &ns_id)) + ; + else if (unformat (input, "secret %lu", &secret)) + secret_set = 1; + else if (unformat (input, "sw_if_index %u", &sw_if_index)) + sw_if_index_set = 1; + else if (unformat (input, "fib_id", &fib_id)) + ; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + if (!ns_id || !secret_set || !sw_if_index_set) + { + vlib_cli_output (vm, "namespace-id, secret and sw_if_index must be " + "provided"); + return 0; + } + + if (is_add) + { + vnet_app_namespace_add_del_args_t args = { + .ns_id = ns_id, + .secret = secret, + .sw_if_index = sw_if_index, + .ip4_fib_id = fib_id, + .is_add = 1 + }; + error = vnet_app_namespace_add_del (&args); + } + + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (app_ns_command, static) = +{ + .path = "app ns", + .short_help = "app ns [add] id <namespace-id> secret <secret> " + "sw_if_index <sw_if_index>", + .function = app_ns_fn, +}; +/* *INDENT-ON* */ + +u8 * +format_app_namespace (u8 * s, va_list * args) +{ + app_namespace_t *app_ns = va_arg (*args, app_namespace_t *); + s = format (s, "%-20v%-20lu%-20u", app_ns->ns_id, app_ns->ns_secret, + app_ns->sw_if_index); + return s; +} + +static clib_error_t * +show_app_ns_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + app_namespace_t *app_ns; + session_table_t *st; + u8 *ns_id, do_table = 0; + + session_cli_return_if_not_enabled (); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "table %_%v%_", &ns_id)) + do_table = 1; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + if (do_table) + { + app_ns = app_namespace_get_from_id (ns_id); + if (!app_ns) + { + vlib_cli_output (vm, "ns %v not found", ns_id); + return 0; + } + st = session_table_get (app_ns->local_table_index); + if (!st) + { + vlib_cli_output (vm, "table for ns %v could not be found", ns_id); + return 0; + } + session_lookup_show_table_entries (vm, st, 0, 1); + vec_free (ns_id); + return 0; + } + + vlib_cli_output (vm, "%-20s%-20s%-20s", "Namespace", "Secret", + "sw_if_index"); + + /* *INDENT-OFF* */ + pool_foreach (app_ns, app_namespace_pool, ({ + vlib_cli_output (vm, "%U", format_app_namespace, app_ns); + })); + /* *INDENT-ON* */ + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_app_ns_command, static) = +{ + .path = "show app ns", + .short_help = "show app ns", + .function = show_app_ns_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/application_namespace.h b/src/vnet/session/application_namespace.h new file mode 100644 index 00000000000..da3f6017961 --- /dev/null +++ b/src/vnet/session/application_namespace.h @@ -0,0 +1,83 @@ +/* + * 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/vnet.h> + +#ifndef SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ +#define SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ + +typedef struct _app_namespace +{ + /** + * Local sw_if_index that supports transport connections for this namespace + */ + u32 sw_if_index; + + /** + * Network namespace (e.g., fib_index associated to the sw_if_index) + * wherein connections are to be established. Since v4 and v6 fibs are + * separate, we actually need to keep pointers to both. + */ + u32 ip4_fib_index; + u32 ip6_fib_index; + + /** + * Local session table associated to ns + */ + u32 local_table_index; + + /** + * Secret apps need to provide to authorize attachment to the namespace + */ + u64 ns_secret; + + /** + * Application namespace id + */ + u8 *ns_id; +} app_namespace_t; + +typedef struct _vnet_app_namespace_add_del_args +{ + u8 *ns_id; + u64 secret; + u32 sw_if_index; + u32 ip4_fib_id; + u32 ip6_fib_id; + u8 is_add; +} vnet_app_namespace_add_del_args_t; + +#define APP_NAMESPACE_INVALID_INDEX ((u32)~0) + +app_namespace_t *app_namespace_alloc (u8 * ns_id); +app_namespace_t *app_namespace_get (u32 index); +app_namespace_t *app_namespace_get_from_id (const u8 * ns_id); +u32 app_namespace_index (app_namespace_t * app_ns); +const u8 *app_namespace_id (app_namespace_t * app_ns); +const u8 *app_namespace_id_from_index (u32 index); +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); + +#endif /* SRC_VNET_SESSION_APPLICATION_NAMESPACE_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/segment_manager.c b/src/vnet/session/segment_manager.c index 48d027553b1..f35dec72d88 100644 --- a/src/vnet/session/segment_manager.c +++ b/src/vnet/session/segment_manager.c @@ -267,7 +267,7 @@ segment_manager_del_sessions (segment_manager_t * sm) session_index = fifo->master_session_index; thread_index = fifo->master_thread_index; - session = stream_session_get (session_index, thread_index); + session = session_get (session_index, thread_index); /* Instead of directly removing the session call disconnect */ if (session->session_state != SESSION_STATE_CLOSED) diff --git a/src/vnet/session/session.api b/src/vnet/session/session.api index 992a0638698..12a5d10429c 100644 --- a/src/vnet/session/session.api +++ b/src/vnet/session/session.api @@ -21,12 +21,16 @@ vl_api_version 1.0.0 @param initial_segment_size - size of the initial shm segment to be allocated @param options - segment size, fifo sizes, etc. + @param namespace_id_len - length of the namespace id c-string + @param namespace_id - 0 terminted c-string */ define application_attach { u32 client_index; u32 context; u32 initial_segment_size; u64 options[16]; + u8 namespace_id_len; + u8 namespace_id [64]; }; /** \brief Application attach reply @@ -99,20 +103,19 @@ autoreply define unbind_uri { /** \brief Connect to a given URI @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param accept_cookie - sender accept cookie, to identify this bind flavor - @param uri - a URI, e.g. "tcp4://0.0.0.0/0/80" - "tcp6://::/0/80" [ipv6], etc. - @param options - socket options, fifo sizes, etc. passed by vpp to the - server when redirecting connects @param client_queue_address - binary API client queue address. Used by local server when connect was redirected. + @param options - socket options, fifo sizes, etc. passed by vpp to the + server when redirecting connects + @param uri - a URI, e.g. "tcp4://0.0.0.0/0/80" + "tcp6://::/0/80" [ipv6], etc. */ autoreply define connect_uri { u32 client_index; u32 context; - u8 uri[128]; u64 client_queue_address; u64 options[16]; + u8 uri[128]; }; /** \brief vpp->client, accept this session @@ -240,26 +243,25 @@ autoreply define unbind_sock { /** \brief Connect to a remote peer @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param app_connect - application connection id to be returned in reply + @param client_queue_address - client's API queue address. Non-zero when + used to perform redirects + @param options - socket options, fifo sizes, etc. when doing redirects @param vrf - connection namespace @param is_ip4 - flag that is 1 if ip address family is IPv4 @param ip - ip address @param port - port @param proto - protocol 0 - TCP 1 - UDP - @param client_queue_address - client's API queue address. Non-zero when - used to perform redirects - @param options - socket options, fifo sizes, etc. when doing redirects */ autoreply define connect_sock { u32 client_index; u32 context; + u64 client_queue_address; + u64 options[16]; u32 vrf; u8 is_ip4; u8 ip[16]; u16 port; u8 proto; - u64 client_queue_address; - u64 options[16]; }; /** \brief Bind reply @@ -326,6 +328,31 @@ autoreply define session_enable_disable { u8 is_enable; }; +/** \brief add/del application namespace + @param client_index - opaque cookie to identify the sender + client to vpp direction only + @param context - sender context, to match reply w/ request + @param secret - secret shared between app and vpp + @param sw_if_index - local interface that "supports" namespace. Set to + ~0 if no preference + @param ip4_fib_id - id of ip4 fib that "supports" the namespace. Ignored + if sw_if_index set. + @param ip6_fib_id - id of ip6 fib that "supports" the namespace. Ignored + if sw_if_index set. + @param namespace_id_len - length of namespace id lower + @param namespace_id - namespace id +*/ +autoreply define app_namespace_add_del { + u32 client_index; + u32 context; + u64 secret; + u32 sw_if_index; + u32 ip4_fib_id; + u32 ip6_fib_id; + u8 namespace_id_len; + u8 namespace_id[64]; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/vnet/session/session.c b/src/vnet/session/session.c index dc930ce87d3..88b38f15a61 100644 --- a/src/vnet/session/session.c +++ b/src/vnet/session/session.c @@ -85,7 +85,7 @@ stream_session_create_i (segment_manager_t * sm, transport_connection_t * tc, /* Add to the main lookup table */ value = stream_session_handle (s); - stream_session_table_add_for_tc (tc, value); + session_lookup_add_connection (tc, value); *ret_s = s; @@ -223,7 +223,7 @@ stream_session_enqueue_data (transport_connection_t * tc, vlib_buffer_t * b, stream_session_t *s; int enqueued = 0, rv, in_order_off; - s = stream_session_get (tc->s_index, tc->thread_index); + s = session_get (tc->s_index, tc->thread_index); if (is_in_order) { @@ -275,7 +275,7 @@ u8 stream_session_no_space (transport_connection_t * tc, u32 thread_index, u16 data_len) { - stream_session_t *s = stream_session_get (tc->s_index, thread_index); + stream_session_t *s = session_get (tc->s_index, thread_index); if (PREDICT_FALSE (s->session_state != SESSION_STATE_READY)) return 1; @@ -289,7 +289,7 @@ stream_session_no_space (transport_connection_t * tc, u32 thread_index, u32 stream_session_tx_fifo_max_dequeue (transport_connection_t * tc) { - stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index); + stream_session_t *s = session_get (tc->s_index, tc->thread_index); if (!s->server_tx_fifo) return 0; return svm_fifo_max_dequeue (s->server_tx_fifo); @@ -299,14 +299,14 @@ int stream_session_peek_bytes (transport_connection_t * tc, u8 * buffer, u32 offset, u32 max_bytes) { - stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index); + stream_session_t *s = session_get (tc->s_index, tc->thread_index); return svm_fifo_peek (s->server_tx_fifo, offset, max_bytes, buffer); } u32 stream_session_dequeue_drop (transport_connection_t * tc, u32 max_bytes) { - stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index); + stream_session_t *s = session_get (tc->s_index, tc->thread_index); return svm_fifo_dequeue_drop (s->server_tx_fifo, max_bytes); } @@ -432,7 +432,7 @@ stream_session_init_fifos_pointers (transport_connection_t * tc, u32 rx_pointer, u32 tx_pointer) { stream_session_t *s; - s = stream_session_get (tc->s_index, tc->thread_index); + s = session_get (tc->s_index, tc->thread_index); svm_fifo_init_pointers (s->server_rx_fifo, rx_pointer); svm_fifo_init_pointers (s->server_tx_fifo, tx_pointer); } @@ -445,20 +445,16 @@ stream_session_connect_notify (transport_connection_t * tc, u8 is_fail) u64 handle; u32 opaque = 0; int error = 0; - u8 st; - st = session_type_from_proto_and_ip (tc->transport_proto, tc->is_ip4); - handle = stream_session_half_open_lookup_handle (&tc->lcl_ip, &tc->rmt_ip, - tc->lcl_port, tc->rmt_port, - st); + handle = session_lookup_half_open_handle (tc); if (handle == HALF_OPEN_LOOKUP_INVALID_VALUE) { - TCP_DBG ("half-open was removed!"); + SESSION_DBG ("half-open was removed!"); return -1; } /* Cleanup half-open table */ - stream_session_half_open_table_del (tc); + session_lookup_del_half_open (tc); /* Get the app's index from the handle we stored when opening connection * and the opaque (api_context for external apps) from transport session @@ -489,7 +485,7 @@ stream_session_connect_notify (transport_connection_t * tc, u8 is_fail) if (app->cb_fns.session_connected_callback (app->index, opaque, new_s, is_fail)) { - clib_warning ("failed to notify app"); + SESSION_DBG ("failed to notify app"); if (!is_fail) stream_session_disconnect (new_s); } @@ -508,7 +504,7 @@ stream_session_accept_notify (transport_connection_t * tc) application_t *server; stream_session_t *s; - s = stream_session_get (tc->s_index, tc->thread_index); + s = session_get (tc->s_index, tc->thread_index); server = application_get (s->app_index); server->cb_fns.session_accept_callback (s); } @@ -526,7 +522,7 @@ stream_session_disconnect_notify (transport_connection_t * tc) application_t *server; stream_session_t *s; - s = stream_session_get (tc->s_index, tc->thread_index); + s = session_get (tc->s_index, tc->thread_index); server = application_get (s->app_index); server->cb_fns.session_disconnect_callback (s); } @@ -541,7 +537,7 @@ stream_session_delete (stream_session_t * s) int rv; /* Delete from the main lookup table. */ - if ((rv = stream_session_table_del (s))) + if ((rv = session_lookup_del_session (s))) clib_warning ("hash delete error, rv %d", rv); /* Cleanup fifo segments */ @@ -581,7 +577,7 @@ stream_session_reset_notify (transport_connection_t * tc) { stream_session_t *s; application_t *app; - s = stream_session_get (tc->s_index, tc->thread_index); + s = session_get (tc->s_index, tc->thread_index); app = application_get (s->app_index); app->cb_fns.session_reset_callback (s); @@ -592,14 +588,16 @@ stream_session_reset_notify (transport_connection_t * tc) */ int stream_session_accept (transport_connection_t * tc, u32 listener_index, - u8 sst, u8 notify) + u8 notify) { application_t *server; stream_session_t *s, *listener; segment_manager_t *sm; - + session_type_t sst; int rv; + sst = session_type_from_proto_and_ip (tc->transport_proto, tc->is_ip4); + /* Find the server */ listener = listen_session_get (sst, listener_index); server = application_get (listener->app_index); @@ -634,22 +632,23 @@ stream_session_accept (transport_connection_t * tc, u32 listener_index, * @param res Resulting transport connection . */ int -stream_session_open (u32 app_index, session_type_t st, - transport_endpoint_t * rmt, +stream_session_open (u32 app_index, session_endpoint_t * rmt, transport_connection_t ** res) { transport_connection_t *tc; + session_type_t sst; int rv; u64 handle; - rv = tp_vfts[st].open (rmt); + sst = session_type_from_proto_and_ip (rmt->transport_proto, rmt->is_ip4); + rv = tp_vfts[sst].open (session_endpoint_to_transport (rmt)); if (rv < 0) { clib_warning ("Transport failed to open connection."); - return VNET_API_ERROR_SESSION_CONNECT_FAIL; + return VNET_API_ERROR_SESSION_CONNECT; } - tc = tp_vfts[st].get_half_open ((u32) rv); + tc = tp_vfts[sst].get_half_open ((u32) rv); /* Save app and tc index. The latter is needed to help establish the * connection while the former is needed when the connect notify comes @@ -657,7 +656,7 @@ stream_session_open (u32 app_index, session_type_t st, handle = (((u64) app_index) << 32) | (u64) tc->c_index; /* Add to the half-open lookup table */ - stream_session_half_open_table_add (tc, handle); + session_lookup_add_half_open (tc, handle); *res = tc; @@ -673,13 +672,14 @@ stream_session_open (u32 app_index, session_type_t st, * @param tep Local endpoint to be listened on. */ int -stream_session_listen (stream_session_t * s, transport_endpoint_t * tep) +stream_session_listen (stream_session_t * s, session_endpoint_t * tep) { transport_connection_t *tc; u32 tci; /* Transport bind/listen */ - tci = tp_vfts[s->session_type].bind (s->session_index, tep); + tci = tp_vfts[s->session_type].bind (s->session_index, + session_endpoint_to_transport (tep)); if (tci == (u32) ~ 0) return -1; @@ -693,7 +693,7 @@ stream_session_listen (stream_session_t * s, transport_endpoint_t * tep) return -1; /* Add to the main lookup table */ - stream_session_table_add_for_tc (tc, s->session_index); + session_lookup_add_connection (tc, s->session_index); return 0; } @@ -721,7 +721,7 @@ stream_session_stop_listen (stream_session_t * s) return VNET_API_ERROR_ADDRESS_NOT_IN_USE; } - stream_session_table_del_for_tc (tc); + session_lookup_del_connection (tc); tp_vfts[s->session_type].unbind (s->connection_index); return 0; } @@ -780,7 +780,7 @@ stream_session_cleanup (stream_session_t * s) s->session_state = SESSION_STATE_CLOSED; /* Delete from the main lookup table to avoid more enqueues */ - rv = stream_session_table_del (s); + rv = session_lookup_del_session (s); if (rv) clib_warning ("hash delete error, rv %d", rv); @@ -837,6 +837,26 @@ session_type_from_proto_and_ip (transport_proto_t proto, u8 is_ip4) return SESSION_N_TYPES; } +int +listen_session_get_local_session_endpoint (stream_session_t * listener, + session_endpoint_t * sep) +{ + transport_connection_t *tc; + tc = + tp_vfts[listener->session_type].get_listener (listener->connection_index); + if (!tc) + { + clib_warning ("no transport"); + return -1; + } + + /* N.B. The ip should not be copied because this is the local endpoint */ + sep->port = tc->lcl_port; + sep->transport_proto = tc->transport_proto; + sep->is_ip4 = tc->is_ip4; + return 0; +} + static clib_error_t * session_manager_main_enable (vlib_main_t * vm) { @@ -903,6 +923,7 @@ session_manager_main_enable (vlib_main_t * vm) } session_lookup_init (); + app_namespaces_init (); smm->is_enabled = 1; @@ -927,14 +948,14 @@ session_node_enable_disable (u8 is_en) clib_error_t * vnet_session_enable_disable (vlib_main_t * vm, u8 is_en) { + clib_error_t *error = 0; if (is_en) { if (session_manager_main.is_enabled) return 0; session_node_enable_disable (is_en); - - return session_manager_main_enable (vm); + error = session_manager_main_enable (vm); } else { @@ -942,7 +963,7 @@ vnet_session_enable_disable (vlib_main_t * vm, u8 is_en) session_node_enable_disable (is_en); } - return 0; + return error; } clib_error_t * diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h index 83addec2744..b1a03d213e9 100644 --- a/src/vnet/session/session.h +++ b/src/vnet/session/session.h @@ -214,7 +214,7 @@ stream_session_is_valid (u32 si, u8 thread_index) } always_inline stream_session_t * -stream_session_get (u32 si, u32 thread_index) +session_get (u32 si, u32 thread_index) { ASSERT (stream_session_is_valid (si, thread_index)); return pool_elt_at_index (session_manager_main.sessions[thread_index], si); @@ -240,31 +240,31 @@ stream_session_handle (stream_session_t * s) } always_inline u32 -stream_session_index_from_handle (u64 handle) +session_index_from_handle (u64 handle) { return handle & 0xFFFFFFFF; } always_inline u32 -stream_session_thread_from_handle (u64 handle) +session_thread_from_handle (u64 handle) { return handle >> 32; } always_inline void -stream_session_parse_handle (u64 handle, u32 * index, u32 * thread_index) +session_parse_handle (u64 handle, u32 * index, u32 * thread_index) { - *index = stream_session_index_from_handle (handle); - *thread_index = stream_session_thread_from_handle (handle); + *index = session_index_from_handle (handle); + *thread_index = session_thread_from_handle (handle); } always_inline stream_session_t * -stream_session_get_from_handle (u64 handle) +session_get_from_handle (u64 handle) { session_manager_main_t *smm = &session_manager_main; - return pool_elt_at_index (smm->sessions[stream_session_thread_from_handle - (handle)], - stream_session_index_from_handle (handle)); + return + pool_elt_at_index (smm->sessions[session_thread_from_handle (handle)], + session_index_from_handle (handle)); } always_inline stream_session_t * @@ -285,14 +285,14 @@ stream_session_get_index (stream_session_t * s) always_inline u32 stream_session_max_rx_enqueue (transport_connection_t * tc) { - stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index); + stream_session_t *s = session_get (tc->s_index, tc->thread_index); return svm_fifo_max_enqueue (s->server_rx_fifo); } always_inline u32 stream_session_rx_fifo_size (transport_connection_t * tc) { - stream_session_t *s = stream_session_get (tc->s_index, tc->thread_index); + stream_session_t *s = session_get (tc->s_index, tc->thread_index); return s->server_rx_fifo->nitems; } @@ -316,12 +316,11 @@ void stream_session_delete_notify (transport_connection_t * tc); void stream_session_reset_notify (transport_connection_t * tc); int stream_session_accept (transport_connection_t * tc, u32 listener_index, - u8 sst, u8 notify); + u8 notify); int -stream_session_open (u32 app_index, session_type_t st, - transport_endpoint_t * tep, +stream_session_open (u32 app_index, session_endpoint_t * tep, transport_connection_t ** tc); -int stream_session_listen (stream_session_t * s, transport_endpoint_t * tep); +int stream_session_listen (stream_session_t * s, session_endpoint_t * tep); int stream_session_stop_listen (stream_session_t * s); void stream_session_disconnect (stream_session_t * s); void stream_session_cleanup (stream_session_t * s); @@ -401,6 +400,10 @@ listen_session_del (stream_session_t * s) pool_put (session_manager_main.listen_sessions[s->session_type], s); } +int +listen_session_get_local_session_endpoint (stream_session_t * listener, + session_endpoint_t * sep); + always_inline stream_session_t * session_manager_get_listener (u8 type, u32 index) { @@ -425,6 +428,12 @@ session_manager_is_enabled () return session_manager_main.is_enabled == 1; } +#define session_cli_return_if_not_enabled() \ +do { \ + if (!session_manager_main.is_enabled) \ + return clib_error_return(0, "session layer is not enabled"); \ +} while (0) + #endif /* __included_session_h__ */ /* diff --git a/src/vnet/session/session_api.c b/src/vnet/session/session_api.c index 60d9b4de51b..5bfca7be6fc 100755 --- a/src/vnet/session/session_api.c +++ b/src/vnet/session/session_api.c @@ -47,10 +47,11 @@ _(DISCONNECT_SESSION, disconnect_session) \ _(DISCONNECT_SESSION_REPLY, disconnect_session_reply) \ _(ACCEPT_SESSION_REPLY, accept_session_reply) \ _(RESET_SESSION_REPLY, reset_session_reply) \ -_(BIND_SOCK, bind_sock) \ +_(BIND_SOCK, bind_sock) \ _(UNBIND_SOCK, unbind_sock) \ _(CONNECT_SOCK, connect_sock) \ _(SESSION_ENABLE_DISABLE, session_enable_disable) \ +_(APP_NAMESPACE_ADD_DEL, app_namespace_add_del) \ static int send_add_segment_callback (u32 api_client_index, const u8 * segment_name, @@ -180,7 +181,7 @@ send_session_connected_callback (u32 app_index, u32 api_context, } else { - mp->retval = clib_host_to_net_u32 (VNET_API_ERROR_SESSION_CONNECT_FAIL); + mp->retval = clib_host_to_net_u32 (VNET_API_ERROR_SESSION_CONNECT); } vl_msg_api_send_shmem (q, (u8 *) & mp); @@ -195,7 +196,7 @@ send_session_connected_callback (u32 app_index, u32 api_context, static int redirect_connect_callback (u32 server_api_client_index, void *mp_arg) { - vl_api_connect_uri_t *mp = mp_arg; + vl_api_connect_sock_t *mp = mp_arg; unix_shared_memory_queue_t *server_q, *client_q; vlib_main_t *vm = vlib_get_main (); f64 timeout = vlib_time_now (vm) + 0.5; @@ -242,7 +243,7 @@ redirect_connect_callback (u32 server_api_client_index, void *mp_arg) { /* correctly enqueued */ case 0: - return VNET_CONNECT_REDIRECTED; + return VNET_API_ERROR_SESSION_REDIRECT; /* continue spinning, wait for pthread_mutex_trylock to work */ case -1: @@ -260,7 +261,7 @@ out: return rv; } -static session_cb_vft_t uri_session_cb_vft = { +static session_cb_vft_t session_cb_vft = { .session_accept_callback = send_session_accept_callback, .session_disconnect_callback = send_session_disconnect_callback, .session_connected_callback = send_session_connected_callback, @@ -285,7 +286,8 @@ vl_api_application_attach_t_handler (vl_api_application_attach_t * mp) { vl_api_application_attach_reply_t *rmp; vnet_app_attach_args_t _a, *a = &_a; - int rv; + clib_error_t *error = 0; + int rv = 0; if (session_manager_is_enabled () == 0) { @@ -298,12 +300,28 @@ vl_api_application_attach_t_handler (vl_api_application_attach_t * mp) "Out of options, fix api message definition"); memset (a, 0, sizeof (*a)); - a->api_client_index = mp->client_index; a->options = mp->options; - a->session_cb_vft = &uri_session_cb_vft; + a->session_cb_vft = &session_cb_vft; - rv = vnet_application_attach (a); + if (mp->namespace_id_len > 64) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto done; + } + + if (mp->namespace_id_len) + { + vec_validate (a->namespace_id, mp->namespace_id_len); + clib_memcpy (a->namespace_id, mp->namespace_id, mp->namespace_id_len); + } + + if ((error = vnet_application_attach (a))) + { + rv = clib_error_get_code (error); + clib_error_report (error); + } + vec_free (a->namespace_id); done: @@ -312,7 +330,6 @@ done: if (!rv) { rmp->segment_name_length = 0; - /* $$$$ policy? */ rmp->segment_size = a->segment_size; if (a->segment_name_length) { @@ -418,7 +435,8 @@ vl_api_connect_uri_t_handler (vl_api_connect_uri_t * mp) vl_api_connect_session_reply_t *rmp; vnet_connect_args_t _a, *a = &_a; application_t *app; - int rv; + clib_error_t *error = 0; + int rv = 0; if (session_manager_is_enabled () == 0) { @@ -433,14 +451,19 @@ vl_api_connect_uri_t_handler (vl_api_connect_uri_t * mp) a->api_context = mp->context; a->app_index = app->index; a->mp = mp; - rv = vnet_connect_uri (a); + if ((error = vnet_connect_uri (a))) + { + rv = clib_error_get_code (error); + if (rv != VNET_API_ERROR_SESSION_REDIRECT) + clib_error_report (error); + } } else { rv = VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } - if (rv == 0 || rv == VNET_CONNECT_REDIRECTED) + if (rv == 0 || rv == VNET_API_ERROR_SESSION_REDIRECT) return; /* Got some error, relay it */ @@ -516,7 +539,7 @@ vl_api_reset_session_reply_t_handler (vl_api_reset_session_reply_t * mp) if (!app) return; - stream_session_parse_handle (mp->handle, &index, &thread_index); + session_parse_handle (mp->handle, &index, &thread_index); s = stream_session_get_if_valid (index, thread_index); if (s == 0 || app->index != s->app_index) { @@ -552,7 +575,7 @@ vl_api_accept_session_reply_t_handler (vl_api_accept_session_reply_t * mp) } else { - stream_session_parse_handle (mp->handle, &session_index, &thread_index); + session_parse_handle (mp->handle, &session_index, &thread_index); s = stream_session_get_if_valid (session_index, thread_index); if (!s) { @@ -564,7 +587,6 @@ vl_api_accept_session_reply_t_handler (vl_api_accept_session_reply_t * mp) clib_warning ("app doesn't own session"); return; } - /* XXX volatile? */ s->session_state = SESSION_STATE_READY; } } @@ -581,7 +603,8 @@ vl_api_bind_sock_t_handler (vl_api_bind_sock_t * mp) { vl_api_bind_sock_reply_t *rmp; vnet_bind_args_t _a, *a = &_a; - int rv = VNET_API_ERROR_APPLICATION_NOT_ATTACHED; + int rv = 0; + clib_error_t *error; application_t *app; if (session_manager_is_enabled () == 0) @@ -594,18 +617,28 @@ vl_api_bind_sock_t_handler (vl_api_bind_sock_t * mp) if (app) { ip46_address_t *ip46 = (ip46_address_t *) mp->ip; - memset (a, 0, sizeof (*a)); - a->tep.is_ip4 = mp->is_ip4; - a->tep.ip = *ip46; - a->tep.port = mp->port; - a->tep.vrf = mp->vrf; + a->sep.is_ip4 = mp->is_ip4; + a->sep.ip = *ip46; + a->sep.port = mp->port; + a->sep.fib_index = mp->vrf; + a->sep.sw_if_index = ENDPOINT_INVALID_INDEX; a->app_index = app->index; + a->proto = mp->proto; - rv = vnet_bind (a); + if ((error = vnet_bind (a))) + { + rv = clib_error_get_code (error); + clib_error_report (error); + } } done: - REPLY_MACRO (VL_API_BIND_SOCK_REPLY); + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_BIND_SOCK_REPLY,({ + if (!rv) + rmp->handle = a->handle; + })); + /* *INDENT-ON* */ } static void @@ -614,7 +647,8 @@ vl_api_unbind_sock_t_handler (vl_api_unbind_sock_t * mp) vl_api_unbind_sock_reply_t *rmp; vnet_unbind_args_t _a, *a = &_a; application_t *app; - int rv = VNET_API_ERROR_APPLICATION_NOT_ATTACHED; + clib_error_t *error; + int rv = 0; if (session_manager_is_enabled () == 0) { @@ -627,7 +661,11 @@ vl_api_unbind_sock_t_handler (vl_api_unbind_sock_t * mp) { a->app_index = mp->client_index; a->handle = mp->handle; - rv = vnet_unbind (a); + if ((error = vnet_unbind (a))) + { + rv = clib_error_get_code (error); + clib_error_report (error); + } } done: @@ -640,7 +678,8 @@ vl_api_connect_sock_t_handler (vl_api_connect_sock_t * mp) vl_api_connect_session_reply_t *rmp; vnet_connect_args_t _a, *a = &_a; application_t *app; - int rv; + clib_error_t *error = 0; + int rv = 0; if (session_manager_is_enabled () == 0) { @@ -656,22 +695,28 @@ vl_api_connect_sock_t_handler (vl_api_connect_sock_t * mp) client_q = vl_api_client_index_to_input_queue (mp->client_index); mp->client_queue_address = pointer_to_uword (client_q); - a->tep.is_ip4 = mp->is_ip4; - a->tep.ip = *ip46; - a->tep.port = mp->port; - a->tep.vrf = mp->vrf; + a->sep.is_ip4 = mp->is_ip4; + a->sep.ip = *ip46; + a->sep.port = mp->port; + a->sep.transport_proto = mp->proto; + a->sep.fib_index = mp->vrf; + a->sep.sw_if_index = ENDPOINT_INVALID_INDEX; a->api_context = mp->context; a->app_index = app->index; - a->proto = mp->proto; a->mp = mp; - rv = vnet_connect (a); + if ((error = vnet_connect (a))) + { + rv = clib_error_get_code (error); + if (rv != VNET_API_ERROR_SESSION_REDIRECT) + clib_error_report (error); + } } else { rv = VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } - if (rv == 0 || rv == VNET_CONNECT_REDIRECTED) + if (rv == 0 || rv == VNET_API_ERROR_SESSION_REDIRECT) return; /* Got some error, relay it */ @@ -680,6 +725,46 @@ done: REPLY_MACRO (VL_API_CONNECT_SESSION_REPLY); } +static void +vl_api_app_namespace_add_del_t_handler (vl_api_app_namespace_add_del_t * mp) +{ + vl_api_app_namespace_add_del_reply_t *rmp; + u8 *ns_id = 0; + clib_error_t *error = 0; + int rv = 0; + if (!session_manager_is_enabled ()) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto done; + } + + if (mp->namespace_id_len > ARRAY_LEN (mp->namespace_id)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto done; + } + + vec_validate (ns_id, mp->namespace_id_len - 1); + clib_memcpy (ns_id, mp->namespace_id, mp->namespace_id_len); + vnet_app_namespace_add_del_args_t args = { + .ns_id = ns_id, + .secret = mp->secret, + .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index), + .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id), + .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id), + .is_add = 1 + }; + error = vnet_app_namespace_add_del (&args); + if (error) + { + rv = clib_error_get_code (error); + clib_error_report (error); + } + vec_free (ns_id); +done: + REPLY_MACRO (VL_API_APP_NAMESPACE_ADD_DEL_REPLY); +} + static clib_error_t * application_reaper_cb (u32 client_index) { diff --git a/src/vnet/session/session_cli.c b/src/vnet/session/session_cli.c index 8c30a1df7eb..588cb603d39 100755 --- a/src/vnet/session/session_cli.c +++ b/src/vnet/session/session_cli.c @@ -138,22 +138,21 @@ unformat_stream_session (unformat_input_t * input, va_list * args) stream_session_t *s; u8 proto = ~0; ip46_address_t lcl, rmt; - u32 lcl_port = 0, rmt_port = 0; - u8 is_ip4 = 0, s_type = ~0; + u32 lcl_port = 0, rmt_port = 0, fib_index = 0; + u8 is_ip4 = 0; if (!unformat (input, "%U", unformat_stream_session_id, &proto, &lcl, &rmt, &lcl_port, &rmt_port, &is_ip4)) return 0; - s_type = session_type_from_proto_and_ip (proto, is_ip4); if (is_ip4) - s = stream_session_lookup4 (&lcl.ip4, &rmt.ip4, - clib_host_to_net_u16 (lcl_port), - clib_host_to_net_u16 (rmt_port), s_type); + s = session_lookup4 (fib_index, &lcl.ip4, &rmt.ip4, + clib_host_to_net_u16 (lcl_port), + clib_host_to_net_u16 (rmt_port), proto); else - s = stream_session_lookup6 (&lcl.ip6, &rmt.ip6, - clib_host_to_net_u16 (lcl_port), - clib_host_to_net_u16 (rmt_port), s_type); + s = session_lookup6 (fib_index, &lcl.ip6, &rmt.ip6, + clib_host_to_net_u16 (lcl_port), + clib_host_to_net_u16 (rmt_port), proto); if (s) { *result = s; @@ -170,8 +169,8 @@ unformat_transport_connection (unformat_input_t * input, va_list * args) transport_connection_t *tc; u8 proto = ~0; ip46_address_t lcl, rmt; - u32 lcl_port = 0, rmt_port = 0; - u8 is_ip4 = 0, s_type = ~0; + u32 lcl_port = 0, rmt_port = 0, fib_index = 0; + u8 is_ip4 = 0; if (!unformat (input, "%U", unformat_stream_session_id, &proto, &lcl, &rmt, &lcl_port, &rmt_port, &is_ip4)) @@ -180,17 +179,14 @@ unformat_transport_connection (unformat_input_t * input, va_list * args) proto = (proto == (u8) ~ 0) ? suggested_proto : proto; if (proto == (u8) ~ 0) return 0; - s_type = session_type_from_proto_and_ip (proto, is_ip4); if (is_ip4) - tc = stream_session_lookup_transport4 (&lcl.ip4, &rmt.ip4, - clib_host_to_net_u16 (lcl_port), - clib_host_to_net_u16 (rmt_port), - s_type); + tc = session_lookup_connection4 (fib_index, &lcl.ip4, &rmt.ip4, + clib_host_to_net_u16 (lcl_port), + clib_host_to_net_u16 (rmt_port), proto); else - tc = stream_session_lookup_transport6 (&lcl.ip6, &rmt.ip6, - clib_host_to_net_u16 (lcl_port), - clib_host_to_net_u16 (rmt_port), - s_type); + tc = session_lookup_connection6 (fib_index, &lcl.ip6, &rmt.ip6, + clib_host_to_net_u16 (lcl_port), + clib_host_to_net_u16 (rmt_port), proto); if (tc) { diff --git a/src/vnet/session/session_debug.h b/src/vnet/session/session_debug.h index eb11f1a00b9..d9b71035314 100644 --- a/src/vnet/session/session_debug.h +++ b/src/vnet/session/session_debug.h @@ -31,11 +31,13 @@ typedef enum _session_evt_dbg #undef _ } session_evt_dbg_e; -#define SESSION_DBG (0) +#define SESSION_DEBUG (0 && TRANSPORT_DEBUG) #define SESSION_DEQ_NODE_EVTS (0) #define SESSION_EVT_POLL_DBG (1) -#if TRANSPORT_DEBUG && SESSION_DBG +#if SESSION_DEBUG + +#define SESSION_DBG(_fmt, _args...) clib_warning (_fmt, ##_args) #define DEC_SESSION_ETD(_s, _e, _size) \ struct \ @@ -78,7 +80,7 @@ typedef enum _session_evt_dbg do { _body; } while (0); \ } -#if SESSION_DEQ_NODE_EVTS +#if SESSION_DEQ_NODE_EVTS && SESSION_DEBUG > 1 #define SESSION_EVT_DEQ_NODE_HANDLER(_node_evt) \ { \ ELOG_TYPE_DECLARE (_e) = \ @@ -96,9 +98,9 @@ typedef enum _session_evt_dbg } #else #define SESSION_EVT_DEQ_NODE_HANDLER(_node_evt) -#endif +#endif /* SESSION_DEQ_NODE_EVTS */ -#if SESSION_DBG && SESSION_EVT_POLL_DBG +#if SESSION_EVT_POLL_DBG && SESSION_DEBUG > 1 #define SESSION_EVT_POLL_GAP(_smm, _my_thread_index) \ { \ ELOG_TYPE_DECLARE (_e) = \ @@ -122,7 +124,7 @@ typedef enum _session_evt_dbg #else #define SESSION_EVT_POLL_GAP(_smm, _my_thread_index) #define SESSION_EVT_POLL_GAP_TRACK_HANDLER(_smm, _my_thread_index) -#endif +#endif /* SESSION_EVT_POLL_DBG */ #define CONCAT_HELPER(_a, _b) _a##_b #define CC(_a, _b) CONCAT_HELPER(_a, _b) @@ -130,7 +132,8 @@ typedef enum _session_evt_dbg #else #define SESSION_EVT_DBG(_evt, _args...) -#endif +#define SESSION_DBG(_fmt, _args...) +#endif /* SESSION_DEBUG */ #endif /* SRC_VNET_SESSION_SESSION_DEBUG_H_ */ /* diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c index 4487b1c305f..796d93ec33e 100644 --- a/src/vnet/session/session_lookup.c +++ b/src/vnet/session/session_lookup.c @@ -27,10 +27,19 @@ #include <vppinfra/bihash_template.c> #include <vnet/session/session_lookup.h> #include <vnet/session/session.h> +#include <vnet/session/application.h> -static session_lookup_t session_lookup; +/** + * External vector of per transport virtual functions table + */ extern transport_proto_vft_t *tp_vfts; +/** + * Network namespace index (i.e., fib index) to session lookup table. We + * should have one per network protocol type but for now we only support IP4/6 + */ +static u32 *fib_index_to_table_index[2]; + /* *INDENT-OFF* */ /* 16 octets */ typedef CLIB_PACKED (struct { @@ -153,461 +162,859 @@ make_v6_ss_kv_from_tc (session_kv6_t * kv, transport_connection_t * t) session_type_from_proto_and_ip (t->transport_proto, 0)); } -/* - * Session lookup key; (src-ip, dst-ip, src-port, dst-port, session-type) - * Value: (owner thread index << 32 | session_index); + +static session_table_t * +session_table_get_or_alloc_for_connection (transport_connection_t * tc) +{ + session_table_t *st; + u32 table_index, fib_proto = transport_connection_fib_proto (tc); + if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index) + { + st = session_table_alloc (); + table_index = session_table_index (st); + vec_validate (fib_index_to_table_index[fib_proto], tc->fib_index); + fib_index_to_table_index[fib_proto][tc->fib_index] = table_index; + return st; + } + else + { + table_index = fib_index_to_table_index[fib_proto][tc->fib_index]; + return session_table_get (table_index); + } +} + +static session_table_t * +session_table_get_for_connection (transport_connection_t * tc) +{ + u32 fib_proto = transport_connection_fib_proto (tc); + if (vec_len (fib_index_to_table_index[fib_proto]) <= tc->fib_index) + return 0; + return + session_table_get (fib_index_to_table_index[fib_proto][tc->fib_index]); +} + +static session_table_t * +session_table_get_for_fib_index (u32 fib_proto, u32 fib_index) +{ + if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index) + return 0; + return session_table_get (fib_index_to_table_index[fib_proto][fib_index]); +} + +u32 +session_lookup_get_index_for_fib (u32 fib_proto, u32 fib_index) +{ + if (vec_len (fib_index_to_table_index[fib_proto]) <= fib_index) + return SESSION_TABLE_INVALID_INDEX; + return fib_index_to_table_index[fib_proto][fib_index]; +} + +/** + * Add transport connection to a session table + * + * Session lookup 5-tuple (src-ip, dst-ip, src-port, dst-port, session-type) + * is added to requested session table. + * + * @param tc transport connection to be added + * @param value value to be stored + * + * @return non-zero if failure */ -void -stream_session_table_add_for_tc (transport_connection_t * tc, u64 value) +int +session_lookup_add_connection (transport_connection_t * tc, u64 value) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + st = session_table_get_or_alloc_for_connection (tc); + if (!st) + return -1; if (tc->is_ip4) { make_v4_ss_kv_from_tc (&kv4, tc); kv4.value = value; - clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4, 1 /* is_add */ ); + return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, + 1 /* is_add */ ); } else { make_v6_ss_kv_from_tc (&kv6, tc); kv6.value = value; - clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6, 1 /* is_add */ ); + return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, + 1 /* is_add */ ); } } -void -stream_session_table_add (session_manager_main_t * smm, stream_session_t * s, - u64 value) +int +session_lookup_add_session_endpoint (u32 table_index, + session_endpoint_t * sep, u64 value) { - transport_connection_t *tc; + session_table_t *st; + session_kv4_t kv4; + session_kv6_t kv6; - tc = tp_vfts[s->session_type].get_connection (s->connection_index, - s->thread_index); - stream_session_table_add_for_tc (tc, value); + st = session_table_get (table_index); + if (!st) + return -1; + if (sep->is_ip4) + { + make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port, + sep->transport_proto); + kv4.value = value; + return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 1); + } + else + { + make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port, + sep->transport_proto); + kv6.value = value; + return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 1); + } } int -stream_session_table_del_for_tc (transport_connection_t * tc) +session_lookup_del_session_endpoint (u32 table_index, + session_endpoint_t * sep) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + st = session_table_get (table_index); + if (!st) + return -1; + if (sep->is_ip4) + { + make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port, + sep->transport_proto); + return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0); + } + else + { + make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port, + sep->transport_proto); + return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0); + } +} + +/** + * Delete transport connection from session table + * + * @param table_index session table index + * @param tc transport connection to be removed + * + * @return non-zero if failure + */ +int +session_lookup_del_connection (transport_connection_t * tc) +{ + session_table_t *st; + session_kv4_t kv4; + session_kv6_t kv6; + + st = session_table_get_for_connection (tc); + if (!st) + return -1; if (tc->is_ip4) { make_v4_ss_kv_from_tc (&kv4, tc); - return clib_bihash_add_del_16_8 (&sl->v4_session_hash, &kv4, + return clib_bihash_add_del_16_8 (&st->v4_session_hash, &kv4, 0 /* is_add */ ); } else { make_v6_ss_kv_from_tc (&kv6, tc); - return clib_bihash_add_del_48_8 (&sl->v6_session_hash, &kv6, + return clib_bihash_add_del_48_8 (&st->v6_session_hash, &kv6, 0 /* is_add */ ); } - - return 0; } int -stream_session_table_del (stream_session_t * s) +session_lookup_del_session (stream_session_t * s) { transport_connection_t *ts; ts = tp_vfts[s->session_type].get_connection (s->connection_index, s->thread_index); - return stream_session_table_del_for_tc (ts); + return session_lookup_del_connection (ts); } - -void -stream_session_half_open_table_add (transport_connection_t * tc, u64 value) +u32 +session_lookup_session_endpoint (u32 table_index, session_endpoint_t * sep) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + int rv; - if (tc->is_ip4) + st = session_table_get (table_index); + if (!st) + return SESSION_INVALID_INDEX; + if (sep->is_ip4) { - make_v4_ss_kv_from_tc (&kv4, tc); - kv4.value = value; - (void) clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4, - 1 /* is_add */ ); + make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port, + sep->transport_proto); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); + if (rv == 0) + return (u32) kv4.value; } else { - make_v6_ss_kv_from_tc (&kv6, tc); - kv6.value = value; - (void) clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6, - 1 /* is_add */ ); + make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port, + sep->transport_proto); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); + if (rv == 0) + return (u32) kv6.value; } + return SESSION_INVALID_INDEX; } -void -stream_session_half_open_table_del (transport_connection_t * tc) +u32 +session_lookup_local_session_endpoint (u32 table_index, + session_endpoint_t * sep) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; + int rv; - if (tc->is_ip4) + st = session_table_get (table_index); + if (!st) + return SESSION_INVALID_INDEX; + if (sep->is_ip4) { - make_v4_ss_kv_from_tc (&kv4, tc); - clib_bihash_add_del_16_8 (&sl->v4_half_open_hash, &kv4, - 0 /* is_add */ ); + make_v4_listener_kv (&kv4, &sep->ip.ip4, sep->port, + sep->transport_proto); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); + if (rv == 0) + return (u32) kv4.value; + + /* + * Zero out the ip. Logic is that connect to local ips, say + * 127.0.0.1:port, can match 0.0.0.0:port + */ + kv4.key[0] = 0; + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); + if (rv == 0) + return (u32) kv4.value; } else { - make_v6_ss_kv_from_tc (&kv6, tc); - clib_bihash_add_del_48_8 (&sl->v6_half_open_hash, &kv6, - 0 /* is_add */ ); + make_v6_listener_kv (&kv6, &sep->ip.ip6, sep->port, + sep->transport_proto); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); + if (rv == 0) + return (u32) kv6.value; + + /* + * Zero out the ip. Same logic as above. + */ + kv6.key[0] = kv6.key[1] = 0; + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); + if (rv == 0) + return (u32) kv6.value; } + return SESSION_INVALID_INDEX; } -stream_session_t * -stream_session_lookup_listener4 (ip4_address_t * lcl, u16 lcl_port, u8 proto) +static stream_session_t * +session_lookup_listener4_i (session_table_t * st, ip4_address_t * lcl, + u16 lcl_port, u8 proto) { - session_lookup_t *sl = &session_lookup; session_kv4_t kv4; int rv; make_v4_listener_kv (&kv4, lcl, lcl_port, proto); - rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) return session_manager_get_listener (proto, (u32) kv4.value); /* Zero out the lcl ip */ kv4.key[0] = 0; - rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) return session_manager_get_listener (proto, (u32) kv4.value); return 0; } -/** Looks up a session based on the 5-tuple passed as argument. - * - * First it tries to find an established session, if this fails, it tries - * finding a listener session if this fails, it tries a lookup with a - * wildcarded local source (listener bound to all interfaces) - */ stream_session_t * -stream_session_lookup4 (ip4_address_t * lcl, ip4_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto) +session_lookup_listener4 (u32 fib_index, ip4_address_t * lcl, u16 lcl_port, + u8 proto) { - session_lookup_t *sl = &session_lookup; - session_kv4_t kv4; - stream_session_t *s; - int rv; - - /* Lookup session amongst established ones */ - make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4); - if (rv == 0) - return stream_session_get_from_handle (kv4.value); - - /* If nothing is found, check if any listener is available */ - if ((s = stream_session_lookup_listener4 (lcl, lcl_port, proto))) - return s; - - /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4); - if (rv == 0) - return stream_session_get_from_handle (kv4.value); - return 0; + session_table_t *st; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index); + if (!st) + return 0; + return session_lookup_listener4_i (st, lcl, lcl_port, proto); } -stream_session_t * -stream_session_lookup_listener6 (ip6_address_t * lcl, u16 lcl_port, u8 proto) +static stream_session_t * +session_lookup_listener6_i (session_table_t * st, ip6_address_t * lcl, + u16 lcl_port, u8 proto) { - session_lookup_t *sl = &session_lookup; session_kv6_t kv6; int rv; make_v6_listener_kv (&kv6, lcl, lcl_port, proto); - rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) return session_manager_get_listener (proto, (u32) kv6.value); /* Zero out the lcl ip */ kv6.key[0] = kv6.key[1] = 0; - rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) return session_manager_get_listener (proto, (u32) kv6.value); return 0; } -/* Looks up a session based on the 5-tuple passed as argument. - * First it tries to find an established session, if this fails, it tries - * finding a listener session if this fails, it tries a lookup with a - * wildcarded local source (listener bound to all interfaces) */ stream_session_t * -stream_session_lookup6 (ip6_address_t * lcl, ip6_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto) +session_lookup_listener6 (u32 fib_index, ip6_address_t * lcl, u16 lcl_port, + u8 proto) { - session_lookup_t *sl = &session_lookup; - session_kv6_t kv6; - stream_session_t *s; - int rv; + session_table_t *st; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index); + if (!st) + return 0; + return session_lookup_listener6_i (st, lcl, lcl_port, proto); +} - make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6); - if (rv == 0) - return stream_session_get_from_handle (kv6.value); +stream_session_t * +session_lookup_listener (u32 table_index, session_endpoint_t * sep) +{ + session_table_t *st; + st = session_table_get (table_index); + if (!st) + return 0; + if (sep->is_ip4) + return session_lookup_listener4_i (st, &sep->ip.ip4, sep->port, + sep->transport_proto); + else + return session_lookup_listener6_i (st, &sep->ip.ip6, sep->port, + sep->transport_proto); + return 0; +} - /* If nothing is found, check if any listener is available */ - if ((s = stream_session_lookup_listener6 (lcl, lcl_port, proto))) - return s; +int +session_lookup_add_half_open (transport_connection_t * tc, u64 value) +{ + session_table_t *st; + session_kv4_t kv4; + session_kv6_t kv6; - /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6); - if (rv == 0) - return stream_session_get_from_handle (kv6.value); - return 0; + st = session_table_get_or_alloc_for_connection (tc); + if (!st) + return 0; + if (tc->is_ip4) + { + make_v4_ss_kv_from_tc (&kv4, tc); + kv4.value = value; + return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4, + 1 /* is_add */ ); + } + else + { + make_v6_ss_kv_from_tc (&kv6, tc); + kv6.value = value; + return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6, + 1 /* is_add */ ); + } } -stream_session_t * -stream_session_lookup_listener (ip46_address_t * lcl, u16 lcl_port, u8 proto) +int +session_lookup_del_half_open (transport_connection_t * tc) { - switch (proto) + session_table_t *st; + session_kv4_t kv4; + session_kv6_t kv6; + + st = session_table_get_for_connection (tc); + if (!st) + return -1; + if (tc->is_ip4) { - case SESSION_TYPE_IP4_UDP: - case SESSION_TYPE_IP4_TCP: - return stream_session_lookup_listener4 (&lcl->ip4, lcl_port, proto); - break; - case SESSION_TYPE_IP6_UDP: - case SESSION_TYPE_IP6_TCP: - return stream_session_lookup_listener6 (&lcl->ip6, lcl_port, proto); - break; + make_v4_ss_kv_from_tc (&kv4, tc); + return clib_bihash_add_del_16_8 (&st->v4_half_open_hash, &kv4, + 0 /* is_add */ ); + } + else + { + make_v6_ss_kv_from_tc (&kv6, tc); + return clib_bihash_add_del_48_8 (&st->v6_half_open_hash, &kv6, + 0 /* is_add */ ); } - return 0; } u64 -stream_session_half_open_lookup_handle (ip46_address_t * lcl, - ip46_address_t * rmt, u16 lcl_port, - u16 rmt_port, u8 proto) +session_lookup_half_open_handle (transport_connection_t * tc) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; session_kv6_t kv6; int rv; - switch (proto) + st = session_table_get_for_fib_index (transport_connection_fib_proto (tc), + tc->fib_index); + if (!st) + return HALF_OPEN_LOOKUP_INVALID_VALUE; + if (tc->is_ip4) { - case SESSION_TYPE_IP4_UDP: - case SESSION_TYPE_IP4_TCP: - make_v4_ss_kv (&kv4, &lcl->ip4, &rmt->ip4, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4); - + make_v4_ss_kv (&kv4, &tc->lcl_ip.ip4, &tc->rmt_ip.ip4, tc->lcl_port, + tc->rmt_port, tc->transport_proto); + rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4); if (rv == 0) return kv4.value; - - return HALF_OPEN_LOOKUP_INVALID_VALUE; - break; - case SESSION_TYPE_IP6_UDP: - case SESSION_TYPE_IP6_TCP: - make_v6_ss_kv (&kv6, &lcl->ip6, &rmt->ip6, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6); - + } + else + { + make_v6_ss_kv (&kv6, &tc->lcl_ip.ip6, &tc->rmt_ip.ip6, tc->lcl_port, + tc->rmt_port, tc->transport_proto); + rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6); if (rv == 0) return kv6.value; - - return HALF_OPEN_LOOKUP_INVALID_VALUE; - break; } return HALF_OPEN_LOOKUP_INVALID_VALUE; } transport_connection_t * -stream_session_half_open_lookup (ip46_address_t * lcl, ip46_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto) +session_lookup_half_open_connection (u64 handle, u8 proto, u8 is_ip4) { - u64 handle; - handle = - stream_session_half_open_lookup_handle (lcl, rmt, lcl_port, rmt_port, - proto); + u32 sst; + if (handle != HALF_OPEN_LOOKUP_INVALID_VALUE) - return tp_vfts[proto].get_half_open (handle & 0xFFFFFFFF); + { + sst = session_type_from_proto_and_ip (proto, is_ip4); + return tp_vfts[sst].get_half_open (handle & 0xFFFFFFFF); + } return 0; } -always_inline stream_session_t * -stream_session_get_tsi (u64 ti_and_si, u32 thread_index) -{ - ASSERT ((u32) (ti_and_si >> 32) == thread_index); - return pool_elt_at_index (session_manager_main.sessions[thread_index], - ti_and_si & 0xFFFFFFFFULL); -} - +/** + * Lookup connection with ip4 and transport layer information + * + * This is used on the fast path so it needs to be fast. Thereby, + * duplication of code and 'hacks' allowed. + * + * The lookup is incremental and returns whenever something is matched. The + * steps are: + * - Try to find an established session + * - Try to find a fully-formed or local source wildcarded (listener bound to + * all interfaces) listener session + * - Try to find a half-open connection + * - return 0 + * + * @param fib_index index of fib wherein the connection was received + * @param lcl local ip4 address + * @param rmt remote ip4 address + * @param lcl_port local port + * @param rmt_port remote port + * @param proto transport protocol (e.g., tcp, udp) + * @param thread_index thread index for request + * + * @return pointer to transport connection, if one is found, 0 otherwise + */ transport_connection_t * -stream_session_lookup_transport_wt4 (ip4_address_t * lcl, ip4_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto, - u32 my_thread_index) +session_lookup_connection_wt4 (u32 fib_index, ip4_address_t * lcl, + ip4_address_t * rmt, u16 lcl_port, + u16 rmt_port, u8 proto, u32 thread_index) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; stream_session_t *s; int rv; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + /* Lookup session amongst established ones */ make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) { - s = stream_session_get_tsi (kv4.value, my_thread_index); + ASSERT ((u32) (kv4.value >> 32) == thread_index); + s = session_get (kv4.value & 0xFFFFFFFFULL, thread_index); return tp_vfts[s->session_type].get_connection (s->connection_index, - my_thread_index); + thread_index); } /* If nothing is found, check if any listener is available */ - s = stream_session_lookup_listener4 (lcl, lcl_port, proto); + s = session_lookup_listener4_i (st, lcl, lcl_port, proto); if (s) return tp_vfts[s->session_type].get_listener (s->connection_index); /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4); if (rv == 0) - return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF); + { + u32 sst = session_type_from_proto_and_ip (proto, 1); + return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF); + } return 0; } +/** + * Lookup connection with ip4 and transport layer information + * + * Not optimized. This is used on the fast path so it needs to be fast. + * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical + * to that of @ref session_lookup_connection_wt4 + * + * @param fib_index index of the fib wherein the connection was received + * @param lcl local ip4 address + * @param rmt remote ip4 address + * @param lcl_port local port + * @param rmt_port remote port + * @param proto transport protocol (e.g., tcp, udp) + * + * @return pointer to transport connection, if one is found, 0 otherwise + */ transport_connection_t * -stream_session_lookup_transport4 (ip4_address_t * lcl, ip4_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto) +session_lookup_connection4 (u32 fib_index, ip4_address_t * lcl, + ip4_address_t * rmt, u16 lcl_port, u16 rmt_port, + u8 proto) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; session_kv4_t kv4; stream_session_t *s; int rv; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + /* Lookup session amongst established ones */ make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_16_8 (&sl->v4_session_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); if (rv == 0) { - s = stream_session_get_from_handle (kv4.value); + s = session_get_from_handle (kv4.value); return tp_vfts[s->session_type].get_connection (s->connection_index, s->thread_index); } /* If nothing is found, check if any listener is available */ - s = stream_session_lookup_listener4 (lcl, lcl_port, proto); + s = session_lookup_listener4_i (st, lcl, lcl_port, proto); if (s) return tp_vfts[s->session_type].get_listener (s->connection_index); /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_16_8 (&sl->v4_half_open_hash, &kv4); + rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4); if (rv == 0) - return tp_vfts[proto].get_half_open (kv4.value & 0xFFFFFFFF); + { + u32 sst = session_type_from_proto_and_ip (proto, 1); + return tp_vfts[sst].get_half_open (kv4.value & 0xFFFFFFFF); + } return 0; } +/** + * Lookup session with ip4 and transport layer information + * + * Lookup logic is identical to that of @ref session_lookup_connection_wt4 but + * this returns a session as opposed to a transport connection; + */ +stream_session_t * +session_lookup4 (u32 fib_index, ip4_address_t * lcl, ip4_address_t * rmt, + u16 lcl_port, u16 rmt_port, u8 proto) +{ + session_table_t *st; + session_kv4_t kv4; + stream_session_t *s; + int rv; + + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP4, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + + /* Lookup session amongst established ones */ + make_v4_ss_kv (&kv4, lcl, rmt, lcl_port, rmt_port, proto); + rv = clib_bihash_search_inline_16_8 (&st->v4_session_hash, &kv4); + if (rv == 0) + return session_get_from_handle (kv4.value); + + /* If nothing is found, check if any listener is available */ + if ((s = session_lookup_listener4_i (st, lcl, lcl_port, proto))) + return s; + + /* Finally, try half-open connections */ + rv = clib_bihash_search_inline_16_8 (&st->v4_half_open_hash, &kv4); + if (rv == 0) + return session_get_from_handle (kv4.value); + return 0; +} + +/** + * Lookup connection with ip6 and transport layer information + * + * This is used on the fast path so it needs to be fast. Thereby, + * duplication of code and 'hacks' allowed. + * + * The lookup is incremental and returns whenever something is matched. The + * steps are: + * - Try to find an established session + * - Try to find a fully-formed or local source wildcarded (listener bound to + * all interfaces) listener session + * - Try to find a half-open connection + * - return 0 + * + * @param fib_index index of the fib wherein the connection was received + * @param lcl local ip6 address + * @param rmt remote ip6 address + * @param lcl_port local port + * @param rmt_port remote port + * @param proto transport protocol (e.g., tcp, udp) + * @param thread_index thread index for request + * + * @return pointer to transport connection, if one is found, 0 otherwise + */ transport_connection_t * -stream_session_lookup_transport_wt6 (ip6_address_t * lcl, ip6_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto, - u32 my_thread_index) +session_lookup_connection_wt6 (u32 fib_index, ip6_address_t * lcl, + ip6_address_t * rmt, u16 lcl_port, + u16 rmt_port, u8 proto, u32 thread_index) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; stream_session_t *s; session_kv6_t kv6; int rv; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) { - s = stream_session_get_tsi (kv6.value, my_thread_index); + ASSERT ((u32) (kv6.value >> 32) == thread_index); + s = session_get (kv6.value & 0xFFFFFFFFULL, thread_index); return tp_vfts[s->session_type].get_connection (s->connection_index, - my_thread_index); + thread_index); } /* If nothing is found, check if any listener is available */ - s = stream_session_lookup_listener6 (lcl, lcl_port, proto); + s = session_lookup_listener6_i (st, lcl, lcl_port, proto); if (s) return tp_vfts[s->session_type].get_listener (s->connection_index); /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6); if (rv == 0) - return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF); + { + u32 sst = session_type_from_proto_and_ip (proto, 1); + return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF); + } return 0; } +/** + * Lookup connection with ip6 and transport layer information + * + * Not optimized. This is used on the fast path so it needs to be fast. + * Thereby, duplication of code and 'hacks' allowed. Lookup logic is identical + * to that of @ref session_lookup_connection_wt4 + * + * @param fib_index index of the fib wherein the connection was received + * @param lcl local ip6 address + * @param rmt remote ip6 address + * @param lcl_port local port + * @param rmt_port remote port + * @param proto transport protocol (e.g., tcp, udp) + * + * @return pointer to transport connection, if one is found, 0 otherwise + */ transport_connection_t * -stream_session_lookup_transport6 (ip6_address_t * lcl, ip6_address_t * rmt, - u16 lcl_port, u16 rmt_port, u8 proto) +session_lookup_connection6 (u32 fib_index, ip6_address_t * lcl, + ip6_address_t * rmt, u16 lcl_port, u16 rmt_port, + u8 proto) { - session_lookup_t *sl = &session_lookup; + session_table_t *st; stream_session_t *s; session_kv6_t kv6; int rv; + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto); - rv = clib_bihash_search_inline_48_8 (&sl->v6_session_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); if (rv == 0) { - s = stream_session_get_from_handle (kv6.value); + s = session_get_from_handle (kv6.value); return tp_vfts[s->session_type].get_connection (s->connection_index, s->thread_index); } /* If nothing is found, check if any listener is available */ - s = stream_session_lookup_listener6 (lcl, lcl_port, proto); + s = session_lookup_listener6 (fib_index, lcl, lcl_port, proto); if (s) return tp_vfts[s->session_type].get_listener (s->connection_index); /* Finally, try half-open connections */ - rv = clib_bihash_search_inline_48_8 (&sl->v6_half_open_hash, &kv6); + rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6); + if (rv == 0) + { + u32 sst = session_type_from_proto_and_ip (proto, 1); + return tp_vfts[sst].get_half_open (kv6.value & 0xFFFFFFFF); + } + + return 0; +} + +/** + * Lookup session with ip6 and transport layer information + * + * Lookup logic is identical to that of @ref session_lookup_connection_wt6 but + * this returns a session as opposed to a transport connection; + */ +stream_session_t * +session_lookup6 (u32 fib_index, ip6_address_t * lcl, ip6_address_t * rmt, + u16 lcl_port, u16 rmt_port, u8 proto) +{ + session_table_t *st; + session_kv6_t kv6; + stream_session_t *s; + int rv; + + st = session_table_get_for_fib_index (FIB_PROTOCOL_IP6, fib_index); + if (PREDICT_FALSE (!st)) + return 0; + + make_v6_ss_kv (&kv6, lcl, rmt, lcl_port, rmt_port, proto); + rv = clib_bihash_search_inline_48_8 (&st->v6_session_hash, &kv6); + if (rv == 0) + return session_get_from_handle (kv6.value); + + /* If nothing is found, check if any listener is available */ + if ((s = session_lookup_listener6_i (st, lcl, lcl_port, proto))) + return s; + + /* Finally, try half-open connections */ + rv = clib_bihash_search_inline_48_8 (&st->v6_half_open_hash, &kv6); if (rv == 0) - return tp_vfts[proto].get_half_open (kv6.value & 0xFFFFFFFF); + return session_get_from_handle (kv6.value); + return 0; +} +u64 +session_lookup_local_listener_make_handle (session_endpoint_t * sep) +{ + return ((u64) SESSION_LOCAL_TABLE_PREFIX << 32 + | (u32) sep->port << 16 | (u32) sep->transport_proto << 8 + | (u32) sep->is_ip4); +} + +u8 +session_lookup_local_is_handle (u64 handle) +{ + if (handle >> 32 == SESSION_LOCAL_TABLE_PREFIX) + return 1; return 0; } -#define foreach_hash_table_parameter \ - _(v4,session,buckets,20000) \ - _(v4,session,memory,(64<<20)) \ - _(v6,session,buckets,20000) \ - _(v6,session,memory,(64<<20)) \ - _(v4,halfopen,buckets,20000) \ - _(v4,halfopen,memory,(64<<20)) \ - _(v6,halfopen,buckets,20000) \ - _(v6,halfopen,memory,(64<<20)) +int +session_lookup_local_listener_parse_handle (u64 handle, + session_endpoint_t * sep) +{ + u32 local_table_handle; + if (handle >> 32 != SESSION_LOCAL_TABLE_PREFIX) + return -1; + local_table_handle = handle & 0xFFFFFFFFULL; + sep->is_ip4 = local_table_handle & 0xff; + local_table_handle >>= 8; + sep->transport_proto = local_table_handle & 0xff; + sep->port = local_table_handle >> 8; + return 0; +} + +u8 * +format_ip4_session_lookup_kvp (u8 * s, va_list * args) +{ + clib_bihash_kv_16_8_t *kvp = va_arg (*args, clib_bihash_kv_16_8_t *); + u32 is_local = va_arg (*args, u32); + u8 *app_name, *str = 0; + stream_session_t *session; + v4_connection_key_t *key = (v4_connection_key_t *) kvp->key; + + char *proto = key->proto == TRANSPORT_PROTO_TCP ? "T" : "U"; + if (!is_local) + { + session = session_get_from_handle (kvp->value); + app_name = application_name_from_index (session->app_index); + str = format (0, "[%s] %U:%d->%U:%d", proto, format_ip4_address, + &key->src, clib_net_to_host_u16 (key->src_port), + format_ip4_address, &key->dst, + clib_net_to_host_u16 (key->dst_port)); + s = format (s, "%-40v%-30v", str, app_name); + } + else + { + app_name = application_name_from_index (kvp->value); + str = format (0, "[%s] %U:%d", proto, format_ip4_address, + &key->src, clib_net_to_host_u16 (key->src_port)); + s = format (s, "%-30v%-30v", str, app_name); + } + vec_free (app_name); + return s; +} + +typedef struct _ip4_session_table_show_ctx_t +{ + vlib_main_t *vm; + u8 is_local; +} ip4_session_table_show_ctx_t; + +static int +ip4_session_table_show (clib_bihash_kv_16_8_t * kvp, void *arg) +{ + ip4_session_table_show_ctx_t *ctx = arg; + vlib_cli_output (ctx->vm, "%U", format_ip4_session_lookup_kvp, kvp, + ctx->is_local); + return 1; +} + +void +session_lookup_show_table_entries (vlib_main_t * vm, session_table_t * table, + u8 type, u8 is_local) +{ + ip4_session_table_show_ctx_t ctx = { + .vm = vm, + .is_local = is_local, + }; + if (!is_local) + vlib_cli_output (vm, "%-40s%-30s", "Session", "Application"); + else + vlib_cli_output (vm, "%-30s%-30s", "Listener", "Application"); + switch (type) + { + /* main table v4 */ + case 0: + ip4_session_table_walk (&table->v4_session_hash, ip4_session_table_show, + &ctx); + break; + default: + clib_warning ("not supported"); + } +} void session_lookup_init (void) { - session_lookup_t *sl = &session_lookup; - -#define _(af,table,parm,value) \ - u32 configured_##af##_##table##_table_##parm = value; - foreach_hash_table_parameter; -#undef _ - -#define _(af,table,parm,value) \ - if (session_manager_main.configured_##af##_##table##_table_##parm) \ - configured_##af##_##table##_table_##parm = \ - session_manager_main.configured_##af##_##table##_table_##parm; - foreach_hash_table_parameter; -#undef _ - - clib_bihash_init_16_8 (&sl->v4_session_hash, "v4 session table", - configured_v4_session_table_buckets, - configured_v4_session_table_memory); - clib_bihash_init_48_8 (&sl->v6_session_hash, "v6 session table", - configured_v6_session_table_buckets, - configured_v6_session_table_memory); - clib_bihash_init_16_8 (&sl->v4_half_open_hash, "v4 half-open table", - configured_v4_halfopen_table_buckets, - configured_v4_halfopen_table_memory); - clib_bihash_init_48_8 (&sl->v6_half_open_hash, "v6 half-open table", - configured_v6_halfopen_table_buckets, - configured_v6_halfopen_table_memory); + /* + * Allocate default table and map it to fib_index 0 + */ + session_table_t *st = session_table_alloc (); + vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP4], 0); + fib_index_to_table_index[FIB_PROTOCOL_IP4][0] = session_table_index (st); + session_table_init (st); + st = session_table_alloc (); + vec_validate (fib_index_to_table_index[FIB_PROTOCOL_IP6], 0); + fib_index_to_table_index[FIB_PROTOCOL_IP6][0] = session_table_index (st); + session_table_init (st); } /* diff --git a/src/vnet/session/session_lookup.h b/src/vnet/session/session_lookup.h index cf1dc01356e..20cbaf2acd6 100644 --- a/src/vnet/session/session_lookup.h +++ b/src/vnet/session/session_lookup.h @@ -16,77 +16,73 @@ #ifndef SRC_VNET_SESSION_SESSION_LOOKUP_H_ #define SRC_VNET_SESSION_SESSION_LOOKUP_H_ +#include <vnet/session/session_table.h> #include <vnet/session/stream_session.h> #include <vnet/session/transport.h> -typedef struct _session_lookup -{ - /** Lookup tables for established sessions and listeners */ - clib_bihash_16_8_t v4_session_hash; - clib_bihash_48_8_t v6_session_hash; - - /** Lookup tables for half-open sessions */ - clib_bihash_16_8_t v4_half_open_hash; - clib_bihash_48_8_t v6_half_open_hash; -} session_lookup_t; - -stream_session_t *stream_session_lookup_listener4 (ip4_address_t * lcl, - u16 lcl_port, u8 proto); -stream_session_t *stream_session_lookup4 (ip4_address_t * lcl, - ip4_address_t * rmt, u16 lcl_port, - u16 rmt_port, u8 proto); -stream_session_t *stream_session_lookup_listener6 (ip6_address_t * lcl, - u16 lcl_port, u8 proto); -stream_session_t *stream_session_lookup6 (ip6_address_t * lcl, - ip6_address_t * rmt, u16 lcl_port, - u16 rmt_port, u8 proto); -transport_connection_t *stream_session_lookup_transport_wt4 (ip4_address_t * - lcl, - ip4_address_t * - rmt, - u16 lcl_port, - u16 rmt_port, - u8 proto, - u32 - thread_index); -transport_connection_t *stream_session_lookup_transport4 (ip4_address_t * lcl, - ip4_address_t * rmt, - u16 lcl_port, - u16 rmt_port, - u8 proto); -transport_connection_t *stream_session_lookup_transport_wt6 (ip6_address_t * - lcl, - ip6_address_t * - rmt, - u16 lcl_port, - u16 rmt_port, +stream_session_t *session_lookup4 (u32 fib_index, ip4_address_t * lcl, + ip4_address_t * rmt, u16 lcl_port, + u16 rmt_port, u8 proto); +stream_session_t *session_lookup6 (u32 fib_index, ip6_address_t * lcl, + ip6_address_t * rmt, u16 lcl_port, + u16 rmt_port, u8 proto); +transport_connection_t *session_lookup_connection_wt4 (u32 fib_index, + ip4_address_t * lcl, + ip4_address_t * rmt, + u16 lcl_port, + u16 rmt_port, u8 proto, + u32 thread_index); +transport_connection_t *session_lookup_connection4 (u32 fib_index, + ip4_address_t * lcl, + ip4_address_t * rmt, + u16 lcl_port, + u16 rmt_port, u8 proto); +transport_connection_t *session_lookup_connection_wt6 (u32 fib_index, + ip6_address_t * lcl, + ip6_address_t * rmt, + u16 lcl_port, + u16 rmt_port, u8 proto, + u32 thread_index); +transport_connection_t *session_lookup_connection6 (u32 fib_index, + ip6_address_t * lcl, + ip6_address_t * rmt, + u16 lcl_port, + u16 rmt_port, u8 proto); +stream_session_t *session_lookup_listener4 (u32 fib_index, + ip4_address_t * lcl, u16 lcl_port, + u8 proto); +stream_session_t *session_lookup_listener6 (u32 fib_index, + ip6_address_t * lcl, u16 lcl_port, + u8 proto); +stream_session_t *session_lookup_listener (u32 table_index, + session_endpoint_t * sep); +int session_lookup_add_connection (transport_connection_t * tc, u64 value); +int session_lookup_del_connection (transport_connection_t * tc); +u32 session_lookup_session_endpoint (u32 table_index, + session_endpoint_t * sep); +u32 session_lookup_local_session_endpoint (u32 table_index, + session_endpoint_t * sep); +int session_lookup_add_session_endpoint (u32 table_index, + session_endpoint_t * sep, u64 value); +int session_lookup_del_session_endpoint (u32 table_index, + session_endpoint_t * sep); +int session_lookup_del_session (stream_session_t * s); +int session_lookup_del_half_open (transport_connection_t * tc); +int session_lookup_add_half_open (transport_connection_t * tc, u64 value); +u64 session_lookup_half_open_handle (transport_connection_t * tc); +transport_connection_t *session_lookup_half_open_connection (u64 handle, u8 proto, - u32 - thread_index); -transport_connection_t *stream_session_lookup_transport6 (ip6_address_t * lcl, - ip6_address_t * rmt, - u16 lcl_port, - u16 rmt_port, - u8 proto); + u8 is_ip4); +u32 session_lookup_get_index_for_fib (u32 fib_proto, u32 fib_index); -stream_session_t *stream_session_lookup_listener (ip46_address_t * lcl, - u16 lcl_port, u8 proto); -u64 stream_session_half_open_lookup_handle (ip46_address_t * lcl, - ip46_address_t * rmt, - u16 lcl_port, - u16 rmt_port, u8 proto); -transport_connection_t *stream_session_half_open_lookup (ip46_address_t * lcl, - ip46_address_t * rmt, - u16 lcl_port, - u16 rmt_port, - u8 proto); -void stream_session_table_add_for_tc (transport_connection_t * tc, u64 value); -int stream_session_table_del_for_tc (transport_connection_t * tc); -int stream_session_table_del (stream_session_t * s); -void stream_session_half_open_table_del (transport_connection_t * tc); -void stream_session_half_open_table_add (transport_connection_t * tc, - u64 value); +u64 session_lookup_local_listener_make_handle (session_endpoint_t * sep); +u8 session_lookup_local_is_handle (u64 handle); +int session_lookup_local_listener_parse_handle (u64 handle, + session_endpoint_t * sep); +void session_lookup_show_table_entries (vlib_main_t * vm, + session_table_t * table, u8 type, + u8 is_local); void session_lookup_init (void); #endif /* SRC_VNET_SESSION_SESSION_LOOKUP_H_ */ diff --git a/src/vnet/session/session_node.c b/src/vnet/session/session_node.c index d015584990e..d2291fa38de 100644 --- a/src/vnet/session/session_node.c +++ b/src/vnet/session/session_node.c @@ -434,7 +434,7 @@ dump_thread_0_event_queue (void) break; case FIFO_EVENT_DISCONNECT: - s0 = stream_session_get_from_handle (e->session_handle); + s0 = session_get_from_handle (e->session_handle); fformat (stdout, "[%04d] disconnect session %d\n", i, s0->session_index); break; @@ -477,7 +477,7 @@ session_node_cmp_event (session_fifo_event_t * e, svm_fifo_t * f) case FIFO_EVENT_DISCONNECT: break; case FIFO_EVENT_RPC: - s = stream_session_get_from_handle (e->session_handle); + s = session_get_from_handle (e->session_handle); if (!s) { clib_warning ("session has event but doesn't exist!"); @@ -644,7 +644,7 @@ skip_dequeue: } break; case FIFO_EVENT_DISCONNECT: - s0 = stream_session_get_from_handle (e0->session_handle); + s0 = session_get_from_handle (e0->session_handle); stream_session_disconnect (s0); break; case FIFO_EVENT_BUILTIN_RX: diff --git a/src/vnet/session/session_table.c b/src/vnet/session/session_table.c new file mode 100644 index 00000000000..04c0c816ab0 --- /dev/null +++ b/src/vnet/session/session_table.c @@ -0,0 +1,124 @@ +/* + * 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/session_table.h> +#include <vnet/session/session.h> + +/** + * Pool of session tables + */ +static session_table_t *lookup_tables; + +session_table_t * +session_table_alloc (void) +{ + session_table_t *slt; + pool_get_aligned (lookup_tables, slt, CLIB_CACHE_LINE_BYTES); + memset (slt, 0, sizeof (*slt)); + return slt; +} + +u32 +session_table_index (session_table_t * slt) +{ + return (slt - lookup_tables); +} + +session_table_t * +session_table_get (u32 table_index) +{ + if (vec_len (lookup_tables) <= table_index) + return 0; + return vec_elt_at_index (lookup_tables, table_index); +} + +#define foreach_hash_table_parameter \ + _(v4,session,buckets,20000) \ + _(v4,session,memory,(64<<20)) \ + _(v6,session,buckets,20000) \ + _(v6,session,memory,(64<<20)) \ + _(v4,halfopen,buckets,20000) \ + _(v4,halfopen,memory,(64<<20)) \ + _(v6,halfopen,buckets,20000) \ + _(v6,halfopen,memory,(64<<20)) + +/** + * Initialize session table hash tables + * + * If vpp configured with set of table parameters it uses them, + * otherwise it uses defaults above. + */ +void +session_table_init (session_table_t * slt) +{ +#define _(af,table,parm,value) \ + u32 configured_##af##_##table##_table_##parm = value; + foreach_hash_table_parameter; +#undef _ + +#define _(af,table,parm,value) \ + if (session_manager_main.configured_##af##_##table##_table_##parm) \ + configured_##af##_##table##_table_##parm = \ + session_manager_main.configured_##af##_##table##_table_##parm; + foreach_hash_table_parameter; +#undef _ + + clib_bihash_init_16_8 (&slt->v4_session_hash, "v4 session table", + configured_v4_session_table_buckets, + configured_v4_session_table_memory); + clib_bihash_init_48_8 (&slt->v6_session_hash, "v6 session table", + configured_v6_session_table_buckets, + configured_v6_session_table_memory); + clib_bihash_init_16_8 (&slt->v4_half_open_hash, "v4 half-open table", + configured_v4_halfopen_table_buckets, + configured_v4_halfopen_table_memory); + clib_bihash_init_48_8 (&slt->v6_half_open_hash, "v6 half-open table", + configured_v6_halfopen_table_buckets, + configured_v6_halfopen_table_memory); +} + +typedef struct _ip4_session_table_walk_ctx_t +{ + ip4_session_table_walk_fn_t fn; + void *ctx; +} ip4_session_table_walk_ctx_t; + +void +ip4_session_table_walk_cb (clib_bihash_kv_16_8_t * kvp, void *arg) +{ + ip4_session_table_walk_ctx_t *ctx = arg; + ctx->fn (kvp, ctx->ctx); +} + +void +ip4_session_table_walk (clib_bihash_16_8_t * hash, + ip4_session_table_walk_fn_t fn, void *arg) +{ + ip4_session_table_walk_ctx_t ctx = { + .fn = fn, + .ctx = arg, + }; + clib_bihash_foreach_key_value_pair_16_8 (hash, ip4_session_table_walk_cb, + &ctx); +} + +/* *INDENT-ON* */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/session_table.h b/src/vnet/session/session_table.h new file mode 100644 index 00000000000..ce0b4a2ff25 --- /dev/null +++ b/src/vnet/session/session_table.h @@ -0,0 +1,61 @@ +/* + * 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_TABLE_H_ +#define SRC_VNET_SESSION_SESSION_TABLE_H_ + +#include <vppinfra/bihash_16_8.h> +#include <vppinfra/bihash_48_8.h> + +typedef struct _session_lookup_table +{ + /** + * Lookup tables for established sessions and listeners + */ + clib_bihash_16_8_t v4_session_hash; + clib_bihash_48_8_t v6_session_hash; + + /** + * Lookup tables for half-open sessions + */ + clib_bihash_16_8_t v4_half_open_hash; + clib_bihash_48_8_t v6_half_open_hash; +} session_table_t; + +#define SESSION_TABLE_INVALID_INDEX ((u32)~0) +#define SESSION_LOCAL_TABLE_PREFIX ((u32)~0) +#define SESSION_INVALID_INDEX ((u32)~0) + +typedef int (*ip4_session_table_walk_fn_t) (clib_bihash_kv_16_8_t * kvp, + void *ctx); + +void ip4_session_table_walk_cb (clib_bihash_kv_16_8_t * kvp, void *arg); +void ip4_session_table_walk (clib_bihash_16_8_t * hash, + ip4_session_table_walk_fn_t fn, void *arg); + +session_table_t *session_table_alloc (void); +session_table_t *session_table_get (u32 table_index); +u32 session_table_index (session_table_t * slt); +void session_table_init (session_table_t * slt); + +#endif /* SRC_VNET_SESSION_SESSION_TABLE_H_ */ +/* *INDENT-ON* */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/session_test.c b/src/vnet/session/session_test.c new file mode 100644 index 00000000000..b46b33d1396 --- /dev/null +++ b/src/vnet/session/session_test.c @@ -0,0 +1,473 @@ +/* + * 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/application_namespace.h> +#include <vnet/session/application_interface.h> +#include <vnet/session/application.h> +#include <vnet/session/session.h> + +#define SESSION_TEST_I(_cond, _comment, _args...) \ +({ \ + int _evald = (_cond); \ + if (!(_evald)) { \ + fformat(stderr, "FAIL:%d: " _comment "\n", \ + __LINE__, ##_args); \ + } else { \ + fformat(stderr, "PASS:%d: " _comment "\n", \ + __LINE__, ##_args); \ + } \ + _evald; \ +}) + +#define SESSION_TEST(_cond, _comment, _args...) \ +{ \ + if (!SESSION_TEST_I(_cond, _comment, ##_args)) { \ + return 1; \ + } \ +} + +void +dummy_session_reset_callback (stream_session_t * s) +{ + clib_warning ("called..."); +} + +int +dummy_session_connected_callback (u32 app_index, u32 api_context, + stream_session_t * s, u8 is_fail) +{ + clib_warning ("called..."); + return -1; +} + +int +dummy_add_segment_callback (u32 client_index, const u8 * seg_name, + u32 seg_size) +{ + clib_warning ("called..."); + return -1; +} + +int +dummy_redirect_connect_callback (u32 client_index, void *mp) +{ + return VNET_API_ERROR_SESSION_REDIRECT; +} + +void +dummy_session_disconnect_callback (stream_session_t * s) +{ + clib_warning ("called..."); +} + +int +dummy_session_accept_callback (stream_session_t * s) +{ + clib_warning ("called..."); + return -1; +} + +int +dummy_server_rx_callback (stream_session_t * s) +{ + clib_warning ("called..."); + return -1; +} + +/* *INDENT-OFF* */ +static session_cb_vft_t dummy_session_cbs = { + .session_reset_callback = dummy_session_reset_callback, + .session_connected_callback = dummy_session_connected_callback, + .session_accept_callback = dummy_session_accept_callback, + .session_disconnect_callback = dummy_session_disconnect_callback, + .builtin_server_rx_callback = dummy_server_rx_callback, + .redirect_connect_callback = dummy_redirect_connect_callback, +}; +/* *INDENT-ON* */ + +static int +session_test_namespace (vlib_main_t * vm, unformat_input_t * input) +{ + u64 options[SESSION_OPTIONS_N_OPTIONS], dummy_secret = 1234; + u32 server_index, server_st_index, server_local_st_index; + u32 dummy_port = 1234, local_listener, client_index; + u32 dummy_api_context = 4321, dummy_client_api_index = 1234; + u32 dummy_server_api_index = ~0, sw_if_index = 0; + session_endpoint_t server_sep = SESSION_ENDPOINT_NULL; + session_endpoint_t client_sep = SESSION_ENDPOINT_NULL; + session_endpoint_t intf_sep = SESSION_ENDPOINT_NULL; + clib_error_t *error = 0; + u8 *ns_id = format (0, "appns1"), intf_mac[6]; + app_namespace_t *app_ns; + u8 segment_name[128]; + application_t *server; + stream_session_t *s; + int code; + + server_sep.is_ip4 = 1; + server_sep.port = dummy_port; + client_sep.is_ip4 = 1; + client_sep.port = dummy_port; + memset (options, 0, sizeof (options)); + memset (intf_mac, 0, sizeof (intf_mac)); + + options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_BUILTIN_APP; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_ACCEPT_REDIRECT; + 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, + }; + + vnet_connect_args_t connect_args = { + .sep = client_sep, + .app_index = 0, + .api_context = 0, + }; + + vnet_unbind_args_t unbind_args = { + .handle = bind_args.handle, + .app_index = 0, + }; + + vnet_app_detach_args_t detach_args = { + .app_index = 0, + }; + + ip4_address_t intf_addr = { + .as_u32 = clib_host_to_net_u32 (0x06000105), + }; + + intf_sep.ip.ip4 = intf_addr; + intf_sep.is_ip4 = 1; + intf_sep.port = dummy_port; + + /* + * Insert namespace and lookup + */ + + vnet_app_namespace_add_del_args_t ns_args = { + .ns_id = ns_id, + .secret = dummy_secret, + .sw_if_index = APP_NAMESPACE_INVALID_INDEX, + .is_add = 1 + }; + error = vnet_app_namespace_add_del (&ns_args); + SESSION_TEST ((error == 0), "app ns insertion should succeed: %d", + clib_error_get_code (error)); + + app_ns = app_namespace_get_from_id (ns_id); + SESSION_TEST ((app_ns != 0), "should find ns %v status", ns_id); + SESSION_TEST ((app_ns->ns_secret == dummy_secret), "secret should be %d", + dummy_secret); + SESSION_TEST ((app_ns->sw_if_index == APP_NAMESPACE_INVALID_INDEX), + "sw_if_index should be invalid"); + + /* + * Try application attach with wrong secret + */ + + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret - 1; + attach_args.namespace_id = ns_id; + attach_args.api_client_index = dummy_server_api_index; + + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error != 0), "app attachment should fail"); + code = clib_error_get_code (error); + SESSION_TEST ((code == VNET_API_ERROR_APP_WRONG_NS_SECRET), + "code should be wrong ns secret: %d", code); + + /* + * Attach server with global default scope + */ + options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + options[APP_OPTIONS_NAMESPACE_SECRET] = 0; + 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 attachment should work"); + server_index = attach_args.app_index; + server = application_get (server_index); + SESSION_TEST ((server->ns_index == 0), + "server should be in the default ns"); + + bind_args.app_index = server_index; + error = vnet_bind (&bind_args); + SESSION_TEST ((error == 0), "server bind should work"); + + server_st_index = application_session_table (server, FIB_PROTOCOL_IP4); + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s != 0), "listener should exist in global table"); + SESSION_TEST ((s->app_index == server_index), "app_index should be that of " + "the server"); + server_local_st_index = application_local_session_table (server); + SESSION_TEST ((server_local_st_index == APP_INVALID_INDEX), + "server shouldn't have access to local table"); + + unbind_args.app_index = server_index; + error = vnet_unbind (&unbind_args); + SESSION_TEST ((error == 0), "unbind should work"); + + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s == 0), "listener should not exist in global table"); + + detach_args.app_index = server_index; + vnet_application_detach (&detach_args); + + /* + * Attach server with local and global scope + */ + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret; + attach_args.namespace_id = ns_id; + attach_args.api_client_index = dummy_server_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "server attachment should work"); + server_index = attach_args.app_index; + server = application_get (server_index); + SESSION_TEST ((server->ns_index == app_namespace_index (app_ns)), + "server should be in the right ns"); + + bind_args.app_index = server_index; + error = vnet_bind (&bind_args); + SESSION_TEST ((error == 0), "bind should work"); + server_st_index = application_session_table (server, FIB_PROTOCOL_IP4); + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s != 0), "listener should exist in global table"); + SESSION_TEST ((s->app_index == server_index), "app_index should be that of " + "the server"); + server_local_st_index = application_local_session_table (server); + local_listener = session_lookup_session_endpoint (server_local_st_index, + &server_sep); + SESSION_TEST ((local_listener != SESSION_INVALID_INDEX), + "listener should exist in local table"); + + /* + * Try client connect with 1) local scope 2) global scope + */ + options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + attach_args.api_client_index = dummy_client_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "client attachment should work"); + client_index = attach_args.app_index; + connect_args.api_context = dummy_api_context; + connect_args.app_index = client_index; + error = vnet_connect (&connect_args); + SESSION_TEST ((error != 0), "client connect should return error code"); + code = clib_error_get_code (error); + SESSION_TEST ((code == VNET_API_ERROR_INVALID_VALUE), + "error code should be invalid value (zero ip)"); + connect_args.sep.ip.ip4.as_u8[0] = 127; + error = vnet_connect (&connect_args); + SESSION_TEST ((error != 0), "client connect should return error code"); + code = clib_error_get_code (error); + SESSION_TEST ((code == VNET_API_ERROR_SESSION_REDIRECT), + "error code should be redirect"); + detach_args.app_index = client_index; + vnet_application_detach (&detach_args); + + options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + attach_args.api_client_index = dummy_client_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "client attachment should work"); + error = vnet_connect (&connect_args); + SESSION_TEST ((error != 0), "client connect should return error code"); + code = clib_error_get_code (error); + SESSION_TEST ((code == VNET_API_ERROR_SESSION_CONNECT), + "error code should be connect (nothing in local scope)"); + detach_args.app_index = client_index; + vnet_application_detach (&detach_args); + + /* + * Unbind and detach server and then re-attach with local scope only + */ + unbind_args.handle = bind_args.handle; + unbind_args.app_index = server_index; + error = vnet_unbind (&unbind_args); + SESSION_TEST ((error == 0), "unbind should work"); + + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s == 0), "listener should not exist in global table"); + local_listener = session_lookup_session_endpoint (server_local_st_index, + &server_sep); + SESSION_TEST ((s == 0), "listener should not exist in local table"); + + detach_args.app_index = server_index; + vnet_application_detach (&detach_args); + + options[APP_OPTIONS_FLAGS] &= ~APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + attach_args.api_client_index = dummy_server_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "app attachment should work"); + server_index = attach_args.app_index; + server = application_get (server_index); + SESSION_TEST ((server->ns_index == app_namespace_index (app_ns)), + "app should be in the right ns"); + + bind_args.app_index = server_index; + error = vnet_bind (&bind_args); + SESSION_TEST ((error == 0), "bind should work"); + + server_st_index = application_session_table (server, FIB_PROTOCOL_IP4); + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s == 0), "listener should not exist in global table"); + server_local_st_index = application_local_session_table (server); + local_listener = session_lookup_session_endpoint (server_local_st_index, + &server_sep); + SESSION_TEST ((local_listener != SESSION_INVALID_INDEX), + "listener should exist in local table"); + + unbind_args.handle = bind_args.handle; + error = vnet_unbind (&unbind_args); + SESSION_TEST ((error == 0), "unbind should work"); + + local_listener = session_lookup_session_endpoint (server_local_st_index, + &server_sep); + SESSION_TEST ((local_listener == SESSION_INVALID_INDEX), + "listener should not exist in local table"); + + /* + * Client attach + connect in default ns with local scope + */ + 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_client_api_index; + vnet_application_attach (&attach_args); + error = vnet_connect (&connect_args); + SESSION_TEST ((error != 0), "client connect should return error code"); + code = clib_error_get_code (error); + SESSION_TEST ((code == VNET_API_ERROR_SESSION_CONNECT), + "error code should be connect (not in same ns)"); + detach_args.app_index = client_index; + vnet_application_detach (&detach_args); + + /* + * Detach server + */ + detach_args.app_index = server_index; + vnet_application_detach (&detach_args); + + /* + * Create loopback interface + */ + if (vnet_create_loopback_interface (&sw_if_index, intf_mac, 0, 0)) + { + clib_warning ("couldn't create loopback. stopping the test!"); + return 0; + } + vnet_sw_interface_set_flags (vnet_get_main (), sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + ip4_add_del_interface_address (vlib_get_main (), sw_if_index, &intf_addr, + 24, 0); + + /* + * Update namespace + */ + ns_args.sw_if_index = sw_if_index; + error = vnet_app_namespace_add_del (&ns_args); + SESSION_TEST ((error == 0), "app ns insertion should succeed: %d", + clib_error_get_code (error)); + + /* + * Attach server with local and global scope + */ + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE; + options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_LOCAL_SCOPE; + options[APP_OPTIONS_NAMESPACE_SECRET] = dummy_secret; + attach_args.namespace_id = ns_id; + attach_args.api_client_index = dummy_server_api_index; + error = vnet_application_attach (&attach_args); + SESSION_TEST ((error == 0), "server attachment should work"); + server_index = attach_args.app_index; + + bind_args.app_index = server_index; + error = vnet_bind (&bind_args); + server_st_index = application_session_table (server, FIB_PROTOCOL_IP4); + s = session_lookup_listener (server_st_index, &server_sep); + SESSION_TEST ((s == 0), "zero listener should not exist in global table"); + + s = session_lookup_listener (server_st_index, &intf_sep); + SESSION_TEST ((s != 0), "intf listener should exist in global table"); + SESSION_TEST ((s->app_index == server_index), "app_index should be that of " + "the server"); + server_local_st_index = application_local_session_table (server); + local_listener = session_lookup_session_endpoint (server_local_st_index, + &server_sep); + SESSION_TEST ((local_listener != SESSION_INVALID_INDEX), + "zero listener should exist in local table"); + detach_args.app_index = server_index; + vnet_application_detach (&detach_args); + + /* + * Cleanup + */ + vec_free (ns_id); + vnet_delete_loopback_interface (sw_if_index); + return 0; +} + +static clib_error_t * +session_test (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd_arg) +{ + int res = 0; + + vnet_session_enable_disable (vm, 1); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "namespace")) + { + res = session_test_namespace (vm, input); + } + else + break; + } + + if (res) + return clib_error_return (0, "Session unit test failed"); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (tcp_test_command, static) = +{ + .path = "test session", + .short_help = "internal session unit tests", + .function = session_test, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/session/stream_session.h b/src/vnet/session/stream_session.h index 275052d3ee5..1ed6e0b9eec 100644 --- a/src/vnet/session/stream_session.h +++ b/src/vnet/session/stream_session.h @@ -18,6 +18,7 @@ #include <vnet/vnet.h> #include <svm/svm_fifo.h> +#include <vnet/session/transport.h> #define foreach_session_type \ _(IP4_TCP, ip4_tcp) \ @@ -81,6 +82,44 @@ typedef struct _stream_session_t CLIB_CACHE_LINE_ALIGN_MARK (pad); } stream_session_t; +typedef struct _session_endpoint +{ + /* + * Network specific + */ +#define _(type, name) type name; + foreach_transport_connection_fields +#undef _ + /* + * Session specific + */ + u8 transport_proto; /**< transport protocol for session */ +} session_endpoint_t; + +#define SESSION_IP46_ZERO \ +{ \ + .ip6 = { \ + { 0, 0, }, \ + }, \ +} +#define SESSION_ENDPOINT_NULL \ +{ \ + .sw_if_index = ENDPOINT_INVALID_INDEX, \ + .ip = SESSION_IP46_ZERO, \ + .fib_index = ENDPOINT_INVALID_INDEX, \ + .is_ip4 = 0, \ + .port = 0, \ + .transport_proto = 0, \ +} + +#define session_endpoint_to_transport(_sep) ((transport_endpoint_t *)_sep) + +always_inline u8 +session_endpoint_fib_proto (session_endpoint_t * sep) +{ + return sep->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; +} + #endif /* SRC_VNET_SESSION_STREAM_SESSION_H_ */ /* diff --git a/src/vnet/session/transport.h b/src/vnet/session/transport.h index e2c479494ca..8c299c46490 100644 --- a/src/vnet/session/transport.h +++ b/src/vnet/session/transport.h @@ -18,8 +18,6 @@ #include <vnet/vnet.h> #include <vnet/ip/ip.h> -#include <vppinfra/bihash_16_8.h> -#include <vppinfra/bihash_48_8.h> #include <vnet/tcp/tcp_debug.h> /* @@ -33,7 +31,7 @@ typedef struct _transport_connection u16 rmt_port; /**< Remote port */ u8 transport_proto; /**< Protocol id */ u8 is_ip4; /**< Flag if IP4 connection */ - u32 vrf; /**< FIB table id */ + u32 fib_index; /**< Network namespace */ u32 s_index; /**< Parent session index */ u32 c_index; /**< Connection index in transport pool */ @@ -57,8 +55,7 @@ typedef struct _transport_connection #define c_lcl_port connection.lcl_port #define c_rmt_port connection.rmt_port #define c_transport_proto connection.transport_proto -#define c_vrf connection.vrf -#define c_state connection.state +#define c_fib_index connection.fib_index #define c_s_index connection.s_index #define c_c_index connection.c_index #define c_is_ip4 connection.is_ip4 @@ -75,14 +72,28 @@ typedef enum _transport_proto TRANSPORT_PROTO_UDP } transport_proto_t; +#define foreach_transport_connection_fields \ + _(u32, sw_if_index) /**< interface endpoint is associated with */ \ + _(ip46_address_t, ip) /**< ip address */ \ + _(u32, fib_index) /**< fib table endpoint is associated with */ \ + _(u8, is_ip4) /**< 1 if ip4 */ \ + _(u16, port) /**< port in net order */ \ + typedef struct _transport_endpoint { - ip46_address_t ip; /** ip address */ - u16 port; /** port in net order */ - u8 is_ip4; /** 1 if ip4 */ - u32 vrf; /** fib table the endpoint is associated with */ +#define _(type, name) type name; + foreach_transport_connection_fields +#undef _ } transport_endpoint_t; +#define ENDPOINT_INVALID_INDEX ((u32)~0) + +always_inline u8 +transport_connection_fib_proto (transport_connection_t * tc) +{ + return tc->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; +} + #endif /* VNET_VNET_URI_TRANSPORT_H_ */ /* |