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.
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, FIELD, HASMASK, 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),
+ nxm_field(header), 0, 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),
+ nxm_field(header), 1, new_len);
}
/* Flow cookie.
* 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, 30, 0, 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. */
* '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;
}
* 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;
}
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));
return 0;
+bad_len:
+ VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry",
+ ofpbuf_size(b));
error:
*header = 0;
*field = NULL;
}
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)
{
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 "
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);
}
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) {
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) {
}
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, nxm_header_len(header));
}
void
\f
/* 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)
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);
}
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) {
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;
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;
for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) {
const char *name;
- uint32_t header;
+ uint64_t header;
int name_len;
size_t n;
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);
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);
}
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;
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);
}
} 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;
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);
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));
}
}
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);
}
}
{
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
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);
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);
}
}
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
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,
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,
# 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.
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
# 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
# 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.
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