MEDIUM: backend: Implement avalanche as a modifier of the hashing functions.

Summary:
Avalanche is supported not as a native hashing choice, but a modifier
on the hashing function. Note that this means that possible configs
written after 1.5-dev4 using "hash-type avalanche" will get an informative
error instead. But as discussed on the mailing list it seems nobody ever
used it anyway, so let's fix it before the final 1.5 release.

The default values were selected for backward compatibility with previous
releases, as discussed on the mailing list, which means that the consistent
hashing will still apply the avalanche hash by default when no explicit
algorithm is specified.

Examples
  (default) hash-type map-based
	Map based hashing using sdbm without avalanche

  (default) hash-type consistent
	Consistent hashing using sdbm with avalanche

Additional Examples:

  (a) hash-type map-based sdbm
	Same as default for map-based above
  (b) hash-type map-based sdbm avalanche
	Map based hashing using sdbm with avalanche
  (c) hash-type map-based djb2
	Map based hashing using djb2 without avalanche
  (d) hash-type map-based djb2 avalanche
	Map based hashing using djb2 with avalanche
  (e) hash-type consistent sdbm avalanche
	Same as default for consistent above
  (f) hash-type consistent sdbm
	Consistent hashing using sdbm without avalanche
  (g) hash-type consistent djb2
	Consistent hashing using djb2 without avalanche
  (h) hash-type consistent djb2 avalanche
	Consistent hashing using djb2 with avalanche
diff --git a/src/backend.c b/src/backend.c
index bf4a81d..e044430 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -147,7 +147,7 @@
 		h ^= ntohl(*(unsigned int *)(&addr[l]));
 		l += sizeof (int);
 	}
-	if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 		h = full_hash(h);
  hash_done:
 	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
@@ -199,7 +199,7 @@
 
 	hash = gen_hash(px, start, (end - start));
 
-	if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 		hash = full_hash(hash);
  hash_done:
 	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
@@ -256,7 +256,7 @@
 				}
 				hash = gen_hash(px, start, (end - start));
 
-				if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+				if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 					hash = full_hash(hash);
 
 				if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
@@ -330,7 +330,7 @@
 				}
 				hash = gen_hash(px, start, (end - start));
 
-				if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+				if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 					hash = full_hash(hash);
 
 				if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
@@ -422,7 +422,7 @@
 		}
 		hash = gen_hash(px, start, (end - start));
 	}
-	if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 		hash = full_hash(hash);
  hash_done:
 	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
@@ -466,7 +466,7 @@
 	 */
 	hash = gen_hash(px, smp.data.str.str, len);
 
-	if ((px->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP)
+	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 		hash = full_hash(hash);
  hash_done:
 	if (px->lbprm.algo & BE_LB_LKUP_CHTREE)
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 051f600..bb79a34 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -4085,7 +4085,13 @@
 		}
 	}
 	else if (!strcmp(args[0], "hash-type")) { /* set hashing method */
-		curproxy->lbprm.algo &= ~(BE_LB_HASH_TYPE | BE_LB_HASH_FUNC);
+		/**
+		 * The syntax for hash-type config element is
+		 * hash-type {map-based|consistent} [[<algo>] avalanche]
+		 *
+		 * The default hash function is sdbm for map-based and sdbm+avalanche for consistent.
+		 */
+		curproxy->lbprm.algo &= ~(BE_LB_HASH_TYPE | BE_LB_HASH_FUNC | BE_LB_HASH_MOD);
 
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
@@ -4096,26 +4102,48 @@
 		else if (strcmp(args[1], "map-based") == 0) {	/* use map-based hashing */
 			curproxy->lbprm.algo |= BE_LB_HASH_MAP;
 		}
-		else if (strcmp(args[1], "avalanche") == 0) {	/* use full hash before map-based hashing */
-			curproxy->lbprm.algo |= BE_LB_HASH_AVAL;
+		else if (strcmp(args[1], "avalanche") == 0) {
+			Alert("parsing [%s:%d] : experimental feature '%s %s' is not supported anymore, please use '%s map-based sdbm avalanche' instead.\n", file, linenum, args[0], args[1], args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
 		}
 		else {
-			Alert("parsing [%s:%d] : '%s' only supports 'avalanche', 'consistent' and 'map-based'.\n", file, linenum, args[0]);
+			Alert("parsing [%s:%d] : '%s' only supports 'consistent' and 'map-based'.\n", file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
 		/* set the hash function to use */
 		if (!*args[2]) {
+			/* the default algo is sdbm */
 			curproxy->lbprm.algo |= BE_LB_HFCN_SDBM;
-		} else if (!strcmp(args[2], "sdbm")) {
-			curproxy->lbprm.algo |= BE_LB_HFCN_SDBM;
-		} else if (!strcmp(args[2], "djb2")) {
-			curproxy->lbprm.algo |= BE_LB_HFCN_DJB2;
+
+			/* if consistent with no argument, then avalanche modifier is also applied */
+			if ((curproxy->lbprm.algo & BE_LB_HASH_TYPE) == BE_LB_HASH_CONS)
+				curproxy->lbprm.algo |= BE_LB_HMOD_AVAL;
 		} else {
-			Alert("parsing [%s:%d] : '%s' only supports 'sdbm' and 'djb2' hash functions.\n", file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
+			/* set the hash function */
+			if (!strcmp(args[2], "sdbm")) {
+				curproxy->lbprm.algo |= BE_LB_HFCN_SDBM;
+			}
+			else if (!strcmp(args[2], "djb2")) {
+				curproxy->lbprm.algo |= BE_LB_HFCN_DJB2;
+			}
+			else {
+				Alert("parsing [%s:%d] : '%s' only supports 'sdbm' and 'djb2' hash functions.\n", file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			/* set the hash modifier */
+			if (!strcmp(args[3], "avalanche")) {
+				curproxy->lbprm.algo |= BE_LB_HMOD_AVAL;
+			}
+			else if (*args[3]) {
+				Alert("parsing [%s:%d] : '%s' only supports 'avalanche' as a modifier for hash functions.\n", file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
 		}
 	}
 	else if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {  /* server address */