[dpdk-dev,v5,19/26] app/testpmd: add item raw to flow command
Checks
Commit Message
Matches arbitrary byte strings with properties:
- relative: look for pattern after the previous item.
- search: search pattern from offset (see also limit).
- offset: absolute or relative offset for pattern.
- limit: search area limit for start of pattern.
- length: pattern length.
- pattern: byte string to look for.
Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Olga Shern <olgas@mellanox.com>
---
app/test-pmd/cmdline_flow.c | 208 +++++++++++++++++++++++++++++++++++++++
1 file changed, 208 insertions(+)
Comments
Hi, Adrien
> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Adrien Mazarguil
> Sent: Wednesday, December 21, 2016 10:52 PM
> To: dev@dpdk.org
> Subject: [dpdk-dev] [PATCH v5 19/26] app/testpmd: add item raw to flow
> command
>
> Matches arbitrary byte strings with properties:
>
> - relative: look for pattern after the previous item.
> - search: search pattern from offset (see also limit).
> - offset: absolute or relative offset for pattern.
> - limit: search area limit for start of pattern.
> - length: pattern length.
> - pattern: byte string to look for.
>
> Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
> Acked-by: Olga Shern <olgas@mellanox.com>
> ---
> app/test-pmd/cmdline_flow.c | 208
> +++++++++++++++++++++++++++++++++++++++
> 1 file changed, 208 insertions(+)
>
> diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
> index 0592969..c52a8f7 100644
> --- a/app/test-pmd/cmdline_flow.c
> +++ b/app/test-pmd/cmdline_flow.c
> @@ -57,6 +57,8 @@ enum index {
> INTEGER,
> UNSIGNED,
> PREFIX,
> + BOOLEAN,
> + STRING,
> RULE_ID,
> PORT_ID,
> GROUP_ID,
> @@ -106,6 +108,12 @@ enum index {
> ITEM_VF_ID,
> ITEM_PORT,
> ITEM_PORT_INDEX,
> + ITEM_RAW,
> + ITEM_RAW_RELATIVE,
> + ITEM_RAW_SEARCH,
> + ITEM_RAW_OFFSET,
> + ITEM_RAW_LIMIT,
> + ITEM_RAW_PATTERN,
>
> /* Validate/create actions. */
> ACTIONS,
> @@ -115,6 +123,13 @@ enum index {
> ACTION_PASSTHRU,
> };
>
> +/** Size of pattern[] field in struct rte_flow_item_raw. */ #define
> +ITEM_RAW_PATTERN_SIZE 36
> +
> +/** Storage size for struct rte_flow_item_raw including pattern. */
> +#define ITEM_RAW_SIZE \
> + (offsetof(struct rte_flow_item_raw, pattern) +
> ITEM_RAW_PATTERN_SIZE)
#define ITEM_RAW_PATTERN_SIZE 36
The size of NIC i350 flex byte filter can accommodate the max length size of 128 byte, and the reason to
Define it as 36 is ?If it is the max length of pattern, maybe 128 is more appropriate?
Maybe I have not understand your purpose.
Thank you.
> +
> /** Maximum number of subsequent tokens and arguments on the stack.
> */ #define CTX_STACK_SIZE 16
>
> @@ -216,6 +231,13 @@ struct token {
> .size = sizeof(*((s *)0)->f), \
> })
>
> +/** Static initializer for ARGS() with arbitrary size. */ #define
> +ARGS_ENTRY_USZ(s, f, sz) \
> + (&(const struct arg){ \
> + .offset = offsetof(s, f), \
> + .size = (sz), \
> + })
> +
> /** Parser output buffer layout expected by cmd_flow_parsed(). */ struct
> buffer {
> enum index command; /**< Flow command. */ @@ -306,6 +328,7
> @@ static const enum index next_item[] = {
> ITEM_PF,
> ITEM_VF,
> ITEM_PORT,
> + ITEM_RAW,
> ZERO,
> };
>
> @@ -327,6 +350,16 @@ static const enum index item_port[] = {
> ZERO,
> };
>
> +static const enum index item_raw[] = {
> + ITEM_RAW_RELATIVE,
> + ITEM_RAW_SEARCH,
> + ITEM_RAW_OFFSET,
> + ITEM_RAW_LIMIT,
> + ITEM_RAW_PATTERN,
> + ITEM_NEXT,
> + ZERO,
> +};
> +
> static const enum index next_action[] = {
> ACTION_END,
> ACTION_VOID,
> @@ -363,11 +396,19 @@ static int parse_int(struct context *, const struct
> token *, static int parse_prefix(struct context *, const struct token *,
> const char *, unsigned int,
> void *, unsigned int);
> +static int parse_boolean(struct context *, const struct token *,
> + const char *, unsigned int,
> + void *, unsigned int);
> +static int parse_string(struct context *, const struct token *,
> + const char *, unsigned int,
> + void *, unsigned int);
> static int parse_port(struct context *, const struct token *,
> const char *, unsigned int,
> void *, unsigned int);
> static int comp_none(struct context *, const struct token *,
> unsigned int, char *, unsigned int);
> +static int comp_boolean(struct context *, const struct token *,
> + unsigned int, char *, unsigned int);
> static int comp_action(struct context *, const struct token *,
> unsigned int, char *, unsigned int); static int
> comp_port(struct context *, const struct token *, @@ -410,6 +451,20 @@
> static const struct token token_list[] = {
> .call = parse_prefix,
> .comp = comp_none,
> },
> + [BOOLEAN] = {
> + .name = "{boolean}",
> + .type = "BOOLEAN",
> + .help = "any boolean value",
> + .call = parse_boolean,
> + .comp = comp_boolean,
> + },
> + [STRING] = {
> + .name = "{string}",
> + .type = "STRING",
> + .help = "fixed string",
> + .call = parse_string,
> + .comp = comp_none,
> + },
> [RULE_ID] = {
> .name = "{rule id}",
> .type = "RULE ID",
> @@ -654,6 +709,52 @@ static const struct token token_list[] = {
> .next = NEXT(item_port, NEXT_ENTRY(UNSIGNED),
> item_param),
> .args = ARGS(ARGS_ENTRY(struct rte_flow_item_port,
> index)),
> },
> + [ITEM_RAW] = {
> + .name = "raw",
> + .help = "match an arbitrary byte string",
> + .priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
> + .next = NEXT(item_raw),
> + .call = parse_vc,
> + },
> + [ITEM_RAW_RELATIVE] = {
> + .name = "relative",
> + .help = "look for pattern after the previous item",
> + .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN),
> item_param),
> + .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
> + relative, 1)),
> + },
> + [ITEM_RAW_SEARCH] = {
> + .name = "search",
> + .help = "search pattern from offset (see also limit)",
> + .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN),
> item_param),
> + .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
> + search, 1)),
> + },
> + [ITEM_RAW_OFFSET] = {
> + .name = "offset",
> + .help = "absolute or relative offset for pattern",
> + .next = NEXT(item_raw, NEXT_ENTRY(INTEGER),
> item_param),
> + .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw,
> offset)),
> + },
> + [ITEM_RAW_LIMIT] = {
> + .name = "limit",
> + .help = "search area limit for start of pattern",
> + .next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED),
> item_param),
> + .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)),
> + },
> + [ITEM_RAW_PATTERN] = {
> + .name = "pattern",
> + .help = "byte string to look for",
> + .next = NEXT(item_raw,
> + NEXT_ENTRY(STRING),
> + NEXT_ENTRY(ITEM_PARAM_IS,
> + ITEM_PARAM_SPEC,
> + ITEM_PARAM_MASK)),
> + .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw,
> length),
> + ARGS_ENTRY_USZ(struct rte_flow_item_raw,
> + pattern,
> + ITEM_RAW_PATTERN_SIZE)),
> + },
> /* Validate/create actions. */
> [ACTIONS] = {
> .name = "actions",
> @@ -1246,6 +1347,96 @@ parse_int(struct context *ctx, const struct token
> *token,
> return -1;
> }
>
> +/**
> + * Parse a string.
> + *
> + * Two arguments (ctx->args) are retrieved from the stack to store data
> +and
> + * its length (in that order).
> + */
> +static int
> +parse_string(struct context *ctx, const struct token *token,
> + const char *str, unsigned int len,
> + void *buf, unsigned int size)
> +{
> + const struct arg *arg_data = pop_args(ctx);
> + const struct arg *arg_len = pop_args(ctx);
> + char tmp[16]; /* Ought to be enough. */
> + int ret;
> +
> + /* Arguments are expected. */
> + if (!arg_data)
> + return -1;
> + if (!arg_len) {
> + push_args(ctx, arg_data);
> + return -1;
> + }
> + size = arg_data->size;
> + /* Bit-mask fill is not supported. */
> + if (arg_data->mask || size < len)
> + goto error;
> + if (!ctx->object)
> + return len;
> + /* Let parse_int() fill length information first. */
> + ret = snprintf(tmp, sizeof(tmp), "%u", len);
> + if (ret < 0)
> + goto error;
> + push_args(ctx, arg_len);
> + ret = parse_int(ctx, token, tmp, ret, NULL, 0);
> + if (ret < 0) {
> + pop_args(ctx);
> + goto error;
> + }
> + buf = (uint8_t *)ctx->object + arg_data->offset;
> + /* Output buffer is not necessarily NUL-terminated. */
> + memcpy(buf, str, len);
> + memset((uint8_t *)buf + len, 0x55, size - len);
> + if (ctx->objmask)
> + memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff,
> len);
> + return len;
> +error:
> + push_args(ctx, arg_len);
> + push_args(ctx, arg_data);
> + return -1;
> +}
> +
> +/** Boolean values (even indices stand for false). */ static const char
> +*const boolean_name[] = {
> + "0", "1",
> + "false", "true",
> + "no", "yes",
> + "N", "Y",
> + NULL,
> +};
> +
> +/**
> + * Parse a boolean value.
> + *
> + * Last argument (ctx->args) is retrieved to determine storage size and
> + * location.
> + */
> +static int
> +parse_boolean(struct context *ctx, const struct token *token,
> + const char *str, unsigned int len,
> + void *buf, unsigned int size)
> +{
> + const struct arg *arg = pop_args(ctx);
> + unsigned int i;
> + int ret;
> +
> + /* Argument is expected. */
> + if (!arg)
> + return -1;
> + for (i = 0; boolean_name[i]; ++i)
> + if (!strncmp(str, boolean_name[i], len))
> + break;
> + /* Process token as integer. */
> + if (boolean_name[i])
> + str = i & 1 ? "1" : "0";
> + push_args(ctx, arg);
> + ret = parse_int(ctx, token, str, strlen(str), buf, size);
> + return ret > 0 ? (int)len : ret;
> +}
> +
> /** Parse port and update context. */
> static int
> parse_port(struct context *ctx, const struct token *token, @@ -1284,6
> +1475,23 @@ comp_none(struct context *ctx, const struct token *token,
> return 0;
> }
>
> +/** Complete boolean values. */
> +static int
> +comp_boolean(struct context *ctx, const struct token *token,
> + unsigned int ent, char *buf, unsigned int size) {
> + unsigned int i;
> +
> + (void)ctx;
> + (void)token;
> + for (i = 0; boolean_name[i]; ++i)
> + if (buf && i == ent)
> + return snprintf(buf, size, "%s", boolean_name[i]);
> + if (buf)
> + return -1;
> + return i;
> +}
> +
> /** Complete action names. */
> static int
> comp_action(struct context *ctx, const struct token *token,
> --
> 2.1.4
Hi Wei,
On Thu, May 11, 2017 at 06:53:52AM +0000, Zhao1, Wei wrote:
> Hi, Adrien
>
> > -----Original Message-----
> > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Adrien Mazarguil
> > Sent: Wednesday, December 21, 2016 10:52 PM
> > To: dev@dpdk.org
> > Subject: [dpdk-dev] [PATCH v5 19/26] app/testpmd: add item raw to flow
> > command
> >
> > Matches arbitrary byte strings with properties:
> >
> > - relative: look for pattern after the previous item.
> > - search: search pattern from offset (see also limit).
> > - offset: absolute or relative offset for pattern.
> > - limit: search area limit for start of pattern.
> > - length: pattern length.
> > - pattern: byte string to look for.
> >
> > Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
> > Acked-by: Olga Shern <olgas@mellanox.com>
[...]
> #define ITEM_RAW_PATTERN_SIZE 36
>
> The size of NIC i350 flex byte filter can accommodate the max length size of 128 byte, and the reason to
> Define it as 36 is ?If it is the max length of pattern, maybe 128 is more appropriate?
> Maybe I have not understand your purpose.
>
> Thank you.
It's more or less an arbitrary compromise due to various limitations.
Once parsed, the result of an entire command is stored in a fixed buffer of
size CMDLINE_PARSE_RESULT_BUFSIZE (8192). Each parsed token ends up
somewhere in that buffer.
Each flow item always consumes sizeof(struct rte_flow_item) + sizeof(struct
rte_flow_item_xxx) * 3 (spec, last and mask) + alignment constraints.
For the raw item, this makes at least:
(sizeof(rte_flow_item) +
(sizeof(rte_flow_item_raw) + ITEM_RAW_PATTERN_SIZE) * 3)
/* (32 + (12 + 36) * 3) => 176 bytes */
Because space is always consumed regardless of the size of the byte string
to match for implementation reasons, there is a chance to fill the buffer
too quickly with a larger ITEM_RAW_PATTERN_SIZE.
Also, this does not prevent users from specifying larger raw patterns (even
larger than 128) by combining them, e.g.:
flow create 0
pattern eth / raw relative is 1 pattern is foobar /
raw relative is 1 pattern is barbaz / end
actions queue index 42 / end
Such a pattern ends up matching a single "foobarbarbaz" string.
To summarize, it is only due to testpmd limitations. Even without PMD
support for combination, the current ability to provide 36 bytes of raw data
to match per specified item is plenty to validate basic functionality. We'll
improve testpmd eventually.
Hi, Adrien Mazarguil
> -----Original Message-----
> From: Adrien Mazarguil [mailto:adrien.mazarguil@6wind.com]
> Sent: Friday, May 12, 2017 5:13 PM
> To: Zhao1, Wei <wei.zhao1@intel.com>
> Cc: dev@dpdk.org; Xing, Beilei <beilei.xing@intel.com>; Lu, Wenzhuo
> <wenzhuo.lu@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v5 19/26] app/testpmd: add item raw to flow
> command
>
> Hi Wei,
>
> On Thu, May 11, 2017 at 06:53:52AM +0000, Zhao1, Wei wrote:
> > Hi, Adrien
> >
> > > -----Original Message-----
> > > From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Adrien
> > > Mazarguil
> > > Sent: Wednesday, December 21, 2016 10:52 PM
> > > To: dev@dpdk.org
> > > Subject: [dpdk-dev] [PATCH v5 19/26] app/testpmd: add item raw to
> > > flow command
> > >
> > > Matches arbitrary byte strings with properties:
> > >
> > > - relative: look for pattern after the previous item.
> > > - search: search pattern from offset (see also limit).
> > > - offset: absolute or relative offset for pattern.
> > > - limit: search area limit for start of pattern.
> > > - length: pattern length.
> > > - pattern: byte string to look for.
> > >
> > > Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
> > > Acked-by: Olga Shern <olgas@mellanox.com>
> [...]
> > #define ITEM_RAW_PATTERN_SIZE 36
> >
> > The size of NIC i350 flex byte filter can accommodate the max length
> > size of 128 byte, and the reason to Define it as 36 is ?If it is the max length
> of pattern, maybe 128 is more appropriate?
> > Maybe I have not understand your purpose.
> >
> > Thank you.
>
> It's more or less an arbitrary compromise due to various limitations.
>
> Once parsed, the result of an entire command is stored in a fixed buffer of
> size CMDLINE_PARSE_RESULT_BUFSIZE (8192). Each parsed token ends up
> somewhere in that buffer.
>
> Each flow item always consumes sizeof(struct rte_flow_item) + sizeof(struct
> rte_flow_item_xxx) * 3 (spec, last and mask) + alignment constraints.
>
> For the raw item, this makes at least:
>
> (sizeof(rte_flow_item) +
> (sizeof(rte_flow_item_raw) + ITEM_RAW_PATTERN_SIZE) * 3)
> /* (32 + (12 + 36) * 3) => 176 bytes */
>
> Because space is always consumed regardless of the size of the byte string to
> match for implementation reasons, there is a chance to fill the buffer too
> quickly with a larger ITEM_RAW_PATTERN_SIZE.
>
> Also, this does not prevent users from specifying larger raw patterns (even
> larger than 128) by combining them, e.g.:
>
> flow create 0
> pattern eth / raw relative is 1 pattern is foobar /
> raw relative is 1 pattern is barbaz / end
> actions queue index 42 / end
>
> Such a pattern ends up matching a single "foobarbarbaz" string.
>
> To summarize, it is only due to testpmd limitations. Even without PMD
> support for combination, the current ability to provide 36 bytes of raw data
> to match per specified item is plenty to validate basic functionality. We'll
> improve testpmd eventually.
>
Thank you for your detailed explanation.
Igb flex byte filter will support for that type combination for raw item.
But this testpmd limitation will make trouble for users and tester.
> --
> Adrien Mazarguil
> 6WIND
@@ -57,6 +57,8 @@ enum index {
INTEGER,
UNSIGNED,
PREFIX,
+ BOOLEAN,
+ STRING,
RULE_ID,
PORT_ID,
GROUP_ID,
@@ -106,6 +108,12 @@ enum index {
ITEM_VF_ID,
ITEM_PORT,
ITEM_PORT_INDEX,
+ ITEM_RAW,
+ ITEM_RAW_RELATIVE,
+ ITEM_RAW_SEARCH,
+ ITEM_RAW_OFFSET,
+ ITEM_RAW_LIMIT,
+ ITEM_RAW_PATTERN,
/* Validate/create actions. */
ACTIONS,
@@ -115,6 +123,13 @@ enum index {
ACTION_PASSTHRU,
};
+/** Size of pattern[] field in struct rte_flow_item_raw. */
+#define ITEM_RAW_PATTERN_SIZE 36
+
+/** Storage size for struct rte_flow_item_raw including pattern. */
+#define ITEM_RAW_SIZE \
+ (offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
+
/** Maximum number of subsequent tokens and arguments on the stack. */
#define CTX_STACK_SIZE 16
@@ -216,6 +231,13 @@ struct token {
.size = sizeof(*((s *)0)->f), \
})
+/** Static initializer for ARGS() with arbitrary size. */
+#define ARGS_ENTRY_USZ(s, f, sz) \
+ (&(const struct arg){ \
+ .offset = offsetof(s, f), \
+ .size = (sz), \
+ })
+
/** Parser output buffer layout expected by cmd_flow_parsed(). */
struct buffer {
enum index command; /**< Flow command. */
@@ -306,6 +328,7 @@ static const enum index next_item[] = {
ITEM_PF,
ITEM_VF,
ITEM_PORT,
+ ITEM_RAW,
ZERO,
};
@@ -327,6 +350,16 @@ static const enum index item_port[] = {
ZERO,
};
+static const enum index item_raw[] = {
+ ITEM_RAW_RELATIVE,
+ ITEM_RAW_SEARCH,
+ ITEM_RAW_OFFSET,
+ ITEM_RAW_LIMIT,
+ ITEM_RAW_PATTERN,
+ ITEM_NEXT,
+ ZERO,
+};
+
static const enum index next_action[] = {
ACTION_END,
ACTION_VOID,
@@ -363,11 +396,19 @@ static int parse_int(struct context *, const struct token *,
static int parse_prefix(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
+static int parse_boolean(struct context *, const struct token *,
+ const char *, unsigned int,
+ void *, unsigned int);
+static int parse_string(struct context *, const struct token *,
+ const char *, unsigned int,
+ void *, unsigned int);
static int parse_port(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
static int comp_none(struct context *, const struct token *,
unsigned int, char *, unsigned int);
+static int comp_boolean(struct context *, const struct token *,
+ unsigned int, char *, unsigned int);
static int comp_action(struct context *, const struct token *,
unsigned int, char *, unsigned int);
static int comp_port(struct context *, const struct token *,
@@ -410,6 +451,20 @@ static const struct token token_list[] = {
.call = parse_prefix,
.comp = comp_none,
},
+ [BOOLEAN] = {
+ .name = "{boolean}",
+ .type = "BOOLEAN",
+ .help = "any boolean value",
+ .call = parse_boolean,
+ .comp = comp_boolean,
+ },
+ [STRING] = {
+ .name = "{string}",
+ .type = "STRING",
+ .help = "fixed string",
+ .call = parse_string,
+ .comp = comp_none,
+ },
[RULE_ID] = {
.name = "{rule id}",
.type = "RULE ID",
@@ -654,6 +709,52 @@ static const struct token token_list[] = {
.next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param),
.args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)),
},
+ [ITEM_RAW] = {
+ .name = "raw",
+ .help = "match an arbitrary byte string",
+ .priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE),
+ .next = NEXT(item_raw),
+ .call = parse_vc,
+ },
+ [ITEM_RAW_RELATIVE] = {
+ .name = "relative",
+ .help = "look for pattern after the previous item",
+ .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+ .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+ relative, 1)),
+ },
+ [ITEM_RAW_SEARCH] = {
+ .name = "search",
+ .help = "search pattern from offset (see also limit)",
+ .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param),
+ .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw,
+ search, 1)),
+ },
+ [ITEM_RAW_OFFSET] = {
+ .name = "offset",
+ .help = "absolute or relative offset for pattern",
+ .next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param),
+ .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, offset)),
+ },
+ [ITEM_RAW_LIMIT] = {
+ .name = "limit",
+ .help = "search area limit for start of pattern",
+ .next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param),
+ .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)),
+ },
+ [ITEM_RAW_PATTERN] = {
+ .name = "pattern",
+ .help = "byte string to look for",
+ .next = NEXT(item_raw,
+ NEXT_ENTRY(STRING),
+ NEXT_ENTRY(ITEM_PARAM_IS,
+ ITEM_PARAM_SPEC,
+ ITEM_PARAM_MASK)),
+ .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, length),
+ ARGS_ENTRY_USZ(struct rte_flow_item_raw,
+ pattern,
+ ITEM_RAW_PATTERN_SIZE)),
+ },
/* Validate/create actions. */
[ACTIONS] = {
.name = "actions",
@@ -1246,6 +1347,96 @@ parse_int(struct context *ctx, const struct token *token,
return -1;
}
+/**
+ * Parse a string.
+ *
+ * Two arguments (ctx->args) are retrieved from the stack to store data and
+ * its length (in that order).
+ */
+static int
+parse_string(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ const struct arg *arg_data = pop_args(ctx);
+ const struct arg *arg_len = pop_args(ctx);
+ char tmp[16]; /* Ought to be enough. */
+ int ret;
+
+ /* Arguments are expected. */
+ if (!arg_data)
+ return -1;
+ if (!arg_len) {
+ push_args(ctx, arg_data);
+ return -1;
+ }
+ size = arg_data->size;
+ /* Bit-mask fill is not supported. */
+ if (arg_data->mask || size < len)
+ goto error;
+ if (!ctx->object)
+ return len;
+ /* Let parse_int() fill length information first. */
+ ret = snprintf(tmp, sizeof(tmp), "%u", len);
+ if (ret < 0)
+ goto error;
+ push_args(ctx, arg_len);
+ ret = parse_int(ctx, token, tmp, ret, NULL, 0);
+ if (ret < 0) {
+ pop_args(ctx);
+ goto error;
+ }
+ buf = (uint8_t *)ctx->object + arg_data->offset;
+ /* Output buffer is not necessarily NUL-terminated. */
+ memcpy(buf, str, len);
+ memset((uint8_t *)buf + len, 0x55, size - len);
+ if (ctx->objmask)
+ memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
+ return len;
+error:
+ push_args(ctx, arg_len);
+ push_args(ctx, arg_data);
+ return -1;
+}
+
+/** Boolean values (even indices stand for false). */
+static const char *const boolean_name[] = {
+ "0", "1",
+ "false", "true",
+ "no", "yes",
+ "N", "Y",
+ NULL,
+};
+
+/**
+ * Parse a boolean value.
+ *
+ * Last argument (ctx->args) is retrieved to determine storage size and
+ * location.
+ */
+static int
+parse_boolean(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ const struct arg *arg = pop_args(ctx);
+ unsigned int i;
+ int ret;
+
+ /* Argument is expected. */
+ if (!arg)
+ return -1;
+ for (i = 0; boolean_name[i]; ++i)
+ if (!strncmp(str, boolean_name[i], len))
+ break;
+ /* Process token as integer. */
+ if (boolean_name[i])
+ str = i & 1 ? "1" : "0";
+ push_args(ctx, arg);
+ ret = parse_int(ctx, token, str, strlen(str), buf, size);
+ return ret > 0 ? (int)len : ret;
+}
+
/** Parse port and update context. */
static int
parse_port(struct context *ctx, const struct token *token,
@@ -1284,6 +1475,23 @@ comp_none(struct context *ctx, const struct token *token,
return 0;
}
+/** Complete boolean values. */
+static int
+comp_boolean(struct context *ctx, const struct token *token,
+ unsigned int ent, char *buf, unsigned int size)
+{
+ unsigned int i;
+
+ (void)ctx;
+ (void)token;
+ for (i = 0; boolean_name[i]; ++i)
+ if (buf && i == ent)
+ return snprintf(buf, size, "%s", boolean_name[i]);
+ if (buf)
+ return -1;
+ return i;
+}
+
/** Complete action names. */
static int
comp_action(struct context *ctx, const struct token *token,