interface-ip: DNS name server sorting support in resolv.conf.auto
authorHans Dedecker <dedeckeh@gmail.com>
Tue, 13 Sep 2016 12:33:39 +0000 (14:33 +0200)
committerFelix Fietkau <nbd@nbd.name>
Wed, 14 Sep 2016 11:01:29 +0000 (13:01 +0200)
Interface name servers when being written to resolv.conf.auto are sorted
based on the following parameters:
-Primary sorting key is interface dns_metric; name servers having lowest
interface dns_metric are listed first
-Secondary sorting key is interface metric; in case of equal interface
dns_metric name servers having lowest interface metric are listed first
-Finally alphabetical order of the interface names in case of equal
interface dns_metric and metric

In case the resolver queries the multiple servers in the order
listed; sorting is usefull in the following scenarios :
-Name resolving over a main and backup interface
-Assign priority to IPv6 name servers over IPv4 or vice versa

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
interface-ip.c
interface.c
interface.h
ubus.c

index 26a28654bcecac86a839e69a17c09c3b2588965b..24ea054942e54157ac720340a681eb4ee8784c54 100644 (file)
@@ -1197,21 +1197,33 @@ write_resolv_conf_entries(FILE *f, struct interface_ip_settings *ip, const char
        }
 }
 
-void
-interface_write_resolv_conf(void)
+/* Sorting of interface resolver entries :               */
+/* Primary on interface dns_metric : lowest metric first */
+/* Secondary on interface metric : lowest metric first   */
+/* Finally alphabetical order of interface names         */
+static int resolv_conf_iface_cmp(const void *k1, const void *k2, void *ptr)
+{
+       const struct interface *iface1 = k1, *iface2 = k2;
+
+       if (iface1->dns_metric != iface2->dns_metric)
+               return iface1->dns_metric - iface2->dns_metric;
+
+       if (iface1->metric != iface2->metric)
+               return iface1->metric - iface2->metric;
+
+       return strcmp(iface1->name, iface2->name);
+}
+
+static void
+__interface_write_dns_entries(FILE *f)
 {
        struct interface *iface;
-       char *path = alloca(strlen(resolv_conf) + 5);
-       FILE *f;
-       uint32_t crcold, crcnew;
+       struct {
+               struct avl_node node;
+       } *entry, *n_entry;
+       struct avl_tree resolv_conf_iface_entries;
 
-       sprintf(path, "%s.tmp", resolv_conf);
-       unlink(path);
-       f = fopen(path, "w+");
-       if (!f) {
-               D(INTERFACE, "Failed to open %s for writing\n", path);
-               return;
-       }
+       avl_init(&resolv_conf_iface_entries, resolv_conf_iface_cmp, false, NULL);
 
        vlist_for_each_element(&interfaces, iface, node) {
                if (iface->state != IFS_UP)
@@ -1219,15 +1231,50 @@ interface_write_resolv_conf(void)
 
                if (vlist_simple_empty(&iface->proto_ip.dns_search) &&
                    vlist_simple_empty(&iface->proto_ip.dns_servers) &&
-                       vlist_simple_empty(&iface->config_ip.dns_search) &&
+                   vlist_simple_empty(&iface->config_ip.dns_search) &&
                    vlist_simple_empty(&iface->config_ip.dns_servers))
                        continue;
 
+               entry = calloc(1, sizeof(*entry));
+               if (!entry)
+                       continue;
+
+               entry->node.key = iface;
+               avl_insert(&resolv_conf_iface_entries, &entry->node);
+       }
+
+       avl_for_each_element(&resolv_conf_iface_entries, entry, node) {
+               iface = (struct interface *)entry->node.key;
+
                fprintf(f, "# Interface %s\n", iface->name);
+
                write_resolv_conf_entries(f, &iface->config_ip, iface->ifname);
+
                if (!iface->proto_ip.no_dns)
                        write_resolv_conf_entries(f, &iface->proto_ip, iface->ifname);
        }
+
+       avl_remove_all_elements(&resolv_conf_iface_entries, entry, node, n_entry)
+               free(entry);
+}
+
+void
+interface_write_resolv_conf(void)
+{
+       char *path = alloca(strlen(resolv_conf) + 5);
+       FILE *f;
+       uint32_t crcold, crcnew;
+
+       sprintf(path, "%s.tmp", resolv_conf);
+       unlink(path);
+       f = fopen(path, "w+");
+       if (!f) {
+               D(INTERFACE, "Failed to open %s for writing\n", path);
+               return;
+       }
+
+       __interface_write_dns_entries(f);
+
        fflush(f);
        rewind(f);
        crcnew = crc32_file(f);
index 71e2ecc282d52a97baba83ebcd7aa47d863ef6b1..5870422f418a30d68d5246b7168c03c1d7ec3f42 100644 (file)
@@ -35,6 +35,7 @@ enum {
        IFACE_ATTR_PEERDNS,
        IFACE_ATTR_DNS,
        IFACE_ATTR_DNS_SEARCH,
+       IFACE_ATTR_DNS_METRIC,
        IFACE_ATTR_METRIC,
        IFACE_ATTR_INTERFACE,
        IFACE_ATTR_IP6ASSIGN,
@@ -57,6 +58,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
        [IFACE_ATTR_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
+       [IFACE_ATTR_DNS_METRIC] = { .name = "dns_metric", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_IP6ASSIGN] = { .name = "ip6assign", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING },
@@ -795,6 +797,9 @@ interface_alloc(const char *name, struct blob_attr *config)
        if ((cur = tb[IFACE_ATTR_DNS_SEARCH]))
                interface_add_dns_search_list(&iface->config_ip, cur);
 
+       if ((cur = tb[IFACE_ATTR_DNS_METRIC]))
+               iface->dns_metric = blobmsg_get_u32(cur);
+
        if ((cur = tb[IFACE_ATTR_METRIC]))
                iface->metric = blobmsg_get_u32(cur);
 
@@ -1185,6 +1190,7 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
        if_old->parent_ifname = if_new->parent_ifname;
        if_old->proto_handler = if_new->proto_handler;
        if_old->force_link = if_new->force_link;
+       if_old->dns_metric = if_new->dns_metric;
 
        if_old->proto_ip.no_dns = if_new->proto_ip.no_dns;
        interface_replace_dns(&if_old->config_ip, &if_new->config_ip);
index 5b89b176e5b2f95d849e981e1d70ceb7137b63c4..aa2085dac0f3625dba329ac411aac6b61cee8ba3 100644 (file)
@@ -145,6 +145,7 @@ struct interface {
        struct vlist_tree host_routes;
 
        int metric;
+       int dns_metric;
        unsigned int ip4table;
        unsigned int ip6table;
 
diff --git a/ubus.c b/ubus.c
index 2b8bb9007dcd38a4873e49d308c7874bd9473d5b..4d43b5756f337821245b14def18f3f4a305c46f9 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -687,6 +687,7 @@ netifd_dump_status(struct interface *iface)
                if (iface->ip6table)
                        blobmsg_add_u32(&b, "ip6table", iface->ip6table);
                blobmsg_add_u32(&b, "metric", iface->metric);
+               blobmsg_add_u32(&b, "dns_metric", iface->dns_metric);
                blobmsg_add_u8(&b, "delegation", !iface->proto_ip.no_delegation);
                a = blobmsg_open_array(&b, "ipv4-address");
                interface_ip_dump_address_list(&iface->config_ip, false, true);