MEDIUM: proto_tcp: add support for tracking L7 information
Until now it was only possible to use track-sc1/sc2 with "src" which
is the IPv4 source address. Now we can use track-sc1/sc2 with any fetch
as well as any transformation type. It works just like the "stick"
directive.
Samples are automatically converted to the correct types for the table.
Only "tcp-request content" rules may use L7 information, and such information
must already be present when the tracking is set up. For example it becomes
possible to track the IP address passed in the X-Forwarded-For header.
HTTP request processing now also considers tracking from backend rules
because we want to be able to update the counters even when the request
was already parsed and tracked.
Some more controls need to be performed (eg: samples do not distinguish
between L4 and L6).
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 4e5963e..f1c45a0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -6263,11 +6263,6 @@
curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
cfgerr++;
}
- else if (trule->act_prm.trk_ctr.type != target->table.type) {
- Alert("Proxy '%s': type of tracking key for sticky counter not usable with type of stick-table '%s'.\n",
- curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
- cfgerr++;
- }
else {
free(trule->act_prm.trk_ctr.table.n);
trule->act_prm.trk_ctr.table.t = &target->table;
@@ -6301,11 +6296,6 @@
curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
cfgerr++;
}
- else if (trule->act_prm.trk_ctr.type != target->table.type) {
- Alert("Proxy '%s': type of tracking key for sticky counter not usable with type of stick-table '%s'.\n",
- curproxy->id, trule->act_prm.trk_ctr.table.n ? trule->act_prm.trk_ctr.table.n : curproxy->id);
- cfgerr++;
- }
else {
free(trule->act_prm.trk_ctr.table.n);
trule->act_prm.trk_ctr.table.t = &target->table;
diff --git a/src/proto_http.c b/src/proto_http.c
index 54850d7..ab8061a 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3013,6 +3013,9 @@
}
}
+ /* just in case we have some per-backend tracking */
+ session_inc_be_http_req_ctr(s);
+
/* evaluate http-request rules */
http_req_last_rule = http_check_access_rule(px, &px->http_req_rules, s, txn);
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index eb44442..de95d7f 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -836,32 +836,22 @@
s->flags |= SN_FINST_R;
return 0;
}
- else if (rule->action == TCP_ACT_TRK_SC1) {
- if (!s->stkctr1_entry) {
- /* only the first valid track-sc1 directive applies.
- * Also, note that right now we can only track SRC so we
- * don't check how to get the key, but later we may need
- * to consider rule->act_prm->trk_ctr.type.
- */
- t = rule->act_prm.trk_ctr.table.t;
- ts = stktable_get_entry(t, addr_to_stktable_key(&s->si[0].conn->addr.from));
- if (ts) {
+ else if ((rule->action == TCP_ACT_TRK_SC1 && !s->stkctr1_entry) ||
+ (rule->action == TCP_ACT_TRK_SC2 && !s->stkctr2_entry)) {
+ /* Note: only the first valid tracking parameter of each
+ * applies.
+ */
+ struct stktable_key *key;
+
+ t = rule->act_prm.trk_ctr.table.t;
+ key = stktable_fetch_key(t, s->be, s, &s->txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->act_prm.trk_ctr.expr);
+
+ if (key && (ts = stktable_get_entry(t, key))) {
+ if (rule->action == TCP_ACT_TRK_SC1) {
session_track_stkctr1(s, t, ts);
if (s->fe != s->be)
s->flags |= SN_BE_TRACK_SC1;
- }
- }
- }
- else if (rule->action == TCP_ACT_TRK_SC2) {
- if (!s->stkctr2_entry) {
- /* only the first valid track-sc2 directive applies.
- * Also, note that right now we can only track SRC so we
- * don't check how to get the key, but later we may need
- * to consider rule->act_prm->trk_ctr.type.
- */
- t = rule->act_prm.trk_ctr.table.t;
- ts = stktable_get_entry(t, addr_to_stktable_key(&s->si[0].conn->addr.from));
- if (ts) {
+ } else {
session_track_stkctr2(s, t, ts);
if (s->fe != s->be)
s->flags |= SN_BE_TRACK_SC2;
@@ -1006,29 +996,20 @@
result = 0;
break;
}
- else if (rule->action == TCP_ACT_TRK_SC1) {
- if (!s->stkctr1_entry) {
- /* only the first valid track-sc1 directive applies.
- * Also, note that right now we can only track SRC so we
- * don't check how to get the key, but later we may need
- * to consider rule->act_prm->trk_ctr.type.
- */
- t = rule->act_prm.trk_ctr.table.t;
- ts = stktable_get_entry(t, addr_to_stktable_key(&s->si[0].conn->addr.from));
- if (ts)
+ else if ((rule->action == TCP_ACT_TRK_SC1 && !s->stkctr1_entry) ||
+ (rule->action == TCP_ACT_TRK_SC2 && !s->stkctr2_entry)) {
+ /* Note: only the first valid tracking parameter of each
+ * applies.
+ */
+ struct stktable_key *key;
+
+ t = rule->act_prm.trk_ctr.table.t;
+ key = stktable_fetch_key(t, s->be, s, &s->txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->act_prm.trk_ctr.expr);
+
+ if (key && (ts = stktable_get_entry(t, key))) {
+ if (rule->action == TCP_ACT_TRK_SC1)
session_track_stkctr1(s, t, ts);
- }
- }
- else if (rule->action == TCP_ACT_TRK_SC2) {
- if (!s->stkctr2_entry) {
- /* only the first valid track-sc2 directive applies.
- * Also, note that right now we can only track SRC so we
- * don't check how to get the key, but later we may need
- * to consider rule->act_prm->trk_ctr.type.
- */
- t = rule->act_prm.trk_ctr.table.t;
- ts = stktable_get_entry(t, addr_to_stktable_key(&s->si[0].conn->addr.from));
- if (ts)
+ else
session_track_stkctr2(s, t, ts);
}
}
@@ -1105,37 +1086,50 @@
arg++;
rule->action = TCP_ACT_REJECT;
}
- else if (strcmp(args[arg], "track-sc1") == 0) {
- int ret;
+ else if (strcmp(args[arg], "track-sc1") == 0 || strcmp(args[arg], "track-sc2") == 0) {
+ struct sample_expr *expr;
int kw = arg;
arg++;
- ret = parse_track_counters(args, &arg, section_type, curpx,
- &rule->act_prm.trk_ctr, defpx, err);
- if (ret < 0) { /* nb: warnings are not handled yet */
+ expr = sample_parse_expr(args, &arg, trash.str, trash.size);
+ if (!expr) {
memprintf(err,
- "'%s %s %s' : %s in %s '%s'",
- args[0], args[1], args[kw], *err, proxy_type_str(curpx), curpx->id);
- return ret;
+ "'%s %s %s' : %s",
+ args[0], args[1], args[kw], trash.str);
+ return -1;
}
- rule->action = TCP_ACT_TRK_SC1;
- }
- else if (strcmp(args[arg], "track-sc2") == 0) {
- int ret;
- int kw = arg;
- arg++;
- ret = parse_track_counters(args, &arg, section_type, curpx,
- &rule->act_prm.trk_ctr, defpx, err);
-
- if (ret < 0) { /* nb: warnings are not handled yet */
+ if (!(expr->fetch->cap & SMP_CAP_REQ)) {
memprintf(err,
- "'%s %s %s' : %s in %s '%s'",
- args[0], args[1], args[kw], *err, proxy_type_str(curpx), curpx->id);
- return ret;
+ "'%s %s %s' : fetch method '%s' cannot be used on request",
+ args[0], args[1], args[kw], trash.str);
+ free(expr);
+ return -1;
+ }
+
+ /* check if we need to allocate an hdr_idx struct for HTTP parsing */
+ if (expr->fetch->cap & SMP_CAP_L7)
+ curpx->acl_requires |= ACL_USE_L7_ANY;
+
+ if (strcmp(args[arg], "table") == 0) {
+ if (!args[arg + 1]) {
+ memprintf(err,
+ "'%s %s %s' : missing table name",
+ args[0], args[1], args[kw]);
+ free(expr);
+ return -1;
+ }
+ /* we copy the table name for now, it will be resolved later */
+ rule->act_prm.trk_ctr.table.n = strdup(args[arg + 1]);
+ arg++;
}
- rule->action = TCP_ACT_TRK_SC2;
+ rule->act_prm.trk_ctr.expr = expr;
+
+ if (args[kw][8] == '1')
+ rule->action = TCP_ACT_TRK_SC1;
+ else
+ rule->action = TCP_ACT_TRK_SC2;
}
else {
memprintf(err,
@@ -1312,6 +1306,15 @@
name, args[0], args[1]);
warn++;
}
+
+ if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
+ (rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_RES)) {
+ memprintf(err,
+ "fetch '%s' involves some response-only criteria which will be ignored in '%s %s'",
+ rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
+ warn++;
+ }
+
LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
}
else if (strcmp(args[1], "connection") == 0) {
@@ -1346,6 +1349,23 @@
name, args[0], args[1]);
warn++;
}
+
+ if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
+ (rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_RES)) {
+ memprintf(err,
+ "fetch '%s' involves some response-only criteria which will be ignored in '%s %s'",
+ rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
+ warn++;
+ }
+
+ if ((rule->action == TCP_ACT_TRK_SC1 || rule->action == TCP_ACT_TRK_SC2) &&
+ (rule->act_prm.trk_ctr.expr->fetch->cap & SMP_CAP_L7)) {
+ memprintf(err,
+ "fetch '%s' involves some layer7-only criteria which will be ignored in '%s %s'",
+ rule->act_prm.trk_ctr.expr->fetch->kw, args[0], args[1]);
+ warn++;
+ }
+
LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
}
else {
diff --git a/src/session.c b/src/session.c
index 6e098e2..0bee957 100644
--- a/src/session.c
+++ b/src/session.c
@@ -3682,54 +3682,6 @@
{ NULL, NULL, NULL, NULL },
}};
-
-/* Parse a "track-sc[12]" line starting with "track-sc[12]" in args[arg-1].
- * Returns the number of warnings emitted, or -1 in case of fatal errors. The
- * <prm> struct is fed with the table name if any. If unspecified, the caller
- * will assume that the current proxy's table is used.
- */
-int parse_track_counters(char **args, int *arg,
- int section_type, struct proxy *curpx,
- struct track_ctr_prm *prm,
- struct proxy *defpx, char **err)
-{
- int sample_type = 0;
-
- /* parse the arguments of "track-sc[12]" before the condition in the
- * following form :
- * track-sc[12] src [ table xxx ] [ if|unless ... ]
- */
- while (args[*arg]) {
- if (strcmp(args[*arg], "src") == 0) {
- prm->type = STKTABLE_TYPE_IP;
- sample_type = 1;
- }
- else if (strcmp(args[*arg], "table") == 0) {
- if (!args[*arg + 1]) {
- memprintf(err, "missing table name");
- return -1;
- }
- /* we copy the table name for now, it will be resolved later */
- prm->table.n = strdup(args[*arg + 1]);
- (*arg)++;
- }
- else {
- /* unhandled keywords are handled by the caller */
- break;
- }
- (*arg)++;
- }
-
- if (!sample_type) {
- memprintf(err,
- "tracking key not specified (found %s, only 'src' is supported)",
- quote_arg(args[*arg]));
- return -1;
- }
-
- return 0;
-}
-
__attribute__((constructor))
static void __session_init(void)
{