Hi Mark, Probably this wasn't needed at the time, but it makes sense to be able to decrement gpc0 and not just increment it. Here is a patch adding sc_dec_gpc0 and since gpc0 is a counter it can't be decremented below zero. If the patch is useful and clean, we can consider merging it.
Regards, On 19/08/2017 14:15, Mark Staudinger wrote: > Hi Folks, > > Probably a question for Willy, but perhaps others worked on this code > so to the mailing list it goes. > > I was curious as to why there is no sc_dec_gpc0 implemented in the > sample fetch / ACL code. > > sc_inc_gpc0 does exist of course, and it's well-documented how it can > be used to mark an event(s) and use the value as a threshold for ACLs. > > I had an idea of using that value as a counterbalance of two types of > traffic so as to use the gpc0 value as the differential between the two. > > Request type A -> sc0_inc_gpc0 > Request type B -> sc0_dec_gpc0 > > after which two requests, the gpc0 value would remain unchanged from > the original value. > > However I quickly determined that there was no sc_dec_gpc0 feature. > > Is there some architectural reason why this would be difficult or > impractical to do? Or is it just something that didn't seem > necessary/useful at the time? > > Regards, > Mark Staudinger > -- Moemen MHEDHBI
From 66707e9fc90fb2726c8e7dd9f060a52325b780bd Mon Sep 17 00:00:00 2001 From: Moemen MHEDHBI <mmhed...@haproxy.com> Date: Mon, 28 Aug 2017 17:55:38 +0200 Subject: [PATCH] MINOR: add sc-dec-gpc0 to decrement gpc0 counter. Since GPC0 is a general purpose counter, it should be possible to decrement it with sc-dec-gpc0 besides incrementing it with sc-inc-gpc0. Decrementing GPC0 counter won't update gpc0_rate. --- doc/configuration.txt | 59 +++++++++++++++++++++---- src/stick_table.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 11 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 9f7f9ff..aef7c7a 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3765,6 +3765,7 @@ http-request { allow | auth [realm <realm>] | redirect <rule> | unset-var(<var name>) | { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] | sc-inc-gpc0(<sc-id>) | + sc-dec-gpc0(<sc-id>) | sc-set-gpt0(<sc-id>) <int> | silent-drop | } @@ -4046,6 +4047,11 @@ http-request { allow | auth [realm <realm>] | redirect <rule> | designated by <sc-id>. If an error occurs, this action silently fails and the actions evaluation continues. + - sc-dec-gpc0(<sc-id>): + This action decrements the GPC0 counter according with the sticky counter + designated by <sc-id>. If an error occurs, this action silently fails and + the actions evaluation continues. + - set-var(<var-name>) <expr> : Is used to set the contents of a variable. The variable is declared inline. @@ -4238,6 +4244,7 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> | unset-var(<var-name>) | { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] | sc-inc-gpc0(<sc-id>) | + sc-dec-gpc0(<sc-id>) | sc-set-gpt0(<sc-id>) <int> | silent-drop | } @@ -4474,6 +4481,11 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> | designated by <sc-id>. If an error occurs, this action silently fails and the actions evaluation continues. + - sc-dec-gpc0(<sc-id>): + This action decrements the GPC0 counter according with the sticky counter + designated by <sc-id>. 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 connection suddenly disappear using a system-dependant way that tries to prevent the client from being notified. The effect it then @@ -9069,6 +9081,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-dec-gpc0(<sc-id>): + The "sc-dec-gpc0" decrements the GPC0 counter according to the sticky + 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 @@ -9228,6 +9245,7 @@ tcp-request content <action> [{if | unless} <condition>] - capture : the specified sample expression is captured - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] - sc-inc-gpc0(<sc-id>) + - sc-dec-gpc0(<sc-id>) - sc-set-gpt0(<sc-id>) <int> - set-var(<var-name>) <expr> - unset-var(<var-name>) @@ -9451,6 +9469,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-dec-gpc0(<sc-id>): + This action decrements the GPC0 counter according to the sticky + 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 @@ -9562,6 +9585,7 @@ tcp-request session <action> [{if | unless} <condition>] - reject : the request is rejected and the connection is closed - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] - sc-inc-gpc0(<sc-id>) + - sc-dec-gpc0(<sc-id>) - sc-set-gpt0(<sc-id>) <int> - set-var(<var-name>) <expr> - unset-var(<var-name>) @@ -13589,7 +13613,8 @@ sc0_get_gpc0([<table>]) : integer sc1_get_gpc0([<table>]) : integer sc2_get_gpc0([<table>]) : 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. + currently tracked counters. See also src_get_gpc0, sc/sc0/sc1/sc2_inc_gpc0 + and sc/sc0/sc1/sc2_dec_gpc0. sc_get_gpt0(<ctr>[,<table>]) : integer sc0_get_gpt0([<table>]) : integer @@ -13604,10 +13629,11 @@ sc1_gpc0_rate([<table>]) : integer sc2_gpc0_rate([<table>]) : integer Returns the average increment rate of the first General Purpose Counter associated to the currently tracked counters. It reports the frequency - which the gpc0 counter was incremented over the configured period. See also - src_gpc0_rate, sc/sc0/sc1/sc2_get_gpc0, and sc/sc0/sc1/sc2_inc_gpc0. Note - that the "gpc0_rate" counter must be stored in the stick-table for a value to - be returned, as "gpc0" only holds the event count. + which the gpc0 counter was incremented over the configured period. + See also src_gpc0_rate, sc/sc0/sc1/sc2_get_gpc0, sc/sc0/sc1/sc2_inc_gpc0 + and sc/sc0/sc1/sc2_dec_gpc0. Note that the "gpc0_rate" counter must be + stored in the stick-table for a value to be returned, as "gpc0" only + holds the event count. sc_http_err_cnt(<ctr>[,<table>]) : integer sc0_http_err_cnt([<table>]) : integer @@ -13658,6 +13684,15 @@ sc2_inc_gpc0([<table>]) : integer acl kill sc0_inc_gpc0 gt 0 tcp-request connection reject if abuse kill +sc_dec_gpc0(<ctr>[,<table>]) : integer +sc0_dec_gpc0([<table>]) : integer +sc1_dec_gpc0([<table>]) : integer +sc2_dec_gpc0([<table>]) : integer + Decrements the first General Purpose Counter associated to the currently + tracked counters, and returns its new value. Before the first invocation, + the stored value is zero which is the minimum, so unless the counter was + already incremented more than once, the returned value will be 0. + sc_kbytes_in(<ctr>[,<table>]) : integer sc0_kbytes_in([<table>]) : integer sc1_kbytes_in([<table>]) : integer @@ -13787,7 +13822,7 @@ src_get_gpc0([<table>]) : integer Returns the value of the first General Purpose Counter 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_gpc0 and src_inc_gpc0. + See also sc/sc0/sc1/sc2_get_gpc0, src_inc_gpc0 and src_dec_gpc0. src_get_gpt0([<table>]) : integer Returns the value of the first General Purpose Tag associated to the @@ -13800,9 +13835,9 @@ src_gpc0_rate([<table>]) : integer associated to the incoming connection's source address in the current proxy's stick-table or in the designated stick-table. It reports the frequency which the gpc0 counter was incremented over the configured period. See also - sc/sc0/sc1/sc2_gpc0_rate, src_get_gpc0, and sc/sc0/sc1/sc2_inc_gpc0. Note - that the "gpc0_rate" counter must be stored in the stick-table for a value to - be returned, as "gpc0" only holds the event count. + sc/sc0/sc1/sc2_gpc0_rate, src_get_gpc0, sc/sc0/sc1/sc2_inc_gpc0 and + sc/sc0/sc1/sc2_dec_gpc0. Note that the "gpc0_rate" counter must be stored in + the stick-table for a value to be returned, as "gpc0" only holds the event count. src_http_err_cnt([<table>]) : integer Returns the cumulated number of HTTP errors from the incoming connection's @@ -13844,6 +13879,12 @@ src_inc_gpc0([<table>]) : integer acl kill src_inc_gpc0 gt 0 tcp-request connection reject if abuse kill +src_dec_gpc0([<table>]) : integer + Decrements the first General Purpose Counter associated to the incoming + connection's source address in the current proxy's stick-table or in the + designated stick-table, and returns its new value. If the address is not + found, an entry is created and 0 is returned. See also sc0/sc2/sc2_dec_gpc0. + src_is_local : boolean Returns true if the source address of the incoming connection is local to the system, or false if the address doesn't exist on the system, meaning that it diff --git a/src/stick_table.c b/src/stick_table.c index a00f1b6..b64eab5 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -1439,6 +1439,76 @@ static enum act_parse_ret parse_inc_gpc0(const char **args, int *arg, struct pro } /* Always returns 1. */ +static enum act_return action_dec_gpc0(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + struct stksess *ts; + struct stkctr *stkctr; + + /* Extract the stksess, return OK if no stksess available. */ + if (s) + stkctr = &s->stkctr[rule->arg.gpc.sc]; + else + stkctr = &sess->stkctr[rule->arg.gpc.sc]; + + ts = stkctr_entry(stkctr); + if (ts) { + void *ptr1; + + ptr1 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GPC0); + if (ptr1) { + if (stktable_data_cast(ptr1, gpc0) > 0) + stktable_data_cast(ptr1, gpc0)--; + stktable_touch(stkctr->table, ts, 1); + } + } + return ACT_RET_CONT; +} + +/* This function is a common parser for using variables. It understands + * the formats: + * + * sc-dec-gpc0(<stick-table ID>) + * + * It returns 0 if fails and <err> is filled with an error message. Otherwise, + * it returns 1 and the variable <expr> is filled with the pointer to the + * expression to execute. + */ +static enum act_parse_ret parse_dec_gpc0(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-dec-gpc0"); + if (*cmd_name == '\0') { + /* default stick table id. */ + rule->arg.gpc.sc = 0; + } else { + /* parse the stick table id. */ + if (*cmd_name != '(') { + memprintf(err, "invalid stick table track ID. Expects %s(<Track ID>)", args[*arg-1]); + return ACT_RET_PRS_ERR; + } + cmd_name++; /* jump the '(' */ + rule->arg.gpc.sc = strtol(cmd_name, &error, 10); /* Convert stick table id. */ + if (*error != ')') { + memprintf(err, "invalid stick table track ID. Expects %s(<Track ID>)", args[*arg-1]); + return ACT_RET_PRS_ERR; + } + + if (rule->arg.gpc.sc >= ACT_ACTION_TRK_SCMAX) { + memprintf(err, "invalid stick table track ID. The max allowed ID is %d", + ACT_ACTION_TRK_SCMAX-1); + return ACT_RET_PRS_ERR; + } + } + rule->action = ACT_CUSTOM; + rule->action_ptr = action_dec_gpc0; + return ACT_RET_PRS_OK; +} + +/* Always returns 1. */ static enum act_return action_set_gpt0(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags) { @@ -1630,8 +1700,8 @@ smp_fetch_sc_stkctr(struct session *sess, struct stream *strm, const struct arg /* same as smp_fetch_sc_stkctr() but dedicated to src_* and can create * the entry if it doesn't exist yet. This is needed for a few fetch - * functions which need to create an entry, such as src_inc_gpc* and - * src_clr_gpc*. + * functions which need to create an entry, such as src_inc_gpc*, + * src_dec_gpc* and src_clr_gpc*. */ struct stkctr * smp_create_src_stkctr(struct session *sess, struct stream *strm, const struct arg *args, const char *kw) @@ -1802,6 +1872,39 @@ smp_fetch_sc_inc_gpc0(const struct arg *args, struct sample *smp, const char *kw return 1; } +/* Decrement the General Purpose Counter 0 value from the stream's tracked + * frontend counters and return it into temp integer. + * Supports being called as "sc[0-9]_dec_gpc0" or "src_dec_gpc0" only. + */ +static int +smp_fetch_sc_dec_gpc0(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct stkctr *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) + stkctr = smp_create_src_stkctr(smp->sess, smp->strm, args, kw); + + if (stkctr && stkctr_entry(stkctr)) { + void *ptr1; + + ptr1 = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_GPC0); + if (ptr1) { + if (stktable_data_cast(ptr1, gpc0) > 0) + smp->data.u.sint = --stktable_data_cast(ptr1, gpc0); + stktable_touch(stkctr->table, stkctr_entry(stkctr), 1); + } + } + return 1; +} + /* Clear the General Purpose Counter 0 value from the stream's tracked * frontend counters and return its previous value into temp integer. * Supports being called as "sc[0-9]_clr_gpc0" or "src_clr_gpc0" only. @@ -2780,36 +2883,42 @@ static struct cli_kw_list cli_kws = {{ },{ static struct action_kw_list tcp_conn_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } }}; static struct action_kw_list tcp_sess_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } }}; static struct action_kw_list tcp_req_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } }}; static struct action_kw_list tcp_res_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } }}; static struct action_kw_list http_req_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } }}; static struct action_kw_list http_res_kws = { { }, { + { "sc-dec-gpc0", parse_dec_gpc0, 1 }, { "sc-inc-gpc0", parse_inc_gpc0, 1 }, { "sc-set-gpt0", parse_set_gpt0, 1 }, { /* END */ } @@ -2840,6 +2949,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc_http_err_rate", smp_fetch_sc_http_err_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_http_req_cnt", smp_fetch_sc_http_req_cnt, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_http_req_rate", smp_fetch_sc_http_req_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc_dec_gpc0", smp_fetch_sc_dec_gpc0, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_inc_gpc0", smp_fetch_sc_inc_gpc0, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_kbytes_in", smp_fetch_sc_kbytes_in, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "sc_kbytes_out", smp_fetch_sc_kbytes_out, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, @@ -2860,6 +2970,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc0_http_err_rate", smp_fetch_sc_http_err_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_http_req_cnt", smp_fetch_sc_http_req_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_http_req_rate", smp_fetch_sc_http_req_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc0_dec_gpc0", smp_fetch_sc_dec_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_inc_gpc0", smp_fetch_sc_inc_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_kbytes_in", smp_fetch_sc_kbytes_in, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "sc0_kbytes_out", smp_fetch_sc_kbytes_out, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, @@ -2880,6 +2991,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc1_http_err_rate", smp_fetch_sc_http_err_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_http_req_cnt", smp_fetch_sc_http_req_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_http_req_rate", smp_fetch_sc_http_req_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc1_dec_gpc0", smp_fetch_sc_dec_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_inc_gpc0", smp_fetch_sc_inc_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_kbytes_in", smp_fetch_sc_kbytes_in, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "sc1_kbytes_out", smp_fetch_sc_kbytes_out, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, @@ -2900,6 +3012,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "sc2_http_err_rate", smp_fetch_sc_http_err_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_http_req_cnt", smp_fetch_sc_http_req_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_http_req_rate", smp_fetch_sc_http_req_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, + { "sc2_dec_gpc0", smp_fetch_sc_dec_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_inc_gpc0", smp_fetch_sc_inc_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_kbytes_in", smp_fetch_sc_kbytes_in, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "sc2_kbytes_out", smp_fetch_sc_kbytes_out, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, @@ -2920,6 +3033,7 @@ static struct sample_fetch_kw_list smp_fetch_keywords = {ILH, { { "src_http_err_rate", smp_fetch_sc_http_err_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_http_req_cnt", smp_fetch_sc_http_req_cnt, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_http_req_rate", smp_fetch_sc_http_req_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, + { "src_dec_gpc0", smp_fetch_sc_dec_gpc0, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_inc_gpc0", smp_fetch_sc_inc_gpc0, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_kbytes_in", smp_fetch_sc_kbytes_in, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_kbytes_out", smp_fetch_sc_kbytes_out, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, -- 2.1.4