/* Common tokens. */
INTEGER,
UNSIGNED,
+ PREFIX,
+ BOOLEAN,
+ STRING,
RULE_ID,
PORT_ID,
GROUP_ID,
/* Validate/create pattern. */
PATTERN,
+ ITEM_PARAM_IS,
+ ITEM_PARAM_SPEC,
+ ITEM_PARAM_LAST,
+ ITEM_PARAM_MASK,
+ ITEM_PARAM_PREFIX,
ITEM_NEXT,
ITEM_END,
ITEM_VOID,
ITEM_INVERT,
+ ITEM_ANY,
+ ITEM_ANY_NUM,
+ ITEM_PF,
+ ITEM_VF,
+ 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,
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
uint16_t port; /**< Current port ID (for completions). */
uint32_t objdata; /**< Object-specific data. */
void *object; /**< Address of current object for relative offsets. */
+ void *objmask; /**< Object a full mask must be written to. */
};
/** Token argument. */
uint32_t sign:1; /**< Value is signed. */
uint32_t offset; /**< Relative offset from ctx->object. */
uint32_t size; /**< Field size. */
+ const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */
};
/** Parser token definition. */
.size = sizeof(((s *)0)->f), \
})
+/** Static initializer for ARGS() to target a bit-field. */
+#define ARGS_ENTRY_BF(s, f, b) \
+ (&(const struct arg){ \
+ .size = sizeof(s), \
+ .mask = (const void *)&(const s){ .f = (1 << (b)) - 1 }, \
+ })
+
/** Static initializer for ARGS() to target a pointer. */
#define ARGS_ENTRY_PTR(s, f) \
(&(const struct arg){ \
.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. */
ZERO,
};
+static const enum index item_param[] = {
+ ITEM_PARAM_IS,
+ ITEM_PARAM_SPEC,
+ ITEM_PARAM_LAST,
+ ITEM_PARAM_MASK,
+ ITEM_PARAM_PREFIX,
+ ZERO,
+};
+
static const enum index next_item[] = {
ITEM_END,
ITEM_VOID,
ITEM_INVERT,
+ ITEM_ANY,
+ ITEM_PF,
+ ITEM_VF,
+ ITEM_PORT,
+ ITEM_RAW,
+ ZERO,
+};
+
+static const enum index item_any[] = {
+ ITEM_ANY_NUM,
+ ITEM_NEXT,
+ ZERO,
+};
+
+static const enum index item_vf[] = {
+ ITEM_VF_ID,
+ ITEM_NEXT,
+ ZERO,
+};
+
+static const enum index item_port[] = {
+ ITEM_PORT_INDEX,
+ ITEM_NEXT,
+ 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 int parse_vc(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
+static int parse_vc_spec(struct context *, const struct token *,
+ const char *, unsigned int, void *, unsigned int);
static int parse_destroy(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
static int parse_int(struct context *, const struct token *,
const char *, unsigned int,
void *, unsigned int);
+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 *,
.call = parse_int,
.comp = comp_none,
},
+ [PREFIX] = {
+ .name = "{prefix}",
+ .type = "PREFIX",
+ .help = "prefix length for bit-mask",
+ .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",
.next = NEXT(next_item),
.call = parse_vc,
},
+ [ITEM_PARAM_IS] = {
+ .name = "is",
+ .help = "match value perfectly (with full bit-mask)",
+ .call = parse_vc_spec,
+ },
+ [ITEM_PARAM_SPEC] = {
+ .name = "spec",
+ .help = "match value according to configured bit-mask",
+ .call = parse_vc_spec,
+ },
+ [ITEM_PARAM_LAST] = {
+ .name = "last",
+ .help = "specify upper bound to establish a range",
+ .call = parse_vc_spec,
+ },
+ [ITEM_PARAM_MASK] = {
+ .name = "mask",
+ .help = "specify bit-mask with relevant bits set to one",
+ .call = parse_vc_spec,
+ },
+ [ITEM_PARAM_PREFIX] = {
+ .name = "prefix",
+ .help = "generate bit-mask from a prefix length",
+ .call = parse_vc_spec,
+ },
[ITEM_NEXT] = {
.name = "/",
.help = "specify next pattern item",
.next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
.call = parse_vc,
},
+ [ITEM_ANY] = {
+ .name = "any",
+ .help = "match any protocol for the current layer",
+ .priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)),
+ .next = NEXT(item_any),
+ .call = parse_vc,
+ },
+ [ITEM_ANY_NUM] = {
+ .name = "num",
+ .help = "number of layers covered",
+ .next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param),
+ .args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)),
+ },
+ [ITEM_PF] = {
+ .name = "pf",
+ .help = "match packets addressed to the physical function",
+ .priv = PRIV_ITEM(PF, 0),
+ .next = NEXT(NEXT_ENTRY(ITEM_NEXT)),
+ .call = parse_vc,
+ },
+ [ITEM_VF] = {
+ .name = "vf",
+ .help = "match packets addressed to a virtual function ID",
+ .priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)),
+ .next = NEXT(item_vf),
+ .call = parse_vc,
+ },
+ [ITEM_VF_ID] = {
+ .name = "id",
+ .help = "destination VF ID",
+ .next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param),
+ .args = ARGS(ARGS_ENTRY(struct rte_flow_item_vf, id)),
+ },
+ [ITEM_PORT] = {
+ .name = "port",
+ .help = "device-specific physical port index to use",
+ .priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)),
+ .next = NEXT(item_port),
+ .call = parse_vc,
+ },
+ [ITEM_PORT_INDEX] = {
+ .name = "index",
+ .help = "physical port index",
+ .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",
return 0;
}
+/** Spread value into buffer according to bit-mask. */
+static size_t
+arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg)
+{
+ uint32_t i = arg->size;
+ uint32_t end = 0;
+ int sub = 1;
+ int add = 0;
+ size_t len = 0;
+
+ if (!arg->mask)
+ return 0;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+ if (!arg->hton) {
+ i = 0;
+ end = arg->size;
+ sub = 0;
+ add = 1;
+ }
+#endif
+ while (i != end) {
+ unsigned int shift = 0;
+ uint8_t *buf = (uint8_t *)dst + arg->offset + (i -= sub);
+
+ for (shift = 0; arg->mask[i] >> shift; ++shift) {
+ if (!(arg->mask[i] & (1 << shift)))
+ continue;
+ ++len;
+ if (!dst)
+ continue;
+ *buf &= ~(1 << shift);
+ *buf |= (val & 1) << shift;
+ val >>= 1;
+ }
+ i += add;
+ }
+ return len;
+}
+
+/**
+ * Parse a prefix length and generate a bit-mask.
+ *
+ * Last argument (ctx->args) is retrieved to determine mask size, storage
+ * location and whether the result must use network byte ordering.
+ */
+static int
+parse_prefix(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);
+ static const uint8_t conv[] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe\xff";
+ char *end;
+ uintmax_t u;
+ unsigned int bytes;
+ unsigned int extra;
+
+ (void)token;
+ /* Argument is expected. */
+ if (!arg)
+ return -1;
+ errno = 0;
+ u = strtoumax(str, &end, 0);
+ if (errno || (size_t)(end - str) != len)
+ goto error;
+ if (arg->mask) {
+ uintmax_t v = 0;
+
+ extra = arg_entry_bf_fill(NULL, 0, arg);
+ if (u > extra)
+ goto error;
+ if (!ctx->object)
+ return len;
+ extra -= u;
+ while (u--)
+ (v <<= 1, v |= 1);
+ v <<= extra;
+ if (!arg_entry_bf_fill(ctx->object, v, arg) ||
+ !arg_entry_bf_fill(ctx->objmask, -1, arg))
+ goto error;
+ return len;
+ }
+ bytes = u / 8;
+ extra = u % 8;
+ size = arg->size;
+ if (bytes > size || bytes + !!extra > size)
+ goto error;
+ if (!ctx->object)
+ return len;
+ buf = (uint8_t *)ctx->object + arg->offset;
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+ if (!arg->hton) {
+ memset((uint8_t *)buf + size - bytes, 0xff, bytes);
+ memset(buf, 0x00, size - bytes);
+ if (extra)
+ ((uint8_t *)buf)[size - bytes - 1] = conv[extra];
+ } else
+#endif
+ {
+ memset(buf, 0xff, bytes);
+ memset((uint8_t *)buf + bytes, 0x00, size - bytes);
+ if (extra)
+ ((uint8_t *)buf)[bytes] = conv[extra];
+ }
+ if (ctx->objmask)
+ memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size);
+ return len;
+error:
+ push_args(ctx, arg);
+ return -1;
+}
+
/** Default parsing function for token name matching. */
static int
parse_default(struct context *ctx, const struct token *token,
memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out));
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
return len;
}
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
out->args.vc.data = (uint8_t *)out + size;
return len;
}
ctx->objdata = 0;
ctx->object = &out->args.vc.attr;
+ ctx->objmask = NULL;
switch (ctx->curr) {
case GROUP:
case PRIORITY:
(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
sizeof(double));
ctx->object = out->args.vc.pattern;
+ ctx->objmask = NULL;
return len;
case ACTIONS:
out->args.vc.actions =
out->args.vc.pattern_n),
sizeof(double));
ctx->object = out->args.vc.actions;
+ ctx->objmask = NULL;
return len;
default:
if (!token->priv)
};
++out->args.vc.pattern_n;
ctx->object = item;
+ ctx->objmask = NULL;
} else {
const struct parse_action_priv *priv = token->priv;
struct rte_flow_action *action =
};
++out->args.vc.actions_n;
ctx->object = action;
+ ctx->objmask = NULL;
}
memset(data, 0, data_size);
out->args.vc.data = data;
return len;
}
+/** Parse pattern item parameter type. */
+static int
+parse_vc_spec(struct context *ctx, const struct token *token,
+ const char *str, unsigned int len,
+ void *buf, unsigned int size)
+{
+ struct buffer *out = buf;
+ struct rte_flow_item *item;
+ uint32_t data_size;
+ int index;
+ int objmask = 0;
+
+ (void)size;
+ /* Token name must match. */
+ if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+ return -1;
+ /* Parse parameter types. */
+ switch (ctx->curr) {
+ case ITEM_PARAM_IS:
+ index = 0;
+ objmask = 1;
+ break;
+ case ITEM_PARAM_SPEC:
+ index = 0;
+ break;
+ case ITEM_PARAM_LAST:
+ index = 1;
+ break;
+ case ITEM_PARAM_PREFIX:
+ /* Modify next token to expect a prefix. */
+ if (ctx->next_num < 2)
+ return -1;
+ ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX);
+ /* Fall through. */
+ case ITEM_PARAM_MASK:
+ index = 2;
+ break;
+ default:
+ return -1;
+ }
+ /* Nothing else to do if there is no buffer. */
+ if (!out)
+ return len;
+ if (!out->args.vc.pattern_n)
+ return -1;
+ item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+ data_size = ctx->objdata / 3; /* spec, last, mask */
+ /* Point to selected object. */
+ ctx->object = out->args.vc.data + (data_size * index);
+ if (objmask) {
+ ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */
+ item->mask = ctx->objmask;
+ } else
+ ctx->objmask = NULL;
+ /* Update relevant item pointer. */
+ *((const void **[]){ &item->spec, &item->last, &item->mask })[index] =
+ ctx->object;
+ return len;
+}
+
/** Parse tokens for destroy command. */
static int
parse_destroy(struct context *ctx, const struct token *token,
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
out->args.destroy.rule =
(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
sizeof(double));
return -1;
ctx->objdata = 0;
ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++;
+ ctx->objmask = NULL;
return len;
}
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
}
return len;
}
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
}
return len;
}
out->command = ctx->curr;
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
out->args.list.group =
(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
sizeof(double));
return -1;
ctx->objdata = 0;
ctx->object = out->args.list.group + out->args.list.group_n++;
+ ctx->objmask = NULL;
return len;
}
goto error;
if (!ctx->object)
return len;
+ if (arg->mask) {
+ if (!arg_entry_bf_fill(ctx->object, u, arg) ||
+ !arg_entry_bf_fill(ctx->objmask, -1, arg))
+ goto error;
+ return len;
+ }
buf = (uint8_t *)ctx->object + arg->offset;
size = arg->size;
+objmask:
switch (size) {
case sizeof(uint8_t):
*(uint8_t *)buf = u;
default:
goto error;
}
+ if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+ u = -1;
+ buf = (uint8_t *)ctx->objmask + arg->offset;
+ goto objmask;
+ }
return len;
error:
push_args(ctx, arg);
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,
else {
ctx->objdata = 0;
ctx->object = out;
+ ctx->objmask = NULL;
size = sizeof(*out);
}
ret = parse_int(ctx, token, str, len, out, size);
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,
ctx->port = 0;
ctx->objdata = 0;
ctx->object = NULL;
+ ctx->objmask = NULL;
}
/** Parse a token (cmdline API). */