Add fixed-size, preallocated pool support
[vpp.git] / src / vnet / session / application_interface.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/session/application_interface.h>
16
17 #include <vnet/session/session.h>
18 #include <vlibmemory/api.h>
19 #include <vnet/dpo/load_balance.h>
20 #include <vnet/fib/ip4_fib.h>
21
22 /** @file
23     VPP's application/session API bind/unbind/connect/disconnect calls
24 */
25
26 static u8
27 ip_is_zero (ip46_address_t * ip46_address, u8 is_ip4)
28 {
29   if (is_ip4)
30     return (ip46_address->ip4.as_u32 == 0);
31   else
32     return (ip46_address->as_u64[0] == 0 && ip46_address->as_u64[1] == 0);
33 }
34
35 static u8
36 ip_is_local (ip46_address_t * ip46_address, u8 is_ip4)
37 {
38   fib_node_index_t fei;
39   fib_entry_flag_t flags;
40   fib_prefix_t prefix;
41
42   /* Check if requester is local */
43   if (is_ip4)
44     {
45       prefix.fp_len = 32;
46       prefix.fp_proto = FIB_PROTOCOL_IP4;
47     }
48   else
49     {
50       prefix.fp_len = 128;
51       prefix.fp_proto = FIB_PROTOCOL_IP6;
52     }
53
54   clib_memcpy (&prefix.fp_addr, ip46_address, sizeof (ip46_address_t));
55   fei = fib_table_lookup (0, &prefix);
56   flags = fib_entry_get_flags (fei);
57
58   return (flags & FIB_ENTRY_FLAG_LOCAL);
59 }
60
61 int
62 api_parse_session_handle (u64 handle, u32 * session_index, u32 * thread_index)
63 {
64   session_manager_main_t *smm = vnet_get_session_manager_main ();
65   stream_session_t *pool;
66
67   *thread_index = handle & 0xFFFFFFFF;
68   *session_index = handle >> 32;
69
70   if (*thread_index >= vec_len (smm->sessions))
71     return VNET_API_ERROR_INVALID_VALUE;
72
73   pool = smm->sessions[*thread_index];
74
75   if (pool_is_free_index (pool, *session_index))
76     return VNET_API_ERROR_INVALID_VALUE_2;
77
78   return 0;
79 }
80
81 int
82 vnet_bind_i (u32 app_index, session_type_t sst,
83              transport_endpoint_t * tep, u64 * handle)
84 {
85   application_t *app;
86   stream_session_t *listener;
87
88   app = application_get_if_valid (app_index);
89   if (!app)
90     {
91       clib_warning ("app not attached");
92       return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
93     }
94
95   listener = stream_session_lookup_listener (&tep->ip,
96                                              clib_host_to_net_u16 (tep->port),
97                                              sst);
98   if (listener)
99     return VNET_API_ERROR_ADDRESS_IN_USE;
100
101   if (!ip_is_zero (&tep->ip, tep->is_ip4)
102       && !ip_is_local (&tep->ip, tep->is_ip4))
103     return VNET_API_ERROR_INVALID_VALUE_2;
104
105   /* Setup listen path down to transport */
106   return application_start_listen (app, sst, tep, handle);
107 }
108
109 int
110 vnet_unbind_i (u32 app_index, u64 handle)
111 {
112   application_t *app = application_get_if_valid (app_index);
113
114   if (!app)
115     {
116       clib_warning ("app (%d) not attached", app_index);
117       return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
118     }
119
120   /* Clear the listener */
121   return application_stop_listen (app, handle);
122 }
123
124 int
125 vnet_connect_i (u32 app_index, u32 api_context, session_type_t sst,
126                 transport_endpoint_t * tep, void *mp)
127 {
128   stream_session_t *listener;
129   application_t *server, *app;
130
131   /*
132    * Figure out if connecting to a local server
133    */
134   listener = stream_session_lookup_listener (&tep->ip,
135                                              clib_host_to_net_u16 (tep->port),
136                                              sst);
137   if (listener)
138     {
139       server = application_get (listener->app_index);
140
141       /*
142        * Server is willing to have a direct fifo connection created
143        * instead of going through the state machine, etc.
144        */
145       if (server->flags & APP_OPTIONS_FLAGS_USE_FIFO)
146         return server->cb_fns.
147           redirect_connect_callback (server->api_client_index, mp);
148     }
149
150   /*
151    * Not connecting to a local server. Create regular session
152    */
153   app = application_get (app_index);
154   return application_open_session (app, sst, tep, api_context);
155 }
156
157 /**
158  * unformat a vnet URI
159  *
160  * fifo://name
161  * tcp://ip46-addr:port
162  * udp://ip46-addr:port
163  *
164  * u8 ip46_address[16];
165  * u16  port_in_host_byte_order;
166  * stream_session_type_t sst;
167  * u8 *fifo_name;
168  *
169  * if (unformat (input, "%U", unformat_vnet_uri, &ip46_address,
170  *              &sst, &port, &fifo_name))
171  *  etc...
172  *
173  */
174 uword
175 unformat_vnet_uri (unformat_input_t * input, va_list * args)
176 {
177   session_type_t *sst = va_arg (*args, session_type_t *);
178   transport_endpoint_t *tep = va_arg (*args, transport_endpoint_t *);
179
180   if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &tep->ip.ip4,
181                 &tep->port))
182     {
183       *sst = SESSION_TYPE_IP4_TCP;
184       tep->is_ip4 = 1;
185       return 1;
186     }
187   if (unformat (input, "udp://%U/%d", unformat_ip4_address, &tep->ip.ip4,
188                 &tep->port))
189     {
190       *sst = SESSION_TYPE_IP4_UDP;
191       tep->is_ip4 = 1;
192       return 1;
193     }
194   if (unformat (input, "udp://%U/%d", unformat_ip6_address, &tep->ip.ip6,
195                 &tep->port))
196     {
197       *sst = SESSION_TYPE_IP6_UDP;
198       return 1;
199     }
200   if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &tep->ip.ip6,
201                 &tep->port))
202     {
203       *sst = SESSION_TYPE_IP6_TCP;
204       return 1;
205     }
206
207   return 0;
208 }
209
210 static u8 *cache_uri;
211 static session_type_t cache_sst;
212 static transport_endpoint_t *cache_tep;
213
214 int
215 parse_uri (char *uri, session_type_t * sst, transport_endpoint_t * tep)
216 {
217   unformat_input_t _input, *input = &_input;
218
219   if (cache_uri && !strncmp (uri, (char *) cache_uri, vec_len (cache_uri)))
220     {
221       *sst = cache_sst;
222       *tep = *cache_tep;
223       return 0;
224     }
225
226   /* Make sure */
227   uri = (char *) format (0, "%s%c", uri, 0);
228
229   /* Parse uri */
230   unformat_init_string (input, uri, strlen (uri));
231   if (!unformat (input, "%U", unformat_vnet_uri, sst, tep))
232     {
233       unformat_free (input);
234       return VNET_API_ERROR_INVALID_VALUE;
235     }
236   unformat_free (input);
237
238   vec_free (cache_uri);
239   cache_uri = (u8 *) uri;
240   cache_sst = *sst;
241   if (cache_tep)
242     clib_mem_free (cache_tep);
243   cache_tep = clib_mem_alloc (sizeof (*tep));
244   *cache_tep = *tep;
245
246   return 0;
247 }
248
249 /**
250  * Attaches application.
251  *
252  * Allocates a vpp app, i.e., a structure that keeps back pointers
253  * to external app and a segment manager for shared memory fifo based
254  * communication with the external app.
255  */
256 int
257 vnet_application_attach (vnet_app_attach_args_t * a)
258 {
259   application_t *app = 0;
260   segment_manager_t *sm;
261   u8 *seg_name;
262   int rv;
263
264   app = application_new ();
265   if ((rv = application_init (app, a->api_client_index, a->options,
266                               a->session_cb_vft)))
267     return rv;
268
269   a->app_event_queue_address = pointer_to_uword (app->event_queue);
270   sm = segment_manager_get (app->first_segment_manager);
271   segment_manager_get_segment_info (sm->segment_indices[0],
272                                     &seg_name, &a->segment_size);
273
274   a->segment_name_length = vec_len (seg_name);
275   a->segment_name = seg_name;
276   ASSERT (vec_len (a->segment_name) <= 128);
277   a->app_index = app->index;
278   return 0;
279 }
280
281 int
282 vnet_application_detach (vnet_app_detach_args_t * a)
283 {
284   application_t *app;
285   app = application_get_if_valid (a->app_index);
286
287   if (!app)
288     {
289       clib_warning ("app not attached");
290       return VNET_API_ERROR_APPLICATION_NOT_ATTACHED;
291     }
292
293   application_del (app);
294   return 0;
295 }
296
297 int
298 vnet_bind_uri (vnet_bind_args_t * a)
299 {
300   session_type_t sst = SESSION_N_TYPES;
301   transport_endpoint_t tep;
302   int rv;
303
304   memset (&tep, 0, sizeof (tep));
305   rv = parse_uri (a->uri, &sst, &tep);
306   if (rv)
307     return rv;
308
309   if ((rv = vnet_bind_i (a->app_index, sst, &tep, &a->handle)))
310     return rv;
311
312   return 0;
313 }
314
315 int
316 vnet_unbind_uri (vnet_unbind_args_t * a)
317 {
318   session_type_t sst = SESSION_N_TYPES;
319   stream_session_t *listener;
320   transport_endpoint_t tep;
321   int rv;
322
323   rv = parse_uri (a->uri, &sst, &tep);
324   if (rv)
325     return rv;
326
327   listener = stream_session_lookup_listener (&tep.ip,
328                                              clib_host_to_net_u16 (tep.port),
329                                              sst);
330   if (!listener)
331     return VNET_API_ERROR_ADDRESS_NOT_IN_USE;
332
333   return vnet_unbind_i (a->app_index, listen_session_get_handle (listener));
334 }
335
336 int
337 vnet_connect_uri (vnet_connect_args_t * a)
338 {
339   transport_endpoint_t tep;
340   session_type_t sst;
341   int rv;
342
343   /* Parse uri */
344   memset (&tep, 0, sizeof (tep));
345   rv = parse_uri (a->uri, &sst, &tep);
346   if (rv)
347     return rv;
348
349   return vnet_connect_i (a->app_index, a->api_context, sst, &tep, a->mp);
350 }
351
352 int
353 vnet_disconnect_session (vnet_disconnect_args_t * a)
354 {
355   u32 index, thread_index;
356   stream_session_t *s;
357
358   stream_session_parse_handle (a->handle, &index, &thread_index);
359   s = stream_session_get_if_valid (index, thread_index);
360
361   if (!s || s->app_index != a->app_index)
362     return VNET_API_ERROR_INVALID_VALUE;
363
364   /* We're peeking into another's thread pool. Make sure */
365   ASSERT (s->session_index == index);
366
367   session_send_session_evt_to_thread (a->handle, FIFO_EVENT_DISCONNECT,
368                                       thread_index);
369   return 0;
370 }
371
372 int
373 vnet_bind (vnet_bind_args_t * a)
374 {
375   session_type_t sst = SESSION_N_TYPES;
376   int rv;
377
378   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
379   if ((rv = vnet_bind_i (a->app_index, sst, &a->tep, &a->handle)))
380     return rv;
381
382   return 0;
383 }
384
385 int
386 vnet_unbind (vnet_unbind_args_t * a)
387 {
388   return vnet_unbind_i (a->app_index, a->handle);
389 }
390
391 int
392 vnet_connect (vnet_connect_args_t * a)
393 {
394   session_type_t sst;
395
396   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
397   return vnet_connect_i (a->app_index, a->api_context, sst, &a->tep, a->mp);
398 }
399
400 /*
401  * fd.io coding-style-patch-verification: ON
402  *
403  * Local Variables:
404  * eval: (c-set-style "gnu")
405  * End:
406  */