Acked-by: Jarno Rajahalme <jrajaha...@nicira.com>

On Sep 30, 2014, at 5:47 PM, Ben Pfaff <b...@nicira.com> wrote:

> OpenFlow 1.2+ defines a means for vendors to define vendor-specific OXM
> fields, called "experimenter OXM".  These OXM fields are expressed with a
> 64-bit OXM header instead of the 32-bit header used for standard OXM (and
> NXM).  Until now, OVS has not implemented experimenter OXM, and indeed we
> have had little need to do so because of a pair of special 32-bit OXM classes
> grandfathered to OVS as part of the OpenFlow 1.2 standardization process.
> 
> However, I want to prototype a feature for OpenFlow 1.5 that uses an
> experimenter OXM as part of the prototype, so to do this OVS needs to
> support experimenter OXM.  This commit adds that support.
> 
> Most of this commit is a fairly straightforward change: it extends the type
> used for OXM/NXM from 32 to 64 bits and adds code to encode and decode the
> longer headers when necessary.  Some other changes are necessary because
> experimenter OXMs have a funny idea of the division between "header" and
> "body": the extra 32 bits for experimenter OXMs are counted as part of the 
> body
> rather than the header according to the OpenFlow standard (even though this
> does not entirely make sense), so arithmetic in various places has to be
> adjusted, which is the reason for the new functions nxm_experimenter_len(),
> nxm_payload_len(), and nxm_header_len().
> 
> Another change that calls for explanation is the new function mf_nxm_header()
> that has been split from mf_oxm_header().  This function is used in actions
> where the space for an NXM or OXM header is fixed so that there is no room
> for a 64-bit experimenter type.  An upcoming commit will add new variations
> of these actions that can support experimenter OXM.
> 
> Testing experimenter OXM is tricky because I do not know of any in
> widespread use.  Two ONF proposals use experimenter OXMs: EXT-256 and
> EXT-233.  EXT-256 is not suitable to implement for testing because its use
> of experimenter OXM is wrong and will be changed.  EXT-233 is not suitable
> to implement for testing because it requires adding a new field to struct
> flow and I am not yet convinced that that field and the feature that it
> supports is worth having in Open vSwitch.  Thus, this commit assigns an
> experimenter OXM code point to an existing OVS field that is currently
> restricted from use by controllers, "dp_hash", and uses that for testing.
> Because controllers cannot use it, this leaves future versions of OVS free
> to drop the support for the experimenter OXM for this field without causing
> backward compatibility problems.
> 
> Signed-off-by: Ben Pfaff <b...@nicira.com>
> ---
> v1->v2: Fix handling of unknown fields when parsing flows loosely. This
>  caused unit test failures that somehow I missed in v1.
> ---
> build-aux/extract-ofp-fields |   35 ++++--
> lib/meta-flow.h              |    8 +-
> lib/nx-match.c               |  246 ++++++++++++++++++++++++++++++++----------
> lib/nx-match.h               |    2 +-
> lib/ofp-actions.c            |   22 ++--
> tests/ofp-print.at           |   14 +++
> tests/ovs-ofctl.at           |   90 +++++++++++++++-
> utilities/ovs-ofctl.c        |    5 +
> 8 files changed, 339 insertions(+), 83 deletions(-)
> 
> diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
> index a82606b..88eb6d2 100755
> --- a/build-aux/extract-ofp-fields
> +++ b/build-aux/extract-ofp-fields
> @@ -48,12 +48,24 @@ PREREQS = {"none": "MFP_NONE",
>           "ND solicit": "MFP_ND_SOLICIT",
>           "ND advert": "MFP_ND_ADVERT"}
> 
> -# Maps a name prefix to an oxm_class.
> +# Maps a name prefix into an (experimenter ID, class) pair, so:
> +#
> +#      - Standard OXM classes are written as (0, <oxm_class>)
> +#
> +#      - Experimenter OXM classes are written as (<oxm_vender>, 0xffff)
> +#
> # If a name matches more than one prefix, the longest one is used.
> -OXM_CLASSES = {"NXM_OF_": 0x0000,
> -               "NXM_NX_": 0x0001,
> -               "OXM_OF_": 0x8000,
> -               "OXM_OF_PKT_REG": 0x8001}
> +OXM_CLASSES = {"NXM_OF_":        (0,          0x0000),
> +               "NXM_NX_":        (0,          0x0001),
> +               "OXM_OF_":        (0,          0x8000),
> +               "OXM_OF_PKT_REG": (0,          0x8001),
> +
> +               # This is the experimenter OXM class for Nicira, which is the
> +               # one that OVS would be using instead of NXM_OF_ and NXM_NX_
> +               # if OVS didn't have those grandfathered in.  It is currently
> +               # used only to test support for experimenter OXM, since there
> +               # are barely any real uses of experimenter OXM in the wild.
> +               "NXOXM_ET_":      (0x00002320, 0xffff)}
> def oxm_name_to_class(name):
>     prefix = ''
>     class_ = None
> @@ -118,12 +130,21 @@ def parse_oxm(s, prefix, n_bytes):
>     if not m:
>         fatal("%s: syntax error parsing %s" % (s, prefix))
> 
> -    name, code, of_version, ovs_version = m.groups()
> +    name, oxm_type, of_version, ovs_version = m.groups()
> 
>     class_ = oxm_name_to_class(name)
>     if class_ is None:
>         fatal("unknown OXM class for %s" % name)
> -    header = ("NXM_HEADER(0x%04x,%s,0,%d)" % (class_, code, n_bytes))
> +    oxm_vendor, oxm_class = class_
> +
> +    # Normally the oxm_length is the size of the field, but for experimenter
> +    # OXMs oxm_length also includes the 4-byte experimenter ID.
> +    oxm_length = n_bytes
> +    if oxm_class == 0xffff:
> +        oxm_length += 4
> +
> +    header = ("NXM_HEADER(0x%x,0x%x,0,%s,%d)"
> +              % (oxm_vendor, oxm_class, oxm_type, oxm_length))
> 
>     if of_version:
>         if of_version not in VERSION:
> diff --git a/lib/meta-flow.h b/lib/meta-flow.h
> index 0195ea3..dc68826 100644
> --- a/lib/meta-flow.h
> +++ b/lib/meta-flow.h
> @@ -263,13 +263,19 @@ enum OVS_PACKED_ENUM mf_field_id {
>      * Flow hash computed in the datapath.  Internal use only, not 
> programmable
>      * from controller.
>      *
> +     * The OXM code point for this is an attempt to test OXM experimenter
> +     * support, which is otherwise difficult to test due to the dearth of use
> +     * out in the wild.  Because controllers can't add flows that match on
> +     * dp_hash, this doesn't commit OVS to supporting this OXM experimenter
> +     * code point in the future.
> +     *
>      * Type: be32.
>      * Maskable: bitwise.
>      * Formatting: hexadecimal.
>      * Prerequisites: none.
>      * Access: read-only.
>      * NXM: NXM_NX_DP_HASH(35) since v2.2.
> -     * OXM: none.
> +     * OXM: NXOXM_ET_DP_HASH(0) since OF1.5 and v2.4.
>      */
>     MFF_DP_HASH,
> 
> diff --git a/lib/nx-match.c b/lib/nx-match.c
> index 63f06dd..93bb866 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -37,6 +37,46 @@
> 
> VLOG_DEFINE_THIS_MODULE(nx_match);
> 
> +/* OXM headers.
> + *
> + *
> + * Standard OXM/NXM
> + * ================
> + *
> + * The header is 32 bits long.  It looks like this:
> + *
> + * |31                              16 15            9| 8 7                0
> + * +----------------------------------+---------------+--+------------------+
> + * |            oxm_class             |   oxm_field   |hm|    oxm_length    |
> + * +----------------------------------+---------------+--+------------------+
> + *
> + * where hm stands for oxm_hasmask.  It is followed by oxm_length bytes of
> + * payload.  When oxm_hasmask is 0, the payload is the value of the field
> + * identified by the header; when oxm_hasmask is 1, the payload is a value 
> for
> + * the field followed by a mask of equal length.
> + *
> + * Internally, we represent a standard OXM header as a 64-bit integer with 
> the
> + * above information in the most-significant bits.
> + *
> + *
> + * Experimenter OXM
> + * ================
> + *
> + * The header is 64 bits long.  It looks like the diagram above except that a
> + * 32-bit experimenter ID, which we call oxm_vendor and which identifies a
> + * vendor, is inserted just before the payload.  Experimenter OXMs are
> + * identified by an all-1-bits oxm_class (OFPXMC12_EXPERIMENTER).  The
> + * oxm_length value *includes* the experimenter ID, so that the real payload 
> is
> + * only oxm_length - 4 bytes long.
> + *
> + * Internally, we represent an experimenter OXM header as a 64-bit integer 
> with
> + * the standard header in the upper 32 bits and the experimenter ID in the
> + * lower 32 bits.  (It would be more convenient to swap the positions of the
> + * two 32-bit words, but this would be more error-prone because experimenter
> + * OXMs are very rarely used, so accidentally passing one through a 32-bit 
> type
> + * somewhere in the OVS code would be hard to find.)
> + */
> +
> /*
>  * OXM Class IDs.
>  * The high order bit differentiate reserved classes from member classes.
> @@ -51,41 +91,90 @@ enum ofp12_oxm_class {
>     OFPXMC12_EXPERIMENTER   = 0xffff, /* Experimenter class */
> };
> 
> -/* Functions for extracting fields from OXM/NXM headers. */
> -static int nxm_class(uint32_t header) { return header >> 16; }
> -static int nxm_field(uint32_t header) { return (header >> 9) & 0x7f; }
> -static bool nxm_hasmask(uint32_t header) { return (header & 0x100) != 0; }
> -static int nxm_length(uint32_t header) { return header & 0xff; }
> +/* Functions for extracting raw field values from OXM/NXM headers. */
> +static uint32_t nxm_vendor(uint64_t header) { return header; }
> +static int nxm_class(uint64_t header) { return header >> 48; }
> +static int nxm_field(uint64_t header) { return (header >> 41) & 0x7f; }
> +static bool nxm_hasmask(uint64_t header) { return (header >> 40) & 1; }
> +static int nxm_length(uint64_t header) { return (header >> 32) & 0xff; }
> +
> +static bool
> +is_experimenter_oxm(uint64_t header)
> +{
> +    return nxm_class(header) == OFPXMC12_EXPERIMENTER;
> +}
> +
> +/* The OXM header "length" field is somewhat tricky:
> + *
> + *     - For a standard OXM header, the length is the number of bytes of the
> + *       payload, and the payload consists of just the value (and mask, if
> + *       present).
> + *
> + *     - For an experimenter OXM header, the length is the number of bytes in
> + *       the payload plus 4 (the length of the experimenter ID).  That is, 
> the
> + *       experimenter ID is included in oxm_length.
> + *
> + * This function returns the length of the experimenter ID field in 'header'.
> + * That is, for an experimenter OXM (when an experimenter ID is present), it
> + * returns 4, and for a standard OXM (when no experimenter ID is present), it
> + * returns 0. */
> +static int
> +nxm_experimenter_len(uint64_t header)
> +{
> +    return is_experimenter_oxm(header) ? 4 : 0;
> +}
> +
> +/* Returns the number of bytes that follow the header for an NXM/OXM entry
> + * with the given 'header'. */
> +static int
> +nxm_payload_len(uint64_t header)
> +{
> +    return nxm_length(header) - nxm_experimenter_len(header);
> +}
> +
> +/* Returns the number of bytes in the header for an NXM/OXM entry with the
> + * given 'header'. */
> +static int
> +nxm_header_len(uint64_t header)
> +{
> +    return 4 + nxm_experimenter_len(header);
> +}
> 
> /* Returns true if 'header' is a legacy NXM header, false if it is an OXM
>  * header.*/
> static bool
> -is_nxm_header(uint32_t header)
> +is_nxm_header(uint64_t header)
> {
>     return nxm_class(header) <= 1;
> }
> 
> -#define NXM_HEADER(CLASS, FIELD, HASMASK, LENGTH)                       \
> -    (((CLASS) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH))
> +#define NXM_HEADER(VENDOR, CLASS, HASMASK, FIELD, LENGTH)       \
> +    (((uint64_t) (CLASS) << 48) |                               \
> +     ((uint64_t) (FIELD) << 41) |                               \
> +     ((uint64_t) (HASMASK) << 40) |                             \
> +     ((uint64_t) (LENGTH) << 32) |                              \
> +     (VENDOR))
> 
> -#define NXM_HEADER_FMT "%d:%d:%d:%d"
> -#define NXM_HEADER_ARGS(HEADER)                 \
> -    nxm_class(HEADER), nxm_field(HEADER),      \
> +#define NXM_HEADER_FMT "%#"PRIx32":%d:%d:%d:%d"
> +#define NXM_HEADER_ARGS(HEADER)                                 \
> +    nxm_vendor(HEADER), nxm_class(HEADER), nxm_field(HEADER),   \
>     nxm_hasmask(HEADER), nxm_length(HEADER)
> 
> /* Functions for turning the "hasmask" bit on or off.  (This also requires
>  * adjusting the length.) */
> -static uint32_t
> -nxm_make_exact_header(uint32_t header)
> +static uint64_t
> +nxm_make_exact_header(uint64_t header)
> {
> -    return NXM_HEADER(nxm_class(header), nxm_field(header), 0,
> -                      nxm_length(header) / 2);
> +    int new_len = nxm_payload_len(header) / 2 + nxm_experimenter_len(header);
> +    return NXM_HEADER(nxm_vendor(header), nxm_class(header), 0,
> +                      nxm_field(header), new_len);
> }
> -static uint32_t
> -nxm_make_wild_header(uint32_t header)
> +static uint64_t
> +nxm_make_wild_header(uint64_t header)
> {
> -    return NXM_HEADER(nxm_class(header), nxm_field(header), 1,
> -                      nxm_length(header) * 2);
> +    int new_len = nxm_payload_len(header) * 2 + nxm_experimenter_len(header);
> +    return NXM_HEADER(nxm_vendor(header), nxm_class(header), 1,
> +                      nxm_field(header), new_len);
> }
> 
> /* Flow cookie.
> @@ -95,23 +184,23 @@ nxm_make_wild_header(uint32_t header)
>  * with specific cookies.  See the "nx_flow_mod" and "nx_flow_stats_request"
>  * structure definitions for more details.  This match is otherwise not
>  * allowed. */
> -#define NXM_NX_COOKIE     NXM_HEADER  (0x0001, 30, 0, 8)
> +#define NXM_NX_COOKIE     NXM_HEADER  (0, 0x0001, 0, 30, 8)
> #define NXM_NX_COOKIE_W   nxm_make_wild_header(NXM_NX_COOKIE)
> 
> struct nxm_field {
> -    uint32_t header;
> +    uint64_t header;
>     enum ofp_version version;
>     const char *name;           /* e.g. "NXM_OF_IN_PORT". */
> 
>     enum mf_field_id id;
> };
> 
> -static const struct nxm_field *nxm_field_by_header(uint32_t header);
> +static const struct nxm_field *nxm_field_by_header(uint64_t header);
> static const struct nxm_field *nxm_field_by_name(const char *name, size_t 
> len);
> static const struct nxm_field *nxm_field_by_mf_id(enum mf_field_id);
> static const struct nxm_field *oxm_field_by_mf_id(enum mf_field_id);
> 
> -static void nx_put_header__(struct ofpbuf *, uint32_t header, bool masked);
> +static void nx_put_header__(struct ofpbuf *, uint64_t header, bool masked);
> 
> /* Rate limit for nx_match parse errors.  These always indicate a bug in the
>  * peer and so there's not much point in showing a lot of them. */
> @@ -132,28 +221,47 @@ nxm_field_from_mf_field(enum mf_field_id id, enum 
> ofp_version version)
>  * 'version'.  Specify 0 for 'version' if an NXM legacy header should be
>  * preferred over any standardized OXM header.  Returns 0 if field 'id' cannot
>  * be expressed in NXM or OXM. */
> -uint32_t
> +static uint64_t
> mf_oxm_header(enum mf_field_id id, enum ofp_version version)
> {
>     const struct nxm_field *f = nxm_field_from_mf_field(id, version);
>     return f ? f->header : 0;
> }
> 
> +/* Returns the 32-bit OXM or NXM header to use for field 'id', preferring an
> + * NXM legacy header over any standardized OXM header.  Returns 0 if field 
> 'id'
> + * cannot be expressed with a 32-bit NXM or OXM header.
> + *
> + * Whenever possible, use nx_pull_header() instead of this function, because
> + * this function cannot support 64-bit experimenter OXM headers. */
> +uint32_t
> +mf_nxm_header(enum mf_field_id id)
> +{
> +    uint64_t oxm = mf_oxm_header(id, 0);
> +    return is_experimenter_oxm(oxm) ? 0 : oxm >> 32;
> +}
> +
> +static const struct mf_field *
> +mf_from_oxm_header(uint64_t header)
> +{
> +    const struct nxm_field *f = nxm_field_by_header(header);
> +    return f ? mf_from_id(f->id) : NULL;
> +}
> +
> /* Returns the "struct mf_field" that corresponds to NXM or OXM header
>  * 'header', or NULL if 'header' doesn't correspond to any known field.  */
> const struct mf_field *
> mf_from_nxm_header(uint32_t header)
> {
> -    const struct nxm_field *f = nxm_field_by_header(header);
> -    return f ? mf_from_id(f->id) : NULL;
> +    return mf_from_oxm_header((uint64_t) header << 32);
> }
> 
> /* Returns the width of the data for a field with the given 'header', in
>  * bytes. */
> static int
> -nxm_field_bytes(uint32_t header)
> +nxm_field_bytes(uint64_t header)
> {
> -    unsigned int length = nxm_length(header);
> +    unsigned int length = nxm_payload_len(header);
>     return nxm_hasmask(header) ? length / 2 : length;
> }
> 
> @@ -172,7 +280,7 @@ mf_oxm_version(enum mf_field_id id)
>  * for any 1-bit in the value where there is a 0-bit in the mask.  Returns 0 
> if
>  * none, otherwise an error code. */
> static bool
> -is_mask_consistent(uint32_t header, const uint8_t *value, const uint8_t 
> *mask)
> +is_mask_consistent(uint64_t header, const uint8_t *value, const uint8_t 
> *mask)
> {
>     unsigned int width = nxm_field_bytes(header);
>     unsigned int i;
> @@ -191,28 +299,37 @@ is_mask_consistent(uint32_t header, const uint8_t 
> *value, const uint8_t *mask)
> }
> 
> static bool
> -is_cookie_pseudoheader(uint32_t header)
> +is_cookie_pseudoheader(uint64_t header)
> {
>     return header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W;
> }
> 
> static enum ofperr
> -nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint32_t *header,
> +nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
>                  const struct mf_field **field)
> {
>     if (ofpbuf_size(b) < 4) {
> -        VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry",
> -                    ofpbuf_size(b));
> -        goto error;
> +        goto bad_len;
>     }
> -    *header = ntohl(get_unaligned_be32(ofpbuf_pull(b, 4)));
> -    if (nxm_length(*header) == 0) {
> -        VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has zero length",
> -                     NXM_HEADER_ARGS(*header));
> +
> +    *header = ((uint64_t) ntohl(get_unaligned_be32(ofpbuf_data(b)))) << 32;
> +    if (is_experimenter_oxm(*header)) {
> +        if (ofpbuf_size(b) < 8) {
> +            goto bad_len;
> +        }
> +        *header = ntohll(get_unaligned_be64(ofpbuf_data(b)));
> +    }
> +    if (nxm_length(*header) <= nxm_experimenter_len(*header)) {
> +        VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has invalid length %d 
> "
> +                     "(minimum is %d)",
> +                     NXM_HEADER_ARGS(*header), nxm_length(*header),
> +                     nxm_header_len(*header) + 1);
>         goto error;
>     }
> +    ofpbuf_pull(b, nxm_header_len(*header));
> +
>     if (field) {
> -        *field = mf_from_nxm_header(*header);
> +        *field = mf_from_oxm_header(*header);
>         if (!*field && !(allow_cookie && is_cookie_pseudoheader(*header))) {
>             VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" is unknown",
>                         NXM_HEADER_ARGS(*header));
> @@ -222,6 +339,9 @@ nx_pull_header__(struct ofpbuf *b, bool allow_cookie, 
> uint32_t *header,
> 
>     return 0;
> 
> +bad_len:
> +    VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry",
> +                ofpbuf_size(b));
> error:
>     *header = 0;
>     *field = NULL;
> @@ -229,7 +349,7 @@ error:
> }
> 
> static enum ofperr
> -nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint32_t *header,
> +nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
>                 const struct mf_field **field,
>                 union mf_value *value, union mf_value *mask)
> {
> @@ -243,7 +363,7 @@ nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, 
> uint32_t *header,
>         return header_error;
>     }
> 
> -    payload_len = nxm_length(*header);
> +    payload_len = nxm_payload_len(*header);
>     payload = ofpbuf_try_pull(b, payload_len);
>     if (!payload) {
>         VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" calls for %u-byte "
> @@ -289,7 +409,7 @@ enum ofperr
> nx_pull_entry(struct ofpbuf *b, const struct mf_field **field,
>               union mf_value *value, union mf_value *mask)
> {
> -    uint32_t header;
> +    uint64_t header;
> 
>     return nx_pull_entry__(b, false, &header, field, value, mask);
> }
> @@ -308,7 +428,7 @@ enum ofperr
> nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked)
> {
>     enum ofperr error;
> -    uint32_t header;
> +    uint64_t header;
> 
>     error = nx_pull_header__(b, false, &header, field);
>     if (masked) {
> @@ -325,7 +445,7 @@ nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie,
>                     union mf_value *value, union mf_value *mask)
> {
>     enum ofperr error;
> -    uint32_t header;
> +    uint64_t header;
> 
>     error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask);
>     if (error) {
> @@ -907,12 +1027,12 @@ oxm_put_match(struct ofpbuf *b, const struct match 
> *match,
> }
> 
> static void
> -nx_put_header__(struct ofpbuf *b, uint32_t header, bool masked)
> +nx_put_header__(struct ofpbuf *b, uint64_t header, bool masked)
> {
> -    uint32_t masked_header = masked ? nxm_make_wild_header(header) : header;
> -    ovs_be32 network_header = htonl(masked_header);
> +    uint64_t masked_header = masked ? nxm_make_wild_header(header) : header;
> +    ovs_be64 network_header = htonll(masked_header);
> 
> -    ofpbuf_put(b, &network_header, sizeof network_header);
> +    ofpbuf_put(b, &network_header, 4 + nxm_experimenter_len(header));
> }
> 
> void
> @@ -939,7 +1059,7 @@ nx_put_entry(struct ofpbuf *b,
> 
> /* nx_match_to_string() and helpers. */
> 
> -static void format_nxm_field_name(struct ds *, uint32_t header);
> +static void format_nxm_field_name(struct ds *, uint64_t header);
> 
> char *
> nx_match_to_string(const uint8_t *p, unsigned int match_len)
> @@ -957,7 +1077,7 @@ nx_match_to_string(const uint8_t *p, unsigned int 
> match_len)
>         union mf_value value;
>         union mf_value mask;
>         enum ofperr error;
> -        uint32_t header;
> +        uint64_t header;
>         int value_len;
> 
>         error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask);
> @@ -1042,7 +1162,7 @@ nx_format_field_name(enum mf_field_id id, enum 
> ofp_version version,
> }
> 
> static void
> -format_nxm_field_name(struct ds *s, uint32_t header)
> +format_nxm_field_name(struct ds *s, uint64_t header)
> {
>     const struct nxm_field *f = nxm_field_by_header(header);
>     if (f) {
> @@ -1065,7 +1185,7 @@ streq_len(const char *a, size_t a_len, const char *b)
>     return strlen(b) == a_len && !memcmp(a, b, a_len);
> }
> 
> -static uint32_t
> +static uint64_t
> parse_nxm_field_name(const char *name, int name_len)
> {
>     const struct nxm_field *f;
> @@ -1086,16 +1206,24 @@ parse_nxm_field_name(const char *name, int name_len)
>         return NXM_NX_COOKIE_W;
>     }
> 
> -    /* Check whether it's a 32-bit field header value as hex.
> +    /* Check whether it's a field header value as hex.
>      * (This isn't ordinarily useful except for testing error behavior.) */
>     if (name_len == 8) {
> -        uint32_t header;
> +        uint64_t header;
>         bool ok;
> 
> -        header = hexits_value(name, name_len, &ok);
> +        header = hexits_value(name, name_len, &ok) << 32;
>         if (ok) {
>             return header;
>         }
> +    } else if (name_len == 16) {
> +        uint64_t header;
> +        bool ok;
> +
> +        header = hexits_value(name, name_len, &ok);
> +        if (ok && is_experimenter_oxm(header)) {
> +            return header;
> +        }
>     }
> 
>     return 0;
> @@ -1117,7 +1245,7 @@ nx_match_from_string_raw(const char *s, struct ofpbuf 
> *b)
> 
>     for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) {
>         const char *name;
> -        uint32_t header;
> +        uint64_t header;
>         int name_len;
>         size_t n;
> 
> @@ -1529,7 +1657,7 @@ oxm_bitmap_from_mf_bitmap(const struct mf_bitmap 
> *fields,
>     int i;
> 
>     BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) {
> -        uint32_t oxm = mf_oxm_header(i, version);
> +        uint64_t oxm = mf_oxm_header(i, version);
>         uint32_t class = nxm_class(oxm);
>         int field = nxm_field(oxm);
> 
> @@ -1550,7 +1678,7 @@ oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, enum 
> ofp_version version)
> 
>     for (enum mf_field_id id = 0; id < MFF_N_IDS; id++) {
>         if (version >= mf_oxm_version(id)) {
> -            uint32_t oxm = mf_oxm_header(id, version);
> +            uint64_t oxm = mf_oxm_header(id, version);
>             uint32_t class = nxm_class(oxm);
>             int field = nxm_field(oxm);
> 
> @@ -1649,7 +1777,7 @@ nxm_init(void)
> }
> 
> static const struct nxm_field *
> -nxm_field_by_header(uint32_t header)
> +nxm_field_by_header(uint64_t header)
> {
>     const struct nxm_field_index *nfi;
> 
> diff --git a/lib/nx-match.h b/lib/nx-match.h
> index 929b1db..62e73a0 100644
> --- a/lib/nx-match.h
> +++ b/lib/nx-match.h
> @@ -76,7 +76,7 @@ void nx_put_header(struct ofpbuf *, enum mf_field_id, enum 
> ofp_version,
>  * nx_put_entry/header() for decoding and encoding OXM/NXM.  In those cases,
>  * the nx_*() functions should be preferred because they can support the 
> 64-bit
>  * "experimenter" OXM format (even though it is not yet implemented). */
> -uint32_t mf_oxm_header(enum mf_field_id, enum ofp_version oxm_version);
> +uint32_t mf_nxm_header(enum mf_field_id);
> const struct mf_field *mf_from_nxm_header(uint32_t nxm_header);
> 
> char *nx_match_to_string(const uint8_t *, unsigned int match_len);
> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> index ca4dac9..a02dd22 100644
> --- a/lib/ofp-actions.c
> +++ b/lib/ofp-actions.c
> @@ -736,7 +736,7 @@ encode_OUTPUT_REG(const struct ofpact_output_reg 
> *output_reg,
> 
>     naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
>                                            output_reg->src.n_bits);
> -    naor->src = htonl(mf_oxm_header(output_reg->src.field->id, 0));
> +    naor->src = htonl(mf_nxm_header(output_reg->src.field->id));
>     naor->max_len = htons(output_reg->max_len);
> }
> 
> @@ -849,7 +849,7 @@ decode_bundle(bool load, const struct nx_action_bundle 
> *nab,
>     } else if (bundle->algorithm != NX_BD_ALG_HRW
>                && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
>         VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) 
> bundle->algorithm);
> -    } else if (slave_type != mf_oxm_header(MFF_IN_PORT, 0)) {
> +    } else if (slave_type != mf_nxm_header(MFF_IN_PORT)) {
>         VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
>     } else {
>         error = 0;
> @@ -930,12 +930,12 @@ encode_BUNDLE(const struct ofpact_bundle *bundle,
>     nab->algorithm = htons(bundle->algorithm);
>     nab->fields = htons(bundle->fields);
>     nab->basis = htons(bundle->basis);
> -    nab->slave_type = htonl(mf_oxm_header(MFF_IN_PORT, 0));
> +    nab->slave_type = htonl(mf_nxm_header(MFF_IN_PORT));
>     nab->n_slaves = htons(bundle->n_slaves);
>     if (bundle->dst.field) {
>         nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
>                                               bundle->dst.n_bits);
> -        nab->dst = htonl(mf_oxm_header(bundle->dst.field->id, 0));
> +        nab->dst = htonl(mf_nxm_header(bundle->dst.field->id));
>     }
> 
>     slaves = ofpbuf_put_zeros(out, slaves_len);
> @@ -1830,8 +1830,8 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
>         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_oxm_header(move->src.field->id, 0));
> -        narm->dst = htonl(mf_oxm_header(move->dst.field->id, 0));
> +        narm->src = htonl(mf_nxm_header(move->src.field->id));
> +        narm->dst = htonl(mf_nxm_header(move->dst.field->id));
>     }
> }
> 
> @@ -2075,7 +2075,7 @@ set_field_to_nxast(const struct ofpact_set_field *sf, 
> struct ofpbuf *openflow)
>     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_oxm_header(dst.field->id, 0));
> +        narl->dst = htonl(mf_nxm_header(dst.field->id));
>         narl->value = htonll(value);
>     }
> }
> @@ -2417,7 +2417,7 @@ encode_STACK_op(const struct ofpact_stack *stack_action,
> {
>     nasp->offset = htons(stack_action->subfield.ofs);
>     nasp->n_bits = htons(stack_action->subfield.n_bits);
> -    nasp->field = htonl(mf_oxm_header(stack_action->subfield.field->id, 0));
> +    nasp->field = htonl(mf_nxm_header(stack_action->subfield.field->id));
> }
> 
> static void
> @@ -3636,7 +3636,7 @@ encode_LEARN(const struct ofpact_learn *learn,
>         put_u16(out, spec->n_bits | spec->dst_type | spec->src_type);
> 
>         if (spec->src_type == NX_LEARN_SRC_FIELD) {
> -            put_u32(out, mf_oxm_header(spec->src.field->id, 0));
> +            put_u32(out, mf_nxm_header(spec->src.field->id));
>             put_u16(out, spec->src.ofs);
>         } else {
>             size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
> @@ -3648,7 +3648,7 @@ encode_LEARN(const struct ofpact_learn *learn,
> 
>         if (spec->dst_type == NX_LEARN_DST_MATCH ||
>             spec->dst_type == NX_LEARN_DST_LOAD) {
> -            put_u32(out, mf_oxm_header(spec->dst.field->id, 0));
> +            put_u32(out, mf_nxm_header(spec->dst.field->id));
>             put_u16(out, spec->dst.ofs);
>         }
>     }
> @@ -3773,7 +3773,7 @@ encode_MULTIPATH(const struct ofpact_multipath *mp,
>     nam->max_link = htons(mp->max_link);
>     nam->arg = htonl(mp->arg);
>     nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
> -    nam->dst = htonl(mf_oxm_header(mp->dst.field->id, 0));
> +    nam->dst = htonl(mf_nxm_header(mp->dst.field->id));
> }
> 
> static char * WARN_UNUSED_RESULT
> diff --git a/tests/ofp-print.at b/tests/ofp-print.at
> index 29555ef..9e0ec54 100644
> --- a/tests/ofp-print.at
> +++ b/tests/ofp-print.at
> @@ -890,6 +890,20 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD 
> priority=255,sctp actions=set_field:
> ])
> AT_CLEANUP
> 
> +AT_SETUP([OFPT_FLOW_MOD - OF1.2 - experimenter OXM])
> +AT_KEYWORDS([ofp-print])
> +AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
> +03 0e 00 48 52 33 45 07 00 00 00 00 00 00 00 00 \
> +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
> +ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
> +00 01 00 14 ff ff 01 0c 00 00 23 20 01 23 45 67 \
> +0f ff ff ff 00 00 00 00
> +" 2], [0], [dnl
> +OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD 
> priority=255,dp_hash=0x1234567/0xfffffff actions=drop
> +], [dnl
> +])
> +AT_CLEANUP
> +
> dnl This triggered a buggy "instructions out of order" message earlier.
> AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
> AT_KEYWORDS([ofp-print])
> diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
> index e5c0e98..d29b4de 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -797,9 +797,17 @@ NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
> NXM_NX_REG0_W(a0e0d050/ffffffff)
> NXM_NX_REG0_W(00000000/00000000)
> 
> +# dp_hash (testing experimenter OXM).
> +NXM_NX_DP_HASH(01234567)
> +NXOXM_ET_DP_HASH(01234567)
> +
> # Invalid field number.
> 01020304(1111/3333)
> 
> +# Invalid field numbers (experimenter OXM).
> +ffff020800002320(11112222)
> +ffff030800002320(1111/3333)
> +
> # Unimplemented registers.
> #
> # This test assumes that at least two registers, but fewer than 16,
> @@ -1080,9 +1088,17 @@ NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
> NXM_NX_REG0(a0e0d050)
> <any>
> 
> +# dp_hash (testing experimenter OXM).
> +NXM_NX_DP_HASH(01234567)
> +NXM_NX_DP_HASH(01234567)
> +
> # Invalid field number.
> nx_pull_match() returned error OFPBMC_BAD_FIELD
> 
> +# Invalid field numbers (experimenter OXM).
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> +
> # Unimplemented registers.
> #
> # This test assumes that at least two registers, but fewer than 16,
> @@ -1096,7 +1112,7 @@ nx_pull_match() returned error OFPBMC_BAD_FIELD
> # Check that at least the first warning made it.  (It's rate-limited
> # so a variable number could show up, especially under valgrind etc.)
> AT_CHECK([grep '1-bits in value' stderr | sed 1q], [0], [dnl
> -nx_match|WARN|Rejecting NXM/OXM entry 0:1:1:12 with 1-bits in value for bits 
> wildcarded by the mask.
> +nx_match|WARN|Rejecting NXM/OXM entry 0:0:1:1:12 with 1-bits in value for 
> bits wildcarded by the mask.
> ])
> 
> # Check that there wasn't any other stderr output.
> @@ -1648,15 +1664,20 @@ AT_SETUP([ovs-ofctl parse-nx-match loose])
> AT_KEYWORDS([nx-match])
> AT_DATA([nx-match.txt], [dnl
> NXM_OF_IN_PORT(0001), 01020304(1111/3333), NXM_OF_ETH_TYPE(0800)
> +NXM_OF_IN_PORT(0001), ffff020800002320(11112222), NXM_OF_ETH_TYPE(0800)
> +NXM_OF_IN_PORT(0001), ffff030800002320(1111/3333), NXM_OF_ETH_TYPE(0800)
> ])
> 
> AT_CHECK([ovs-ofctl --strict parse-nx-match < nx-match.txt], [0], [dnl
> nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> ])
> 
> -AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' -vnx_match parse-nx-match < 
> nx-match.txt], [0], [dnl
> +AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl
> +NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800)
> +NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800)
> NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800)
> -], [nx_match|DBG|OXM header 258:1:1:4 is unknown
> ])
> AT_CLEANUP
> 
> @@ -1897,6 +1918,10 @@ OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), 
> OXM_OF_PKT_REG1_W(a0e0d050
> 
> # Invalid field number.
> 01020304(1111/3333)
> +
> +# Invalid field numbers (experimenter OXM).
> +ffff020800002320(11112222)
> +ffff030800002320(1111/3333)
> ])
> AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm 
> OpenFlow12 < oxm.txt],
>   [0], [dnl
> @@ -2134,12 +2159,16 @@ NXM_NX_REG1_W(a0e0d050/f0f0f0f0), 
> NXM_NX_REG2(a0e0d050)
> 
> # Invalid field number.
> nx_pull_match() returned error OFPBMC_BAD_FIELD
> +
> +# Invalid field numbers (experimenter OXM).
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> ], [stderr])
> 
> # Check that at least the first warning made it.  (It's rate-limited
> # so a variable number could show up, especially under valgrind etc.)
> AT_CHECK([grep '1-bits in value' stderr | sed 1q], [0], [dnl
> -nx_match|WARN|Rejecting NXM/OXM entry 32768:2:1:16 with 1-bits in value for 
> bits wildcarded by the mask.
> +nx_match|WARN|Rejecting NXM/OXM entry 0:32768:2:1:16 with 1-bits in value 
> for bits wildcarded by the mask.
> ])
> 
> # Check that there wasn't any other stderr output.
> @@ -2199,14 +2228,67 @@ AT_SETUP([ovs-ofctl parse-oxm loose])
> AT_KEYWORDS([oxm])
> AT_DATA([oxm.txt], [dnl
> OXM_OF_IN_PORT(00000001), 01020304(1111/3333), OXM_OF_ETH_TYPE(0800)
> +OXM_OF_IN_PORT(00000001), ffff020800002320(11112222), OXM_OF_ETH_TYPE(0800)
> +OXM_OF_IN_PORT(00000001), ffff030800002320(1111/3333), OXM_OF_ETH_TYPE(0800)
> ])
> 
> AT_CHECK([ovs-ofctl --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl
> nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> +nx_pull_match() returned error OFPBMC_BAD_FIELD
> ])
> 
> AT_CHECK([ovs-ofctl parse-oxm OpenFlow12 < oxm.txt], [0], [dnl
> OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800)
> +OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800)
> +OXM_OF_IN_PORT(00000001), OXM_OF_ETH_TYPE(0800)
> +])
> +AT_CLEANUP
> +
> +AT_SETUP([experimenter OXM encoding])
> +AT_DATA([oxm.txt], [dnl
> +NXM_NX_DP_HASH(01234567)
> +NXOXM_ET_DP_HASH(01234567)
> +
> +NXM_NX_DP_HASH_W(01234567/0fffffff)
> +NXOXM_ET_DP_HASH_W(01234567/0fffffff)
> +])
> +
> +# To allow for testing experimenter OXM, which doesn't really have many
> +# examples in the wild, we've defined a variant of NXM_NX_DP_HASH that uses
> +# the experimenter OXM mechanism, called NXOXM_ET_DP_HASH.  We've defined
> +# it as if it were introduced with OpenFlow 1.5, which gives us the
> +# opportunity to see that both forms are accepted in all OpenFlow versions
> +# but the experimenter form is used for encoding in OF1.5+.
> +#
> +# First verify that both forms are accepted and NXOXM_ET_DP_HASH is encoded
> +# in OF1.5.
> +AT_CHECK([ovs-ofctl -m --strict parse-oxm OpenFlow15 < oxm.txt], [0], [dnl
> +NXOXM_ET_DP_HASH(01234567)
> +00000000  00 01 00 10 ff ff 00 08-00 00 23 20 01 23 45 67 @&t@
> +NXOXM_ET_DP_HASH(01234567)
> +00000000  00 01 00 10 ff ff 00 08-00 00 23 20 01 23 45 67 @&t@
> +
> +NXOXM_ET_DP_HASH_W(01234567/0fffffff)
> +00000000  00 01 00 14 ff ff 01 0c-00 00 23 20 01 23 45 67 @&t@
> +00000010  0f ff ff ff 00 00 00 00-
> +NXOXM_ET_DP_HASH_W(01234567/0fffffff)
> +00000000  00 01 00 14 ff ff 01 0c-00 00 23 20 01 23 45 67 @&t@
> +00000010  0f ff ff ff 00 00 00 00-
> +])
> +
> +# Then verify that both forms are accepted and NXM_NX_DP_HASH is encoded
> +# in OF1.2.
> +AT_CHECK([ovs-ofctl -m --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl
> +NXM_NX_DP_HASH(01234567)
> +00000000  00 01 00 0c 00 01 46 04-01 23 45 67 00 00 00 00 @&t@
> +NXM_NX_DP_HASH(01234567)
> +00000000  00 01 00 0c 00 01 46 04-01 23 45 67 00 00 00 00 @&t@
> +
> +NXM_NX_DP_HASH_W(01234567/0fffffff)
> +00000000  00 01 00 10 00 01 47 08-01 23 45 67 0f ff ff ff @&t@
> +NXM_NX_DP_HASH_W(01234567/0fffffff)
> +00000000  00 01 00 10 00 01 47 08-01 23 45 67 0f ff ff ff @&t@
> ])
> AT_CLEANUP
> 
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 8dcd72c..ae8d59d 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -2879,6 +2879,11 @@ ofctl_parse_nxm__(bool oxm, enum ofp_version version)
> 
>             puts(out);
>             free(out);
> +
> +            if (verbosity > 0) {
> +                ovs_hex_dump(stdout, ofpbuf_data(&nx_match),
> +                             ofpbuf_size(&nx_match), 0, false);
> +            }
>         } else {
>             printf("nx_pull_match() returned error %s\n",
>                    ofperr_get_name(error));
> -- 
> 1.7.10.4
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to