VPP-598: tcp stack initial commit
[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));
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 bind 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   stream_session_open (sst, ip46, port, app->index);
190
191   return 0;
192 }
193
194 /**
195  * unformat a vnet URI
196  *
197  * fifo://name
198  * tcp://ip46-addr:port
199  * udp://ip46-addr:port
200  *
201  * u8 ip46_address[16];
202  * u16  port_in_host_byte_order;
203  * stream_session_type_t sst;
204  * u8 *fifo_name;
205  *
206  * if (unformat (input, "%U", unformat_vnet_uri, &ip46_address,
207  *              &sst, &port, &fifo_name))
208  *  etc...
209  *
210  */
211 uword
212 unformat_vnet_uri (unformat_input_t * input, va_list * args)
213 {
214   ip46_address_t *address = va_arg (*args, ip46_address_t *);
215   session_type_t *sst = va_arg (*args, session_type_t *);
216   u16 *port = va_arg (*args, u16 *);
217
218   if (unformat (input, "tcp://%U/%d", unformat_ip4_address, &address->ip4,
219                 port))
220     {
221       *sst = SESSION_TYPE_IP4_TCP;
222       return 1;
223     }
224   if (unformat (input, "udp://%U/%d", unformat_ip4_address, &address->ip4,
225                 port))
226     {
227       *sst = SESSION_TYPE_IP4_UDP;
228       return 1;
229     }
230   if (unformat (input, "udp://%U/%d", unformat_ip6_address, &address->ip6,
231                 port))
232     {
233       *sst = SESSION_TYPE_IP6_UDP;
234       return 1;
235     }
236   if (unformat (input, "tcp://%U/%d", unformat_ip6_address, &address->ip6,
237                 port))
238     {
239       *sst = SESSION_TYPE_IP6_TCP;
240       return 1;
241     }
242
243   return 0;
244 }
245
246 int
247 parse_uri (char *uri, session_type_t * sst, ip46_address_t * addr,
248            u16 * port_number_host_byte_order)
249 {
250   unformat_input_t _input, *input = &_input;
251
252   /* Make sure */
253   uri = (char *) format (0, "%s%c", uri, 0);
254
255   /* Parse uri */
256   unformat_init_string (input, uri, strlen (uri));
257   if (!unformat (input, "%U", unformat_vnet_uri, addr, sst,
258                  port_number_host_byte_order))
259     {
260       unformat_free (input);
261       return VNET_API_ERROR_INVALID_VALUE;
262     }
263   unformat_free (input);
264
265   return 0;
266 }
267
268 int
269 vnet_bind_uri (vnet_bind_args_t * a)
270 {
271   application_t *server = 0;
272   u16 port_host_order;
273   session_type_t sst = SESSION_N_TYPES;
274   ip46_address_t ip46;
275   int rv;
276
277   memset (&ip46, 0, sizeof (ip46));
278   rv = parse_uri (a->uri, &sst, &ip46, &port_host_order);
279   if (rv)
280     return rv;
281
282   if ((rv = vnet_bind_i (a->api_client_index, &ip46, port_host_order, sst,
283                          a->options, a->session_cb_vft, &server,
284                          &a->segment_name_length, a->segment_name)))
285     return rv;
286
287   a->server_event_queue_address = (u64) server->event_queue;
288   return 0;
289 }
290
291 session_type_t
292 session_type_from_proto_and_ip (session_api_proto_t proto, u8 is_ip4)
293 {
294   if (proto == SESSION_PROTO_TCP)
295     {
296       if (is_ip4)
297         return SESSION_TYPE_IP4_TCP;
298       else
299         return SESSION_TYPE_IP6_TCP;
300     }
301   else
302     {
303       if (is_ip4)
304         return SESSION_TYPE_IP4_UDP;
305       else
306         return SESSION_TYPE_IP6_UDP;
307     }
308
309   return SESSION_N_TYPES;
310 }
311
312 int
313 vnet_unbind_uri (char *uri, u32 api_client_index)
314 {
315   u16 port_number_host_byte_order;
316   session_type_t sst = SESSION_N_TYPES;
317   ip46_address_t ip46_address;
318   stream_session_t *listener;
319   int rv;
320
321   rv = parse_uri (uri, &sst, &ip46_address, &port_number_host_byte_order);
322   if (rv)
323     return rv;
324
325   listener =
326     stream_session_lookup_listener (&ip46_address,
327                                     clib_host_to_net_u16
328                                     (port_number_host_byte_order), sst);
329
330   if (!listener)
331     return VNET_API_ERROR_ADDRESS_NOT_IN_USE;
332
333   /* External client? */
334   if (api_client_index != ~0)
335     {
336       ASSERT (vl_api_client_index_to_registration (api_client_index));
337     }
338
339   return vnet_unbind_i (api_client_index);
340 }
341
342 int
343 vnet_connect_uri (vnet_connect_args_t * a)
344 {
345   ip46_address_t ip46_address;
346   u16 port;
347   session_type_t sst;
348   application_t *app;
349   int rv;
350
351   app = application_lookup (a->api_client_index);
352   if (app)
353     {
354       clib_warning ("Already have a connect from this app");
355       return VNET_API_ERROR_INVALID_VALUE_2;
356     }
357
358   /* Parse uri */
359   rv = parse_uri (a->uri, &sst, &ip46_address, &port);
360   if (rv)
361     return rv;
362
363   return vnet_connect_i (a->api_client_index, a->api_context, sst,
364                          &ip46_address, port, a->options, a->mp,
365                          a->session_cb_vft);
366 }
367
368 int
369 vnet_disconnect_session (u32 client_index, u32 session_index,
370                          u32 thread_index)
371 {
372   stream_session_t *session;
373
374   session = stream_session_get (session_index, thread_index);
375   stream_session_disconnect (session);
376
377   return 0;
378 }
379
380
381 int
382 vnet_bind (vnet_bind_args_t * a)
383 {
384   application_t *server = 0;
385   session_type_t sst = SESSION_N_TYPES;
386   int rv;
387
388   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
389   if ((rv = vnet_bind_i (a->api_client_index, &a->tep.ip, a->tep.port, sst,
390                          a->options, a->session_cb_vft, &server,
391                          &a->segment_name_length, a->segment_name)))
392     return rv;
393
394   a->server_event_queue_address = (u64) server->event_queue;
395   a->handle = (u64) a->tep.vrf << 32 | (u64) server->session_index;
396   return 0;
397 }
398
399 int
400 vnet_unbind (vnet_unbind_args_t * a)
401 {
402   application_t *server;
403
404   if (a->api_client_index != ~0)
405     {
406       ASSERT (vl_api_client_index_to_registration (a->api_client_index));
407     }
408
409   /* Make sure this is the right one */
410   server = application_lookup (a->api_client_index);
411   ASSERT (server->session_index == (0xFFFFFFFF & a->handle));
412
413   /* TODO use handle to disambiguate namespaces/vrfs */
414   return vnet_unbind_i (a->api_client_index);
415 }
416
417 int
418 vnet_connect (vnet_connect_args_t * a)
419 {
420   session_type_t sst;
421   application_t *app;
422
423   app = application_lookup (a->api_client_index);
424   if (app)
425     {
426       clib_warning ("Already have a connect from this app");
427       return VNET_API_ERROR_INVALID_VALUE_2;
428     }
429
430   sst = session_type_from_proto_and_ip (a->proto, a->tep.is_ip4);
431   return vnet_connect_i (a->api_client_index, a->api_context, sst, &a->tep.ip,
432                          a->tep.port, a->options, a->mp, a->session_cb_vft);
433 }
434
435 int
436 vnet_disconnect (vnet_disconnect_args_t * a)
437 {
438   stream_session_t *session;
439   u32 session_index, thread_index;
440
441   if (api_parse_session_handle (a->handle, &session_index, &thread_index))
442     {
443       clib_warning ("Invalid handle");
444       return -1;
445     }
446
447   session = stream_session_get (session_index, thread_index);
448   stream_session_disconnect (session);
449
450   return 0;
451 }
452
453 /*
454  * fd.io coding-style-patch-verification: ON
455  *
456  * Local Variables:
457  * eval: (c-set-style "gnu")
458  * End:
459  */