blob: a8da3cb7b1f6abb9fc3d6f8fd626f96672537bdc [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
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020041/* Retrieve a connection's source address. Returns -1 on failure. */
42int quic_sock_get_src(struct connection *conn, struct sockaddr *addr, socklen_t len)
43{
44 struct quic_conn *qc;
45
Willy Tarreau784b8682022-04-11 14:18:10 +020046 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020047 return -1;
48
Willy Tarreau784b8682022-04-11 14:18:10 +020049 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020050 if (conn_is_back(conn)) {
51 /* no source address defined for outgoing connections for now */
52 return -1;
53 } else {
54 /* front connection, return the peer's address */
55 if (len > sizeof(qc->peer_addr))
56 len = sizeof(qc->peer_addr);
57 memcpy(addr, &qc->peer_addr, len);
58 return 0;
59 }
60}
61
62/* Retrieve a connection's destination address. Returns -1 on failure. */
63int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t len)
64{
65 struct quic_conn *qc;
66
Willy Tarreau784b8682022-04-11 14:18:10 +020067 if (!conn || !conn->handle.qc)
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020068 return -1;
69
Willy Tarreau784b8682022-04-11 14:18:10 +020070 qc = conn->handle.qc;
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020071 if (conn_is_back(conn)) {
72 /* back connection, return the peer's address */
73 if (len > sizeof(qc->peer_addr))
74 len = sizeof(qc->peer_addr);
75 memcpy(addr, &qc->peer_addr, len);
76 } else {
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +020077 struct sockaddr_storage *from;
78
79 /* Return listener address if IP_PKTINFO or friends are not
80 * supported by the socket.
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020081 */
82 BUG_ON(!qc->li);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +020083 from = is_addr(&qc->local_addr) ? &qc->local_addr :
84 &qc->li->rx.addr;
85 if (len > sizeof(*from))
86 len = sizeof(*from);
87 memcpy(addr, from, len);
Willy Tarreaucdf7c8e2022-04-11 16:20:00 +020088 }
89 return 0;
90}
91
Frédéric Lécaille026a7922020-11-23 15:46:36 +010092/*
93 * Inspired from session_accept_fd().
94 * Instantiate a new connection (connection struct) to be attached to <qc>
95 * QUIC connection of <l> listener.
96 * Returns 1 if succeeded, 0 if not.
97 */
98static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
99 struct sockaddr_storage *saddr)
100{
101 struct connection *cli_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100102
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100103 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
104 goto out;
105
Willy Tarreau9cc88c32022-04-08 14:34:31 +0200106 if (!sockaddr_alloc(&cli_conn->src, saddr, sizeof *saddr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100107 goto out_free_conn;
108
Willy Tarreau030b3e62022-05-02 17:47:46 +0200109 cli_conn->flags |= CO_FL_FDLESS;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100110 qc->conn = cli_conn;
Willy Tarreau784b8682022-04-11 14:18:10 +0200111 cli_conn->handle.qc = qc;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100112
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100113 cli_conn->target = &l->obj_type;
114
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100115 return 1;
116
117 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200118 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100119 conn_stop_tracking(cli_conn);
120 conn_xprt_close(cli_conn);
121 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100122 out:
123
124 return 0;
125}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100126
127/* Tests if the receiver supports accepting connections. Returns positive on
128 * success, 0 if not possible
129 */
130int quic_sock_accepting_conn(const struct receiver *rx)
131{
132 return 1;
133}
134
135/* Accept an incoming connection from listener <l>, and return it, as well as
136 * a CO_AC_* status code into <status> if not null. Null is returned on error.
137 * <l> must be a valid listener with a valid frontend.
138 */
139struct connection *quic_sock_accept_conn(struct listener *l, int *status)
140{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100141 struct quic_conn *qc;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100142 struct li_per_thread *lthr = &l->per_thr[tid];
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100143
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100144 qc = MT_LIST_POP(&lthr->quic_accept.conns, struct quic_conn *, accept_list);
145 if (!qc)
146 goto done;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100147
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100148 if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100149 goto err;
150
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100151 done:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100152 *status = CO_AC_DONE;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100153 return qc ? qc->conn : NULL;
154
155 err:
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100156 /* in case of error reinsert the element to process it later. */
157 MT_LIST_INSERT(&lthr->quic_accept.conns, &qc->accept_list);
158
159 *status = CO_AC_PAUSE;
160 return NULL;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100161}
162
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200163/* Retrieve the DCID from the datagram found in <buf> and deliver it to the
164 * correct datagram handler.
165 * Return 1 if a correct datagram could be found, 0 if not.
166 */
167static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner,
168 struct sockaddr_storage *saddr,
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200169 struct sockaddr_storage *daddr,
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200170 struct quic_dgram *new_dgram, struct list *dgrams)
171{
172 struct quic_dgram *dgram;
Willy Tarreauf5a0c8a2022-10-13 16:14:11 +0200173 const struct listener *l = owner;
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200174 unsigned char *dcid;
175 size_t dcid_len;
176 int cid_tid;
177
178 if (!len || !quic_get_dgram_dcid(buf, buf + len, &dcid, &dcid_len))
179 goto err;
180
181 dgram = new_dgram ? new_dgram : pool_alloc(pool_head_quic_dgram);
182 if (!dgram)
183 goto err;
184
Willy Tarreauf5a0c8a2022-10-13 16:14:11 +0200185 cid_tid = quic_get_cid_tid(dcid, l->bind_conf);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200186
187 /* All the members must be initialized! */
188 dgram->owner = owner;
189 dgram->buf = buf;
190 dgram->len = len;
191 dgram->dcid = dcid;
192 dgram->dcid_len = dcid_len;
193 dgram->saddr = *saddr;
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200194 dgram->daddr = *daddr;
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200195 dgram->qc = NULL;
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200196
197 /* Attached datagram to its quic_receiver_buf and quic_dghdlrs. */
198 LIST_APPEND(dgrams, &dgram->recv_list);
199 MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->handler_list);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200200
Willy Tarreauf9d4a7d2022-08-05 08:45:56 +0200201 /* typically quic_lstnr_dghdlr() */
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200202 tasklet_wakeup(quic_dghdlrs[cid_tid].task);
203
204 return 1;
205
206 err:
Frédéric Lécaillebfb077a2022-08-12 11:55:20 +0200207 pool_free(pool_head_quic_dgram, new_dgram);
Frédéric Lécaille6492e662022-05-17 17:23:16 +0200208 return 0;
209}
210
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200211/* This function is responsible to remove unused datagram attached in front of
212 * <buf>. Each instances will be freed until a not yet consumed datagram is
213 * found or end of the list is hit. The last unused datagram found is not freed
214 * and is instead returned so that the caller can reuse it if needed.
215 *
Ilya Shipitsin4a689da2022-10-29 09:34:32 +0500216 * Returns the last unused datagram or NULL if no occurrence found.
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200217 */
218static struct quic_dgram *quic_rxbuf_purge_dgrams(struct quic_receiver_buf *buf)
219{
220 struct quic_dgram *cur, *prev = NULL;
221
222 while (!LIST_ISEMPTY(&buf->dgram_list)) {
223 cur = LIST_ELEM(buf->dgram_list.n, struct quic_dgram *, recv_list);
224
225 /* Loop until a not yet consumed datagram is found. */
Amaury Denoyelle0b13e942022-10-25 11:38:21 +0200226 if (HA_ATOMIC_LOAD(&cur->buf))
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200227 break;
228
229 /* Clear buffer of current unused datagram. */
230 LIST_DELETE(&cur->recv_list);
231 b_del(&buf->buf, cur->len);
232
233 /* Free last found unused datagram. */
234 if (prev)
235 pool_free(pool_head_quic_dgram, prev);
236 prev = cur;
237 }
238
239 /* Return last unused datagram found. */
240 return prev;
241}
242
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200243/* Receive data from datagram socket <fd>. Data are placed in <out> buffer of
244 * length <len>.
245 *
246 * Datagram addresses will be returned via the next arguments. <from> will be
247 * the peer address and <to> the reception one. Note that <to> can only be
248 * retrieved if the socket supports IP_PKTINFO or affiliated options. If not,
249 * <to> will be set as AF_UNSPEC. The caller must specify <to_port> to ensure
250 * that <to> address is completely filled.
251 *
252 * Returns value from recvmsg syscall.
253 */
254static ssize_t quic_recv(int fd, void *out, size_t len,
255 struct sockaddr *from, socklen_t from_len,
256 struct sockaddr *to, socklen_t to_len,
257 uint16_t dst_port)
258{
259 union pktinfo {
260#ifdef IP_PKTINFO
261 struct in_pktinfo in;
262#else /* !IP_PKTINFO */
263 struct in_addr addr;
264#endif
265#ifdef IPV6_RECVPKTINFO
266 struct in6_pktinfo in6;
267#endif
268 };
269 char cdata[CMSG_SPACE(sizeof(union pktinfo))];
270 struct msghdr msg;
271 struct iovec vec;
272 struct cmsghdr *cmsg;
273 ssize_t ret;
274
275 vec.iov_base = out;
276 vec.iov_len = len;
277
278 memset(&msg, 0, sizeof(msg));
279 msg.msg_name = from;
280 msg.msg_namelen = from_len;
281 msg.msg_iov = &vec;
282 msg.msg_iovlen = 1;
283 msg.msg_control = &cdata;
284 msg.msg_controllen = sizeof(cdata);
285
286 clear_addr((struct sockaddr_storage *)to);
287
288 do {
289 ret = recvmsg(fd, &msg, 0);
290 } while (ret < 0 && errno == EINTR);
291
292 /* TODO handle errno. On EAGAIN/EWOULDBLOCK use fd_cant_recv() if
293 * using dedicated connection socket.
294 */
295
296 if (ret < 0)
297 goto end;
298
299 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
300 switch (cmsg->cmsg_level) {
301 case IPPROTO_IP:
302#if defined(IP_PKTINFO)
303 if (cmsg->cmsg_type == IP_PKTINFO) {
304 struct sockaddr_in *in = (struct sockaddr_in *)to;
305 struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg);
306
307 if (to_len >= sizeof(struct sockaddr_in)) {
308 in->sin_family = AF_INET;
309 in->sin_addr = info->ipi_addr;
310 in->sin_port = dst_port;
311 }
312 }
313#elif defined(IP_RECVDSTADDR)
314 if (cmsg->cmsg_type == IP_RECVDSTADDR) {
315 struct sockaddr_in *in = (struct sockaddr_in *)to;
316 struct in_addr *info = (struct in_addr *)CMSG_DATA(cmsg);
317
318 if (to_len >= sizeof(struct sockaddr_in)) {
319 in->sin_family = AF_INET;
320 in->sin_addr.s_addr = info->s_addr;
321 in->sin_port = dst_port;
322 }
323 }
324#endif /* IP_PKTINFO || IP_RECVDSTADDR */
325 break;
326
327 case IPPROTO_IPV6:
328#ifdef IPV6_RECVPKTINFO
329 if (cmsg->cmsg_type == IPV6_PKTINFO) {
330 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)to;
331 struct in6_pktinfo *info6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
332
333 if (to_len >= sizeof(struct sockaddr_in6)) {
334 in6->sin6_family = AF_INET6;
335 memcpy(&in6->sin6_addr, &info6->ipi6_addr, sizeof(in6->sin6_addr));
336 in6->sin6_port = dst_port;
337 }
338 }
339#endif
340 break;
341 }
342 }
343
344 end:
345 return ret;
346}
347
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100348/* Function called on a read event from a listening socket. It tries
349 * to handle as many connections as possible.
350 */
351void quic_sock_fd_iocb(int fd)
352{
353 ssize_t ret;
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200354 struct quic_receiver_buf *rxbuf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100355 struct buffer *buf;
356 struct listener *l = objt_listener(fdtab[fd].owner);
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100357 struct quic_transport_params *params;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100358 /* Source address */
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200359 struct sockaddr_storage saddr = {0}, daddr = {0};
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100360 size_t max_sz, cspace;
Frédéric Lécaille2bed1f12022-06-23 21:05:05 +0200361 struct quic_dgram *new_dgram;
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100362 unsigned char *dgram_buf;
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200363 int max_dgrams;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100364
Tim Duesterhus16554242021-09-15 13:58:49 +0200365 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100366
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200367 new_dgram = NULL;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100368 if (!l)
369 return;
370
Willy Tarreauf5090652021-04-06 17:23:40 +0200371 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100372 return;
373
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200374 rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100375 if (!rxbuf)
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100376 goto out;
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100377
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100378 buf = &rxbuf->buf;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100379
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200380 max_dgrams = global.tune.maxpollevents;
381 start:
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500382 /* Try to reuse an existing dgram. Note that there is always at
Frédéric Lécaille2bed1f12022-06-23 21:05:05 +0200383 * least one datagram to pick, except the first time we enter
384 * this function for this <rxbuf> buffer.
385 */
Amaury Denoyelle91b23052022-10-06 14:45:09 +0200386 new_dgram = quic_rxbuf_purge_dgrams(rxbuf);
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100387
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100388 params = &l->bind_conf->quic_params;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100389 max_sz = params->max_udp_payload_size;
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100390 cspace = b_contig_space(buf);
391 if (cspace < max_sz) {
Amaury Denoyelle735b44f2022-10-27 17:56:27 +0200392 struct proxy *px = l->bind_conf->frontend;
393 struct quic_counters *prx_counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, &quic_stats_module);
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100394 struct quic_dgram *dgram;
395
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200396 /* Do no mark <buf> as full, and do not try to consume it
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200397 * if the contiguous remaining space is not at the end
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200398 */
Amaury Denoyelle735b44f2022-10-27 17:56:27 +0200399 if (b_tail(buf) + cspace < b_wrap(buf)) {
400 HA_ATOMIC_INC(&prx_counters->rxbuf_full);
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200401 goto out;
Amaury Denoyelle735b44f2022-10-27 17:56:27 +0200402 }
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200403
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100404 /* Allocate a fake datagram, without data to locate
405 * the end of the RX buffer (required during purging).
406 */
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200407 dgram = pool_alloc(pool_head_quic_dgram);
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100408 if (!dgram)
409 goto out;
410
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200411 /* Initialize only the useful members of this fake datagram. */
412 dgram->buf = NULL;
Frédéric Lécaille1712b1d2022-01-28 13:10:24 +0100413 dgram->len = cspace;
Frédéric Lécailleba19acd2022-08-08 21:10:58 +0200414 /* Append this datagram only to the RX buffer list. It will
415 * not be treated by any datagram handler.
416 */
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200417 LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list);
Frédéric Lécaille0c535682022-06-23 17:47:10 +0200418
Frédéric Lécaille320744b2022-01-27 12:19:28 +0100419 /* Consume the remaining space */
420 b_add(buf, cspace);
Amaury Denoyelle735b44f2022-10-27 17:56:27 +0200421 if (b_contig_space(buf) < max_sz) {
422 HA_ATOMIC_INC(&prx_counters->rxbuf_full);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100423 goto out;
Amaury Denoyelle735b44f2022-10-27 17:56:27 +0200424 }
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100425 }
426
Frédéric Lécaillef6f75202022-02-02 09:44:22 +0100427 dgram_buf = (unsigned char *)b_tail(buf);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200428 ret = quic_recv(fd, dgram_buf, max_sz,
429 (struct sockaddr *)&saddr, sizeof(saddr),
430 (struct sockaddr *)&daddr, sizeof(daddr),
431 get_net_port(&l->rx.addr));
432 if (ret <= 0)
433 goto out;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100434
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100435 b_add(buf, ret);
Amaury Denoyelle97ecc7a2022-09-23 17:15:58 +0200436 if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr, &daddr,
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200437 new_dgram, &rxbuf->dgram_list)) {
Frédéric Lécaille37ae5052022-01-27 11:31:50 +0100438 /* If wrong, consume this datagram */
439 b_del(buf, ret);
440 }
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200441 new_dgram = NULL;
Frédéric Lécaille1b0707f2022-06-30 11:28:56 +0200442 if (--max_dgrams > 0)
443 goto start;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100444 out:
Frédéric Lécaille19ef6362022-06-23 18:00:37 +0200445 pool_free(pool_head_quic_dgram, new_dgram);
Amaury Denoyelle1cba8d62022-10-06 15:16:22 +0200446 MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100447}
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100448
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200449/* Send a datagram stored into <buf> buffer with <sz> as size.
450 * The caller must ensure there is at least <sz> bytes in this buffer.
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200451 *
452 * Returns 0 on success else non-zero.
453 *
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200454 * TODO standardize this function for a generic UDP sendto wrapper. This can be
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100455 * done by removing the <qc> arg and replace it with address/port.
456 */
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200457int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
458 int flags)
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100459{
460 ssize_t ret;
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100461
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200462 do {
463 ret = sendto(qc->li->rx.fd, b_peek(buf, b_head_ofs(buf)), sz,
464 MSG_DONTWAIT | MSG_NOSIGNAL,
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100465 (struct sockaddr *)&qc->peer_addr, get_addr_len(&qc->peer_addr));
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200466 } while (ret < 0 && errno == EINTR);
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100467
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200468 if (ret < 0 || ret != sz) {
Amaury Denoyelle1d9f1702022-10-24 10:03:33 +0200469 struct proxy *prx = qc->li->bind_conf->frontend;
470 struct quic_counters *prx_counters =
471 EXTRA_COUNTERS_GET(prx->extra_counters_fe,
472 &quic_stats_module);
473
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200474 /* TODO adjust errno for UDP context. */
475 if (errno == EAGAIN || errno == EWOULDBLOCK ||
476 errno == ENOTCONN || errno == EINPROGRESS || errno == EBADF) {
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200477 if (errno == EAGAIN || errno == EWOULDBLOCK)
478 HA_ATOMIC_INC(&prx_counters->socket_full);
479 else
480 HA_ATOMIC_INC(&prx_counters->sendto_err);
481 }
482 else if (errno) {
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500483 /* TODO unlisted errno : handle it explicitly. */
Amaury Denoyelle1d9f1702022-10-24 10:03:33 +0200484 HA_ATOMIC_INC(&prx_counters->sendto_err_unknown);
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200485 }
486
487 return 1;
Frédéric Lécaille48bb8752022-08-03 20:52:20 +0200488 }
489
Amaury Denoyelle6715cbf2022-08-05 11:56:36 +0200490 /* we count the total bytes sent, and the send rate for 32-byte blocks.
491 * The reason for the latter is that freq_ctr are limited to 4GB and
492 * that it's not enough per second.
493 */
494 _HA_ATOMIC_ADD(&global.out_bytes, ret);
495 update_freq_ctr(&global.out_32bps, (ret + 16) / 32);
496
497 return 0;
Amaury Denoyelle58a77042022-02-09 15:43:07 +0100498}
499
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100500
501/*********************** QUIC accept queue management ***********************/
502/* per-thread accept queues */
503struct quic_accept_queue *quic_accept_queues;
504
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100505/* 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 +0100506 * up. If <qc> accept is already scheduled or done, nothing is done.
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100507 */
508void quic_accept_push_qc(struct quic_conn *qc)
509{
510 struct quic_accept_queue *queue = &quic_accept_queues[qc->tid];
511 struct li_per_thread *lthr = &qc->li->per_thr[qc->tid];
512
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100513 /* early return if accept is already in progress/done for this
514 * connection
515 */
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200516 if (qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)
Frédéric Lécaille91f083a2022-01-28 21:43:48 +0100517 return;
518
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100519 BUG_ON(MT_LIST_INLIST(&qc->accept_list));
520
Frédéric Lécaillefc790062022-03-28 17:10:31 +0200521 qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
Amaury Denoyellecfa2d562022-01-19 16:01:05 +0100522 /* 1. insert the listener in the accept queue
523 *
524 * Use TRY_APPEND as there is a possible race even with INLIST if
525 * multiple threads try to add the same listener instance from several
526 * quic_conn.
527 */
528 if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
529 MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
530
531 /* 2. insert the quic_conn in the listener per-thread queue. */
532 MT_LIST_APPEND(&lthr->quic_accept.conns, &qc->accept_list);
533
534 /* 3. wake up the queue tasklet */
535 tasklet_wakeup(quic_accept_queues[qc->tid].tasklet);
536}
537
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100538/* Tasklet handler to accept QUIC connections. Call listener_accept on every
539 * listener instances registered in the accept queue.
540 */
Willy Tarreau41e701e2022-09-08 15:12:59 +0200541struct task *quic_accept_run(struct task *t, void *ctx, unsigned int i)
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100542{
543 struct li_per_thread *lthr;
544 struct mt_list *elt1, elt2;
545 struct quic_accept_queue *queue = &quic_accept_queues[tid];
546
547 mt_list_for_each_entry_safe(lthr, &queue->listeners, quic_accept.list, elt1, elt2) {
548 listener_accept(lthr->li);
549 MT_LIST_DELETE_SAFE(elt1);
550 }
551
552 return NULL;
553}
554
555static int quic_alloc_accept_queues(void)
556{
557 int i;
558
Tim Duesterhus9fb57e82022-06-01 21:58:37 +0200559 quic_accept_queues = calloc(global.nbthread,
560 sizeof(*quic_accept_queues));
Amaury Denoyelle2ce99fe2022-01-19 15:46:11 +0100561 if (!quic_accept_queues) {
562 ha_alert("Failed to allocate the quic accept queues.\n");
563 return 0;
564 }
565
566 for (i = 0; i < global.nbthread; ++i) {
567 struct tasklet *task;
568 if (!(task = tasklet_new())) {
569 ha_alert("Failed to allocate the quic accept queue on thread %d.\n", i);
570 return 0;
571 }
572
573 tasklet_set_tid(task, i);
574 task->process = quic_accept_run;
575 quic_accept_queues[i].tasklet = task;
576
577 MT_LIST_INIT(&quic_accept_queues[i].listeners);
578 }
579
580 return 1;
581}
582REGISTER_POST_CHECK(quic_alloc_accept_queues);
583
584static int quic_deallocate_accept_queues(void)
585{
586 int i;
587
588 if (quic_accept_queues) {
589 for (i = 0; i < global.nbthread; ++i)
590 tasklet_free(quic_accept_queues[i].tasklet);
591 free(quic_accept_queues);
592 }
593
594 return 1;
595}
596REGISTER_POST_DEINIT(quic_deallocate_accept_queues);