MEDIUM: counters: add support for tracking a third counter

We're often missin a third counter to track base, src and base+src at
the same time. Here we introduce track_sc3 to have this third counter.
It would be wise not to add much more counters because that slightly
increases the session size and processing time though the real issue
is more the declaration of the keywords in the code and in the doc.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 18fcedc..276766b 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -6335,7 +6335,7 @@
 		list_for_each_entry(trule, &curproxy->tcp_req.l4_rules, list) {
 			struct proxy *target;
 
-			if (trule->action < TCP_ACT_TRK_SC1 || trule->action > TCP_ACT_TRK_SC2)
+			if (trule->action < TCP_ACT_TRK_SC1 || trule->action > TCP_ACT_TRK_SC3)
 				continue;
 
 			if (trule->act_prm.trk_ctr.table.n)
@@ -6374,7 +6374,7 @@
 		list_for_each_entry(trule, &curproxy->tcp_req.inspect_rules, list) {
 			struct proxy *target;
 
-			if (trule->action < TCP_ACT_TRK_SC1 || trule->action > TCP_ACT_TRK_SC2)
+			if (trule->action < TCP_ACT_TRK_SC1 || trule->action > TCP_ACT_TRK_SC3)
 				continue;
 
 			if (trule->act_prm.trk_ctr.table.n)
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 86a67c4..e1b5d8b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -901,7 +901,7 @@
 					s->flags |= SN_FINST_R;
 				return 0;
 			}
-			else if ((rule->action >= TCP_ACT_TRK_SC1 && rule->action <= TCP_ACT_TRK_SC2) &&
+			else if ((rule->action >= TCP_ACT_TRK_SC1 && rule->action <= TCP_ACT_TRK_SC3) &&
 				 !s->stkctr[tcp_trk_idx(rule->action)].entry) {
 				/* Note: only the first valid tracking parameter of each
 				 * applies.
@@ -1055,7 +1055,7 @@
 				result = 0;
 				break;
 			}
-			else if ((rule->action >= TCP_ACT_TRK_SC1 && rule->action <= TCP_ACT_TRK_SC2) &&
+			else if ((rule->action >= TCP_ACT_TRK_SC1 && rule->action <= TCP_ACT_TRK_SC3) &&
 				 !s->stkctr[tcp_trk_idx(rule->action)].entry) {
 				/* Note: only the first valid tracking parameter of each
 				 * applies.
@@ -1143,7 +1143,7 @@
 		arg++;
 		rule->action = TCP_ACT_REJECT;
 	}
-	else if (strcmp(args[arg], "track-sc1") == 0 || strcmp(args[arg], "track-sc2") == 0) {
+	else if (strcmp(args[arg], "track-sc1") == 0 || strcmp(args[arg], "track-sc2") == 0 || strcmp(args[arg], "track-sc3") == 0) {
 		struct sample_expr *expr;
 		int kw = arg;
 
@@ -1187,8 +1187,8 @@
 	}
 	else {
 		memprintf(err,
-		          "'%s %s' expects 'accept', 'reject', 'track-sc1' "
-		          "or 'track-sc2' in %s '%s' (got '%s')",
+		          "'%s %s' expects 'accept', 'reject', 'track-sc1', 'track-sc2' "
+		          " or 'track-sc3' in %s '%s' (got '%s')",
 		          args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
 		return -1;
 	}
diff --git a/src/session.c b/src/session.c
index 404fd06..10d3feb 100644
--- a/src/session.c
+++ b/src/session.c
@@ -2615,6 +2615,18 @@
 	return smp_fetch_get_gpc0(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the General Purpose Counter 0 value from the session's tracked
+ * backend counters.
+ */
+static int
+smp_fetch_sc3_get_gpc0(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+	return smp_fetch_get_gpc0(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the General Purpose Counter 0 value from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -2675,6 +2687,18 @@
 	return smp_fetch_inc_gpc0(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* Increment the General Purpose Counter 0 value from the session's tracked
+ * backend counters and return it into temp integer.
+ */
+static int
+smp_fetch_sc3_inc_gpc0(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+	return smp_fetch_inc_gpc0(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* Increment the General Purpose Counter 0 value from the session's source
  * address in the table pointed to by expr, and return it into temp integer.
  * Accepts exactly 1 argument of type table.
@@ -2736,6 +2760,18 @@
 	return smp_fetch_clr_gpc0(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* Clear the General Purpose Counter 0 value from the session's tracked
+ * backend counters and return its previous value into temp integer.
+ */
+static int
+smp_fetch_sc3_clr_gpc0(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+	return smp_fetch_clr_gpc0(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* Clear the General Purpose Counter 0 value from the session's source address
  * in the table pointed to by expr, and return its previous value into temp integer.
  * Accepts exactly 1 argument of type table.
@@ -2792,6 +2828,17 @@
 	return smp_fetch_conn_cnt(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the cumulated number of connections from the session's tracked BE counters */
+static int
+smp_fetch_sc3_conn_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_conn_cnt(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the cumulated number of connections from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -2853,6 +2900,19 @@
 	return smp_fetch_conn_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the connection rate from the session's tracked BE counters over
+ * the configured period.
+ */
+static int
+smp_fetch_sc3_conn_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                        const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_conn_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the connection rate from the session's source address in the
  * table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -2942,6 +3002,17 @@
 	return smp_fetch_conn_cur(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the number of concurrent connections from the session's tracked BE counters */
+static int
+smp_fetch_sc3_conn_cur(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_conn_cur(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the number of concurrent connections from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -2998,6 +3069,17 @@
 	return smp_fetch_sess_cnt(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the cumulated number of sessions from the session's tracked BE counters */
+static int
+smp_fetch_sc3_sess_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_sess_cnt(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -3059,6 +3141,19 @@
 	return smp_fetch_sess_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the session rate from the session's tracked BE counters over
+ * the configured period.
+ */
+static int
+smp_fetch_sc3_sess_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                        const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_sess_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -3115,6 +3210,17 @@
 	return smp_fetch_http_req_cnt(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the cumulated number of sessions from the session's tracked BE counters */
+static int
+smp_fetch_sc3_http_req_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                           const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_http_req_cnt(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -3176,6 +3282,19 @@
 	return smp_fetch_http_req_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the session rate from the session's tracked BE counters over
+ * the configured period.
+ */
+static int
+smp_fetch_sc3_http_req_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                            const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_http_req_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -3232,6 +3351,17 @@
 	return smp_fetch_http_err_cnt(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the cumulated number of sessions from the session's tracked BE counters */
+static int
+smp_fetch_sc3_http_err_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                           const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_http_err_cnt(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the cumulated number of session from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -3293,6 +3423,19 @@
 	return smp_fetch_http_err_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the session rate from the session's tracked BE counters over
+ * the configured period.
+ */
+static int
+smp_fetch_sc3_http_err_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                            const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_http_err_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the session rate from the session's source address in the
  * table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -3354,6 +3497,19 @@
 	return smp_fetch_kbytes_in(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the number of kbytes received from clients according to the
+ * session's tracked BE counters.
+ */
+static int
+smp_fetch_sc3_kbytes_in(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                        const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_kbytes_in(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the number of kbytes received from the session's source
  * address in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -3417,6 +3573,19 @@
 	return smp_fetch_bytes_in_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the bytes rate from clients from the session's tracked BE
+ * counters over the configured period.
+ */
+static int
+smp_fetch_sc3_bytes_in_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                            const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_bytes_in_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the bytes rate from clients from the session's source address
  * in the table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -3478,6 +3647,19 @@
 	return smp_fetch_kbytes_out(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the number of kbytes sent to clients according to the session's
+ * tracked BE counters.
+ */
+static int
+smp_fetch_sc3_kbytes_out(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                         const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_kbytes_out(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the number of kbytes sent to the session's source address in
  * the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
@@ -3541,6 +3723,19 @@
 	return smp_fetch_bytes_out_rate(l4->stkctr[1].table, smp, l4->stkctr[1].entry);
 }
 
+/* set temp integer to the bytes rate to clients from the session's tracked BE counters
+ * over the configured period.
+ */
+static int
+smp_fetch_sc3_bytes_out_rate(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                             const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return smp_fetch_bytes_out_rate(l4->stkctr[2].table, smp, l4->stkctr[2].entry);
+}
+
 /* set temp integer to the bytes rate to client from the session's source address in
  * the table pointed to by expr, over the configured period.
  * Accepts exactly 1 argument of type table.
@@ -3581,6 +3776,17 @@
 	return l4->stkctr[1].entry->ref_cnt;
 }
 
+/* set temp integer to the number of active trackers on the SC1 entry */
+static int
+smp_fetch_sc3_trackers(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+                       const struct arg *args, struct sample *smp)
+{
+	if (!l4->stkctr[2].entry)
+		return 0;
+
+	return l4->stkctr[2].entry->ref_cnt;
+}
+
 /* set temp integer to the number of used entries in the table pointed to by expr.
  * Accepts exactly 1 argument of type table.
  */
@@ -3646,6 +3852,23 @@
 	{ "sc2_sess_cnt",       NULL, acl_parse_int, acl_match_int },
 	{ "sc2_sess_rate",      NULL, acl_parse_int, acl_match_int },
 	{ "sc2_trackers",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_bytes_in_rate",  NULL, acl_parse_int, acl_match_int },
+	{ "sc3_bytes_out_rate", NULL, acl_parse_int, acl_match_int },
+	{ "sc3_clr_gpc0",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_conn_cnt",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_conn_cur",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_conn_rate",      NULL, acl_parse_int, acl_match_int },
+	{ "sc3_get_gpc0",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_http_err_cnt",   NULL, acl_parse_int, acl_match_int },
+	{ "sc3_http_err_rate",  NULL, acl_parse_int, acl_match_int },
+	{ "sc3_http_req_cnt",   NULL, acl_parse_int, acl_match_int },
+	{ "sc3_http_req_rate",  NULL, acl_parse_int, acl_match_int },
+	{ "sc3_inc_gpc0",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_kbytes_in",      NULL, acl_parse_int, acl_match_int },
+	{ "sc3_kbytes_out",     NULL, acl_parse_int, acl_match_int },
+	{ "sc3_sess_cnt",       NULL, acl_parse_int, acl_match_int },
+	{ "sc3_sess_rate",      NULL, acl_parse_int, acl_match_int },
+	{ "sc3_trackers",       NULL, acl_parse_int, acl_match_int },
 	{ "src_bytes_in_rate",  NULL, acl_parse_int, acl_match_int },
 	{ "src_bytes_out_rate", NULL, acl_parse_int, acl_match_int },
 	{ "src_clr_gpc0",       NULL, acl_parse_int, acl_match_int },
@@ -3706,6 +3929,23 @@
 	{ "sc2_sess_cnt",       smp_fetch_sc2_sess_cnt,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
 	{ "sc2_sess_rate",      smp_fetch_sc2_sess_rate,      0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
 	{ "sc2_trackers",       smp_fetch_sc2_trackers,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_bytes_in_rate",  smp_fetch_sc3_bytes_in_rate,  0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_bytes_out_rate", smp_fetch_sc3_bytes_out_rate, 0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_clr_gpc0",       smp_fetch_sc3_clr_gpc0,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_conn_cnt",       smp_fetch_sc3_conn_cnt,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_conn_cur",       smp_fetch_sc3_conn_cur,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_conn_rate",      smp_fetch_sc3_conn_rate,      0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_get_gpc0",       smp_fetch_sc3_get_gpc0,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_http_err_cnt",   smp_fetch_sc3_http_err_cnt,   0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_http_err_rate",  smp_fetch_sc3_http_err_rate,  0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_http_req_cnt",   smp_fetch_sc3_http_req_cnt,   0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_http_req_rate",  smp_fetch_sc3_http_req_rate,  0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_inc_gpc0",       smp_fetch_sc3_inc_gpc0,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_kbytes_in",      smp_fetch_sc3_kbytes_in,      0,           NULL, SMP_T_UINT, SMP_USE_L4CLI, },
+	{ "sc3_kbytes_out",     smp_fetch_sc3_kbytes_out,     0,           NULL, SMP_T_UINT, SMP_USE_L4CLI, },
+	{ "sc3_sess_cnt",       smp_fetch_sc3_sess_cnt,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_sess_rate",      smp_fetch_sc3_sess_rate,      0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
+	{ "sc3_trackers",       smp_fetch_sc3_trackers,       0,           NULL, SMP_T_UINT, SMP_USE_INTRN, },
 	{ "src_bytes_in_rate",  smp_fetch_src_bytes_in_rate,  ARG1(1,TAB), NULL, SMP_T_UINT, SMP_USE_L4CLI, },
 	{ "src_bytes_out_rate", smp_fetch_src_bytes_out_rate, ARG1(1,TAB), NULL, SMP_T_UINT, SMP_USE_L4CLI, },
 	{ "src_clr_gpc0",       smp_fetch_src_clr_gpc0,       ARG1(1,TAB), NULL, SMP_T_UINT, SMP_USE_L4CLI, },