blob: 6207af7034ad4cbc09bfb2a222b49da1b72cfbb0 [file] [log] [blame]
Frédéric Lécaille70da8892020-11-06 15:49:49 +01001/*
2 * QUIC socket management.
3 *
Willy Tarreau3dfb7da2022-03-02 22:33:39 +01004 * Copyright 2020 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
Frédéric Lécaille70da8892020-11-06 15:49:49 +01005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14
15#include <sys/socket.h>
16#include <sys/types.h>
17
18#include <haproxy/connection.h>
19#include <haproxy/listener.h>
Frédéric Lécaille6492e662022-05-17 17:23:16 +020020#include <haproxy/proto_quic.h>
Amaury Denoyelle4d295042022-01-19 16:18:44 +010021#include <haproxy/quic_sock.h>
Amaury Denoyelleeb01f592021-10-07 16:44:05 +020022#include <haproxy/session.h>
Amaury Denoyelle777969c2022-03-24 16:06:26 +010023#include <haproxy/tools.h>
Frédéric Lécaille026a7922020-11-23 15:46:36 +010024#include <haproxy/xprt_quic.h>
25
26/* This function is called from the protocol layer accept() in order to
27 * instantiate a new session on behalf of a given listener and frontend. It
28 * returns a positive value upon success, 0 if the connection can be ignored,
29 * or a negative value upon critical failure. The accepted connection is
30 * closed if we return <= 0. If no handshake is needed, it immediately tries
31 * to instantiate a new stream. The connection must already have been filled
32 * with the incoming connection handle (a fd), a target (the listener) and a
33 * source address.
34 */
35int quic_session_accept(struct connection *cli_conn)
36{
37 struct listener *l = __objt_listener(cli_conn->target);
38 struct proxy *p = l->bind_conf->frontend;
39 struct session *sess;
40
41 cli_conn->proxy_netns = l->rx.settings->netns;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010042 /* This flag is ordinarily set by conn_ctrl_init() which cannot
43 * be called for now.
44 */
45 cli_conn->flags |= CO_FL_CTRL_READY;
46
47 /* wait for a PROXY protocol header */
48 if (l->options & LI_O_ACC_PROXY)
49 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
50
51 /* wait for a NetScaler client IP insertion protocol header */
52 if (l->options & LI_O_ACC_CIP)
53 cli_conn->flags |= CO_FL_ACCEPT_CIP;
54
Frédéric Lécaille026a7922020-11-23 15:46:36 +010055 /* Add the handshake pseudo-XPRT */
56 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
57 if (xprt_add_hs(cli_conn) != 0)
58 goto out_free_conn;
59 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010060
Frédéric Lécaille026a7922020-11-23 15:46:36 +010061 sess = session_new(p, l, &cli_conn->obj_type);
62 if (!sess)
63 goto out_free_conn;
64
65 conn_set_owner(cli_conn, sess, NULL);
66
Frédéric Lécailleecb58722021-05-27 17:12:36 +020067 if (conn_complete_session(cli_conn) < 0)
68 goto out_free_sess;
69
Amaury Denoyelle622ec412022-04-13 16:58:26 +020070 if (conn_xprt_start(cli_conn) < 0) {
71 /* conn_complete_session has succeeded : conn is the owner of
72 * the session and the MUX is initialized.
73 * Let the MUX free all resources on error.
74 */
75 cli_conn->mux->destroy(cli_conn->ctx);
76 return -1;
77 }
78
79 return 1;
Frédéric Lécaille27faba72021-03-03 16:21:00 +010080
Frédéric Lécaille026a7922020-11-23 15:46:36 +010081 out_free_sess:
82 /* prevent call to listener_release during session_free. It will be
83 * done below, for all errors. */
84 sess->listener = NULL;
85 session_free(sess);
86 out_free_conn:
Willy Tarreau784b8682022-04-11 14:18:10 +020087 cli_conn->handle.qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010088 conn_stop_tracking(cli_conn);
89 conn_xprt_close(cli_conn);
90 conn_free(cli_conn);
91 out:
92
Frédéric Lécaillee8139f32021-03-11 17:06:30 +010093 return -1;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010094}
95
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020096/* Retrieve a connection's source address. Returns -1 on failure. */
97int quic_sock_get_src(struct connection *conn, struct sockaddr *addr, socklen_t len)
98{
99 struct quic_conn *qc;
100
Willy Tarreau784b8682022-04-11 14:18:10 +0200101 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200102 return -1;
103
Willy Tarreau784b8682022-04-11 14:18:10 +0200104 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200105 if (conn_is_back(conn)) {
106 /* no source address defined for outgoing connections for now */
107 return -1;
108 } else {
109 /* front connection, return the peer's address */
110 if (len > sizeof(qc->peer_addr))
111 len = sizeof(qc->peer_addr);
112 memcpy(addr, &qc->peer_addr, len);
113 return 0;
114 }
115}
116
117/* Retrieve a connection's destination address. Returns -1 on failure. */
118int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t len)
119{
120 struct quic_conn *qc;
121
Willy Tarreau784b8682022-04-11 14:18:10 +0200122 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200123 return -1;
124
Willy Tarreau784b8682022-04-11 14:18:10 +0200125 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200126 if (conn_is_back(conn)) {
127 /* back connection, return the peer's address */
128 if (len > sizeof(qc->peer_addr))
129 len = sizeof(qc->peer_addr);
130 memcpy(addr, &qc->peer_addr, len);
131 } else {
132 /* FIXME: front connection, no local address for now, we'll
133 * return the listener's address instead.
134 */
135 BUG_ON(!qc->li);
136
137 if (len > sizeof(qc->li->rx.addr))
138 len = sizeof(qc->li->rx.addr);
139 memcpy(addr, &qc->li->rx.addr, len);
140 }
141 return 0;
142}
143
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100144/*
145 * Inspired from session_accept_fd().
146 * Instantiate a new connection (connection struct) to be attached to <qc>
147 * QUIC connection of <l> listener.
148 * Returns 1 if succeeded, 0 if not.
149 */
150static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
151 struct sockaddr_storage *saddr)
152{
153 struct connection *cli_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100154
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100155 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
156 goto out;
157
Willy Tarreau9cc88c32022-04-08 14:34:31 +0200158 if (!sockaddr_alloc(&cli_conn->src, saddr, sizeof *saddr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100159 goto out_free_conn;
160
Willy Tarreau030b3e62022-05-02 17:47:46 +0200161 cli_conn->flags |= CO_FL_FDLESS;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100162 qc->conn = cli_conn;
Willy Tarreau784b8682022-04-11 14:18:10 +0200163 cli_conn->handle.qc = qc;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100164
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100165 cli_conn->target = &l->obj_type;
166
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200167 /* We need the xprt context before accepting (->accept()) the connection:
168 * we may receive packet before this connection acception.
169 */
170 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
171 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100172
173 return 1;
174
175 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200176 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100177 conn_stop_tracking(cli_conn);
178 conn_xprt_close(cli_conn);
179 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100180 out:
181
182 return 0;
183}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100184
185/* Tests if the receiver supports accepting connections. Returns positive on
186 * success, 0 if not possible
187 */
188int quic_sock_accepting_conn(const struct receiver *rx)
189{
190 return 1;
191}
192
193/* Accept an incoming connection from listener <l>, and return it, as well as
194 * a CO_AC_* status code into <status> if not null. Null is returned on error.
195 * <l> must be a valid listener with a valid frontend.
196 */
197struct connection *quic_sock_accept_conn(struct listener *l, int *status)
198{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100199 struct quic_conn *qc;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100200 struct li_per_thread *lthr = &l->per_thr[tid];
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100201
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100202 qc = MT_LIST_POP(&lthr->quic_accept.conns, struct quic_conn *, accept_list);
203 if (!qc)
204 goto done;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100205
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100206 if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100207 goto err;
208
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100209 done:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100210 *status = CO_AC_DONE;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100211 return qc ? qc->conn : NULL;
212
213 err:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100214 /* in case of error reinsert the element to process it later. */
215 MT_LIST_INSERT(&lthr->quic_accept.conns, &qc->accept_list);
216
217 *status = CO_AC_PAUSE;
218 return NULL;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100219}
220
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200221/* Retrieve the DCID from the datagram found in <buf> and deliver it to the
222 * correct datagram handler.
223 * Return 1 if a correct datagram could be found, 0 if not.
224 */
225static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner,
226 struct sockaddr_storage *saddr,
227 struct quic_dgram *new_dgram, struct list *dgrams)
228{
229 struct quic_dgram *dgram;
230 unsigned char *dcid;
231 size_t dcid_len;
232 int cid_tid;
233
234 if (!len || !quic_get_dgram_dcid(buf, buf + len, &dcid, &dcid_len))
235 goto err;
236
237 dgram = new_dgram ? new_dgram : pool_alloc(pool_head_quic_dgram);
238 if (!dgram)
239 goto err;
240
241 cid_tid = quic_get_cid_tid(dcid);
242
243 /* All the members must be initialized! */
244 dgram->owner = owner;
245 dgram->buf = buf;
246 dgram->len = len;
247 dgram->dcid = dcid;
248 dgram->dcid_len = dcid_len;
249 dgram->saddr = *saddr;
250 dgram->qc = NULL;
251 LIST_APPEND(dgrams, &dgram->list);
252 MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->mt_list);
253
254 tasklet_wakeup(quic_dghdlrs[cid_tid].task);
255
256 return 1;
257
258 err:
259 return 0;
260}
261
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100262/* Function called on a read event from a listening socket. It tries
263 * to handle as many connections as possible.
264 */
265void quic_sock_fd_iocb(int fd)
266{
267 ssize_t ret;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100268 struct rxbuf *rxbuf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100269 struct buffer *buf;
270 struct listener *l = objt_listener(fdtab[fd].owner);
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100271 struct quic_transport_params *params;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100272 /* Source address */
273 struct sockaddr_storage saddr = {0};
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100274 size_t max_sz, cspace;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100275 socklen_t saddrlen;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100276 struct quic_dgram *dgram, *dgramp, *new_dgram;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100277 unsigned char *dgram_buf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100278
Tim Duesterhus16554242021-09-15 13:58:49 +0200279 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100280
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100281 if (!l)
282 return;
283
Willy Tarreauf5090652021-04-06 17:23:40 +0200284 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100285 return;
286
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100287 rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), mt_list);
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100288 if (!rxbuf)
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100289 goto out;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100290
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100291 buf = &rxbuf->buf;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100292
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100293 new_dgram = NULL;
294 /* Remove all consumed datagrams of this buffer */
295 list_for_each_entry_safe(dgram, dgramp, &rxbuf->dgrams, list) {
296 if (HA_ATOMIC_LOAD(&dgram->buf))
297 break;
298
299 LIST_DELETE(&dgram->list);
300 b_del(buf, dgram->len);
301 if (!new_dgram)
302 new_dgram = dgram;
303 else
304 pool_free(pool_head_quic_dgram, dgram);
305 }
306
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100307 params = &l->bind_conf->quic_params;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100308 max_sz = params->max_udp_payload_size;
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100309 cspace = b_contig_space(buf);
310 if (cspace < max_sz) {
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100311 struct quic_dgram *dgram;
312
313 /* Allocate a fake datagram, without data to locate
314 * the end of the RX buffer (required during purging).
315 */
316 dgram = pool_zalloc(pool_head_quic_dgram);
317 if (!dgram)
318 goto out;
319
320 dgram->len = cspace;
321 LIST_APPEND(&rxbuf->dgrams, &dgram->list);
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100322 /* Consume the remaining space */
323 b_add(buf, cspace);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100324 if (b_contig_space(buf) < max_sz)
325 goto out;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100326
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100327 }
328
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100329 dgram_buf = (unsigned char *)b_tail(buf);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100330 saddrlen = sizeof saddr;
331 do {
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100332 ret = recvfrom(fd, dgram_buf, max_sz, 0,
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100333 (struct sockaddr *)&saddr, &saddrlen);
Willy Tarreauacef5e22022-04-25 20:32:15 +0200334 if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
Frédéric Lécaille439c4642022-02-02 14:33:10 +0100335 fd_cant_recv(fd);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100336 goto out;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100337 }
Frédéric Lécaille439c4642022-02-02 14:33:10 +0100338 } while (ret < 0 && errno == EINTR);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100339
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100340 b_add(buf, ret);
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100341 if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr,
342 new_dgram, &rxbuf->dgrams)) {
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100343 /* If wrong, consume this datagram */
344 b_del(buf, ret);
345 }
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100346 out:
347 MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->mt_list);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100348}
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100349
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100350/* TODO standardize this function for a generic UDP sendto wrapper. This can be
351 * done by removing the <qc> arg and replace it with address/port.
352 */
353size_t qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
354 int flags)
355{
356 ssize_t ret;
357 size_t try, done;
358 int send_flag;
359
360 done = 0;
361 /* send the largest possible block. For this we perform only one call
362 * to send() unless the buffer wraps and we exactly fill the first hunk,
363 * in which case we accept to do it once again.
364 */
365 while (count) {
366 try = b_contig_data(buf, done);
367 if (try > count)
368 try = count;
369
370 send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
371 if (try < count || flags & CO_SFL_MSG_MORE)
372 send_flag |= MSG_MORE;
373
374 ret = sendto(qc->li->rx.fd, b_peek(buf, done), try, send_flag,
375 (struct sockaddr *)&qc->peer_addr, get_addr_len(&qc->peer_addr));
376 if (ret > 0) {
377 /* TODO remove partial sending support for UDP */
378 count -= ret;
379 done += ret;
380
381 if (ret < try)
382 break;
383 }
Amaury Denoyellead5df382022-05-18 18:26:13 +0200384 else if (errno == EINTR) {
385 /* try again */
386 continue;
387 }
Amaury Denoyelle3dde0d82022-05-19 11:53:56 +0200388 else if (ret == 0 || errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) {
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100389 /* TODO must be handle properly. It is justified for UDP ? */
Frédéric Lécaille8726d632022-05-03 10:32:21 +0200390 qc->sendto_err++;
Amaury Denoyelle8fa66662022-05-18 18:14:12 +0200391 break;
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100392 }
Amaury Denoyellead5df382022-05-18 18:26:13 +0200393 else if (errno) {
394 /* TODO unlisted errno : handle it explicitely. */
395 ABORT_NOW();
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100396 }
397 }
398
399 if (done > 0) {
400 /* we count the total bytes sent, and the send rate for 32-byte
401 * blocks. The reason for the latter is that freq_ctr are
402 * limited to 4GB and that it's not enough per second.
403 */
404 _HA_ATOMIC_ADD(&global.out_bytes, done);
405 update_freq_ctr(&global.out_32bps, (done + 16) / 32);
406 }
407 return done;
408}
409
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100410
411/*********************** QUIC accept queue management ***********************/
412/* per-thread accept queues */
413struct quic_accept_queue *quic_accept_queues;
414
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100415/* Install <qc> on the queue ready to be accepted. The queue task is then woken
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100416 * up. If <qc> accept is already scheduled or done, nothing is done.
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100417 */
418void quic_accept_push_qc(struct quic_conn *qc)
419{
420 struct quic_accept_queue *queue = &quic_accept_queues[qc->tid];
421 struct li_per_thread *lthr = &qc->li->per_thr[qc->tid];
422
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100423 /* early return if accept is already in progress/done for this
424 * connection
425 */
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200426 if (qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100427 return;
428
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100429 BUG_ON(MT_LIST_INLIST(&qc->accept_list));
430
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200431 qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100432 /* 1. insert the listener in the accept queue
433 *
434 * Use TRY_APPEND as there is a possible race even with INLIST if
435 * multiple threads try to add the same listener instance from several
436 * quic_conn.
437 */
438 if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
439 MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
440
441 /* 2. insert the quic_conn in the listener per-thread queue. */
442 MT_LIST_APPEND(&lthr->quic_accept.conns, &qc->accept_list);
443
444 /* 3. wake up the queue tasklet */
445 tasklet_wakeup(quic_accept_queues[qc->tid].tasklet);
446}
447
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100448/* Tasklet handler to accept QUIC connections. Call listener_accept on every
449 * listener instances registered in the accept queue.
450 */
451static struct task *quic_accept_run(struct task *t, void *ctx, unsigned int i)
452{
453 struct li_per_thread *lthr;
454 struct mt_list *elt1, elt2;
455 struct quic_accept_queue *queue = &quic_accept_queues[tid];
456
457 mt_list_for_each_entry_safe(lthr, &queue->listeners, quic_accept.list, elt1, elt2) {
458 listener_accept(lthr->li);
459 MT_LIST_DELETE_SAFE(elt1);
460 }
461
462 return NULL;
463}
464
465static int quic_alloc_accept_queues(void)
466{
467 int i;
468
469 quic_accept_queues = calloc(global.nbthread, sizeof(struct quic_accept_queue));
470 if (!quic_accept_queues) {
471 ha_alert("Failed to allocate the quic accept queues.\n");
472 return 0;
473 }
474
475 for (i = 0; i < global.nbthread; ++i) {
476 struct tasklet *task;
477 if (!(task = tasklet_new())) {
478 ha_alert("Failed to allocate the quic accept queue on thread %d.\n", i);
479 return 0;
480 }
481
482 tasklet_set_tid(task, i);
483 task->process = quic_accept_run;
484 quic_accept_queues[i].tasklet = task;
485
486 MT_LIST_INIT(&quic_accept_queues[i].listeners);
487 }
488
489 return 1;
490}
491REGISTER_POST_CHECK(quic_alloc_accept_queues);
492
493static int quic_deallocate_accept_queues(void)
494{
495 int i;
496
497 if (quic_accept_queues) {
498 for (i = 0; i < global.nbthread; ++i)
499 tasklet_free(quic_accept_queues[i].tasklet);
500 free(quic_accept_queues);
501 }
502
503 return 1;
504}
505REGISTER_POST_DEINIT(quic_deallocate_accept_queues);