diff --git a/include/haproxy/errors.h b/include/haproxy/errors.h index bebad844a..e9ad58835 100644 --- a/include/haproxy/errors.h +++ b/include/haproxy/errors.h @@ -60,6 +60,7 @@ enum { PE_ARG_INVC, /* invalid char in argument (pointer not provided) */ PE_ARG_INVC_PTR, /* invalid char in argument (pointer provided) */ PE_ARG_NOT_FOUND, /* argument references something not found */ + PE_ARG_VALUE_OOR, /* argument value is out of range */ }; diff --git a/include/haproxy/stick_table-t.h b/include/haproxy/stick_table-t.h index f09956aaf..89d1b2a2a 100644 --- a/include/haproxy/stick_table-t.h +++ b/include/haproxy/stick_table-t.h @@ -34,6 +34,7 @@ #include #include +#define STKTABLE_MAX_DT_ARRAY_SIZE 100 /* The types of extra data we can store in a stick table */ enum { @@ -125,6 +126,7 @@ struct stktable_data_type { const char *name; /* name of the data type */ int std_type; /* standard type we can use for this data, STD_T_* */ int arg_type; /* type of optional argument, ARG_T_* */ + int is_array; }; /* stick table keyword type */ @@ -185,6 +187,7 @@ struct stktable { int expire; /* time to live for sticky sessions (milliseconds) */ int data_size; /* the size of the data that is prepended *before* stksess */ int data_ofs[STKTABLE_DATA_TYPES]; /* negative offsets of present data types, or 0 if absent */ + unsigned int data_nbelem[STKTABLE_DATA_TYPES]; /* to store nb_elem in case of array types */ union { int i; unsigned int u; diff --git a/include/haproxy/stick_table.h b/include/haproxy/stick_table.h index 4dffe2a85..ff172f458 100644 --- a/include/haproxy/stick_table.h +++ b/include/haproxy/stick_table.h @@ -136,7 +136,7 @@ static inline int stktable_type_size(int type) return 0; } -int stktable_alloc_data_type(struct stktable *t, int type, const char *sa); +int stktable_alloc_data_type(struct stktable *t, int type, const char *sa, const char *sa2); /* return pointer for data type in sticky session of table , all * of which must exist (otherwise use stktable_data_ptr() if unsure). @@ -163,6 +163,31 @@ static inline void *stktable_data_ptr(struct stktable *t, struct stksess *ts, in return __stktable_data_ptr(t, ts, type); } +/* return pointer on the element of index from the array data type + * in sticky session of table , or NULL if either is NULL + * or this element is not stored because this type is not stored or + * requested index is greater than the number of elements of the array. + * Note: this function is also usable on non array types, they are + * considered as array of size 1, so a call with at 0 + * as the same behavior than 'stktable_data_ptr'. + */ +static inline void *stktable_data_ptr_idx(struct stktable *t, struct stksess *ts, int type, unsigned int idx) +{ + if (type >= STKTABLE_DATA_TYPES) + return NULL; + + if (!t->data_ofs[type]) /* type not stored */ + return NULL; + + if (!ts) + return NULL; + + if (t->data_nbelem[type] <= idx) + return NULL; + + return __stktable_data_ptr(t, ts, type) + idx*stktable_type_size(stktable_data_types[type].std_type); +} + /* kill an entry if it's expired and its ref_cnt is zero */ static inline int __stksess_kill_if_expired(struct stktable *t, struct stksess *ts) { diff --git a/src/cfgparse.c b/src/cfgparse.c index aa07a2fd1..6bf8e6d59 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -3028,8 +3028,8 @@ int check_config_validity() else { ha_free(&mrule->table.name); mrule->table.t = target; - stktable_alloc_data_type(target, STKTABLE_DT_SERVER_ID, NULL); - stktable_alloc_data_type(target, STKTABLE_DT_SERVER_KEY, NULL); + stktable_alloc_data_type(target, STKTABLE_DT_SERVER_ID, NULL, NULL); + stktable_alloc_data_type(target, STKTABLE_DT_SERVER_KEY, NULL, NULL); if (!in_proxies_list(target->proxies_list, curproxy)) { curproxy->next_stkt_ref = target->proxies_list; target->proxies_list = curproxy; @@ -3062,8 +3062,8 @@ int check_config_validity() else { ha_free(&mrule->table.name); mrule->table.t = target; - stktable_alloc_data_type(target, STKTABLE_DT_SERVER_ID, NULL); - stktable_alloc_data_type(target, STKTABLE_DT_SERVER_KEY, NULL); + stktable_alloc_data_type(target, STKTABLE_DT_SERVER_ID, NULL, NULL); + stktable_alloc_data_type(target, STKTABLE_DT_SERVER_KEY, NULL, NULL); if (!in_proxies_list(target->proxies_list, curproxy)) { curproxy->next_stkt_ref = target->proxies_list; target->proxies_list = curproxy; diff --git a/src/stick_table.c b/src/stick_table.c index 73c8fc184..349e138c8 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -708,14 +708,18 @@ int stktable_parse_type(char **args, int *myidx, unsigned long *type, size_t *ke return 1; } -/* reserve some space for data type , and associate argument at if - * not NULL. Returns PE_NONE (0) if OK or an error code among : +/* reserve some space for data type , there is 2 optionnals + * argument at and to configure this data type and + * they can be NULL if unused for a given type. + * Returns PE_NONE (0) if OK or an error code among : * - PE_ENUM_OOR if does not exist * - PE_EXIST if is already registered - * - PE_ARG_NOT_USE if was provided but not expected - * - PE_ARG_MISSING if was expected but not provided + * - PE_ARG_NOT_USE if / was provided but not expected + * - PE_ARG_MISSING if / was expected but not provided + * - PE_ARG_VALUE_OOR if type is an array and it out of array size range. */ -int stktable_alloc_data_type(struct stktable *t, int type, const char *sa) +int stktable_alloc_data_type(struct stktable *t, int type, const char *sa, const char *sa2) + { if (type >= STKTABLE_DATA_TYPES) return PE_ENUM_OOR; @@ -724,6 +728,17 @@ int stktable_alloc_data_type(struct stktable *t, int type, const char *sa) /* already allocated */ return PE_EXIST; + t->data_nbelem[type] = 1; + if (stktable_data_types[type].is_array) { + /* arrays take their element count on first argument */ + if (!sa) + return PE_ARG_MISSING; + t->data_nbelem[type] = atoi(sa); + if (!t->data_nbelem[type] || (t->data_nbelem[type] > STKTABLE_MAX_DT_ARRAY_SIZE)) + return PE_ARG_VALUE_OOR; + sa = sa2; + } + switch (stktable_data_types[type].arg_type) { case ARG_T_NONE: if (sa) @@ -743,7 +758,7 @@ int stktable_alloc_data_type(struct stktable *t, int type, const char *sa) break; } - t->data_size += stktable_type_size(stktable_data_types[type].std_type); + t->data_size += t->data_nbelem[type] * stktable_type_size(stktable_data_types[type].std_type); t->data_ofs[type] = -t->data_size; return PE_NONE; } @@ -860,7 +875,7 @@ int parse_stick_table(const char *file, int linenum, char **args, } else if (strcmp(args[idx], "store") == 0) { int type, err; - char *cw, *nw, *sa; + char *cw, *nw, *sa, *sa2; idx++; nw = args[idx]; @@ -868,6 +883,7 @@ int parse_stick_table(const char *file, int linenum, char **args, /* the "store" keyword supports a comma-separated list */ cw = nw; sa = NULL; /* store arg */ + sa2 = NULL; while (*nw && *nw != ',') { if (*nw == '(') { *nw = 0; @@ -879,6 +895,10 @@ int parse_stick_table(const char *file, int linenum, char **args, err_code |= ERR_ALERT | ERR_FATAL; goto out; } + if (*nw == ',') { + *nw = '\0'; + sa2 = nw + 1; + } nw++; } *nw = '\0'; @@ -895,7 +915,7 @@ int parse_stick_table(const char *file, int linenum, char **args, goto out; } - err = stktable_alloc_data_type(t, type, sa); + err = stktable_alloc_data_type(t, type, sa, sa2); switch (err) { case PE_NONE: break; case PE_EXIST: @@ -915,6 +935,11 @@ int parse_stick_table(const char *file, int linenum, char **args, file, linenum, args[0], cw); err_code |= ERR_ALERT | ERR_FATAL; goto out; + case PE_ARG_VALUE_OOR: + ha_alert("parsing [%s:%d] : %s: array size is out of allowed range (1-%d) for store option '%s'.\n", + file, linenum, args[0], STKTABLE_MAX_DT_ARRAY_SIZE, cw); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; default: ha_alert("parsing [%s:%d] : %s: error when processing store option '%s'.\n", @@ -3614,6 +3639,53 @@ static int table_dump_entry_to_buffer(struct buffer *msg, if (t->data_ofs[dt] == 0) continue; + if (stktable_data_types[dt].is_array) { + char tmp[16] = {}; + const char *name_pfx = stktable_data_types[dt].name; + const char *name_sfx = NULL; + unsigned int idx = 0; + int i = 0; + + /* split name to show index before first _ of the name + * for example: 'gpc3_rate' if array name is 'gpc_rate'. + */ + for (i = 0 ; i < (sizeof(tmp) - 1); i++) { + if (!name_pfx[i]) + break; + if (name_pfx[i] == '_') { + name_pfx = &tmp[0]; + name_sfx = &stktable_data_types[dt].name[i]; + break; + } + tmp[i] = name_pfx[i]; + } + + ptr = stktable_data_ptr_idx(t, entry, dt, idx); + while (ptr) { + if (stktable_data_types[dt].arg_type == ARG_T_DELAY) + chunk_appendf(msg, " %s%u%s(%u)=", name_pfx, idx, name_sfx ? name_sfx : "", t->data_arg[dt].u); + else + chunk_appendf(msg, " %s%u%s=", name_pfx, idx, name_sfx ? name_sfx : ""); + switch (stktable_data_types[dt].std_type) { + case STD_T_SINT: + chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint)); + break; + case STD_T_UINT: + chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint)); + break; + case STD_T_ULL: + chunk_appendf(msg, "%llu", stktable_data_cast(ptr, std_t_ull)); + break; + case STD_T_FRQP: + chunk_appendf(msg, "%u", + read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + t->data_arg[dt].u)); + break; + } + ptr = stktable_data_ptr_idx(t, entry, dt, ++idx); + } + continue; + } if (stktable_data_types[dt].arg_type == ARG_T_DELAY) chunk_appendf(msg, " %s(%u)=", stktable_data_types[dt].name, t->data_arg[dt].u); else