interface-ip: DNS name server sorting support in resolv.conf.auto
[project/netifd.git] / interface-ip.c
index 26a2865..24ea054 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);