diff options
author | Florin Coras <fcoras@cisco.com> | 2017-10-02 00:18:51 -0700 |
---|---|---|
committer | Dave Barach <openvpp@barachs.net> | 2017-10-10 20:42:50 +0000 |
commit | cea194d8f973a2f2b5ef72d212533057174cc70a (patch) | |
tree | 6fdd2e8a929c62625d1ad35bfbec342129989aef /src/vnet/session/application_interface.c | |
parent | 1f36a93d3d68f5ba6dcda08809394ce757cefd72 (diff) |
session: add support for application namespacing
Applications are now provided the option to select the namespace they
are to be attached to and the scope of their attachement. Application
namespaces are meant to:
1) constrain the scope of communication through the network by
association with source interfaces and/or fib tables that provide the
source ips to be used and limit the scope of routing
2) provide a namespace local scope to session layer communication, as
opposed to the global scope provided by 1). That is, sessions can be
established without assistance from transport and network layers.
Albeit, zero/local-host ip addresses must still be provided in session
establishment messages due to existing application idiosyncrasies. This
mode of communication uses shared-memory fifos (cut-through sessions)
exclusively.
If applications request no namespace, they are assigned to the default
one, which at its turn uses the default fib. Applications can request
access to both local and global scopes for a namespace. If no scope is
specified, session layer defaults to the global one.
When a sw_if_index is provided for a namespace, zero-ip (INADDR_ANY)
binds are converted to binds to the requested interface.
Change-Id: Ia0f660bbf7eec7f89673f75b4821fc7c3d58e3d1
Signed-off-by: Florin Coras <fcoras@cisco.com>
Diffstat (limited to 'src/vnet/session/application_interface.c')
-rw-r--r-- | src/vnet/session/application_interface.c | 380 |
1 files changed, 256 insertions, 124 deletions
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; } /* |