]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: TCP fast open connections
authorIwan Timmer <irtimmer@gmail.com>
Fri, 27 Apr 2018 11:20:31 +0000 (13:20 +0200)
committerIwan Timmer <irtimmer@gmail.com>
Mon, 11 Jun 2018 19:35:58 +0000 (21:35 +0200)
Add suport for TCP fast open connection to reduce latency for successive DNS request over TCP

src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stream.h
src/resolve/resolved-dns-stub.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-llmnr.c

index 763789c450f76d308e440e28b7a732c69e176f3a..b92bcd557ea98c2aec8113d8ac74243344ddef68 100644 (file)
@@ -306,10 +306,11 @@ static int dns_scope_socket(
                 int family,
                 const union in_addr_union *address,
                 DnsServer *server,
-                uint16_t port) {
+                uint16_t port,
+                union sockaddr_union *ret_socket_address) {
 
         _cleanup_close_ int fd = -1;
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
         socklen_t salen;
         static const int one = 1;
         int r, ifindex;
@@ -392,19 +393,27 @@ static int dns_scope_socket(
                 }
         }
 
-        r = connect(fd, &sa.sa, salen);
-        if (r < 0 && errno != EINPROGRESS)
-                return -errno;
+        if (ret_socket_address)
+                *ret_socket_address = sa;
+        else {
+                r = connect(fd, &sa.sa, salen);
+                if (r < 0 && errno != EINPROGRESS)
+                        return -errno;
+        }
 
         return TAKE_FD(fd);
 }
 
 int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
-        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
+        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port, NULL);
 }
 
-int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
-        return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) {
+        /* If ret_socket_address is not NULL, the caller is responisble
+         * for calling connect() or sendmsg(). This is required by TCP
+         * Fast Open, to be able to send the initial SYN packet along
+         * with the first data packet. */
+        return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address);
 }
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
index 15c58251d0dc36d26ed90601516b606e916a416e..0042193e2049ba6fc806b55ebda72ae1e7c71923 100644 (file)
@@ -75,7 +75,7 @@ void dns_scope_packet_received(DnsScope *s, usec_t rtt);
 void dns_scope_packet_lost(DnsScope *s, usec_t usec);
 
 int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
-int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address);
 int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
index f537fe6ab48cde4e0ab2ecdb80969860bf450672..5d2cc1ad8b4f4d493d818c9abc41f5faeb3da310 100644 (file)
@@ -182,6 +182,39 @@ static int dns_stream_identify(DnsStream *s) {
         return 0;
 }
 
+static ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt) {
+        ssize_t r;
+
+        assert(s);
+        assert(iov);
+
+        if (s->tfo_salen > 0) {
+                struct msghdr hdr = {
+                        .msg_iov = (struct iovec*) iov,
+                        .msg_iovlen = iovcnt,
+                        .msg_name = &s->tfo_address.sa,
+                        .msg_namelen = s->tfo_salen
+                };
+
+                r = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
+                if (r < 0) {
+                        if (errno == EOPNOTSUPP) {
+                                s->tfo_salen = 0;
+                                r = connect(s->fd, &s->tfo_address.sa, s->tfo_salen);
+                                if (r < 0)
+                                        return -errno;
+
+                                r = -EAGAIN;
+                        } else if (errno == EINPROGRESS)
+                                r = -EAGAIN;
+                } else
+                        s->tfo_salen = 0; /* connection is made */
+        } else
+                r = writev(s->fd, iov, iovcnt);
+
+        return r;
+}
+
 static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
         DnsStream *s = userdata;
 
@@ -196,9 +229,12 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
 
         assert(s);
 
-        r = dns_stream_identify(s);
-        if (r < 0)
-                return dns_stream_complete(s, -r);
+        /* only identify after connecting */
+        if (s->tfo_salen == 0) {
+                r = dns_stream_identify(s);
+                if (r < 0)
+                        return dns_stream_complete(s, -r);
+        }
 
         if ((revents & EPOLLOUT) &&
             s->write_packet &&
@@ -214,7 +250,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
 
                 IOVEC_INCREMENT(iov, 2, s->n_written);
 
-                ss = writev(fd, iov, 2);
+                ss = dns_stream_writev(s, iov, 2);
                 if (ss < 0) {
                         if (!IN_SET(errno, EINTR, EAGAIN))
                                 return dns_stream_complete(s, errno);
@@ -366,7 +402,7 @@ DnsStream *dns_stream_ref(DnsStream *s) {
         return s;
 }
 
-int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
+int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address) {
         _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
         int r;
 
@@ -408,6 +444,11 @@ int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) {
         LIST_PREPEND(streams, m->dns_streams, s);
         s->manager = m;
         s->fd = fd;
+        if (tfo_address) {
+                s->tfo_address = *tfo_address;
+                s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
+        }
+
         m->n_dns_streams++;
 
         *ret = TAKE_PTR(s);
index f392c13b0b5a3b8804cfe2b17ac7ca9fa233104c..5ba2bd1814421bbbd8fcfc882653e958db72b143 100644 (file)
@@ -37,6 +37,10 @@ struct DnsStream {
         uint32_t ttl;
         bool identified;
 
+        /* only when using TCP fast open */
+        union sockaddr_union tfo_address;
+        socklen_t tfo_salen;
+
         sd_event_source *io_event_source;
         sd_event_source *timeout_event_source;
 
@@ -55,7 +59,7 @@ struct DnsStream {
         LIST_FIELDS(DnsStream, streams);
 };
 
-int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd);
+int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address);
 DnsStream *dns_stream_unref(DnsStream *s);
 DnsStream *dns_stream_ref(DnsStream *s);
 
index 6b47a48df67681c4af628e8b2237cc1e459fbbc4..298d8132be0e6d0ecaf08f18ac9580f7b42d05ce 100644 (file)
@@ -469,7 +469,7 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
                 return -errno;
         }
 
-        r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd);
+        r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd, NULL);
         if (r < 0) {
                 safe_close(cfd);
                 return r;
index 5d3f6bae27809020efa1e56dd4b3f225899b4fde..5e4125cac50f3294c6c953e68f99d3376717331a 100644 (file)
@@ -557,6 +557,7 @@ static int dns_stream_on_packet(DnsStream *s) {
 static int dns_transaction_emit_tcp(DnsTransaction *t) {
         _cleanup_close_ int fd = -1;
         _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
+        union sockaddr_union sa;
         int r;
 
         assert(t);
@@ -580,14 +581,14 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
                 if (t->server->stream)
                         s = dns_stream_ref(t->server->stream);
                 else
-                        fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53);
+                        fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, 53, &sa);
 
                 break;
 
         case DNS_PROTOCOL_LLMNR:
                 /* When we already received a reply to this (but it was truncated), send to its sender address */
                 if (t->received)
-                        fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port);
+                        fd = dns_scope_socket_tcp(t->scope, t->received->family, &t->received->sender, NULL, t->received->sender_port, &sa);
                 else {
                         union in_addr_union address;
                         int family = AF_UNSPEC;
@@ -604,7 +605,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
                         if (family != t->scope->family)
                                 return -ESRCH;
 
-                        fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT);
+                        fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT, &sa);
                 }
 
                 break;
@@ -617,7 +618,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
                 if (fd < 0)
                         return fd;
 
-                r = dns_stream_new(t->scope->manager, &s, t->scope->protocol, fd);
+                r = dns_stream_new(t->scope->manager, &s, t->scope->protocol, fd, &sa);
                 if (r < 0)
                         return r;
 
index ca49df8090721e9d1d8e8486eba3151fc3ae1772..9eef59be0dfaaf3aee2961af4deca257447603ac 100644 (file)
@@ -345,7 +345,7 @@ static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *u
                 return -errno;
         }
 
-        r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
+        r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd, NULL);
         if (r < 0) {
                 safe_close(cfd);
                 return r;