blob: ddfd865e80f5768df5a540ab2a96d2f5434be552 [file] [log] [blame]
Frédéric Lécaille70da8892020-11-06 15:49:49 +01001/*
2 * QUIC socket management.
3 *
4 * Copyright 2020 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
5 *
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>
Amaury Denoyelle4d295042022-01-19 16:18:44 +010020#include <haproxy/quic_sock.h>
Amaury Denoyelleeb01f592021-10-07 16:44:05 +020021#include <haproxy/session.h>
Frédéric Lécaille026a7922020-11-23 15:46:36 +010022#include <haproxy/xprt_quic.h>
23
24/* This function is called from the protocol layer accept() in order to
25 * instantiate a new session on behalf of a given listener and frontend. It
26 * returns a positive value upon success, 0 if the connection can be ignored,
27 * or a negative value upon critical failure. The accepted connection is
28 * closed if we return <= 0. If no handshake is needed, it immediately tries
29 * to instantiate a new stream. The connection must already have been filled
30 * with the incoming connection handle (a fd), a target (the listener) and a
31 * source address.
32 */
33int quic_session_accept(struct connection *cli_conn)
34{
35 struct listener *l = __objt_listener(cli_conn->target);
36 struct proxy *p = l->bind_conf->frontend;
37 struct session *sess;
38
39 cli_conn->proxy_netns = l->rx.settings->netns;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010040 /* This flag is ordinarily set by conn_ctrl_init() which cannot
41 * be called for now.
42 */
43 cli_conn->flags |= CO_FL_CTRL_READY;
44
45 /* wait for a PROXY protocol header */
46 if (l->options & LI_O_ACC_PROXY)
47 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
48
49 /* wait for a NetScaler client IP insertion protocol header */
50 if (l->options & LI_O_ACC_CIP)
51 cli_conn->flags |= CO_FL_ACCEPT_CIP;
52
Frédéric Lécaille026a7922020-11-23 15:46:36 +010053 /* Add the handshake pseudo-XPRT */
54 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
55 if (xprt_add_hs(cli_conn) != 0)
56 goto out_free_conn;
57 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010058
Frédéric Lécaille026a7922020-11-23 15:46:36 +010059 sess = session_new(p, l, &cli_conn->obj_type);
60 if (!sess)
61 goto out_free_conn;
62
63 conn_set_owner(cli_conn, sess, NULL);
64
Frédéric Lécailleecb58722021-05-27 17:12:36 +020065 if (conn_complete_session(cli_conn) < 0)
66 goto out_free_sess;
67
68 if (conn_xprt_start(cli_conn) >= 0)
Frédéric Lécaille27faba72021-03-03 16:21:00 +010069 return 1;
70
Frédéric Lécaille026a7922020-11-23 15:46:36 +010071 out_free_sess:
72 /* prevent call to listener_release during session_free. It will be
73 * done below, for all errors. */
74 sess->listener = NULL;
75 session_free(sess);
76 out_free_conn:
77 cli_conn->qc->conn = NULL;
78 conn_stop_tracking(cli_conn);
79 conn_xprt_close(cli_conn);
80 conn_free(cli_conn);
81 out:
82
Frédéric Lécaillee8139f32021-03-11 17:06:30 +010083 return -1;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010084}
85
86/*
87 * Inspired from session_accept_fd().
88 * Instantiate a new connection (connection struct) to be attached to <qc>
89 * QUIC connection of <l> listener.
90 * Returns 1 if succeeded, 0 if not.
91 */
92static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
93 struct sockaddr_storage *saddr)
94{
95 struct connection *cli_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010096
Frédéric Lécaille026a7922020-11-23 15:46:36 +010097 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
98 goto out;
99
Frédéric Lécailled169efe2021-11-05 11:40:50 +0100100 if (!sockaddr_alloc(&cli_conn->dst, saddr, sizeof *saddr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100101 goto out_free_conn;
102
Frédéric Lécailled169efe2021-11-05 11:40:50 +0100103 cli_conn->flags |= CO_FL_ADDR_TO_SET;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100104 qc->conn = cli_conn;
105 cli_conn->qc = qc;
106
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100107 cli_conn->handle.fd = l->rx.fd;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100108 cli_conn->target = &l->obj_type;
109
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200110 /* We need the xprt context before accepting (->accept()) the connection:
111 * we may receive packet before this connection acception.
112 */
113 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
114 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100115
116 return 1;
117
118 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200119 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100120 conn_stop_tracking(cli_conn);
121 conn_xprt_close(cli_conn);
122 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100123 out:
124
125 return 0;
126}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100127
128/* Tests if the receiver supports accepting connections. Returns positive on
129 * success, 0 if not possible
130 */
131int quic_sock_accepting_conn(const struct receiver *rx)
132{
133 return 1;
134}
135
136/* Accept an incoming connection from listener <l>, and return it, as well as
137 * a CO_AC_* status code into <status> if not null. Null is returned on error.
138 * <l> must be a valid listener with a valid frontend.
139 */
140struct connection *quic_sock_accept_conn(struct listener *l, int *status)
141{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100142 struct quic_conn *qc;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100143 struct li_per_thread *lthr = &l->per_thr[tid];
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100144
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100145 qc = MT_LIST_POP(&lthr->quic_accept.conns, struct quic_conn *, accept_list);
146 if (!qc)
147 goto done;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100148
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100149 if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100150 goto err;
151
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100152 done:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100153 *status = CO_AC_DONE;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100154 return qc ? qc->conn : NULL;
155
156 err:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100157 /* in case of error reinsert the element to process it later. */
158 MT_LIST_INSERT(&lthr->quic_accept.conns, &qc->accept_list);
159
160 *status = CO_AC_PAUSE;
161 return NULL;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100162}
163
164/* Function called on a read event from a listening socket. It tries
165 * to handle as many connections as possible.
166 */
167void quic_sock_fd_iocb(int fd)
168{
169 ssize_t ret;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100170 struct rxbuf *rxbuf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100171 struct buffer *buf;
172 struct listener *l = objt_listener(fdtab[fd].owner);
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100173 struct quic_transport_params *params;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100174 /* Source address */
175 struct sockaddr_storage saddr = {0};
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100176 size_t max_sz, cspace;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100177 socklen_t saddrlen;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100178 struct quic_dgram *dgram, *dgramp, *new_dgram;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100179 unsigned char *dgram_buf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100180
Tim Duesterhus16554242021-09-15 13:58:49 +0200181 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100182
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100183 if (!l)
184 return;
185
Willy Tarreauf5090652021-04-06 17:23:40 +0200186 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100187 return;
188
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100189 rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), mt_list);
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100190 if (!rxbuf)
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100191 goto out;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100192
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100193 buf = &rxbuf->buf;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100194
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100195 new_dgram = NULL;
196 /* Remove all consumed datagrams of this buffer */
197 list_for_each_entry_safe(dgram, dgramp, &rxbuf->dgrams, list) {
198 if (HA_ATOMIC_LOAD(&dgram->buf))
199 break;
200
201 LIST_DELETE(&dgram->list);
202 b_del(buf, dgram->len);
203 if (!new_dgram)
204 new_dgram = dgram;
205 else
206 pool_free(pool_head_quic_dgram, dgram);
207 }
208
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100209 params = &l->bind_conf->quic_params;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100210 max_sz = params->max_udp_payload_size;
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100211 cspace = b_contig_space(buf);
212 if (cspace < max_sz) {
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100213 struct quic_dgram *dgram;
214
215 /* Allocate a fake datagram, without data to locate
216 * the end of the RX buffer (required during purging).
217 */
218 dgram = pool_zalloc(pool_head_quic_dgram);
219 if (!dgram)
220 goto out;
221
222 dgram->len = cspace;
223 LIST_APPEND(&rxbuf->dgrams, &dgram->list);
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100224 /* Consume the remaining space */
225 b_add(buf, cspace);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100226 if (b_contig_space(buf) < max_sz)
227 goto out;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100228
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100229 }
230
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100231 dgram_buf = (unsigned char *)b_tail(buf);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100232 saddrlen = sizeof saddr;
233 do {
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100234 ret = recvfrom(fd, dgram_buf, max_sz, 0,
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100235 (struct sockaddr *)&saddr, &saddrlen);
236 if (ret < 0) {
237 if (errno == EINTR)
238 continue;
239 if (errno == EAGAIN)
240 fd_cant_recv(fd);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100241 goto out;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100242 }
243 } while (0);
244
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100245 b_add(buf, ret);
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100246 if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr,
247 new_dgram, &rxbuf->dgrams)) {
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100248 /* If wrong, consume this datagram */
249 b_del(buf, ret);
250 }
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100251 out:
252 MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->mt_list);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100253}
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100254
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100255/* TODO standardize this function for a generic UDP sendto wrapper. This can be
256 * done by removing the <qc> arg and replace it with address/port.
257 */
258size_t qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
259 int flags)
260{
261 ssize_t ret;
262 size_t try, done;
263 int send_flag;
264
265 done = 0;
266 /* send the largest possible block. For this we perform only one call
267 * to send() unless the buffer wraps and we exactly fill the first hunk,
268 * in which case we accept to do it once again.
269 */
270 while (count) {
271 try = b_contig_data(buf, done);
272 if (try > count)
273 try = count;
274
275 send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
276 if (try < count || flags & CO_SFL_MSG_MORE)
277 send_flag |= MSG_MORE;
278
279 ret = sendto(qc->li->rx.fd, b_peek(buf, done), try, send_flag,
280 (struct sockaddr *)&qc->peer_addr, get_addr_len(&qc->peer_addr));
281 if (ret > 0) {
282 /* TODO remove partial sending support for UDP */
283 count -= ret;
284 done += ret;
285
286 if (ret < try)
287 break;
288 }
289 else if (ret == 0 || errno == EAGAIN || errno == ENOTCONN || errno == EINPROGRESS) {
290 /* TODO must be handle properly. It is justified for UDP ? */
291 ABORT_NOW();
292 }
293 else if (errno != EINTR) {
294 /* TODO must be handle properly. It is justified for UDP ? */
295 ABORT_NOW();
296 }
297 }
298
299 if (done > 0) {
300 /* we count the total bytes sent, and the send rate for 32-byte
301 * blocks. The reason for the latter is that freq_ctr are
302 * limited to 4GB and that it's not enough per second.
303 */
304 _HA_ATOMIC_ADD(&global.out_bytes, done);
305 update_freq_ctr(&global.out_32bps, (done + 16) / 32);
306 }
307 return done;
308}
309
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100310
311/*********************** QUIC accept queue management ***********************/
312/* per-thread accept queues */
313struct quic_accept_queue *quic_accept_queues;
314
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100315/* 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 +0100316 * up. If <qc> accept is already scheduled or done, nothing is done.
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100317 */
318void quic_accept_push_qc(struct quic_conn *qc)
319{
320 struct quic_accept_queue *queue = &quic_accept_queues[qc->tid];
321 struct li_per_thread *lthr = &qc->li->per_thr[qc->tid];
322
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100323 /* early return if accept is already in progress/done for this
324 * connection
325 */
326 if (HA_ATOMIC_BTS(&qc->flags, QUIC_FL_ACCEPT_REGISTERED_BIT))
327 return;
328
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100329 BUG_ON(MT_LIST_INLIST(&qc->accept_list));
330
331 /* 1. insert the listener in the accept queue
332 *
333 * Use TRY_APPEND as there is a possible race even with INLIST if
334 * multiple threads try to add the same listener instance from several
335 * quic_conn.
336 */
337 if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
338 MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
339
340 /* 2. insert the quic_conn in the listener per-thread queue. */
341 MT_LIST_APPEND(&lthr->quic_accept.conns, &qc->accept_list);
342
343 /* 3. wake up the queue tasklet */
344 tasklet_wakeup(quic_accept_queues[qc->tid].tasklet);
345}
346
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100347/* Tasklet handler to accept QUIC connections. Call listener_accept on every
348 * listener instances registered in the accept queue.
349 */
350static struct task *quic_accept_run(struct task *t, void *ctx, unsigned int i)
351{
352 struct li_per_thread *lthr;
353 struct mt_list *elt1, elt2;
354 struct quic_accept_queue *queue = &quic_accept_queues[tid];
355
356 mt_list_for_each_entry_safe(lthr, &queue->listeners, quic_accept.list, elt1, elt2) {
357 listener_accept(lthr->li);
358 MT_LIST_DELETE_SAFE(elt1);
359 }
360
361 return NULL;
362}
363
364static int quic_alloc_accept_queues(void)
365{
366 int i;
367
368 quic_accept_queues = calloc(global.nbthread, sizeof(struct quic_accept_queue));
369 if (!quic_accept_queues) {
370 ha_alert("Failed to allocate the quic accept queues.\n");
371 return 0;
372 }
373
374 for (i = 0; i < global.nbthread; ++i) {
375 struct tasklet *task;
376 if (!(task = tasklet_new())) {
377 ha_alert("Failed to allocate the quic accept queue on thread %d.\n", i);
378 return 0;
379 }
380
381 tasklet_set_tid(task, i);
382 task->process = quic_accept_run;
383 quic_accept_queues[i].tasklet = task;
384
385 MT_LIST_INIT(&quic_accept_queues[i].listeners);
386 }
387
388 return 1;
389}
390REGISTER_POST_CHECK(quic_alloc_accept_queues);
391
392static int quic_deallocate_accept_queues(void)
393{
394 int i;
395
396 if (quic_accept_queues) {
397 for (i = 0; i < global.nbthread; ++i)
398 tasklet_free(quic_accept_queues[i].tasklet);
399 free(quic_accept_queues);
400 }
401
402 return 1;
403}
404REGISTER_POST_DEINIT(quic_deallocate_accept_queues);