blob: 761c2a4e8e03844f7693915a6072e0255c2e498b [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;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010038 /* This flag is ordinarily set by conn_ctrl_init() which cannot
39 * be called for now.
40 */
41 cli_conn->flags |= CO_FL_CTRL_READY;
42
43 /* wait for a PROXY protocol header */
44 if (l->options & LI_O_ACC_PROXY)
45 cli_conn->flags |= CO_FL_ACCEPT_PROXY;
46
47 /* wait for a NetScaler client IP insertion protocol header */
48 if (l->options & LI_O_ACC_CIP)
49 cli_conn->flags |= CO_FL_ACCEPT_CIP;
50
Frédéric Lécaille026a7922020-11-23 15:46:36 +010051 /* Add the handshake pseudo-XPRT */
52 if (cli_conn->flags & (CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP)) {
53 if (xprt_add_hs(cli_conn) != 0)
54 goto out_free_conn;
55 }
Olivier Houchard1b3c9312021-03-05 23:37:48 +010056
Frédéric Lécaille026a7922020-11-23 15:46:36 +010057 sess = session_new(p, l, &cli_conn->obj_type);
58 if (!sess)
59 goto out_free_conn;
60
61 conn_set_owner(cli_conn, sess, NULL);
62
Frédéric Lécailleecb58722021-05-27 17:12:36 +020063 if (conn_complete_session(cli_conn) < 0)
64 goto out_free_sess;
65
66 if (conn_xprt_start(cli_conn) >= 0)
Frédéric Lécaille27faba72021-03-03 16:21:00 +010067 return 1;
68
Frédéric Lécaille026a7922020-11-23 15:46:36 +010069 out_free_sess:
70 /* prevent call to listener_release during session_free. It will be
71 * done below, for all errors. */
72 sess->listener = NULL;
73 session_free(sess);
74 out_free_conn:
75 cli_conn->qc->conn = NULL;
76 conn_stop_tracking(cli_conn);
77 conn_xprt_close(cli_conn);
78 conn_free(cli_conn);
79 out:
80
Frédéric Lécaillee8139f32021-03-11 17:06:30 +010081 return -1;
Frédéric Lécaille026a7922020-11-23 15:46:36 +010082}
83
84/*
85 * Inspired from session_accept_fd().
86 * Instantiate a new connection (connection struct) to be attached to <qc>
87 * QUIC connection of <l> listener.
88 * Returns 1 if succeeded, 0 if not.
89 */
90static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
91 struct sockaddr_storage *saddr)
92{
93 struct connection *cli_conn;
94 struct sockaddr_storage *dst;
95
96 dst = NULL;
97 if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
98 goto out;
99
100 if (!sockaddr_alloc(&dst, saddr, sizeof *saddr))
101 goto out_free_conn;
102
103 qc->conn = cli_conn;
104 cli_conn->qc = qc;
105
106 cli_conn->dst = dst;
107 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
110 /* XXX Should not be there. */
111 l->accept = quic_session_accept;
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200112 /* We need the xprt context before accepting (->accept()) the connection:
113 * we may receive packet before this connection acception.
114 */
115 if (conn_prepare(cli_conn, l->rx.proto, l->bind_conf->xprt) < 0)
116 goto out_free_conn;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100117
118 return 1;
119
120 out_free_conn:
Frédéric Lécaille01ab6612021-06-14 10:31:43 +0200121 qc->conn = NULL;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100122 conn_stop_tracking(cli_conn);
123 conn_xprt_close(cli_conn);
124 conn_free(cli_conn);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100125 out:
126
127 return 0;
128}
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100129
130/* Tests if the receiver supports accepting connections. Returns positive on
131 * success, 0 if not possible
132 */
133int quic_sock_accepting_conn(const struct receiver *rx)
134{
135 return 1;
136}
137
138/* Accept an incoming connection from listener <l>, and return it, as well as
139 * a CO_AC_* status code into <status> if not null. Null is returned on error.
140 * <l> must be a valid listener with a valid frontend.
141 */
142struct connection *quic_sock_accept_conn(struct listener *l, int *status)
143{
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100144 struct quic_conn *qc;
145 struct quic_rx_packet *pkt;
Frédéric Lécaille3d77fa72021-05-31 09:30:14 +0200146 int ret;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100147
148 qc = NULL;
Frédéric Lécaillec28aba22021-06-07 10:28:10 +0200149 pkt = MT_LIST_POP(&l->rx.pkts, struct quic_rx_packet *, rx_list);
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100150 /* Should never happen. */
Frédéric Lécaillec28aba22021-06-07 10:28:10 +0200151 if (!pkt)
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100152 goto err;
153
154 qc = pkt->qc;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100155 if (!new_quic_cli_conn(qc, l, &pkt->saddr))
156 goto err;
157
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100158 ret = CO_AC_DONE;
159
160 done:
161 if (status)
162 *status = ret;
163
164 return qc ? qc->conn : NULL;
165
166 err:
167 ret = CO_AC_PAUSE;
168 goto done;
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100169}
170
171/* Function called on a read event from a listening socket. It tries
172 * to handle as many connections as possible.
173 */
174void quic_sock_fd_iocb(int fd)
175{
176 ssize_t ret;
177 struct buffer *buf;
178 struct listener *l = objt_listener(fdtab[fd].owner);
179 /* Source address */
180 struct sockaddr_storage saddr = {0};
181 socklen_t saddrlen;
182
Tim Duesterhus16554242021-09-15 13:58:49 +0200183 BUG_ON(!l);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100184
Willy Tarreauf5090652021-04-06 17:23:40 +0200185 if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100186 return;
187
188 buf = get_trash_chunk();
189 saddrlen = sizeof saddr;
190 do {
191 ret = recvfrom(fd, buf->area, buf->size, 0,
192 (struct sockaddr *)&saddr, &saddrlen);
193 if (ret < 0) {
194 if (errno == EINTR)
195 continue;
196 if (errno == EAGAIN)
197 fd_cant_recv(fd);
198 return;
199 }
200 } while (0);
201
202 buf->data = ret;
Frédéric Lécaille026a7922020-11-23 15:46:36 +0100203 quic_lstnr_dgram_read(buf->area, buf->data, l, &saddr);
Frédéric Lécaille70da8892020-11-06 15:49:49 +0100204}