Hi all,

This patch adds a new keyword "resolve-opts" which can take a list of comma
separated options.
2 options have been implemented for now:
* prevent-dup-ip: (default and historical way of working for HAProxy)
ensure this server will be the single one configured to an IP address, when
sharing the same fqdn than other servers in the same backend
* allow-dup-ip: allow multiple servers (they must all have this option
enabled) sharing the same fqdn to get an IP which is already used by an
other

The resolve-opts is compatible with server, default-server and
server-template. The latest configured value will win.
IE:
backend foobar
 default-server resolve-opts allow-dup-ip
 server s1 www.domain.tld
 server s2 www.domain.tld
 server s3 www.domain.tld resolve-opts prevent-dup-ip

==> only s1 and s2 could share the same IP in common.

Note that if the DNS server returns 2 records, there is no guarantee that
IPA will be affected to s1 and s2 and IPB to s3.
That's because, for now, the resolution is "atomic" and linked to the
server itself and that because the algorithm still search a different IP
before allowing a failover to an already used one (if allowed to).

The first 3 patches are clean up and the code is in the 4th one.

Note that I may move the other resolve-* keywords into the resolve-opts
(older keywords will still be valid for backward compatibility).

Baptiste
From 348effd9e5182687a51b52312ac054286599af07 Mon Sep 17 00:00:00 2001
From: Baptiste Assmann <bed...@gmail.com>
Date: Fri, 22 Jun 2018 15:04:43 +0200
Subject: [PATCH 4/4] MINOR: dns: new DNS options to allow/prevent IP address
 duplication

By default, HAProxy's DNS resolution at runtime ensure that there is no
IP address duplication in a backend (for servers being resolved by the
same hostname).
There are a few cases where people want, on purpose, to disable this
feature.

This patch introduces a couple of new server side options for this purpose:
"resolve-opts allow-dup-ip" or "resolve-opts prevent-dup-ip".
---
 doc/configuration.txt | 34 ++++++++++++++++++++++++++++++++++
 include/types/dns.h   |  2 ++
 src/dns.c             |  6 +++++-
 src/server.c          | 27 +++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index e901d7e..b443de6 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11682,6 +11682,40 @@ rise <count>
   after <count> consecutive successful health checks. This value defaults to 2
   if unspecified. See also the "check", "inter" and "fall" parameters.
 
+resolve-opts <option>,<option>,...
+  Comma separated list of options to apply to DNS resolution linked to this
+  server.
+
+  Available options:
+
+  * allow-dup-ip
+    By default, HAProxy prevents IP address duplication in a backend when DNS
+    resolution at runtime is in operation.
+    That said, for some cases, it makes sense that two servers (in the same
+    backend, being resolved by the same FQDN) have the same IP address.
+    For such case, simply enable this option.
+    This is the opposite of prevent-dup-ip.
+
+  * prevent-dup-ip
+    Ensure HAProxy's default behavior is enforced on a server: prevent re-using
+    an IP address already set to a server in the same backend and sharing the
+    same fqdn.
+    This is the opposite of allow-dup-ip.
+
+  Example:
+    backend b_myapp
+      default-server init-addr none resolvers dns
+      server s1 myapp.example.com:80 check resolve-opts allow-dup-ip
+      server s2 myapp.example.com:81 check resolve-opts allow-dup-ip
+
+  With the option allow-dup-ip set:
+  * if the nameserver returns a single IP address, then both servers will use
+    it
+  * If the nameserver returns 2 IP addresses, then each server will pick up a
+    different address
+
+  Default value: not set
+
 resolve-prefer <family>
   When DNS resolution is enabled for a server and multiple IP addresses from
   different families are returned, HAProxy will prefer using an IP address
diff --git a/include/types/dns.h b/include/types/dns.h
index 9b1d08d..488d399 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -245,6 +245,8 @@ struct dns_options {
 		} mask;
 	} pref_net[SRV_MAX_PREF_NET];
 	int pref_net_nb; /* The number of registered prefered networks. */
+	int accept_duplicate_ip; /* flag to indicate whether the associated object can use an IP address
+				    already set to an other object of the same group */
 };
 
 /* Resolution structure associated to single server and used to manage name
diff --git a/src/dns.c b/src/dns.c
index 018c86a..77bf5c0 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -962,8 +962,10 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
 	int currentip_sel;
 	int j;
 	int score, max_score;
+	int allowed_duplicated_ip;
 
 	family_priority   = dns_opts->family_prio;
+	allowed_duplicated_ip = dns_opts->accept_duplicate_ip;
 	*newip = newip4   = newip6 = NULL;
 	currentip_found   = 0;
 	*newip_sin_family = AF_UNSPEC;
@@ -1027,7 +1029,9 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
 		 * member of a group.  If not, the score should be incremented
 		 * by 2. */
 		if (owner && snr_check_ip_callback(owner, ip, &ip_type)) {
-			continue;
+			if (!allowed_duplicated_ip) {
+				continue;
+			}
 		} else {
 			score += 2;
 		}
diff --git a/src/server.c b/src/server.c
index 277d140..17d7663 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1544,6 +1544,7 @@ static void srv_settings_cpy(struct server *srv, struct server *src, int srv_tmp
 	if (src->resolvers_id != NULL)
 		srv->resolvers_id = strdup(src->resolvers_id);
 	srv->dns_opts.family_prio = src->dns_opts.family_prio;
+	srv->dns_opts.accept_duplicate_ip = src->dns_opts.accept_duplicate_ip;
 	if (srv->dns_opts.family_prio == AF_UNSPEC)
 		srv->dns_opts.family_prio = AF_INET6;
 	memcpy(srv->dns_opts.pref_net,
@@ -2082,6 +2083,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			newsrv = &curproxy->defsrv;
 			cur_arg = 1;
 			newsrv->dns_opts.family_prio = AF_INET6;
+			newsrv->dns_opts.accept_duplicate_ip = 0;
 		}
 
 		while (*args[cur_arg]) {
@@ -2177,6 +2179,31 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				newsrv->resolvers_id = strdup(args[cur_arg + 1]);
 				cur_arg += 2;
 			}
+			else if (!strcmp(args[cur_arg], "resolve-opts")) {
+				char *p, *end;
+
+				for (p = args[cur_arg + 1]; *p; p = end) {
+					/* cut on next comma */
+					for (end = p; *end && *end != ','; end++);
+					if (*end)
+						*(end++) = 0;
+
+					if (!strcmp(p, "allow-dup-ip")) {
+						newsrv->dns_opts.accept_duplicate_ip = 1;
+					}
+					else if (!strcmp(p, "prevent-dup-ip")) {
+						newsrv->dns_opts.accept_duplicate_ip = 0;
+					}
+					else {
+						ha_alert("parsing [%s:%d]: '%s' : unknown resolve-opts option '%s', supported methods are 'allow-dup-ip' and 'prevent-dup-ip'.\n",
+								file, linenum, args[cur_arg], p);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+				}
+
+				cur_arg += 2;
+			}
 			else if (!strcmp(args[cur_arg], "resolve-prefer")) {
 				if (!strcmp(args[cur_arg + 1], "ipv4"))
 					newsrv->dns_opts.family_prio = AF_INET;
-- 
2.7.4

From e815c74a26fed67a55030608c7d08962c84458f8 Mon Sep 17 00:00:00 2001
From: Baptiste Assmann <bed...@gmail.com>
Date: Fri, 22 Jun 2018 13:03:50 +0200
Subject: [PATCH 3/4] MINOR: dns: fix wrong score computation in
 dns_get_ip_from_response

dns_get_ip_from_response() is used to compare the caller current IP to
the IP available in the records returned by the DNS server.
A scoring system is in place to get the best IP address available.
That said, in the current implementation, there are a couple of issues:
1. a comment does not match what the code does
2. the code does not match what the commet says (score value is not
   incremented with '2')

This patch fixes both issues.

Backport status: 1.8
---
 src/dns.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/dns.c b/src/dns.c
index c86e57d..018c86a 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -1024,10 +1024,13 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
 		}
 
 		/* Check if the IP found in the record is already affected to a
-		 * member of a group.  If yes, the score should be incremented
+		 * member of a group.  If not, the score should be incremented
 		 * by 2. */
-		if (owner && snr_check_ip_callback(owner, ip, &ip_type))
+		if (owner && snr_check_ip_callback(owner, ip, &ip_type)) {
 			continue;
+		} else {
+			score += 2;
+		}
 
 		/* Check for current ip matching. */
 		if (ip_type == currentip_sin_family &&
-- 
2.7.4

From 3434dfa90885e3547882758b112b53e3bfda3414 Mon Sep 17 00:00:00 2001
From: Baptiste Assmann <bed...@gmail.com>
Date: Fri, 22 Jun 2018 12:51:51 +0200
Subject: [PATCH 2/4] TYPO: dns: inacurate comment about prefered IP score

The comment was about "prefered network ip version" while it's actually
"prefered ip version" in the code.
Fixed

Backport status: 1.7 and 1.8
  Be careful, this patch may not apply on 1.7, since the score was '4'
  for this item at that time.
---
 src/dns.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dns.c b/src/dns.c
index 8ae183f..c86e57d 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -976,7 +976,7 @@ int dns_get_ip_from_response(struct dns_response_packet *dns_p,
 	 *
 	 * For these three priorities, a score is calculated. The
 	 * weight are:
-	 *  8 - prefered netwok ip version.
+	 *  8 - prefered ip version.
 	 *  4 - prefered network.
 	 *  2 - if the ip in the record is not affected to any other server in the same backend (duplication)
 	 *  1 - current ip.
-- 
2.7.4

From f7370f826838166b3940e28fe6bf128785935279 Mon Sep 17 00:00:00 2001
From: Baptiste Assmann <bed...@gmail.com>
Date: Thu, 21 Jun 2018 11:45:58 +0200
Subject: [PATCH 1/4] CLEANUP: dns: remove obsolete macro DNS_MAX_IP_REC

Since a8c6db8d2d97629b2734c1d2be0860b6b11e5709, this macro is not used
anymore and can be safely removed.

Backport status: 1.8
---
 src/dns.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/dns.c b/src/dns.c
index 8d6a6d6..8ae183f 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -949,7 +949,6 @@ static int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend,
  * For both cases above, dns_validate_dns_response is required
  * returns one of the DNS_UPD_* code
  */
-#define DNS_MAX_IP_REC 20
 int dns_get_ip_from_response(struct dns_response_packet *dns_p,
                              struct dns_options *dns_opts, void *currentip,
                              short currentip_sin_family,
-- 
2.7.4

Reply via email to