[MEDIUM] add support for RDP cookie load-balancing
This patch adds support for hashing RDP cookies in order to
use them as a load-balancing key. The new "rdp-cookie(name)"
load-balancing metric has to be used for this. It is still
mandatory to wait for an RDP cookie in the frontend, otherwise
it will randomly work.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e8fd090..3e0b7c3 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -956,6 +956,22 @@
specific headers such as 'Host'. For instance, in the Host
value "haproxy.1wt.eu", only "1wt" will be considered.
+ rdp-cookie
+ rdp-cookie(name)
+ The RDP cookie <name> (or "mstshash" if omitted) will be
+ looked up and hashed for each incoming TCP request. Just as
+ with the equivalent ACL 'req_rdp_cookie()' function, the name
+ is not case-sensitive. This mechanism is useful as a degraded
+ persistence mode, as it makes it possible to always send the
+ same user (or the same session ID) to the same server. If the
+ cookie is not found, the normal round-robind algorithm is
+ used instead.
+
+ Note that for this to work, the frontend must ensure that an
+ RDP cookie is already present in the request buffer. For this
+ you must use 'tcp-request content accept' rule combined with
+ a 'req_rdp_cookie_cnt' ACL.
+
<arguments> is an optional list of arguments which may be needed by some
algorithms. Right now, only "url_param" and "uri" support an
optional argument.
diff --git a/include/types/backend.h b/include/types/backend.h
index 9f15c08..983d009 100644
--- a/include/types/backend.h
+++ b/include/types/backend.h
@@ -44,6 +44,7 @@
#define BE_LB_ALGO_PH (BE_LB_PROP_L7 | 0x04) /* balance on URL parameter hash */
#define BE_LB_ALGO_LC (BE_LB_PROP_DYN | 0x05) /* fast weighted leastconn mode (dynamic) */
#define BE_LB_ALGO_HH (BE_LB_PROP_L7 | 0x06) /* balance on Http Header value */
+#define BE_LB_ALGO_RCH (BE_LB_PROP_L4 | 0x07) /* balance on RDP Cookie value */
/* various constants */
diff --git a/src/backend.c b/src/backend.c
index bf9f789..d6d47fa 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1367,6 +1367,46 @@
return px->lbprm.map.srv[hash % px->lbprm.tot_weight];
}
+struct server *get_server_rch(struct session *s)
+{
+ unsigned long hash = 0;
+ struct proxy *px = s->be;
+ unsigned long len;
+ const char *p;
+ int ret;
+ struct acl_expr expr;
+ struct acl_test test;
+
+ /* tot_weight appears to mean srv_count */
+ if (px->lbprm.tot_weight == 0)
+ return NULL;
+
+ if (px->lbprm.map.state & PR_MAP_RECALC)
+ recalc_server_map(px);
+
+ memset(&expr, 0, sizeof(expr));
+ memset(&test, 0, sizeof(test));
+
+ expr.arg.str = px->hh_name;
+ expr.arg_len = px->hh_len;
+
+ ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test);
+ if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || test.len == 0)
+ return NULL;
+
+ /* Found a the hh_name in the headers.
+ * we will compute the hash based on this value ctx.val.
+ */
+ len = test.len;
+ p = (char *)test.ptr;
+ while (len) {
+ hash = *p + (hash << 6) + (hash << 16) - hash;
+ len--;
+ p++;
+ }
+
+ return px->lbprm.map.srv[hash % px->lbprm.tot_weight];
+}
/*
* This function applies the load-balancing algorithm to the session, as
@@ -1499,6 +1539,19 @@
}
}
break;
+ case BE_LB_ALGO_RCH:
+ /* RDP Cookie hashing */
+ s->srv = get_server_rch(s);
+
+ if (!s->srv) {
+ /* parameter not found, fall back to round robin on the map */
+ s->srv = get_server_rr_with_conns(s->be, s->prev_srv);
+ if (!s->srv) {
+ err = SRV_STATUS_FULL;
+ goto out;
+ }
+ }
+ break;
default:
/* unknown balancing algorithm */
err = SRV_STATUS_INTERNAL;
@@ -2205,9 +2258,38 @@
curproxy->hh_match_domain = 1;
}
+ }
+ else if (!strncmp(args[0], "rdp-cookie", 10)) {
+ curproxy->lbprm.algo &= ~BE_LB_ALGO;
+ curproxy->lbprm.algo |= BE_LB_ALGO_RCH;
+
+ if ( *(args[0] + 10 ) == '(' ) { /* cookie name */
+ const char *beg, *end;
+
+ beg = args[0] + 11;
+ end = strchr(beg, ')');
+
+ if (!end || end == beg) {
+ snprintf(err, errlen, "'balance rdp-cookie(name)' requires an rdp cookie name.");
+ return -1;
+ }
+
+ free(curproxy->hh_name);
+ curproxy->hh_name = my_strndup(beg, end - beg);
+ curproxy->hh_len = end - beg;
+ }
+ else if ( *(args[0] + 10 ) == '\0' ) { /* default cookie name 'mstshash' */
+ free(curproxy->hh_name);
+ curproxy->hh_name = strdup("mstshash");
+ curproxy->hh_len = strlen(curproxy->hh_name);
+ }
+ else { /* syntax */
+ snprintf(err, errlen, "'balance rdp-cookie(name)' requires an rdp cookie name.");
+ return -1;
+ }
}
else {
- snprintf(err, errlen, "'balance' only supports 'roundrobin', 'leastconn', 'source', 'uri', 'url_param' and 'hdr(name)' options.");
+ snprintf(err, errlen, "'balance' only supports 'roundrobin', 'leastconn', 'source', 'uri', 'url_param', 'hdr(name)' and 'rdp-cookie(name)' options.");
return -1;
}
return 0;