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;
#include <string.h>
#include <netinet/in.h>
+#include <netinet/udp.h>
#include <sys/socket.h>
#include <sys/types.h>
}
}
-/* 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
* 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;
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;
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);