1
0
mirror of http://git.haproxy.org/git/haproxy.git/ synced 2025-04-11 03:31:36 +00:00

MEDIUM: tcp: add a new tcp-request capture directive

This new directive captures the specified fetch expression, converts
it to text and puts it into the next capture slot. The capture slots
are shared with header captures so that it is possible to dump all
captures at once or selectively in logs and header processing.

The purpose is to permit logs to contain whatever payload is found in
a request, for example bytes at a fixed location or the SNI of forwarded
SSL traffic.
This commit is contained in:
Willy Tarreau 2014-06-13 16:18:52 +02:00
parent 3a4ac422ce
commit 18bf01e900
2 changed files with 142 additions and 13 deletions

View File

@ -7171,7 +7171,7 @@ tcp-request connection <action> [{if | unless} <condition>]
accept the incoming connection. There is no specific limit to the number of
rules which may be inserted.
Three types of actions are supported :
Five types of actions are supported :
- accept :
accepts the connection if the condition is true (when used with "if")
or false (when used with "unless"). The first such rule executed ends
@ -7200,6 +7200,18 @@ tcp-request connection <action> [{if | unless} <condition>]
of load balancers are passed through by traffic coming from public
hosts.
- capture <sample> len <length> :
This only applies to "tcp-request content" rules. It captures sample
expression <sample> from the request buffer, and converts it to a
string of at most <len> characters. The resulting string is stored into
the next request "capture" slot, so it will possibly appear next to
some captured HTTP headers. It will then automatically appear in the
logs, and it will be possible to extract it using sample fetch rules to
feed it into headers or anything. The length should be limited given
that this size will be allocated for each capture during the whole
session life. Since it applies to Please check section 7.3 (Fetching
samples) and "capture request header" for more information.
- { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
enables tracking of sticky counters from current connection. These
rules do not stop evaluation and do not change default action. Two sets
@ -7278,8 +7290,8 @@ tcp-request content <action> [{if | unless} <condition>]
Arguments :
<action> defines the action to perform if the condition applies. Valid
actions include : "accept", "reject", "track-sc0", "track-sc1",
and "track-sc2". See "tcp-request connection" above for their
signification.
"track-sc2" and "capture". See "tcp-request connection" above
for their signification.
<condition> is a standard layer 4-7 ACL-based condition (see section 7).
@ -7307,9 +7319,10 @@ tcp-request content <action> [{if | unless} <condition>]
contents. There is no specific limit to the number of rules which may be
inserted.
Three types of actions are supported :
- accept :
- reject :
Four types of actions are supported :
- accept : the request is accepted
- reject : the request is rejected and the connection is closed
- capture : the specified sample expression is captured
- { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>]
They have the same meaning as their counter-parts in "tcp-request connection"

View File

@ -35,6 +35,7 @@
#include <common/standard.h>
#include <types/global.h>
#include <types/capture.h>
#include <types/server.h>
#include <proto/acl.h>
@ -990,13 +991,8 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
if (rule->cond) {
ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial);
if (ret == ACL_TEST_MISS) {
channel_dont_connect(req);
/* just set the request timeout once at the beginning of the request */
if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
return 0;
}
if (ret == ACL_TEST_MISS)
goto missing_data;
ret = acl_pass(ret);
if (rule->cond->pol == ACL_COND_UNLESS)
@ -1040,6 +1036,32 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
stkctr_set_flags(&s->stkctr[tcp_trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
}
}
else if (rule->action == TCP_ACT_CAPTURE) {
struct sample *key;
struct cap_hdr *h = rule->act_prm.cap.hdr;
char **cap = s->txn.req.cap;
int len;
key = sample_fetch_string(s->be, s, &s->txn, SMP_OPT_DIR_REQ | partial, rule->act_prm.cap.expr);
if (!key)
continue;
if (key->flags & SMP_F_MAY_CHANGE)
goto missing_data;
if (cap[h->index] == NULL)
cap[h->index] = pool_alloc2(h->pool);
if (cap[h->index] == NULL) /* no more capture memory */
continue;
len = key->data.str.len;
if (len > h->len)
len = h->len;
memcpy(cap[h->index], key->data.str.str, len);
cap[h->index][len] = 0;
}
else {
/* otherwise accept */
break;
@ -1053,6 +1075,14 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit)
req->analysers &= ~an_bit;
req->analyse_exp = TICK_ETERNITY;
return 1;
missing_data:
channel_dont_connect(req);
/* just set the request timeout once at the beginning of the request */
if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
return 0;
}
/* This function performs the TCP response analysis on the current response. It
@ -1287,6 +1317,92 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
arg++;
rule->action = TCP_ACT_REJECT;
}
else if (strcmp(args[arg], "capture") == 0) {
struct sample_expr *expr;
struct cap_hdr *hdr;
int kw = arg;
int len = 0;
if (!(curpx->cap & PR_CAP_FE)) {
memprintf(err,
"'%s %s %s' : proxy '%s' has no frontend capability",
args[0], args[1], args[kw], curpx->id);
return -1;
}
if (!(where & SMP_VAL_FE_REQ_CNT)) {
memprintf(err,
"'%s %s' is not allowed in '%s %s' rules in %s '%s'",
args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
return -1;
}
arg++;
curpx->conf.args.ctx = ARGC_CAP;
expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
if (!expr) {
memprintf(err,
"'%s %s %s' : %s",
args[0], args[1], args[kw], *err);
return -1;
}
if (!(expr->fetch->val & where)) {
memprintf(err,
"'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
free(expr);
return -1;
}
if (strcmp(args[arg], "len") == 0) {
arg++;
if (!args[arg]) {
memprintf(err,
"'%s %s %s' : missing length value",
args[0], args[1], args[kw]);
free(expr);
return -1;
}
/* we copy the table name for now, it will be resolved later */
len = atoi(args[arg]);
if (len <= 0) {
memprintf(err,
"'%s %s %s' : length must be > 0",
args[0], args[1], args[kw]);
free(expr);
return -1;
}
arg++;
}
if (!len) {
memprintf(err,
"'%s %s %s' : a positive 'len' argument is mandatory",
args[0], args[1], args[kw]);
free(expr);
return -1;
}
hdr = calloc(sizeof(struct cap_hdr), 1);
hdr->next = curpx->req_cap;
hdr->name = NULL; /* not a header capture */
hdr->namelen = 0;
hdr->len = len;
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
hdr->index = curpx->nb_req_cap++;
curpx->req_cap = hdr;
curpx->to_log |= LW_REQHDR;
/* check if we need to allocate an hdr_idx struct for HTTP parsing */
curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
rule->act_prm.cap.expr = expr;
rule->act_prm.cap.hdr = hdr;
rule->action = TCP_ACT_CAPTURE;
}
else if (strncmp(args[arg], "track-sc", 8) == 0 &&
args[arg][9] == '\0' && args[arg][8] >= '0' &&
args[arg][8] <= '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */