[MEDIUM] add support for RDP cookie persistence

The new statement "persist rdp-cookie" enables RDP cookie
persistence. The RDP cookie is then extracted from the RDP
protocol, and compared against available servers. If a server
matches the RDP cookie, then it gets the connection.
This commit is contained in:
Emeric Brun 2009-06-30 17:57:00 +02:00 committed by Willy Tarreau
parent 736aa238a3
commit 647caf1ebc
8 changed files with 174 additions and 0 deletions

View File

@ -712,6 +712,7 @@ option tcpka X X X X
option tcplog X X X X
[no] option tcpsplice X X X X
[no] option transparent X - X X
persist rdp-cookie X - X X
rate-limit sessions X X X -
redirect - X X X
redisp X - X X (deprecated)
@ -2944,6 +2945,46 @@ no option transparent
"transparent" option of the "bind" keyword.
persist rdp-cookie
persist rdp-cookie(name)
Enable RDP cookie-based persistence
May be used in sections : defaults | frontend | listen | backend
yes | no | yes | yes
Arguments :
<name> is the optional name of the RDP cookie to check. If omitted, the
default cookie name "mstshash" will be used. There currently is
no valid reason to change this name.
This statement enables persistence based on an RDP cookie. The RDP cookie
contains all information required to find the server in the list of known
servers. So when this option is set in the backend, the request is analysed
and if an RDP cookie is found, it is decoded. If it matches a known server
which is still UP (or if "option persist" is set), then the connection is
forwarded to this server.
Note that this only makes sense in a TCP backend, but for this to work, the
frontend must have waited long enough to ensure that an RDP cookie is present
in the request buffer. This is the same requirement as with the "rdp-cookie"
load-balancing method. Thus it is higly recommended to put all statements in
a single "listen" section.
Example :
listen tse-farm
bind :3389
# wait up to 5s for an RDP cookie in the request
tcp-request inspect-delay 5s
tcp-request content accept if RDP_COOKIE
# apply RDP cookie persistence
persist rdp-cookie
# if server is unknown, let's balance on the same cookie.
# alternatively, "balance leastconn" may be useful too.
balance rdp-cookie
server srv1 1.1.1.1:3389
server srv2 1.1.1.2:3389
See also : "balance rdp-cookie", "tcp-request" and the "req_rdp_cookie" ACL.
rate-limit sessions <rate>
Set a limit on the number of new sessions accepted per second on a frontend
May be used in sections : defaults | frontend | listen | backend

View File

@ -35,6 +35,7 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen);
int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir,
struct acl_expr *expr, struct acl_test *test);
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
#endif /* _PROTO_PROTO_TCP_H */

View File

@ -116,6 +116,7 @@
#define AN_REQ_UNIX_STATS 0x00000100 /* process unix stats socket request */
#define AN_RTR_HTTP_HDR 0x00000200 /* inspect HTTP response headers */
#define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */
/* describes a chunk of string */
struct chunk {

View File

@ -119,6 +119,7 @@
#define PR_O2_LOGERRORS 0x00000040 /* log errors and retries at level LOG_ERR */
#define PR_O2_SMARTACC 0x00000080 /* don't immediately ACK request after accept */
#define PR_O2_SMARTCON 0x00000100 /* don't immediately send empty ACK after connect */
#define PR_O2_RDPC_PRST 0x00000200 /* Actvate rdp cookie analyser */
/* This structure is used to apply fast weighted round robin on a server group */
struct fwrr_group {
@ -197,6 +198,8 @@ struct proxy {
char *cookie_domain; /* domain used to insert the cookie */
char *cookie_name; /* name of the cookie to look for */
int cookie_len; /* strlen(cookie_name), computed only once */
char *rdp_cookie_name; /* name of the RDP cookie to look for */
int rdp_cookie_len; /* strlen(rdp_cookie_name), computed only once */
char *url_param_name; /* name of the URL parameter used for hashing */
int url_param_len; /* strlen(url_param_name), computed only once */
unsigned url_param_post_limit; /* if checking POST body for URI parameter, max body to wait for */

View File

@ -824,6 +824,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
curproxy->cookie_name = strdup(defproxy.cookie_name);
curproxy->cookie_len = defproxy.cookie_len;
if (defproxy.rdp_cookie_name)
curproxy->rdp_cookie_name = strdup(defproxy.rdp_cookie_name);
curproxy->rdp_cookie_len = defproxy.rdp_cookie_len;
if (defproxy.url_param_name)
curproxy->url_param_name = strdup(defproxy.url_param_name);
curproxy->url_param_len = defproxy.url_param_len;
@ -891,6 +895,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
*/
free(defproxy.check_req);
free(defproxy.cookie_name);
free(defproxy.rdp_cookie_name);
free(defproxy.url_param_name);
free(defproxy.hh_name);
free(defproxy.capture_name);
@ -1222,6 +1227,49 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
return -1;
}
}/* end else if (!strcmp(args[0], "cookie")) */
else if (!strcmp(args[0], "persist")) { /* persist */
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : missing persist method.\n",
file, linenum);
return -1;
}
if (!strncmp(args[1], "rdp-cookie", 10)) {
curproxy->options2 |= PR_O2_RDPC_PRST;
if (*(args[1] + 10 ) == '(') { /* cookie name */
const char *beg, *end;
beg = args[1] + 11;
end = strchr(beg, ')');
if (!end || end == beg) {
Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
file, linenum);
return -1;
}
free(curproxy->rdp_cookie_name);
curproxy->rdp_cookie_name = my_strndup(beg, end - beg);
curproxy->rdp_cookie_len = end-beg;
}
else if (*(args[1] + 10 ) == '\0') { /* default cookie name 'msts' */
free(curproxy->rdp_cookie_name);
curproxy->rdp_cookie_name = strdup("msts");
curproxy->rdp_cookie_len = strlen(curproxy->rdp_cookie_name);
}
else { /* syntax */
Alert("parsing [%s:%d] : persist rdp-cookie(name)' requires an rdp cookie name.\n",
file, linenum);
return -1;
}
}
else {
Alert("parsing [%s:%d] : unknown persist method.\n",
file, linenum);
return -1;
}
}
else if (!strcmp(args[0], "appsession")) { /* cookie name */
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))

View File

@ -446,6 +446,74 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
return 1;
}
/* Apply RDP cookie persistence to the current session. For this, the function
* tries to extract an RDP cookie from the request buffer, and look for the
* matching server in the list. If the server is found, it is assigned to the
* session. This always returns 1, and the analyser removes itself from the
* list. Nothing is performed if a server was already assigned.
*/
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit)
{
struct proxy *px = s->be;
int ret;
struct acl_expr expr;
struct acl_test test;
struct server *srv = px->srv;
struct sockaddr_in addr;
char *p;
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
now_ms, __FUNCTION__,
s,
req,
req->rex, req->wex,
req->flags,
req->l,
req->analysers);
if (s->flags & SN_ASSIGNED)
goto no_cookie;
memset(&expr, 0, sizeof(expr));
memset(&test, 0, sizeof(test));
expr.arg.str = s->be->rdp_cookie_name;
expr.arg_len = s->be->rdp_cookie_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)
goto no_cookie;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
/* Considering an rdp cookie detected using acl, test.ptr ended with <cr><lf> and should return */
addr.sin_addr.s_addr = strtoul(test.ptr, &p, 10);
if (*p != '.')
goto no_cookie;
p++;
addr.sin_port = (unsigned short)strtoul(p, &p, 10);
if (*p != '.')
goto no_cookie;
while (srv) {
if (memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) {
if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
/* we found the server and it is usable */
s->flags |= SN_DIRECT | SN_ASSIGNED;
s->srv = srv;
break;
}
}
srv = srv->next;
}
no_cookie:
req->analysers &= ~an_bit;
req->analyse_exp = TICK_ETERNITY;
return 1;
}
/* This function should be called to parse a line starting with the "tcp-request"
* keyword.

View File

@ -677,6 +677,12 @@ int session_set_backend(struct session *s, struct proxy *be)
s->req->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_BE | AN_REQ_HTTP_INNER;
}
/* If the backend does requires RDP cookie persistence, we have to
* enable the corresponding analyser.
*/
if (s->be->options2 & PR_O2_RDPC_PRST)
s->req->analysers |= AN_REQ_PRST_RDP_COOKIE;
return 1;
}

View File

@ -850,6 +850,12 @@ struct task *process_session(struct task *t)
if (!http_process_request_body(s, s->req, AN_REQ_HTTP_BODY))
break;
}
if (s->req->analysers & AN_REQ_PRST_RDP_COOKIE) {
last_ana |= AN_REQ_PRST_RDP_COOKIE;
if (!tcp_persist_rdp_cookie(s, s->req, AN_REQ_PRST_RDP_COOKIE))
break;
}
}
}