[Sorry for multiple posting. I'm having trouble with sending this patch via GMail *SMTP*; trying the WUI...]
Thanks Willy for your feedback. I'm not sure whether I should further split stktable_fetch_expr() into some new sample_...() in sample.c or if there is some other *.c file where a generic function, including the required act_rule, would make sense. Also, I'm not HAProxy-savvy enough to know whether this proposal is multi-thread safe (is it ?). [patch] Allow the sc-set-gpt0 action to set GPT0 to a value dynamically evaluated from its <expr> argument (in addition to the existing static <int> alternative). --- doc/configuration.txt | 44 ++++++++++++----------- include/types/action.h | 1 + src/stick_table.c | 82 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 8dedbfc48..bea62bd98 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4465,11 +4465,13 @@ http-request sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ] counter designated by <sc-id>. If an error occurs, this action silently fails and the actions evaluation continues. -http-request sc-set-gpt0(<sc-id>) <int> [ { if | unless } <condition> ] +http-request sc-set-gpt0(<sc-id>) { <int> | <expr> } + [ { if | unless } <condition> ] - This action sets the GPT0 tag according to the sticky counter designated by - <sc-id> and the value of <int>. The expected result is a boolean. If an error - occurs, this action silently fails and the actions evaluation continues. + This action sets the 32-bit unsigned GPT0 tag according to the sticky counter + designated by <sc-id> and the value of <int>/<expr>. The expected result is a + boolean. If an error occurs, this action silently fails and the actions + evaluation continues. http-request set-dst <expr> [ { if | unless } <condition> ] @@ -4974,11 +4976,13 @@ http-response sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ] counter designated by <sc-id>. If an error occurs, this action silently fails and the actions evaluation continues. -http-response sc-set-gpt0(<sc-id>) <int> [ { if | unless } <condition> ] +http-response sc-set-gpt0(<sc-id>) { <int> | <expr> } + [ { if | unless } <condition> ] - This action sets the GPT0 tag according to the sticky counter designated by - <sc-id> and the value of <int>. The expected result is a boolean. If an error - occurs, this action silently fails and the actions evaluation continues. + This action sets the 32-bit unsigned GPT0 tag according to the sticky counter + designated by <sc-id> and the value of <int>/<expr>. The expected result is a + boolean. If an error occurs, this action silently fails and the actions + evaluation continues. http-response send-spoe-group [ { if | unless } <condition> ] @@ -9394,11 +9398,11 @@ tcp-request connection <action> [{if | unless} <condition>] counter designated by <sc-id>. If an error occurs, this action silently fails and the actions evaluation continues. - - sc-set-gpt0(<sc-id>) <int>: - This action sets the GPT0 tag according to the sticky counter designated - by <sc-id> and the value of <int>. The expected result is a boolean. If - an error occurs, this action silently fails and the actions evaluation - continues. + - sc-set-gpt0(<sc-id>) { <int> | <expr> }: + This action sets the 32-bit unsigned GPT0 tag according to the sticky + counter designated by <sc-id> and the value of <int>/<expr>. The + expected result is a boolean. If an error occurs, this action silently + fails and the actions evaluation continues. - set-src <expr> : Is used to set the source IP address to the value of specified @@ -9556,7 +9560,7 @@ tcp-request content <action> [{if | unless} <condition>] - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] - sc-inc-gpc0(<sc-id>) - sc-inc-gpc1(<sc-id>) - - sc-set-gpt0(<sc-id>) <int> + - sc-set-gpt0(<sc-id>) { <int> | <expr> } - set-dst <expr> - set-dst-port <expr> - set-var(<var-name>) <expr> @@ -9820,11 +9824,11 @@ tcp-response content <action> [{if | unless} <condition>] counter designated by <sc-id>. If an error occurs, this action fails silently and the actions evaluation continues. - - sc-set-gpt0(<sc-id>) <int> : - This action sets the GPT0 tag according to the sticky counter designated - by <sc-id> and the value of <int>. The expected result is a boolean. If - an error occurs, this action silently fails and the actions evaluation - continues. + - sc-set-gpt0(<sc-id>) { <int> | <expr> } + This action sets the 32-bit unsigned GPT0 tag according to the sticky + counter designated by <sc-id> and the value of <int>/<expr>. The + expected result is a boolean. If an error occurs, this action silently + fails and the actions evaluation continues. - "silent-drop" : This stops the evaluation of the rules and makes the client-facing @@ -9945,7 +9949,7 @@ tcp-request session <action> [{if | unless} <condition>] - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] - sc-inc-gpc0(<sc-id>) - sc-inc-gpc1(<sc-id>) - - sc-set-gpt0(<sc-id>) <int> + - sc-set-gpt0(<sc-id>) { <int> | <expr> } - set-var(<var-name>) <expr> - unset-var(<var-name>) - silent-drop diff --git a/include/types/action.h b/include/types/action.h index 54a6f71a4..516ffddd0 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -168,6 +168,7 @@ struct act_rule { struct { int sc; long long int value; + struct sample_expr *expr; } gpt; struct track_ctr_prm trk_ctr; struct { diff --git a/src/stick_table.c b/src/stick_table.c index c9f3e0636..cd0cbe862 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -1845,6 +1845,48 @@ static int sample_conv_table_trackers(const struct arg *arg_p, struct sample *sm return 1; } +/* Fetches and hydrates the sample data from the given expression. + * Returns 1 on success, 0 otherwise. + */ +static int stktable_fetch_expr(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, + struct sample_expr *expr, + int smp_type, struct sample *smp) +{ + int smp_opt_dir; + + switch (rule->from) { + case ACT_F_TCP_REQ_SES: smp_opt_dir = SMP_OPT_DIR_REQ; break; + case ACT_F_TCP_REQ_CNT: smp_opt_dir = SMP_OPT_DIR_REQ; break; + case ACT_F_TCP_RES_CNT: smp_opt_dir = SMP_OPT_DIR_RES; break; + case ACT_F_HTTP_REQ: smp_opt_dir = SMP_OPT_DIR_REQ; break; + case ACT_F_HTTP_RES: smp_opt_dir = SMP_OPT_DIR_RES; break; + default: + send_log(px, LOG_ERR, "stick table: internal error while fetching expression."); + if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) + ha_alert("stick table: internal error while executing fetching expression.\n"); + return 0; + } + + /* Process the expression. */ + memset(smp, 0, sizeof(*smp)); + if (!sample_process(px, sess, s, smp_opt_dir|SMP_OPT_FINAL, expr, smp)) + return 0; + + /* Check the sample data type. */ + if (!sample_casts[smp->data.type][smp_type] || + !sample_casts[smp->data.type][smp_type](smp)) { + send_log(px, LOG_WARNING, "stick table: invalid data type while fetching expression."); + if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) + ha_alert("stick table: invalid data type while fetching expression.\n"); + return 0; + } + + /* OK cast succeeded. */ + + return 1; +} + /* Always returns 1. */ static enum act_return action_inc_gpc0(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags) @@ -2016,6 +2058,8 @@ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px, void *ptr; struct stksess *ts; struct stkctr *stkctr; + unsigned int value = 0; + struct sample smp; /* Extract the stksess, return OK if no stksess available. */ if (s) @@ -2030,9 +2074,17 @@ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px, /* Store the sample in the required sc, and ignore errors. */ ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPT0); if (ptr) { + if (!rule->arg.gpt.expr) + value = (unsigned int)(rule->arg.gpt.value); + else { + if (!stktable_fetch_expr(rule, px, sess, s, rule->arg.gpt.expr, SMP_T_SINT, &smp)) + return ACT_RET_CONT; + value = (unsigned int)(smp.data.u.sint); + } + HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock); - stktable_data_cast(ptr, gpt0) = rule->arg.gpt.value; + stktable_data_cast(ptr, gpt0) = value; HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock); @@ -2058,6 +2110,7 @@ static enum act_parse_ret parse_set_gpt0(const char **args, int *arg, struct pro { const char *cmd_name = args[*arg-1]; char *error; + int smp_val; cmd_name += strlen("sc-set-gpt0"); if (*cmd_name == '\0') { @@ -2077,16 +2130,35 @@ static enum act_parse_ret parse_set_gpt0(const char **args, int *arg, struct pro } if (rule->arg.gpt.sc >= ACT_ACTION_TRK_SCMAX) { - memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d", - args[*arg-1], ACT_ACTION_TRK_SCMAX-1); + memprintf(err, "invalid stick table track ID '%s'. The max allowed ID is %d", args[*arg-1], ACT_ACTION_TRK_SCMAX-1); return ACT_RET_PRS_ERR; } } + rule->arg.gpt.expr = NULL; rule->arg.gpt.value = strtol(args[*arg], &error, 10); if (*error != '\0') { - memprintf(err, "invalid integer value '%s'", args[*arg]); - return ACT_RET_PRS_ERR; + rule->arg.gpt.expr = sample_parse_expr((char **)args, arg, px->conf.args.file, + px->conf.args.line, err, &px->conf.args); + if (!rule->arg.gpt.expr) + return ACT_RET_PRS_ERR; + + switch (rule->from) { + case ACT_F_TCP_REQ_SES: smp_val = SMP_VAL_FE_SES_ACC; break; + case ACT_F_TCP_REQ_CNT: smp_val = SMP_VAL_FE_REQ_CNT; break; + case ACT_F_TCP_RES_CNT: smp_val = SMP_VAL_BE_RES_CNT; break; + case ACT_F_HTTP_REQ: smp_val = SMP_VAL_FE_HRQ_HDR; break; + case ACT_F_HTTP_RES: smp_val = SMP_VAL_BE_HRS_HDR; break; + default: + memprintf(err, "internal error, unexpected rule->from=%d, please report this bug!", rule->from); + return ACT_RET_PRS_ERR; + } + if (!(rule->arg.gpt.expr->fetch->val & smp_val)) { + memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here", + args[*arg-1], sample_src_names(rule->arg.gpt.expr->fetch->use)); + free(rule->arg.gpt.expr); + return ACT_RET_PRS_ERR; + } } (*arg)++; -- 2.20.1