blob: 8ded8289038c7ebdf55ee651d728ad45ba597bd4 [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>
Frédéric Lécaille026a7922020-11-23 15:46:36 +010020#include <haproxy/xprt_quic.h>
21
22/* This function is called from the protocol layer accept() in order to
23 * instantiate a new session on behalf of a given listener and frontend. It
24 * returns a positive value upon success, 0 if the connection can be ignored,
25 * or a negative value upon critical failure. The accepted connection is
26 * closed if we return <= 0. If no handshake is needed, it immediately tries
27 * to instantiate a new stream. The connection must already have been filled
28 * with the incoming connection handle (a fd), a target (the listener) and a
29 * source address.
30 */
31int quic_session_accept(struct connection *cli_conn)
32{
33 struct listener *l = __objt_listener(cli_conn->target);
34 struct proxy *p = l->bind_conf->frontend;
35 struct session *sess;
36
37 cli_conn->proxy_netns = l->rx.settings->netns;
Olivier Houchard1b3c9312021-03-05 23:37:48 +010038 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
39 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010040
41 /* This flag is ordinarily set by conn_ctrl_init() which cannot
42 * be called for now.
43 */
44 cli_conn->flags |= CO_FL_CTRL_READY;
45
46 /* wait for a PROXY protocol header */
47 if (l->options & LI_O_ACC_PROXY)
48 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
49
50 /* wait for a NetScaler client IP insertion protocol header */
51 if (l->options & LI_O_ACC_CIP)
52 cli_conn->flags |= CO_FL_ACCEPT_CIP;
53
Frédéric Lécaille026a7922020-11-23 15:46:36 +010054 /* Add the handshake pseudo-XPRT */
55 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
56 if (xprt_add_hs(cli_conn) != 0)
57 goto out_free_conn;
58 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010059
Olivier Houchard26c51092021-03-19 20:09:22 +010060 if (conn_xprt_start(cli_conn) < 0)
Olivier Houchard1b3c9312021-03-05 23:37:48 +010061 goto out_free_conn;
62
Frédéric Lécaille026a7922020-11-23 15:46:36 +010063 sess = session_new(p, l, &cli_conn->obj_type);
64 if (!sess)
65 goto out_free_conn;
66
67 conn_set_owner(cli_conn, sess, NULL);
68
69 return 1;
70
71 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
83 return 0;
84}
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;
96 struct sockaddr_storage *dst;
97
98 dst = NULL;
99 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
100 goto out;
101
102 if (!sockaddr_alloc(&dst, saddr, sizeof *saddr))
103 goto out_free_conn;
104
105 qc->conn = cli_conn;
106 cli_conn->qc = qc;
107
108 cli_conn->dst = dst;
109 cli_conn->handle.fd = l->rx.fd;
110 cli_conn->flags |= CO_FL_ADDR_FROM_SET;
111 cli_conn->target = &l->obj_type;
112
113 /* XXX Should not be there. */
114 l->accept = quic_session_accept;
115
116 return 1;
117
118 out_free_conn:
119 conn_stop_tracking(cli_conn);
120 conn_xprt_close(cli_conn);
121 conn_free(cli_conn);
122 qc->conn = NULL;
123 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;
143 struct quic_rx_packet *pkt;
144 struct quic_cid *odcid;
145 int ret, ipv4;
146
147 qc = NULL;
148 pkt = LIST_ELEM(l->rx.qpkts.n, struct quic_rx_packet *, rx_list);
149 /* Should never happen. */
150 if (&pkt->rx_list == &l->rx.qpkts)
151 goto err;
152
153 qc = pkt->qc;
154 LIST_DEL(&pkt->rx_list);
155 if (!new_quic_cli_conn(qc, l, &pkt->saddr))
156 goto err;
157
158 ipv4 = pkt->saddr.ss_family == AF_INET;
159 if (!qc_new_conn_init(qc, ipv4, &l->rx.odcids, &l->rx.cids,
160 pkt->dcid.data, pkt->dcid.len,
161 pkt->scid.data, pkt->scid.len))
162 goto err;
163
164 odcid = &qc->params.original_destination_connection_id;
165 /* Copy the transport parameters. */
166 qc->params = l->bind_conf->quic_params;
167 /* Copy original_destination_connection_id transport parameter. */
168 memcpy(odcid->data, &pkt->dcid, pkt->odcid_len);
169 odcid->len = pkt->odcid_len;
170 /* Copy the initial source connection ID. */
171 quic_cid_cpy(&qc->params.initial_source_connection_id, &qc->scid);
172 qc->enc_params_len =
173 quic_transport_params_encode(qc->enc_params,
174 qc->enc_params + sizeof qc->enc_params,
175 &qc->params, 1);
176 if (!qc->enc_params_len)
177 goto err;
178
179 ret = CO_AC_DONE;
180
181 done:
182 if (status)
183 *status = ret;
184
185 return qc ? qc->conn : NULL;
186
187 err:
188 ret = CO_AC_PAUSE;
189 goto done;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100190}
191
192/* Function called on a read event from a listening socket. It tries
193 * to handle as many connections as possible.
194 */
195void quic_sock_fd_iocb(int fd)
196{
197 ssize_t ret;
198 struct buffer *buf;
199 struct listener *l = objt_listener(fdtab[fd].owner);
200 /* Source address */
201 struct sockaddr_storage saddr = {0};
202 socklen_t saddrlen;
203
204 if (!l)
205 ABORT_NOW();
206
Willy Tarreauf5090652021-04-06 17:23:40 +0200207 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100208 return;
209
210 buf = get_trash_chunk();
211 saddrlen = sizeof saddr;
212 do {
213 ret = recvfrom(fd, buf->area, buf->size, 0,
214 (struct sockaddr *)&saddr, &saddrlen);
215 if (ret < 0) {
216 if (errno == EINTR)
217 continue;
218 if (errno == EAGAIN)
219 fd_cant_recv(fd);
220 return;
221 }
222 } while (0);
223
224 buf->data = ret;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100225 quic_lstnr_dgram_read(buf->area, buf->data, l, &saddr);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100226}