]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic: add GSO parameter on quic_sock send API
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 28 May 2024 13:04:45 +0000 (15:04 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 11 Jul 2024 09:02:44 +0000 (11:02 +0200)
Add <gso_size> parameter to qc_snd_buf(). When non-null, this specifies
the value for socket option SOL_UDP/UDP_SEGMENT. This allows to send
several datagrams in a single call by splitting data multiple times at
<gso_size> boundary.

For now, <gso_size> remains set to 0 by caller, as such there should not
be any functional change.

include/haproxy/quic_sock.h
src/quic_conn.c
src/quic_sock.c
src/quic_tx.c

index 7236147526232edffc364972b3cefb080dc1b761..de741902192bb0a1a12d22ae4ec3e6805ec50c77 100644 (file)
@@ -44,7 +44,7 @@ struct connection *quic_sock_accept_conn(struct listener *l, int *status);
 struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state);
 void quic_lstnr_sock_fd_iocb(int fd);
 int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count,
-               int flags);
+               int flags, uint16_t gso_size);
 int qc_rcv_buf(struct quic_conn *qc);
 void quic_conn_sock_fd_iocb(int fd);
 
index 680158cdb3a50b652ab243f343aceabb8c557dbd..f04ed727c28a2a011906c3df3efbe4c6afa9cf71 100644 (file)
@@ -654,7 +654,7 @@ static struct task *quic_conn_closed_io_cb(struct task *t, void *context, unsign
 
        buf = b_make(cc_qc->cc_buf_area + headlen,
                     QUIC_MAX_CC_BUFSIZE - headlen, 0, cc_qc->cc_dgram_len);
-       if (qc_snd_buf(qc, &buf, buf.data, 0) < 0) {
+       if (qc_snd_buf(qc, &buf, buf.data, 0, 0) < 0) {
                TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_IO_CB, qc);
                quic_release_cc_conn(cc_qc);
                cc_qc = NULL;
index 06b65e083d2e206ce833fff1499d5cd2a35a8d9f..b0153822ddb4e7752b3b7081caa5dcca2733b6f2 100644 (file)
@@ -16,6 +16,7 @@
 #include <string.h>
 
 #include <netinet/in.h>
+#include <netinet/udp.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 
@@ -652,8 +653,33 @@ static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg,
        }
 }
 
-/* Send a datagram stored into <buf> buffer with <sz> as size.
- * The caller must ensure there is at least <sz> bytes in this buffer.
+static void cmsg_set_gso(struct msghdr *msg, struct cmsghdr **cmsg,
+                         uint16_t gso_size)
+{
+#ifdef UDP_SEGMENT
+       struct cmsghdr *c;
+       size_t sz = sizeof(gso_size);
+
+       /* 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);
+
+       c->cmsg_level = SOL_UDP;
+       c->cmsg_type = UDP_SEGMENT;
+       c->cmsg_len = CMSG_LEN(sz);
+       *((uint16_t *)CMSG_DATA(c)) = gso_size;
+#endif
+}
+
+/* Send a datagram stored into <buf> buffer with <sz> as size. The caller must
+ * ensure there is at least <sz> bytes in this buffer.
+ *
+ * If <gso_size> is non null, it will be used as value for UDP_SEGMENT option.
+ * This allows to transmit multiple datagrams in a single syscall.
  *
  * Returns the total bytes sent over the socket. 0 is returned if a transient
  * error is encountered which allows send to be retry later. A negative value
@@ -664,7 +690,7 @@ static void cmsg_set_saddr(struct msghdr *msg, struct cmsghdr **cmsg,
  * done by removing the <qc> arg and replace it with address/port.
  */
 int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
-               int flags)
+               int flags, uint16_t gso_size)
 {
        ssize_t ret;
        struct msghdr msg;
@@ -673,15 +699,25 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
 
        union {
 #ifdef IP_PKTINFO
-               char buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
+               char buf[CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(gso_size))];
 #endif /* IP_PKTINFO */
 #ifdef IPV6_RECVPKTINFO
-               char buf6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+               char buf6[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(gso_size))];
 #endif /* IPV6_RECVPKTINFO */
-               char bufaddr[CMSG_SPACE(sizeof(struct in_addr))];
+               char bufaddr[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(gso_size))];
                struct cmsghdr align;
        } ancillary_data;
 
+       /* man 3 cmsg
+        *
+        * When initializing a buffer that will contain a
+        * series of cmsghdr structures (e.g., to be sent with
+        * sendmsg(2)), that buffer should first be
+        * zero-initialized to ensure the correct operation of
+        * CMSG_NXTHDR().
+        */
+       memset(&ancillary_data, 0, sizeof(ancillary_data));
+
        vec.iov_base = b_peek(buf, b_head_ofs(buf));
        vec.iov_len = sz;
 
@@ -717,6 +753,16 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
                cmsg_set_saddr(&msg, &cmsg, &qc->local_addr);
        }
 
+       /* Set GSO parameter if datagram size is bigger than MTU. */
+       if (gso_size) {
+               /* GSO size must be less than total data to sent for multiple datagrams. */
+               BUG_ON_HOT(b_data(buf) <= gso_size);
+
+               if (!msg.msg_control)
+                       msg.msg_control = ancillary_data.bufaddr;
+               cmsg_set_gso(&msg, &cmsg, gso_size);
+       }
+
        do {
                ret = sendmsg(qc_fd(qc), &msg, MSG_DONTWAIT|MSG_NOSIGNAL);
        } while (ret < 0 && errno == EINTR);
index bff49ac4ce7ef4e3e75b845c7fdc38ac1169ca42..3d8ae6815f918dc6cdf18f8a9f06c25e4fee8678 100644 (file)
@@ -300,7 +300,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
 
                TRACE_PROTO("TX dgram", QUIC_EV_CONN_SPPKTS, qc);
                if (!skip_sendto) {
-                       int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0);
+                       int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, 0);
                        if (ret < 0) {
                                TRACE_ERROR("sendto fatal error", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
                                qc_kill_conn(qc);