/* * Copyright (c) 2016 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_interface.h> #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) { if (is_ip4) return (ip46_address->ip4.as_u32 == 0); else return (ip46_address->as_u64[0] == 0 && ip46_address->as_u64[1] == 0); } static u8 ip_is_local (ip46_address_t * ip46_address, u8 is_ip4) { fib_node_index_t fei; fib_entry_flag_t flags; fib_prefix_t prefix; /* Check if requester is local */ if (is_ip4) { prefix.fp_len = 32; prefix.fp_proto = FIB_PROTOCOL_IP4; } else { prefix.fp_len = 128; prefix.fp_proto = FIB_PROTOCOL_IP6; } 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); } int api_parse_session_handle (u64 handle, u32 * session_index, u32 * thread_index) { session_manager_main_t *smm = vnet_get_session_manager_main (); stream_session_t *pool; *thread_index = handle & 0xFFFFFFFF; *session_index = handle >> 32; if (*thread_index >= vec_len (smm->sessions)) return VNET_API_ERROR_INVALID_VALUE; pool = smm->sessions[*thread_index]; if (pool_is_free_index (pool, *session_index)) return VNET_API_ERROR_INVALID_VALUE_2; return 0; } int vnet_bind_i (u32 app_index, session_type_t sst, transport_endpoint_t * tep, u64 * handle) { application_t *app; stream_session_t *listener; app = application_get_if_valid (app_index); if (!app) { clib_warning ("app not attached"); return VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } listener = stream_session_lookup_listener (&tep->ip, clib_host_to_net_u16 (tep->port), sst); if (listener) 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; /* Setup listen path down to transport */ return application_start_listen (app, sst, tep, handle); } int vnet_unbind_i (u32 app_index, u64 handle) { application_t *app = application_get_if_valid (app_index); if (!app) { clib_warning ("app not attached"); return VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } /* Clear the listener */ return application_stop_listen (app, handle); } int vnet_connect_i (u32 app_index, u32 api_context, session_type_t sst, transport_endpoint_t * tep, void *mp) { stream_session_t *listener; application_t *server, *app; /* * Figure out if connecting to a local server */ listener = stream_session_lookup_listener (&tep->ip, clib_host_to_net_u16 (tep->port), sst); if (listener) { server = application_get (listener->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); } /* * Not connecting to a local server. Create regular session */ app = application_get (app_index); return application_open_session (app, sst, tep, api_context); } /** * unformat a vnet URI * * fifo://name * tcp://ip46-addr:port * udp://ip46-addr:port * * u8 ip46_address[16]; * u16 port_in_host_byte_order; * stream_session_type_t sst; * u8 *fifo_name; * * if (unformat (input, "%U", unformat_vnet_uri, &ip46_address, * &sst, &port, &fifo_name)) * etc... * */ 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 *); if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &tep->ip.ip4, &tep->port)) { *sst = SESSION_TYPE_IP4_TCP; tep->is_ip4 = 1; return 1; } if (unformat (input, "udp://%U/%d", unformat_ip4_address, &tep->ip.ip4, &tep->port)) { *sst = SESSION_TYPE_IP4_UDP; tep->is_ip4 = 1; return 1; } if (unformat (input, "udp://%U/%d", unformat_ip6_address, &tep->ip.ip6, &tep->port)) { *sst = SESSION_TYPE_IP6_UDP; return 1; } if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &tep->ip.ip6, &tep->port)) { *sst = SESSION_TYPE_IP6_TCP; return 1; } return 0; } int parse_uri (char *uri, session_type_t * sst, transport_endpoint_t * tep) { unformat_input_t _input, *input = &_input; /* Make sure */ uri = (char *) format (0, "%s%c", uri, 0); /* Parse uri */ unformat_init_string (input, uri, strlen (uri)); if (!unformat (input, "%U", unformat_vnet_uri, sst, tep)) { unformat_free (input); return VNET_API_ERROR_INVALID_VALUE; } unformat_free (input); return 0; } /** * Attaches application. * * 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 vnet_application_attach (vnet_app_attach_args_t * a) { application_t *app = 0; segment_manager_t *sm; u8 *seg_name; int rv; app = application_new (); if ((rv = application_init (app, a->api_client_index, a->options, a->session_cb_vft))) return rv; a->app_event_queue_address = pointer_to_uword (app->event_queue); sm = segment_manager_get (app->first_segment_manager); segment_manager_get_segment_info (sm->segment_indices[0], &seg_name, &a->segment_size); a->segment_name_length = vec_len (seg_name); a->segment_name = seg_name; ASSERT (vec_len (a->segment_name) <= 128); a->app_index = app->index; return 0; } int vnet_application_detach (vnet_app_detach_args_t * a) { application_t *app; app = application_get_if_valid (a->app_index); if (!app) { clib_warning ("app not attached"); return VNET_API_ERROR_APPLICATION_NOT_ATTACHED; } application_del (app); return 0; } session_type_t session_type_from_proto_and_ip (session_api_proto_t proto, u8 is_ip4) { if (proto == SESSION_PROTO_TCP) { if (is_ip4) return SESSION_TYPE_IP4_TCP; else return SESSION_TYPE_IP6_TCP; } else { if (is_ip4) return SESSION_TYPE_IP4_UDP; else return SESSION_TYPE_IP6_UDP; } return SESSION_N_TYPES; } int vnet_bind_uri (vnet_bind_args_t * a) { session_type_t sst = SESSION_N_TYPES; transport_endpoint_t tep; int rv; memset (&tep, 0, sizeof (tep)); rv = parse_uri (a->uri, &sst, &tep); if (rv) return rv; if ((rv = vnet_bind_i (a->app_index, sst, &tep, &a->handle))) return rv; return 0; } int vnet_unbind_uri (vnet_unbind_args_t * a) { session_type_t sst = SESSION_N_TYPES; stream_session_t *listener; transport_endpoint_t tep; int rv; rv = parse_uri (a->uri, &sst, &tep); if (rv) return rv; listener = stream_session_lookup_listener (&tep.ip, clib_host_to_net_u16 (tep.port), sst); if (!listener) return VNET_API_ERROR_ADDRESS_NOT_IN_USE; return vnet_unbind_i (a->app_index, listen_session_get_handle (listener)); } int vnet_connect_uri (vnet_connect_args_t * a) { transport_endpoint_t tep; session_type_t sst; int rv; /* Parse uri */ memset (&tep, 0, sizeof (tep)); rv = parse_uri (a->uri, &sst, &tep); if (rv) return rv; return vnet_connect_i (a->app_index, a->api_context, sst, &tep, a->mp); } int 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); s = stream_session_get_if_valid (index, thread_index); if (!s || s->app_index != a->app_index) return VNET_API_ERROR_INVALID_VALUE; /* We're peeking into another's thread pool. Make sure */ ASSERT (s->session_index == index); session_send_session_evt_to_thread (a->handle, FIFO_EVENT_DISCONNECT, thread_index); return 0; } int 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; return 0; } int vnet_unbind (vnet_unbind_args_t * a) { return vnet_unbind_i (a->app_index, a->handle); } int 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); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */