diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | man/systemd.netdev.xml | 10 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/netlink-internal.h | 2 | ||||
-rw-r--r-- | src/libsystemd/sd-netlink/netlink-message.c | 48 | ||||
-rw-r--r-- | src/network/networkd-netdev-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-netdev-tuntap.c | 3 | ||||
-rw-r--r-- | src/network/networkd-netdev-tuntap.h | 1 | ||||
-rw-r--r-- | src/resolve-host/resolve-host.c | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.c | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-packet.h | 3 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.c | 24 | ||||
-rw-r--r-- | src/resolve/resolved-dns-scope.h | 4 | ||||
-rw-r--r-- | src/resolve/resolved-dns-server.c | 44 | ||||
-rw-r--r-- | src/resolve/resolved-dns-server.h | 7 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 131 | ||||
-rw-r--r-- | src/resolve/resolved-dns-transaction.h | 12 | ||||
-rw-r--r-- | src/resolve/resolved-link.c | 23 | ||||
-rw-r--r-- | src/resolve/resolved-manager.c | 121 | ||||
-rw-r--r-- | src/resolve/resolved-manager.h | 9 |
19 files changed, 278 insertions, 175 deletions
@@ -351,8 +351,6 @@ Features: - edns0 - dname - cname on PTR (?) - - maybe randomize DNS UDP source ports - - maybe compare query section of DNS replies * Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 01c31c5ef5..ff7b85453f 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -563,6 +563,16 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>VnetHeader=</varname></term> + <listitem><para>Takes a boolean argument. Configures + IFF_VNET_HDR flag for a tap device. It allows sending + and receiving larger Generic Segmentation Offload (GSO) + packets. This may increase throughput significantly. + Defaults to + <literal>no</literal>.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>User=</varname></term> <listitem><para>User to grant access to the <filename>/dev/net/tun</filename> device.</para> diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 6f51ebe73d..4026e2c341 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -94,6 +94,8 @@ struct sd_netlink { struct netlink_attribute { size_t offset; /* offset from hdr to attribute */ + bool nested:1; + bool net_byteorder:1; }; struct netlink_container { diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 13573dcea8..b0ed2f2882 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -38,6 +38,7 @@ #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) +#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { sd_netlink_message *m; @@ -475,7 +476,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m) { return 0; } -static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data) { +static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) { struct netlink_attribute *attribute; struct rtattr *rta; @@ -495,6 +496,9 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t *data = RTA_DATA(rta); + if (net_byteorder) + *net_byteorder = attribute->net_byteorder; + return RTA_PAYLOAD(rta); } @@ -508,7 +512,7 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if (strnlen(attr_data, r) >= (size_t) r) @@ -530,7 +534,7 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8 if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if ((size_t) r < sizeof(uint8_t)) @@ -543,8 +547,9 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8 } int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { - int r; void *attr_data; + bool net_byteorder; + int r; assert_return(m, -EINVAL); @@ -552,21 +557,26 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); if (r < 0) return r; else if ((size_t) r < sizeof(uint16_t)) return -EIO; - if (data) - *data = *(uint16_t *) attr_data; + if (data) { + if (net_byteorder) + *data = be16toh(*(uint16_t *) attr_data); + else + *data = *(uint16_t *) attr_data; + } return 0; } int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { - int r; void *attr_data; + bool net_byteorder; + int r; assert_return(m, -EINVAL); @@ -574,14 +584,18 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); if (r < 0) return r; else if ((size_t)r < sizeof(uint32_t)) return -EIO; - if (data) - *data = *(uint32_t *) attr_data; + if (data) { + if (net_byteorder) + *data = be32toh(*(uint32_t *) attr_data); + else + *data = *(uint32_t *) attr_data; + } return 0; } @@ -596,7 +610,7 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if ((size_t)r < sizeof(struct ether_addr)) @@ -618,7 +632,7 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if ((size_t)r < sizeof(struct ifa_cacheinfo)) @@ -640,7 +654,7 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if ((size_t)r < sizeof(struct in_addr)) @@ -662,7 +676,7 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, if (r < 0) return r; - r = netlink_message_read_internal(m, type, &attr_data); + r = netlink_message_read_internal(m, type, &attr_data, NULL); if (r < 0) return r; else if ((size_t)r < sizeof(struct in6_addr)) @@ -699,6 +713,8 @@ static int netlink_container_parse(sd_netlink_message *m, log_debug("rtnl: message parse - overwriting repeated attribute"); attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; + attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED; + attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; } container->attributes = attributes; @@ -781,7 +797,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ } else return -EINVAL; - r = netlink_message_read_internal(m, type_id, &container); + r = netlink_message_read_internal(m, type_id, &container, NULL); if (r < 0) return r; else diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 66ed2e013c..010c106610 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -59,6 +59,7 @@ Tun.Group, config_parse_string, 0, Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.VnetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c index 378312f091..ba84e802fc 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/networkd-netdev-tuntap.c @@ -51,6 +51,9 @@ static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) { if (t->multi_queue) ifr->ifr_flags |= IFF_MULTI_QUEUE; + if (t->vnet_hdr) + ifr->ifr_flags |= IFF_VNET_HDR; + strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1); return 0; diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h index b804875bbb..29f8bb0ea5 100644 --- a/src/network/networkd-netdev-tuntap.h +++ b/src/network/networkd-netdev-tuntap.h @@ -33,6 +33,7 @@ struct TunTap { bool one_queue; bool multi_queue; bool packet_info; + bool vnet_hdr; }; extern const NetDevVTable tun_vtable; diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index f9448e3bc5..0edba415b6 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -89,10 +89,6 @@ static int resolve_host(sd_bus *bus, const char *name) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - return bus_log_create_error(r); - r = sd_bus_message_append(req, "isit", arg_ifindex, name, arg_family, arg_flags); if (r < 0) return bus_log_create_error(r); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index ea7975a094..1ebebf8ea4 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -32,10 +32,10 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { assert(ret); - if (mtu <= 0) + if (mtu <= UDP_PACKET_HEADER_SIZE) a = DNS_PACKET_SIZE_START; else - a = mtu; + a = mtu - UDP_PACKET_HEADER_SIZE; if (a < DNS_PACKET_HEADER_SIZE) a = DNS_PACKET_HEADER_SIZE; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index bf998aa84e..6588ed9df5 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -21,6 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <netinet/udp.h> +#include <netinet/ip.h> #include "macro.h" #include "sparse-endian.h" @@ -53,6 +55,7 @@ struct DnsPacketHeader { }; #define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader) +#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr)) /* The various DNS protocols deviate in how large a packet can grow, but the TCP transport has a 16bit size field, hence that appears to diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 25392d21d7..7b72c090c2 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -125,7 +125,8 @@ void dns_scope_next_dns_server(DnsScope *s) { manager_next_dns_server(s->manager); } -int dns_scope_emit(DnsScope *s, DnsPacket *p) { +int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server) { + DnsServer *srv = NULL; union in_addr_union addr; int ifindex = 0, r; int family; @@ -144,8 +145,6 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) { mtu = manager_find_mtu(s->manager); if (s->protocol == DNS_PROTOCOL_DNS) { - DnsServer *srv; - if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; @@ -160,13 +159,13 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) { if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) return -EMSGSIZE; - if (p->size > mtu) + if (p->size + UDP_PACKET_HEADER_SIZE > mtu) return -EMSGSIZE; if (family == AF_INET) - fd = manager_dns_ipv4_fd(s->manager); + fd = transaction_dns_ipv4_fd(t); else if (family == AF_INET6) - fd = manager_dns_ipv6_fd(s->manager); + fd = transaction_dns_ipv6_fd(t); else return -EAFNOSUPPORT; if (fd < 0) @@ -200,10 +199,14 @@ int dns_scope_emit(DnsScope *s, DnsPacket *p) { if (r < 0) return r; + if (server) + *server = srv; + return 1; } -int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) { +int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) { + DnsServer *srv = NULL; _cleanup_close_ int fd = -1; union sockaddr_union sa = {}; socklen_t salen; @@ -214,8 +217,6 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add assert((family == AF_UNSPEC) == !address); if (family == AF_UNSPEC) { - DnsServer *srv; - srv = dns_scope_get_dns_server(s); if (!srv) return -ESRCH; @@ -288,6 +289,9 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add if (r < 0 && errno != EINPROGRESS) return -errno; + if (server) + *server = srv; + ret = fd; fd = -1; @@ -696,7 +700,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata return 0; } - r = dns_scope_emit(scope, p); + r = dns_scope_emit(scope, NULL, p, NULL); if (r < 0) log_debug_errno(r, "Failed to send conflict packet: %m"); } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index cfbde1343f..5c5ccc71c5 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -65,8 +65,8 @@ struct DnsScope { int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); DnsScope* dns_scope_free(DnsScope *s); -int dns_scope_emit(DnsScope *s, DnsPacket *p); -int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port); +int dns_scope_emit(DnsScope *s, DnsTransaction *t, DnsPacket *p, DnsServer **server); +int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server); DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain); int dns_scope_good_key(DnsScope *s, DnsResourceKey *key); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 9a62a63258..92e48ae442 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -41,6 +41,7 @@ int dns_server_new( if (!s) return -ENOMEM; + s->n_ref = 1; s->type = type; s->family = family; s->address = *in_addr; @@ -74,33 +75,46 @@ int dns_server_new( return 0; } -DnsServer* dns_server_free(DnsServer *s) { +DnsServer* dns_server_ref(DnsServer *s) { if (!s) return NULL; - if (s->link) { - if (s->type == DNS_SERVER_LINK) - LIST_REMOVE(servers, s->link->dns_servers, s); + assert(s->n_ref > 0); - if (s->link->current_dns_server == s) - link_set_dns_server(s->link, NULL); - } + s->n_ref ++; - if (s->manager) { - if (s->type == DNS_SERVER_SYSTEM) - LIST_REMOVE(servers, s->manager->dns_servers, s); - else if (s->type == DNS_SERVER_FALLBACK) - LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + return s; +} + +static DnsServer* dns_server_free(DnsServer *s) { + if (!s) + return NULL; - if (s->manager->current_dns_server == s) - manager_set_dns_server(s->manager, NULL); - } + if (s->link && s->link->current_dns_server == s) + link_set_dns_server(s->link, NULL); + + if (s->manager && s->manager->current_dns_server == s) + manager_set_dns_server(s->manager, NULL); free(s); return NULL; } +DnsServer* dns_server_unref(DnsServer *s) { + if (!s) + return NULL; + + assert(s->n_ref > 0); + + if (s->n_ref == 1) + dns_server_free(s); + else + s->n_ref --; + + return NULL; +} + static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { const DnsServer *s = p; uint64_t u; diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 70ff35b08f..06059e8829 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -37,6 +37,8 @@ typedef enum DnsServerType { struct DnsServer { Manager *manager; + unsigned n_ref; + DnsServerType type; Link *link; @@ -57,6 +59,9 @@ int dns_server_new( int family, const union in_addr_union *address); -DnsServer* dns_server_free(DnsServer *s); +DnsServer* dns_server_ref(DnsServer *s); +DnsServer* dns_server_unref(DnsServer *s); + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 3f4673df7a..e468f245f7 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -39,6 +39,12 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_packet_unref(t->received); dns_answer_unref(t->cached); + sd_event_source_unref(t->dns_ipv4_event_source); + sd_event_source_unref(t->dns_ipv6_event_source); + safe_close(t->dns_ipv4_fd); + safe_close(t->dns_ipv6_fd); + + dns_server_unref(t->server); dns_stream_free(t->stream); if (t->scope) { @@ -88,6 +94,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { if (!t) return -ENOMEM; + t->dns_ipv4_fd = t->dns_ipv6_fd = -1; + t->question = dns_question_ref(q); do @@ -237,6 +245,7 @@ static int on_stream_complete(DnsStream *s, int error) { } static int dns_transaction_open_tcp(DnsTransaction *t) { + _cleanup_(dns_server_unrefp) DnsServer *server = NULL; _cleanup_close_ int fd = -1; int r; @@ -246,12 +255,12 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { return 0; if (t->scope->protocol == DNS_PROTOCOL_DNS) - fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53); + fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server); else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { /* When we already received a query to this (but it was truncated), send to its sender address */ if (t->received) - fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port); + fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL); else { union in_addr_union address; int family = AF_UNSPEC; @@ -265,7 +274,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { if (r == 0) return -EINVAL; - fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT); + fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL); } } else return -EAFNOSUPPORT; @@ -285,6 +294,9 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { return r; } + + dns_server_unref(t->server); + t->server = dns_server_ref(server); t->received = dns_packet_unref(t->received); t->stream->complete = on_stream_complete; t->stream->transaction = t; @@ -333,10 +345,15 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { if (t->scope->protocol == DNS_PROTOCOL_DNS) { /* For DNS we are fine with accepting packets on any - * interface, but the source IP address must be one of - * a valid DNS server */ + * interface, but the source IP address must be the + * one of the DNS server we queried */ + + assert(t->server); - if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender)) + if (t->server->family != p->family) + return; + + if (!in_addr_equal(p->family, &p->sender, &t->server->address)) return; if (p->sender_port != 53) @@ -398,6 +415,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } + /* Only consider responses with equivalent query section to the request */ + if (!dns_question_is_superset(p->question, t->question) || + !dns_question_is_superset(t->question, p->question)) + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); @@ -492,6 +514,7 @@ int dns_transaction_go(DnsTransaction *t) { } t->n_attempts++; + t->server = dns_server_unref(t->server); t->received = dns_packet_unref(t->received); t->cached = dns_answer_unref(t->cached); t->cached_rcode = 0; @@ -571,17 +594,20 @@ int dns_transaction_go(DnsTransaction *t) { * always be made via TCP on LLMNR */ r = dns_transaction_open_tcp(t); } else { + DnsServer *server; + /* Try via UDP, and if that fails due to large size try via TCP */ - r = dns_scope_emit(t->scope, t->sent); - if (r == -EMSGSIZE) + r = dns_scope_emit(t->scope, t, t->sent, &server); + if (r >= 0) + t->server = dns_server_ref(server); + else if (r == -EMSGSIZE) r = dns_transaction_open_tcp(t); } if (r == -ESRCH) { /* No servers to send this to? */ dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS); return 0; - } - if (r < 0) { + } else if (r < 0) { if (t->scope->protocol != DNS_PROTOCOL_DNS) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); return 0; @@ -606,6 +632,91 @@ int dns_transaction_go(DnsTransaction *t) { return 1; } +static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsTransaction *t = userdata; + int r; + + assert(t); + assert(t->scope); + + r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); + if (r <= 0) + return r; + + if (dns_packet_validate_reply(p) > 0 && + DNS_PACKET_ID(p) == t->id) { + dns_transaction_process_reply(t, p); + } else + log_debug("Invalid DNS packet."); + + return 0; +} + +int transaction_dns_ipv4_fd(DnsTransaction *t) { + const int one = 1; + int r; + + assert(t); + assert(t->scope); + assert(t->scope->manager); + + if (t->dns_ipv4_fd >= 0) + return t->dns_ipv4_fd; + + t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (t->dns_ipv4_fd < 0) + return -errno; + + r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + goto fail; + + return t->dns_ipv4_fd; + +fail: + t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd); + return r; +} + +int transaction_dns_ipv6_fd(DnsTransaction *t) { + const int one = 1; + int r; + + assert(t); + assert(t->scope); + assert(t->scope->manager); + + if (t->dns_ipv6_fd >= 0) + return t->dns_ipv6_fd; + + t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (t->dns_ipv6_fd < 0) + return -errno; + + r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + goto fail; + + return t->dns_ipv6_fd; + +fail: + t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd); + return r; +} + static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { [DNS_TRANSACTION_NULL] = "null", [DNS_TRANSACTION_PENDING] = "pending", diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index f6d539d315..87f342ca11 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -61,6 +61,15 @@ struct DnsTransaction { sd_event_source *timeout_event_source; unsigned n_attempts; + int dns_ipv4_fd; + int dns_ipv6_fd; + + sd_event_source *dns_ipv4_event_source; + sd_event_source *dns_ipv6_event_source; + + /* the active server */ + DnsServer *server; + /* TCP connection logic, if we need it */ DnsStream *stream; @@ -86,6 +95,9 @@ int dns_transaction_go(DnsTransaction *t); void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p); void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state); +int transaction_dns_ipv4_fd(DnsTransaction *t); +int transaction_dns_ipv6_fd(DnsTransaction *t); + const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index ff8dc3a5bc..d66b3a88fc 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -58,7 +58,6 @@ int link_new(Manager *m, Link **ret, int ifindex) { } Link *link_free(Link *l) { - if (!l) return NULL; @@ -68,8 +67,12 @@ Link *link_free(Link *l) { if (l->manager) hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - while (l->dns_servers) - dns_server_free(l->dns_servers); + while (l->dns_servers) { + DnsServer *s = l->dns_servers; + + LIST_REMOVE(servers, l->dns_servers, s); + dns_server_unref(s); + } dns_scope_free(l->unicast_scope); dns_scope_free(l->llmnr_ipv4_scope); @@ -182,14 +185,20 @@ static int link_update_dns_servers(Link *l) { } LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers) - if (s->marked) - dns_server_free(s); + if (s->marked) { + LIST_REMOVE(servers, l->dns_servers, s); + dns_server_unref(s); + } return 0; clear: - while (l->dns_servers) - dns_server_free(l->dns_servers); + while (l->dns_servers) { + s = l->dns_servers; + + LIST_REMOVE(servers, l->dns_servers, s); + dns_server_unref(s); + } return r; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 6785a2e3c7..17de14bae1 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -404,7 +404,6 @@ int manager_new(Manager **ret) { if (!m) return -ENOMEM; - m->dns_ipv4_fd = m->dns_ipv6_fd = -1; m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1; m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1; m->hostname_fd = -1; @@ -486,11 +485,6 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->network_event_source); sd_network_monitor_unref(m->network_monitor); - sd_event_source_unref(m->dns_ipv4_event_source); - sd_event_source_unref(m->dns_ipv6_event_source); - safe_close(m->dns_ipv4_fd); - safe_close(m->dns_ipv6_fd); - manager_llmnr_stop(m); sd_bus_slot_unref(m->prepare_for_sleep_slot); @@ -603,8 +597,10 @@ int manager_read_resolv_conf(Manager *m) { } LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers) - if (s->marked) - dns_server_free(s); + if (s->marked) { + LIST_REMOVE(servers, m->dns_servers, s); + dns_server_unref(s); + } /* Whenever /etc/resolv.conf changes, start using the first * DNS server of it. This is useful to deal with broken @@ -619,8 +615,12 @@ int manager_read_resolv_conf(Manager *m) { return 0; clear: - while (m->dns_servers) - dns_server_free(m->dns_servers); + while (m->dns_servers) { + s = m->dns_servers; + + LIST_REMOVE(servers, m->dns_servers, s); + dns_server_unref(s); + } return r; } @@ -923,89 +923,6 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { return 1; } -static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t = NULL; - Manager *m = userdata; - int r; - - r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p); - if (r <= 0) - return r; - - if (dns_packet_validate_reply(p) > 0) { - t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (!t) - return 0; - - dns_transaction_process_reply(t, p); - - } else - log_debug("Invalid DNS packet."); - - return 0; -} - -int manager_dns_ipv4_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv4_fd >= 0) - return m->dns_ipv4_fd; - - m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv4_fd < 0) - return -errno; - - r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv4_fd; - -fail: - m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd); - return r; -} - -int manager_dns_ipv6_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv6_fd >= 0) - return m->dns_ipv6_fd; - - m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv6_fd < 0) - return -errno; - - r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv6_fd; - -fail: - m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd); - return r; -} - static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { int r; @@ -1381,15 +1298,25 @@ void manager_verify_all(Manager *m) { } void manager_flush_dns_servers(Manager *m, DnsServerType t) { + DnsServer *s; + assert(m); if (t == DNS_SERVER_SYSTEM) - while (m->dns_servers) - dns_server_free(m->dns_servers); + while (m->dns_servers) { + s = m->dns_servers; + + LIST_REMOVE(servers, m->dns_servers, s); + dns_server_unref(s); + } if (t == DNS_SERVER_FALLBACK) - while (m->fallback_dns_servers) - dns_server_free(m->fallback_dns_servers); + while (m->fallback_dns_servers) { + s = m->fallback_dns_servers; + + LIST_REMOVE(servers, m->fallback_dns_servers, s); + dns_server_unref(s); + } } static const char* const support_table[_SUPPORT_MAX] = { diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 4e70a5b500..005f844df2 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -65,12 +65,6 @@ struct Manager { unsigned n_dns_streams; /* Unicast dns */ - int dns_ipv4_fd; - int dns_ipv6_fd; - - sd_event_source *dns_ipv4_event_source; - sd_event_source *dns_ipv6_event_source; - LIST_HEAD(DnsServer, dns_servers); LIST_HEAD(DnsServer, fallback_dns_servers); DnsServer *current_dns_server; @@ -128,9 +122,6 @@ uint32_t manager_find_mtu(Manager *m); int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p); int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); -int manager_dns_ipv4_fd(Manager *m); -int manager_dns_ipv6_fd(Manager *m); - int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr); LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr); |