[ovs-dev] [PATCH 16/17] ofp-actions: Support experimenter OXMs in Nicira extensions.

Ben Pfaff blp at nicira.com
Wed Sep 17 05:57:09 UTC 2014


Some of the Nicira extension actions include fixed-size 32-bit members that
designate NXM fields.  These actions can't accommodate 64-bit experimenter
OXMs, so we need to figure out some kind of solution.  This commit does
that, in different ways for different actions.

For some actions, I did not think it was worthwhile to worry about
experimenter OXM, so I just disabled use of them.  This is what I did for
bundle, learn, and multipath actions.

Other actions could be gracefully reinterpreted to support experimenter
OXM.  This is true of reg_move, which use NXM headers only at the end of
the action and such that using an experimenter OXM would make the action
longer (which unambigously signals to older OVS that the action is an
error, which is desired behavior since older OVS cannot interpret this
action).  The stack push and pop actions are also in this category.

reg_load was the most frustrating case.  In OpenFlow 1.5 we had already
eliminated this action in favor of OF1.5+ set_field.  In other OpenFlow
versions, though, reg_load is more powerful than set_field because it
can modify partial fields.  This commit therefore adds a new variant of
reg_load, called reg_load2, which is simply OF1.5+ set_field with a Nicira
extension header on it.

Signed-off-by: Ben Pfaff <blp at nicira.com>
---
 lib/bundle.c      |   5 ++
 lib/learn.c       |   8 ++
 lib/multipath.c   |   4 +
 lib/ofp-actions.c | 263 +++++++++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 236 insertions(+), 44 deletions(-)

diff --git a/lib/bundle.c b/lib/bundle.c
index a514ebb..793eb82 100644
--- a/lib/bundle.c
+++ b/lib/bundle.c
@@ -207,6 +207,11 @@ bundle_parse__(const char *s, char **save_ptr,
         if (error) {
             return error;
         }
+
+        if (!mf_nxm_header(bundle->dst.field->id)) {
+            return xasprintf("%s: experimenter OXM field '%s' not supported",
+                             s, dst);
+        }
     }
 
     return NULL;
diff --git a/lib/learn.c b/lib/learn.c
index c04e1f3..e93015c 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -221,6 +221,10 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
     if (error) {
         return error;
     }
+    if (!mf_nxm_header(dst.field->id)) {
+        return xasprintf("%s: experimenter OXM field '%s' not supported",
+                         full_s, s);
+    }
 
     if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
                               (8 * sizeof imm) - dst.n_bits)) {
@@ -269,6 +273,10 @@ learn_parse_spec(const char *orig, char *name, char *value,
         if (error) {
             return error;
         }
+        if (!mf_nxm_header(spec->dst.field->id)) {
+            return xasprintf("%s: experimenter OXM field '%s' not supported",
+                             orig, name);
+        }
 
         /* Parse source and check prerequisites. */
         if (value[0] != '\0') {
diff --git a/lib/multipath.c b/lib/multipath.c
index 8983c6a..edd295f 100644
--- a/lib/multipath.c
+++ b/lib/multipath.c
@@ -189,6 +189,10 @@ multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s)
     if (error) {
         return error;
     }
+    if (!mf_nxm_header(mp->dst.field->id)) {
+        return xasprintf("%s: experimenter OXM field '%s' not supported",
+                         s, dst);
+    }
     if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
         return xasprintf("%s: %d-bit destination field has %u possible "
                          "values, less than specified n_links %d",
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 95200af..7ce508f 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -210,10 +210,15 @@ enum ofp_raw_action_type {
      * [In OpenFlow 1.5, set_field is a superset of reg_load functionality, so
      * we drop reg_load.] */
     NXAST_RAW_REG_LOAD,
+    /* NX1.0-1.4(33): struct nx_action_reg_load2, ...
+     *
+     * [In OpenFlow 1.5, set_field is a superset of reg_load2 functionality, so
+     * we drop reg_load2.] */
+    NXAST_RAW_REG_LOAD2,
 
     /* OF1.5+(28): struct ofp15_action_copy_field, ... */
     OFPAT_RAW15_COPY_FIELD,
-    /* NX1.0-1.4(6): struct nx_action_reg_move. */
+    /* NX1.0-1.4(6): struct nx_action_reg_move, ... */
     NXAST_RAW_REG_MOVE,
 
 /* ## ------------------------- ## */
@@ -248,6 +253,8 @@ enum ofp_raw_action_type {
 
     /* NX1.0+(15): struct nx_action_output_reg. */
     NXAST_RAW_OUTPUT_REG,
+    /* NX1.0+(32): struct nx_action_output_reg2. */
+    NXAST_RAW_OUTPUT_REG2,
 
     /* NX1.0+(16): struct nx_action_learn, ... */
     NXAST_RAW_LEARN,
@@ -708,6 +715,27 @@ struct nx_action_output_reg {
 };
 OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 
+/* Action structure for NXAST_OUTPUT_REG2.
+ *
+ * Like the NXAST_OUTPUT_REG but organized so that there is room for a 64-bit
+ * experimenter OXM as 'src'.
+ */
+struct nx_action_output_reg2 {
+    ovs_be16 type;              /* OFPAT_VENDOR. */
+    ovs_be16 len;               /* 24. */
+    ovs_be32 vendor;            /* NX_VENDOR_ID. */
+    ovs_be16 subtype;           /* NXAST_OUTPUT_REG2. */
+
+    ovs_be16 ofs_nbits;         /* (ofs << 6) | (n_bits - 1). */
+    ovs_be16 max_len;           /* Max length to send to controller. */
+
+    /* Followed by:
+     * - 'src', as an OXM/NXM header (either 4 or 8 bytes).
+     * - Enough 0-bytes to pad the action out to 24 bytes. */
+    uint8_t pad[10];
+};
+OFP_ASSERT(sizeof(struct nx_action_output_reg2) == 24);
+
 static enum ofperr
 decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor,
                             struct ofpbuf *out)
@@ -719,6 +747,7 @@ decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor,
     }
 
     output_reg = ofpact_put_OUTPUT_REG(out);
+    output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG;
     output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
     output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
     output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
@@ -727,17 +756,61 @@ decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor,
     return mf_check_src(&output_reg->src, NULL);
 }
 
+static enum ofperr
+decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor,
+                            struct ofpbuf *out)
+{
+    struct ofpact_output_reg *output_reg;
+    enum ofperr error;
+    struct ofpbuf b;
+
+    output_reg = ofpact_put_OUTPUT_REG(out);
+    output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG2;
+    output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
+    output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
+    output_reg->max_len = ntohs(naor->max_len);
+
+    ofpbuf_use_const(&b, naor, ntohs(naor->len));
+    ofpbuf_pull(&b, OBJECT_OFFSETOF(naor, pad));
+    error = nx_pull_header(&b, &output_reg->src.field, NULL);
+    if (error) {
+        return error;
+    }
+    if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    return mf_check_src(&output_reg->src, NULL);
+}
+
 static void
 encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg,
                   enum ofp_version ofp_version OVS_UNUSED,
                   struct ofpbuf *out)
 {
-    struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out);
+    /* If 'output_reg' came in as an NXAST_RAW_OUTPUT_REG2 action, or if it
+     * cannot be encoded in the older form, encode it as
+     * NXAST_RAW_OUTPUT_REG2. */
+    if (output_reg->ofpact.raw == NXAST_RAW_OUTPUT_REG2
+        || !mf_nxm_header(output_reg->src.field->id)) {
+        struct nx_action_output_reg2 *naor = put_NXAST_OUTPUT_REG2(out);
+        size_t size = ofpbuf_size(out);
+
+        naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
+                                               output_reg->src.n_bits);
+        naor->max_len = htons(output_reg->max_len);
+
+        ofpbuf_set_size(out, size - sizeof naor->pad);
+        nx_put_header(out, output_reg->src.field->id, 0, false);
+        ofpbuf_set_size(out, size);
+    } else {
+        struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out);
 
-    naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
-                                           output_reg->src.n_bits);
-    naor->src = htonl(mf_nxm_header(output_reg->src.field->id));
-    naor->max_len = htons(output_reg->max_len);
+        naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
+                                               output_reg->src.n_bits);
+        naor->src = htonl(mf_nxm_header(output_reg->src.field->id));
+        naor->max_len = htons(output_reg->max_len);
+    }
 }
 
 static char * WARN_UNUSED_RESULT
@@ -1747,10 +1820,12 @@ struct nx_action_reg_move {
     ovs_be16 n_bits;                /* Number of bits. */
     ovs_be16 src_ofs;               /* Starting bit offset in source. */
     ovs_be16 dst_ofs;               /* Starting bit offset in destination. */
-    ovs_be32 src;                   /* Source register. */
-    ovs_be32 dst;                   /* Destination register. */
+    /* Followed by:
+     * - OXM/NXM header for source field (4 or 8 bytes).
+     * - OXM/NXM header for destination field (4 or 8 bytes).
+     * - Padding with 0-bytes to a multiple of 8 bytes, if necessary. */
 };
-OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24);
+OFP_ASSERT(sizeof(struct nx_action_reg_move) == 16);
 
 static enum ofperr
 decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf,
@@ -1794,15 +1869,29 @@ decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm,
                           struct ofpbuf *ofpacts)
 {
     struct ofpact_reg_move *move;
+    enum ofperr error;
+    struct ofpbuf b;
 
     move = ofpact_put_REG_MOVE(ofpacts);
-    move->src.field = mf_from_nxm_header(ntohl(narm->src));
     move->src.ofs = ntohs(narm->src_ofs);
     move->src.n_bits = ntohs(narm->n_bits);
-    move->dst.field = mf_from_nxm_header(ntohl(narm->dst));
     move->dst.ofs = ntohs(narm->dst_ofs);
     move->dst.n_bits = ntohs(narm->n_bits);
 
+    ofpbuf_use_const(&b, narm, ntohs(narm->len));
+    ofpbuf_pull(&b, sizeof *narm);
+    error = nx_pull_header(&b, &move->src.field, NULL);
+    if (error) {
+        return error;
+    }
+    error = nx_pull_header(&b, &move->dst.field, NULL);
+    if (error) {
+        return error;
+    }
+    if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
     return nxm_reg_move_check(move, NULL);
 }
 
@@ -1810,11 +1899,9 @@ static void
 encode_REG_MOVE(const struct ofpact_reg_move *move,
                 enum ofp_version ofp_version, struct ofpbuf *out)
 {
+    size_t start_ofs = ofpbuf_size(out);
     if (ofp_version >= OFP15_VERSION) {
-        struct ofp15_action_copy_field *copy;
-        size_t start_ofs = ofpbuf_size(out);
-
-        copy = put_OFPAT15_COPY_FIELD(out);
+        struct ofp15_action_copy_field *copy = put_OFPAT15_COPY_FIELD(out);
         copy->n_bits = htons(move->dst.n_bits);
         copy->src_offset = htons(move->src.ofs);
         copy->dst_offset = htons(move->dst.ofs);
@@ -1822,17 +1909,15 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
         ofpbuf_set_size(out, ofpbuf_size(out) - sizeof copy->pad);
         nx_put_header(out, move->src.field->id, ofp_version, false);
         nx_put_header(out, move->dst.field->id, ofp_version, false);
-        pad_ofpat(out, start_ofs);
     } else {
-        struct nx_action_reg_move *narm;
-
-        narm = put_NXAST_REG_MOVE(out);
+        struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out);
         narm->n_bits = htons(move->dst.n_bits);
         narm->src_ofs = htons(move->src.ofs);
         narm->dst_ofs = htons(move->dst.ofs);
-        narm->src = htonl(mf_nxm_header(move->src.field->id));
-        narm->dst = htonl(mf_nxm_header(move->dst.field->id));
+        nx_put_header(out, move->src.field->id, 0, false);
+        nx_put_header(out, move->dst.field->id, 0, false);
     }
+    pad_ofpat(out, start_ofs);
 }
 
 static char * WARN_UNUSED_RESULT
@@ -1921,6 +2006,26 @@ struct nx_action_reg_load {
 };
 OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
 
+/* Action structure for NXAST_REG_LOAD2.
+ *
+ * Compared to OFPAT_SET_FIELD, we can use this to set whole or partial fields
+ * in any OpenFlow version.  Compared to NXAST_REG_LOAD, we can use this to set
+ * OXM experimenter fields. */
+struct nx_action_reg_load2 {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* At least 16. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_SET_FIELD. */
+
+    /* Followed by:
+     * - An NXM/OXM header, value, and optionally a mask.
+     * - Enough 0-bytes to pad out to a multiple of 64 bits.
+     *
+     * The "pad" member is the beginning of the above. */
+    uint8_t pad[6];
+};
+OFP_ASSERT(sizeof(struct nx_action_reg_load2) == 16);
+
 static enum ofperr
 decode_ofpat_set_field(const struct ofp12_action_set_field *oasf,
                        bool may_mask, struct ofpbuf *ofpacts)
@@ -2027,6 +2132,35 @@ decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl,
     return 0;
 }
 
+static enum ofperr
+decode_NXAST_RAW_REG_LOAD2(const struct nx_action_reg_load2 *narl,
+                           struct ofpbuf *out)
+{
+    struct ofpact_set_field *sf;
+    enum ofperr error;
+    struct ofpbuf b;
+
+    sf = ofpact_put_SET_FIELD(out);
+    sf->ofpact.raw = NXAST_RAW_REG_LOAD2;
+
+    ofpbuf_use_const(&b, narl, ntohs(narl->len));
+    ofpbuf_pull(&b, OBJECT_OFFSETOF(narl, pad));
+    error = nx_pull_entry(&b, &sf->field, &sf->value, &sf->mask);
+    if (error) {
+        return error;
+    }
+    if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) {
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+    }
+
+    if (!sf->field->writable) {
+        VLOG_WARN_RL(&rl, "destination field %s is not writable",
+                     sf->field->name);
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+    }
+    return 0;
+}
+
 static void
 ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version,
                      enum mf_field_id field, uint64_t value_)
@@ -2068,15 +2202,30 @@ next_load_segment(const struct ofpact_set_field *sf,
 static void
 set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
 {
-    struct mf_subfield dst;
-    uint64_t value;
+    /* If 'sf' cannot be encoded as NXAST_REG_LOAD because it requires an
+     * experimenter OXM (or if it came in as NXAST_REG_LOAD2), encode as
+     * NXAST_REG_LOAD2.  Otherwise use NXAST_REG_LOAD, which is backward
+     * compatible. */
+    if (sf->ofpact.raw == NXAST_RAW_REG_LOAD2
+        || !mf_nxm_header(sf->field->id)) {
+        struct nx_action_reg_load2 *narl OVS_UNUSED;
+        size_t start_ofs = ofpbuf_size(openflow);
+
+        narl = put_NXAST_REG_LOAD2(openflow);
+        ofpbuf_set_size(openflow, ofpbuf_size(openflow) - sizeof narl->pad);
+        nx_put_entry(openflow, sf->field->id, 0, &sf->value, &sf->mask);
+        pad_ofpat(openflow, start_ofs);
+    } else {
+        struct mf_subfield dst;
+        uint64_t value;
 
-    dst.ofs = dst.n_bits = 0;
-    while (next_load_segment(sf, &dst, &value)) {
-        struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow);
-        narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
-        narl->dst = htonl(mf_nxm_header(dst.field->id));
-        narl->value = htonll(value);
+        dst.ofs = dst.n_bits = 0;
+        while (next_load_segment(sf, &dst, &value)) {
+            struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow);
+            narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
+            narl->dst = htonl(mf_nxm_header(dst.field->id));
+            narl->value = htonll(value);
+        }
     }
 }
 
@@ -2219,10 +2368,12 @@ encode_SET_FIELD(const struct ofpact_set_field *sf,
                  enum ofp_version ofp_version, struct ofpbuf *out)
 {
     if (ofp_version >= OFP15_VERSION) {
-        /* OF1.5+ only has Set-Field (we drop NXAST_REG_LOAD entirely). */
+        /* OF1.5+ only has Set-Field (reg_load is redundant so we drop it
+         * entirely). */
         set_field_to_set_field(sf, ofp_version, out);
-    } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD) {
-        /* It came in as NXAST_REG_LOAD, send it out the same way. */
+    } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD ||
+               sf->ofpact.raw == NXAST_RAW_REG_LOAD2) {
+        /* It came in as reg_load, send it out the same way. */
         set_field_to_nxast(sf, out);
     } else if (ofp_version < OFP12_VERSION) {
         /* OpenFlow 1.0 and 1.1 don't have Set-Field. */
@@ -2378,19 +2529,36 @@ struct nx_action_stack {
     ovs_be32 vendor;                /* NX_VENDOR_ID. */
     ovs_be16 subtype;               /* NXAST_STACK_PUSH or NXAST_STACK_POP. */
     ovs_be16 offset;                /* Bit offset into the field. */
-    ovs_be32 field;                 /* The field used for push or pop. */
-    ovs_be16 n_bits;                /* (n_bits + 1) bits of the field. */
-    uint8_t zero[6];                /* Reserved, must be zero. */
+    /* Followed by:
+     * - OXM/NXM header for field to push or pop (4 or 8 bytes).
+     * - ovs_be16 'n_bits', the number of bits to extract from the field.
+     * - Enough 0-bytes to pad out the action to 24 bytes. */
+    uint8_t pad[12];                /* See above. */
 };
 OFP_ASSERT(sizeof(struct nx_action_stack) == 24);
 
-static void
+static enum ofperr
 decode_stack_action(const struct nx_action_stack *nasp,
                     struct ofpact_stack *stack_action)
 {
-    stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field));
+    enum ofperr error;
+    struct ofpbuf b;
+
     stack_action->subfield.ofs = ntohs(nasp->offset);
-    stack_action->subfield.n_bits = ntohs(nasp->n_bits);
+
+    ofpbuf_use_const(&b, nasp, sizeof *nasp);
+    ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad));
+    error = nx_pull_header(&b, &stack_action->subfield.field, NULL);
+    if (error) {
+        return error;
+    }
+    stack_action->subfield.n_bits = ntohs(*(const ovs_be16 *) ofpbuf_data(&b));
+    ofpbuf_pull(&b, 2);
+    if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    return 0;
 }
 
 static enum ofperr
@@ -2398,8 +2566,8 @@ decode_NXAST_RAW_STACK_PUSH(const struct nx_action_stack *nasp,
                              struct ofpbuf *ofpacts)
 {
     struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts);
-    decode_stack_action(nasp, push);
-    return nxm_stack_push_check(push, NULL);
+    enum ofperr error = decode_stack_action(nasp, push);
+    return error ? error : nxm_stack_push_check(push, NULL);
 }
 
 static enum ofperr
@@ -2407,17 +2575,24 @@ decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp,
                            struct ofpbuf *ofpacts)
 {
     struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts);
-    decode_stack_action(nasp, pop);
-    return nxm_stack_pop_check(pop, NULL);
+    enum ofperr error = decode_stack_action(nasp, pop);
+    return error ? error : nxm_stack_pop_check(pop, NULL);
 }
 
 static void
 encode_STACK_op(const struct ofpact_stack *stack_action,
                 struct nx_action_stack *nasp)
 {
+    struct ofpbuf b;
+    ovs_be16 n_bits;
+
     nasp->offset = htons(stack_action->subfield.ofs);
-    nasp->n_bits = htons(stack_action->subfield.n_bits);
-    nasp->field = htonl(mf_nxm_header(stack_action->subfield.field->id));
+
+    ofpbuf_use_stack(&b, nasp, ntohs(nasp->len));
+    ofpbuf_put_uninit(&b, OBJECT_OFFSETOF(nasp, pad));
+    nx_put_header(&b, stack_action->subfield.field->id, 0, false);
+    n_bits = htons(stack_action->subfield.n_bits);
+    ofpbuf_put(&b, &n_bits, sizeof n_bits);
 }
 
 static void
-- 
1.9.1




More information about the dev mailing list