From 2d380926bad1673f79842d3260372bb3f7707c58 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Thu, 19 Jan 2023 18:05:54 +0100 Subject: [PATCH] MEDIUM: quic-sock: fix udp source address for send on listener socket When receiving a QUIC datagram, destination address is retrieved via recvmsg() and stored in quic-conn as qc.local_addr. This address is then reused when using the quic-conn owned socket. When listener socket mode is preferred, send operation did not specify the source address of the emitted datagram. If listener socket is bound on a wildcard address, the kernel is free to choose any address assigned to the local machine. This may be different from the address selected by the client on its first datagram which will prevent the client to emit next replies. To address this, this patch fixes the UDP source address via sendmsg(). This process is similar to the reception and relies on ancillary message, so the socket is left untouched after the operation. This is heavily platform specific and may not be supported by some kernels. This change has only an impact if listener socket only is used for QUIC communications. This is the default behavior for 2.7 branch but not anymore on 2.8. Use tune.quic.socket-owner set to listener to ensure set it. This should be backported up to 2.7. --- src/quic_sock.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/quic_sock.c b/src/quic_sock.c index ba8d36f44..068541f89 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -516,6 +516,91 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, 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; +#ifdef IP_PKTINFO + struct in_pktinfo in; +#endif /* IP_PKTINFO */ +#ifdef IPV6_RECVPKTINFO + struct in6_pktinfo in6; +#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; + + 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; + + default: + break; + } + + 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,