blob: 49a4af71111f3e05e784ad76dc5a9cfddd7c7f5c [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
Simon Zhang1146ff42019-09-02 22:54:00 +080052#define TRANSPORT_PACER_MAX_BURST (43 * 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 {
Florin Coras5bb23ec2019-08-31 09:45:13 -070060#define _(sym, str, sstr) \
61 case TRANSPORT_PROTO_ ## sym: \
62 s = format (s, str); \
Florin Coras1c710452017-10-17 00:03:13 -070063 break;
Florin Coras5bb23ec2019-08-31 09:45:13 -070064 foreach_transport_proto
65#undef _
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020066 default:
67 s = format (s, "UNKNOWN");
68 break;
Florin Coras1c710452017-10-17 00:03:13 -070069 }
70 return s;
71}
72
Florin Coras561af9b2017-12-09 10:19:43 -080073u8 *
74format_transport_proto_short (u8 * s, va_list * args)
75{
76 u32 transport_proto = va_arg (*args, u32);
77 switch (transport_proto)
78 {
Florin Coras5bb23ec2019-08-31 09:45:13 -070079#define _(sym, str, sstr) \
80 case TRANSPORT_PROTO_ ## sym: \
81 s = format (s, sstr); \
Florin Coras561af9b2017-12-09 10:19:43 -080082 break;
Florin Coras5bb23ec2019-08-31 09:45:13 -070083 foreach_transport_proto
84#undef _
Nathan Skrzypczakba65ca42019-05-16 16:35:40 +020085 default:
86 s = format (s, "?");
87 break;
Florin Coras561af9b2017-12-09 10:19:43 -080088 }
89 return s;
90}
91
Florin Corasde9a8492018-10-24 22:18:58 -070092u8 *
93format_transport_connection (u8 * s, va_list * args)
94{
95 u32 transport_proto = va_arg (*args, u32);
96 u32 conn_index = va_arg (*args, u32);
97 u32 thread_index = va_arg (*args, u32);
98 u32 verbose = va_arg (*args, u32);
99 transport_proto_vft_t *tp_vft;
100 transport_connection_t *tc;
101 u32 indent;
102
103 tp_vft = transport_protocol_get_vft (transport_proto);
104 if (!tp_vft)
105 return s;
106
107 s = format (s, "%U", tp_vft->format_connection, conn_index, thread_index,
108 verbose);
109 tc = tp_vft->get_connection (conn_index, thread_index);
110 if (tc && transport_connection_is_tx_paced (tc) && verbose > 1)
111 {
112 indent = format_get_indent (s) + 1;
113 s = format (s, "%Upacer: %U\n", format_white_space, indent,
114 format_transport_pacer, &tc->pacer);
115 }
116 return s;
117}
118
119u8 *
120format_transport_listen_connection (u8 * s, va_list * args)
121{
122 u32 transport_proto = va_arg (*args, u32);
Florin Corasde9a8492018-10-24 22:18:58 -0700123 transport_proto_vft_t *tp_vft;
124
125 tp_vft = transport_protocol_get_vft (transport_proto);
126 if (!tp_vft)
127 return s;
128
Florin Coras3389dd22019-02-01 18:00:05 -0800129 s = (tp_vft->format_listener) (s, args);
Florin Corasde9a8492018-10-24 22:18:58 -0700130 return s;
131}
132
133u8 *
134format_transport_half_open_connection (u8 * s, va_list * args)
135{
136 u32 transport_proto = va_arg (*args, u32);
137 u32 listen_index = va_arg (*args, u32);
138 transport_proto_vft_t *tp_vft;
139
140 tp_vft = transport_protocol_get_vft (transport_proto);
141 if (!tp_vft)
142 return s;
143
144 s = format (s, "%U", tp_vft->format_half_open, listen_index);
145 return s;
146}
147
Florin Coras1c710452017-10-17 00:03:13 -0700148uword
149unformat_transport_proto (unformat_input_t * input, va_list * args)
150{
151 u32 *proto = va_arg (*args, u32 *);
Florin Coras5bb23ec2019-08-31 09:45:13 -0700152
153#define _(sym, str, sstr) \
154 if (unformat (input, str)) \
155 { \
156 *proto = TRANSPORT_PROTO_ ## sym; \
157 return 1; \
158 }
159 foreach_transport_proto
160#undef _
Florin Coras1c710452017-10-17 00:03:13 -0700161 return 0;
Florin Coras1c710452017-10-17 00:03:13 -0700162}
Florin Coras3cbc04b2017-10-02 00:18:51 -0700163
164u32
165transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
166 ip46_address_t * ip, u16 port)
167{
168 clib_bihash_kv_24_8_t kv;
169 int rv;
170
171 kv.key[0] = ip->as_u64[0];
172 kv.key[1] = ip->as_u64[1];
173 kv.key[2] = (u64) port << 8 | (u64) proto;
174
175 rv = clib_bihash_search_inline_24_8 (ht, &kv);
176 if (rv == 0)
177 return kv.value;
178
179 return ENDPOINT_INVALID_INDEX;
180}
181
182void
183transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
184 transport_endpoint_t * te, u32 value)
185{
186 clib_bihash_kv_24_8_t kv;
187
188 kv.key[0] = te->ip.as_u64[0];
189 kv.key[1] = te->ip.as_u64[1];
190 kv.key[2] = (u64) te->port << 8 | (u64) proto;
191 kv.value = value;
192
193 clib_bihash_add_del_24_8 (ht, &kv, 1);
194}
195
196void
197transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
198 transport_endpoint_t * te)
199{
200 clib_bihash_kv_24_8_t kv;
201
202 kv.key[0] = te->ip.as_u64[0];
203 kv.key[1] = te->ip.as_u64[1];
204 kv.key[2] = (u64) te->port << 8 | (u64) proto;
205
206 clib_bihash_add_del_24_8 (ht, &kv, 0);
207}
208
209/**
210 * Register transport virtual function table.
211 *
Florin Coras561af9b2017-12-09 10:19:43 -0800212 * @param transport_proto - transport protocol type (i.e., TCP, UDP ..)
213 * @param vft - virtual function table for transport proto
214 * @param fib_proto - network layer protocol
215 * @param output_node - output node index that session layer will hand off
216 * buffers to, for requested fib proto
Florin Coras3cbc04b2017-10-02 00:18:51 -0700217 */
218void
Florin Coras561af9b2017-12-09 10:19:43 -0800219transport_register_protocol (transport_proto_t transport_proto,
220 const transport_proto_vft_t * vft,
221 fib_protocol_t fib_proto, u32 output_node)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700222{
Florin Coras561af9b2017-12-09 10:19:43 -0800223 u8 is_ip4 = fib_proto == FIB_PROTOCOL_IP4;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700224
Florin Coras561af9b2017-12-09 10:19:43 -0800225 vec_validate (tp_vfts, transport_proto);
226 tp_vfts[transport_proto] = *vft;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700227
Florin Coras561af9b2017-12-09 10:19:43 -0800228 session_register_transport (transport_proto, vft, is_ip4, output_node);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700229}
230
231/**
232 * Get transport virtual function table
233 *
234 * @param type - session type (not protocol type)
235 */
236transport_proto_vft_t *
Florin Coras561af9b2017-12-09 10:19:43 -0800237transport_protocol_get_vft (transport_proto_t transport_proto)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700238{
Florin Coras561af9b2017-12-09 10:19:43 -0800239 if (transport_proto >= vec_len (tp_vfts))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700240 return 0;
Florin Coras561af9b2017-12-09 10:19:43 -0800241 return &tp_vfts[transport_proto];
Florin Coras3cbc04b2017-10-02 00:18:51 -0700242}
243
Nathan Skrzypczaka26349d2019-06-26 13:53:08 +0200244u8
245transport_half_open_has_fifos (transport_proto_t tp)
246{
247 return tp_vfts[tp].transport_options.half_open_has_fifos;
248}
249
Florin Coras7fb0fe12018-04-09 09:24:52 -0700250transport_service_type_t
251transport_protocol_service_type (transport_proto_t tp)
252{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200253 return tp_vfts[tp].transport_options.service_type;
Florin Coras7fb0fe12018-04-09 09:24:52 -0700254}
255
Florin Corasf08f26d2018-05-10 13:20:47 -0700256transport_tx_fn_type_t
257transport_protocol_tx_fn_type (transport_proto_t tp)
258{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200259 return tp_vfts[tp].transport_options.tx_type;
Florin Corasf08f26d2018-05-10 13:20:47 -0700260}
261
Florin Coras1ee78302019-02-05 15:51:15 -0800262void
263transport_cleanup (transport_proto_t tp, u32 conn_index, u8 thread_index)
Florin Coras4edc37e2019-02-04 23:01:34 -0800264{
Florin Coras1ee78302019-02-05 15:51:15 -0800265 tp_vfts[tp].cleanup (conn_index, thread_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800266}
267
Florin Coras1ee78302019-02-05 15:51:15 -0800268int
269transport_connect (transport_proto_t tp, transport_endpoint_cfg_t * tep)
Florin Coras4edc37e2019-02-04 23:01:34 -0800270{
Florin Coras1ee78302019-02-05 15:51:15 -0800271 return tp_vfts[tp].connect (tep);
272}
273
274void
275transport_close (transport_proto_t tp, u32 conn_index, u8 thread_index)
276{
277 tp_vfts[tp].close (conn_index, thread_index);
278}
279
Florin Corasdfb3b872019-08-16 17:48:44 -0700280void
281transport_reset (transport_proto_t tp, u32 conn_index, u8 thread_index)
282{
283 if (tp_vfts[tp].reset)
284 tp_vfts[tp].reset (conn_index, thread_index);
285 else
286 tp_vfts[tp].close (conn_index, thread_index);
287}
288
Florin Coras1ee78302019-02-05 15:51:15 -0800289u32
290transport_start_listen (transport_proto_t tp, u32 session_index,
291 transport_endpoint_t * tep)
292{
293 return tp_vfts[tp].start_listen (session_index, tep);
294}
295
296u32
297transport_stop_listen (transport_proto_t tp, u32 conn_index)
298{
299 return tp_vfts[tp].stop_listen (conn_index);
Florin Coras4edc37e2019-02-04 23:01:34 -0800300}
301
Florin Coras40903ac2018-06-10 14:41:23 -0700302u8
303transport_protocol_is_cl (transport_proto_t tp)
304{
Nathan Skrzypczake971bc92019-06-19 13:42:37 +0200305 return (tp_vfts[tp].transport_options.service_type == TRANSPORT_SERVICE_CL);
Florin Coras40903ac2018-06-10 14:41:23 -0700306}
307
Aloys Augustincdb71702019-04-08 17:54:39 +0200308always_inline void
309default_get_transport_endpoint (transport_connection_t * tc,
Florin Coras09d18c22019-04-24 11:10:02 -0700310 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200311{
312 if (is_lcl)
313 {
Florin Coras09d18c22019-04-24 11:10:02 -0700314 tep->port = tc->lcl_port;
315 tep->is_ip4 = tc->is_ip4;
316 clib_memcpy_fast (&tep->ip, &tc->lcl_ip, sizeof (tc->lcl_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200317 }
318 else
319 {
Florin Coras09d18c22019-04-24 11:10:02 -0700320 tep->port = tc->rmt_port;
321 tep->is_ip4 = tc->is_ip4;
322 clib_memcpy_fast (&tep->ip, &tc->rmt_ip, sizeof (tc->rmt_ip));
Aloys Augustincdb71702019-04-08 17:54:39 +0200323 }
324}
325
326void
327transport_get_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700328 u32 thread_index, transport_endpoint_t * tep,
329 u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200330{
331 if (tp_vfts[tp].get_transport_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700332 tp_vfts[tp].get_transport_endpoint (conn_index, thread_index, tep,
333 is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200334 else
335 {
336 transport_connection_t *tc;
337 tc = transport_get_connection (tp, conn_index, thread_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700338 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200339 }
340}
341
342void
343transport_get_listener_endpoint (transport_proto_t tp, u32 conn_index,
Florin Coras09d18c22019-04-24 11:10:02 -0700344 transport_endpoint_t * tep, u8 is_lcl)
Aloys Augustincdb71702019-04-08 17:54:39 +0200345{
346 if (tp_vfts[tp].get_transport_listener_endpoint)
Florin Coras09d18c22019-04-24 11:10:02 -0700347 tp_vfts[tp].get_transport_listener_endpoint (conn_index, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200348 else
349 {
350 transport_connection_t *tc;
351 tc = transport_get_listener (tp, conn_index);
Florin Coras09d18c22019-04-24 11:10:02 -0700352 default_get_transport_endpoint (tc, tep, is_lcl);
Aloys Augustincdb71702019-04-08 17:54:39 +0200353 }
354}
355
Florin Coras3cbc04b2017-10-02 00:18:51 -0700356#define PORT_MASK ((1 << 16)- 1)
357
358void
359transport_endpoint_del (u32 tepi)
360{
361 clib_spinlock_lock_if_init (&local_endpoints_lock);
362 pool_put_index (local_endpoints, tepi);
363 clib_spinlock_unlock_if_init (&local_endpoints_lock);
364}
365
366always_inline transport_endpoint_t *
367transport_endpoint_new (void)
368{
369 transport_endpoint_t *tep;
Florin Coras5665ced2018-10-25 18:03:45 -0700370 pool_get_zero (local_endpoints, tep);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700371 return tep;
372}
373
374void
375transport_endpoint_cleanup (u8 proto, ip46_address_t * lcl_ip, u16 port)
376{
377 u32 tepi;
378 transport_endpoint_t *tep;
379
380 /* Cleanup local endpoint if this was an active connect */
381 tepi = transport_endpoint_lookup (&local_endpoints_table, proto, lcl_ip,
382 clib_net_to_host_u16 (port));
383 if (tepi != ENDPOINT_INVALID_INDEX)
384 {
385 tep = pool_elt_at_index (local_endpoints, tepi);
386 transport_endpoint_table_del (&local_endpoints_table, proto, tep);
387 transport_endpoint_del (tepi);
388 }
389}
390
Florin Coras5665ced2018-10-25 18:03:45 -0700391static void
392transport_endpoint_mark_used (u8 proto, ip46_address_t * ip, u16 port)
393{
394 transport_endpoint_t *tep;
395 clib_spinlock_lock_if_init (&local_endpoints_lock);
396 tep = transport_endpoint_new ();
Dave Barach178cf492018-11-13 16:34:13 -0500397 clib_memcpy_fast (&tep->ip, ip, sizeof (*ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700398 tep->port = port;
399 transport_endpoint_table_add (&local_endpoints_table, proto, tep,
400 tep - local_endpoints);
401 clib_spinlock_unlock_if_init (&local_endpoints_lock);
402}
403
Florin Coras3cbc04b2017-10-02 00:18:51 -0700404/**
405 * Allocate local port and add if successful add entry to local endpoint
406 * table to mark the pair as used.
407 */
408int
409transport_alloc_local_port (u8 proto, ip46_address_t * ip)
410{
Florin Coras3cbc04b2017-10-02 00:18:51 -0700411 u16 min = 1024, max = 65535; /* XXX configurable ? */
412 int tries, limit;
Florin Coras5665ced2018-10-25 18:03:45 -0700413 u32 tei;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700414
415 limit = max - min;
416
417 /* Only support active opens from thread 0 */
418 ASSERT (vlib_get_thread_index () == 0);
419
420 /* Search for first free slot */
421 for (tries = 0; tries < limit; tries++)
422 {
423 u16 port = 0;
424
425 /* Find a port in the specified range */
426 while (1)
427 {
428 port = random_u32 (&port_allocator_seed) & PORT_MASK;
429 if (PREDICT_TRUE (port >= min && port < max))
430 break;
431 }
432
433 /* Look it up. If not found, we're done */
434 tei = transport_endpoint_lookup (&local_endpoints_table, proto, ip,
435 port);
436 if (tei == ENDPOINT_INVALID_INDEX)
437 {
Florin Coras5665ced2018-10-25 18:03:45 -0700438 transport_endpoint_mark_used (proto, ip, port);
439 return port;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700440 }
441 }
442 return -1;
443}
444
Florin Coras5665ced2018-10-25 18:03:45 -0700445static clib_error_t *
446transport_get_interface_ip (u32 sw_if_index, u8 is_ip4, ip46_address_t * addr)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700447{
Florin Coras5665ced2018-10-25 18:03:45 -0700448 if (is_ip4)
Florin Coras3cbc04b2017-10-02 00:18:51 -0700449 {
450 ip4_address_t *ip4;
451 ip4 = ip_interface_get_first_ip (sw_if_index, 1);
Florin Corasfc804d92018-01-26 01:27:01 -0800452 if (!ip4)
Florin Coras5665ced2018-10-25 18:03:45 -0700453 return clib_error_return (0, "no routable ip4 address on %U",
454 format_vnet_sw_if_index_name,
455 vnet_get_main (), sw_if_index);
456 addr->ip4.as_u32 = ip4->as_u32;
Florin Coras3cbc04b2017-10-02 00:18:51 -0700457 }
458 else
459 {
460 ip6_address_t *ip6;
461 ip6 = ip_interface_get_first_ip (sw_if_index, 0);
462 if (ip6 == 0)
Florin Coras5665ced2018-10-25 18:03:45 -0700463 return clib_error_return (0, "no routable ip6 addresses on %U",
464 format_vnet_sw_if_index_name,
465 vnet_get_main (), sw_if_index);
Dave Barach178cf492018-11-13 16:34:13 -0500466 clib_memcpy_fast (&addr->ip6, ip6, sizeof (*ip6));
Florin Coras5665ced2018-10-25 18:03:45 -0700467 }
468 return 0;
469}
470
471static clib_error_t *
472transport_find_local_ip_for_remote (u32 sw_if_index,
473 transport_endpoint_t * rmt,
474 ip46_address_t * lcl_addr)
475{
476 fib_node_index_t fei;
477 fib_prefix_t prefix;
478
479 if (sw_if_index == ENDPOINT_INVALID_INDEX)
480 {
481 /* Find a FIB path to the destination */
Dave Barach178cf492018-11-13 16:34:13 -0500482 clib_memcpy_fast (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700483 prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
484 prefix.fp_len = rmt->is_ip4 ? 32 : 128;
485
486 ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
487 fei = fib_table_lookup (rmt->fib_index, &prefix);
488
489 /* Couldn't find route to destination. Bail out. */
490 if (fei == FIB_NODE_INDEX_INVALID)
491 return clib_error_return (0, "no route to %U", format_ip46_address,
492 &rmt->ip, (rmt->is_ip4 == 0) + 1);
493
494 sw_if_index = fib_entry_get_resolving_interface (fei);
495 if (sw_if_index == ENDPOINT_INVALID_INDEX)
496 return clib_error_return (0, "no resolving interface for %U",
497 format_ip46_address, &rmt->ip,
498 (rmt->is_ip4 == 0) + 1);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700499 }
500
Florin Coras5665ced2018-10-25 18:03:45 -0700501 clib_memset (lcl_addr, 0, sizeof (*lcl_addr));
502 return transport_get_interface_ip (sw_if_index, rmt->is_ip4, lcl_addr);
503}
504
505int
506transport_alloc_local_endpoint (u8 proto, transport_endpoint_cfg_t * rmt_cfg,
507 ip46_address_t * lcl_addr, u16 * lcl_port)
508{
509 transport_endpoint_t *rmt = (transport_endpoint_t *) rmt_cfg;
510 clib_error_t *error;
511 int port;
512 u32 tei;
513
514 /*
515 * Find the local address
516 */
517 if (ip_is_zero (&rmt_cfg->peer.ip, rmt_cfg->peer.is_ip4))
Florin Coras3cbc04b2017-10-02 00:18:51 -0700518 {
Florin Coras5665ced2018-10-25 18:03:45 -0700519 error = transport_find_local_ip_for_remote (rmt_cfg->peer.sw_if_index,
520 rmt, lcl_addr);
521 if (error)
Florin Corasdc2e2512018-12-03 17:47:26 -0800522 {
523 clib_error_report (error);
524 return -1;
525 }
Florin Coras3cbc04b2017-10-02 00:18:51 -0700526 }
Florin Coras5665ced2018-10-25 18:03:45 -0700527 else
528 {
529 /* Assume session layer vetted this address */
Dave Barach178cf492018-11-13 16:34:13 -0500530 clib_memcpy_fast (lcl_addr, &rmt_cfg->peer.ip,
531 sizeof (rmt_cfg->peer.ip));
Florin Coras5665ced2018-10-25 18:03:45 -0700532 }
533
534 /*
535 * Allocate source port
536 */
537 if (rmt_cfg->peer.port == 0)
538 {
539 port = transport_alloc_local_port (proto, lcl_addr);
540 if (port < 1)
541 {
542 clib_warning ("Failed to allocate src port");
543 return -1;
544 }
545 *lcl_port = port;
546 }
547 else
548 {
549 port = clib_net_to_host_u16 (rmt_cfg->peer.port);
550 tei = transport_endpoint_lookup (&local_endpoints_table, proto,
551 lcl_addr, port);
552 if (tei != ENDPOINT_INVALID_INDEX)
553 return -1;
554
555 transport_endpoint_mark_used (proto, lcl_addr, port);
556 *lcl_port = port;
557 }
558
Florin Coras3cbc04b2017-10-02 00:18:51 -0700559 return 0;
560}
561
Florin Corasd67f1122018-05-21 17:47:40 -0700562#define SPACER_CPU_TICKS_PER_PERIOD_SHIFT 10
563#define SPACER_CPU_TICKS_PER_PERIOD (1 << SPACER_CPU_TICKS_PER_PERIOD_SHIFT)
564
565u8 *
566format_transport_pacer (u8 * s, va_list * args)
567{
568 spacer_t *pacer = va_arg (*args, spacer_t *);
Florin Corasbe237bf2019-09-27 08:16:40 -0700569 vlib_main_t *vm = vlib_get_main ();
570 u64 now, diff;
Florin Corasd67f1122018-05-21 17:47:40 -0700571
Florin Corasbe237bf2019-09-27 08:16:40 -0700572 now = vm->clib_time.last_cpu_time;
573 diff = now - (pacer->last_update << SPACER_CPU_TICKS_PER_PERIOD_SHIFT);
574 s = format (s, "rate %u bucket %u t/p %.3f last_update %.3f",
575 pacer->bytes_per_sec, pacer->bucket, pacer->tokens_per_period,
576 diff * vm->clib_time.seconds_per_clock);
Florin Corasd67f1122018-05-21 17:47:40 -0700577 return s;
578}
579
580static inline u32
581spacer_max_burst (spacer_t * pacer, u64 norm_time_now)
582{
583 u64 n_periods = norm_time_now - pacer->last_update;
Florin Corase55a6d72018-10-31 23:09:22 -0700584 u64 inc;
Florin Corasd67f1122018-05-21 17:47:40 -0700585
Florin Coras36ebcff2019-09-12 18:36:44 -0700586 if (n_periods > 0
587 && (inc = (f32) n_periods * pacer->tokens_per_period) > 10)
Florin Corase55a6d72018-10-31 23:09:22 -0700588 {
589 pacer->last_update = norm_time_now;
Florin Coras7c8f8282019-09-15 16:28:45 -0700590 pacer->bucket = clib_min (pacer->bucket + inc, pacer->bytes_per_sec);
Florin Corase55a6d72018-10-31 23:09:22 -0700591 }
592
Florin Coras6bc6fd02019-03-27 18:55:11 -0700593 return clib_min (pacer->bucket, TRANSPORT_PACER_MAX_BURST);
Florin Corasd67f1122018-05-21 17:47:40 -0700594}
595
596static inline void
597spacer_update_bucket (spacer_t * pacer, u32 bytes)
598{
599 ASSERT (pacer->bucket >= bytes);
600 pacer->bucket -= bytes;
601}
602
603static inline void
Florin Corasd67f1122018-05-21 17:47:40 -0700604spacer_set_pace_rate (spacer_t * pacer, u64 rate_bytes_per_sec)
605{
606 ASSERT (rate_bytes_per_sec != 0);
Florin Coras7c8f8282019-09-15 16:28:45 -0700607 pacer->bytes_per_sec = rate_bytes_per_sec;
Florin Corasd67f1122018-05-21 17:47:40 -0700608 pacer->tokens_per_period = rate_bytes_per_sec / transport_pacer_period;
609}
610
Florin Coras52814732019-06-12 15:38:19 -0700611static inline u64
612spacer_pace_rate (spacer_t * pacer)
613{
Florin Coras7c8f8282019-09-15 16:28:45 -0700614 return pacer->bytes_per_sec;
Florin Coras52814732019-06-12 15:38:19 -0700615}
616
Florin Coras36ebcff2019-09-12 18:36:44 -0700617static inline void
618spacer_reset_bucket (spacer_t * pacer, u64 norm_time_now)
619{
620 pacer->last_update = norm_time_now;
621 pacer->bucket = 0;
622}
623
Florin Corasd67f1122018-05-21 17:47:40 -0700624void
Florin Corasc44a5582018-11-01 16:30:54 -0700625transport_connection_tx_pacer_reset (transport_connection_t * tc,
626 u32 rate_bytes_per_sec,
627 u32 start_bucket, u64 time_now)
Florin Corasd67f1122018-05-21 17:47:40 -0700628{
Florin Corasd67f1122018-05-21 17:47:40 -0700629 spacer_t *pacer = &tc->pacer;
Florin Corasd67f1122018-05-21 17:47:40 -0700630 spacer_set_pace_rate (&tc->pacer, rate_bytes_per_sec);
631 pacer->last_update = time_now >> SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
Florin Corasc44a5582018-11-01 16:30:54 -0700632 pacer->bucket = start_bucket;
633}
634
635void
636transport_connection_tx_pacer_init (transport_connection_t * tc,
637 u32 rate_bytes_per_sec,
638 u32 initial_bucket)
639{
640 vlib_main_t *vm = vlib_get_main ();
641 tc->flags |= TRANSPORT_CONNECTION_F_IS_TX_PACED;
642 transport_connection_tx_pacer_reset (tc, rate_bytes_per_sec,
643 initial_bucket,
644 vm->clib_time.last_cpu_time);
Florin Corasd67f1122018-05-21 17:47:40 -0700645}
646
647void
648transport_connection_tx_pacer_update (transport_connection_t * tc,
649 u64 bytes_per_sec)
650{
Florin Corasd67f1122018-05-21 17:47:40 -0700651 spacer_set_pace_rate (&tc->pacer, bytes_per_sec);
Florin Corasd67f1122018-05-21 17:47:40 -0700652}
653
654u32
Florin Corase55a6d72018-10-31 23:09:22 -0700655transport_connection_tx_pacer_burst (transport_connection_t * tc,
656 u64 time_now)
657{
658 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
659 return spacer_max_burst (&tc->pacer, time_now);
660}
661
Florin Coras36ebcff2019-09-12 18:36:44 -0700662void
663transport_connection_tx_pacer_reset_bucket (transport_connection_t * tc,
664 u64 time_now)
665{
666 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
667 spacer_reset_bucket (&tc->pacer, time_now);
668}
669
Florin Corase55a6d72018-10-31 23:09:22 -0700670u32
671transport_connection_snd_space (transport_connection_t * tc, u64 time_now,
672 u16 mss)
Florin Corasd67f1122018-05-21 17:47:40 -0700673{
674 u32 snd_space, max_paced_burst;
Florin Corasd67f1122018-05-21 17:47:40 -0700675
676 snd_space = tp_vfts[tc->proto].send_space (tc);
677 if (transport_connection_is_tx_paced (tc))
678 {
679 time_now >>= SPACER_CPU_TICKS_PER_PERIOD_SHIFT;
680 max_paced_burst = spacer_max_burst (&tc->pacer, time_now);
Simon Zhang1146ff42019-09-02 22:54:00 +0800681 max_paced_burst =
682 (max_paced_burst < TRANSPORT_PACER_MIN_BURST) ? 0 : max_paced_burst;
Florin Corasd67f1122018-05-21 17:47:40 -0700683 snd_space = clib_min (snd_space, max_paced_burst);
Simon Zhang1146ff42019-09-02 22:54:00 +0800684 return snd_space >= mss ? snd_space - snd_space % mss : snd_space;
Florin Corasd67f1122018-05-21 17:47:40 -0700685 }
686 return snd_space;
687}
688
Florin Coras52814732019-06-12 15:38:19 -0700689u64
690transport_connection_tx_pacer_rate (transport_connection_t * tc)
691{
692 return spacer_pace_rate (&tc->pacer);
693}
694
Florin Corasd67f1122018-05-21 17:47:40 -0700695void
Florin Coras00482232019-08-02 12:52:00 -0700696transport_connection_update_tx_bytes (transport_connection_t * tc, u32 bytes)
Florin Corasd67f1122018-05-21 17:47:40 -0700697{
Florin Corasd67f1122018-05-21 17:47:40 -0700698 if (transport_connection_is_tx_paced (tc))
699 spacer_update_bucket (&tc->pacer, bytes);
700}
701
702void
Florin Corase55a6d72018-10-31 23:09:22 -0700703transport_connection_tx_pacer_update_bytes (transport_connection_t * tc,
704 u32 bytes)
705{
706 spacer_update_bucket (&tc->pacer, bytes);
707}
708
709void
Florin Corasd67f1122018-05-21 17:47:40 -0700710transport_init_tx_pacers_period (void)
711{
712 f64 cpu_freq = os_cpu_clock_frequency ();
713 transport_pacer_period = cpu_freq / SPACER_CPU_TICKS_PER_PERIOD;
714}
715
Florin Coras3cbc04b2017-10-02 00:18:51 -0700716void
Florin Coras561af9b2017-12-09 10:19:43 -0800717transport_update_time (f64 time_now, u8 thread_index)
718{
719 transport_proto_vft_t *vft;
720 vec_foreach (vft, tp_vfts)
721 {
722 if (vft->update_time)
723 (vft->update_time) (time_now, thread_index);
724 }
725}
726
727void
728transport_enable_disable (vlib_main_t * vm, u8 is_en)
729{
730 transport_proto_vft_t *vft;
731 vec_foreach (vft, tp_vfts)
732 {
733 if (vft->enable)
734 (vft->enable) (vm, is_en);
735 }
736}
737
738void
Florin Coras3cbc04b2017-10-02 00:18:51 -0700739transport_init (void)
740{
741 vlib_thread_main_t *vtm = vlib_get_thread_main ();
Florin Coras31c99552019-03-01 13:00:58 -0800742 session_main_t *smm = vnet_get_session_main ();
Florin Coras3cbc04b2017-10-02 00:18:51 -0700743 u32 num_threads;
744
Florin Coras93e65802017-11-29 00:07:11 -0500745 if (smm->local_endpoints_table_buckets == 0)
746 smm->local_endpoints_table_buckets = 250000;
747 if (smm->local_endpoints_table_memory == 0)
748 smm->local_endpoints_table_memory = 512 << 20;
749
Florin Coras3cbc04b2017-10-02 00:18:51 -0700750 /* Initialize [port-allocator] random number seed */
751 port_allocator_seed = (u32) clib_cpu_time_now ();
752
753 clib_bihash_init_24_8 (&local_endpoints_table, "local endpoints table",
Florin Coras93e65802017-11-29 00:07:11 -0500754 smm->local_endpoints_table_buckets,
755 smm->local_endpoints_table_memory);
Florin Coras3cbc04b2017-10-02 00:18:51 -0700756 num_threads = 1 /* main thread */ + vtm->n_threads;
757 if (num_threads > 1)
758 clib_spinlock_init (&local_endpoints_lock);
759}
760
761/*
762 * fd.io coding-style-patch-verification: ON
763 *
764 * Local Variables:
765 * eval: (c-set-style "gnu")
766 * End:
767 */