blob: f971288f90ca0fac760327d808c8af4273817db4 [file] [log] [blame]
Florin Coras3cbc04b2017-10-02 00:18:51 -07001/*
Florin Coras288eaab2019-02-03 15:26:14 -08002 * Copyright (c) 2017-2019 Cisco and/or its affiliates.
Florin Coras3cbc04b2017-10-02 00:18:51 -07003 * 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
Florin Coras1ee78302019-02-05 15:51:15 -080016#include <vnet/session/transport.h>
Florin Coras3cbc04b2017-10-02 00:18:51 -070017#include <vnet/session/session.h>
18#include <vnet/fib/fib.h>
19
20/**
21 * Per-type vector of transport protocol virtual function tables
22 */
23transport_proto_vft_t *tp_vfts;
24
25/*
26 * Port allocator seed
27 */
28static u32 port_allocator_seed;
29
30/*
31 * Local endpoints table
32 */
33static transport_endpoint_table_t local_endpoints_table;
34
35/*
36 * Pool of local endpoints
37 */
38static transport_endpoint_t *local_endpoints;
39
40/*
41 * Local endpoints pool lock
42 */
43static clib_spinlock_t local_endpoints_lock;
44
Florin Corasd67f1122018-05-21 17:47:40 -070045/*
46 * Period used by transport pacers. Initialized by session layer
47 */
48static double transport_pacer_period;
49
Florin Coras7ac053b2018-11-05 15:57:21 -080050#define TRANSPORT_PACER_MIN_MSS 1460
51#define TRANSPORT_PACER_MIN_BURST TRANSPORT_PACER_MIN_MSS
Florin Corasf65074e2019-03-31 17:17:11 -070052#define TRANSPORT_PACER_MAX_BURST (32 * TRANSPORT_PACER_MIN_MSS)
Florin Corasd67f1122018-05-21 17:47:40 -070053
Florin Coras1c710452017-10-17 00:03:13 -070054u8 *
55format_transport_proto (u8 * s, va_list * args)
56{
57 u32 transport_proto = va_arg (*args, u32);
58 switch (transport_proto)
59 {
60 case TRANSPORT_PROTO_TCP:
61 s = format (s, "TCP");
62 break;
63 case TRANSPORT_PROTO_UDP:
64 s = format (s, "UDP");
65 break;
Marco Varlese191a5942017-10-30 18:17:21 +010066 case TRANSPORT_PROTO_SCTP:
67 s = format (s, "SCTP");
68 break;
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020069 case TRANSPORT_PROTO_NONE:
70 s = format (s, "NONE");
71 break;
72 case TRANSPORT_PROTO_TLS:
73 s = format (s, "TLS");
74 break;
Florin Coras7fb0fe12018-04-09 09:24:52 -070075 case TRANSPORT_PROTO_UDPC:
76 s = format (s, "UDPC");
77 break;
Nathan Skrzypczak60f3e652019-03-19 13:57:31 +010078 case TRANSPORT_PROTO_QUIC:
79 s = format (s, "QUIC");
80 break;
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020081 default:
82 s = format (s, "UNKNOWN");
83 break;
Florin Coras1c710452017-10-17 00:03:13 -070084 }
85 return s;
86}
87
Florin Coras561af9b2017-12-09 10:19:43 -080088u8 *
89format_transport_proto_short (u8 * s, va_list * args)
90{
91 u32 transport_proto = va_arg (*args, u32);
92 switch (transport_proto)
93 {
94 case TRANSPORT_PROTO_TCP:
95 s = format (s, "T");
96 break;
97 case TRANSPORT_PROTO_UDP:
98 s = format (s, "U");
99 break;
Florin Coras4399c2e2018-01-25 06:34:42 -0800100 case TRANSPORT_PROTO_SCTP:
101 s = format (s, "S");
102 break;
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +0200103 case TRANSPORT_PROTO_NONE:
104 s = format (s, "N");
105 break;
106 case TRANSPORT_PROTO_TLS:
107 s = format (s, "J");
108 break;
Florin Coras7fb0fe12018-04-09 09:24:52 -0700109 case TRANSPORT_PROTO_UDPC:
110 s = format (s, "U");
111 break;
Nathan Skrzypczak60f3e652019-03-19 13:57:31 +0100112 case TRANSPORT_PROTO_QUIC:
113 s = format (s, "Q");
114 break;
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +0200115 default:
116 s = format (s, "?");
117 break;
Florin Coras561af9b2017-12-09 10:19:43 -0800118 }
119 return s;
120}
121
Florin Corasde9a8492018-10-24 22:18:58 -0700122u8 *
123format_transport_connection (u8 * s, va_list * args)
124{
125 u32 transport_proto = va_arg (*args, u32);
126 u32 conn_index = va_arg (*args, u32);
127 u32 thread_index = va_arg (*args, u32);
128 u32 verbose = va_arg (*args, u32);
129 transport_proto_vft_t *tp_vft;
130 transport_connection_t *tc;
131 u32 indent;
132
133 tp_vft = transport_protocol_get_vft (transport_proto);
134 if (!tp_vft)
135 return s;
136
137 s = format (s, "%U", tp_vft->format_connection, conn_index, thread_index,
138 verbose);
139 tc = tp_vft->get_connection (conn_index, thread_index);
140 if (tc && transport_connection_is_tx_paced (tc) && verbose > 1)
141 {
142 indent = format_get_indent (s) + 1;
143 s = format (s, "%Upacer: %U\n", format_white_space, indent,
144 format_transport_pacer, &tc->pacer);
145 }
146 return s;
147}
148
149u8 *
150format_transport_listen_connection (u8 * s, va_list * args)
151{
152 u32 transport_proto = va_arg (*args, u32);
Florin Corasde9a8492018-10-24 22:18:58 -0700153 transport_proto_vft_t *tp_vft;
154
155 tp_vft = transport_protocol_get_vft (transport_proto);
156 if (!tp_vft)
157 return s;
158
Florin Coras3389dd22019-02-01 18:00:05 -0800159 s = (tp_vft->format_listener) (s, args);
Florin Corasde9a8492018-10-24 22:18:58 -0700160 return s;
161}
162
163u8 *
164format_transport_half_open_connection (u8 * s, va_list * args)
165{
166 u32 transport_proto = va_arg (*args, u32);
167 u32 listen_index = va_arg (*args, u32);
168 transport_proto_vft_t *tp_vft;
169
170 tp_vft = transport_protocol_get_vft (transport_proto);
171 if (!tp_vft)
172 return s;
173
174 s = format (s, "%U", tp_vft->format_half_open, listen_index);
175 return s;
176}
177
Florin Coras1c710452017-10-17 00:03:13 -0700178uword
179unformat_transport_proto (unformat_input_t * input, va_list * args)
180{
181 u32 *proto = va_arg (*args, u32 *);
182 if (unformat (input, "tcp"))
183 *proto = TRANSPORT_PROTO_TCP;
184 else if (unformat (input, "TCP"))
185 *proto = TRANSPORT_PROTO_TCP;
Nathan Skrzypczakef71ef02019-03-25 10:20:56 +0100186 else if (unformat (input, "udpc"))
187 *proto = TRANSPORT_PROTO_UDPC;
188 else if (unformat (input, "UDPC"))
189 *proto = TRANSPORT_PROTO_UDPC;
Florin Coras1c710452017-10-17 00:03:13 -0700190 else if (unformat (input, "udp"))
191 *proto = TRANSPORT_PROTO_UDP;
192 else if (unformat (input, "UDP"))
193 *proto = TRANSPORT_PROTO_UDP;
Florin Coras4399c2e2018-01-25 06:34:42 -0800194 else if (unformat (input, "sctp"))
Marco Varlese191a5942017-10-30 18:17:21 +0100195 *proto = TRANSPORT_PROTO_SCTP;
196 else if (unformat (input, "SCTP"))
197 *proto = TRANSPORT_PROTO_SCTP;
Florin Coras371ca502018-02-21 12:07:41 -0800198 else if (unformat (input, "tls"))
199 *proto = TRANSPORT_PROTO_TLS;
200 else if (unformat (input, "TLS"))
201 *proto = TRANSPORT_PROTO_TLS;
Nathan Skrzypczak60f3e652019-03-19 13:57:31 +0100202 else if (unformat (input, "quic"))
203 *proto = TRANSPORT_PROTO_QUIC;
204 else if (unformat (input, "QUIC"))
205 *proto = TRANSPORT_PROTO_QUIC;
Florin Coras1c710452017-10-17 00:03:13 -0700206 else
207 return 0;
208 return 1;
209}
Florin Coras3cbc04b2017-10-02 00:18:51 -0700210
211u32
212transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
213 ip46_address_t * ip, u16 port)
214{
215 clib_bihash_kv_24_8_t kv;
216 int rv;
217
218 kv.key[0] = ip->as_u64[0];
219 kv.key[1] = ip->as_u64[1];
220 kv.key[2] = (u64) port << 8 | (u64) proto;
221
222 rv = clib_bihash_search_inline_24_8 (ht, &kv);
223 if (rv == 0)
224 return kv.value;
225
226 return ENDPOINT_INVALID_INDEX;
227}
228
229void
230transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
231 transport_endpoint_t * te, u32 value)
232{
233 clib_bihash_kv_24_8_t kv;
234
235 kv.key[0] = te->ip.as_u64[0];
236 kv.key[1] = te->ip.as_u64[1];
237 kv.key[2] = (u64) te->port << 8 | (u64) proto;
238 kv.value = value;
239
240 clib_bihash_add_del_24_8 (ht, &kv, 1);
241}
242
243void
244transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
245 transport_endpoint_t * te)
246{
247 clib_bihash_kv_24_8_t kv;
248
249 kv.key[0] = te->ip.as_u64[0];
250 kv.key[1] = te->ip.as_u64[1];
251 kv.key[2] = (u64) te->port << 8 | (u64) proto;
252
253 clib_bihash_add_del_24_8 (ht, &kv, 0);
254}
255
256/**
257 * Register transport virtual function table.
258 *
Florin Coras561af9b2017-12-09 10:19:43 -0800259 * @param transport_proto - transport protocol type (i.e., TCP, UDP ..)
260 * @param vft - virtual function table for transport proto
261 * @param fib_proto - network layer protocol
262 * @param output_node - output node index that session layer will hand off
263 * buffers to, for requested fib proto
Florin Coras3cbc04b2017-10-02 00:18:51 -0700264 */
265void
Florin Coras561af9b2017-12-09 10:19:43 -0800266transport_register_protocol (transport_proto_t transport_proto,
267 const transport_proto_vft_t * vft,
268 fib_protocol_t fib_proto, u32 output_node)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700269{
Florin Coras561af9b2017-12-09 10:19:43 -0800270 u8 is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700271
Florin Coras561af9b2017-12-09 10:19:43 -0800272 vec_validate (tp_vfts, transport_proto);
273 tp_vfts[transport_proto] = *vft;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700274
Florin Coras561af9b2017-12-09 10:19:43 -0800275 session_register_transport (transport_proto, vft, is_ip4, output_node);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700276}
277
278/**
279 * Get transport virtual function table
280 *
281 * @param type - session type (not protocol type)
282 */
283transport_proto_vft_t *
Florin Coras561af9b2017-12-09 10:19:43 -0800284transport_protocol_get_vft (transport_proto_t transport_proto)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700285{
Florin Coras561af9b2017-12-09 10:19:43 -0800286 if (transport_proto >= vec_len (tp_vfts))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700287 return 0;
Florin Coras561af9b2017-12-09 10:19:43 -0800288 return &tp_vfts[transport_proto];
Florin Coras3cbc04b2017-10-02 00:18:51 -0700289}
290
Florin Coras7fb0fe12018-04-09 09:24:52 -0700291transport_service_type_t
292transport_protocol_service_type (transport_proto_t tp)
293{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200294 return tp_vfts[tp].transport_options.service_type;
Florin Coras7fb0fe12018-04-09 09:24:52 -0700295}
296
Florin Corasf08f26d2018-05-10 13:20:47 -0700297transport_tx_fn_type_t
298transport_protocol_tx_fn_type (transport_proto_t tp)
299{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200300 return tp_vfts[tp].transport_options.tx_type;
Florin Corasf08f26d2018-05-10 13:20:47 -0700301}
302
Florin Coras1ee78302019-02-05 15:51:15 -0800303void
304transport_cleanup (transport_proto_t tp, u32 conn_index, u8 thread_index)
Florin Coras4edc37e2019-02-04 23:01:34 -0800305{
Florin Coras1ee78302019-02-05 15:51:15 -0800306 tp_vfts[tp].cleanup (conn_index, thread_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800307}
308
Florin Coras1ee78302019-02-05 15:51:15 -0800309int
310transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
Florin Coras4edc37e2019-02-04 23:01:34 -0800311{
Florin Coras1ee78302019-02-05 15:51:15 -0800312 return tp_vfts[tp].connect (tep);
313}
314
315void
316transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
317{
318 tp_vfts[tp].close (conn_index, thread_index);
319}
320
321u32
322transport_start_listen (transport_proto_t tp, u32 session_index,
323 transport_endpoint_t * tep)
324{
325 return tp_vfts[tp].start_listen (session_index, tep);
326}
327
328u32
329transport_stop_listen (transport_proto_t tp, u32 conn_index)
330{
331 return tp_vfts[tp].stop_listen (conn_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800332}
333
Florin Coras40903ac2018-06-10 14:41:23 -0700334u8
335transport_protocol_is_cl (transport_proto_t tp)
336{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200337 return (tp_vfts[tp].transport_options.service_type == TRANSPORT_SERVICE_CL);
Florin Coras40903ac2018-06-10 14:41:23 -0700338}
339
Aloys Augustincdb71702019-04-08 17:54:39 +0200340always_inline void
341default_get_transport_endpoint (transport_connection_t * tc,
Florin Coras09d18c22019-04-24 11:10:02 -0700342 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200343{
344 if (is_lcl)
345 {
Florin Coras09d18c22019-04-24 11:10:02 -0700346 tep->port = tc->lcl_port;
347 tep->is_ip4 = tc->is_ip4;
348 clib_memcpy_fast (&tep->ip, &tc->lcl_ip, sizeof (tc->lcl_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200349 }
350 else
351 {
Florin Coras09d18c22019-04-24 11:10:02 -0700352 tep->port = tc->rmt_port;
353 tep->is_ip4 = tc->is_ip4;
354 clib_memcpy_fast (&tep->ip, &tc->rmt_ip, sizeof (tc->rmt_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200355 }
356}
357
358void
359transport_get_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700360 u32 thread_index, transport_endpoint_t * tep,
361 u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200362{
363 if (tp_vfts[tp].get_transport_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700364 tp_vfts[tp].get_transport_endpoint (conn_index, thread_index, tep,
365 is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200366 else
367 {
368 transport_connection_t *tc;
369 tc = transport_get_connection (tp, conn_index, thread_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700370 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200371 }
372}
373
374void
375transport_get_listener_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700376 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200377{
378 if (tp_vfts[tp].get_transport_listener_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700379 tp_vfts[tp].get_transport_listener_endpoint (conn_index, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200380 else
381 {
382 transport_connection_t *tc;
383 tc = transport_get_listener (tp, conn_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700384 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200385 }
386}
387
Florin Coras3cbc04b2017-10-02 00:18:51 -0700388#define PORT_MASK ((1 << 16)- 1)
389
390void
391transport_endpoint_del (u32 tepi)
392{
393 clib_spinlock_lock_if_init (&local_endpoints_lock);
394 pool_put_index (local_endpoints, tepi);
395 clib_spinlock_unlock_if_init (&local_endpoints_lock);
396}
397
398always_inline transport_endpoint_t *
399transport_endpoint_new (void)
400{
401 transport_endpoint_t *tep;
Florin Coras5665ced2018-10-25 18:03:45 -0700402 pool_get_zero (local_endpoints, tep);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700403 return tep;
404}
405
406void
407transport_endpoint_cleanup (u8 proto, ip46_address_t * lcl_ip, u16 port)
408{
409 u32 tepi;
410 transport_endpoint_t *tep;
411
412 /* Cleanup local endpoint if this was an active connect */
413 tepi = transport_endpoint_lookup (&local_endpoints_table, proto, lcl_ip,
414 clib_net_to_host_u16 (port));
415 if (tepi != ENDPOINT_INVALID_INDEX)
416 {
417 tep = pool_elt_at_index (local_endpoints, tepi);
418 transport_endpoint_table_del (&local_endpoints_table, proto, tep);
419 transport_endpoint_del (tepi);
420 }
421}
422
Florin Coras5665ced2018-10-25 18:03:45 -0700423static void
424transport_endpoint_mark_used (u8 proto, ip46_address_t * ip, u16 port)
425{
426 transport_endpoint_t *tep;
427 clib_spinlock_lock_if_init (&local_endpoints_lock);
428 tep = transport_endpoint_new ();
Dave Barach178cf492018-11-13 16:34:13 -0500429 clib_memcpy_fast (&tep->ip, ip, sizeof (*ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700430 tep->port = port;
431 transport_endpoint_table_add (&local_endpoints_table, proto, tep,
432 tep - local_endpoints);
433 clib_spinlock_unlock_if_init (&local_endpoints_lock);
434}
435
Florin Coras3cbc04b2017-10-02 00:18:51 -0700436/**
437 * Allocate local port and add if successful add entry to local endpoint
438 * table to mark the pair as used.
439 */
440int
441transport_alloc_local_port (u8 proto, ip46_address_t * ip)
442{
Florin Coras3cbc04b2017-10-02 00:18:51 -0700443 u16 min = 1024, max = 65535; /* XXX configurable ? */
444 int tries, limit;
Florin Coras5665ced2018-10-25 18:03:45 -0700445 u32 tei;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700446
447 limit = max - min;
448
449 /* Only support active opens from thread 0 */
450 ASSERT (vlib_get_thread_index () == 0);
451
452 /* Search for first free slot */
453 for (tries = 0; tries < limit; tries++)
454 {
455 u16 port = 0;
456
457 /* Find a port in the specified range */
458 while (1)
459 {
460 port = random_u32 (&port_allocator_seed) & PORT_MASK;
461 if (PREDICT_TRUE (port >= min && port < max))
462 break;
463 }
464
465 /* Look it up. If not found, we're done */
466 tei = transport_endpoint_lookup (&local_endpoints_table, proto, ip,
467 port);
468 if (tei == ENDPOINT_INVALID_INDEX)
469 {
Florin Coras5665ced2018-10-25 18:03:45 -0700470 transport_endpoint_mark_used (proto, ip, port);
471 return port;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700472 }
473 }
474 return -1;
475}
476
Florin Coras5665ced2018-10-25 18:03:45 -0700477static clib_error_t *
478transport_get_interface_ip (u32 sw_if_index, u8 is_ip4, ip46_address_t * addr)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700479{
Florin Coras5665ced2018-10-25 18:03:45 -0700480 if (is_ip4)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700481 {
482 ip4_address_t *ip4;
483 ip4 = ip_interface_get_first_ip (sw_if_index, 1);
Florin Corasfc804d92018-01-26 01:27:01 -0800484 if (!ip4)
Florin Coras5665ced2018-10-25 18:03:45 -0700485 return clib_error_return (0, "no routable ip4 address on %U",
486 format_vnet_sw_if_index_name,
487 vnet_get_main (), sw_if_index);
488 addr->ip4.as_u32 = ip4->as_u32;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700489 }
490 else
491 {
492 ip6_address_t *ip6;
493 ip6 = ip_interface_get_first_ip (sw_if_index, 0);
494 if (ip6 == 0)
Florin Coras5665ced2018-10-25 18:03:45 -0700495 return clib_error_return (0, "no routable ip6 addresses on %U",
496 format_vnet_sw_if_index_name,
497 vnet_get_main (), sw_if_index);
Dave Barach178cf492018-11-13 16:34:13 -0500498 clib_memcpy_fast (&addr->ip6, ip6, sizeof (*ip6));
Florin Coras5665ced2018-10-25 18:03:45 -0700499 }
500 return 0;
501}
502
503static clib_error_t *
504transport_find_local_ip_for_remote (u32 sw_if_index,
505 transport_endpoint_t * rmt,
506 ip46_address_t * lcl_addr)
507{
508 fib_node_index_t fei;
509 fib_prefix_t prefix;
510
511 if (sw_if_index == ENDPOINT_INVALID_INDEX)
512 {
513 /* Find a FIB path to the destination */
Dave Barach178cf492018-11-13 16:34:13 -0500514 clib_memcpy_fast (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700515 prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
516 prefix.fp_len = rmt->is_ip4 ? 32 : 128;
517
518 ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
519 fei = fib_table_lookup (rmt->fib_index, &prefix);
520
521 /* Couldn't find route to destination. Bail out. */
522 if (fei == FIB_NODE_INDEX_INVALID)
523 return clib_error_return (0, "no route to %U", format_ip46_address,
524 &rmt->ip, (rmt->is_ip4 == 0) + 1);
525
526 sw_if_index = fib_entry_get_resolving_interface (fei);
527 if (sw_if_index == ENDPOINT_INVALID_INDEX)
528 return clib_error_return (0, "no resolving interface for %U",
529 format_ip46_address, &rmt->ip,
530 (rmt->is_ip4 == 0) + 1);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700531 }
532
Florin Coras5665ced2018-10-25 18:03:45 -0700533 clib_memset (lcl_addr, 0, sizeof (*lcl_addr));
534 return transport_get_interface_ip (sw_if_index, rmt->is_ip4, lcl_addr);
535}
536
537int
538transport_alloc_local_endpoint (u8 proto, transport_endpoint_cfg_t * rmt_cfg,
539 ip46_address_t * lcl_addr, u16 * lcl_port)
540{
541 transport_endpoint_t *rmt = (transport_endpoint_t *) rmt_cfg;
542 clib_error_t *error;
543 int port;
544 u32 tei;
545
546 /*
547 * Find the local address
548 */
549 if (ip_is_zero (&rmt_cfg->peer.ip, rmt_cfg->peer.is_ip4))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700550 {
Florin Coras5665ced2018-10-25 18:03:45 -0700551 error = transport_find_local_ip_for_remote (rmt_cfg->peer.sw_if_index,
552 rmt, lcl_addr);
553 if (error)
Florin Corasdc2e2512018-12-03 17:47:26 -0800554 {
555 clib_error_report (error);
556 return -1;
557 }
Florin Coras3cbc04b2017-10-02 00:18:51 -0700558 }
Florin Coras5665ced2018-10-25 18:03:45 -0700559 else
560 {
561 /* Assume session layer vetted this address */
Dave Barach178cf492018-11-13 16:34:13 -0500562 clib_memcpy_fast (lcl_addr, &rmt_cfg->peer.ip,
563 sizeof (rmt_cfg->peer.ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700564 }
565
566 /*
567 * Allocate source port
568 */
569 if (rmt_cfg->peer.port == 0)
570 {
571 port = transport_alloc_local_port (proto, lcl_addr);
572 if (port < 1)
573 {
574 clib_warning ("Failed to allocate src port");
575 return -1;
576 }
577 *lcl_port = port;
578 }
579 else
580 {
581 port = clib_net_to_host_u16 (rmt_cfg->peer.port);
582 tei = transport_endpoint_lookup (&local_endpoints_table, proto,
583 lcl_addr, port);
584 if (tei != ENDPOINT_INVALID_INDEX)
585 return -1;
586
587 transport_endpoint_mark_used (proto, lcl_addr, port);
588 *lcl_port = port;
589 }
590
Florin Coras3cbc04b2017-10-02 00:18:51 -0700591 return 0;
592}
593
Florin Corasd67f1122018-05-21 17:47:40 -0700594#define SPACER_CPU_TICKS_PER_PERIOD_SHIFT 10
595#define SPACER_CPU_TICKS_PER_PERIOD (1 << SPACER_CPU_TICKS_PER_PERIOD_SHIFT)
596
597u8 *
598format_transport_pacer (u8 * s, va_list * args)
599{
600 spacer_t *pacer = va_arg (*args, spacer_t *);
601
602 s = format (s, "bucket %u max_burst %u tokens/period %.3f last_update %x",
603 pacer->bucket, pacer->max_burst_size, pacer->tokens_per_period,
604 pacer->last_update);
605 return s;
606}
607
608static inline u32
609spacer_max_burst (spacer_t * pacer, u64 norm_time_now)
610{
611 u64 n_periods = norm_time_now - pacer->last_update;
Florin Corase55a6d72018-10-31 23:09:22 -0700612 u64 inc;
Florin Corasd67f1122018-05-21 17:47:40 -0700613
Florin Corase55a6d72018-10-31 23:09:22 -0700614 if (n_periods > 0 && (inc = n_periods * pacer->tokens_per_period) > 10)
615 {
616 pacer->last_update = norm_time_now;
617 pacer->bucket += inc;
618 }
619
Florin Coras6bc6fd02019-03-27 18:55:11 -0700620 return clib_min (pacer->bucket, TRANSPORT_PACER_MAX_BURST);
Florin Corasd67f1122018-05-21 17:47:40 -0700621}
622
623static inline void
624spacer_update_bucket (spacer_t * pacer, u32 bytes)
625{
626 ASSERT (pacer->bucket >= bytes);
627 pacer->bucket -= bytes;
628}
629
630static inline void
Florin Corasd67f1122018-05-21 17:47:40 -0700631spacer_set_pace_rate (spacer_t * pacer, u64 rate_bytes_per_sec)
632{
633 ASSERT (rate_bytes_per_sec != 0);
634 pacer->tokens_per_period = rate_bytes_per_sec / transport_pacer_period;
635}
636
Florin Coras52814732019-06-12 15:38:19 -0700637static inline u64
638spacer_pace_rate (spacer_t * pacer)
639{
640 return pacer->tokens_per_period * transport_pacer_period;
641}
642
Florin Corasd67f1122018-05-21 17:47:40 -0700643void
Florin Corasc44a5582018-11-01 16:30:54 -0700644transport_connection_tx_pacer_reset (transport_connection_t * tc,
645 u32 rate_bytes_per_sec,
646 u32 start_bucket, u64 time_now)
Florin Corasd67f1122018-05-21 17:47:40 -0700647{
Florin Corasd67f1122018-05-21 17:47:40 -0700648 spacer_t *pacer = &tc->pacer;
Florin Corasd67f1122018-05-21 17:47:40 -0700649 spacer_set_pace_rate (&tc->pacer, rate_bytes_per_sec);
650 pacer->last_update = time_now >> SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
Florin Corasc44a5582018-11-01 16:30:54 -0700651 pacer->bucket = start_bucket;
652}
653
654void
655transport_connection_tx_pacer_init (transport_connection_t * tc,
656 u32 rate_bytes_per_sec,
657 u32 initial_bucket)
658{
659 vlib_main_t *vm = vlib_get_main ();
660 tc->flags |= TRANSPORT_CONNECTION_F_IS_TX_PACED;
661 transport_connection_tx_pacer_reset (tc, rate_bytes_per_sec,
662 initial_bucket,
663 vm->clib_time.last_cpu_time);
Florin Corasd67f1122018-05-21 17:47:40 -0700664}
665
666void
667transport_connection_tx_pacer_update (transport_connection_t * tc,
668 u64 bytes_per_sec)
669{
Florin Corasd67f1122018-05-21 17:47:40 -0700670 spacer_set_pace_rate (&tc->pacer, bytes_per_sec);
Florin Corasd67f1122018-05-21 17:47:40 -0700671}
672
673u32
Florin Corase55a6d72018-10-31 23:09:22 -0700674transport_connection_tx_pacer_burst (transport_connection_t * tc,
675 u64 time_now)
676{
677 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
678 return spacer_max_burst (&tc->pacer, time_now);
679}
680
681u32
682transport_connection_snd_space (transport_connection_t * tc, u64 time_now,
683 u16 mss)
Florin Corasd67f1122018-05-21 17:47:40 -0700684{
685 u32 snd_space, max_paced_burst;
Florin Corasd67f1122018-05-21 17:47:40 -0700686
687 snd_space = tp_vfts[tc->proto].send_space (tc);
688 if (transport_connection_is_tx_paced (tc))
689 {
690 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
691 max_paced_burst = spacer_max_burst (&tc->pacer, time_now);
Florin Corasd67f1122018-05-21 17:47:40 -0700692 max_paced_burst = (max_paced_burst < mss) ? 0 : max_paced_burst;
693 snd_space = clib_min (snd_space, max_paced_burst);
694 snd_space = snd_space - snd_space % mss;
695 }
696 return snd_space;
697}
698
Florin Coras52814732019-06-12 15:38:19 -0700699u64
700transport_connection_tx_pacer_rate (transport_connection_t * tc)
701{
702 return spacer_pace_rate (&tc->pacer);
703}
704
Florin Corasd67f1122018-05-21 17:47:40 -0700705void
706transport_connection_update_tx_stats (transport_connection_t * tc, u32 bytes)
707{
708 tc->stats.tx_bytes += bytes;
709 if (transport_connection_is_tx_paced (tc))
710 spacer_update_bucket (&tc->pacer, bytes);
711}
712
713void
Florin Corase55a6d72018-10-31 23:09:22 -0700714transport_connection_tx_pacer_update_bytes (transport_connection_t * tc,
715 u32 bytes)
716{
717 spacer_update_bucket (&tc->pacer, bytes);
718}
719
720void
Florin Corasd67f1122018-05-21 17:47:40 -0700721transport_init_tx_pacers_period (void)
722{
723 f64 cpu_freq = os_cpu_clock_frequency ();
724 transport_pacer_period = cpu_freq / SPACER_CPU_TICKS_PER_PERIOD;
725}
726
Florin Coras3cbc04b2017-10-02 00:18:51 -0700727void
Florin Coras561af9b2017-12-09 10:19:43 -0800728transport_update_time (f64 time_now, u8 thread_index)
729{
730 transport_proto_vft_t *vft;
731 vec_foreach (vft, tp_vfts)
732 {
733 if (vft->update_time)
734 (vft->update_time) (time_now, thread_index);
735 }
736}
737
738void
739transport_enable_disable (vlib_main_t * vm, u8 is_en)
740{
741 transport_proto_vft_t *vft;
742 vec_foreach (vft, tp_vfts)
743 {
744 if (vft->enable)
745 (vft->enable) (vm, is_en);
746 }
747}
748
749void
Florin Coras3cbc04b2017-10-02 00:18:51 -0700750transport_init (void)
751{
752 vlib_thread_main_t *vtm = vlib_get_thread_main ();
Florin Coras31c99552019-03-01 13:00:58 -0800753 session_main_t *smm = vnet_get_session_main ();
Florin Coras3cbc04b2017-10-02 00:18:51 -0700754 u32 num_threads;
755
Florin Coras93e65802017-11-29 00:07:11 -0500756 if (smm->local_endpoints_table_buckets == 0)
757 smm->local_endpoints_table_buckets = 250000;
758 if (smm->local_endpoints_table_memory == 0)
759 smm->local_endpoints_table_memory = 512 << 20;
760
Florin Coras3cbc04b2017-10-02 00:18:51 -0700761 /* Initialize [port-allocator] random number seed */
762 port_allocator_seed = (u32) clib_cpu_time_now ();
763
764 clib_bihash_init_24_8 (&local_endpoints_table, "local endpoints table",
Florin Coras93e65802017-11-29 00:07:11 -0500765 smm->local_endpoints_table_buckets,
766 smm->local_endpoints_table_memory);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700767 num_threads = 1 /* main thread */ + vtm->n_threads;
768 if (num_threads > 1)
769 clib_spinlock_init (&local_endpoints_lock);
770}
771
772/*
773 * fd.io coding-style-patch-verification: ON
774 *
775 * Local Variables:
776 * eval: (c-set-style "gnu")
777 * End:
778 */