app/testpmd: support flow bit-field
[dpdk.git] / app / test-pmd / cmdline_flow.c
index 7bc1aa7..fc4d824 100644 (file)
@@ -56,6 +56,7 @@ enum index {
        /* Common tokens. */
        INTEGER,
        UNSIGNED,
+       PREFIX,
        RULE_ID,
        PORT_ID,
        GROUP_ID,
@@ -93,6 +94,7 @@ enum index {
        ITEM_PARAM_SPEC,
        ITEM_PARAM_LAST,
        ITEM_PARAM_MASK,
+       ITEM_PARAM_PREFIX,
        ITEM_NEXT,
        ITEM_END,
        ITEM_VOID,
@@ -134,6 +136,7 @@ struct arg {
        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. */
@@ -193,6 +196,13 @@ struct token {
                .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){ \
@@ -278,6 +288,7 @@ static const enum index item_param[] = {
        ITEM_PARAM_SPEC,
        ITEM_PARAM_LAST,
        ITEM_PARAM_MASK,
+       ITEM_PARAM_PREFIX,
        ZERO,
 };
 
@@ -321,6 +332,9 @@ static int parse_list(struct context *, const struct token *,
 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_port(struct context *, const struct token *,
                      const char *, unsigned int,
                      void *, unsigned int);
@@ -361,6 +375,13 @@ static const struct token token_list[] = {
                .call = parse_int,
                .comp = comp_none,
        },
+       [PREFIX] = {
+               .name = "{prefix}",
+               .type = "PREFIX",
+               .help = "prefix length for bit-mask",
+               .call = parse_prefix,
+               .comp = comp_none,
+       },
        [RULE_ID] = {
                .name = "{rule id}",
                .type = "RULE ID",
@@ -528,6 +549,11 @@ static const struct token token_list[] = {
                .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",
@@ -605,6 +631,118 @@ push_args(struct context *ctx, const struct arg *arg)
        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,
@@ -776,6 +914,12 @@ parse_vc_spec(struct context *ctx, const struct token *token,
        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;
@@ -992,6 +1136,12 @@ parse_int(struct context *ctx, const struct token *token,
                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: