[dpdk-dev,v5,19/26] app/testpmd: add item raw to flow command

Message ID 180091c2bf814c846abbbfb1c2c50e9ec32d7324.1482331076.git.adrien.mazarguil@6wind.com (mailing list archive)
State Accepted, archived
Delegated to: Thomas Monjalon
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel compilation success Compilation OK

Commit Message

Adrien Mazarguil Dec. 21, 2016, 2:51 p.m. UTC
  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

Zhao1, Wei May 11, 2017, 6:53 a.m. UTC | #1
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
  
Adrien Mazarguil May 12, 2017, 9:12 a.m. UTC | #2
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.
  
Zhao1, Wei May 16, 2017, 5:05 a.m. UTC | #3
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
  

Patch

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)
+
 /** 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,