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)
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:
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 = []
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
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
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)
+
* 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
#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. ## */
/* ## --------------------- ## */
/* 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.
*/
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
#include "classifier.h"
#include "dynamic-string.h"
+#include "nx-match.h"
#include "ofp-errors.h"
#include "ofp-util.h"
#include "ovs-thread.h"
#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;
* 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
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);
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.
*
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, "<unknown>");
- } 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 [<bit>] or "
- "[<start>..<end>]", *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)
{
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
/* 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)
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 *);
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 *);
#include "classifier.h"
#include "dynamic-string.h"
+#include "hmap.h"
#include "meta-flow.h"
#include "ofp-actions.h"
#include "ofp-errors.h"
#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;
}
-\f
+ \f
/* 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;
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));
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
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);
}
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;
}
}
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;
}
}
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;
}
}
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);
}
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);
}
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);
+ }
+}
\f
/* nx_match_to_string() and helpers. */
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("<any>");
}
+ 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, ", ");
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);
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) {
} 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.
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",
}
}
}
+\f
+/* 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, "<unknown>");
+ } 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 [<bit>] or "
+ "[<start>..<end>]", *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;
+}
+\f
+/* 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;
+}
+\f
+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];
+}
+
#include <netinet/in.h>
#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;
* 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);
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 *)
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. */
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);
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);
}
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);
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));
}
}
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];
}
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);
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;
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) {
/* 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);
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);
}
}
{
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
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);
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
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
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);
+}
+
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)
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) {
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);
}
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,
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;
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;
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];
[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])
# 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
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)
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)
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)
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)
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)
NXM_NX_REG0_W(00000000/00000000)
# Invalid field number.
-01020304(1111/2222)
+01020304(1111/3333)
# Unimplemented registers.
#
# 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.
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
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)
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)
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
# 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.
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
#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"