diff --git a/doc/configuration.txt b/doc/configuration.txt index 83f337d00..8447ff1d3 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3365,6 +3365,7 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | set-map() | set-var() | { track-sc0 | track-sc1 | track-sc2 } [table ] | + sc-set-gpt0() | lua } [ { if | unless } ] @@ -3628,6 +3629,12 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | advantage over just checking the keys, because only one table lookup is performed for all ACL checks that make use of it. + - sc-set-gpt0() : + This action sets the GPT0 tag according to the sticky counter designated + by and the value of . The expected result is a boolean. If + an error occurs, this action silently fails and the actions evaluation + continues. + - "lua" is used to run a Lua function if the action is executed. The single parameter is the name of the function to run. The prototype of the function is documented in the API documentation. @@ -3738,6 +3745,7 @@ http-response { allow | deny | add-header | set-nice | del-map() | set-map() | set-var() | + sc-set-gpt0() | lua } [ { if | unless } ] @@ -3936,6 +3944,12 @@ http-response { allow | deny | add-header | set-nice | http-response set-var(sess.last_redir) res.hdr(location) + - sc-set-gpt0() : + This action sets the GPT0 tag according to the sticky counter designated + by and the value of . The expected result is a boolean. If + an error occurs, this action silently fails and the actions evaluation + continues. + There is no limit to the number of http-response statements per instance. It is important to know that http-response rules are processed very early in @@ -8298,6 +8312,12 @@ tcp-request connection [{if | unless} ] advantage over just checking the keys, because only one table lookup is performed for all ACL checks that make use of it. + - sc-set-gpt0() : + This action sets the GPT0 tag according to the sticky counter designated + by and the value of . The expected result is a boolean. If + an error occurs, this action silently fails and the actions evaluation + continues. + Note that the "if/unless" condition is optional. If no condition is set on the action, it is simply performed unconditionally. That can be useful for "track-sc*" actions as well as for changing the default action to a reject. @@ -8334,8 +8354,8 @@ tcp-request content [{if | unless} ] Arguments : defines the action to perform if the condition applies. Valid actions include : "accept", "reject", "track-sc0", "track-sc1", - "track-sc2", "capture" and "lua". See "tcp-request connection" - above for their signification. + "track-sc2", "sc-set-gpt0", "capture" and "lua". See + "tcp-request connection" above for their signification. is a standard layer 4-7 ACL-based condition (see section 7). @@ -8363,11 +8383,12 @@ tcp-request content [{if | unless} ] contents. There is no specific limit to the number of rules which may be inserted. - Four types of actions are supported : + Several 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 } [table
] + - set-gpt0() - lua - set-var() @@ -8535,7 +8556,8 @@ tcp-response content [{if | unless} ] no | no | yes | yes Arguments : defines the action to perform if the condition applies. Valid - actions include : "accept", "close", "reject", "lua". + actions include : "accept", "close", "reject", "lua", and + "sc-set-gpt0". is a standard layer 4-7 ACL-based condition (see section 7). @@ -8552,7 +8574,7 @@ tcp-response content [{if | unless} ] contents. There is no specific limit to the number of rules which may be inserted. - Two types of actions are supported : + Several types of actions are supported : - accept : accepts the response if the condition is true (when used with "if") or false (when used with "unless"). The first such rule executed ends @@ -8579,6 +8601,12 @@ tcp-response content [{if | unless} ] - set-var() Sets a variable. + - sc-set-gpt0() : + This action sets the GPT0 tag according to the sticky counter designated + by and the value of . The expected result is a boolean. If + an error occurs, this action silently fails and the actions evaluation + continues. + Note that the "if/unless" condition is optional. If no condition is set on the action, it is simply performed unconditionally. That can be useful for for changing the default action to a reject. @@ -11545,6 +11573,13 @@ table_conn_rate(
) rate associated with the input sample in the designated table. See also the sc_conn_rate sample fetch keyword. +table_gpt0(
) + Uses the string representation of the input sample to perform a look up in + the specified table. If the key is not found in the table, boolean value zero + is returned. Otherwise the converter returns the current value of the first + general purpose tag associated with the input sample in the designated table. + See also the sc_get_gpt0 sample fetch keyword. + table_gpc0(
) Uses the string representation of the input sample to perform a look up in the specified table. If the key is not found in the table, integer value zero @@ -12069,6 +12104,13 @@ sc2_get_gpc0([
]) : integer Returns the value of the first General Purpose Counter associated to the currently tracked counters. See also src_get_gpc0 and sc/sc0/sc1/sc2_inc_gpc0. +sc_get_gpt0([,
]) : integer +sc0_get_gpt0([
]) : integer +sc1_get_gpt0([
]) : integer +sc2_get_gpt0([
]) : integer + Returns the value of the first General Purpose Tag associated to the + currently tracked counters. See also src_get_gpt0. + sc_gpc0_rate([,
]) : integer sc0_gpc0_rate([
]) : integer sc1_gpc0_rate([
]) : integer @@ -12257,6 +12299,12 @@ src_get_gpc0([
]) : integer the designated stick-table. If the address is not found, zero is returned. See also sc/sc0/sc1/sc2_get_gpc0 and src_inc_gpc0. +src_get_gpt0([
]) : integer + Returns the value of the first General Purpose Tag associated to the + incoming connection's source address in the current proxy's stick-table or in + the designated stick-table. If the address is not found, zero is returned. + See also sc/sc0/sc1/sc2_get_gpt0. + src_gpc0_rate([
]) : integer Returns the average increment rate of the first General Purpose Counter associated to the incoming connection's source address in the current proxy's diff --git a/include/types/action.h b/include/types/action.h index fd264cbb3..5d1b2501f 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -130,6 +130,10 @@ struct act_rule { const char *name; enum vars_scope scope; } vars; + struct { + int sc; + long long int value; + } gpt; struct track_ctr_prm trk_ctr; struct { void *p[4]; diff --git a/src/stick_table.c b/src/stick_table.c index 5e7a2c48f..623667f29 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include @@ -854,6 +856,41 @@ static int sample_conv_table_bytes_out_rate(const struct arg *arg_p, struct samp return 1; } +/* Casts sample to the type of the table specified in arg(0), and looks + * it up into this table. Returns the value of the GPT0 tag for the key + * if the key is present in the table, otherwise false, so that comparisons can + * be easily performed. If the inspected parameter is not stored in the table, + * is returned. + */ +static int sample_conv_table_gpt0(const struct arg *arg_p, struct sample *smp, void *private) +{ + struct stktable *t; + struct stktable_key *key; + struct stksess *ts; + void *ptr; + + t = &arg_p[0].data.prx->table; + + key = smp_to_stkey(smp, t); + if (!key) + return 0; + + smp->flags = SMP_F_VOL_TEST; + smp->data.type = SMP_T_SINT; + smp->data.u.sint = 0; + + ts = stktable_lookup_key(t, key); + if (!ts) /* key not present */ + return 1; + + ptr = stktable_data_ptr(t, ts, STKTABLE_DT_GPT0); + if (!ptr) + return 0; /* parameter not stored */ + + smp->data.u.sint = stktable_data_cast(ptr, gpt0); + return 1; +} + /* Casts sample to the type of the table specified in arg(0), and looks * it up into this table. Returns the value of the GPC0 counter for the key * if the key is present in the table, otherwise zero, so that comparisons can @@ -1272,6 +1309,109 @@ static int sample_conv_table_trackers(const struct arg *arg_p, struct sample *sm return 1; } +/* Always returns 1. */ +static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s) +{ + void *ptr; + struct stksess *ts; + struct stkctr *stkctr; + + /* Extract the stksess, return OK if no stksess available. */ + if (s) + stkctr = &s->stkctr[rule->arg.gpt.sc]; + else + stkctr = &sess->stkctr[rule->arg.gpt.sc]; + ts = stkctr_entry(stkctr); + if (!ts) + return ACT_RET_CONT; + + /* Store the sample in the required sc, and ignore errors. */ + ptr = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPT0); + if (ptr) + stktable_data_cast(ptr, gpt0) = rule->arg.gpt.value; + return ACT_RET_CONT; +} + +/* This function is a common parser for using variables. It understands + * the format: + * + * set-gpt0() + * + * It returns 0 if fails and is filled with an error message. Otherwise, + * it returns 1 and the variable is filled with the pointer to the + * expression to execute. + */ +static enum act_parse_ret parse_set_gpt0(const char **args, int *arg, struct proxy *px, + struct act_rule *rule, char **err) + + +{ + const char *cmd_name = args[*arg-1]; + char *error; + + cmd_name += strlen("sc-set-gpt0"); + if (*cmd_name == '\0') { + /* default stick table id. */ + rule->arg.gpt.sc = 0; + } else { + /* parse the stick table id. */ + if (*cmd_name != '(') { + memprintf(err, "invalid stick table track ID '%s'. Expects sc-set-gpt0()", args[*arg-1]); + return ACT_RET_PRS_ERR; + } + cmd_name++; /* jump the '(' */ + rule->arg.gpt.sc = strtol(cmd_name, &error, 10); /* Convert stick table id. */ + if (*error != ')') { + memprintf(err, "invalid stick table track ID '%s'. Expects sc-set-gpt0()", args[*arg-1]); + return ACT_RET_PRS_ERR; + } + + 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); + return ACT_RET_PRS_ERR; + } + } + + 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; + } + (*arg)++; + + rule->action = ACT_ACTION_CONT; + rule->action_ptr = action_set_gpt0; + + return ACT_RET_PRS_OK; +} + +static struct action_kw_list tcp_conn_kws = { { }, { + { "sc-set-gpt0", parse_set_gpt0, 1 }, + { /* END */ } +}}; + +static struct action_kw_list tcp_req_kws = { { }, { + { "sc-set-gpt0", parse_set_gpt0, 1 }, + { /* END */ } +}}; + +static struct action_kw_list tcp_res_kws = { { }, { + { "sc-set-gpt0", parse_set_gpt0, 1 }, + { /* END */ } +}}; + +static struct action_kw_list http_req_kws = { { }, { + { "sc-set-gpt0", parse_set_gpt0, 1 }, + { /* END */ } +}}; + +static struct action_kw_list http_res_kws = { { }, { + { "sc-set-gpt0", parse_set_gpt0, 1 }, + { /* END */ } +}}; + /* Note: must not be declared as its list will be overwritten */ static struct sample_conv_kw_list sample_conv_kws = {ILH, { { "in_table", sample_conv_in_table, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_BOOL }, @@ -1280,6 +1420,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, { { "table_conn_cnt", sample_conv_table_conn_cnt, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, { "table_conn_cur", sample_conv_table_conn_cur, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, { "table_conn_rate", sample_conv_table_conn_rate, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, + { "table_gpt0", sample_conv_table_gpt0, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, { "table_gpc0", sample_conv_table_gpc0, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, { "table_gpc0_rate", sample_conv_table_gpc0_rate, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, { "table_http_err_cnt", sample_conv_table_http_err_cnt, ARG1(1,TAB), NULL, SMP_T_STR, SMP_T_SINT }, @@ -1298,6 +1439,13 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, { __attribute__((constructor)) static void __stick_table_init(void) { + /* register som action keywords. */ + tcp_req_conn_keywords_register(&tcp_conn_kws); + tcp_req_cont_keywords_register(&tcp_req_kws); + tcp_res_cont_keywords_register(&tcp_res_kws); + http_req_keywords_register(&http_req_kws); + http_res_keywords_register(&http_res_kws); + /* register sample fetch and format conversion keywords */ sample_register_convs(&sample_conv_kws); } diff --git a/src/stream.c b/src/stream.c index 278bdb856..b8973544f 100644 --- a/src/stream.c +++ b/src/stream.c @@ -2694,6 +2694,32 @@ smp_fetch_sc_tracked(const struct arg *args, struct sample *smp, const char *kw, return 1; } +/* set to the General Purpose Flag 0 value from the stream's tracked + * frontend counters or from the src. + * Supports being called as "sc[0-9]_get_gpc0" or "src_get_gpt0" only. Value + * zero is returned if the key is new. + */ +static int +smp_fetch_sc_get_gpt0(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct stkctr *stkctr = smp_fetch_sc_stkctr(smp->sess, smp->strm, args, kw); + + if (!stkctr) + return 0; + + smp->flags = SMP_F_VOL_TEST; + smp->data.type = SMP_T_SINT; + smp->data.u.sint = 0; + + if (stkctr_entry(stkctr) != NULL) { + void *ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPT0); + if (!ptr) + return 0; /* parameter not stored */ + smp->data.u.sint = stktable_data_cast(ptr, gpt0); + } + return 1; +} + /* set to the General Purpose Counter 0 value from the stream's tracked * frontend counters or from the src. * Supports being called as "sc[0-9]_get_gpc0" or "src_get_gpc0" only. Value @@ -3240,6 +3266,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc_conn_cnt", smp_fetch_sc_conn_cnt, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_conn_cur", smp_fetch_sc_conn_cur, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_conn_rate", smp_fetch_sc_conn_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc_get_gpt0", smp_fetch_sc_get_gpt0, ARG2(1,SINT,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, { "sc_get_gpc0", smp_fetch_sc_get_gpc0, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -3259,6 +3286,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc0_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc0_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, { "sc0_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -3278,6 +3306,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc1_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc1_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, { "sc1_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -3297,6 +3326,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc2_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc2_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, { "sc2_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -3316,6 +3346,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "src_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_conn_cur", smp_fetch_sc_conn_cur, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_conn_rate", smp_fetch_sc_conn_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, + { "src_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(1,TAB), NULL, SMP_T_BOOL, SMP_USE_L4CLI, }, { "src_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, },