blob: 38a8aab4b558f103fb9912fed24f420183ea741a [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 Denoyelleeb01f592021-10-07 16:44:05 +020020#include <haproxy/session.h>
Frédéric Lécaille026a7922020-11-23 15:46:36 +010021#include <haproxy/xprt_quic.h>
22
23/* This function is called from the protocol layer accept() in order to
24 * instantiate a new session on behalf of a given listener and frontend. It
25 * returns a positive value upon success, 0 if the connection can be ignored,
26 * or a negative value upon critical failure. The accepted connection is
27 * closed if we return <= 0. If no handshake is needed, it immediately tries
28 * to instantiate a new stream. The connection must already have been filled
29 * with the incoming connection handle (a fd), a target (the listener) and a
30 * source address.
31 */
32int quic_session_accept(struct connection *cli_conn)
33{
34 struct listener *l = __objt_listener(cli_conn->target);
35 struct proxy *p = l->bind_conf->frontend;
36 struct session *sess;
37
38 cli_conn->proxy_netns = l->rx.settings->netns;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010039 /* This flag is ordinarily set by conn_ctrl_init() which cannot
40 * be called for now.
41 */
42 cli_conn->flags |= CO_FL_CTRL_READY;
43
44 /* wait for a PROXY protocol header */
45 if (l->options & LI_O_ACC_PROXY)
46 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
47
48 /* wait for a NetScaler client IP insertion protocol header */
49 if (l->options & LI_O_ACC_CIP)
50 cli_conn->flags |= CO_FL_ACCEPT_CIP;
51
Frédéric Lécaille026a7922020-11-23 15:46:36 +010052 /* Add the handshake pseudo-XPRT */
53 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
54 if (xprt_add_hs(cli_conn) != 0)
55 goto out_free_conn;
56 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010057
Frédéric Lécaille026a7922020-11-23 15:46:36 +010058 sess = session_new(p, l, &cli_conn->obj_type);
59 if (!sess)
60 goto out_free_conn;
61
62 conn_set_owner(cli_conn, sess, NULL);
63
Frédéric Lécailleecb58722021-05-27 17:12:36 +020064 if (conn_complete_session(cli_conn) < 0)
65 goto out_free_sess;
66
67 if (conn_xprt_start(cli_conn) >= 0)
Frédéric Lécaille27faba72021-03-03 16:21:00 +010068 return 1;
69
Frédéric Lécaille026a7922020-11-23 15:46:36 +010070 out_free_sess:
71 /* prevent call to listener_release during session_free. It will be
72 * done below, for all errors. */
73 sess->listener = NULL;
74 session_free(sess);
75 out_free_conn:
76 cli_conn->qc->conn = NULL;
77 conn_stop_tracking(cli_conn);
78 conn_xprt_close(cli_conn);
79 conn_free(cli_conn);
80 out:
81
Frédéric Lécaillee8139f32021-03-11 17:06:30 +010082 return -1;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010083}
84
85/*
86 * Inspired from session_accept_fd().
87 * Instantiate a new connection (connection struct) to be attached to <qc>
88 * QUIC connection of <l> listener.
89 * Returns 1 if succeeded, 0 if not.
90 */
91static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
92 struct sockaddr_storage *saddr)
93{
94 struct connection *cli_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010095
Frédéric Lécaille026a7922020-11-23 15:46:36 +010096 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
97 goto out;
98
Frédéric Lécailled169efe2021-11-05 11:40:50 +010099 if (!sockaddr_alloc(&cli_conn->dst, saddr, sizeof *saddr))
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100100 goto out_free_conn;
101
Frédéric Lécailled169efe2021-11-05 11:40:50 +0100102 cli_conn->flags |= CO_FL_ADDR_TO_SET;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100103 qc->conn = cli_conn;
104 cli_conn->qc = qc;
105
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100106 cli_conn->handle.fd = l->rx.fd;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100107 cli_conn->target = &l->obj_type;
108
109 /* XXX Should not be there. */
110 l->accept = quic_session_accept;
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200111 /* We need the xprt context before accepting (->accept()) the connection:
112 * we may receive packet before this connection acception.
113 */
114 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
115 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100116
117 return 1;
118
119 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200120 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100121 conn_stop_tracking(cli_conn);
122 conn_xprt_close(cli_conn);
123 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100124 out:
125
126 return 0;
127}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100128
129/* Tests if the receiver supports accepting connections. Returns positive on
130 * success, 0 if not possible
131 */
132int quic_sock_accepting_conn(const struct receiver *rx)
133{
134 return 1;
135}
136
137/* Accept an incoming connection from listener <l>, and return it, as well as
138 * a CO_AC_* status code into <status> if not null. Null is returned on error.
139 * <l> must be a valid listener with a valid frontend.
140 */
141struct connection *quic_sock_accept_conn(struct listener *l, int *status)
142{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100143 struct quic_conn *qc;
144 struct quic_rx_packet *pkt;
Frédéric Lécaille3d77fa72021-05-31 09:30:14 +0200145 int ret;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100146
147 qc = NULL;
Frédéric Lécaillec28aba22021-06-07 10:28:10 +0200148 pkt = MT_LIST_POP(&l->rx.pkts, struct quic_rx_packet *, rx_list);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100149 /* Should never happen. */
Frédéric Lécaillec28aba22021-06-07 10:28:10 +0200150 if (!pkt)
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100151 goto err;
152
153 qc = pkt->qc;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100154 if (!new_quic_cli_conn(qc, l, &pkt->saddr))
155 goto err;
156
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100157 ret = CO_AC_DONE;
158
159 done:
160 if (status)
161 *status = ret;
162
163 return qc ? qc->conn : NULL;
164
165 err:
166 ret = CO_AC_PAUSE;
167 goto done;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100168}
169
170/* Function called on a read event from a listening socket. It tries
171 * to handle as many connections as possible.
172 */
173void quic_sock_fd_iocb(int fd)
174{
175 ssize_t ret;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100176 struct rxbuf *rxbuf;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100177 struct buffer *buf;
178 struct listener *l = objt_listener(fdtab[fd].owner);
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100179 struct quic_transport_params *params;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100180 /* Source address */
181 struct sockaddr_storage saddr = {0};
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100182 size_t max_sz;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100183 socklen_t saddrlen;
184
Tim Duesterhus16554242021-09-15 13:58:49 +0200185 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100186
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100187 if (!l)
188 return;
189
Willy Tarreauf5090652021-04-06 17:23:40 +0200190 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100191 return;
192
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100193 rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), mt_list);
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100194 if (!rxbuf)
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100195 goto out;
Amaury Denoyelleee72a432021-11-19 15:49:29 +0100196 buf = &rxbuf->buf;
Frédéric Lécaillec4becf52021-11-08 11:23:17 +0100197
198 params = &l->bind_conf->quic_params;
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100199 max_sz = params->max_udp_payload_size;
200 if (b_contig_space(buf) < max_sz) {
201 /* Note that when we enter this function, <buf> is always empty */
202 b_reset(buf);
203 if (b_contig_space(buf) < max_sz)
204 goto out;
205 }
206
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100207 saddrlen = sizeof saddr;
208 do {
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100209 ret = recvfrom(fd, b_tail(buf), max_sz, 0,
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100210 (struct sockaddr *)&saddr, &saddrlen);
211 if (ret < 0) {
212 if (errno == EINTR)
213 continue;
214 if (errno == EAGAIN)
215 fd_cant_recv(fd);
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100216 goto out;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100217 }
218 } while (0);
219
Frédéric Lécaille324ecda2021-11-02 10:14:44 +0100220 b_add(buf, ret);
221 quic_lstnr_dgram_read(buf, ret, l, &saddr);
222 b_del(buf, ret);
223 out:
224 MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->mt_list);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100225}