blob: 29f30ccbe03ec184b3cba0d373830079a6617806 [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
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +020013#define _GNU_SOURCE /* required for struct in6_pktinfo */
Frédéric Lécaille70da8892020-11-06 15:49:49 +010014#include <errno.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020015#include <stdlib.h>
16#include <string.h>
Frédéric Lécaille70da8892020-11-06 15:49:49 +010017
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +020018#include <netinet/in.h>
Frédéric Lécaille70da8892020-11-06 15:49:49 +010019#include <sys/socket.h>
20#include <sys/types.h>
21
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020022#include <haproxy/api.h>
23#include <haproxy/buf.h>
Frédéric Lécaille70da8892020-11-06 15:49:49 +010024#include <haproxy/connection.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020025#include <haproxy/fd.h>
26#include <haproxy/freq_ctr.h>
27#include <haproxy/global-t.h>
28#include <haproxy/list.h>
Frédéric Lécaille70da8892020-11-06 15:49:49 +010029#include <haproxy/listener.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020030#include <haproxy/pool.h>
Frédéric Lécaille6492e662022-05-17 17:23:16 +020031#include <haproxy/proto_quic.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020032#include <haproxy/proxy-t.h>
Amaury Denoyelle92fa63f2022-09-30 18:11:13 +020033#include <haproxy/quic_conn.h>
Amaury Denoyelle4d295042022-01-19 16:18:44 +010034#include <haproxy/quic_sock.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020035#include <haproxy/quic_tp-t.h>
Amaury Denoyelleeb01f592021-10-07 16:44:05 +020036#include <haproxy/session.h>
Amaury Denoyelle5c25dc52022-09-30 17:44:15 +020037#include <haproxy/stats-t.h>
38#include <haproxy/task.h>
Amaury Denoyelle777969c2022-03-24 16:06:26 +010039#include <haproxy/tools.h>
Frédéric Lécaille026a7922020-11-23 15:46:36 +010040
41/* This function is called from the protocol layer accept() in order to
42 * instantiate a new session on behalf of a given listener and frontend. It
43 * returns a positive value upon success, 0 if the connection can be ignored,
44 * or a negative value upon critical failure. The accepted connection is
45 * closed if we return <= 0. If no handshake is needed, it immediately tries
46 * to instantiate a new stream. The connection must already have been filled
47 * with the incoming connection handle (a fd), a target (the listener) and a
48 * source address.
49 */
50int quic_session_accept(struct connection *cli_conn)
51{
52 struct listener *l = __objt_listener(cli_conn->target);
53 struct proxy *p = l->bind_conf->frontend;
54 struct session *sess;
55
56 cli_conn->proxy_netns = l->rx.settings->netns;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010057 /* This flag is ordinarily set by conn_ctrl_init() which cannot
58 * be called for now.
59 */
60 cli_conn->flags |= CO_FL_CTRL_READY;
61
62 /* wait for a PROXY protocol header */
63 if (l->options & LI_O_ACC_PROXY)
64 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
65
66 /* wait for a NetScaler client IP insertion protocol header */
67 if (l->options & LI_O_ACC_CIP)
68 cli_conn->flags |= CO_FL_ACCEPT_CIP;
69
Frédéric Lécaille026a7922020-11-23 15:46:36 +010070 /* Add the handshake pseudo-XPRT */
71 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
72 if (xprt_add_hs(cli_conn) != 0)
73 goto out_free_conn;
74 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010075
Frédéric Lécaille026a7922020-11-23 15:46:36 +010076 sess = session_new(p, l, &cli_conn->obj_type);
77 if (!sess)
78 goto out_free_conn;
79
80 conn_set_owner(cli_conn, sess, NULL);
81
Frédéric Lécailleecb58722021-05-27 17:12:36 +020082 if (conn_complete_session(cli_conn) < 0)
83 goto out_free_sess;
84
Amaury Denoyelle622ec412022-04-13 16:58:26 +020085 if (conn_xprt_start(cli_conn) < 0) {
86 /* conn_complete_session has succeeded : conn is the owner of
87 * the session and the MUX is initialized.
88 * Let the MUX free all resources on error.
89 */
90 cli_conn->mux->destroy(cli_conn->ctx);
91 return -1;
92 }
93
94 return 1;
Frédéric Lécaille27faba72021-03-03 16:21:00 +010095
Frédéric Lécaille026a7922020-11-23 15:46:36 +010096 out_free_sess:
97 /* prevent call to listener_release during session_free. It will be
98 * done below, for all errors. */
99 sess->listener = NULL;
100 session_free(sess);
101 out_free_conn:
Willy Tarreau784b8682022-04-11 14:18:10 +0200102 cli_conn->handle.qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100103 conn_stop_tracking(cli_conn);
104 conn_xprt_close(cli_conn);
105 conn_free(cli_conn);
106 out:
107
Frédéric Lécaillee8139f32021-03-11 17:06:30 +0100108 return -1;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100109}
110
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200111/* Retrieve a connection's source address. Returns -1 on failure. */
112int quic_sock_get_src(struct connection *conn, struct sockaddr *addr, socklen_t len)
113{
114 struct quic_conn *qc;
115
Willy Tarreau784b8682022-04-11 14:18:10 +0200116 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200117 return -1;
118
Willy Tarreau784b8682022-04-11 14:18:10 +0200119 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200120 if (conn_is_back(conn)) {
121 /* no source address defined for outgoing connections for now */
122 return -1;
123 } else {
124 /* front connection, return the peer's address */
125 if (len > sizeof(qc->peer_addr))
126 len = sizeof(qc->peer_addr);
127 memcpy(addr, &qc->peer_addr, len);
128 return 0;
129 }
130}
131
132/* Retrieve a connection's destination address. Returns -1 on failure. */
133int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t len)
134{
135 struct quic_conn *qc;
136
Willy Tarreau784b8682022-04-11 14:18:10 +0200137 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200138 return -1;
139
Willy Tarreau784b8682022-04-11 14:18:10 +0200140 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200141 if (conn_is_back(conn)) {
142 /* back connection, return the peer's address */
143 if (len > sizeof(qc->peer_addr))
144 len = sizeof(qc->peer_addr);
145 memcpy(addr, &qc->peer_addr, len);
146 } else {
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200147 struct sockaddr_storage *from;
148
149 /* Return listener address if IP_PKTINFO or friends are not
150 * supported by the socket.
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200151 */
152 BUG_ON(!qc->li);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200153 from = is_addr(&qc->local_addr) ? &qc->local_addr :
154 &qc->li->rx.addr;
155 if (len > sizeof(*from))
156 len = sizeof(*from);
157 memcpy(addr, from, len);
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +0200158 }
159 return 0;
160}
161
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100162/*
163 * Inspired from session_accept_fd().
164 * Instantiate a new connection (connection struct) to be attached to <qc>
165 * QUIC connection of <l> listener.
166 * Returns 1 if succeeded, 0 if not.
167 */
168static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
169 struct sockaddr_storage *saddr)
170{
171 struct connection *cli_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100172
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100173 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
174 goto out;
175
Willy Tarreau9cc88c32022-04-08 14:34:31 +0200176 if (!sockaddr_alloc(&cli_conn->src, saddr, sizeof *saddr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100177 goto out_free_conn;
178
Willy Tarreau030b3e62022-05-02 17:47:46 +0200179 cli_conn->flags |= CO_FL_FDLESS;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100180 qc->conn = cli_conn;
Willy Tarreau784b8682022-04-11 14:18:10 +0200181 cli_conn->handle.qc = qc;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100182
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100183 cli_conn->target = &l->obj_type;
184
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200185 /* We need the xprt context before accepting (->accept()) the connection:
186 * we may receive packet before this connection acception.
187 */
188 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
189 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100190
191 return 1;
192
193 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200194 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100195 conn_stop_tracking(cli_conn);
196 conn_xprt_close(cli_conn);
197 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100198 out:
199
200 return 0;
201}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100202
203/* Tests if the receiver supports accepting connections. Returns positive on
204 * success, 0 if not possible
205 */
206int quic_sock_accepting_conn(const struct receiver *rx)
207{
208 return 1;
209}
210
211/* Accept an incoming connection from listener <l>, and return it, as well as
212 * a CO_AC_* status code into <status> if not null. Null is returned on error.
213 * <l> must be a valid listener with a valid frontend.
214 */
215struct connection *quic_sock_accept_conn(struct listener *l, int *status)
216{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100217 struct quic_conn *qc;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100218 struct li_per_thread *lthr = &l->per_thr[tid];
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100219
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100220 qc = MT_LIST_POP(&lthr->quic_accept.conns, struct quic_conn *, accept_list);
221 if (!qc)
222 goto done;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100223
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100224 if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100225 goto err;
226
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100227 done:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100228 *status = CO_AC_DONE;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100229 return qc ? qc->conn : NULL;
230
231 err:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100232 /* in case of error reinsert the element to process it later. */
233 MT_LIST_INSERT(&lthr->quic_accept.conns, &qc->accept_list);
234
235 *status = CO_AC_PAUSE;
236 return NULL;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100237}
238
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200239/* Retrieve the DCID from the datagram found in <buf> and deliver it to the
240 * correct datagram handler.
241 * Return 1 if a correct datagram could be found, 0 if not.
242 */
243static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner,
244 struct sockaddr_storage *saddr,
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200245 struct sockaddr_storage *daddr,
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200246 struct quic_dgram *new_dgram, struct list *dgrams)
247{
248 struct quic_dgram *dgram;
Willy Tarreauf5a0c8a2022-10-13 16:14:11 +0200249 const struct listener *l = owner;
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200250 unsigned char *dcid;
251 size_t dcid_len;
252 int cid_tid;
253
254 if (!len || !quic_get_dgram_dcid(buf, buf + len, &dcid, &dcid_len))
255 goto err;
256
257 dgram = new_dgram ? new_dgram : pool_alloc(pool_head_quic_dgram);
258 if (!dgram)
259 goto err;
260
Willy Tarreauf5a0c8a2022-10-13 16:14:11 +0200261 cid_tid = quic_get_cid_tid(dcid, l->bind_conf);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200262
263 /* All the members must be initialized! */
264 dgram->owner = owner;
265 dgram->buf = buf;
266 dgram->len = len;
267 dgram->dcid = dcid;
268 dgram->dcid_len = dcid_len;
269 dgram->saddr = *saddr;
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200270 dgram->daddr = *daddr;
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200271 dgram->qc = NULL;
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200272
273 /* Attached datagram to its quic_receiver_buf and quic_dghdlrs. */
274 LIST_APPEND(dgrams, &dgram->recv_list);
275 MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->handler_list);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200276
Willy Tarreauf9d4a7d2022-08-05 08:45:56 +0200277 /* typically quic_lstnr_dghdlr() */
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200278 tasklet_wakeup(quic_dghdlrs[cid_tid].task);
279
280 return 1;
281
282 err:
Frédéric Lécaillebfb077a2022-08-12 11:55:20 +0200283 pool_free(pool_head_quic_dgram, new_dgram);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200284 return 0;
285}
286
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200287/* This function is responsible to remove unused datagram attached in front of
288 * <buf>. Each instances will be freed until a not yet consumed datagram is
289 * found or end of the list is hit. The last unused datagram found is not freed
290 * and is instead returned so that the caller can reuse it if needed.
291 *
292 * Returns the last unused datagram or NULL if no occurence found.
293 */
294static struct quic_dgram *quic_rxbuf_purge_dgrams(struct quic_receiver_buf *buf)
295{
296 struct quic_dgram *cur, *prev = NULL;
297
298 while (!LIST_ISEMPTY(&buf->dgram_list)) {
299 cur = LIST_ELEM(buf->dgram_list.n, struct quic_dgram *, recv_list);
300
301 /* Loop until a not yet consumed datagram is found. */
302 if (cur->buf)
303 break;
304
305 /* Clear buffer of current unused datagram. */
306 LIST_DELETE(&cur->recv_list);
307 b_del(&buf->buf, cur->len);
308
309 /* Free last found unused datagram. */
310 if (prev)
311 pool_free(pool_head_quic_dgram, prev);
312 prev = cur;
313 }
314
315 /* Return last unused datagram found. */
316 return prev;
317}
318
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200319/* Receive data from datagram socket <fd>. Data are placed in <out> buffer of
320 * length <len>.
321 *
322 * Datagram addresses will be returned via the next arguments. <from> will be
323 * the peer address and <to> the reception one. Note that <to> can only be
324 * retrieved if the socket supports IP_PKTINFO or affiliated options. If not,
325 * <to> will be set as AF_UNSPEC. The caller must specify <to_port> to ensure
326 * that <to> address is completely filled.
327 *
328 * Returns value from recvmsg syscall.
329 */
330static ssize_t quic_recv(int fd, void *out, size_t len,
331 struct sockaddr *from, socklen_t from_len,
332 struct sockaddr *to, socklen_t to_len,
333 uint16_t dst_port)
334{
335 union pktinfo {
336#ifdef IP_PKTINFO
337 struct in_pktinfo in;
338#else /* !IP_PKTINFO */
339 struct in_addr addr;
340#endif
341#ifdef IPV6_RECVPKTINFO
342 struct in6_pktinfo in6;
343#endif
344 };
345 char cdata[CMSG_SPACE(sizeof(union pktinfo))];
346 struct msghdr msg;
347 struct iovec vec;
348 struct cmsghdr *cmsg;
349 ssize_t ret;
350
351 vec.iov_base = out;
352 vec.iov_len = len;
353
354 memset(&msg, 0, sizeof(msg));
355 msg.msg_name = from;
356 msg.msg_namelen = from_len;
357 msg.msg_iov = &vec;
358 msg.msg_iovlen = 1;
359 msg.msg_control = &cdata;
360 msg.msg_controllen = sizeof(cdata);
361
362 clear_addr((struct sockaddr_storage *)to);
363
364 do {
365 ret = recvmsg(fd, &msg, 0);
366 } while (ret < 0 && errno == EINTR);
367
368 /* TODO handle errno. On EAGAIN/EWOULDBLOCK use fd_cant_recv() if
369 * using dedicated connection socket.
370 */
371
372 if (ret < 0)
373 goto end;
374
375 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
376 switch (cmsg->cmsg_level) {
377 case IPPROTO_IP:
378#if defined(IP_PKTINFO)
379 if (cmsg->cmsg_type == IP_PKTINFO) {
380 struct sockaddr_in *in = (struct sockaddr_in *)to;
381 struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg);
382
383 if (to_len >= sizeof(struct sockaddr_in)) {
384 in->sin_family = AF_INET;
385 in->sin_addr = info->ipi_addr;
386 in->sin_port = dst_port;
387 }
388 }
389#elif defined(IP_RECVDSTADDR)
390 if (cmsg->cmsg_type == IP_RECVDSTADDR) {
391 struct sockaddr_in *in = (struct sockaddr_in *)to;
392 struct in_addr *info = (struct in_addr *)CMSG_DATA(cmsg);
393
394 if (to_len >= sizeof(struct sockaddr_in)) {
395 in->sin_family = AF_INET;
396 in->sin_addr.s_addr = info->s_addr;
397 in->sin_port = dst_port;
398 }
399 }
400#endif /* IP_PKTINFO || IP_RECVDSTADDR */
401 break;
402
403 case IPPROTO_IPV6:
404#ifdef IPV6_RECVPKTINFO
405 if (cmsg->cmsg_type == IPV6_PKTINFO) {
406 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)to;
407 struct in6_pktinfo *info6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
408
409 if (to_len >= sizeof(struct sockaddr_in6)) {
410 in6->sin6_family = AF_INET6;
411 memcpy(&in6->sin6_addr, &info6->ipi6_addr, sizeof(in6->sin6_addr));
412 in6->sin6_port = dst_port;
413 }
414 }
415#endif
416 break;
417 }
418 }
419
420 end:
421 return ret;
422}
423
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100424/* Function called on a read event from a listening socket. It tries
425 * to handle as many connections as possible.
426 */
427void quic_sock_fd_iocb(int fd)
428{
429 ssize_t ret;
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200430 struct quic_receiver_buf *rxbuf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100431 struct buffer *buf;
432 struct listener *l = objt_listener(fdtab[fd].owner);
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100433 struct quic_transport_params *params;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100434 /* Source address */
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200435 struct sockaddr_storage saddr = {0}, daddr = {0};
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100436 size_t max_sz, cspace;
Frédéric Lécaille2bed1f12022-06-23 21:05:05 +0200437 struct quic_dgram *new_dgram;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100438 unsigned char *dgram_buf;
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200439 int max_dgrams;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100440
Tim Duesterhus16554242021-09-15 13:58:49 +0200441 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100442
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200443 new_dgram = NULL;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100444 if (!l)
445 return;
446
Willy Tarreauf5090652021-04-06 17:23:40 +0200447 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100448 return;
449
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200450 rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100451 if (!rxbuf)
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100452 goto out;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100453
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100454 buf = &rxbuf->buf;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100455
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200456 max_dgrams = global.tune.maxpollevents;
457 start:
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500458 /* Try to reuse an existing dgram. Note that there is always at
Frédéric Lécaille2bed1f12022-06-23 21:05:05 +0200459 * least one datagram to pick, except the first time we enter
460 * this function for this <rxbuf> buffer.
461 */
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200462 new_dgram = quic_rxbuf_purge_dgrams(rxbuf);
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100463
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100464 params = &l->bind_conf->quic_params;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100465 max_sz = params->max_udp_payload_size;
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100466 cspace = b_contig_space(buf);
467 if (cspace < max_sz) {
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100468 struct quic_dgram *dgram;
469
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200470 /* Do no mark <buf> as full, and do not try to consume it
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200471 * if the contiguous remaining space is not at the end
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200472 */
473 if (b_tail(buf) + cspace < b_wrap(buf))
474 goto out;
475
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100476 /* Allocate a fake datagram, without data to locate
477 * the end of the RX buffer (required during purging).
478 */
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200479 dgram = pool_alloc(pool_head_quic_dgram);
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100480 if (!dgram)
481 goto out;
482
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200483 /* Initialize only the useful members of this fake datagram. */
484 dgram->buf = NULL;
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100485 dgram->len = cspace;
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200486 /* Append this datagram only to the RX buffer list. It will
487 * not be treated by any datagram handler.
488 */
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200489 LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list);
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200490
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100491 /* Consume the remaining space */
492 b_add(buf, cspace);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100493 if (b_contig_space(buf) < max_sz)
494 goto out;
495 }
496
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100497 dgram_buf = (unsigned char *)b_tail(buf);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200498 ret = quic_recv(fd, dgram_buf, max_sz,
499 (struct sockaddr *)&saddr, sizeof(saddr),
500 (struct sockaddr *)&daddr, sizeof(daddr),
501 get_net_port(&l->rx.addr));
502 if (ret <= 0)
503 goto out;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100504
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100505 b_add(buf, ret);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200506 if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr, &daddr,
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200507 new_dgram, &rxbuf->dgram_list)) {
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100508 /* If wrong, consume this datagram */
509 b_del(buf, ret);
510 }
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200511 new_dgram = NULL;
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200512 if (--max_dgrams > 0)
513 goto start;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100514 out:
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200515 pool_free(pool_head_quic_dgram, new_dgram);
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200516 MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100517}
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100518
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200519/* Send a datagram stored into <buf> buffer with <sz> as size.
520 * The caller must ensure there is at least <sz> bytes in this buffer.
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200521 *
522 * Returns 0 on success else non-zero.
523 *
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200524 * TODO standardize this function for a generic UDP sendto wrapper. This can be
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100525 * done by removing the <qc> arg and replace it with address/port.
526 */
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200527int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
528 int flags)
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100529{
530 ssize_t ret;
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100531
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200532 do {
533 ret = sendto(qc->li->rx.fd, b_peek(buf, b_head_ofs(buf)), sz,
534 MSG_DONTWAIT | MSG_NOSIGNAL,
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100535 (struct sockaddr *)&qc->peer_addr, get_addr_len(&qc->peer_addr));
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200536 } while (ret < 0 && errno == EINTR);
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100537
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200538 if (ret < 0 || ret != sz) {
Amaury Denoyelle1d9f1702022-10-24 10:03:33 +0200539 struct proxy *prx = qc->li->bind_conf->frontend;
540 struct quic_counters *prx_counters =
541 EXTRA_COUNTERS_GET(prx->extra_counters_fe,
542 &quic_stats_module);
543
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200544 /* TODO adjust errno for UDP context. */
545 if (errno == EAGAIN || errno == EWOULDBLOCK ||
546 errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) {
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200547 if (errno == EAGAIN || errno == EWOULDBLOCK)
548 HA_ATOMIC_INC(&prx_counters->socket_full);
549 else
550 HA_ATOMIC_INC(&prx_counters->sendto_err);
551 }
552 else if (errno) {
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500553 /* TODO unlisted errno : handle it explicitly. */
Amaury Denoyelle1d9f1702022-10-24 10:03:33 +0200554 HA_ATOMIC_INC(&prx_counters->sendto_err_unknown);
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200555 }
556
557 return 1;
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200558 }
559
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200560 /* we count the total bytes sent, and the send rate for 32-byte blocks.
561 * The reason for the latter is that freq_ctr are limited to 4GB and
562 * that it's not enough per second.
563 */
564 _HA_ATOMIC_ADD(&global.out_bytes, ret);
565 update_freq_ctr(&global.out_32bps, (ret + 16) / 32);
566
567 return 0;
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100568}
569
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100570
571/*********************** QUIC accept queue management ***********************/
572/* per-thread accept queues */
573struct quic_accept_queue *quic_accept_queues;
574
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100575/* 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 +0100576 * up. If <qc> accept is already scheduled or done, nothing is done.
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100577 */
578void quic_accept_push_qc(struct quic_conn *qc)
579{
580 struct quic_accept_queue *queue = &quic_accept_queues[qc->tid];
581 struct li_per_thread *lthr = &qc->li->per_thr[qc->tid];
582
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100583 /* early return if accept is already in progress/done for this
584 * connection
585 */
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200586 if (qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100587 return;
588
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100589 BUG_ON(MT_LIST_INLIST(&qc->accept_list));
590
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200591 qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100592 /* 1. insert the listener in the accept queue
593 *
594 * Use TRY_APPEND as there is a possible race even with INLIST if
595 * multiple threads try to add the same listener instance from several
596 * quic_conn.
597 */
598 if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
599 MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
600
601 /* 2. insert the quic_conn in the listener per-thread queue. */
602 MT_LIST_APPEND(&lthr->quic_accept.conns, &qc->accept_list);
603
604 /* 3. wake up the queue tasklet */
605 tasklet_wakeup(quic_accept_queues[qc->tid].tasklet);
606}
607
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100608/* Tasklet handler to accept QUIC connections. Call listener_accept on every
609 * listener instances registered in the accept queue.
610 */
Willy Tarreau41e701e2022-09-08 15:12:59 +0200611struct task *quic_accept_run(struct task *t, void *ctx, unsigned int i)
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100612{
613 struct li_per_thread *lthr;
614 struct mt_list *elt1, elt2;
615 struct quic_accept_queue *queue = &quic_accept_queues[tid];
616
617 mt_list_for_each_entry_safe(lthr, &queue->listeners, quic_accept.list, elt1, elt2) {
618 listener_accept(lthr->li);
619 MT_LIST_DELETE_SAFE(elt1);
620 }
621
622 return NULL;
623}
624
625static int quic_alloc_accept_queues(void)
626{
627 int i;
628
Tim Duesterhus9fb57e82022-06-01 21:58:37 +0200629 quic_accept_queues = calloc(global.nbthread,
630 sizeof(*quic_accept_queues));
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100631 if (!quic_accept_queues) {
632 ha_alert("Failed to allocate the quic accept queues.\n");
633 return 0;
634 }
635
636 for (i = 0; i < global.nbthread; ++i) {
637 struct tasklet *task;
638 if (!(task = tasklet_new())) {
639 ha_alert("Failed to allocate the quic accept queue on thread %d.\n", i);
640 return 0;
641 }
642
643 tasklet_set_tid(task, i);
644 task->process = quic_accept_run;
645 quic_accept_queues[i].tasklet = task;
646
647 MT_LIST_INIT(&quic_accept_queues[i].listeners);
648 }
649
650 return 1;
651}
652REGISTER_POST_CHECK(quic_alloc_accept_queues);
653
654static int quic_deallocate_accept_queues(void)
655{
656 int i;
657
658 if (quic_accept_queues) {
659 for (i = 0; i < global.nbthread; ++i)
660 tasklet_free(quic_accept_queues[i].tasklet);
661 free(quic_accept_queues);
662 }
663
664 return 1;
665}
666REGISTER_POST_DEINIT(quic_deallocate_accept_queues);