diff options
Diffstat (limited to 'src/quic_sock.c')
-rw-r--r-- | src/quic_sock.c | 237 |
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); |