VPP-659 TCP improvements
[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 api_client_index, ip46_address_t * ip46, u16 port_host_order,
83              session_type_t sst, u64 * options, session_cb_vft_t * cb_fns,
84              application_t ** app, u32 * len_seg_name, char *seg_name)
85 {
86   u8 *segment_name = 0;
87   application_t *server = 0;
88   stream_session_t *listener;
89   u8 is_ip4;
90
91   listener =
92     stream_session_lookup_listener (ip46,
93                                     clib_host_to_net_u16 (port_host_order),
94                                     sst);
95
96   if (listener)
97     return VNET_API_ERROR_ADDRESS_IN_USE;
98
99   if (application_lookup (api_client_index))
100     {
101       clib_warning ("Only one connection supported for now");
102       return VNET_API_ERROR_ADDRESS_IN_USE;
103     }
104
105   is_ip4 = SESSION_TYPE_IP4_UDP == sst || SESSION_TYPE_IP4_TCP == sst;
106   if (!ip_is_zero (ip46, is_ip4) && !ip_is_local (ip46, is_ip4))
107     return VNET_API_ERROR_INVALID_VALUE;
108
109   /* Allocate and initialize stream server */
110   server = application_new (APP_SERVER, sst, api_client_index,
111                             options[SESSION_OPTIONS_FLAGS], cb_fns);
112
113   application_server_init (server, options[SESSION_OPTIONS_SEGMENT_SIZE],
114                            options[SESSION_OPTIONS_ADD_SEGMENT_SIZE],
115                            options[SESSION_OPTIONS_RX_FIFO_SIZE],
116                            options[SESSION_OPTIONS_TX_FIFO_SIZE],
117                            &segment_name);
118
119   /* Setup listen path down to transport */
120   stream_session_start_listen (server->index, ip46, port_host_order);
121
122   /*
123    * Return values
124    */
125
126   ASSERT (vec_len (segment_name) <= 128);
127   *len_seg_name = vec_len (segment_name);
128   memcpy (seg_name, segment_name, *len_seg_name);
129   *app = server;
130
131   return 0;
132 }
133
134 int
135 vnet_unbind_i (u32 api_client_index)
136 {
137   application_t *server;
138
139   /*
140    * Find the stream_server_t corresponding to the api client
141    */
142   server = application_lookup (api_client_index);
143   if (!server)
144     return VNET_API_ERROR_INVALID_VALUE_2;
145
146   /* Clear the listener */
147   stream_session_stop_listen (server->index);
148   application_del (server);
149
150   return 0;
151 }
152
153 int
154 vnet_connect_i (u32 api_client_index, u32 api_context, session_type_t sst,
155                 ip46_address_t * ip46, u16 port, u64 * options, void *mp,
156                 session_cb_vft_t * cb_fns)
157 {
158   stream_session_t *listener;
159   application_t *server, *app;
160
161   /*
162    * Figure out if connecting to a local server
163    */
164   listener = stream_session_lookup_listener (ip46,
165                                              clib_host_to_net_u16 (port),
166                                              sst);
167   if (listener)
168     {
169       server = application_get (listener->app_index);
170
171       /*
172        * Server is willing to have a direct fifo connection created
173        * instead of going through the state machine, etc.
174        */
175       if (server->flags & SESSION_OPTIONS_FLAGS_USE_FIFO)
176         return server->cb_fns.
177           redirect_connect_callback (server->api_client_index, mp);
178     }
179
180   /* Create client app */
181   app = application_new (APP_CLIENT, sst, api_client_index,
182                          options[SESSION_OPTIONS_FLAGS], cb_fns);
183
184   app->api_context = api_context;
185
186   /*
187    * Not connecting to a local server. Create regular session
188    */
189   return stream_session_open (sst, ip46, port, app->index);
190 }
191
192 /**
193  * unformat a vnet URI
194  *
195  * fifo://name
196  * tcp://ip46-addr:port
197  * udp://ip46-addr:port
198  *
199  * u8 ip46_address[16];
200  * u16  port_in_host_byte_order;
201  * stream_session_type_t sst;
202  * u8 *fifo_name;
203  *
204  * if (unformat (input, "%U", unformat_vnet_uri, &ip46_address,
205  *              &sst, &port, &fifo_name))
206  *  etc...
207  *
208  */
209 uword
210 unformat_vnet_uri (unformat_input_t * input, va_list * args)
211 {
212   ip46_address_t *address = va_arg (*args, ip46_address_t *);
213   session_type_t *sst = va_arg (*args, session_type_t *);
214   u16 *port = va_arg (*args, u16 *);
215
216   if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &address->ip4,
217                 port))
218     {
219       *sst = SESSION_TYPE_IP4_TCP;
220       return 1;
221     }
222   if (unformat (input, "udp://%U/%d", unformat_ip4_address, &address->ip4,
223                 port))
224     {
225       *sst = SESSION_TYPE_IP4_UDP;
226       return 1;
227     }
228   if (unformat (input, "udp://%U/%d", unformat_ip6_address, &address->ip6,
229                 port))
230     {
231       *sst = SESSION_TYPE_IP6_UDP;
232       return 1;
233     }
234   if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &address->ip6,
235                 port))
236     {
237       *sst = SESSION_TYPE_IP6_TCP;
238       return 1;
239     }
240
241   return 0;
242 }
243
244 int
245 parse_uri (char *uri, session_type_t * sst, ip46_address_t * addr,
246            u16 * port_number_host_byte_order)
247 {
248   unformat_input_t _input, *input = &_input;
249
250   /* Make sure */
251   uri = (char *) format (0, "%s%c", uri, 0);
252
253   /* Parse uri */
254   unformat_init_string (input, uri, strlen (uri));
255   if (!unformat (input, "%U", unformat_vnet_uri, addr, sst,
256                  port_number_host_byte_order))
257     {
258       unformat_free (input);
259       return VNET_API_ERROR_INVALID_VALUE;
260     }
261   unformat_free (input);
262
263   return 0;
264 }
265
266 int
267 vnet_bind_uri (vnet_bind_args_t * a)
268 {
269   application_t *server = 0;
270   u16 port_host_order;
271   session_type_t sst = SESSION_N_TYPES;
272   ip46_address_t ip46;
273   int rv;
274
275   memset (&ip46, 0, sizeof (ip46));
276   rv = parse_uri (a->uri, &sst, &ip46, &port_host_order);
277   if (rv)
278     return rv;
279
280   if ((rv = vnet_bind_i (a->api_client_index, &ip46, port_host_order, sst,
281                          a->options, a->session_cb_vft, &server,
282                          &a->segment_name_length, a->segment_name)))
283     return rv;
284
285   a->server_event_queue_address = (u64) server->event_queue;
286   return 0;
287 }
288
289 session_type_t
290 session_type_from_proto_and_ip (session_api_proto_t proto, u8 is_ip4)
291 {
292   if (proto == SESSION_PROTO_TCP)
293     {
294       if (is_ip4)
295         return SESSION_TYPE_IP4_TCP;
296       else
297         return SESSION_TYPE_IP6_TCP;
298     }
299   else
300     {
301       if (is_ip4)
302         return SESSION_TYPE_IP4_UDP;
303       else
304         return SESSION_TYPE_IP6_UDP;
305     }
306
307   return SESSION_N_TYPES;
308 }
309
310 int
311 vnet_unbind_uri (char *uri, u32 api_client_index)
312 {
313   u16 port_number_host_byte_order;
314   session_type_t sst = SESSION_N_TYPES;
315   ip46_address_t ip46_address;
316   stream_session_t *listener;
317   int rv;
318
319   rv = parse_uri (uri, &sst, &ip46_address, &port_number_host_byte_order);
320   if (rv)
321     return rv;
322
323   listener =
324     stream_session_lookup_listener (&ip46_address,
325                                     clib_host_to_net_u16
326                                     (port_number_host_byte_order), sst);
327
328   if (!listener)
329     return VNET_API_ERROR_ADDRESS_NOT_IN_USE;
330
331   /* External client? */
332   if (api_client_index != ~0)
333     {
334       ASSERT (vl_api_client_index_to_registration (api_client_index));
335     }
336
337   return vnet_unbind_i (api_client_index);
338 }
339
340 int
341 vnet_connect_uri (vnet_connect_args_t * a)
342 {
343   ip46_address_t ip46_address;
344   u16 port;
345   session_type_t sst;
346   application_t *app;
347   int rv;
348
349   app = application_lookup (a->api_client_index);
350   if (app)
351     {
352       clib_warning ("Already have a connect from this app");
353       return VNET_API_ERROR_INVALID_VALUE_2;
354     }
355
356   /* Parse uri */
357   rv = parse_uri (a->uri, &sst, &ip46_address, &port);
358   if (rv)
359     return rv;
360
361   return vnet_connect_i (a->api_client_index, a->api_context, sst,
362                          &ip46_address, port, a->options, a->mp,
363                          a->session_cb_vft);
364 }
365
366 int
367 vnet_disconnect_session (u32 session_index, u32 thread_index)
368 {
369   stream_session_t *session;
370
371   session = stream_session_get (session_index, thread_index);
372   stream_session_disconnect (session);
373
374   return 0;
375 }
376
377
378 int
379 vnet_bind (vnet_bind_args_t * a)
380 {
381   application_t *server = 0;
382   session_type_t sst = SESSION_N_TYPES;
383   int rv;
384
385   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
386   if ((rv = vnet_bind_i (a->api_client_index, &a->tep.ip, a->tep.port, sst,
387                          a->options, a->session_cb_vft, &server,
388                          &a->segment_name_length, a->segment_name)))
389     return rv;
390
391   a->server_event_queue_address = (u64) server->event_queue;
392   a->handle = (u64) a->tep.vrf << 32 | (u64) server->session_index;
393   return 0;
394 }
395
396 int
397 vnet_unbind (vnet_unbind_args_t * a)
398 {
399   application_t *server;
400
401   if (a->api_client_index != ~0)
402     {
403       ASSERT (vl_api_client_index_to_registration (a->api_client_index));
404     }
405
406   /* Make sure this is the right one */
407   server = application_lookup (a->api_client_index);
408   ASSERT (server->session_index == (0xFFFFFFFF & a->handle));
409
410   /* TODO use handle to disambiguate namespaces/vrfs */
411   return vnet_unbind_i (a->api_client_index);
412 }
413
414 int
415 vnet_connect (vnet_connect_args_t * a)
416 {
417   session_type_t sst;
418   application_t *app;
419
420   app = application_lookup (a->api_client_index);
421   if (app)
422     {
423       clib_warning ("Already have a connect from this app");
424       return VNET_API_ERROR_INVALID_VALUE_2;
425     }
426
427   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
428   return vnet_connect_i (a->api_client_index, a->api_context, sst, &a->tep.ip,
429                          a->tep.port, a->options, a->mp, a->session_cb_vft);
430 }
431
432 int
433 vnet_disconnect (vnet_disconnect_args_t * a)
434 {
435   stream_session_t *session;
436   u32 session_index, thread_index;
437
438   if (api_parse_session_handle (a->handle, &session_index, &thread_index))
439     {
440       clib_warning ("Invalid handle");
441       return -1;
442     }
443
444   session = stream_session_get (session_index, thread_index);
445   stream_session_disconnect (session);
446
447   return 0;
448 }
449
450 /*
451  * fd.io coding-style-patch-verification: ON
452  *
453  * Local Variables:
454  * eval: (c-set-style "gnu")
455  * End:
456  */