blob: 8f7e30c86025a5f30c4be27aae565dc348de263f [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 Coras1c710452017-10-17 00:03:13 -070050u8 *
51format_transport_proto (u8 * s, va_list * args)
52{
53 u32 transport_proto = va_arg (*args, u32);
54 switch (transport_proto)
55 {
Florin Coras5bb23ec2019-08-31 09:45:13 -070056#define _(sym, str, sstr) \
57 case TRANSPORT_PROTO_ ## sym: \
58 s = format (s, str); \
Florin Coras1c710452017-10-17 00:03:13 -070059 break;
Florin Coras5bb23ec2019-08-31 09:45:13 -070060 foreach_transport_proto
61#undef _
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020062 default:
63 s = format (s, "UNKNOWN");
64 break;
Florin Coras1c710452017-10-17 00:03:13 -070065 }
66 return s;
67}
68
Florin Coras561af9b2017-12-09 10:19:43 -080069u8 *
70format_transport_proto_short (u8 * s, va_list * args)
71{
72 u32 transport_proto = va_arg (*args, u32);
73 switch (transport_proto)
74 {
Florin Coras5bb23ec2019-08-31 09:45:13 -070075#define _(sym, str, sstr) \
76 case TRANSPORT_PROTO_ ## sym: \
77 s = format (s, sstr); \
Florin Coras561af9b2017-12-09 10:19:43 -080078 break;
Florin Coras5bb23ec2019-08-31 09:45:13 -070079 foreach_transport_proto
80#undef _
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020081 default:
82 s = format (s, "?");
83 break;
Florin Coras561af9b2017-12-09 10:19:43 -080084 }
85 return s;
86}
87
Florin Corasde9a8492018-10-24 22:18:58 -070088u8 *
89format_transport_connection (u8 * s, va_list * args)
90{
91 u32 transport_proto = va_arg (*args, u32);
92 u32 conn_index = va_arg (*args, u32);
93 u32 thread_index = va_arg (*args, u32);
94 u32 verbose = va_arg (*args, u32);
95 transport_proto_vft_t *tp_vft;
96 transport_connection_t *tc;
97 u32 indent;
98
99 tp_vft = transport_protocol_get_vft (transport_proto);
100 if (!tp_vft)
101 return s;
102
103 s = format (s, "%U", tp_vft->format_connection, conn_index, thread_index,
104 verbose);
105 tc = tp_vft->get_connection (conn_index, thread_index);
106 if (tc && transport_connection_is_tx_paced (tc) && verbose > 1)
107 {
108 indent = format_get_indent (s) + 1;
109 s = format (s, "%Upacer: %U\n", format_white_space, indent,
110 format_transport_pacer, &tc->pacer);
111 }
112 return s;
113}
114
115u8 *
116format_transport_listen_connection (u8 * s, va_list * args)
117{
118 u32 transport_proto = va_arg (*args, u32);
Florin Corasde9a8492018-10-24 22:18:58 -0700119 transport_proto_vft_t *tp_vft;
120
121 tp_vft = transport_protocol_get_vft (transport_proto);
122 if (!tp_vft)
123 return s;
124
Florin Coras3389dd22019-02-01 18:00:05 -0800125 s = (tp_vft->format_listener) (s, args);
Florin Corasde9a8492018-10-24 22:18:58 -0700126 return s;
127}
128
129u8 *
130format_transport_half_open_connection (u8 * s, va_list * args)
131{
132 u32 transport_proto = va_arg (*args, u32);
133 u32 listen_index = va_arg (*args, u32);
134 transport_proto_vft_t *tp_vft;
135
136 tp_vft = transport_protocol_get_vft (transport_proto);
137 if (!tp_vft)
138 return s;
139
140 s = format (s, "%U", tp_vft->format_half_open, listen_index);
141 return s;
142}
143
Florin Coras1c710452017-10-17 00:03:13 -0700144uword
145unformat_transport_proto (unformat_input_t * input, va_list * args)
146{
147 u32 *proto = va_arg (*args, u32 *);
Florin Coras5bb23ec2019-08-31 09:45:13 -0700148
149#define _(sym, str, sstr) \
150 if (unformat (input, str)) \
151 { \
152 *proto = TRANSPORT_PROTO_ ## sym; \
153 return 1; \
154 }
155 foreach_transport_proto
156#undef _
Florin Coras1c710452017-10-17 00:03:13 -0700157 return 0;
Florin Coras1c710452017-10-17 00:03:13 -0700158}
Florin Coras3cbc04b2017-10-02 00:18:51 -0700159
160u32
161transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
162 ip46_address_t * ip, u16 port)
163{
164 clib_bihash_kv_24_8_t kv;
165 int rv;
166
167 kv.key[0] = ip->as_u64[0];
168 kv.key[1] = ip->as_u64[1];
169 kv.key[2] = (u64) port << 8 | (u64) proto;
170
171 rv = clib_bihash_search_inline_24_8 (ht, &kv);
172 if (rv == 0)
173 return kv.value;
174
175 return ENDPOINT_INVALID_INDEX;
176}
177
178void
179transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
180 transport_endpoint_t * te, u32 value)
181{
182 clib_bihash_kv_24_8_t kv;
183
184 kv.key[0] = te->ip.as_u64[0];
185 kv.key[1] = te->ip.as_u64[1];
186 kv.key[2] = (u64) te->port << 8 | (u64) proto;
187 kv.value = value;
188
189 clib_bihash_add_del_24_8 (ht, &kv, 1);
190}
191
192void
193transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
194 transport_endpoint_t * te)
195{
196 clib_bihash_kv_24_8_t kv;
197
198 kv.key[0] = te->ip.as_u64[0];
199 kv.key[1] = te->ip.as_u64[1];
200 kv.key[2] = (u64) te->port << 8 | (u64) proto;
201
202 clib_bihash_add_del_24_8 (ht, &kv, 0);
203}
204
205/**
206 * Register transport virtual function table.
207 *
Florin Coras561af9b2017-12-09 10:19:43 -0800208 * @param transport_proto - transport protocol type (i.e., TCP, UDP ..)
209 * @param vft - virtual function table for transport proto
210 * @param fib_proto - network layer protocol
211 * @param output_node - output node index that session layer will hand off
212 * buffers to, for requested fib proto
Florin Coras3cbc04b2017-10-02 00:18:51 -0700213 */
214void
Florin Coras561af9b2017-12-09 10:19:43 -0800215transport_register_protocol (transport_proto_t transport_proto,
216 const transport_proto_vft_t * vft,
217 fib_protocol_t fib_proto, u32 output_node)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700218{
Florin Coras561af9b2017-12-09 10:19:43 -0800219 u8 is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700220
Florin Coras561af9b2017-12-09 10:19:43 -0800221 vec_validate (tp_vfts, transport_proto);
222 tp_vfts[transport_proto] = *vft;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700223
Florin Coras561af9b2017-12-09 10:19:43 -0800224 session_register_transport (transport_proto, vft, is_ip4, output_node);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700225}
226
227/**
228 * Get transport virtual function table
229 *
230 * @param type - session type (not protocol type)
231 */
232transport_proto_vft_t *
Florin Coras561af9b2017-12-09 10:19:43 -0800233transport_protocol_get_vft (transport_proto_t transport_proto)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700234{
Florin Coras561af9b2017-12-09 10:19:43 -0800235 if (transport_proto >= vec_len (tp_vfts))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700236 return 0;
Florin Coras561af9b2017-12-09 10:19:43 -0800237 return &tp_vfts[transport_proto];
Florin Coras3cbc04b2017-10-02 00:18:51 -0700238}
239
Nathan Skrzypczaka26349d2019-06-26 13:53:08 +0200240u8
241transport_half_open_has_fifos (transport_proto_t tp)
242{
243 return tp_vfts[tp].transport_options.half_open_has_fifos;
244}
245
Florin Coras7fb0fe12018-04-09 09:24:52 -0700246transport_service_type_t
247transport_protocol_service_type (transport_proto_t tp)
248{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200249 return tp_vfts[tp].transport_options.service_type;
Florin Coras7fb0fe12018-04-09 09:24:52 -0700250}
251
Florin Corasf08f26d2018-05-10 13:20:47 -0700252transport_tx_fn_type_t
253transport_protocol_tx_fn_type (transport_proto_t tp)
254{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200255 return tp_vfts[tp].transport_options.tx_type;
Florin Corasf08f26d2018-05-10 13:20:47 -0700256}
257
Florin Coras1ee78302019-02-05 15:51:15 -0800258void
259transport_cleanup (transport_proto_t tp, u32 conn_index, u8 thread_index)
Florin Coras4edc37e2019-02-04 23:01:34 -0800260{
Florin Coras1ee78302019-02-05 15:51:15 -0800261 tp_vfts[tp].cleanup (conn_index, thread_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800262}
263
Florin Coras1ee78302019-02-05 15:51:15 -0800264int
265transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
Florin Coras4edc37e2019-02-04 23:01:34 -0800266{
Florin Coras1ee78302019-02-05 15:51:15 -0800267 return tp_vfts[tp].connect (tep);
268}
269
270void
271transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
272{
273 tp_vfts[tp].close (conn_index, thread_index);
274}
275
Florin Corasdfb3b872019-08-16 17:48:44 -0700276void
277transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index)
278{
279 if (tp_vfts[tp].reset)
280 tp_vfts[tp].reset (conn_index, thread_index);
281 else
282 tp_vfts[tp].close (conn_index, thread_index);
283}
284
Florin Coras1ee78302019-02-05 15:51:15 -0800285u32
286transport_start_listen (transport_proto_t tp, u32 session_index,
287 transport_endpoint_t * tep)
288{
289 return tp_vfts[tp].start_listen (session_index, tep);
290}
291
292u32
293transport_stop_listen (transport_proto_t tp, u32 conn_index)
294{
295 return tp_vfts[tp].stop_listen (conn_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800296}
297
Florin Coras40903ac2018-06-10 14:41:23 -0700298u8
299transport_protocol_is_cl (transport_proto_t tp)
300{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200301 return (tp_vfts[tp].transport_options.service_type == TRANSPORT_SERVICE_CL);
Florin Coras40903ac2018-06-10 14:41:23 -0700302}
303
Aloys Augustincdb71702019-04-08 17:54:39 +0200304always_inline void
305default_get_transport_endpoint (transport_connection_t * tc,
Florin Coras09d18c22019-04-24 11:10:02 -0700306 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200307{
308 if (is_lcl)
309 {
Florin Coras09d18c22019-04-24 11:10:02 -0700310 tep->port = tc->lcl_port;
311 tep->is_ip4 = tc->is_ip4;
312 clib_memcpy_fast (&tep->ip, &tc->lcl_ip, sizeof (tc->lcl_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200313 }
314 else
315 {
Florin Coras09d18c22019-04-24 11:10:02 -0700316 tep->port = tc->rmt_port;
317 tep->is_ip4 = tc->is_ip4;
318 clib_memcpy_fast (&tep->ip, &tc->rmt_ip, sizeof (tc->rmt_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200319 }
320}
321
322void
323transport_get_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700324 u32 thread_index, transport_endpoint_t * tep,
325 u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200326{
327 if (tp_vfts[tp].get_transport_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700328 tp_vfts[tp].get_transport_endpoint (conn_index, thread_index, tep,
329 is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200330 else
331 {
332 transport_connection_t *tc;
333 tc = transport_get_connection (tp, conn_index, thread_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700334 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200335 }
336}
337
338void
339transport_get_listener_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700340 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200341{
342 if (tp_vfts[tp].get_transport_listener_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700343 tp_vfts[tp].get_transport_listener_endpoint (conn_index, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200344 else
345 {
346 transport_connection_t *tc;
347 tc = transport_get_listener (tp, conn_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700348 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200349 }
350}
351
Florin Coras3cbc04b2017-10-02 00:18:51 -0700352#define PORT_MASK ((1 << 16)- 1)
353
354void
355transport_endpoint_del (u32 tepi)
356{
357 clib_spinlock_lock_if_init (&local_endpoints_lock);
358 pool_put_index (local_endpoints, tepi);
359 clib_spinlock_unlock_if_init (&local_endpoints_lock);
360}
361
362always_inline transport_endpoint_t *
363transport_endpoint_new (void)
364{
365 transport_endpoint_t *tep;
Florin Coras5665ced2018-10-25 18:03:45 -0700366 pool_get_zero (local_endpoints, tep);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700367 return tep;
368}
369
370void
371transport_endpoint_cleanup (u8 proto, ip46_address_t * lcl_ip, u16 port)
372{
373 u32 tepi;
374 transport_endpoint_t *tep;
375
376 /* Cleanup local endpoint if this was an active connect */
377 tepi = transport_endpoint_lookup (&local_endpoints_table, proto, lcl_ip,
378 clib_net_to_host_u16 (port));
379 if (tepi != ENDPOINT_INVALID_INDEX)
380 {
381 tep = pool_elt_at_index (local_endpoints, tepi);
382 transport_endpoint_table_del (&local_endpoints_table, proto, tep);
383 transport_endpoint_del (tepi);
384 }
385}
386
Florin Coras5665ced2018-10-25 18:03:45 -0700387static void
388transport_endpoint_mark_used (u8 proto, ip46_address_t * ip, u16 port)
389{
390 transport_endpoint_t *tep;
391 clib_spinlock_lock_if_init (&local_endpoints_lock);
392 tep = transport_endpoint_new ();
Dave Barach178cf492018-11-13 16:34:13 -0500393 clib_memcpy_fast (&tep->ip, ip, sizeof (*ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700394 tep->port = port;
395 transport_endpoint_table_add (&local_endpoints_table, proto, tep,
396 tep - local_endpoints);
397 clib_spinlock_unlock_if_init (&local_endpoints_lock);
398}
399
Florin Coras3cbc04b2017-10-02 00:18:51 -0700400/**
401 * Allocate local port and add if successful add entry to local endpoint
402 * table to mark the pair as used.
403 */
404int
405transport_alloc_local_port (u8 proto, ip46_address_t * ip)
406{
Florin Coras3cbc04b2017-10-02 00:18:51 -0700407 u16 min = 1024, max = 65535; /* XXX configurable ? */
408 int tries, limit;
Florin Coras5665ced2018-10-25 18:03:45 -0700409 u32 tei;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700410
411 limit = max - min;
412
413 /* Only support active opens from thread 0 */
414 ASSERT (vlib_get_thread_index () == 0);
415
416 /* Search for first free slot */
417 for (tries = 0; tries < limit; tries++)
418 {
419 u16 port = 0;
420
421 /* Find a port in the specified range */
422 while (1)
423 {
424 port = random_u32 (&port_allocator_seed) & PORT_MASK;
425 if (PREDICT_TRUE (port >= min && port < max))
426 break;
427 }
428
429 /* Look it up. If not found, we're done */
430 tei = transport_endpoint_lookup (&local_endpoints_table, proto, ip,
431 port);
432 if (tei == ENDPOINT_INVALID_INDEX)
433 {
Florin Coras5665ced2018-10-25 18:03:45 -0700434 transport_endpoint_mark_used (proto, ip, port);
435 return port;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700436 }
437 }
438 return -1;
439}
440
Florin Coras5665ced2018-10-25 18:03:45 -0700441static clib_error_t *
442transport_get_interface_ip (u32 sw_if_index, u8 is_ip4, ip46_address_t * addr)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700443{
Florin Coras5665ced2018-10-25 18:03:45 -0700444 if (is_ip4)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700445 {
446 ip4_address_t *ip4;
447 ip4 = ip_interface_get_first_ip (sw_if_index, 1);
Florin Corasfc804d92018-01-26 01:27:01 -0800448 if (!ip4)
Florin Coras5665ced2018-10-25 18:03:45 -0700449 return clib_error_return (0, "no routable ip4 address on %U",
450 format_vnet_sw_if_index_name,
451 vnet_get_main (), sw_if_index);
452 addr->ip4.as_u32 = ip4->as_u32;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700453 }
454 else
455 {
456 ip6_address_t *ip6;
457 ip6 = ip_interface_get_first_ip (sw_if_index, 0);
458 if (ip6 == 0)
Florin Coras5665ced2018-10-25 18:03:45 -0700459 return clib_error_return (0, "no routable ip6 addresses on %U",
460 format_vnet_sw_if_index_name,
461 vnet_get_main (), sw_if_index);
Dave Barach178cf492018-11-13 16:34:13 -0500462 clib_memcpy_fast (&addr->ip6, ip6, sizeof (*ip6));
Florin Coras5665ced2018-10-25 18:03:45 -0700463 }
464 return 0;
465}
466
467static clib_error_t *
468transport_find_local_ip_for_remote (u32 sw_if_index,
469 transport_endpoint_t * rmt,
470 ip46_address_t * lcl_addr)
471{
472 fib_node_index_t fei;
473 fib_prefix_t prefix;
474
475 if (sw_if_index == ENDPOINT_INVALID_INDEX)
476 {
477 /* Find a FIB path to the destination */
Dave Barach178cf492018-11-13 16:34:13 -0500478 clib_memcpy_fast (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700479 prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
480 prefix.fp_len = rmt->is_ip4 ? 32 : 128;
481
482 ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
483 fei = fib_table_lookup (rmt->fib_index, &prefix);
484
485 /* Couldn't find route to destination. Bail out. */
486 if (fei == FIB_NODE_INDEX_INVALID)
487 return clib_error_return (0, "no route to %U", format_ip46_address,
488 &rmt->ip, (rmt->is_ip4 == 0) + 1);
489
490 sw_if_index = fib_entry_get_resolving_interface (fei);
491 if (sw_if_index == ENDPOINT_INVALID_INDEX)
492 return clib_error_return (0, "no resolving interface for %U",
493 format_ip46_address, &rmt->ip,
494 (rmt->is_ip4 == 0) + 1);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700495 }
496
Florin Coras5665ced2018-10-25 18:03:45 -0700497 clib_memset (lcl_addr, 0, sizeof (*lcl_addr));
498 return transport_get_interface_ip (sw_if_index, rmt->is_ip4, lcl_addr);
499}
500
501int
502transport_alloc_local_endpoint (u8 proto, transport_endpoint_cfg_t * rmt_cfg,
503 ip46_address_t * lcl_addr, u16 * lcl_port)
504{
505 transport_endpoint_t *rmt = (transport_endpoint_t *) rmt_cfg;
506 clib_error_t *error;
507 int port;
508 u32 tei;
509
510 /*
511 * Find the local address
512 */
513 if (ip_is_zero (&rmt_cfg->peer.ip, rmt_cfg->peer.is_ip4))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700514 {
Florin Coras5665ced2018-10-25 18:03:45 -0700515 error = transport_find_local_ip_for_remote (rmt_cfg->peer.sw_if_index,
516 rmt, lcl_addr);
517 if (error)
Florin Corasdc2e2512018-12-03 17:47:26 -0800518 {
519 clib_error_report (error);
520 return -1;
521 }
Florin Coras3cbc04b2017-10-02 00:18:51 -0700522 }
Florin Coras5665ced2018-10-25 18:03:45 -0700523 else
524 {
525 /* Assume session layer vetted this address */
Dave Barach178cf492018-11-13 16:34:13 -0500526 clib_memcpy_fast (lcl_addr, &rmt_cfg->peer.ip,
527 sizeof (rmt_cfg->peer.ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700528 }
529
530 /*
531 * Allocate source port
532 */
533 if (rmt_cfg->peer.port == 0)
534 {
535 port = transport_alloc_local_port (proto, lcl_addr);
536 if (port < 1)
537 {
538 clib_warning ("Failed to allocate src port");
539 return -1;
540 }
541 *lcl_port = port;
542 }
543 else
544 {
545 port = clib_net_to_host_u16 (rmt_cfg->peer.port);
546 tei = transport_endpoint_lookup (&local_endpoints_table, proto,
547 lcl_addr, port);
548 if (tei != ENDPOINT_INVALID_INDEX)
549 return -1;
550
551 transport_endpoint_mark_used (proto, lcl_addr, port);
552 *lcl_port = port;
553 }
554
Florin Coras3cbc04b2017-10-02 00:18:51 -0700555 return 0;
556}
557
Florin Corasd67f1122018-05-21 17:47:40 -0700558#define SPACER_CPU_TICKS_PER_PERIOD_SHIFT 10
559#define SPACER_CPU_TICKS_PER_PERIOD (1 << SPACER_CPU_TICKS_PER_PERIOD_SHIFT)
560
561u8 *
562format_transport_pacer (u8 * s, va_list * args)
563{
564 spacer_t *pacer = va_arg (*args, spacer_t *);
Florin Corasbe237bf2019-09-27 08:16:40 -0700565 vlib_main_t *vm = vlib_get_main ();
566 u64 now, diff;
Florin Corasd67f1122018-05-21 17:47:40 -0700567
Florin Corasbe237bf2019-09-27 08:16:40 -0700568 now = vm->clib_time.last_cpu_time;
569 diff = now - (pacer->last_update << SPACER_CPU_TICKS_PER_PERIOD_SHIFT);
570 s = format (s, "rate %u bucket %u t/p %.3f last_update %.3f",
571 pacer->bytes_per_sec, pacer->bucket, pacer->tokens_per_period,
572 diff * vm->clib_time.seconds_per_clock);
Florin Corasd67f1122018-05-21 17:47:40 -0700573 return s;
574}
575
576static inline u32
577spacer_max_burst (spacer_t * pacer, u64 norm_time_now)
578{
579 u64 n_periods = norm_time_now - pacer->last_update;
Florin Corase55a6d72018-10-31 23:09:22 -0700580 u64 inc;
Florin Corasd67f1122018-05-21 17:47:40 -0700581
Florin Corasc31dc312019-10-06 14:06:14 -0700582 if (PREDICT_FALSE (n_periods > 5e5))
583 {
584 pacer->last_update = norm_time_now;
585 pacer->bucket = TRANSPORT_PACER_MIN_BURST;
586 return TRANSPORT_PACER_MIN_BURST;
587 }
588
Florin Coras36ebcff2019-09-12 18:36:44 -0700589 if (n_periods > 0
590 && (inc = (f32) n_periods * pacer->tokens_per_period) > 10)
Florin Corase55a6d72018-10-31 23:09:22 -0700591 {
592 pacer->last_update = norm_time_now;
Florin Coras7c8f8282019-09-15 16:28:45 -0700593 pacer->bucket = clib_min (pacer->bucket + inc, pacer->bytes_per_sec);
Florin Corase55a6d72018-10-31 23:09:22 -0700594 }
595
Florin Coras6bc6fd02019-03-27 18:55:11 -0700596 return clib_min (pacer->bucket, TRANSPORT_PACER_MAX_BURST);
Florin Corasd67f1122018-05-21 17:47:40 -0700597}
598
599static inline void
600spacer_update_bucket (spacer_t * pacer, u32 bytes)
601{
602 ASSERT (pacer->bucket >= bytes);
603 pacer->bucket -= bytes;
604}
605
606static inline void
Florin Corasd67f1122018-05-21 17:47:40 -0700607spacer_set_pace_rate (spacer_t * pacer, u64 rate_bytes_per_sec)
608{
609 ASSERT (rate_bytes_per_sec != 0);
Florin Coras7c8f8282019-09-15 16:28:45 -0700610 pacer->bytes_per_sec = rate_bytes_per_sec;
Florin Corasd67f1122018-05-21 17:47:40 -0700611 pacer->tokens_per_period = rate_bytes_per_sec / transport_pacer_period;
612}
613
Florin Coras52814732019-06-12 15:38:19 -0700614static inline u64
615spacer_pace_rate (spacer_t * pacer)
616{
Florin Coras7c8f8282019-09-15 16:28:45 -0700617 return pacer->bytes_per_sec;
Florin Coras52814732019-06-12 15:38:19 -0700618}
619
Florin Coras36ebcff2019-09-12 18:36:44 -0700620static inline void
621spacer_reset_bucket (spacer_t * pacer, u64 norm_time_now)
622{
623 pacer->last_update = norm_time_now;
624 pacer->bucket = 0;
625}
626
Florin Corasd67f1122018-05-21 17:47:40 -0700627void
Florin Corasc44a5582018-11-01 16:30:54 -0700628transport_connection_tx_pacer_reset (transport_connection_t * tc,
629 u32 rate_bytes_per_sec,
630 u32 start_bucket, u64 time_now)
Florin Corasd67f1122018-05-21 17:47:40 -0700631{
Florin Corasd67f1122018-05-21 17:47:40 -0700632 spacer_t *pacer = &tc->pacer;
Florin Corasd67f1122018-05-21 17:47:40 -0700633 spacer_set_pace_rate (&tc->pacer, rate_bytes_per_sec);
634 pacer->last_update = time_now >> SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
Florin Corasc44a5582018-11-01 16:30:54 -0700635 pacer->bucket = start_bucket;
636}
637
638void
639transport_connection_tx_pacer_init (transport_connection_t * tc,
640 u32 rate_bytes_per_sec,
641 u32 initial_bucket)
642{
643 vlib_main_t *vm = vlib_get_main ();
644 tc->flags |= TRANSPORT_CONNECTION_F_IS_TX_PACED;
645 transport_connection_tx_pacer_reset (tc, rate_bytes_per_sec,
646 initial_bucket,
647 vm->clib_time.last_cpu_time);
Florin Corasd67f1122018-05-21 17:47:40 -0700648}
649
650void
651transport_connection_tx_pacer_update (transport_connection_t * tc,
652 u64 bytes_per_sec)
653{
Florin Corasd67f1122018-05-21 17:47:40 -0700654 spacer_set_pace_rate (&tc->pacer, bytes_per_sec);
Florin Corasd67f1122018-05-21 17:47:40 -0700655}
656
657u32
Florin Corase55a6d72018-10-31 23:09:22 -0700658transport_connection_tx_pacer_burst (transport_connection_t * tc,
659 u64 time_now)
660{
661 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
662 return spacer_max_burst (&tc->pacer, time_now);
663}
664
Florin Coras36ebcff2019-09-12 18:36:44 -0700665void
666transport_connection_tx_pacer_reset_bucket (transport_connection_t * tc,
667 u64 time_now)
668{
669 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
670 spacer_reset_bucket (&tc->pacer, time_now);
671}
672
Florin Corase55a6d72018-10-31 23:09:22 -0700673u32
674transport_connection_snd_space (transport_connection_t * tc, u64 time_now,
675 u16 mss)
Florin Corasd67f1122018-05-21 17:47:40 -0700676{
677 u32 snd_space, max_paced_burst;
Florin Corasd67f1122018-05-21 17:47:40 -0700678
679 snd_space = tp_vfts[tc->proto].send_space (tc);
Florin Corasc31dc312019-10-06 14:06:14 -0700680 if (snd_space && transport_connection_is_tx_paced (tc))
Florin Corasd67f1122018-05-21 17:47:40 -0700681 {
682 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
683 max_paced_burst = spacer_max_burst (&tc->pacer, time_now);
Simon Zhang1146ff42019-09-02 22:54:00 +0800684 max_paced_burst =
685 (max_paced_burst < TRANSPORT_PACER_MIN_BURST) ? 0 : max_paced_burst;
Florin Corasd67f1122018-05-21 17:47:40 -0700686 snd_space = clib_min (snd_space, max_paced_burst);
Simon Zhang1146ff42019-09-02 22:54:00 +0800687 return snd_space >= mss ? snd_space - snd_space % mss : snd_space;
Florin Corasd67f1122018-05-21 17:47:40 -0700688 }
689 return snd_space;
690}
691
Florin Coras52814732019-06-12 15:38:19 -0700692u64
693transport_connection_tx_pacer_rate (transport_connection_t * tc)
694{
695 return spacer_pace_rate (&tc->pacer);
696}
697
Florin Corasd67f1122018-05-21 17:47:40 -0700698void
Florin Coras00482232019-08-02 12:52:00 -0700699transport_connection_update_tx_bytes (transport_connection_t * tc, u32 bytes)
Florin Corasd67f1122018-05-21 17:47:40 -0700700{
Florin Corasd67f1122018-05-21 17:47:40 -0700701 if (transport_connection_is_tx_paced (tc))
702 spacer_update_bucket (&tc->pacer, bytes);
703}
704
705void
Florin Corase55a6d72018-10-31 23:09:22 -0700706transport_connection_tx_pacer_update_bytes (transport_connection_t * tc,
707 u32 bytes)
708{
709 spacer_update_bucket (&tc->pacer, bytes);
710}
711
712void
Florin Corasd67f1122018-05-21 17:47:40 -0700713transport_init_tx_pacers_period (void)
714{
715 f64 cpu_freq = os_cpu_clock_frequency ();
716 transport_pacer_period = cpu_freq / SPACER_CPU_TICKS_PER_PERIOD;
717}
718
Florin Coras3cbc04b2017-10-02 00:18:51 -0700719void
Florin Coras561af9b2017-12-09 10:19:43 -0800720transport_update_time (f64 time_now, u8 thread_index)
721{
722 transport_proto_vft_t *vft;
723 vec_foreach (vft, tp_vfts)
724 {
725 if (vft->update_time)
726 (vft->update_time) (time_now, thread_index);
727 }
728}
729
730void
731transport_enable_disable (vlib_main_t * vm, u8 is_en)
732{
733 transport_proto_vft_t *vft;
734 vec_foreach (vft, tp_vfts)
735 {
736 if (vft->enable)
737 (vft->enable) (vm, is_en);
738 }
739}
740
741void
Florin Coras3cbc04b2017-10-02 00:18:51 -0700742transport_init (void)
743{
744 vlib_thread_main_t *vtm = vlib_get_thread_main ();
Florin Coras31c99552019-03-01 13:00:58 -0800745 session_main_t *smm = vnet_get_session_main ();
Florin Coras3cbc04b2017-10-02 00:18:51 -0700746 u32 num_threads;
747
Florin Coras93e65802017-11-29 00:07:11 -0500748 if (smm->local_endpoints_table_buckets == 0)
749 smm->local_endpoints_table_buckets = 250000;
750 if (smm->local_endpoints_table_memory == 0)
751 smm->local_endpoints_table_memory = 512 << 20;
752
Florin Coras3cbc04b2017-10-02 00:18:51 -0700753 /* Initialize [port-allocator] random number seed */
754 port_allocator_seed = (u32) clib_cpu_time_now ();
755
756 clib_bihash_init_24_8 (&local_endpoints_table, "local endpoints table",
Florin Coras93e65802017-11-29 00:07:11 -0500757 smm->local_endpoints_table_buckets,
758 smm->local_endpoints_table_memory);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700759 num_threads = 1 /* main thread */ + vtm->n_threads;
760 if (num_threads > 1)
761 clib_spinlock_init (&local_endpoints_lock);
762}
763
764/*
765 * fd.io coding-style-patch-verification: ON
766 *
767 * Local Variables:
768 * eval: (c-set-style "gnu")
769 * End:
770 */