From: Emeric Brun Date: Wed, 30 Jun 2021 16:01:02 +0000 (+0200) Subject: MEDIUM: stick-table: handle arrays of standard types into stick-tables X-Git-Tag: v2.5-dev2~49 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c64a2a307ccd304f0e5ee5a842b019cf6d4053b1;p=thirdparty%2Fhaproxy.git MEDIUM: stick-table: handle arrays of standard types into stick-tables This patch provides the code to handle arrays of some standard types (SINT, UINT, ULL and FRQP) in stick table. This way we could define new "array" data types. Note: the number of elements of an array was limited to 100 to put a limit and to ensure that an encoded update message will continue to fit into a buffer when the peer protocol will handle such data types. --- diff --git a/include/haproxy/errors.h b/include/haproxy/errors.h index bebad844af..e9ad588357 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 f09956aaf2..89d1b2a2a3 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 4dffe2a854..ff172f4584 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 aa07a2fd10..6bf8e6d59c 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 73c8fc184f..349e138c8f 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