Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn').  Allow also leaving out the
trailing '[]' to indicate full field.  These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.

Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation.  For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:

... reg0[14..15]=2 ...

instead of

... reg0=0x8000/0xc000 ...

Note that only contiguous masks can be specified with the bracket
notation.

Signed-of-by: Jarno Rajahalme <ja...@ovn.org>
Signed-off-by: Jarno Rajahalme <ja...@ovn.org>
---
 lib/learn.c              | 145 +++++++++++++++++++++++++++++------------------
 lib/nx-match.c           |  47 +++++++++------
 lib/ofp-actions.c        |  52 ++++++-----------
 lib/ofp-parse.c          |  54 +++++++++++++++++-
 tests/learn.at           |   4 +-
 tests/ovs-ofctl.at       |  28 +++++++--
 utilities/ovs-ofctl.8.in |  40 ++++++++-----
 7 files changed, 236 insertions(+), 134 deletions(-)

diff --git a/lib/learn.c b/lib/learn.c
index 9cab759..ce52c35 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -187,24 +187,12 @@ learn_mask(const struct ofpact_learn *learn, struct 
flow_wildcards *wc)
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec,
+learn_parse_load_immediate(union mf_subvalue *imm, const char *s,
+                           const char *full_s, struct ofpact_learn_spec *spec,
                            struct ofpbuf *ofpacts)
 {
-    const char *full_s = s;
     struct mf_subfield dst;
-    union mf_subvalue imm;
     char *error;
-    int err;
-
-    err = parse_int_string(s, imm.u8, sizeof imm.u8, (char **) &s);
-    if (err) {
-        return xasprintf("%s: too many bits in immediate value", full_s);
-    }
-
-    if (strncmp(s, "->", 2)) {
-        return xasprintf("%s: missing `->' following value", full_s);
-    }
-    s += 2;
 
     error = mf_parse_subfield(&dst, s);
     if (error) {
@@ -215,8 +203,8 @@ learn_parse_load_immediate(const char *s, struct 
ofpact_learn_spec *spec,
                          full_s, s);
     }
 
-    if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
-                              (8 * sizeof imm) - dst.n_bits)) {
+    if (!bitwise_is_all_zeros(imm, sizeof *imm, dst.n_bits,
+                              (8 * sizeof *imm) - dst.n_bits)) {
         return xasprintf("%s: value does not fit into %u bits",
                          full_s, dst.n_bits);
     }
@@ -229,7 +217,7 @@ learn_parse_load_immediate(const char *s, struct 
ofpact_learn_spec *spec,
     /* Push value last, as this may reallocate 'spec'! */
     unsigned int n_bytes = DIV_ROUND_UP(dst.n_bits, 8);
     uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(n_bytes));
-    memcpy(src_imm, &imm.u8[sizeof imm.u8 - n_bytes], n_bytes);
+    memcpy(src_imm, &imm->u8[sizeof imm->u8 - n_bytes], n_bytes);
 
     return NULL;
 }
@@ -241,50 +229,78 @@ learn_parse_spec(const char *orig, char *name, char 
*value,
                  struct ofpact_learn_spec *spec,
                  struct ofpbuf *ofpacts, struct match *match)
 {
-    if (mf_from_name(name)) {
-        const struct mf_field *dst = mf_from_name(name);
-        union mf_value imm;
-        char *error;
-
-        error = mf_parse_value(dst, value, &imm);
-        if (error) {
-            return error;
-        }
+    /* Parse destination and check prerequisites. */
+    struct mf_subfield dst;
+    char *error;
 
-        spec->n_bits = dst->n_bits;
-        spec->src_type = NX_LEARN_SRC_IMMEDIATE;
-        spec->dst_type = NX_LEARN_DST_MATCH;
-        spec->dst.field = dst;
-        spec->dst.ofs = 0;
-        spec->dst.n_bits = dst->n_bits;
-
-        /* Update 'match' to allow for satisfying destination
-         * prerequisites. */
-        mf_set_value(dst, &imm, match, NULL);
-
-        /* Push value last, as this may reallocate 'spec'! */
-        uint8_t *src_imm = ofpbuf_put_zeros(ofpacts,
-                                            OFPACT_ALIGN(dst->n_bytes));
-        memcpy(src_imm, &imm, dst->n_bytes);
-    } else if (strchr(name, '[')) {
-        /* Parse destination and check prerequisites. */
-        char *error;
-
-        error = mf_parse_subfield(&spec->dst, name);
-        if (error) {
-            return error;
-        }
-        if (!mf_nxm_header(spec->dst.field->id)) {
+    error = mf_parse_subfield(&dst, name);
+    if (!error) {
+        if (!mf_nxm_header(dst.field->id)) {
             return xasprintf("%s: experimenter OXM field '%s' not supported",
                              orig, name);
         }
+        spec->dst = dst;
+        spec->n_bits = dst.n_bits;
+        spec->dst_type = NX_LEARN_DST_MATCH;
 
         /* Parse source and check prerequisites. */
         if (value[0] != '\0') {
-            error = mf_parse_subfield(&spec->src, value);
+            struct mf_subfield src;
+            error = mf_parse_subfield(&src, value);
             if (error) {
-                return error;
+                union mf_value imm;
+                char *imm_error = NULL;
+
+                /* Try an immediate value. */
+                if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) {
+                    /* Full field value. */
+                    imm_error = mf_parse_value(dst.field, value, &imm);
+                } else {
+                    char *tail;
+                    /* Partial field value. */
+                    if (parse_int_string(value, (uint8_t *)&imm,
+                                          dst.field->n_bytes, &tail)
+                        || *tail != 0) {
+                        imm_error = xasprintf("%s: cannot parse integer 
value", orig);
+                    }
+
+                    if (!imm_error &&
+                        !bitwise_is_all_zeros(&imm, dst.field->n_bytes,
+                                              dst.n_bits,
+                                              dst.field->n_bytes * 8 - 
dst.n_bits)) {
+                        struct ds ds;
+
+                        ds_init(&ds);
+                        mf_format(dst.field, &imm, NULL, &ds);
+                        imm_error = xasprintf("%s: value %s does not fit into 
%d bits",
+                                              orig, ds_cstr(&ds), dst.n_bits);
+                        ds_destroy(&ds);
+                    }
+                }
+                if (imm_error) {
+                    char *err = xasprintf("%s: %s value %s cannot be parsed as 
a subfield (%s) or an immediate value (%s)",
+                                          orig, name, value, error, imm_error);
+                    free(error);
+                    free(imm_error);
+                    return err;
+                }
+
+                spec->src_type = NX_LEARN_SRC_IMMEDIATE;
+
+                /* Update 'match' to allow for satisfying destination
+                 * prerequisites. */
+                mf_write_subfield_value(&dst, &imm, match);
+
+                /* Push value last, as this may reallocate 'spec'! */
+                unsigned int imm_bytes = DIV_ROUND_UP(dst.n_bits, 8);
+                uint8_t *src_imm = ofpbuf_put_zeros(ofpacts,
+                                                    OFPACT_ALIGN(imm_bytes));
+                memcpy(src_imm, &imm, imm_bytes);
+
+                free(error);
+                return NULL;
             }
+            spec->src = src;
             if (spec->src.n_bits != spec->dst.n_bits) {
                 return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
                                  "differ", orig, name, spec->src.n_bits, value,
@@ -294,12 +310,29 @@ learn_parse_spec(const char *orig, char *name, char 
*value,
             spec->src = spec->dst;
         }
 
-        spec->n_bits = spec->src.n_bits;
         spec->src_type = NX_LEARN_SRC_FIELD;
-        spec->dst_type = NX_LEARN_DST_MATCH;
     } else if (!strcmp(name, "load")) {
-        if (value[strcspn(value, "[-")] == '-') {
-            char *error = learn_parse_load_immediate(value, spec, ofpacts);
+        union mf_subvalue imm;
+        char *tail;
+        char *dst_value = strstr(value, "->");
+
+        if (dst_value == value) {
+            return xasprintf("%s: missing source before `->' in `%s'", name,
+                             value);
+        }
+        if (!dst_value) {
+            return xasprintf("%s: missing `->' in `%s'", name, value);
+        }
+
+        if (!parse_int_string(value, imm.u8, sizeof imm.u8, (char **) &tail)
+            && tail != value) {
+            if (tail != dst_value) {
+                return xasprintf("%s: garbage before `->' in `%s'",
+                                 name, value);
+            }
+
+            char *error = learn_parse_load_immediate(&imm, dst_value + 2, 
value, spec,
+                                                     ofpacts);
             if (error) {
                 return error;
             }
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 9201aae..9c769c0 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1812,7 +1812,7 @@ mf_parse_subfield_name(const char *name, int name_len, 
bool *wild)
 char * OVS_WARN_UNUSED_RESULT
 mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
 {
-    const struct mf_field *field;
+    const struct mf_field *field = NULL;
     const struct nxm_field *f;
     const char *name;
     int start, end;
@@ -1822,30 +1822,41 @@ mf_parse_subfield__(struct mf_subfield *sf, const char 
**sp)
 
     s = *sp;
     name = s;
-    name_len = strcspn(s, "[");
-    if (s[name_len] != '[') {
-        return xasprintf("%s: missing [ looking for field name", *sp);
-    }
+    name_len = strcspn(s, "[-");
 
     f = mf_parse_subfield_name(name, name_len, &wild);
-    if (!f) {
+    if (f) {
+        field = mf_from_id(f->id);
+    } else if (name_len < 256) {
+        char name_buf[256];
+        if (s[name_len] != '\0') {
+            memcpy(name_buf, name, name_len);
+            name_buf[name_len] = '\0';
+            name = name_buf;
+        }
+        field = mf_from_name(name);
+    }
+    if (!field) {
         return xasprintf("%s: unknown field `%.*s'", *sp, name_len, s);
     }
-    field = mf_from_id(f->id);
 
     s += name_len;
-    if (ovs_scan(s, "[%d..%d]", &start, &end)) {
-        /* Nothing to do. */
-    } else if (ovs_scan(s, "[%d]", &start)) {
-        end = start;
-    } else if (!strncmp(s, "[]", 2)) {
-        start = 0;
-        end = field->n_bits - 1;
-    } else {
-        return xasprintf("%s: syntax error expecting [] or [<bit>] or "
-                         "[<start>..<end>]", *sp);
+    /* Assume full field. */
+    start = 0;
+    end = field->n_bits - 1;
+    if (*s == '[') {
+        if (!strncmp(s, "[]", 2)) {
+            /* Nothing to do. */
+        } else if (ovs_scan(s, "[%d..%d]", &start, &end)) {
+            /* Nothing to do. */
+        } else if (ovs_scan(s, "[%d]", &start)) {
+            end = start;
+        } else {
+            return xasprintf("%s: syntax error expecting [] or [<bit>] or "
+                             "[<start>..<end>]", *sp);
+        }
+        s = strchr(s, ']') + 1;
     }
-    s = strchr(s, ']') + 1;
 
     if (start > end) {
         return xasprintf("%s: starting bit %d is after ending bit %d",
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 7507558..2ff9abf 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -584,25 +584,30 @@ static char * OVS_WARN_UNUSED_RESULT
 parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
-    if (strchr(arg, '[')) {
-        struct ofpact_output_reg *output_reg;
-
-        output_reg = ofpact_put_OUTPUT_REG(ofpacts);
-        output_reg->max_len = UINT16_MAX;
-        return mf_parse_subfield(&output_reg->src, arg);
-    } else if (strstr(arg, "port") && strstr(arg, "max_len")) {
+    if (strstr(arg, "port") && strstr(arg, "max_len")) {
         struct ofpact_output_trunc *output_trunc;
 
         output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
         return parse_truncate_subfield(output_trunc, arg);
     } else {
-        struct ofpact_output *output;
+        struct mf_subfield src;
+        char *error = mf_parse_subfield(&src, arg);
+        if (!error) {
+            struct ofpact_output_reg *output_reg;
 
-        output = ofpact_put_OUTPUT(ofpacts);
-        if (!ofputil_port_from_string(arg, &output->port)) {
-            return xasprintf("%s: output to unknown port", arg);
+            output_reg = ofpact_put_OUTPUT_REG(ofpacts);
+            output_reg->max_len = UINT16_MAX;
+            output_reg->src = src;
+        } else {
+            free(error);
+            struct ofpact_output *output;
+
+            output = ofpact_put_OUTPUT(ofpacts);
+            if (!ofputil_port_from_string(arg, &output->port)) {
+                return xasprintf("%s: output to unknown port", arg);
+            }
+            output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
         }
-        output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
         return NULL;
     }
 }
@@ -2360,28 +2365,7 @@ parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts,
                enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
-    const char *full_arg = arg;
-    char *error;
-
-    error = mf_parse_subfield__(&move->src, &arg);
-    if (error) {
-        return error;
-    }
-    if (strncmp(arg, "->", 2)) {
-        return xasprintf("%s: missing `->' following source", full_arg);
-    }
-    arg += 2;
-    error = mf_parse_subfield(&move->dst, arg);
-    if (error) {
-        return error;
-    }
-
-    if (move->src.n_bits != move->dst.n_bits) {
-        return xasprintf("%s: source field is %d bits wide but destination is "
-                         "%d bits wide", full_arg,
-                         move->src.n_bits, move->dst.n_bits);
-    }
-    return NULL;
+    return nxm_parse_reg_move(move, arg);
 }
 
 static void
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 553991c..af19fae 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -250,6 +250,52 @@ parse_field(const struct mf_field *mf, const char *s, 
struct match *match,
     return error;
 }
 
+/* Parses 'str_value' as the value of subfield 'name', and updates
+ * 'match' appropriately.  Restricts the set of usable protocols to ones
+ * supporting the parsed field.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_subfield(const char *name, const char *str_value, struct match *match,
+               enum ofputil_protocol *usable_protocols)
+{
+    struct mf_subfield sf;
+    char *error;
+
+    error = mf_parse_subfield(&sf, name);
+    if (!error) {
+        union mf_value val;
+        char *tail;
+        if (parse_int_string(str_value, (uint8_t *)&val, sf.field->n_bytes,
+                             &tail) || *tail != 0) {
+            return xasprintf("%s: cannot parse integer value: %s", name,
+                             str_value);
+        }
+        if (!bitwise_is_all_zeros(&val, sf.field->n_bytes, sf.n_bits,
+                                  sf.field->n_bytes * 8 - sf.n_bits)) {
+            struct ds ds;
+
+            ds_init(&ds);
+            mf_format(sf.field, &val, NULL, &ds);
+            error = xasprintf("%s: value %s does not fit into %d bits",
+                              name, ds_cstr(&ds), sf.n_bits);
+            ds_destroy(&ds);
+            return error;
+        }
+
+        const struct mf_field *field = sf.field;
+        union mf_value value, mask;
+        unsigned int size = DIV_ROUND_UP(sf.n_bits, 8);
+
+        mf_get(field, match, &value, &mask);
+        bitwise_copy(&val, size, 0, &value, field->n_bytes, sf.ofs, sf.n_bits);
+        bitwise_one (               &mask,  field->n_bytes, sf.ofs, sf.n_bits);
+        *usable_protocols &= mf_set(field, &value, &mask, match, &error);
+    }
+    return error;
+}
+
 static char *
 extract_actions(char *s)
 {
@@ -356,6 +402,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, 
char *string,
 
     while (ofputil_parse_key_value(&string, &name, &value)) {
         const struct protocol *p;
+        const struct mf_field *mf;
         char *error = NULL;
 
         if (parse_protocol(name, &p)) {
@@ -379,9 +426,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, 
char *string,
         } else if (!strcmp(name, "no_readonly_table")
                    || !strcmp(name, "allow_hidden_fields")) {
              /* ignore these fields. */
-        } else if (mf_from_name(name)) {
-            error = parse_field(mf_from_name(name), value, &fm->match,
-                                usable_protocols);
+        } else if ((mf = mf_from_name(name)) != NULL) {
+            error = parse_field(mf, value, &fm->match, usable_protocols);
+        } else if (strchr(name, '[')) {
+            error = parse_subfield(name, value, &fm->match, usable_protocols);
         } else {
             if (!*value) {
                 return xasprintf("field %s missing value", name);
diff --git a/tests/learn.at b/tests/learn.at
index 3e72dff..3f6fb5a 100644
--- a/tests/learn.at
+++ b/tests/learn.at
@@ -7,7 +7,7 @@ actions=learn(send_flow_rem)
 actions=learn(delete_learned)
 actions=learn(send_flow_rem,delete_learned)
 actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], 
output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10])
-actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, 
fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, 
in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
+actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, 
fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, 
in_port=99,eth_dst=eth_src,load:in_port->reg1[16..31])
 ]])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
 [[usable protocols: any
@@ -26,7 +26,7 @@ AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(table=1, 
in_port_oxm=123456)']],
   [1], [], [stderr])
 AT_CHECK([sed -e 's/.*|ofp_util|WARN|//' < stderr], [0],
   [[port 123456 is outside the supported range 0 through ffff or 0xffffff00 
through 0xffffffff
-ovs-ofctl: 123456: port value out of range for in_port_oxm
+ovs-ofctl: table=1, in_port_oxm=123456: in_port_oxm value 123456 cannot be 
parsed as a subfield (123456: unknown field `123456') or an immediate value 
(123456: port value out of range for in_port_oxm)
 ]], [[]])
 AT_CLEANUP
 
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index f2ee970..8c74bdb 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -79,6 +79,9 @@ for test_case in \
     'xxreg2=2/1                                  NXM,OXM' \
     'xxreg3=3                                    NXM,OXM' \
     'xxreg3=3/1                                  NXM,OXM' \
+    'xxreg3[[0..0]]=1                            NXM,OXM' \
+    'xxreg3[[126..127]]=3                        NXM,OXM' \
+    'reg3[[]]=3                                  NXM,OXM' \
     'dl_src=00:11:22:33:44:55                    any' \
     'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM,OpenFlow11' \
     'dl_dst=00:11:22:33:44:55                    any' \
@@ -147,6 +150,7 @@ for test_case in \
     'udp,udp_dst=0x1000/0x1000                   NXM,OXM' \
     'udp6,udp_dst=80                             NXM,OXM' \
     'udp6,udp_dst=0x1000/0x1000                  NXM,OXM' \
+    'udp6,udp_dst[[12]]=1                        NXM,OXM' \
     'icmp,icmp_type=1                            any' \
     'icmp,icmp_code=2                            any' \
     'icmp6,icmpv6_type=1                         NXM,OXM' \
@@ -395,7 +399,7 @@ cookie=0x123456789abcdef hard_timeout=10 priority=60000 
actions=controller
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 tcp,tp_src=0x1230/0xfff0,tun_id=0x1234,cookie=0x5678,actions=flood
 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
-actions=multipath(eth_src, 50, hrw, 12, 0, 
NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, 
NXM_NX_REG0[0..12])
+actions=multipath(eth_src, 50, hrw, 12, 0, 
NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, 
reg0[0..12])
 table=1,actions=drop
 tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
 metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop
@@ -403,13 +407,13 @@ actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
 actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2
-actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1)
+actions=bundle_load(eth_src,50,active_backup,ofport,reg0,slaves:1)
 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
-actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:[2,3])
+actions=bundle_load(symmetric_l4,60,hrw,ofport,reg0[0..15],slaves:[2,3])
 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
 
actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
-send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
+send_flow_rem,actions=output:1,output:NXM_NX_REG0,output:2,output:reg1[16..31],output:3
 check_overlap,actions=output:1,exit,output:2
 tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 actions=controller(max_len=123,reason=invalid_ttl,id=555)
@@ -419,6 +423,9 @@ ip,actions=ct(commit,zone=5)
 ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[])))
 ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[])))
 ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->NXM_NX_CT_LABEL[32..95])))
+ip,actions=ct(commit,exec(load(1->ct_mark)))
+ip,actions=ct(commit,exec(load(0x1->ct_label[])))
+ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->ct_label[32..95])))
 ip,actions=ct(commit,exec(set_field(0x1->ct_label)))
 ip,ct_state=+trk,ct_label=0x1234567890abcdef12345678,actions=ct(commit)
 actions=output(max_len=100,port=123)
@@ -466,6 +473,9 @@ NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5)
 NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
 NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
 NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95]))
+NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[]))
+NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
+NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95]))
 NXT_FLOW_MOD: ADD table:255 ip 
actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127]))
 NXT_FLOW_MOD: ADD table:255 
ct_state=+trk,ct_label=0x1234567890abcdef12345678,ip actions=ct(commit)
 NXT_FLOW_MOD: ADD table:255 actions=output(port=123,max_len=100)
@@ -579,9 +589,12 @@ cookie=0x123456789abcdef hard_timeout=10 priority=60000 
actions=controller
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 tun_id=0x1234,cookie=0x5678,actions=flood
 actions=drop
-reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci
 actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
-actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[]
+actions=push:reg0[0..31],pop:NXM_NX_REG0[]
+reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci
+actions=move:eth_dst->eth_src[]
+actions=push:reg0[0..31],pop:reg0
 vlan_tci=0x1123/0x1fff,actions=drop
 
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
@@ -619,6 +632,9 @@ NXT_FLOW_MOD: ADD <any> actions=drop
 NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) 
actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
 NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
 NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
+NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) 
actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
+NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
+NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
 NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
 NXT_FLOW_MOD: ADD <any> 
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 NXT_FLOW_MOD: ADD <any> 
actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789)
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 96135ea..e52a3cf 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1545,7 +1545,9 @@ is the packet's input port, the packet is not output.
 .
 .IP \fBoutput:\fIsrc\fB[\fIstart\fB..\fIend\fB]
 Outputs the packet to the OpenFlow port number read from \fIsrc\fR,
-which must be an NXM field as described above.  For example,
+which must be an NXM field as described above.  Also the match field
+name can be used, for example, instead of 'NXM_NX_REG0' the
+name 'reg0' can be used.  For example,
 \fBoutput:NXM_NX_REG0[16..31]\fR outputs to the OpenFlow port number
 written in the upper half of register 0.  If the port number is the
 packet's input port, the packet is not output.
@@ -2009,16 +2011,18 @@ bytes with value 0 to make the total number 6 more than 
a multiple of
 Copies the named bits from field \fIsrc\fR to field \fIdst\fR.
 \fIsrc\fR and \fIdst\fR must be NXM field names as defined in
 \fBnicira\-ext.h\fR, e.g. \fBNXM_OF_UDP_SRC\fR or \fBNXM_NX_REG0\fR.
-Each \fIstart\fR and \fIend\fR pair, which are inclusive, must specify
-the same number of bits and must fit within its respective field.
+Also the corresponding match field names can be used, for example,
+instead of 'NXM_NX_REG0' the name 'reg0' can be used.  Each
+\fIstart\fR and \fIend\fR pair, which are inclusive, must specify the
+same number of bits and must fit within its respective field.
 Shorthands for \fB[\fIstart\fB..\fIend\fB]\fR exist: use
 \fB[\fIbit\fB]\fR to specify a single bit or \fB[]\fR to specify an
-entire field.
+entire field (in the latter case the brackets can also be left off).
 .IP
 Examples: \fBmove:NXM_NX_REG0[0..5]\->NXM_NX_REG1[26..31]\fR copies the
 six bits numbered 0 through 5, inclusive, in register 0 into bits 26
 through 31, inclusive;
-\fBmove:NXM_NX_REG0[0..15]\->NXM_OF_VLAN_TCI[]\fR copies the least
+\fBmove:reg0[0..15]\->vlan_tci\fR copies the least
 significant 16 bits of register 0 into the VLAN TCI field.
 .IP
 In OpenFlow 1.0 through 1.4, \fBmove\fR ordinarily uses an Open
@@ -2037,9 +2041,9 @@ the customary syntax for field \fIdst\fR, which is 
expressed as a
 field name.  For example, \fBset_field:00:11:22:33:44:55->eth_src\fR
 sets the Ethernet source address to 00:11:22:33:44:55.  With
 \fBload\fR, \fIvalue\fR must be an integer value (in decimal or
-prefixed by \fB0x\fR for hexadecimal) and \fIdst\fR is the NXM or OXM
-name for the field.  For example,
-\fBload:0x001122334455->OXM_OF_ETH_DST[]\fR has the same effect as the
+prefixed by \fB0x\fR for hexadecimal) and \fIdst\fR can also be the
+NXM or OXM name for the field.  For example,
+\fBload:0x001122334455->OXM_OF_ETH_SRC[]\fR has the same effect as the
 prior \fBset_field\fR example.
 .IP
 The two forms exist for historical reasons.  Open vSwitch 1.1
@@ -2059,8 +2063,9 @@ subfield, \fBOFPAT_SET_FIELD\fR otherwise; and OpenFlow 
1.5 and later,
 Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields
 on top of the stack.
 .IP
-Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register
-2 bits 0 through 5, inclusive, on to the internal stack.
+Example: \fBpush:NXM_NX_REG2[0..5]\fR or \fBpush:reg2[0..5]\fR push
+the value stored in register 2 bits 0 through 5, inclusive, on to the
+internal stack.
 .
 .IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]"
 Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits
@@ -2068,9 +2073,9 @@ inclusive, from the value popped and store them into the 
corresponding
 bits in \fIdst\fR.
 .
 .IP
-Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack.
-Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the
-value just popped.
+Example: \fBpop:NXM_NX_REG2[0..5]\fR or \fBpop:reg2[0..5]\fR pops the
+value from top of the stack.  Set register 2 bits 0 through 5,
+inclusive, based on bits 0 through 5 from the value just popped.
 .
 .
 .IP "\fBmultipath(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIn_links\fB, 
\fIarg\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR"
@@ -2137,7 +2142,10 @@ above.
 Example: \fBbundle_load(eth_src, 0, hrw, ofport, NXM_NX_REG0[],
 slaves:4, 8)\fR uses an Ethernet source hash with basis 0, to select
 between OpenFlow ports 4 and 8 using the Highest Random Weight
-algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR.
+algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR.  Also the
+match field name can be used, for example, instead of 'NXM_NX_REG0'
+the name 'reg0' can be used.  When the while field is indicated the
+empty brackets can also be left off.
 .IP
 Refer to \fBnicira\-ext.h\fR for more details.
 .
@@ -3280,7 +3288,9 @@ Implements a level 2 MAC learning switch using the learn.
 \fBovs\-ofctl add\-flow br0 'table=0,priority=0 
actions=load:3->NXM_NX_REG0[0..15],learn(table=0,priority=1,idle_timeout=10,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2\fR
 In this use of a learn action, the first packet from each source MAC
 will be sent to port 2. Subsequent packets will be output to port 3,
-with an idle timeout of 10 seconds.
+with an idle timeout of 10 seconds.  Also the match field names can be
+used, for example, instead of 'NXM_NX_REG0' the name 'reg0' can be
+used.  Empty brackets can also be left off.
 .
 Additional examples may be found documented as part of related sections.
 .
-- 
2.1.4

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to