Hi everyone, My colleague Christian Norbert Menges (in CC) has written a patch that I now polished and made ready for submission. It adds a converter function to convert bytes, that are assumed to be saved in little endian, into decimal representation, similarly to the existing be2dec sample fetch. This is necessary since Azure saves their PPv2 TLVs in little endian, see https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview. This way, we can write something like `fc_pp_tlv(0xee),bytes(1,4),le2dec(,4)` to read the TLV. There are some formatting issues with commas, but the found lines are consistent with existing code, so it should be fine.
Please let me know what you think. We’re not aware of any other way of achieving this. Best regards, Alexander From 8a81f15041dc90e2c7f171d32236a95c9d092b97 Mon Sep 17 00:00:00 2001 From: Alexander Stephan alexander.step...@sap.com<mailto:alexander.step...@sap.com> Date: Fri, 14 Mar 2025 11:17:09 +0000 Subject: [PATCH] MINOR: sample: Add le2dec (little endian to decimal) sample fetch This commit introduces a sample fetch, `le2dec`, to convert little-endian binary input samples into their decimal representations. The function converts the input into a string containing unsigned integer numbers, with each number derived from a specified number of input bytes. The numbers are separated using a user-defined separator. This new sample is achieved by adding a parametrized sample_conv_2dec function, unifying the logic for be2dec and le2dec converters. Co-authored-by: Christian Norbert Menges christian.norbert.men...@sap.com<mailto:christian.norbert.men...@sap.com> --- doc/configuration.txt | 14 +++++++++ reg-tests/converter/le2dec.vtc | 56 ++++++++++++++++++++++++++++++++++ src/sample.c | 41 ++++++++++++++++++++----- 3 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 reg-tests/converter/le2dec.vtc diff --git a/doc/configuration.txt b/doc/configuration.txt index 2404544c3..cf8639818 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -20264,6 +20264,7 @@ and(value) integer integer b64dec string binary base64 binary string be2dec(separator,chunk_size[,truncate]) binary string +le2dec(separator,chunk_size[,truncate]) binary string be2hex([separator[,chunk_size[,truncate]]]) binary string bool integer boolean bytes(offset[,length]) binary binary @@ -20504,6 +20505,19 @@ be2dec(<separator>,<chunk_size>[,<truncate>]) bin(01020304050607),be2dec(,2,1) # 2587721286 bin(7f000001),be2dec(.,1) # 127.0.0.1 +le2dec(<separator>,<chunk_size>[,<truncate>]) + Converts little-endian binary input sample to a string containing an unsigned + integer number per <chunk_size> input bytes. <separator> is inserted every + <chunk_size> binary input bytes if specified. The <truncate> flag indicates + whether the binary input is truncated at <chunk_size> boundaries. The maximum + value for <chunk_size> is limited by the size of long long int (8 bytes). + + Example: + bin(01020304050607),le2dec(:,2) # 513:1284:2055:7 + bin(01020304050607),le2dec(-,2,1) # 513-1284-2055 + bin(01020304050607),le2dec(,2,1) # 51312842055 + bin(7f000001),le2dec(.,1) # 127.0.0.1 + be2hex([<separator>[,<chunk_size>[,<truncate>]]]) Converts big-endian binary input sample to a hex string containing two hex digits per input byte. It is used to log or transfer hex dumps of some diff --git a/reg-tests/converter/le2dec.vtc b/reg-tests/converter/le2dec.vtc new file mode 100644 index 000000000..327b91253 --- /dev/null +++ b/reg-tests/converter/le2dec.vtc @@ -0,0 +1,56 @@ +varnishtest "le2dec converter Test" + +feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.0-dev0)'" +feature ignore_unknown_macro + +server s1 { + rxreq + txresp -hdr "Connection: close" +} -repeat 3 -start + +haproxy h1 -conf { + defaults + mode http + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe + bind "fd@${fe}" + + #### requests + http-request set-var(txn.input) req.hdr(input) + + http-response set-header le2dec-1 "%[var(txn.input),le2dec(:,1)]" + http-response set-header le2dec-2 "%[var(txn.input),le2dec(-,3)]" + http-response set-header le2dec-3 "%[var(txn.input),le2dec(::,3,1)]" + + default_backend be + + backend be + server s1 ${s1_addr}:${s1_port} +} -start + +client c1 -connect ${h1_fe_sock} { + txreq -url "/" \ + -hdr "input:" + rxresp + expect resp.status == 200 + expect resp.http.le2dec-1 == "" + expect resp.http.le2dec-2 == "" + expect resp.http.le2dec-3 == "" + txreq -url "/" \ + -hdr "input: 0123456789" + rxresp + expect resp.status == 200 + expect resp.http.le2dec-1 == "48:49:50:51:52:53:54:55:56:57" + expect resp.http.le2dec-2 == "3289392-3486771-3684150-57" + expect resp.http.le2dec-3 == "3289392::3486771::3684150" + txreq -url "/" \ + -hdr "input: abcdefghijklmnopqrstuvwxyz" + rxresp + expect resp.status == 200 + expect resp.http.le2dec-1 == "97:98:99:100:101:102:103:104:105:106:107:108:109:110:111:112:113:114:115:116:117:118:119:120:121:122" + expect resp.http.le2dec-2 == "6513249-6710628-6908007-7105386-7302765-7500144-7697523-7894902-31353" + expect resp.http.le2dec-3 == "6513249::6710628::6908007::7105386::7302765::7500144::7697523::7894902" +} -run diff --git a/src/sample.c b/src/sample.c index ab44bfecc..f4522769a 100644 --- a/src/sample.c +++ b/src/sample.c @@ -1983,7 +1983,7 @@ int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp) } } -static int sample_conv_be2dec_check(struct arg *args, struct sample_conv *conv, +static int sample_conv_2dec_check(struct arg *args, struct sample_conv *conv, const char *file, int line, char **err) { if (args[1].data.sint <= 0 || args[1].data.sint > sizeof(unsigned long long)) { @@ -1999,13 +1999,13 @@ static int sample_conv_be2dec_check(struct arg *args, struct sample_conv *conv, return 1; } -/* Converts big-endian binary input sample to a string containing an unsigned +/* Converts big-endian/little-endian binary input sample to a string containing an unsigned * integer number per <chunk_size> input bytes separated with <separator>. * Optional <truncate> flag indicates if input is truncated at <chunk_size> * boundaries. - * Arguments: separator (string), chunk_size (integer), truncate (0,1) + * Arguments: separator (string), chunk_size (integer), truncate (0,1), big endian (0,1) */ -static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void *private) +static int sample_conv_2dec(const struct arg *args, struct sample *smp, void *private, int be) { struct buffer *trash = get_trash_chunk(); const int last = args[2].data.sint ? smp->data.u.str.data - args[1].data.sint + 1 : smp->data.u.str.data; @@ -2029,8 +2029,12 @@ static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void * max_size -= args[0].data.str.data; /* Add integer */ - for (number = 0, i = 0; i < args[1].data.sint && ptr < smp->data.u.str.data; i++) - number = (number << 8) + (unsigned char)smp->data.u.str.area[ptr++]; + for (number = 0, i = 0; i < args[1].data.sint && ptr < smp->data.u.str.data; i++) { + if (be) + number = (number << 8) + (unsigned char)smp->data.u.str.area[ptr++]; + else + number |= (unsigned char)smp->data.u.str.area[ptr++] << (i*8); + } pos = ulltoa(number, trash->area + trash->data, trash->size - trash->data); if (pos) @@ -2047,6 +2051,28 @@ static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void * return 1; } +/* Converts big-endian binary input sample to a string containing an unsigned + * integer number per <chunk_size> input bytes separated with <separator>. + * Optional <truncate> flag indicates if input is truncated at <chunk_size> + * boundaries. + * Arguments: separator (string), chunk_size (integer), truncate (0,1) + */ +static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void *private) +{ + return sample_conv_2dec(args, smp, private, 1); +} + +/* Converts little-endian binary input sample to a string containing an unsigned + * integer number per <chunk_size> input bytes separated with <separator>. + * Optional <truncate> flag indicates if input is truncated at <chunk_size> + * boundaries. + * Arguments: separator (string), chunk_size (integer), truncate (0,1) + */ +static int sample_conv_le2dec(const struct arg *args, struct sample *smp, void *private) +{ + return sample_conv_2dec(args, smp, private, 0); +} + static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv, const char *file, int line, char **err) { @@ -5372,7 +5398,8 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, { { "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR, SMP_T_STR }, { "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR, SMP_T_STR }, { "length", sample_conv_length, 0, NULL, SMP_T_STR, SMP_T_SINT }, - { "be2dec", sample_conv_be2dec, ARG3(1,STR,SINT,SINT), sample_conv_be2dec_check, SMP_T_BIN, SMP_T_STR }, + { "be2dec", sample_conv_be2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR }, + { "le2dec", sample_conv_le2dec, ARG3(1,STR,SINT,SINT), sample_conv_2dec_check, SMP_T_BIN, SMP_T_STR }, { "be2hex", sample_conv_be2hex, ARG3(1,STR,SINT,SINT), sample_conv_be2hex_check, SMP_T_BIN, SMP_T_STR }, { "hex", sample_conv_bin2hex, 0, NULL, SMP_T_BIN, SMP_T_STR }, { "hex2i", sample_conv_hex2int, 0, NULL, SMP_T_STR, SMP_T_SINT }, -- 2.35.3