MINOR: session: make the number of stick counter entries more configurable

In preparation of more flexibility in the stick counters, make their
number configurable. It still defaults to 3 which is the minimum
accepted value. Changing the value alone is not sufficient to get
more counters, some bitfields still need to be updated and the TCP
actions need to be updated as well, but this update tries to be
easier, which is nice for experimentation purposes.
diff --git a/include/common/defaults.h b/include/common/defaults.h
index 5000d9c..874d2e2 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -73,6 +73,13 @@
 #define MAX_HDR_HISTORY 10
 #endif
 
+// max # of stick counters per session (at least 3 for sc0..sc2)
+// Some changes are needed in TCP_ACT_TRK_SC* and SN_BE_TRACK_SC* if more
+// values are required.
+#ifndef MAX_SESS_STKCTR
+#define MAX_SESS_STKCTR 3
+#endif
+
 // max # of loops we can perform around a read() which succeeds.
 // It's very frequent that the system returns a few TCP segments at a time.
 #ifndef MAX_READ_POLL_LOOPS
diff --git a/include/proto/session.h b/include/proto/session.h
index cc1242d..f597d59 100644
--- a/include/proto/session.h
+++ b/include/proto/session.h
@@ -59,7 +59,7 @@
 	void *ptr;
 	int i;
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		if (!s->stkctr[i].entry)
 			continue;
 		ptr = stktable_data_ptr(s->stkctr[i].table, s->stkctr[i].entry, STKTABLE_DT_CONN_CUR);
@@ -83,7 +83,7 @@
 	if (likely(!(s->flags & SN_BE_TRACK_ANY)))
 		return;
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		if (!s->stkctr[i].entry)
 			continue;
 
@@ -145,7 +145,7 @@
 	void *ptr;
 	int i;
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		if (!s->stkctr[i].entry)
 			continue;
 
@@ -169,7 +169,7 @@
 	if (likely(!(s->flags & SN_BE_TRACK_ANY)))
 		return;
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		if (!s->stkctr[i].entry)
 			continue;
 
@@ -198,7 +198,7 @@
 	void *ptr;
 	int i;
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		if (!s->stkctr[i].entry)
 			continue;
 
diff --git a/include/types/proto_tcp.h b/include/types/proto_tcp.h
index a820462..662cde4 100644
--- a/include/types/proto_tcp.h
+++ b/include/types/proto_tcp.h
@@ -33,9 +33,10 @@
 	TCP_ACT_ACCEPT = 1,
 	TCP_ACT_REJECT = 2,
 	TCP_ACT_EXPECT_PX = 3,
-	TCP_ACT_TRK_SC0 = 4, /* TCP request tracking : must be contiguous */
+	TCP_ACT_TRK_SC0 = 4, /* TCP request tracking : must be contiguous and cover up to MAX_SESS_STKCTR values */
 	TCP_ACT_TRK_SC1 = 5,
 	TCP_ACT_TRK_SC2 = 6,
+	TCP_ACT_TRK_SCMAX = TCP_ACT_TRK_SC0 + MAX_SESS_STKCTR - 1,
 };
 
 struct tcp_rule {
diff --git a/include/types/session.h b/include/types/session.h
index 42d37db..8f731dd 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -90,7 +90,9 @@
 
 #define SN_COMP_READY   0x00100000	/* the compression is initialized */
 
-/* session tracking flags: these ones must absolutely be contiguous. See also s->stkctr */
+/* session tracking flags: these ones must absolutely be contiguous and cover
+ * at least MAX_SESS_STKCTR flags.
+ */
 #define SN_BE_TRACK_SC0 0x00200000	/* backend tracks stick-counter 0 */
 #define SN_BE_TRACK_SC1 0x00400000	/* backend tracks stick-counter 1 */
 #define SN_BE_TRACK_SC2 0x00800000	/* backend tracks stick-counter 2 */
@@ -146,7 +148,7 @@
 	} store[8];				/* tracked stickiness values to store */
 	int store_count;
 
-	struct stkctr stkctr[3];                /* stick counters */
+	struct stkctr stkctr[MAX_SESS_STKCTR];  /* stick counters */
 
 	struct stream_interface si[2];          /* client and server stream interfaces */
 	struct {
diff --git a/src/cfgparse.c b/src/cfgparse.c
index a85bbed..d51e1b6 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -6367,7 +6367,7 @@
 		list_for_each_entry(trule, &curproxy->tcp_req.l4_rules, list) {
 			struct proxy *target;
 
-			if (trule->action < TCP_ACT_TRK_SC0 || trule->action > TCP_ACT_TRK_SC2)
+			if (trule->action < TCP_ACT_TRK_SC0 || trule->action > TCP_ACT_TRK_SCMAX)
 				continue;
 
 			if (trule->act_prm.trk_ctr.table.n)
@@ -6406,7 +6406,7 @@
 		list_for_each_entry(trule, &curproxy->tcp_req.inspect_rules, list) {
 			struct proxy *target;
 
-			if (trule->action < TCP_ACT_TRK_SC0 || trule->action > TCP_ACT_TRK_SC2)
+			if (trule->action < TCP_ACT_TRK_SC0 || trule->action > TCP_ACT_TRK_SCMAX)
 				continue;
 
 			if (trule->act_prm.trk_ctr.table.n)
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index fe4a0d2..797f335 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -938,7 +938,7 @@
 					s->flags |= SN_FINST_R;
 				return 0;
 			}
-			else if ((rule->action >= TCP_ACT_TRK_SC0 && rule->action <= TCP_ACT_TRK_SC2) &&
+			else if ((rule->action >= TCP_ACT_TRK_SC0 && rule->action <= TCP_ACT_TRK_SCMAX) &&
 				 !s->stkctr[tcp_trk_idx(rule->action)].entry) {
 				/* Note: only the first valid tracking parameter of each
 				 * applies.
@@ -1092,7 +1092,7 @@
 				result = 0;
 				break;
 			}
-			else if ((rule->action >= TCP_ACT_TRK_SC0 && rule->action <= TCP_ACT_TRK_SC2) &&
+			else if ((rule->action >= TCP_ACT_TRK_SC0 && rule->action <= TCP_ACT_TRK_SCMAX) &&
 				 !s->stkctr[tcp_trk_idx(rule->action)].entry) {
 				/* Note: only the first valid tracking parameter of each
 				 * applies.
@@ -1184,7 +1184,9 @@
 		arg++;
 		rule->action = TCP_ACT_REJECT;
 	}
-	else if (strcmp(args[arg], "track-sc0") == 0 || strcmp(args[arg], "track-sc1") == 0 || strcmp(args[arg], "track-sc2") == 0) {
+	else if (strncmp(args[arg], "track-sc", 8) == 0 &&
+		 args[arg][9] == '\0' && args[arg][8] >= '0' &&
+		 args[arg][8] <= '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */
 		struct sample_expr *expr;
 		int kw = arg;
 
@@ -1246,9 +1248,9 @@
 	}
 	else {
 		memprintf(err,
-		          "'%s %s' expects 'accept', 'reject', 'track-sc0', 'track-sc1' "
-		          " or 'track-sc2' in %s '%s' (got '%s')",
-		          args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
+		          "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d' "
+		          " in %s '%s' (got '%s')",
+		          args[0], args[1], MAX_SESS_STKCTR, proxy_type_str(curpx), curpx->id, args[arg]);
 		return -1;
 	}
 
diff --git a/src/session.c b/src/session.c
index 9be5faa..ec78be1 100644
--- a/src/session.c
+++ b/src/session.c
@@ -420,7 +420,7 @@
 	/* Let's count a session now */
 	proxy_inc_fe_sess_ctr(l, p);
 
-	for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+	for (i = 0; i < MAX_SESS_STKCTR; i++) {
 		void *ptr;
 
 		if (!s->stkctr[i].entry)
@@ -707,7 +707,7 @@
 			if (s->listener->counters)
 				s->listener->counters->bytes_in += bytes;
 
-			for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+			for (i = 0; i < MAX_SESS_STKCTR; i++) {
 				if (!s->stkctr[i].entry)
 					continue;
 
@@ -741,7 +741,7 @@
 			if (s->listener->counters)
 				s->listener->counters->bytes_out += bytes;
 
-			for (i = 0; i < sizeof(s->stkctr) / sizeof(s->stkctr[0]); i++) {
+			for (i = 0; i < MAX_SESS_STKCTR; i++) {
 				if (!s->stkctr[i].entry)
 					continue;