MINOR: stick-tables: Add GPT0 access

This patch adds acces to GPT0. The access can be done with http and
tcp actions, and through a converter.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 83f337d..8447ff1 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3365,6 +3365,7 @@
               set-map(<file name>) <key fmt> <value fmt> |
               set-var(<var name>) <expr> |
               { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
+              sc-set-gpt0(<sc-id>) <int> |
               lua <function name>
              }
              [ { if | unless } <condition> ]
@@ -3628,6 +3629,12 @@
       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(<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.
+
     - "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 @@
                 del-map(<file name>) <key fmt> |
                 set-map(<file name>) <key fmt> <value fmt> |
                 set-var(<var-name>) <expr> |
+                sc-set-gpt0(<sc-id>) <int> |
                 lua <function name>
               }
               [ { if | unless } <condition> ]
@@ -3936,6 +3944,12 @@
 
          http-response set-var(sess.last_redir) res.hdr(location)
 
+    - 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.
+
   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 @@
         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(<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.
+
   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 @@
   Arguments :
     <action>    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.
 
     <condition> is a standard layer 4-7 ACL-based condition (see section 7).
 
@@ -8363,11 +8383,12 @@
   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 } <key> [table <table>]
+    - set-gpt0(<sc-id>) <int>
     - lua <function>
     - set-var(<var-name>) <expr>
 
@@ -8535,7 +8556,8 @@
                                  no    |    no    |   yes  |   yes
   Arguments :
     <action>    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".
 
     <condition> is a standard layer 4-7 ACL-based condition (see section 7).
 
@@ -8552,7 +8574,7 @@
   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 @@
     - set-var(<var-name>) <expr>
         Sets a variable.
 
+    - 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.
+
   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 @@
   rate associated with the input sample in the designated table. See also the
   sc_conn_rate sample fetch keyword.
 
+table_gpt0(<table>)
+  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(<table>)
   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 @@
   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(<ctr>[,<table>]) : integer
+sc0_get_gpt0([<table>]) : integer
+sc1_get_gpt0([<table>]) : integer
+sc2_get_gpt0([<table>]) : integer
+  Returns the value of the first General Purpose Tag associated to the
+  currently tracked counters. See also src_get_gpt0.
+
 sc_gpc0_rate(<ctr>[,<table>]) : integer
 sc0_gpc0_rate([<table>]) : integer
 sc1_gpc0_rate([<table>]) : integer
@@ -12257,6 +12299,12 @@
   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([<table>]) : 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([<table>]) : 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 fd264cb..5d1b250 100644
--- a/include/types/action.h
+++ b/include/types/action.h
@@ -130,6 +130,10 @@
 			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 5e7a2c4..623667f 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -23,6 +23,8 @@
 #include <ebsttree.h>
 
 #include <proto/arg.h>
+#include <proto/proto_http.h>
+#include <proto/proto_tcp.h>
 #include <proto/proxy.h>
 #include <proto/sample.h>
 #include <proto/stream.h>
@@ -855,6 +857,41 @@
 }
 
 /* Casts sample <smp> 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,
+ * <not found> 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 <smp> 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
  * be easily performed. If the inspected parameter is not stored in the table,
@@ -1272,6 +1309,109 @@
 	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(<stick-table ID>) <expression>
+ *
+ * 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_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(<Track ID>)", 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(<Track ID>)", 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 <const> 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 @@
 	{ "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 @@
 __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 278bdb8..b897354 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -2694,6 +2694,32 @@
 	return 1;
 }
 
+/* set <smp> 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 <smp> 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 @@
 	{ "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 @@
 	{ "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 @@
 	{ "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 @@
 	{ "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 @@
 	{ "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, },