diff mbox

[OpenWrt-Devel,netifd,2/3] interface-ip: DNS name server sorting support in resolv.conf.auto

Message ID 1473770020-19533-2-git-send-email-dedeckeh@gmail.com
State Changes Requested
Delegated to: John Crispin
Headers show

Commit Message

Hans Dedecker Sept. 13, 2016, 12:33 p.m. UTC
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 | 73 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 interface.c    |  6 +++++
 interface.h    |  1 +
 ubus.c         |  1 +
 4 files changed, 68 insertions(+), 13 deletions(-)
diff mbox

Patch

diff --git a/interface-ip.c b/interface-ip.c
index 26a2865..24ea054 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -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);
diff --git a/interface.c b/interface.c
index 71e2ecc..5870422 100644
--- a/interface.c
+++ b/interface.c
@@ -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);
diff --git a/interface.h b/interface.h
index 5b89b17..aa2085d 100644
--- a/interface.h
+++ b/interface.h
@@ -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 74cdf28..1cbc479 100644
--- 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);