diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 119adf88c..1a4571d2b 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -625,6 +625,8 @@ struct quic_conn { const struct quic_version *negotiated_version; /* Negotiated version Initial TLS context */ struct quic_tls_ctx negotiated_ictx; + /* Connection owned socket FD. */ + int fd; /* QUIC transport parameters TLS extension */ int tps_tls_ext; /* Thread ID this connection is attached to */ diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index eeec93223..2788a2305 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -32,6 +32,7 @@ #include #include #include +#include #include int quic_session_accept(struct connection *cli_conn); @@ -45,6 +46,24 @@ void quic_sock_fd_iocb(int fd); int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count, int flags); +/* Set default value for socket as uninitialized. */ +static inline void qc_init_fd(struct quic_conn *qc) +{ + qc->fd = -1; +} + +/* Returns true if socket is initialized else false. */ +static inline char qc_test_fd(struct quic_conn *qc) +{ + /* quic-conn socket should not be accessed once it has been released. */ + BUG_ON(qc->fd == DEAD_FD_MAGIC); + return qc->fd >= 0; +} + +void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src, + const struct sockaddr_storage *dst); +void qc_release_fd(struct quic_conn *qc); + void quic_accept_push_qc(struct quic_conn *qc); #endif /* USE_QUIC */ diff --git a/src/quic_conn.c b/src/quic_conn.c index 5a011eb5f..5c48b55d3 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -4817,6 +4817,15 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, goto err; } + if ((global.tune.options & GTUNE_QUIC_SOCK_PER_CONN) && + is_addr(local_addr)) { + TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc); + qc_alloc_fd(qc, local_addr, peer_addr); + } + else { + qc_init_fd(qc); + } + /* insert the allocated CID in the receiver datagram handler tree */ if (server) ebmb_insert(&quic_dghdlrs[tid].cids, &icid->node, icid->cid.len); @@ -4936,6 +4945,9 @@ void quic_conn_release(struct quic_conn *qc) /* We must not free the quic-conn if the MUX is still allocated. */ BUG_ON(qc->mux_state == QC_MUX_READY); + /* Close quic-conn socket fd. */ + qc_release_fd(qc); + /* in the unlikely (but possible) case the connection was just added to * the accept_list we must delete it from there. */ diff --git a/src/quic_sock.c b/src/quic_sock.c index 10e8b3f9c..bce8a1b1a 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -531,6 +532,81 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, return 0; } +/* Allocate a socket file-descriptor specific for QUIC connection . + * Endpoint addresses are specified by the two following arguments : is + * the local address and is the remote one. + * + * Return the socket FD or a negative error code. On error, socket is marked as + * uninitialized. + */ +void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src, + const struct sockaddr_storage *dst) +{ + struct proxy *p = qc->li->bind_conf->frontend; + int fd = -1; + int ret; + + /* Must not happen. */ + BUG_ON(src->ss_family != dst->ss_family); + + qc_init_fd(qc); + + fd = socket(src->ss_family, SOCK_DGRAM, 0); + if (fd < 0) + goto err; + + if (fd >= global.maxsock) { + send_log(p, LOG_EMERG, + "Proxy %s reached the configured maximum connection limit. Please check the global 'maxconn' value.\n", + p->id); + goto err; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (ret < 0) + goto err; + + switch (src->ss_family) { + case AF_INET: +#if defined(IP_PKTINFO) + ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); +#elif defined(IP_RECVDSTADDR) + ret = setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one)); +#endif /* IP_PKTINFO || IP_RECVDSTADDR */ + break; + case AF_INET6: +#ifdef IPV6_RECVPKTINFO + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); +#endif + break; + } + if (ret < 0) + goto err; + + ret = bind(fd, (struct sockaddr *)src, get_addr_len(src)); + if (ret < 0) + goto err; + + ret = connect(fd, (struct sockaddr *)dst, get_addr_len(dst)); + if (ret < 0) + goto err; + + qc->fd = fd; + fd_set_nonblock(fd); + + return; + + err: + if (fd >= 0) + close(fd); +} + +/* Release socket file-descriptor specific for QUIC connection . */ +void qc_release_fd(struct quic_conn *qc) +{ + if (qc_test_fd(qc)) + qc->fd = DEAD_FD_MAGIC; +} /*********************** QUIC accept queue management ***********************/ /* per-thread accept queues */