From 178742f941354475c3f242c676b3e772ad5d7d9e Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 16 Sep 2014 22:13:44 -0700 Subject: [PATCH] nx-match: Move all knowledge of OXM/NXM here. This improves the general abstraction of OXM/NXM by eliminating direct knowledge of it from the meta-flow code and other places. Some function renaming might be called for; for example, mf_oxm_header() may not be the best name now that the function is implemented within nx-match. However, these renamings would make this commit larger and harder to review, so I'm postponing them. Signed-off-by: Ben Pfaff Acked-by: YAMAMOTO Takashi --- build-aux/extract-ofp-fields | 206 ++++---- include/openflow/nicira-ext.h | 32 -- include/openflow/openflow-1.2.h | 16 - lib/automake.mk | 7 +- lib/meta-flow.c | 210 +------- lib/meta-flow.h | 44 -- lib/nx-match.c | 850 +++++++++++++++++++++++++------- lib/nx-match.h | 39 +- lib/ofp-actions.c | 143 +++--- lib/ofp-util.c | 103 +--- ofproto/ofproto.c | 20 +- tests/ofp-actions.at | 33 +- tests/ovs-ofctl.at | 29 +- vswitchd/bridge.c | 1 + 14 files changed, 968 insertions(+), 765 deletions(-) diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index 554364704..a82606bd2 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -94,10 +94,11 @@ def usage(): argv0 = os.path.basename(sys.argv[0]) print '''\ %(argv0)s, for extracting OpenFlow field properties from meta-flow.h -usage: %(argv0)s INPUT +usage: %(argv0)s INPUT [--meta-flow | --nx-match] where INPUT points to lib/meta-flow.h in the source directory. -The output written to stdout is intended to be saved as lib/meta-flow.inc, -which lib/meta-flow.c \"#include\"s.\ +Depending on the option given, the output written to stdout is intended to be +saved either as lib/meta-flow.inc or lib/nx-match.inc for the respective C +file to #include.\ ''' % {"argv0": argv0} sys.exit(0) @@ -122,7 +123,7 @@ def parse_oxm(s, prefix, n_bytes): class_ = oxm_name_to_class(name) if class_ is None: fatal("unknown OXM class for %s" % name) - header = ("NXM_HEADER(0x%04x, %s, %d)" % (class_, code, n_bytes)) + header = ("NXM_HEADER(0x%04x,%s,0,%d)" % (class_, code, n_bytes)) if of_version: if of_version not in VERSION: @@ -244,7 +245,97 @@ def protocols_to_c(protocols): else: assert False -def extract_ofp_fields(): +def make_meta_flow(fields): + output = [] + for f in fields: + output += ["{"] + output += [" %s," % f['mff']] + if f['extra_name']: + output += [" \"%s\", \"%s\"," % (f['name'], f['extra_name'])] + else: + output += [" \"%s\", NULL," % f['name']] + output += [" %d, %d," % (f['n_bytes'], f['n_bits'])] + + if f['writable']: + rw = 'true' + else: + rw = 'false' + output += [" %s, %s, %s, %s," + % (f['mask'], f['string'], f['prereqs'], rw)] + + nxm = f['NXM'] + oxm = f['OXM'] + if not nxm: + nxm = oxm + elif not oxm: + oxm = nxm + + of10 = f['OF1.0'] + of11 = f['OF1.1'] + if f['mff'] in ('MFF_DL_VLAN', 'MFF_DL_VLAN_PCP'): + # MFF_DL_VLAN and MFF_DL_VLAN_PCP don't exactly correspond to + # OF1.1, nor do they have NXM or OXM assignments, but their + # meanings can be expressed in every protocol, which is the goal of + # this member. + protocols = set(["of10", "of11", "oxm"]) + else: + protocols = set([]) + if of10: + protocols |= set(["of10"]) + if of11: + protocols |= set(["of11"]) + if nxm or oxm: + protocols |= set(["oxm"]) + + if f['mask'] == 'MFM_FULLY': + cidr_protocols = protocols.copy() + bitwise_protocols = protocols.copy() + + if of10 == 'exact match': + bitwise_protocols -= set(['of10']) + cidr_protocols -= set(['of10']) + elif of10 == 'CIDR mask': + bitwise_protocols -= set(['of10']) + else: + assert of10 is None + + if of11 == 'exact match': + bitwise_protocols -= set(['of11']) + cidr_protocols -= set(['of11']) + else: + assert of11 in (None, 'bitwise mask') + else: + assert f['mask'] == 'MFM_NONE' + cidr_protocols = set([]) + bitwise_protocols = set([]) + + output += [" %s," % protocols_to_c(protocols)] + output += [" %s," % protocols_to_c(cidr_protocols)] + output += [" %s," % protocols_to_c(bitwise_protocols)] + + if f['prefix']: + output += [" FLOW_U32OFS(%s)," % f['prefix']] + else: + output += [" -1, /* not usable for prefix lookup */"] + + output += ["},"] + return output + +def print_oxm_field(oxm, mff): + if oxm: + print """{ .nf = { %s, %d, "%s", %s } },""" % ( + oxm[0], oxm[2], oxm[1], mff) + +def make_nx_match(fields): + output = [] + print "static struct nxm_field_index all_nxm_fields[] = {"; + for f in fields: + print_oxm_field(f['NXM'], f['mff']) + print_oxm_field(f['OXM'], f['mff']) + print "};" + return output + +def extract_ofp_fields(mode): global line fields = [] @@ -349,91 +440,16 @@ def extract_ofp_fields(): if n_errors: sys.exit(1) - output = [] - output += ["/* Generated automatically; do not modify! " - "-*- buffer-read-only: t -*- */"] - output += [""] - - for f in fields: - output += ["{"] - output += [" %s," % f['mff']] - if f['extra_name']: - output += [" \"%s\", \"%s\"," % (f['name'], f['extra_name'])] - else: - output += [" \"%s\", NULL," % f['name']] - output += [" %d, %d," % (f['n_bytes'], f['n_bits'])] - - if f['writable']: - rw = 'true' - else: - rw = 'false' - output += [" %s, %s, %s, %s," - % (f['mask'], f['string'], f['prereqs'], rw)] - - nxm = f['NXM'] - oxm = f['OXM'] - if not nxm: - nxm = oxm - elif not oxm: - oxm = nxm - if nxm: - output += [" %s, \"%s\"," % (nxm[0], nxm[1])] - output += [" %s, \"%s\", %s," % (oxm[0], oxm[1], oxm[2])] - else: - output += [" 0, NULL, 0, NULL, 0, /* no NXM or OXM */"] - - of10 = f['OF1.0'] - of11 = f['OF1.1'] - if f['mff'] in ('MFF_DL_VLAN', 'MFF_DL_VLAN_PCP'): - # MFF_DL_VLAN and MFF_DL_VLAN_PCP don't exactly correspond to - # OF1.1, nor do they have NXM or OXM assignments, but their - # meanings can be expressed in every protocol, which is the goal of - # this member. - protocols = set(["of10", "of11", "oxm"]) - else: - protocols = set([]) - if of10: - protocols |= set(["of10"]) - if of11: - protocols |= set(["of11"]) - if nxm or oxm: - protocols |= set(["oxm"]) + print """\ +/* Generated automatically; do not modify! "-*- buffer-read-only: t -*- */ +""" - if f['mask'] == 'MFM_FULLY': - cidr_protocols = protocols.copy() - bitwise_protocols = protocols.copy() - - if of10 == 'exact match': - bitwise_protocols -= set(['of10']) - cidr_protocols -= set(['of10']) - elif of10 == 'CIDR mask': - bitwise_protocols -= set(['of10']) - else: - assert of10 is None - - if of11 == 'exact match': - bitwise_protocols -= set(['of11']) - cidr_protocols -= set(['of11']) - else: - assert of11 in (None, 'bitwise mask') - else: - assert f['mask'] == 'MFM_NONE' - cidr_protocols = set([]) - bitwise_protocols = set([]) - - output += [" %s," % protocols_to_c(protocols)] - output += [" %s," % protocols_to_c(cidr_protocols)] - output += [" %s," % protocols_to_c(bitwise_protocols)] - - if f['prefix']: - output += [" FLOW_U32OFS(%s)," % f['prefix']] - else: - output += [" -1, /* not usable for prefix lookup */"] - - output += ["},"] - - if n_errors: - sys.exit(1) + if mode == '--meta-flow': + output = make_meta_flow(fields) + elif mode == '--nx-match': + output = make_nx_match(fields) + else: + assert False return output @@ -441,11 +457,11 @@ def extract_ofp_fields(): if __name__ == '__main__': if '--help' in sys.argv: usage() - elif len(sys.argv) != 2: - sys.stderr.write("exactly one non-option argument required; " + elif len(sys.argv) != 3: + sys.stderr.write("exactly two arguments required; " "use --help for help\n") sys.exit(1) - else: + elif sys.argv[2] in ('--meta-flow', '--nx-match'): global file_name global input_file global line_number @@ -453,6 +469,10 @@ if __name__ == '__main__': input_file = open(file_name) line_number = 0 - for oline in extract_ofp_fields(): + for oline in extract_ofp_fields(sys.argv[2]): print oline + else: + sys.stderr.write("invalid arguments; use --help for help\n") + sys.exit(1) + diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index b1885b276..65ba950ee 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -467,21 +467,6 @@ OFP_ASSERT(sizeof(struct nx_async_config) == 24); * nx_match error. */ -#define NXM_HEADER__(VENDOR, FIELD, HASMASK, LENGTH) \ - (((VENDOR) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH)) -#define NXM_HEADER(VENDOR, FIELD, LENGTH) \ - NXM_HEADER__(VENDOR, FIELD, 0, LENGTH) -#define NXM_HEADER_W(VENDOR, FIELD, LENGTH) \ - NXM_HEADER__(VENDOR, FIELD, 1, (LENGTH) * 2) -#define NXM_VENDOR(HEADER) ((HEADER) >> 16) -#define NXM_FIELD(HEADER) (((HEADER) >> 9) & 0x7f) -#define NXM_TYPE(HEADER) (((HEADER) >> 9) & 0x7fffff) -#define NXM_HASMASK(HEADER) (((HEADER) >> 8) & 1) -#define NXM_LENGTH(HEADER) ((HEADER) & 0xff) - -#define NXM_MAKE_WILD_HEADER(HEADER) \ - NXM_HEADER_W(NXM_VENDOR(HEADER), NXM_FIELD(HEADER), NXM_LENGTH(HEADER)) - /* Number of registers allocated NXM field IDs. */ #define NXM_NX_MAX_REGS 16 @@ -489,23 +474,6 @@ OFP_ASSERT(sizeof(struct nx_async_config) == 24); #define NX_IP_FRAG_ANY (1 << 0) /* Is this a fragment? */ #define NX_IP_FRAG_LATER (1 << 1) /* Is this a fragment with nonzero offset? */ -/* Flow cookie. - * - * This may be used to gain the OpenFlow 1.1-like ability to restrict - * certain NXM-based Flow Mod and Flow Stats Request messages to flows - * with specific cookies. See the "nx_flow_mod" and "nx_flow_stats_request" - * structure definitions for more details. This match is otherwise not - * allowed. - * - * Prereqs: None. - * - * Format: 64-bit integer in network byte order. - * - * Masking: Arbitrary masks. */ -#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 8) -#define NXM_NX_COOKIE_W NXM_HEADER_W(0x0001, 30, 8) - - /* ## --------------------- ## */ /* ## Requests and replies. ## */ /* ## --------------------- ## */ diff --git a/include/openflow/openflow-1.2.h b/include/openflow/openflow-1.2.h index ef1d340ec..f4c97a1e5 100644 --- a/include/openflow/openflow-1.2.h +++ b/include/openflow/openflow-1.2.h @@ -58,22 +58,6 @@ /* Error type for experimenter error messages. */ #define OFPET12_EXPERIMENTER 0xffff -/* - * OXM Class IDs. - * The high order bit differentiate reserved classes from member classes. - * Classes 0x0000 to 0x7FFF are member classes, allocated by ONF. - * Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation. - */ -enum ofp12_oxm_class { - OFPXMC12_NXM_0 = 0x0000, /* Backward compatibility with NXM */ - OFPXMC12_NXM_1 = 0x0001, /* Backward compatibility with NXM */ - OFPXMC12_OPENFLOW_BASIC = 0x8000, /* Basic class for OpenFlow */ - OFPXMC15_PACKET_REGS = 0x8001, /* Packet registers (pipeline fields). */ - OFPXMC12_EXPERIMENTER = 0xffff, /* Experimenter class */ -}; - -#define IS_OXM_HEADER(header) (NXM_VENDOR(header) == OFPXMC12_OPENFLOW_BASIC) - /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate * special conditions. */ diff --git a/lib/automake.mk b/lib/automake.mk index 329eca7b8..ec4ad8e8a 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -444,9 +444,12 @@ lib/dirs.c: lib/dirs.c.in Makefile mv lib/dirs.c.tmp lib/dirs.c lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h - $(AM_V_GEN)$(run_python) $^ > $@.tmp && mv $@.tmp $@ + $(AM_V_GEN)$(run_python) $^ --meta-flow > $@.tmp && mv $@.tmp $@ lib/meta-flow.lo: lib/meta-flow.inc -CLEANFILES += lib/meta-flow.inc +lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h + $(AM_V_GEN)$(run_python) $^ --nx-match > $@.tmp && mv $@.tmp $@ +lib/nx-match.lo: lib/nx-match.inc +CLEANFILES += lib/meta-flow.inc lib/nx-match.inc EXTRA_DIST += build-aux/extract-ofp-fields lib/ofp-actions.inc1: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c diff --git a/lib/meta-flow.c b/lib/meta-flow.c index b59aae7d9..5056be5ae 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -25,6 +25,7 @@ #include "classifier.h" #include "dynamic-string.h" +#include "nx-match.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ovs-thread.h" @@ -50,16 +51,6 @@ const struct mf_field mf_fields[MFF_N_IDS] = { #include "meta-flow.inc" }; -/* Maps an NXM or OXM header value to an mf_field. */ -struct nxm_field { - struct hmap_node hmap_node; /* In 'all_fields' hmap. */ - uint32_t header; /* NXM or OXM header value. */ - const struct mf_field *mf; -}; - -/* Contains 'struct nxm_field's. */ -static struct hmap all_fields; - /* Maps from an mf_field's 'name' or 'extra_name' to the mf_field. */ static struct shash mf_by_name; @@ -67,7 +58,6 @@ static struct shash mf_by_name; * controller and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -const struct mf_field *mf_from_nxm_header__(uint32_t header); static void nxm_init(void); /* Returns the field with the given 'name', or a null pointer if no field has @@ -79,46 +69,17 @@ mf_from_name(const char *name) return shash_find_data(&mf_by_name, name); } -static void -add_nxm_field(uint32_t header, const struct mf_field *mf) -{ - struct nxm_field *f; - - f = xmalloc(sizeof *f); - hmap_insert(&all_fields, &f->hmap_node, hash_int(header, 0)); - f->header = header; - f->mf = mf; -} - -static void -nxm_init_add_field(const struct mf_field *mf, uint32_t header) -{ - if (header) { - ovs_assert(!mf_from_nxm_header__(header)); - add_nxm_field(header, mf); - if (mf->maskable != MFM_NONE) { - add_nxm_field(NXM_MAKE_WILD_HEADER(header), mf); - } - } -} - static void nxm_do_init(void) { int i; - hmap_init(&all_fields); shash_init(&mf_by_name); for (i = 0; i < MFF_N_IDS; i++) { const struct mf_field *mf = &mf_fields[i]; ovs_assert(mf->id == i); /* Fields must be in the enum order. */ - nxm_init_add_field(mf, mf->nxm_header); - if (mf->oxm_header != mf->nxm_header) { - nxm_init_add_field(mf, mf->oxm_header); - } - shash_add_once(&mf_by_name, mf->name, mf); if (mf->extra_name) { shash_add_once(&mf_by_name, mf->extra_name, mf); @@ -133,37 +94,6 @@ nxm_init(void) pthread_once(&once, nxm_do_init); } -const struct mf_field * -mf_from_nxm_header(uint32_t header) -{ - nxm_init(); - return mf_from_nxm_header__(header); -} - -const struct mf_field * -mf_from_nxm_header__(uint32_t header) -{ - const struct nxm_field *f; - - HMAP_FOR_EACH_IN_BUCKET (f, hmap_node, hash_int(header, 0), &all_fields) { - if (f->header == header) { - return f->mf; - } - } - - return NULL; -} - -uint32_t -mf_oxm_header(enum mf_field_id id, enum ofp_version oxm_version) -{ - const struct mf_field *field = mf_from_id(id); - - return (oxm_version >= field->oxm_version - ? field->oxm_header - : field->nxm_header); -} - /* Returns true if 'wc' wildcards all the bits in field 'mf', false if 'wc' * specifies at least one bit in the field. * @@ -2230,144 +2160,6 @@ mf_get_subfield(const struct mf_subfield *sf, const struct flow *flow) return bitwise_get(&value, sf->field->n_bytes, sf->ofs, sf->n_bits); } -/* Formats 'sf' into 's' in a format normally acceptable to - * mf_parse_subfield(). (It won't be acceptable if sf->field is NULL or if - * sf->field has no NXM name.) */ -void -mf_format_subfield(const struct mf_subfield *sf, struct ds *s) -{ - if (!sf->field) { - ds_put_cstr(s, ""); - } else if (sf->field->nxm_name) { - ds_put_cstr(s, sf->field->nxm_name); - } else if (sf->field->nxm_header) { - uint32_t header = sf->field->nxm_header; - ds_put_format(s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header)); - } else { - ds_put_cstr(s, sf->field->name); - } - - if (sf->field && sf->ofs == 0 && sf->n_bits == sf->field->n_bits) { - ds_put_cstr(s, "[]"); - } else if (sf->n_bits == 1) { - ds_put_format(s, "[%d]", sf->ofs); - } else { - ds_put_format(s, "[%d..%d]", sf->ofs, sf->ofs + sf->n_bits - 1); - } -} - -static const struct mf_field * -mf_parse_subfield_name(const char *name, int name_len, bool *wild) -{ - int i; - - *wild = name_len > 2 && !memcmp(&name[name_len - 2], "_W", 2); - if (*wild) { - name_len -= 2; - } - - for (i = 0; i < MFF_N_IDS; i++) { - const struct mf_field *mf = mf_from_id(i); - - if (mf->nxm_name - && !strncmp(mf->nxm_name, name, name_len) - && mf->nxm_name[name_len] == '\0') { - return mf; - } - if (mf->oxm_name - && !strncmp(mf->oxm_name, name, name_len) - && mf->oxm_name[name_len] == '\0') { - return mf; - } - } - - return NULL; -} - -/* Parses a subfield from the beginning of '*sp' into 'sf'. If successful, - * returns NULL and advances '*sp' to the first byte following the parsed - * string. On failure, returns a malloc()'d error message, does not modify - * '*sp', and does not properly initialize 'sf'. - * - * The syntax parsed from '*sp' takes the form "header[start..end]" where - * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) - * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" - * may both be omitted (the [] are still required) to indicate an entire - * field. */ -char * WARN_UNUSED_RESULT -mf_parse_subfield__(struct mf_subfield *sf, const char **sp) -{ - const struct mf_field *field; - const char *name; - int start, end; - const char *s; - int name_len; - bool wild; - - s = *sp; - name = s; - name_len = strcspn(s, "["); - if (s[name_len] != '[') { - return xasprintf("%s: missing [ looking for field name", *sp); - } - - field = mf_parse_subfield_name(name, name_len, &wild); - if (!field) { - return xasprintf("%s: unknown field `%.*s'", *sp, name_len, s); - } - - 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 [] or " - "[..]", *sp); - } - s = strchr(s, ']') + 1; - - if (start > end) { - return xasprintf("%s: starting bit %d is after ending bit %d", - *sp, start, end); - } else if (start >= field->n_bits) { - return xasprintf("%s: starting bit %d is not valid because field is " - "only %d bits wide", *sp, start, field->n_bits); - } else if (end >= field->n_bits){ - return xasprintf("%s: ending bit %d is not valid because field is " - "only %d bits wide", *sp, end, field->n_bits); - } - - sf->field = field; - sf->ofs = start; - sf->n_bits = end - start + 1; - - *sp = s; - return NULL; -} - -/* Parses a subfield from the entirety of 's' into 'sf'. Returns NULL if - * successful, otherwise a malloc()'d string describing the error. The caller - * is responsible for freeing the returned string. - * - * The syntax parsed from 's' takes the form "header[start..end]" where - * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) - * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" - * may both be omitted (the [] are still required) to indicate an entire - * field. */ -char * WARN_UNUSED_RESULT -mf_parse_subfield(struct mf_subfield *sf, const char *s) -{ - char *error = mf_parse_subfield__(sf, &s); - if (!error && s[0]) { - error = xstrdup("unexpected input following field syntax"); - } - return error; -} - void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s) { diff --git a/lib/meta-flow.h b/lib/meta-flow.h index 2dd087a1d..7cdacca16 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -1448,39 +1448,6 @@ struct mf_field { enum mf_prereqs prereqs; bool writable; /* May be written by actions? */ - /* NXM and OXM properties. - * - * There are the following possibilities for these members for a given - * mf_field: - * - * - Neither NXM nor OXM defines such a field: these members will all be - * zero or NULL. - * - * - NXM and OXM both define such a field: nxm_header and oxm_header will - * both be nonzero and different, similarly for nxm_name and oxm_name. - * In this case, 'oxm_version' is significant: if it is greater than - * OFP12_VERSION, then only that version of OpenFlow introduced this - * OXM header, so ovs-vswitchd should send 'nxm_header' instead with - * earlier protocol versions to avoid confusing controllers that were - * using a previous Open vSwitch extension. - * - * - Only NXM or only OXM defines such a field: nxm_header and oxm_header - * will both have the same value (either an OXM_* or NXM_* value) and - * similarly for nxm_name and oxm_name. - * - * Thus, 'nxm_header' is the appropriate header to use when outputting an - * NXM formatted match, since it will be an NXM_* constant when possible - * for compatibility with OpenFlow implementations that expect that, with - * OXM_* constants used for fields that OXM adds. Conversely, 'oxm_header' - * is the header to use when outputting an OXM formatted match to an - * OpenFlow connection of version 'oxm_version' or above (and otherwise - * 'nxm_header'). */ - uint32_t nxm_header; /* An NXM_* (or OXM_*) constant. */ - const char *nxm_name; /* The nxm_header constant's name. */ - uint32_t oxm_header; /* An OXM_* (or NXM_*) constant. */ - const char *oxm_name; /* The oxm_header constant's name */ - enum ofp_version oxm_version; /* OpenFlow version that added oxm_header. */ - /* Usable protocols. * * NXM and OXM are extensible, allowing later extensions to be sent in @@ -1536,8 +1503,6 @@ BUILD_ASSERT_DECL(sizeof(union mf_value) == sizeof (union mf_subvalue)); /* Finding mf_fields. */ const struct mf_field *mf_from_name(const char *name); -const struct mf_field *mf_from_nxm_header(uint32_t nxm_header); -const struct mf_field *mf_from_nxm_name(const char *nxm_name); static inline const struct mf_field * mf_from_id(enum mf_field_id id) @@ -1547,9 +1512,6 @@ mf_from_id(enum mf_field_id id) return &mf_fields[id]; } -/* NXM and OXM protocol headers. */ -uint32_t mf_oxm_header(enum mf_field_id, enum ofp_version oxm_version); - /* Inspecting wildcarded bits. */ bool mf_is_all_wild(const struct mf_field *, const struct flow_wildcards *); @@ -1601,12 +1563,6 @@ void mf_read_subfield(const struct mf_subfield *, const struct flow *, uint64_t mf_get_subfield(const struct mf_subfield *, const struct flow *); -void mf_format_subfield(const struct mf_subfield *, struct ds *); -char *mf_parse_subfield__(struct mf_subfield *sf, const char **s) - WARN_UNUSED_RESULT; -char *mf_parse_subfield(struct mf_subfield *, const char *s) - WARN_UNUSED_RESULT; - enum ofperr mf_check_src(const struct mf_subfield *, const struct flow *); enum ofperr mf_check_dst(const struct mf_subfield *, const struct flow *); diff --git a/lib/nx-match.c b/lib/nx-match.c index 4b9a9c4b0..38233dbdb 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -22,6 +22,7 @@ #include "classifier.h" #include "dynamic-string.h" +#include "hmap.h" #include "meta-flow.h" #include "ofp-actions.h" #include "ofp-errors.h" @@ -29,87 +30,313 @@ #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "packets.h" +#include "shash.h" #include "unaligned.h" #include "util.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(nx_match); +/* + * OXM Class IDs. + * The high order bit differentiate reserved classes from member classes. + * Classes 0x0000 to 0x7FFF are member classes, allocated by ONF. + * Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation. + */ +enum ofp12_oxm_class { + OFPXMC12_NXM_0 = 0x0000, /* Backward compatibility with NXM */ + OFPXMC12_NXM_1 = 0x0001, /* Backward compatibility with NXM */ + OFPXMC12_OPENFLOW_BASIC = 0x8000, /* Basic class for OpenFlow */ + OFPXMC15_PACKET_REGS = 0x8001, /* Packet registers (pipeline fields). */ + OFPXMC12_EXPERIMENTER = 0xffff, /* Experimenter class */ +}; + +/* Functions for extracting fields from OXM/NXM headers. */ +static int nxm_vendor(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; } + +/* Returns true if 'header' is a legacy NXM header, false if it is an OXM + * header.*/ +static bool +is_nxm_header(uint32_t header) +{ + return nxm_vendor(header) <= 1; +} + +#define NXM_HEADER(VENDOR, FIELD, HASMASK, LENGTH) \ + (((VENDOR) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH)) + +#define NXM_HEADER_FMT "%d:%d:%d:%d" +#define NXM_HEADER_ARGS(HEADER) \ + nxm_vendor(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) +{ + return NXM_HEADER(nxm_vendor(header), nxm_field(header), 0, + nxm_length(header) / 2); +} +static uint32_t +nxm_make_wild_header(uint32_t header) +{ + return NXM_HEADER(nxm_vendor(header), nxm_field(header), 1, + nxm_length(header) * 2); +} + +/* Flow cookie. + * + * This may be used to gain the OpenFlow 1.1-like ability to restrict + * certain NXM-based Flow Mod and Flow Stats Request messages to flows + * 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_W nxm_make_wild_header(NXM_NX_COOKIE) + +struct nxm_field { + uint32_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_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); + /* 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. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); +static const struct nxm_field * +mf_parse_subfield_name(const char *name, int name_len, bool *wild); + +static const struct nxm_field * +nxm_field_from_mf_field(enum mf_field_id id, enum ofp_version version) +{ + const struct nxm_field *oxm = oxm_field_by_mf_id(id); + const struct nxm_field *nxm = nxm_field_by_mf_id(id); + return oxm && (version >= oxm->version || !nxm) ? oxm : nxm; +} + +/* Returns the preferred OXM header to use for field 'id' in OpenFlow 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 +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 "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; +} + /* Returns the width of the data for a field with the given 'header', in * bytes. */ -int +static int nxm_field_bytes(uint32_t header) { - unsigned int length = NXM_LENGTH(header); - return NXM_HASMASK(header) ? length / 2 : length; + unsigned int length = nxm_length(header); + return nxm_hasmask(header) ? length / 2 : length; } -/* Returns the width of the data for a field with the given 'header', in - * bits. */ -int -nxm_field_bits(uint32_t header) +/* Returns the earliest version of OpenFlow that standardized an OXM header for + * field 'id', or UINT8_MAX if no version of OpenFlow does. */ +static enum ofp_version +mf_oxm_version(enum mf_field_id id) { - return nxm_field_bytes(header) * 8; + const struct nxm_field *oxm = oxm_field_by_mf_id(id); + return oxm ? oxm->version : UINT8_MAX; } - + /* nx_pull_match() and helpers. */ -static uint32_t -nx_entry_ok(const void *p, unsigned int match_len) +/* Given NXM/OXM value 'value' and mask 'mask' associated with 'header', checks + * 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) { - unsigned int payload_len; - ovs_be32 header_be; - uint32_t header; + unsigned int width = nxm_field_bytes(header); + unsigned int i; - if (match_len < 4) { - if (match_len) { - VLOG_DBG_RL(&rl, "nx_match ends with partial (%u-byte) nxm_header", - match_len); + for (i = 0; i < width; i++) { + if (value[i] & ~mask[i]) { + if (!VLOG_DROP_WARN(&rl)) { + VLOG_WARN_RL(&rl, "Rejecting NXM/OXM entry "NXM_HEADER_FMT " " + "with 1-bits in value for bits wildcarded by the " + "mask.", NXM_HEADER_ARGS(header)); + } + return false; } - return 0; } - memcpy(&header_be, p, 4); - header = ntohl(header_be); + return true; +} - payload_len = NXM_LENGTH(header); - if (!payload_len) { - VLOG_DBG_RL(&rl, "nxm_entry %08"PRIx32" has invalid payload " - "length 0", header); - return 0; +static bool +is_cookie_pseudoheader(uint32_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, + 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; } - if (match_len < payload_len + 4) { - VLOG_DBG_RL(&rl, "%"PRIu32"-byte nxm_entry but only " - "%u bytes left in nx_match", payload_len + 4, match_len); - return 0; + *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)); + goto error; + } + if (field) { + *field = mf_from_nxm_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 OFPERR_OFPBMC_BAD_FIELD; + } } - return header; + return 0; + +error: + *header = 0; + *field = NULL; + return OFPERR_OFPBMC_BAD_LEN; } -/* Given NXM/OXM value 'value' and mask 'mask', each 'width' bytes long, checks - * 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 enum ofperr -check_mask_consistency(const uint8_t *p, const struct mf_field *mf) +nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint32_t *header, + const struct mf_field **field, + union mf_value *value, union mf_value *mask) { - unsigned int width = mf->n_bytes; - const uint8_t *value = p + 4; - const uint8_t *mask = p + 4 + width; - unsigned int i; + enum ofperr header_error; + unsigned int payload_len; + const uint8_t *payload; + int width; - for (i = 0; i < width; i++) { - if (value[i] & ~mask[i]) { - if (!VLOG_DROP_WARN(&rl)) { - char *s = nx_match_to_string(p, width * 2 + 4); - VLOG_WARN_RL(&rl, "Rejecting NXM/OXM entry %s with 1-bits in " - "value for bits wildcarded by the mask.", s); - free(s); - } - return OFPERR_OFPBMC_BAD_WILDCARDS; + header_error = nx_pull_header__(b, allow_cookie, header, field); + if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) { + return header_error; + } + + payload_len = nxm_length(*header); + payload = ofpbuf_try_pull(b, payload_len); + if (!payload) { + VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" calls for %u-byte " + "payload but only %"PRIu32" bytes follow OXM header", + NXM_HEADER_ARGS(*header), payload_len, ofpbuf_size(b)); + return OFPERR_OFPBMC_BAD_LEN; + } + + width = nxm_field_bytes(*header); + if (nxm_hasmask(*header) + && !is_mask_consistent(*header, payload, payload + width)) { + return OFPERR_OFPBMC_BAD_WILDCARDS; + } + + memcpy(value, payload, MIN(width, sizeof *value)); + if (mask) { + if (nxm_hasmask(*header)) { + memcpy(mask, payload + width, MIN(width, sizeof *mask)); + } else { + memset(mask, 0xff, MIN(width, sizeof *mask)); + } + } else if (nxm_hasmask(*header)) { + VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" includes mask but " + "masked OXMs are not allowed here", + NXM_HEADER_ARGS(*header)); + return OFPERR_OFPBMC_BAD_MASK; + } + + return header_error; +} + +/* Attempts to pull an NXM or OXM header, value, and mask (if present) from the + * beginning of 'b'. If successful, stores a pointer to the "struct mf_field" + * corresponding to the pulled header in '*field', the value into '*value', + * and the mask into '*mask', and returns 0. On error, returns an OpenFlow + * error; in this case, some bytes might have been pulled off 'b' anyhow, and + * the output parameters might have been modified. + * + * If a NULL 'mask' is supplied, masked OXM or NXM entries are treated as + * errors (with OFPERR_OFPBMC_BAD_MASK). + */ +enum ofperr +nx_pull_entry(struct ofpbuf *b, const struct mf_field **field, + union mf_value *value, union mf_value *mask) +{ + uint32_t header; + + return nx_pull_entry__(b, false, &header, field, value, mask); +} + +/* Attempts to pull an NXM or OXM header from the beginning of 'b'. If + * successful, stores a pointer to the "struct mf_field" corresponding to the + * pulled header in '*field', stores the header's hasmask bit in '*masked' + * (true if hasmask=1, false if hasmask=0), and returns 0. On error, returns + * an OpenFlow error; in this case, some bytes might have been pulled off 'b' + * anyhow, and the output parameters might have been modified. + * + * If NULL 'masked' is supplied, masked OXM or NXM headers are treated as + * errors (with OFPERR_OFPBMC_BAD_MASK). + */ +enum ofperr +nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked) +{ + enum ofperr error; + uint32_t header; + + error = nx_pull_header__(b, false, &header, field); + if (masked) { + *masked = !error && nxm_hasmask(header); + } else if (!error && nxm_hasmask(header)) { + error = OFPERR_OFPBMC_BAD_MASK; + } + return error; +} + +static enum ofperr +nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie, + const struct mf_field **field, + union mf_value *value, union mf_value *mask) +{ + enum ofperr error; + uint32_t header; + + error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask); + if (error) { + return error; + } + if (field && *field) { + if (!mf_is_mask_valid(*field, mask)) { + VLOG_DBG_RL(&rl, "bad mask for field %s", (*field)->name); + return OFPERR_OFPBMC_BAD_MASK; + } + if (!mf_is_value_valid(*field, value)) { + VLOG_DBG_RL(&rl, "bad value for field %s", (*field)->name); + return OFPERR_OFPBMC_BAD_VALUE; } } return 0; @@ -119,7 +346,7 @@ static enum ofperr nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { - uint32_t header; + struct ofpbuf b; ovs_assert((cookie != NULL) == (cookie_mask != NULL)); @@ -127,81 +354,46 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict, if (cookie) { *cookie = *cookie_mask = htonll(0); } - if (!match_len) { - return 0; - } - for (; - (header = nx_entry_ok(p, match_len)) != 0; - p += 4 + NXM_LENGTH(header), match_len -= 4 + NXM_LENGTH(header)) { - const struct mf_field *mf; + ofpbuf_use_const(&b, p, match_len); + while (ofpbuf_size(&b)) { + const uint8_t *pos = ofpbuf_data(&b); + const struct mf_field *field; + union mf_value value; + union mf_value mask; enum ofperr error; - mf = mf_from_nxm_header(header); - if (!mf) { - if (strict) { + error = nx_pull_match_entry(&b, cookie != NULL, &field, &value, &mask); + if (error) { + if (error == OFPERR_OFPBMC_BAD_FIELD && !strict) { + continue; + } + } else if (!field) { + if (!cookie) { error = OFPERR_OFPBMC_BAD_FIELD; + } else if (*cookie_mask) { + error = OFPERR_OFPBMC_DUP_FIELD; } else { - continue; + *cookie = value.be64; + *cookie_mask = mask.be64; } - } else if (!mf_are_prereqs_ok(mf, &match->flow)) { + } else if (!mf_are_prereqs_ok(field, &match->flow)) { error = OFPERR_OFPBMC_BAD_PREREQ; - } else if (!mf_is_all_wild(mf, &match->wc)) { + } else if (!mf_is_all_wild(field, &match->wc)) { error = OFPERR_OFPBMC_DUP_FIELD; } else { - unsigned int width = mf->n_bytes; - union mf_value value; - - memcpy(&value, p + 4, width); - if (!mf_is_value_valid(mf, &value)) { - error = OFPERR_OFPBMC_BAD_VALUE; - } else if (!NXM_HASMASK(header)) { - error = 0; - mf_set_value(mf, &value, match); - } else { - union mf_value mask; - - memcpy(&mask, p + 4 + width, width); - if (!mf_is_mask_valid(mf, &mask)) { - error = OFPERR_OFPBMC_BAD_MASK; - } else { - error = check_mask_consistency(p, mf); - if (!error) { - mf_set(mf, &value, &mask, match); - } - } - } - } - - /* Check if the match is for a cookie rather than a classifier rule. */ - if ((header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W) && cookie) { - if (*cookie_mask) { - error = OFPERR_OFPBMC_DUP_FIELD; - } else { - unsigned int width = sizeof *cookie; - - memcpy(cookie, p + 4, width); - if (NXM_HASMASK(header)) { - memcpy(cookie_mask, p + 4 + width, width); - } else { - *cookie_mask = OVS_BE64_MAX; - } - error = 0; - } + mf_set(field, &value, &mask, match); } if (error) { - VLOG_DBG_RL(&rl, "bad nxm_entry %#08"PRIx32" (vendor=%"PRIu32", " - "field=%"PRIu32", hasmask=%"PRIu32", len=%"PRIu32"), " - "(%s)", header, - NXM_VENDOR(header), NXM_FIELD(header), - NXM_HASMASK(header), NXM_LENGTH(header), - ofperr_to_string(error)); + VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" " + "within match (%s)", pos - + p, ofperr_to_string(error)); return error; } } - return match_len ? OFPERR_OFPBMC_BAD_LEN : 0; + return 0; } static enum ofperr @@ -334,7 +526,7 @@ nxm_put_8m(struct ofpbuf *b, uint32_t header, uint8_t value, uint8_t mask) break; default: - nxm_put_header(b, NXM_MAKE_WILD_HEADER(header)); + nxm_put_header(b, nxm_make_wild_header(header)); ofpbuf_put(b, &value, sizeof value); ofpbuf_put(b, &mask, sizeof mask); } @@ -367,7 +559,7 @@ nxm_put_16m(struct ofpbuf *b, uint32_t header, ovs_be16 value, ovs_be16 mask) break; default: - nxm_put_16w(b, NXM_MAKE_WILD_HEADER(header), value, mask); + nxm_put_16w(b, nxm_make_wild_header(header), value, mask); break; } } @@ -399,7 +591,7 @@ nxm_put_32m(struct ofpbuf *b, uint32_t header, ovs_be32 value, ovs_be32 mask) break; default: - nxm_put_32w(b, NXM_MAKE_WILD_HEADER(header), value, mask); + nxm_put_32w(b, nxm_make_wild_header(header), value, mask); break; } } @@ -431,7 +623,7 @@ nxm_put_64m(struct ofpbuf *b, uint32_t header, ovs_be64 value, ovs_be64 mask) break; default: - nxm_put_64w(b, NXM_MAKE_WILD_HEADER(header), value, mask); + nxm_put_64w(b, nxm_make_wild_header(header), value, mask); break; } } @@ -453,7 +645,7 @@ nxm_put_eth_masked(struct ofpbuf *b, uint32_t header, if (eth_mask_is_exact(mask)) { nxm_put_eth(b, header, value); } else { - nxm_put_header(b, NXM_MAKE_WILD_HEADER(header)); + nxm_put_header(b, nxm_make_wild_header(header)); ofpbuf_put(b, value, ETH_ADDR_LEN); ofpbuf_put(b, mask, ETH_ADDR_LEN); } @@ -470,7 +662,7 @@ nxm_put_ipv6(struct ofpbuf *b, uint32_t header, nxm_put_header(b, header); ofpbuf_put(b, value, sizeof *value); } else { - nxm_put_header(b, NXM_MAKE_WILD_HEADER(header)); + nxm_put_header(b, nxm_make_wild_header(header)); ofpbuf_put(b, value, sizeof *value); ofpbuf_put(b, mask, sizeof *mask); } @@ -800,6 +992,29 @@ oxm_put_match(struct ofpbuf *b, const struct match *match, return match_len; } + +void +nx_put_header(struct ofpbuf *b, enum mf_field_id field, + enum ofp_version version, bool masked) +{ + uint32_t header = mf_oxm_header(field, version); + nxm_put_header(b, masked ? nxm_make_wild_header(header) : header); +} + +void +nx_put_entry(struct ofpbuf *b, + enum mf_field_id field, enum ofp_version version, + const union mf_value *value, const union mf_value *mask) +{ + int n_bytes = mf_from_id(field)->n_bytes; + bool masked = mask && !is_all_ones(mask, n_bytes); + + nx_put_header(b, field, version, masked); + ofpbuf_put(b, value, n_bytes); + if (masked) { + ofpbuf_put(b, mask, n_bytes); + } +} /* nx_match_to_string() and helpers. */ @@ -808,20 +1023,27 @@ static void format_nxm_field_name(struct ds *, uint32_t header); char * nx_match_to_string(const uint8_t *p, unsigned int match_len) { - uint32_t header; + struct ofpbuf b; struct ds s; if (!match_len) { return xstrdup(""); } + ofpbuf_use_const(&b, p, match_len); ds_init(&s); - while ((header = nx_entry_ok(p, match_len)) != 0) { - unsigned int length = NXM_LENGTH(header); - unsigned int value_len = nxm_field_bytes(header); - const uint8_t *value = p + 4; - const uint8_t *mask = value + value_len; - unsigned int i; + while (ofpbuf_size(&b)) { + union mf_value value; + union mf_value mask; + enum ofperr error; + uint32_t header; + int value_len; + + error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask); + if (error) { + break; + } + value_len = MIN(sizeof value, nxm_field_bytes(header)); if (s.length) { ds_put_cstr(&s, ", "); @@ -830,27 +1052,24 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len) format_nxm_field_name(&s, header); ds_put_char(&s, '('); - for (i = 0; i < value_len; i++) { - ds_put_format(&s, "%02x", value[i]); + for (int i = 0; i < value_len; i++) { + ds_put_format(&s, "%02x", ((const uint8_t *) &value)[i]); } - if (NXM_HASMASK(header)) { + if (nxm_hasmask(header)) { ds_put_char(&s, '/'); - for (i = 0; i < value_len; i++) { - ds_put_format(&s, "%02x", mask[i]); + for (int i = 0; i < value_len; i++) { + ds_put_format(&s, "%02x", ((const uint8_t *) &mask)[i]); } } ds_put_char(&s, ')'); - - p += 4 + length; - match_len -= 4 + length; } - if (match_len) { + if (ofpbuf_size(&b)) { if (s.length) { ds_put_cstr(&s, ", "); } - ds_put_format(&s, "<%u invalid bytes>", match_len); + ds_put_format(&s, "<%u invalid bytes>", ofpbuf_size(&b)); } return ds_steal_cstr(&s); @@ -894,13 +1113,20 @@ err: return ds_steal_cstr(&s); } +void +nx_format_field_name(enum mf_field_id id, enum ofp_version version, + struct ds *s) +{ + format_nxm_field_name(s, mf_oxm_header(id, version)); +} + static void format_nxm_field_name(struct ds *s, uint32_t header) { - const struct mf_field *mf = mf_from_nxm_header(header); - if (mf) { - ds_put_cstr(s, IS_OXM_HEADER(header) ? mf->oxm_name : mf->nxm_name); - if (NXM_HASMASK(header)) { + const struct nxm_field *f = nxm_field_by_header(header); + if (f) { + ds_put_cstr(s, f->name); + if (nxm_hasmask(header)) { ds_put_cstr(s, "_W"); } } else if (header == NXM_NX_COOKIE) { @@ -908,52 +1134,35 @@ format_nxm_field_name(struct ds *s, uint32_t header) } else if (header == NXM_NX_COOKIE_W) { ds_put_cstr(s, "NXM_NX_COOKIE_W"); } else { - ds_put_format(s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header)); + ds_put_format(s, "%d:%d", nxm_vendor(header), nxm_field(header)); } } +static bool +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 parse_nxm_field_name(const char *name, int name_len) { + const struct nxm_field *f; bool wild; - int i; - - /* Check whether it's a field name. */ - wild = name_len > 2 && !memcmp(&name[name_len - 2], "_W", 2); - if (wild) { - name_len -= 2; - } - - for (i = 0; i < MFF_N_IDS; i++) { - const struct mf_field *mf = mf_from_id(i); - uint32_t header; - - if (mf->nxm_name && - !strncmp(mf->nxm_name, name, name_len) && - mf->nxm_name[name_len] == '\0') { - header = mf->nxm_header; - } else if (mf->oxm_name && - !strncmp(mf->oxm_name, name, name_len) && - mf->oxm_name[name_len] == '\0') { - header = mf->oxm_header; - } else { - continue; - } + f = mf_parse_subfield_name(name, name_len, &wild); + if (f) { if (!wild) { - return header; - } else if (mf->maskable != MFM_NONE) { - return NXM_MAKE_WILD_HEADER(header); + return f->header; + } else if (mf_from_id(f->id)->maskable != MFM_NONE) { + return nxm_make_wild_header(f->header); } } - if (!strncmp("NXM_NX_COOKIE", name, name_len) && - (name_len == strlen("NXM_NX_COOKIE"))) { - if (!wild) { - return NXM_NX_COOKIE; - } else { - return NXM_NX_COOKIE_W; - } + if (streq_len(name, name_len, "NXM_NX_COOKIE")) { + return NXM_NX_COOKIE; + } else if (streq_len(name, name_len, "NXM_NX_COOKIE_W")) { + return NXM_NX_COOKIE_W; } /* Check whether it's a 32-bit field header value as hex. @@ -1006,7 +1215,7 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b) if (n != nxm_field_bytes(header)) { ovs_fatal(0, "%.2s: hex digits expected", s); } - if (NXM_HASMASK(header)) { + if (nxm_hasmask(header)) { s += strspn(s, " "); if (*s != '/') { ovs_fatal(0, "%s: missing / in masked field %.*s", @@ -1339,3 +1548,304 @@ nxm_execute_stack_pop(const struct ofpact_stack *pop, } } } + +/* Formats 'sf' into 's' in a format normally acceptable to + * mf_parse_subfield(). (It won't be acceptable if sf->field is NULL or if + * sf->field has no NXM name.) */ +void +mf_format_subfield(const struct mf_subfield *sf, struct ds *s) +{ + if (!sf->field) { + ds_put_cstr(s, ""); + } else { + const struct nxm_field *f = nxm_field_from_mf_field(sf->field->id, 0); + ds_put_cstr(s, f ? f->name : sf->field->name); + } + + if (sf->field && sf->ofs == 0 && sf->n_bits == sf->field->n_bits) { + ds_put_cstr(s, "[]"); + } else if (sf->n_bits == 1) { + ds_put_format(s, "[%d]", sf->ofs); + } else { + ds_put_format(s, "[%d..%d]", sf->ofs, sf->ofs + sf->n_bits - 1); + } +} + +static const struct nxm_field * +mf_parse_subfield_name(const char *name, int name_len, bool *wild) +{ + *wild = name_len > 2 && !memcmp(&name[name_len - 2], "_W", 2); + if (*wild) { + name_len -= 2; + } + + return nxm_field_by_name(name, name_len); +} + +/* Parses a subfield from the beginning of '*sp' into 'sf'. If successful, + * returns NULL and advances '*sp' to the first byte following the parsed + * string. On failure, returns a malloc()'d error message, does not modify + * '*sp', and does not properly initialize 'sf'. + * + * The syntax parsed from '*sp' takes the form "header[start..end]" where + * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) + * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" + * may both be omitted (the [] are still required) to indicate an entire + * field. */ +char * WARN_UNUSED_RESULT +mf_parse_subfield__(struct mf_subfield *sf, const char **sp) +{ + const struct mf_field *field; + const struct nxm_field *f; + const char *name; + int start, end; + const char *s; + int name_len; + bool wild; + + s = *sp; + name = s; + name_len = strcspn(s, "["); + if (s[name_len] != '[') { + return xasprintf("%s: missing [ looking for field name", *sp); + } + + f = mf_parse_subfield_name(name, name_len, &wild); + if (!f) { + 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 [] or " + "[..]", *sp); + } + s = strchr(s, ']') + 1; + + if (start > end) { + return xasprintf("%s: starting bit %d is after ending bit %d", + *sp, start, end); + } else if (start >= field->n_bits) { + return xasprintf("%s: starting bit %d is not valid because field is " + "only %d bits wide", *sp, start, field->n_bits); + } else if (end >= field->n_bits){ + return xasprintf("%s: ending bit %d is not valid because field is " + "only %d bits wide", *sp, end, field->n_bits); + } + + sf->field = field; + sf->ofs = start; + sf->n_bits = end - start + 1; + + *sp = s; + return NULL; +} + +/* Parses a subfield from the entirety of 's' into 'sf'. Returns NULL if + * successful, otherwise a malloc()'d string describing the error. The caller + * is responsible for freeing the returned string. + * + * The syntax parsed from 's' takes the form "header[start..end]" where + * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) + * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" + * may both be omitted (the [] are still required) to indicate an entire + * field. */ +char * WARN_UNUSED_RESULT +mf_parse_subfield(struct mf_subfield *sf, const char *s) +{ + char *error = mf_parse_subfield__(sf, &s); + if (!error && s[0]) { + error = xstrdup("unexpected input following field syntax"); + } + return error; +} + +/* Returns an bitmap in which each bit corresponds to the like-numbered field + * in the OFPXMC12_OPENFLOW_BASIC OXM class, in which the bit values are taken + * from the 'fields' bitmap. Only fields defined in OpenFlow 'version' are + * considered. + * + * This is useful for encoding OpenFlow 1.2 table stats messages. */ +ovs_be64 +oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *fields, + enum ofp_version version) +{ + uint64_t oxm_bitmap = 0; + int i; + + BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) { + uint32_t oxm = mf_oxm_header(i, version); + uint32_t vendor = nxm_vendor(oxm); + int field = nxm_field(oxm); + + if (vendor == OFPXMC12_OPENFLOW_BASIC && field < 64) { + oxm_bitmap |= UINT64_C(1) << field; + } + } + return htonll(oxm_bitmap); +} + +/* Opposite conversion from oxm_bitmap_from_mf_bitmap(). + * + * This is useful for decoding OpenFlow 1.2 table stats messages. */ +struct mf_bitmap +oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, enum ofp_version version) +{ + struct mf_bitmap fields = MF_BITMAP_INITIALIZER; + + 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); + uint32_t vendor = nxm_vendor(oxm); + int field = nxm_field(oxm); + + if (vendor == OFPXMC12_OPENFLOW_BASIC + && field < 64 + && oxm_bitmap & htonll(UINT64_C(1) << field)) { + bitmap_set1(fields.bm, id); + } + } + } + return fields; +} + +/* Returns a bitmap of fields that can be encoded in OXM and that can be + * modified with a "set_field" action. */ +struct mf_bitmap +oxm_writable_fields(void) +{ + struct mf_bitmap b = MF_BITMAP_INITIALIZER; + int i; + + for (i = 0; i < MFF_N_IDS; i++) { + if (mf_oxm_header(i, 0) && mf_from_id(i)->writable) { + bitmap_set1(b.bm, i); + } + } + return b; +} + +/* Returns a bitmap of fields that can be encoded in OXM and that can be + * matched in a flow table. */ +struct mf_bitmap +oxm_matchable_fields(void) +{ + struct mf_bitmap b = MF_BITMAP_INITIALIZER; + int i; + + for (i = 0; i < MFF_N_IDS; i++) { + if (mf_oxm_header(i, 0)) { + bitmap_set1(b.bm, i); + } + } + return b; +} + +/* Returns a bitmap of fields that can be encoded in OXM and that can be + * matched in a flow table with an arbitrary bitmask. */ +struct mf_bitmap +oxm_maskable_fields(void) +{ + struct mf_bitmap b = MF_BITMAP_INITIALIZER; + int i; + + for (i = 0; i < MFF_N_IDS; i++) { + if (mf_oxm_header(i, 0) && mf_from_id(i)->maskable == MFM_FULLY) { + bitmap_set1(b.bm, i); + } + } + return b; +} + +struct nxm_field_index { + struct hmap_node header_node; + struct hmap_node name_node; + struct nxm_field nf; +}; + +#include "nx-match.inc" + +static struct hmap nxm_header_map; +static struct hmap nxm_name_map; +static struct nxm_field *nxm_fields[MFF_N_IDS]; +static struct nxm_field *oxm_fields[MFF_N_IDS]; + +static void +nxm_init(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + if (ovsthread_once_start(&once)) { + hmap_init(&nxm_header_map); + hmap_init(&nxm_name_map); + for (struct nxm_field_index *nfi = all_nxm_fields; + nfi < &all_nxm_fields[ARRAY_SIZE(all_nxm_fields)]; nfi++) { + hmap_insert(&nxm_header_map, &nfi->header_node, + hash_int(nfi->nf.header, 0)); + hmap_insert(&nxm_name_map, &nfi->name_node, + hash_string(nfi->nf.name, 0)); + if (is_nxm_header(nfi->nf.header)) { + nxm_fields[nfi->nf.id] = &nfi->nf; + } else { + oxm_fields[nfi->nf.id] = &nfi->nf; + } + } + ovsthread_once_done(&once); + } +} + +static const struct nxm_field * +nxm_field_by_header(uint32_t header) +{ + const struct nxm_field_index *nfi; + + nxm_init(); + if (nxm_hasmask(header)) { + header = nxm_make_exact_header(header); + } + + HMAP_FOR_EACH_IN_BUCKET (nfi, header_node, hash_int(header, 0), + &nxm_header_map) { + if (header == nfi->nf.header) { + return &nfi->nf; + } + } + return NULL; +} + +static const struct nxm_field * +nxm_field_by_name(const char *name, size_t len) +{ + const struct nxm_field_index *nfi; + + nxm_init(); + HMAP_FOR_EACH_WITH_HASH (nfi, name_node, hash_bytes(name, len, 0), + &nxm_name_map) { + if (strlen(nfi->nf.name) == len && !memcmp(nfi->nf.name, name, len)) { + return &nfi->nf; + } + } + return NULL; +} + +static const struct nxm_field * +nxm_field_by_mf_id(enum mf_field_id id) +{ + nxm_init(); + return nxm_fields[id]; +} + +static const struct nxm_field * +oxm_field_by_mf_id(enum mf_field_id id) +{ + nxm_init(); + return oxm_fields[id]; +} + diff --git a/lib/nx-match.h b/lib/nx-match.h index 559ff05e7..b3f9694ee 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -22,13 +22,12 @@ #include #include "compiler.h" #include "flow.h" +#include "meta-flow.h" #include "ofp-errors.h" #include "openvswitch/types.h" struct ds; struct match; -struct mf_field; -struct mf_subfield; struct ofpact_reg_move; struct ofpact_reg_load; struct ofpact_stack; @@ -42,6 +41,12 @@ struct nx_action_reg_move; * See include/openflow/nicira-ext.h for NXM specification. */ +void mf_format_subfield(const struct mf_subfield *, struct ds *); +char *mf_parse_subfield__(struct mf_subfield *sf, const char **s) + WARN_UNUSED_RESULT; +char *mf_parse_subfield(struct mf_subfield *, const char *s) + WARN_UNUSED_RESULT; + enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len, struct match *, ovs_be64 *cookie, ovs_be64 *cookie_mask); @@ -54,11 +59,33 @@ int nx_put_match(struct ofpbuf *, const struct match *, ovs_be64 cookie, ovs_be64 cookie_mask); int oxm_put_match(struct ofpbuf *, const struct match *, enum ofp_version); +/* Decoding and encoding OXM/NXM headers (just a field ID) or entries (a field + * ID followed by a value and possibly a mask). */ +enum ofperr nx_pull_entry(struct ofpbuf *, const struct mf_field **, + union mf_value *value, union mf_value *mask); +enum ofperr nx_pull_header(struct ofpbuf *, const struct mf_field **, + bool *masked); +void nx_put_entry(struct ofpbuf *, enum mf_field_id, enum ofp_version, + const union mf_value *value, const union mf_value *mask); +void nx_put_header(struct ofpbuf *, enum mf_field_id, enum ofp_version, + bool masked); + +/* NXM and OXM protocol headers values. + * + * These are often alternatives to nx_pull_entry/header() and + * 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); +const struct mf_field *mf_from_nxm_header(uint32_t nxm_header); + char *nx_match_to_string(const uint8_t *, unsigned int match_len); char *oxm_match_to_string(const struct ofpbuf *, unsigned int match_len); int nx_match_from_string(const char *, struct ofpbuf *); int oxm_match_from_string(const char *, struct ofpbuf *); +void nx_format_field_name(enum mf_field_id, enum ofp_version, struct ds *); + char *nxm_parse_reg_move(struct ofpact_reg_move *, const char *) WARN_UNUSED_RESULT; char *nxm_parse_reg_load(struct ofpact_reg_load *, const char *) @@ -97,8 +124,12 @@ void nxm_execute_stack_pop(const struct ofpact_stack *, struct flow *, struct flow_wildcards *, struct ofpbuf *); -int nxm_field_bytes(uint32_t header); -int nxm_field_bits(uint32_t header); +ovs_be64 oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *, enum ofp_version); +struct mf_bitmap oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, + enum ofp_version); +struct mf_bitmap oxm_writable_fields(void); +struct mf_bitmap oxm_matchable_fields(void); +struct mf_bitmap oxm_maskable_fields(void); /* Dealing with the 'ofs_nbits' members in several Nicira extensions. */ diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 39c5a29b5..ee4f7cfb4 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -301,6 +301,7 @@ OVS_INSTRUCTIONS static void ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs); +static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs); static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len, uint32_t allowed_ovsinsts); @@ -730,7 +731,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(output_reg->src.field->nxm_header); + naor->src = htonl(mf_oxm_header(output_reg->src.field->id, 0)); naor->max_len = htons(output_reg->max_len); } @@ -929,7 +930,7 @@ encode_BUNDLE(const struct ofpact_bundle *bundle, if (bundle->dst.field) { nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, bundle->dst.n_bits); - nab->dst = htonl(bundle->dst.field->nxm_header); + nab->dst = htonl(mf_oxm_header(bundle->dst.field->id, 0)); } slaves = ofpbuf_put_zeros(out, slaves_len); @@ -1809,8 +1810,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(move->src.field->nxm_header); - narm->dst = htonl(move->dst.field->nxm_header); + narm->src = htonl(mf_oxm_header(move->src.field->id, 0)); + narm->dst = htonl(mf_oxm_header(move->dst.field->id, 0)); } } @@ -1916,7 +1917,7 @@ encode_REG_LOAD(const struct ofpact_reg_load *load, narl = put_NXAST_REG_LOAD(out); narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits); - narl->dst = htonl(load->dst.field->nxm_header); + narl->dst = htonl(mf_oxm_header(load->dst.field->id, 0)); narl->value = load->subvalue.be64[1]; } @@ -1937,11 +1938,12 @@ format_REG_LOAD(const struct ofpact_reg_load *a, struct ds *s) struct ofp12_action_set_field { ovs_be16 type; /* OFPAT12_SET_FIELD. */ ovs_be16 len; /* Length is padded to 64 bits. */ - ovs_be32 dst; /* OXM TLV header */ /* Followed by: - * - Exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7) - * bytes of all-zero bytes - */ + * - An OXM header and value (no mask allowed). + * - Enough 0-bytes to pad out to a multiple of 64 bits. + * + * The "pad" member is the beginning of the above. */ + uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); @@ -1949,47 +1951,48 @@ static enum ofperr decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, struct ofpbuf *ofpacts) { - uint16_t oasf_len = ntohs(oasf->len); - uint32_t oxm_header = ntohl(oasf->dst); - uint8_t oxm_length = NXM_LENGTH(oxm_header); struct ofpact_set_field *sf; - const struct mf_field *mf; + enum ofperr error; + struct ofpbuf b; - /* ofp12_action_set_field is padded to 64 bits by zero */ - if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) { - return OFPERR_OFPBAC_BAD_SET_LEN; + sf = ofpact_put_SET_FIELD(ofpacts); + + ofpbuf_use_const(&b, oasf, ntohs(oasf->len)); + ofpbuf_pull(&b, 4); + error = nx_pull_entry(&b, &sf->field, &sf->value, NULL); + if (error) { + /* OF1.5 specifically says to use OFPBAC_BAD_SET_MASK in this case. */ + return (error == OFPERR_OFPBMC_BAD_MASK + ? OFPERR_OFPBAC_BAD_SET_MASK + : error); } - if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length, - oasf_len - oxm_length - sizeof *oasf)) { + + if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } - if (NXM_HASMASK(oxm_header)) { - return OFPERR_OFPBAC_BAD_SET_MASK; - } - mf = mf_from_nxm_header(oxm_header); - if (!mf) { - return OFPERR_OFPBAC_BAD_SET_TYPE; + /* OpenFlow says specifically that one may not set OXM_OF_IN_PORT via + * Set-Field. */ + if (sf->field->id == MFF_IN_PORT_OXM) { + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } - ovs_assert(mf->n_bytes == oxm_length); + /* oxm_length is now validated to be compatible with mf_value. */ - if (!mf->writable) { - VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name); + if (!sf->field->writable) { + VLOG_WARN_RL(&rl, "destination field %s is not writable", + sf->field->name); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } - sf = ofpact_put_SET_FIELD(ofpacts); - sf->field = mf; - memcpy(&sf->value, oasf + 1, mf->n_bytes); /* The value must be valid for match and must have the OFPVID_PRESENT bit * on for OXM_OF_VLAN_VID. */ - if (!mf_is_value_valid(mf, &sf->value) - || (mf->id == MFF_VLAN_VID + if (!mf_is_value_valid(sf->field, &sf->value) + || (sf->field->id == MFF_VLAN_VID && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) { struct ds ds = DS_EMPTY_INITIALIZER; - mf_format(mf, &sf->value, NULL, &ds); + mf_format(sf->field, &sf->value, NULL, &ds); VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", - mf->name, ds_cstr(&ds)); + sf->field->name, ds_cstr(&ds)); ds_destroy(&ds); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; @@ -1999,29 +2002,27 @@ decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, - enum mf_field_id field, uint64_t value) + enum mf_field_id field, uint64_t value_) { - const struct mf_field *mf = mf_from_id(field); - struct ofp12_action_set_field *oasf; - ovs_be64 n_value; + int n_bytes = mf_from_id(field)->n_bytes; + size_t start_ofs = ofpbuf_size(openflow); + union mf_value value; - oasf = put_OFPAT12_SET_FIELD(openflow); - oasf->dst = htonl(mf_oxm_header(mf->id, ofp_version)); - oasf->len = htons(sizeof *oasf + 8); + value.be64 = htonll(value_ << (8 * (8 - n_bytes))); - ovs_assert(mf->n_bytes <= 8); - if (mf->n_bytes < 8) { - value <<= 8 * (8 - mf->n_bytes); - } - n_value = htonll(value); - ofpbuf_put(openflow, &n_value, 8); + put_OFPAT12_SET_FIELD(openflow); + ofpbuf_set_size(openflow, ofpbuf_size(openflow) - 4); + nx_put_entry(openflow, field, ofp_version, &value, NULL); + pad_ofpat(openflow, start_ofs); } + /* Convert 'sf' to one or two REG_LOADs. */ static void set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) { const struct mf_field *mf = sf->field; + ovs_be32 header = htonl(mf_oxm_header(mf->id, 0)); struct nx_action_reg_load *narl; if (mf->n_bits > 64) { @@ -2030,17 +2031,17 @@ set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) /* Lower bits first. */ narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(0, 64); - narl->dst = htonl(mf->nxm_header); + narl->dst = header; memcpy(&narl->value, &sf->value.ipv6.s6_addr[8], sizeof narl->value); /* Higher bits next. */ narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(64, mf->n_bits - 64); - narl->dst = htonl(mf->nxm_header); + narl->dst = header; memcpy(&narl->value, &sf->value.ipv6.s6_addr[0], sizeof narl->value); } else { narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(0, mf->n_bits); - narl->dst = htonl(mf->nxm_header); + narl->dst = header; memset(&narl->value, 0, 8 - mf->n_bytes); memcpy((char*)&narl->value + (8 - mf->n_bytes), &sf->value, mf->n_bytes); @@ -2175,16 +2176,13 @@ encode_SET_FIELD(const struct ofpact_set_field *sf, if (ofp_version < OFP12_VERSION) { set_field_to_legacy_openflow(sf, ofp_version, out); } else { - uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8); - struct ofp12_action_set_field *oasf; - char *value; - - oasf = ofpact_put_raw(out, ofp_version, OFPAT_RAW12_SET_FIELD, 0); - oasf->dst = htonl(mf_oxm_header(sf->field->id, ofp_version)); - oasf->len = htons(sizeof *oasf + padded_value_len); + /* Use Set-Field. */ + size_t start_ofs = ofpbuf_size(out); - value = ofpbuf_put_zeros(out, padded_value_len); - memcpy(value, &sf->value, sf->field->n_bytes); + put_OFPAT12_SET_FIELD(out); + ofpbuf_set_size(out, ofpbuf_size(out) - 4); + nx_put_entry(out, sf->field->id, ofp_version, &sf->value, NULL); + pad_ofpat(out, start_ofs); } } @@ -2309,7 +2307,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(stack_action->subfield.field->nxm_header); + nasp->field = htonl(mf_oxm_header(stack_action->subfield.field->id, 0)); } static void @@ -3528,7 +3526,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, spec->src.field->nxm_header); + put_u32(out, mf_oxm_header(spec->src.field->id, 0)); put_u16(out, spec->src.ofs); } else { size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); @@ -3540,17 +3538,12 @@ 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, spec->dst.field->nxm_header); + put_u32(out, mf_oxm_header(spec->dst.field->id, 0)); put_u16(out, spec->dst.ofs); } } - if ((ofpbuf_size(out) - start_ofs) % 8) { - ofpbuf_put_zeros(out, 8 - (ofpbuf_size(out) - start_ofs) % 8); - } - - nal = ofpbuf_at_assert(out, start_ofs, sizeof *nal); - nal->len = htons(ofpbuf_size(out) - start_ofs); + pad_ofpat(out, start_ofs); } static char * WARN_UNUSED_RESULT @@ -3670,7 +3663,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(mp->dst.field->nxm_header); + nam->dst = htonl(mf_oxm_header(mp->dst.field->id, 0)); } static char * WARN_UNUSED_RESULT @@ -6219,3 +6212,15 @@ ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, return oah; } + +static void +pad_ofpat(struct ofpbuf *openflow, size_t start_ofs) +{ + struct ofp_action_header *oah; + + ofpbuf_put_zeros(openflow, PAD_SIZE(ofpbuf_size(openflow) - start_ofs, 8)); + + oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah); + oah->len = htons(ofpbuf_size(openflow) - start_ofs); +} + diff --git a/lib/ofp-util.c b/lib/ofp-util.c index ff84abbea..d765d0344 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -4560,38 +4560,6 @@ parse_table_features_next_table(struct ofpbuf *payload, return 0; } -static enum ofperr -parse_oxm(struct ofpbuf *b, bool loose, - const struct mf_field **fieldp, bool *hasmask) -{ - ovs_be32 *oxmp; - uint32_t oxm; - - oxmp = ofpbuf_try_pull(b, sizeof *oxmp); - if (!oxmp) { - return OFPERR_OFPBPC_BAD_LEN; - } - oxm = ntohl(*oxmp); - - /* Determine '*hasmask'. If 'oxm' is masked, convert it to the equivalent - * unmasked version, because the table of OXM fields we support only has - * masked versions of fields that we support with masks, but we should be - * able to parse the masked versions of those here. */ - *hasmask = NXM_HASMASK(oxm); - if (*hasmask) { - if (NXM_LENGTH(oxm) & 1) { - return OFPERR_OFPBPC_BAD_VALUE; - } - oxm = NXM_HEADER(NXM_VENDOR(oxm), NXM_FIELD(oxm), NXM_LENGTH(oxm) / 2); - } - - *fieldp = mf_from_nxm_header(oxm); - if (!*fieldp) { - log_property(loose, "unknown OXM field %#"PRIx32, ntohl(*oxmp)); - } - return *fieldp ? 0 : OFPERR_OFPBMC_BAD_FIELD; -} - static enum ofperr parse_oxms(struct ofpbuf *payload, bool loose, struct mf_bitmap *exactp, struct mf_bitmap *maskedp) @@ -4604,7 +4572,7 @@ parse_oxms(struct ofpbuf *payload, bool loose, enum ofperr error; bool hasmask; - error = parse_oxm(payload, loose, &field, &hasmask); + error = nx_pull_header(payload, &field, &hasmask); if (!error) { bitmap_set1(hasmask ? masked.bm : exact.bm, field->id); } else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) { @@ -4821,15 +4789,8 @@ put_fields_property(struct ofpbuf *reply, start_ofs = start_property(reply, property); BITMAP_FOR_EACH_1 (field, MFF_N_IDS, fields->bm) { - uint32_t h_oxm = mf_oxm_header(field, version); - ovs_be32 n_oxm; - - if (masks && bitmap_is_set(masks->bm, field)) { - h_oxm = NXM_MAKE_WILD_HEADER(h_oxm); - } - - n_oxm = htonl(h_oxm); - ofpbuf_put(reply, &n_oxm, sizeof n_oxm); + nx_put_header(reply, field, version, + masks && bitmap_is_set(masks->bm, field)); } end_property(reply, start_ofs); } @@ -5357,46 +5318,6 @@ ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats, out->matched_count = htonll(stats->matched_count); } -static ovs_be64 -mf_bitmap_to_oxm_bitmap(const struct mf_bitmap *fields, - enum ofp_version version) -{ - uint64_t oxm_bitmap = 0; - int i; - - BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) { - uint32_t oxm = mf_oxm_header(i, version); - uint32_t vendor = NXM_VENDOR(oxm); - int field = NXM_FIELD(oxm); - - if (vendor == OFPXMC12_OPENFLOW_BASIC && field < 64) { - oxm_bitmap |= UINT64_C(1) << field; - } - } - return htonll(oxm_bitmap); -} - -static struct mf_bitmap -mf_bitmap_from_oxm_bitmap(ovs_be64 oxm_bitmap, enum ofp_version version) -{ - struct mf_bitmap fields = MF_BITMAP_INITIALIZER; - - for (enum mf_field_id id = 0; id < MFF_N_IDS; id++) { - const struct mf_field *f = mf_from_id(id); - uint32_t oxm = f->oxm_header; - uint32_t vendor = NXM_VENDOR(oxm); - int field = NXM_FIELD(oxm); - - if (version >= f->oxm_version - && vendor == OFPXMC12_OPENFLOW_BASIC - && field < 64 - && oxm_bitmap & htonll(UINT64_C(1) << field)) { - bitmap_set1(fields.bm, id); - } - } - return fields; -} - static void ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, const struct ofputil_table_features *features, @@ -5407,16 +5328,16 @@ ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, out = ofpbuf_put_zeros(buf, sizeof *out); out->table_id = features->table_id; ovs_strlcpy(out->name, features->name, sizeof out->name); - out->match = mf_bitmap_to_oxm_bitmap(&features->match, OFP12_VERSION); - out->wildcards = mf_bitmap_to_oxm_bitmap(&features->wildcard, + out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION); + out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard, OFP12_VERSION); out->write_actions = ofpact_bitmap_to_openflow( features->nonmiss.write.ofpacts, OFP12_VERSION); out->apply_actions = ofpact_bitmap_to_openflow( features->nonmiss.apply.ofpacts, OFP12_VERSION); - out->write_setfields = mf_bitmap_to_oxm_bitmap( + out->write_setfields = oxm_bitmap_from_mf_bitmap( &features->nonmiss.write.set_fields, OFP12_VERSION); - out->apply_setfields = mf_bitmap_to_oxm_bitmap( + out->apply_setfields = oxm_bitmap_from_mf_bitmap( &features->nonmiss.apply.set_fields, OFP12_VERSION); out->metadata_match = features->metadata_match; out->metadata_write = features->metadata_write; @@ -5569,15 +5490,15 @@ ofputil_decode_ofp12_table_stats(struct ofpbuf *msg, ots->write_actions, OFP12_VERSION); features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow( ots->apply_actions, OFP12_VERSION); - features->nonmiss.write.set_fields = mf_bitmap_from_oxm_bitmap( + features->nonmiss.write.set_fields = oxm_bitmap_to_mf_bitmap( ots->write_setfields, OFP12_VERSION); - features->nonmiss.apply.set_fields = mf_bitmap_from_oxm_bitmap( + features->nonmiss.apply.set_fields = oxm_bitmap_to_mf_bitmap( ots->apply_setfields, OFP12_VERSION); features->miss = features->nonmiss; - features->match = mf_bitmap_from_oxm_bitmap(ots->match, OFP12_VERSION); - features->wildcard = mf_bitmap_from_oxm_bitmap(ots->wildcards, - OFP12_VERSION); + features->match = oxm_bitmap_to_mf_bitmap(ots->match, OFP12_VERSION); + features->wildcard = oxm_bitmap_to_mf_bitmap(ots->wildcards, + OFP12_VERSION); bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS); stats->table_id = ots->table_id; diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 127d8c3dd..2cb93b04f 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2895,28 +2895,14 @@ query_tables(struct ofproto *ofproto, struct ofputil_table_features **featuresp, struct ofputil_table_stats **statsp) { - struct mf_bitmap rw_fields = MF_BITMAP_INITIALIZER; - struct mf_bitmap match = MF_BITMAP_INITIALIZER; - struct mf_bitmap mask = MF_BITMAP_INITIALIZER; + struct mf_bitmap rw_fields = oxm_writable_fields(); + struct mf_bitmap match = oxm_matchable_fields(); + struct mf_bitmap mask = oxm_maskable_fields(); struct ofputil_table_features *features; struct ofputil_table_stats *stats; int i; - for (i = 0; i < MFF_N_IDS; i++) { - const struct mf_field *mf = mf_from_id(i); - - if (mf->writable) { - bitmap_set1(rw_fields.bm, i); - } - if (mf->oxm_header || mf->nxm_header) { - bitmap_set1(match.bm, i); - if (mf->maskable == MFM_FULLY) { - bitmap_set1(mask.bm, i); - } - } - } - features = *featuresp = xcalloc(ofproto->n_tables, sizeof *features); for (i = 0; i < ofproto->n_tables; i++) { struct ofputil_table_features *f = &features[i]; diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 9fbae5e3d..5b5a500c7 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -502,6 +502,32 @@ AT_CHECK( [0], [expout], [experr]) AT_CLEANUP +dnl Our primary goal here is to verify OpenFlow 1.2-specific changes, +dnl so the list of tests is short. +AT_SETUP([OpenFlow 1.2 action translation]) +AT_KEYWORDS([ofp-actions OF1.2]) +AT_DATA([test-data], [dnl +# actions=LOCAL +0000 0010 fffffffe 04d2 000000000000 + +# bad OpenFlow12 actions: OFPBAC_BAD_SET_MASK +& ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_SET_MASK): +& 00000000 00 19 00 18 80 00 09 0c-00 00 00 00 12 34 00 00 +& 00000010 00 00 ff ff 00 00 00 00- +0019 0018 8000090c 000000001234 00000000ffff 00000000 + +]) +sed '/^[[#&]]/d' < test-data > input.txt +sed -n 's/^# //p; /^$/p' < test-data > expout +sed -n 's/^& //p' < test-data > experr +AT_CAPTURE_FILE([input.txt]) +AT_CAPTURE_FILE([expout]) +AT_CAPTURE_FILE([experr]) +AT_CHECK( + [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-actions OpenFlow12 < input.txt], + [0], [expout], [experr]) +AT_CLEANUP + dnl Our primary goal here is to verify that OpenFlow 1.5-specific changes, dnl so the list of tests is short. AT_SETUP([OpenFlow 1.5 action translation]) @@ -513,12 +539,11 @@ AT_DATA([test-data], [dnl # actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] 001c 0018 0010 0000 0000 0008 00000002 00000802 00000000 -dnl This action has a lot more wrong with it than the hasmask bit, but -dnl the current OVS implementation checks for that first. # bad OpenFlow15 actions: OFPBAC_BAD_SET_MASK & ofp_actions|WARN|bad action at offset 0 (OFPBAC_BAD_SET_MASK): -& 00000000 00 19 00 08 00 00 01 00- -0019 0008 0000 0100 +& 00000000 00 19 00 18 80 00 09 0c-00 00 00 00 12 34 00 00 +& 00000010 00 00 ff ff 00 00 00 00- +0019 0018 8000090c 000000001234 00000000ffff 00000000 ]) sed '/^[[#&]]/d' < test-data > input.txt diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 4269311f3..e5c0e9808 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -617,7 +617,7 @@ NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS(0131) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00F0/0FF0) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(01E2/ffff) -NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_NX_TCP_FLAGS(4321) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_NX_TCP_FLAGS(0fff) # UDP source port NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732) @@ -654,7 +654,7 @@ NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/FFFFFF00) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00) NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014) -NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000) +NXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # ARP destination protocol address NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014) @@ -687,7 +687,7 @@ NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81200/FFFFFF00) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/aaaaaa00) NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_SPA_W(C0a81234/ffffffff) NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014) -NXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000) +NXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # RARP destination protocol address NXM_OF_ETH_TYPE(8035) NXM_OF_ARP_TPA(ac100014) @@ -714,7 +714,7 @@ NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/fffffff NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/5a5a5a5a5a5a5a5a0000000000000000) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffffffffffffffff) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000) -NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/00000000000000000000000000000000) +NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffffffff000000000000) # IPv6 destination NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005) @@ -783,7 +783,7 @@ NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_FRAG(f3) NXM_NX_COOKIE(00000000abcdef01) NXM_NX_COOKIE_W(84200000abcdef01/84200000FFFFFFFF) NXM_NX_COOKIE_W(84200000abcdef01/ffffffffffffffff) -NXM_NX_COOKIE_W(84200000abcdef01/0000000000000000) +NXM_NX_COOKIE_W(0000000000000000/0000000000000000) # Tunnel ID. NXM_NX_TUN_ID(00000000abcdef01) @@ -798,7 +798,7 @@ NXM_NX_REG0_W(a0e0d050/ffffffff) NXM_NX_REG0_W(00000000/00000000) # Invalid field number. -01020304(1111/2222) +01020304(1111/3333) # Unimplemented registers. # @@ -1096,7 +1096,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 NXM_OF_ETH_DST_W(ffffffffffff/010000000000) with 1-bits in value for bits wildcarded by the mask. +nx_match|WARN|Rejecting NXM/OXM entry 0:1:1:12 with 1-bits in value for bits wildcarded by the mask. ]) # Check that there wasn't any other stderr output. @@ -1647,15 +1647,16 @@ AT_CLEANUP 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/2222), NXM_OF_ETH_TYPE(0800) +NXM_OF_IN_PORT(0001), 01020304(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 ]) -AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl +AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' -vnx_match parse-nx-match < nx-match.txt], [0], [dnl NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800) +], [nx_match|DBG|OXM header 258:1:1:4 is unknown ]) AT_CLEANUP @@ -1715,7 +1716,7 @@ OXM_OF_VLAN_VID_W(1000/1000), OXM_OF_VLAN_PCP(01) # Packets with any VID, PCP=1 OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(f0) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(41) OXM_OF_ETH_TYPE(0800) OXM_OF_IP_DSCP(3f) -OXM_OF_IP_DSCP(f0) +OXM_OF_IP_DSCP(3f) # IP ECN OXM_OF_ETH_TYPE(0800) OXM_OF_IP_ECN(03) @@ -1808,7 +1809,7 @@ OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81200/FFFFFF00) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(C0a81234/FFFFFFFF) OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_SPA_W(00000000/00000000) OXM_OF_ETH_TYPE(0800) OXM_OF_ARP_SPA(ac100014) -OXM_OF_ARP_SPA_W(C0D8fedc/FFFF0000) +OXM_OF_ARP_SPA_W(C0D80000/FFFF0000) # ARP destination protocol address OXM_OF_ETH_TYPE(0806) OXM_OF_ARP_TPA(ac100014) @@ -1895,7 +1896,7 @@ OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d050 OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000) # Invalid field number. -01020304(1111/2222) +01020304(1111/3333) ]) AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl @@ -2138,7 +2139,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 OXM_OF_METADATA_W(1234567890abcdef/ffff0000ffff0000) with 1-bits in value for bits wildcarded by the mask. +nx_match|WARN|Rejecting NXM/OXM entry 32768:2:1:16 with 1-bits in value for bits wildcarded by the mask. ]) # Check that there wasn't any other stderr output. @@ -2197,7 +2198,7 @@ AT_CLEANUP AT_SETUP([ovs-ofctl parse-oxm loose]) AT_KEYWORDS([oxm]) AT_DATA([oxm.txt], [dnl -OXM_OF_IN_PORT(00000001), 01020304(1111/2222), OXM_OF_ETH_TYPE(0800) +OXM_OF_IN_PORT(00000001), 01020304(1111/3333), OXM_OF_ETH_TYPE(0800) ]) AT_CHECK([ovs-ofctl --strict parse-oxm OpenFlow12 < oxm.txt], [0], [dnl diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 872f020e3..2918f049f 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -37,6 +37,7 @@ #include "mcast-snooping.h" #include "meta-flow.h" #include "netdev.h" +#include "nx-match.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" -- 2.20.1