summaryrefslogtreecommitdiffstats
path: root/src/quic_sock.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/quic_sock.c237
1 files changed, 136 insertions, 101 deletions
diff --git a/src/quic_sock.c b/src/quic_sock.c
index f796513..a3f3768 100644
--- a/src/quic_sock.c
+++ b/src/quic_sock.c
@@ -29,6 +29,7 @@
#include <haproxy/listener.h>
#include <haproxy/log.h>
#include <haproxy/pool.h>
+#include <haproxy/protocol-t.h>
#include <haproxy/proto_quic.h>
#include <haproxy/proxy-t.h>
#include <haproxy/quic_cid.h>
@@ -337,8 +338,8 @@ static struct quic_dgram *quic_rxbuf_purge_dgrams(struct quic_receiver_buf *rbuf
return prev;
}
-/* Receive data from datagram socket <fd>. Data are placed in <out> buffer of
- * length <len>.
+/* Receive a single message from datagram socket <fd>. Data are placed in <out>
+ * buffer of length <len>.
*
* Datagram addresses will be returned via the next arguments. <from> will be
* the peer address and <to> the reception one. Note that <to> can only be
@@ -393,6 +394,11 @@ static ssize_t quic_recv(int fd, void *out, size_t len,
if (ret < 0)
goto end;
+ if (unlikely(port_is_restricted((struct sockaddr_storage *)from, HA_PROTO_QUIC))) {
+ ret = -1;
+ goto end;
+ }
+
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
switch (cmsg->cmsg_level) {
case IPPROTO_IP:
@@ -566,6 +572,86 @@ void quic_conn_sock_fd_iocb(int fd)
TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
}
+static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg,
+ struct sockaddr_storage *saddr)
+{
+ struct cmsghdr *c;
+#ifdef IP_PKTINFO
+ struct in_pktinfo *in;
+#endif /* IP_PKTINFO */
+#ifdef IPV6_RECVPKTINFO
+ struct in6_pktinfo *in6;
+#endif /* IPV6_RECVPKTINFO */
+ size_t sz = 0;
+
+ /* First determine size of ancillary data depending on the system support. */
+ switch (saddr->ss_family) {
+ case AF_INET:
+#if defined(IP_PKTINFO)
+ sz = sizeof(struct in_pktinfo);
+#elif defined(IP_RECVDSTADDR)
+ sz = sizeof(struct in_addr);
+#endif /* IP_PKTINFO || IP_RECVDSTADDR */
+ break;
+ case AF_INET6:
+#ifdef IPV6_RECVPKTINFO
+ sz = sizeof(struct in6_pktinfo);
+#endif /* IPV6_RECVPKTINFO */
+ break;
+ default:
+ break;
+ }
+
+ /* Size is null if system does not support send source address setting. */
+ if (!sz)
+ return;
+
+ /* Set first msg_controllen to be able to use CMSG_* macros. */
+ msg->msg_controllen += CMSG_SPACE(sz);
+
+ *cmsg = !(*cmsg) ? CMSG_FIRSTHDR(msg) : CMSG_NXTHDR(msg, *cmsg);
+ ALREADY_CHECKED(*cmsg);
+ c = *cmsg;
+ c->cmsg_len = CMSG_LEN(sz);
+
+ switch (saddr->ss_family) {
+ case AF_INET:
+ c->cmsg_level = IPPROTO_IP;
+#if defined(IP_PKTINFO)
+ c->cmsg_type = IP_PKTINFO;
+ in = (struct in_pktinfo *)CMSG_DATA(c);
+ in->ipi_ifindex = 0;
+ in->ipi_addr.s_addr = 0;
+ memcpy(&in->ipi_spec_dst,
+ &((struct sockaddr_in *)saddr)->sin_addr,
+ sizeof(struct in_addr));
+#elif defined(IP_RECVDSTADDR)
+ c->cmsg_type = IP_SENDSRCADDR;
+ memcpy(CMSG_DATA(c),
+ &((struct sockaddr_in *)saddr)->sin_addr,
+ sizeof(struct in_addr));
+#endif /* IP_PKTINFO || IP_RECVDSTADDR */
+
+ break;
+
+ case AF_INET6:
+#ifdef IPV6_RECVPKTINFO
+ c->cmsg_level = IPPROTO_IPV6;
+ c->cmsg_type = IPV6_PKTINFO;
+ in6 = (struct in6_pktinfo *)CMSG_DATA(c);
+ in6->ipi6_ifindex = 0;
+ memcpy(&in6->ipi6_addr,
+ &((struct sockaddr_in6 *)saddr)->sin6_addr,
+ sizeof(struct in6_addr));
+#endif /* IPV6_RECVPKTINFO */
+
+ break;
+
+ default:
+ break;
+ }
+}
+
/* Send a datagram stored into <buf> buffer with <sz> as size.
* The caller must ensure there is at least <sz> bytes in this buffer.
*
@@ -581,106 +667,58 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
int flags)
{
ssize_t ret;
+ struct msghdr msg;
+ struct iovec vec;
+ struct cmsghdr *cmsg __maybe_unused = NULL;
- do {
- if (qc_test_fd(qc)) {
- if (!fd_send_ready(qc->fd))
- return 0;
-
- ret = send(qc->fd, b_peek(buf, b_head_ofs(buf)), sz,
- MSG_DONTWAIT | MSG_NOSIGNAL);
- }
-#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) || defined(IPV6_RECVPKTINFO)
- else if (is_addr(&qc->local_addr)) {
- struct msghdr msg = { 0 };
- struct iovec vec;
- struct cmsghdr *cmsg;
+ union {
#ifdef IP_PKTINFO
- struct in_pktinfo in;
+ char buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
#endif /* IP_PKTINFO */
#ifdef IPV6_RECVPKTINFO
- struct in6_pktinfo in6;
+ char buf6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif /* IPV6_RECVPKTINFO */
- union {
-#ifdef IP_PKTINFO
- char buf[CMSG_SPACE(sizeof(in))];
-#endif /* IP_PKTINFO */
-#ifdef IPV6_RECVPKTINFO
- char buf6[CMSG_SPACE(sizeof(in6))];
-#endif /* IPV6_RECVPKTINFO */
- char bufaddr[CMSG_SPACE(sizeof(struct in_addr))];
- struct cmsghdr align;
- } u;
-
- vec.iov_base = b_peek(buf, b_head_ofs(buf));
- vec.iov_len = sz;
- msg.msg_name = &qc->peer_addr;
- msg.msg_namelen = get_addr_len(&qc->peer_addr);
- msg.msg_iov = &vec;
- msg.msg_iovlen = 1;
-
- switch (qc->local_addr.ss_family) {
- case AF_INET:
-#if defined(IP_PKTINFO)
- memset(&in, 0, sizeof(in));
- memcpy(&in.ipi_spec_dst,
- &((struct sockaddr_in *)&qc->local_addr)->sin_addr,
- sizeof(struct in_addr));
-
- msg.msg_control = u.buf;
- msg.msg_controllen = sizeof(u.buf);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
- memcpy(CMSG_DATA(cmsg), &in, sizeof(in));
-#elif defined(IP_RECVDSTADDR)
- msg.msg_control = u.bufaddr;
- msg.msg_controllen = sizeof(u.bufaddr);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_SENDSRCADDR;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
- memcpy(CMSG_DATA(cmsg),
- &((struct sockaddr_in *)&qc->local_addr)->sin_addr,
- sizeof(struct in_addr));
-#endif /* IP_PKTINFO || IP_RECVDSTADDR */
- break;
+ char bufaddr[CMSG_SPACE(sizeof(struct in_addr))];
+ struct cmsghdr align;
+ } ancillary_data;
- case AF_INET6:
-#ifdef IPV6_RECVPKTINFO
- memset(&in6, 0, sizeof(in6));
- memcpy(&in6.ipi6_addr,
- &((struct sockaddr_in6 *)&qc->local_addr)->sin6_addr,
- sizeof(struct in6_addr));
-
- msg.msg_control = u.buf6;
- msg.msg_controllen = sizeof(u.buf6);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
- memcpy(CMSG_DATA(cmsg), &in6, sizeof(in6));
-#endif /* IPV6_RECVPKTINFO */
- break;
+ vec.iov_base = b_peek(buf, b_head_ofs(buf));
+ vec.iov_len = sz;
- default:
- break;
- }
+ /* man 2 sendmsg
+ *
+ * The msg_name field is used on an unconnected socket to specify the
+ * target address for a datagram. It points to a buffer containing the
+ * address; the msg_namelen field should be set to the size of the
+ * address. For a connected socket, these fields should be specified
+ * as NULL and 0, respectively.
+ */
+ if (!qc_test_fd(qc)) {
+ msg.msg_name = &qc->peer_addr;
+ msg.msg_namelen = get_addr_len(&qc->peer_addr);
+ }
+ else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
- ret = sendmsg(qc->li->rx.fd, &msg,
- MSG_DONTWAIT|MSG_NOSIGNAL);
- }
-#endif /* IP_PKTINFO || IP_RECVDSTADDR || IPV6_RECVPKTINFO */
- else {
- ret = sendto(qc->li->rx.fd, b_peek(buf, b_head_ofs(buf)), sz,
- MSG_DONTWAIT|MSG_NOSIGNAL,
- (struct sockaddr *)&qc->peer_addr,
- get_addr_len(&qc->peer_addr));
- }
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ if (qc_test_fd(qc) && !fd_send_ready(qc->fd))
+ return 0;
+
+ /* Set source address when using listener socket if possible. */
+ if (!qc_test_fd(qc) && is_addr(&qc->local_addr)) {
+ msg.msg_control = ancillary_data.bufaddr;
+ cmsg_set_saddr(&msg, &cmsg, &qc->local_addr);
+ }
+
+ do {
+ ret = sendmsg(qc_fd(qc), &msg, MSG_DONTWAIT|MSG_NOSIGNAL);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
@@ -740,7 +778,7 @@ int qc_rcv_buf(struct quic_conn *qc)
max_sz = params->max_udp_payload_size;
do {
- if (!b_alloc(&buf))
+ if (!b_alloc(&buf, DB_MUX_RX))
break; /* TODO subscribe for memory again available. */
b_reset(&buf);
@@ -967,18 +1005,15 @@ void qc_want_recv(struct quic_conn *qc)
struct quic_accept_queue *quic_accept_queues;
/* Install <qc> on the queue ready to be accepted. The queue task is then woken
- * up. If <qc> accept is already scheduled or done, nothing is done.
+ * up.
*/
void quic_accept_push_qc(struct quic_conn *qc)
{
struct quic_accept_queue *queue = &quic_accept_queues[tid];
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
- /* early return if accept is already in progress/done for this
- * connection
- */
- if (qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)
- return;
+ /* A connection must only be accepted once per instance. */
+ BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);