1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
|
/*
* Copyright (c) 2015 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _included_clib_mem_h
#define _included_clib_mem_h
#include <stdarg.h>
#include <unistd.h>
#include <sys/mman.h>
#include <vppinfra/clib.h> /* uword, etc */
#include <vppinfra/clib_error.h>
#include <vppinfra/dlmalloc.h>
#include <vppinfra/os.h>
#include <vppinfra/string.h> /* memcpy, clib_memset */
#include <vppinfra/sanitizer.h>
#define CLIB_MAX_MHEAPS 256
#define CLIB_MAX_NUMAS 8
/* Unspecified NUMA socket */
#define VEC_NUMA_UNSPECIFIED (0xFF)
/* Per CPU heaps. */
extern void *clib_per_cpu_mheaps[CLIB_MAX_MHEAPS];
extern void *clib_per_numa_mheaps[CLIB_MAX_NUMAS];
always_inline void *
clib_mem_get_per_cpu_heap (void)
{
int cpu = os_get_thread_index ();
return clib_per_cpu_mheaps[cpu];
}
always_inline void *
clib_mem_set_per_cpu_heap (u8 * new_heap)
{
int cpu = os_get_thread_index ();
void *old = clib_per_cpu_mheaps[cpu];
clib_per_cpu_mheaps[cpu] = new_heap;
return old;
}
always_inline void *
clib_mem_get_per_numa_heap (u32 numa_id)
{
ASSERT (numa_id < ARRAY_LEN (clib_per_numa_mheaps));
return clib_per_numa_mheaps[numa_id];
}
always_inline void *
clib_mem_set_per_numa_heap (u8 * new_heap)
{
int numa = os_get_numa_index ();
void *old = clib_per_numa_mheaps[numa];
clib_per_numa_mheaps[numa] = new_heap;
return old;
}
always_inline void
clib_mem_set_thread_index (void)
{
/*
* Find an unused slot in the per-cpu-mheaps array,
* and grab it for this thread. We need to be able to
* push/pop the thread heap without affecting other thread(s).
*/
int i;
if (__os_thread_index != 0)
return;
for (i = 0; i < ARRAY_LEN (clib_per_cpu_mheaps); i++)
if (clib_atomic_bool_cmp_and_swap (&clib_per_cpu_mheaps[i],
0, clib_per_cpu_mheaps[0]))
{
os_set_thread_index (i);
break;
}
ASSERT (__os_thread_index > 0);
}
always_inline uword
clib_mem_size_nocheck (void *p)
{
return mspace_usable_size_with_delta (p);
}
/* Memory allocator which may call os_out_of_memory() if it fails */
always_inline void *
clib_mem_alloc_aligned_at_offset (uword size, uword align, uword align_offset,
int os_out_of_memory_on_failure)
{
void *heap, *p;
uword cpu;
if (align_offset > align)
{
if (align > 0)
align_offset %= align;
else
align_offset = align;
}
cpu = os_get_thread_index ();
heap = clib_per_cpu_mheaps[cpu];
p = mspace_get_aligned (heap, size, align, align_offset);
if (PREDICT_FALSE (0 == p))
{
if (os_out_of_memory_on_failure)
os_out_of_memory ();
return 0;
}
CLIB_MEM_UNPOISON (p, size);
return p;
}
/* Memory allocator which calls os_out_of_memory() when it fails */
always_inline void *
clib_mem_alloc (uword size)
{
return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
/* align_offset */ 0,
/* os_out_of_memory */ 1);
}
always_inline void *
clib_mem_alloc_aligned (uword size, uword align)
{
return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
/* os_out_of_memory */ 1);
}
/* Memory allocator which calls os_out_of_memory() when it fails */
always_inline void *
clib_mem_alloc_or_null (uword size)
{
return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
/* align_offset */ 0,
/* os_out_of_memory */ 0);
}
always_inline void *
clib_mem_alloc_aligned_or_null (uword size, uword align)
{
return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
/* os_out_of_memory */ 0);
}
/* Memory allocator which panics when it fails.
Use macro so that clib_panic macro can expand __FUNCTION__ and __LINE__. */
#define clib_mem_alloc_aligned_no_fail(size,align) \
({ \
uword _clib_mem_alloc_size = (size); \
void * _clib_mem_alloc_p; \
_clib_mem_alloc_p = clib_mem_alloc_aligned (_clib_mem_alloc_size, (align)); \
if (! _clib_mem_alloc_p) \
clib_panic ("failed to allocate %d bytes", _clib_mem_alloc_size); \
_clib_mem_alloc_p; \
})
#define clib_mem_alloc_no_fail(size) clib_mem_alloc_aligned_no_fail(size,1)
/* Alias to stack allocator for naming consistency. */
#define clib_mem_alloc_stack(bytes) __builtin_alloca(bytes)
always_inline uword
clib_mem_is_heap_object (void *p)
{
void *heap = clib_mem_get_per_cpu_heap ();
return mspace_is_heap_object (heap, p);
}
always_inline void
clib_mem_free (void *p)
{
u8 *heap = clib_mem_get_per_cpu_heap ();
/* Make sure object is in the correct heap. */
ASSERT (clib_mem_is_heap_object (p));
CLIB_MEM_POISON (p, clib_mem_size_nocheck (p));
mspace_put (heap, p);
}
always_inline void *
clib_mem_realloc (void *p, uword new_size, uword old_size)
{
/* By default use alloc, copy and free to emulate realloc. */
void *q = clib_mem_alloc (new_size);
if (q)
{
uword copy_size;
if (old_size < new_size)
copy_size = old_size;
else
copy_size = new_size;
clib_memcpy_fast (q, p, copy_size);
clib_mem_free (p);
}
return q;
}
always_inline uword
clib_mem_size (void *p)
{
ASSERT (clib_mem_is_heap_object (p));
return clib_mem_size_nocheck (p);
}
always_inline void
clib_mem_free_s (void *p)
{
uword size = clib_mem_size (p);
CLIB_MEM_UNPOISON (p, size);
memset_s_inline (p, size, 0, size);
clib_mem_free (p);
}
always_inline void *
clib_mem_get_heap (void)
{
return clib_mem_get_per_cpu_heap ();
}
always_inline void *
clib_mem_set_heap (void *heap)
{
return clib_mem_set_per_cpu_heap (heap);
}
void *clib_mem_init (void *heap, uword size);
void *clib_mem_init_thread_safe (void *memory, uword memory_size);
void *clib_mem_init_thread_safe_numa (void *memory, uword memory_size,
u8 numa);
void clib_mem_exit (void);
uword clib_mem_get_page_size (void);
void clib_mem_validate (void);
void clib_mem_trace (int enable);
int clib_mem_is_traced (void);
typedef struct
{
/* Total number of objects allocated. */
uword object_count;
/* Total allocated bytes. Bytes used and free.
used + free = total */
uword bytes_total, bytes_used, bytes_free;
/* Number of bytes used by mheap data structure overhead
(e.g. free lists, mheap header). */
uword bytes_overhead;
/* Amount of free space returned to operating system. */
uword bytes_free_reclaimed;
/* For malloc which puts small objects in sbrk region and
large objects in mmap'ed regions. */
uword bytes_used_sbrk;
uword bytes_used_mmap;
/* Max. number of bytes in this heap. */
uword bytes_max;
} clib_mem_usage_t;
void clib_mem_usage (clib_mem_usage_t * usage);
u8 *format_clib_mem_usage (u8 * s, va_list * args);
/* Allocate virtual address space. */
always_inline void *
clib_mem_vm_alloc (uword size)
{
void *mmap_addr;
uword flags = MAP_PRIVATE;
#ifdef MAP_ANONYMOUS
flags |= MAP_ANONYMOUS;
#endif
mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
if (mmap_addr == (void *) -1)
mmap_addr = 0;
else
CLIB_MEM_UNPOISON (mmap_addr, size);
return mmap_addr;
}
always_inline void
clib_mem_vm_free (void *addr, uword size)
{
munmap (addr, size);
}
always_inline void *
clib_mem_vm_unmap (void *addr, uword size)
{
void *mmap_addr;
uword flags = MAP_PRIVATE | MAP_FIXED;
/* To unmap we "map" with no protection. If we actually called
munmap then other callers could steal the address space. By
changing to PROT_NONE the kernel can free up the pages which is
really what we want "unmap" to mean. */
mmap_addr = mmap (addr, size, PROT_NONE, flags, -1, 0);
if (mmap_addr == (void *) -1)
mmap_addr = 0;
else
CLIB_MEM_UNPOISON (mmap_addr, size);
return mmap_addr;
}
always_inline void *
clib_mem_vm_map (void *addr, uword size)
{
void *mmap_addr;
uword flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS/*
* Copyright (c) 2018-2019 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/session/application_interface.h>
#include <vppinfra/lock.h>
#include <vnet/tls/tls.h>
static tls_main_t tls_main;
static tls_engine_vft_t *tls_vfts;
#define TLS_INVALID_HANDLE ~0
#define TLS_IDX_MASK 0x00FFFFFF
#define TLS_ENGINE_TYPE_SHIFT 28
void tls_disconnect (u32 ctx_handle, u32 thread_index);
void
tls_disconnect_transport (tls_ctx_t * ctx)
{
vnet_disconnect_args_t a = {
.handle = ctx->tls_session_handle,
.app_index = tls_main.app_index,
};
if (vnet_disconnect_session (&a))
clib_warning ("disconnect returned");
}
crypto_engine_type_t
tls_get_available_engine (void)
{
int i;
for (i = 0; i < vec_len (tls_vfts); i++)
{
if (tls_vfts[i].ctx_alloc)
return i;
}
return CRYPTO_ENGINE_NONE;
}
int
tls_add_vpp_q_rx_evt (session_t * s)
{
if (svm_fifo_set_event (s->rx_fifo))
session_send_io_evt_to_thread (s->rx_fifo, SESSION_IO_EVT_RX);
return 0;
}
int
tls_add_vpp_q_builtin_rx_evt (session_t * s)
{
session_enqueue_notify (s);
return 0;
}
int
tls_add_vpp_q_tx_evt (session_t * s)
{
if (svm_fifo_set_event (s->tx_fifo))
session_send_io_evt_to_thread (s->tx_fifo, SESSION_IO_EVT_TX);
return 0;
}
static inline int
tls_add_app_q_evt (app_worker_t *app_wrk, session_t *app_session)
{
app_worker_add_event (app_wrk, app_session, SESSION_IO_EVT_RX);
return 0;
}
u32
tls_listener_ctx_alloc (void)
{
tls_main_t *tm = &tls_main;
tls_ctx_t *ctx;
pool_get (tm->listener_ctx_pool, ctx);
clib_memset (ctx, 0, sizeof (*ctx));
return ctx - tm->listener_ctx_pool;
}
void
tls_listener_ctx_free (tls_ctx_t * ctx)
{
if (CLIB_DEBUG)
memset (ctx, 0xfb, sizeof (*ctx));
pool_put (tls_main.listener_ctx_pool, ctx);
}
tls_ctx_t *
tls_listener_ctx_get (u32 ctx_index)
{
return pool_elt_at_index (tls_main.listener_ctx_pool, ctx_index);
}
u32
tls_listener_ctx_index (tls_ctx_t * ctx)
{
return (ctx - tls_main.listener_ctx_pool);
}
u32
tls_ctx_half_open_alloc (void)
{
tls_main_t *tm = &tls_main;
tls_ctx_t *ctx;
pool_get_aligned_safe (tm->half_open_ctx_pool, ctx, CLIB_CACHE_LINE_BYTES);
clib_memset (ctx, 0, sizeof (*ctx));
ctx->c_c_index = ctx - tm->half_open_ctx_pool;
ctx->c_thread_index = transport_cl_thread ();
return ctx->c_c_index;
}
void
tls_ctx_half_open_free (u32 ho_index)
{
pool_put_index (tls_main.half_open_ctx_pool, ho_index);
}
tls_ctx_t *
tls_ctx_half_open_get (u32 ctx_index)
{
tls_main_t *tm = &tls_main;
return pool_elt_at_index (tm->half_open_ctx_pool, ctx_index);
}
void
tls_notify_app_enqueue (tls_ctx_t * ctx, session_t * app_session)
{
app_worker_t *app_wrk;
app_wrk = app_worker_get_if_valid (app_session->app_wrk_index);
if (PREDICT_TRUE (app_wrk != 0))
tls_add_app_q_evt (app_wrk, app_session);
}
int
tls_notify_app_accept (tls_ctx_t * ctx)
{
session_t *app_listener, *app_session;
app_worker_t *app_wrk;
tls_ctx_t *lctx;
int rv;
lctx = tls_listener_ctx_get (ctx->listener_ctx_index);
app_listener = listen_session_get_from_handle (lctx->app_session_handle);
app_session = session_get (ctx->c_s_index, ctx->c_thread_index);
app_session->app_wrk_index = ctx->parent_app_wrk_index;
app_session->connection_index = ctx->tls_ctx_handle;
app_session->session_type = app_listener->session_type;
app_session->listener_handle = listen_session_get_handle (app_listener);
app_session->session_state = SESSION_STATE_ACCEPTING;
if ((rv = app_worker_init_accepted (app_session)))
{
TLS_DBG (1, "failed to allocate fifos");
session_free (app_session);
return rv;
}
ctx->app_session_handle = session_handle (app_session);
ctx->parent_app_wrk_index = app_session->app_wrk_index;
app_wrk = app_worker_get (app_session->app_wrk_index);
return app_worker_accept_notify (app_wrk, app_session);
}
int
tls_notify_app_connected (tls_ctx_t * ctx, session_error_t err)
{
u32 parent_app_api_ctx;
session_t *app_session;
app_worker_t *app_wrk;
app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_index);
if (!app_wrk)
{
if (ctx->tls_type == TRANSPORT_PROTO_TLS)
session_free (session_get (ctx->c_s_index, ctx->c_thread_index));
ctx->no_app_session = 1;
return -1;
}
if (err)
{
/* Free app session pre-allocated when transport was established */
if (ctx->tls_type == TRANSPORT_PROTO_TLS)
session_free (session_get (ctx->c_s_index, ctx->c_thread_index));
ctx->no_app_session = 1;
goto send_reply;
}
/* For DTLS the app session is not preallocated because the underlying udp
* session might migrate to a different worker during the handshake */
if (ctx->tls_type == TRANSPORT_PROTO_DTLS)
{
session_type_t st;
/* Cleanup half-open session as we don't get notification from udp */
session_half_open_delete_notify (&ctx->connection);
app_session = session_alloc (ctx->c_thread_index);
app_session->session_state = SESSION_STATE_CREATED;
ctx->c_s_index = app_session->session_index;
st =
session_type_from_proto_and_ip (TRANSPORT_PROTO_DTLS, ctx->tcp_is_ip4);
app_session->session_type = st;
app_session->connection_index = ctx->tls_ctx_handle;
}
else
{
app_session = session_get (ctx->c_s_index, ctx->c_thread_index);
}
app_session->app_wrk_index = ctx->parent_app_wrk_index;
if ((err = app_worker_init_connected (app_wrk, app_session)))
goto failed;
app_session->session_state = SESSION_STATE_READY;
parent_app_api_ctx = ctx->parent_app_api_context;
ctx->app_session_handle = session_handle (app_session);
if (app_worker_connect_notify (app_wrk, app_session, SESSION_E_NONE,
parent_app_api_ctx))
{
TLS_DBG (1, "failed to notify app");
session_free (session_get (ctx->c_s_index, ctx->c_thread_index));
ctx->no_app_session = 1;
return -1;
}
return 0;
failed:
ctx->no_app_session = 1;
tls_disconnect (ctx->tls_ctx_handle, vlib_get_thread_index ());
send_reply:
return app_worker_connect_notify (app_wrk, 0, err,
ctx->parent_app_api_context);
}
static inline void
tls_ctx_parse_handle (u32 ctx_handle, u32 * ctx_index, u32 * engine_type)
{
*ctx_index = ctx_handle & TLS_IDX_MASK;
*engine_type = ctx_handle >> TLS_ENGINE_TYPE_SHIFT;
}
static inline crypto_engine_type_t
tls_get_engine_type (crypto_engine_type_t requested,
crypto_engine_type_t preferred)
{
if (requested != CRYPTO_ENGINE_NONE)
{
if (tls_vfts[requested].ctx_alloc)
return requested;
return CRYPTO_ENGINE_NONE;
}
if (!tls_vfts[preferred].ctx_alloc)
return tls_get_available_engine ();
return preferred;
}
static inline u32
tls_ctx_alloc (crypto_engine_type_t engine_type)
{
u32 ctx_index;
ctx_index = tls_vfts[engine_type].ctx_alloc ();
return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
}
static inline u32
tls_ctx_alloc_w_thread (crypto_engine_type_t engine_type, u32 thread_index)
{
u32 ctx_index;
ctx_index = tls_vfts[engine_type].ctx_alloc_w_thread (thread_index);
return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
}
static inline u32
tls_ctx_attach (crypto_engine_type_t engine_type, u32 thread_index, void *ctx)
{
u32 ctx_index;
ctx_index = tls_vfts[engine_type].ctx_attach (thread_index, ctx);
return (((u32) engine_type << TLS_ENGINE_TYPE_SHIFT) | ctx_index);
}
static inline void *
tls_ctx_detach (tls_ctx_t *ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_detach (ctx);
}
static inline tls_ctx_t *
tls_ctx_get (u32 ctx_handle)
{
u32 ctx_index, engine_type;
tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
return tls_vfts[engine_type].ctx_get (ctx_index);
}
static inline tls_ctx_t *
tls_ctx_get_w_thread (u32 ctx_handle, u8 thread_index)
{
u32 ctx_index, engine_type;
tls_ctx_parse_handle (ctx_handle, &ctx_index, &engine_type);
return tls_vfts[engine_type].ctx_get_w_thread (ctx_index, thread_index);
}
static inline int
tls_ctx_init_server (tls_ctx_t * ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_init_server (ctx);
}
static inline int
tls_ctx_init_client (tls_ctx_t * ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_init_client (ctx);
}
static inline int
tls_ctx_write (tls_ctx_t * ctx, session_t * app_session,
transport_send_params_t * sp)
{
u32 n_wrote;
sp->max_burst_size = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
n_wrote = tls_vfts[ctx->tls_ctx_engine].ctx_write (ctx, app_session, sp);
sp->bytes_dequeued = n_wrote;
return n_wrote > 0 ? clib_max (n_wrote / TRANSPORT_PACER_MIN_MSS, 1) : 0;
}
static inline int
tls_ctx_read (tls_ctx_t * ctx, session_t * tls_session)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_read (ctx, tls_session);
}
static inline int
tls_ctx_transport_close (tls_ctx_t * ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_transport_close (ctx);
}
static inline int
tls_ctx_app_close (tls_ctx_t * ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_app_close (ctx);
}
void
tls_ctx_free (tls_ctx_t * ctx)
{
tls_vfts[ctx->tls_ctx_engine].ctx_free (ctx);
}
u8
tls_ctx_handshake_is_over (tls_ctx_t * ctx)
{
return tls_vfts[ctx->tls_ctx_engine].ctx_handshake_is_over (ctx);
}
int
tls_reinit_ca_chain (crypto_engine_type_t tls_engine_id)
{
return tls_vfts[tls_engine_id].ctx_reinit_cachain ();
}
void
tls_notify_app_io_error (tls_ctx_t *ctx)
{
ASSERT (tls_ctx_handshake_is_over (ctx));
session_transport_reset_notify (&ctx->connection);
session_transport_closed_notify (&ctx->connection);
tls_disconnect_transport (ctx);
}
void
tls_session_reset_callback (session_t * s)
{
tls_ctx_t *ctx;
transport_connection_t *tc;
session_t *app_session;
ctx = tls_ctx_get (s->opaque);
ctx->is_passive_close = 1;
tc = &ctx->connection;
if (tls_ctx_handshake_is_over (ctx))
{
session_transport_reset_notify (tc);
session_transport_closed_notify (tc);
tls_disconnect_transport (ctx);
}
else
if ((app_session =
session_get_if_valid (ctx->c_s_index, ctx->c_thread_index)))
{
session_free (app_session);
ctx->c_s_index = SESSION_INVALID_INDEX;
tls_disconnect_transport (ctx);
}
}
static void
tls_session_cleanup_ho (session_t *s)
{
tls_ctx_t *ctx;
u32 ho_index;
/* session opaque stores the opaque passed on connect */
ho_index = s->opaque;
ctx = tls_ctx_half_open_get (ho_index);
session_half_open_delete_notify (&ctx->connection);
tls_ctx_half_open_free (ho_index);
}
int
tls_add_segment_callback (u32 client_index, u64 segment_handle)
{
/* No-op for builtin */
return 0;
}
int
tls_del_segment_callback (u32 client_index, u64 segment_handle)
{
return 0;
}
void
tls_session_disconnect_callback (session_t * tls_session)
{
tls_ctx_t *ctx;
TLS_DBG (1, "TCP disconnecting handle %x session %u", tls_session->opaque,
tls_session->session_index);
ASSERT (tls_session->thread_index == vlib_get_thread_index ()
|| vlib_thread_is_main_w_barrier ());
ctx = tls_ctx_get_w_thread (tls_session->opaque, tls_session->thread_index);
ctx->is_passive_close = 1;
tls_ctx_transport_close (ctx);
}
int
tls_session_accept_callback (session_t * tls_session)
{
session_t *tls_listener, *app_session;
tls_ctx_t *lctx, *ctx;
u32 ctx_handle;
int rv;
tls_listener =
listen_session_get_from_handle (tls_session->listener_handle);
lctx = tls_listener_ctx_get (tls_listener->opaque);
ctx_handle = tls_ctx_alloc (lctx->tls_ctx_engine);
ctx = tls_ctx_get (ctx_handle);
memcpy (ctx, lctx, sizeof (*lctx));
ctx->c_thread_index = vlib_get_thread_index ();
ctx->tls_ctx_handle = ctx_handle;
tls_session->session_state = SESSION_STATE_READY;
tls_session->opaque = ctx_handle;
ctx->tls_session_handle = session_handle (tls_session);
ctx->listener_ctx_index = tls_listener->opaque;
ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
ctx->ckpair_index = lctx->ckpair_index;
/* Preallocate app session. Avoids allocating a session post handshake
* on tls_session rx and potentially invalidating the session pool */
app_session = session_alloc (ctx->c_thread_index);
app_session->session_state = SESSION_STATE_CREATED;
ctx->c_s_index = app_session->session_index;
TLS_DBG (1, "Accept on listener %u new connection [%u]%x",
tls_listener->opaque, vlib_get_thread_index (), ctx_handle);
rv = tls_ctx_init_server (ctx);
if (rv)
{
session_free (app_session);
tls_ctx_free (ctx);
}
return rv;
}
int
tls_app_rx_callback (session_t * tls_session)
{
tls_ctx_t *ctx;
/* DTLS session migrating, wait for next notification */
if (PREDICT_FALSE (tls_session->flags & SESSION_F_IS_MIGRATING))
return 0;
ctx = tls_ctx_get (tls_session->opaque);
if (PREDICT_FALSE (ctx->no_app_session))
{
TLS_DBG (1, "Local App closed");
return 0;
}
tls_ctx_read (ctx, tls_session);
return 0;
}
int
tls_app_tx_callback (session_t * tls_session)
{
tls_ctx_t *ctx;
ctx = tls_ctx_get (tls_session->opaque);
transport_connection_reschedule (&ctx->connection);
return 0;
}
int
tls_session_connected_cb (u32 tls_app_index, u32 ho_ctx_index,
session_t *tls_session, session_error_t err)
{
session_t *app_session;
tls_ctx_t *ho_ctx, *ctx;
session_type_t st;
u32 ctx_handle;
int rv;
ho_ctx = tls_ctx_half_open_get (ho_ctx_index);
ctx_handle = tls_ctx_alloc (ho_ctx->tls_ctx_engine);
ctx = tls_ctx_get (ctx_handle);
clib_memcpy_fast (ctx, ho_ctx, sizeof (*ctx));
/* Half-open freed on tcp half-open cleanup notification */
ctx->c_thread_index = vlib_get_thread_index ();
ctx->tls_ctx_handle = ctx_handle;
ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
TLS_DBG (1, "TCP connect for %u returned %u. New connection [%u]%x",
ho_ctx_index, err, vlib_get_thread_index (),
(ctx) ? ctx_handle : ~0);
ctx->tls_session_handle = session_handle (tls_session);
tls_session->opaque = ctx_handle;
tls_session->session_state = SESSION_STATE_READY;
/* Preallocate app session. Avoids allocating a session post handshake
* on tls_session rx and potentially invalidating the session pool */
app_session = session_alloc (ctx->c_thread_index);
app_session->session_state = SESSION_STATE_CREATED;
ctx->c_s_index = app_session->session_index;
st = session_type_from_proto_and_ip (TRANSPORT_PROTO_TLS, ctx->tcp_is_ip4);
app_session->session_type = st;
app_session->connection_index = ctx->tls_ctx_handle;
rv = tls_ctx_init_client (ctx);
if (rv)
{
session_free (app_session);
tls_ctx_free (ctx);
}
return rv;
}
int
dtls_session_connected_cb (u32 app_wrk_index, u32 ctx_handle, session_t *us,
session_error_t err)
{
tls_ctx_t *ctx;
ctx = tls_ctx_get_w_thread (ctx_handle, transport_cl_thread ());
ctx->tls_session_handle = session_handle (us);
ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
us->opaque = ctx_handle;
/* We don't preallocate the app session because the udp session might
* actually migrate to a different worker at the end of the handshake */
return tls_ctx_init_client (ctx);
}
int
tls_session_connected_callback (u32 tls_app_index, u32 ho_ctx_index,
session_t *tls_session, session_error_t err)
{
if (err)
{
app_worker_t *app_wrk;
tls_ctx_t *ho_ctx;
u32 api_context;
ho_ctx = tls_ctx_half_open_get (ho_ctx_index);
app_wrk = app_worker_get_if_valid (ho_ctx->parent_app_wrk_index);
if (app_wrk)
{
api_context = ho_ctx->parent_app_api_context;
app_worker_connect_notify (app_wrk, 0, err, api_context);
}
return 0;
}
if (session_get_transport_proto (tls_session) == TRANSPORT_PROTO_TCP)
return tls_session_connected_cb (tls_app_index, ho_ctx_index, tls_session,
err);
else
return dtls_session_connected_cb (tls_app_index, ho_ctx_index, tls_session,
err);
}
static void
tls_app_session_cleanup (session_t * s, session_cleanup_ntf_t ntf)
{
tls_ctx_t *ctx;
if (ntf == SESSION_CLEANUP_TRANSPORT)
{
/* Allow cleanup of tcp session */
if (s->session_state == SESSION_STATE_TRANSPORT_DELETED)
session_close (s);
return;
}
ctx = tls_ctx_get (s->opaque);
if (!ctx->no_app_session)
session_transport_delete_notify (&ctx->connection);
tls_ctx_free (ctx);
}
static void
dtls_migrate_ctx (void *arg)
{
tls_ctx_t *ctx = (tls_ctx_t *) arg;
u32 ctx_handle, thread_index;
session_t *us;
thread_index = session_thread_from_handle (ctx->tls_session_handle);
ASSERT (thread_index == vlib_get_thread_index ());
ctx_handle = tls_ctx_attach (ctx->tls_ctx_engine, thread_index, ctx);
ctx = tls_ctx_get_w_thread (ctx_handle, thread_index);
ctx->tls_ctx_handle = ctx_handle;
us = session_get_from_handle (ctx->tls_session_handle);
us->opaque = ctx_handle;
us->flags &= ~SESSION_F_IS_MIGRATING;
/* Probably the app detached while the session was migrating. Cleanup */
if (session_half_open_migrated_notify (&ctx->connection))
{
ctx->no_app_session = 1;
tls_disconnect (ctx->tls_ctx_handle, vlib_get_thread_index ());
return;
}
if (svm_fifo_max_dequeue (us->tx_fifo))
session_send_io_evt_to_thread (us->tx_fifo, SESSION_IO_EVT_TX);
}
static void
dtls_session_migrate_callback (session_t *us, session_handle_t new_sh)
{
u32 new_thread = session_thread_from_handle (new_sh);
tls_ctx_t *ctx, *cloned_ctx;
/* Migrate dtls context to new thread */
ctx = tls_ctx_get_w_thread (us->opaque, us->thread_index);
ctx->tls_session_handle = new_sh;
cloned_ctx = tls_ctx_detach (ctx);
ctx->is_migrated = 1;
session_half_open_migrate_notify (&ctx->connection);
session_send_rpc_evt_to_thread (new_thread, dtls_migrate_ctx,
(void *) cloned_ctx);
tls_ctx_free (ctx);
}
static session_cb_vft_t tls_app_cb_vft = {
.session_accept_callback = tls_session_accept_callback,
.session_disconnect_callback = tls_session_disconnect_callback,
.session_connected_callback = tls_session_connected_callback,
.session_reset_callback = tls_session_reset_callback,
.half_open_cleanup_callback = tls_session_cleanup_ho,
.add_segment_callback = tls_add_segment_callback,
.del_segment_callback = tls_del_segment_callback,
.builtin_app_rx_callback = tls_app_rx_callback,
.builtin_app_tx_callback = tls_app_tx_callback,
.session_migrate_callback = dtls_session_migrate_callback,
.session_cleanup_callback = tls_app_session_cleanup,
};
int
tls_connect (transport_endpoint_cfg_t * tep)
{
vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
transport_endpt_crypto_cfg_t *ccfg;
crypto_engine_type_t engine_type;
session_endpoint_cfg_t *sep;
tls_main_t *tm = &tls_main;
app_worker_t *app_wrk;
application_t *app;
tls_ctx_t *ctx;
u32 ctx_index;
int rv;
sep = (session_endpoint_cfg_t *) tep;
if (!sep->ext_cfg)
return SESSION_E_NOEXTCFG;
app_wrk = app_worker_get (sep->app_wrk_index);
app = application_get (app_wrk->app_index);
ccfg = &sep->ext_cfg->crypto;
engine_type = tls_get_engine_type (ccfg->crypto_engine, app->tls_engine);
if (engine_type == CRYPTO_ENGINE_NONE)
{
clib_warning ("No tls engine_type available");
return SESSION_E_NOCRYPTOENG;
}
ctx_index = tls_ctx_half_open_alloc ();
ctx = tls_ctx_half_open_get (ctx_index);
ctx->parent_app_wrk_index = sep->app_wrk_index;
ctx->parent_app_api_context = sep->opaque;
ctx->tcp_is_ip4 = sep->is_ip4;
ctx->tls_type = sep->transport_proto;
ctx->ckpair_index = ccfg->ckpair_index;
ctx->c_proto = TRANSPORT_PROTO_TLS;
ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
if (ccfg->hostname[0])
{
ctx->srv_hostname = format (0, "%s", ccfg->hostname);
vec_terminate_c_string (ctx->srv_hostname);
}
ctx->tls_ctx_engine = engine_type;
clib_memcpy_fast (&cargs->sep, sep, sizeof (session_endpoint_t));
cargs->sep.transport_proto = TRANSPORT_PROTO_TCP;
cargs->app_index = tm->app_index;
cargs->api_context = ctx_index;
cargs->sep_ext.ns_index = app->ns_index;
if ((rv = vnet_connect (cargs)))
return rv;
/* Track half-open tcp session in case we need to clean it up */
ctx->tls_session_handle = cargs->sh;
TLS_DBG (1, "New connect request %u engine %d", ctx_index, engine_type);
return ctx_index;
}
void
tls_disconnect (u32 ctx_handle, u32 thread_index)
{
tls_ctx_t *ctx;
TLS_DBG (1, "Disconnecting %x", ctx_handle);
ctx = tls_ctx_get (ctx_handle);
tls_ctx_app_close (ctx);
}
u32
tls_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
{
vnet_listen_args_t _bargs, *args = &_bargs;
transport_endpt_crypto_cfg_t *ccfg;
app_worker_t *app_wrk;
tls_main_t *tm = &tls_main;
session_handle_t tls_al_handle;
session_endpoint_cfg_t *sep;
session_t *tls_listener;
session_t *app_listener;
crypto_engine_type_t engine_type;
application_t *app;
app_listener_t *al;
tls_ctx_t *lctx;
u32 lctx_index;
int rv;
sep = (session_endpoint_cfg_t *) tep;
if (!sep->ext_cfg)
return SESSION_E_NOEXTCFG;
app_wrk = app_worker_get (sep->app_wrk_index);
app = application_get (app_wrk->app_index);
ccfg = &sep->ext_cfg->crypto;
engine_type = tls_get_engine_type (ccfg->crypto_engine, app->tls_engine);
if (engine_type == CRYPTO_ENGINE_NONE)
{
clib_warning ("No tls engine_type available");
return SESSION_E_NOCRYPTOENG;
}
clib_memset (args, 0, sizeof (*args));
args->app_index = tm->app_index;
args->sep_ext = *sep;
args->sep_ext.ns_index = app->ns_index;
args->sep_ext.transport_proto = TRANSPORT_PROTO_TCP;
if (sep->transport_proto == TRANSPORT_PROTO_DTLS)
{
args->sep_ext.transport_proto = TRANSPORT_PROTO_UDP;
args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
}
if ((rv = vnet_listen (args)))
return rv;
lctx_index = tls_listener_ctx_alloc ();
tls_al_handle = args->handle;
al = app_listener_get_w_handle (tls_al_handle);
tls_listener = app_listener_get_session (al);
tls_listener->opaque = lctx_index;
app_listener = listen_session_get (app_listener_index);
lctx = tls_listener_ctx_get (lctx_index);
lctx->parent_app_wrk_index = sep->app_wrk_index;
lctx->tls_session_handle = tls_al_handle;
lctx->app_session_handle = listen_session_get_handle (app_listener);
lctx->tcp_is_ip4 = sep->is_ip4;
lctx->tls_ctx_engine = engine_type;
lctx->tls_type = sep->transport_proto;
lctx->ckpair_index = ccfg->ckpair_index;
lctx->c_s_index = app_listener_index;
lctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
if (tls_vfts[engine_type].ctx_start_listen (lctx))
{
vnet_unlisten_args_t a = {
.handle = lctx->tls_session_handle,
.app_index = tls_main.app_index,
.wrk_map_index = 0
};
if ((vnet_unlisten (&a)))
clib_warning ("unlisten returned");
tls_listener_ctx_free (lctx);
lctx_index = SESSION_INVALID_INDEX;
}
TLS_DBG (1, "Started listening %d, engine type %d", lctx_index,
engine_type);
return lctx_index;
}
u32
tls_stop_listen (u32 lctx_index)
{
session_endpoint_t sep = SESSION_ENDPOINT_NULL;
crypto_engine_type_t engine_type;
transport_connection_t *lc;
tls_ctx_t *lctx;
session_t *ls;
int rv;
lctx = tls_listener_ctx_get (lctx_index);
/* Cleanup listener from session lookup table */
ls = session_get_from_handle (lctx->tls_session_handle);
lc = session_get_transport (ls);
sep.fib_index = lc->fib_index;
sep.port = lc->lcl_port;
sep.is_ip4 = lc->is_ip4;
sep.transport_proto = lctx->tls_type;
clib_memcpy (&sep.ip, &lc->lcl_ip, sizeof (lc->lcl_ip));
session_lookup_del_session_endpoint2 (&sep);
vnet_unlisten_args_t a = {
.handle = lctx->tls_session_handle,
.app_index = tls_main.app_index,
.wrk_map_index = 0 /* default wrk */
};
if ((rv = vnet_unlisten (&a)))
clib_warning ("unlisten returned %d", rv);
engine_type = lctx->tls_ctx_engine;
tls_vfts[engine_type].ctx_stop_listen (lctx);
tls_listener_ctx_free (lctx);
return 0;
}
transport_connection_t *
tls_connection_get (u32 ctx_index, u32 thread_index)
{
tls_ctx_t *ctx;
ctx = tls_ctx_get_w_thread (ctx_index, thread_index);
return &ctx->connection;
}
transport_connection_t *
tls_listener_get (u32 listener_index)
{
tls_ctx_t *ctx;
ctx = tls_listener_ctx_get (listener_index);
return &ctx->connection;
}
static transport_connection_t *
tls_half_open_get (u32 ho_index)
{
tls_ctx_t *ctx;
ctx = tls_ctx_half_open_get (ho_index);
return &ctx->connection;
}
static void
tls_cleanup_ho (u32 ho_index)
{
tls_ctx_t *ctx;
ctx = tls_ctx_half_open_get (ho_index);
session_cleanup_half_open (ctx->tls_session_handle);
tls_ctx_half_open_free (ho_index);
}
int
tls_custom_tx_callback (void *session, transport_send_params_t * sp)
{
session_t *app_session = (session_t *) session;
tls_ctx_t *ctx;
if (PREDICT_FALSE (app_session->session_state
>= SESSION_STATE_TRANSPORT_CLOSED))
return 0;
ctx = tls_ctx_get (app_session->connection_index);
return tls_ctx_write (ctx, app_session, sp);
}
u8 *
format_tls_ctx (u8 * s, va_list * args)
{
u32 tcp_si, tcp_ti, ctx_index, ctx_engine;
tls_ctx_t *ctx = va_arg (*args, tls_ctx_t *);
char *proto;
proto = ctx->tls_type == TRANSPORT_PROTO_TLS ? "TLS" : "DTLS";
session_parse_handle (ctx->tls_session_handle, &tcp_si, &tcp_ti);
tls_ctx_parse_handle (ctx->tls_ctx_handle, &ctx_index, &ctx_engine);
s =
format (s, "[%d:%d][%s] app_wrk %u index %u engine %u ts %d:%d",
ctx->c_thread_index, ctx->c_s_index, proto,
ctx->parent_app_wrk_index, ctx_index, ctx_engine, tcp_ti, tcp_si);
return s;
}
static u8 *
format_tls_listener_ctx (u8 * s, va_list * args)
{
session_t *tls_listener;
app_listener_t *al;
tls_ctx_t *ctx;
char *proto;
ctx = va_arg (*args, tls_ctx_t *);
proto = ctx->tls_type == TRANSPORT_PROTO_TLS ? "TLS" : "DTLS";
al = app_listener_get_w_handle (ctx->tls_session_handle);
tls_listener = app_listener_get_session (al);
s = format (s, "[%d:%d][%s] app_wrk %u engine %u ts %d:%d",
ctx->c_thread_index, ctx->c_s_index, proto,
ctx->parent_app_wrk_index, ctx->tls_ctx_engine,
tls_listener->thread_index, tls_listener->session_index);
return s;
}
static u8 *
format_tls_ctx_state (u8 * s, va_list * args)
{
tls_ctx_t *ctx;
session_t *ts;
ctx = va_arg (*args, tls_ctx_t *);
ts = session_get (ctx->c_s_index, ctx->c_thread_index);
if (ts->session_state == SESSION_STATE_LISTENING)
s = format (s, "%s", "LISTEN");
else
{
if (ts->session_state >= SESSION_STATE_TRANSPORT_CLOSED)
s = format (s, "%s", "CLOSED");
else if (ts->session_state == SESSION_STATE_APP_CLOSED)
s = format (s, "%s", "APP-CLOSED");
else if (ts->session_state >= SESSION_STATE_TRANSPORT_CLOSING)
s = format (s, "%s", "CLOSING");
else if (tls_ctx_handshake_is_over (ctx))
s = format (s, "%s", "ESTABLISHED");
else
s = format (s, "%s", "HANDSHAKE");
}
return s;
}
u8 *
format_tls_connection (u8 * s, va_list * args)
{
u32 ctx_index = va_arg (*args, u32);
u32 thread_index = va_arg (*args, u32);
u32 verbose = va_arg (*args, u32);
tls_ctx_t *ctx;
ctx = tls_ctx_get_w_thread (ctx_index, thread_index);
if (!ctx)
return s;
s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_tls_ctx, ctx);
if (verbose)
{
s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_tls_ctx_state,
ctx);
if (verbose > 1)
s = format (s, "\n");
}
return s;
}
u8 *
format_tls_listener (u8 * s, va_list * args)
{
u32 tc_index = va_arg (*args, u32);
u32 __clib_unused thread_index = va_arg (*args, u32);
u32 verbose = va_arg (*args, u32);
tls_ctx_t *ctx = tls_listener_ctx_get (tc_index);
s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_tls_listener_ctx, ctx);
if (verbose)
s = format (s, "%-" SESSION_CLI_STATE_LEN "U", format_tls_ctx_state, ctx);
return s;
}
u8 *
format_tls_half_open (u8 * s, va_list * args)
{
u32 ho_index = va_arg (*args, u32);
u32 __clib_unused thread_index = va_arg (*args, u32);
session_t *tcp_ho;
tls_ctx_t *ho_ctx;
ho_ctx = tls_ctx_half_open_get (ho_index);
tcp_ho = session_get_from_handle (ho_ctx->tls_session_handle);
s = format (s, "[%d:%d][%s] half-open app_wrk %u engine %u ts %d:%d",
ho_ctx->c_thread_index, ho_ctx->c_s_index, "TLS",
ho_ctx->parent_app_wrk_index, ho_ctx->tls_ctx_engine,
tcp_ho->thread_index, tcp_ho->session_index);
return s;
}
static void
tls_transport_endpoint_get (u32 ctx_handle, u32 thread_index,
transport_endpoint_t * tep, u8 is_lcl)
{
tls_ctx_t *ctx = tls_ctx_get_w_thread (ctx_handle, thread_index);
session_t *tcp_session;
tcp_session = session_get_from_handle (ctx->tls_session_handle);
session_get_endpoint (tcp_session, tep, is_lcl);
}
static void
tls_transport_listener_endpoint_get (u32 ctx_handle,
transport_endpoint_t * tep, u8 is_lcl)
{
session_t *tls_listener;
app_listener_t *al;
tls_ctx_t *ctx = tls_listener_ctx_get (ctx_handle);
al = app_listener_get_w_handle (ctx->tls_session_handle);
tls_listener = app_listener_get_session (al);
session_get_endpoint (tls_listener, tep, is_lcl);
}
static clib_error_t *
tls_enable (vlib_main_t * vm, u8 is_en)
{
vnet_app_detach_args_t _da, *da = &_da;
vnet_app_attach_args_t _a, *a = &_a;
u64 options[APP_OPTIONS_N_OPTIONS];
tls_main_t *tm = &tls_main;
u32 fifo_size = 128 << 12;
if (!is_en)
{
da->app_index = tm->app_index;
da->api_client_index = APP_INVALID_INDEX;
vnet_application_detach (da);
return 0;
}
fifo_size = tm->fifo_size ? tm->fifo_size : fifo_size;
clib_memset (a, 0, sizeof (*a));
clib_memset (options, 0, sizeof (options));
a->session_cb_vft = &tls_app_cb_vft;
a->api_client_index = APP_INVALID_INDEX;
a->options = options;
a->name = format (0, "tls");
a->options[APP_OPTIONS_SEGMENT_SIZE] = tm->first_seg_size;
a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = tm->add_seg_size;
a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
if (vnet_application_attach (a))
{
clib_warning ("failed to attach tls app");
return clib_error_return (0, "failed to attach tls app");
}
tm->app_index = a->app_index;
vec_free (a->name);
return 0;
}
static const transport_proto_vft_t tls_proto = {
.enable = tls_enable,
.connect = tls_connect,
.close = tls_disconnect,
.start_listen = tls_start_listen,
.stop_listen = tls_stop_listen,
.get_connection = tls_connection_get,
.get_listener = tls_listener_get,
.get_half_open = tls_half_open_get,
.cleanup_ho = tls_cleanup_ho,
.custom_tx = tls_custom_tx_callback,
.format_connection = format_tls_connection,
.format_half_open = format_tls_half_open,
.format_listener = format_tls_listener,
.get_transport_endpoint = tls_transport_endpoint_get,
.get_transport_listener_endpoint = tls_transport_listener_endpoint_get,
.transport_options = {
.name = "tls",
.short_name = "J",
.tx_type = TRANSPORT_TX_INTERNAL,
.service_type = TRANSPORT_SERVICE_VC,
},
};
int
dtls_connect (transport_endpoint_cfg_t *tep)
{
vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
transport_endpt_crypto_cfg_t *ccfg;
crypto_engine_type_t engine_type;
session_endpoint_cfg_t *sep;
tls_main_t *tm = &tls_main;
app_worker_t *app_wrk;
application_t *app;
tls_ctx_t *ctx;
u32 ctx_handle;
int rv;
sep = (session_endpoint_cfg_t *) tep;
if (!sep->ext_cfg)
return -1;
app_wrk = app_worker_get (sep->app_wrk_index);
app = application_get (app_wrk->app_index);
ccfg = &sep->ext_cfg->crypto;
engine_type = tls_get_engine_type (ccfg->crypto_engine, app->tls_engine);
if (engine_type == CRYPTO_ENGINE_NONE)
{
clib_warning ("No tls engine_type available");
return -1;
}
ctx_handle = tls_ctx_alloc_w_thread (engine_type, transport_cl_thread ());
ctx = tls_ctx_get_w_thread (ctx_handle, transport_cl_thread ());
ctx->parent_app_wrk_index = sep->app_wrk_index;
ctx->parent_app_api_context = sep->opaque;
ctx->tcp_is_ip4 = sep->is_ip4;
ctx->ckpair_index = ccfg->ckpair_index;
ctx->tls_type = sep->transport_proto;
ctx->tls_ctx_handle = ctx_handle;
ctx->c_proto = TRANSPORT_PROTO_DTLS;
ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
if (ccfg->hostname[0])
{
ctx->srv_hostname = format (0, "%s", ccfg->hostname);
vec_terminate_c_string (ctx->srv_hostname);
}
ctx->tls_ctx_engine = engine_type;
clib_memcpy_fast (&cargs->sep, sep, sizeof (session_endpoint_t));
cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
cargs->app_index = tm->app_index;
cargs->api_context = ctx_handle;
cargs->sep_ext.ns_index = app->ns_index;
cargs->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
if ((rv = vnet_connect (cargs)))
return rv;
TLS_DBG (1, "New DTLS connect request %x engine %d", ctx_handle,
engine_type);
return ctx_handle;
}
static transport_connection_t *
dtls_half_open_get (u32 ho_index)
{
tls_ctx_t *ho_ctx;
ho_ctx = tls_ctx_get_w_thread (ho_index, transport_cl_thread ());
return &ho_ctx->connection;
}
static void
dtls_cleanup_callback (u32 ctx_index, u32 thread_index)
{
/* No op */
}
static void
dtls_cleanup_ho (u32 ho_index)
{
tls_ctx_t *ctx;
ctx = tls_ctx_get_w_thread (ho_index, transport_cl_thread ());
tls_ctx_free (ctx);
}
u8 *
format_dtls_half_open (u8 *s, va_list *args)
{
u32 ho_index = va_arg (*args, u32);
u32 __clib_unused thread_index = va_arg (*args, u32);
tls_ctx_t *ho_ctx;
session_t *us;
ho_ctx = tls_ctx_get_w_thread (ho_index, transport_cl_thread ());
us = session_get_from_handle (ho_ctx->tls_session_handle);
s = format (s, "[%d:%d][%s] half-open app_wrk %u engine %u us %d:%d",
ho_ctx->c_thread_index, ho_ctx->c_s_index, "DTLS",
ho_ctx->parent_app_wrk_index, ho_ctx->tls_ctx_engine,
us->thread_index, us->session_index);
return s;
}
static const transport_proto_vft_t dtls_proto = {
.enable = 0,
.connect = dtls_connect,
.close = tls_disconnect,
.start_listen = tls_start_listen,
.stop_listen = tls_stop_listen,
.get_connection = tls_connection_get,
.get_listener = tls_listener_get,
.get_half_open = dtls_half_open_get,
.custom_tx = tls_custom_tx_callback,
.cleanup = dtls_cleanup_callback,
.cleanup_ho = dtls_cleanup_ho,
.format_connection = format_tls_connection,
.format_half_open = format_dtls_half_open,
.format_listener = format_tls_listener,
.get_transport_endpoint = tls_transport_endpoint_get,
.get_transport_listener_endpoint = tls_transport_listener_endpoint_get,
.transport_options = {
.name = "dtls",
.short_name = "D",
.tx_type = TRANSPORT_TX_INTERNAL,
.service_type = TRANSPORT_SERVICE_VC,
},
};
void
tls_register_engine (const tls_engine_vft_t * vft, crypto_engine_type_t type)
{
vec_validate (tls_vfts, type);
tls_vfts[type] = *vft;
}
static clib_error_t *
tls_init (vlib_main_t * vm)
{
vlib_thread_main_t *vtm = vlib_get_thread_main ();
tls_main_t *tm = &tls_main;
u32 num_threads;
num_threads = 1 /* main thread */ + vtm->n_threads;
if (!tm->ca_cert_path)
tm->ca_cert_path = TLS_CA_CERT_PATH;
vec_validate (tm->rx_bufs, num_threads - 1);
vec_validate (tm->tx_bufs, num_threads - 1);
tm->first_seg_size = 32 << 20;
tm->add_seg_size = 256 << 20;
transport_register_protocol (TRANSPORT_PROTO_TLS, &tls_proto,
FIB_PROTOCOL_IP4, ~0);
transport_register_protocol (TRANSPORT_PROTO_TLS, &tls_proto,
FIB_PROTOCOL_IP6, ~0);
transport_register_protocol (TRANSPORT_PROTO_DTLS, &dtls_proto,
FIB_PROTOCOL_IP4, ~0);
transport_register_protocol (TRANSPORT_PROTO_DTLS, &dtls_proto,
FIB_PROTOCOL_IP6, ~0);
return 0;
}
VLIB_INIT_FUNCTION (tls_init);
static clib_error_t *
tls_config_fn (vlib_main_t * vm, unformat_input_t * input)
{
tls_main_t *tm = &tls_main;
uword tmp;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "use-test-cert-in-ca"))
tm->use_test_cert_in_ca = 1;
else if (unformat (input, "ca-cert-path %s", &tm->ca_cert_path))
;
else if (unformat (input, "first-segment-size %U", unformat_memory_size,
&tm->first_seg_size))
;
else if (unformat (input, "add-segment-size %U", unformat_memory_size,
&tm->add_seg_size))
;
else if (unformat (input, "fifo-size %U", unformat_memory_size, &tmp))
{
if (tmp >= 0x100000000ULL)
{
return clib_error_return
(0, "fifo-size %llu (0x%llx) too large", tmp, tmp);
}
tm->fifo_size = tmp;
}
else
return clib_error_return (0, "unknown input `%U'",
format_unformat_error, input);
}
return 0;
}
VLIB_CONFIG_FUNCTION (tls_config_fn, "tls");
tls_main_t *
vnet_tls_get_main (void)
{
return &tls_main;
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
|