X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fnx-match.c;h=72fa29bb653230003e9a366aae2578571939a566;hb=ca6ba70092b1528e12d3140d70232175a13c335d;hp=de062eb30715c536cf3231bc946b29f4655affe9;hpb=0429d9599ca7382afecdf2d9d62fffcfa495fbd7;p=cascardo%2Fovs.git diff --git a/lib/nx-match.c b/lib/nx-match.c index de062eb30..72fa29bb6 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -37,6 +37,46 @@ VLOG_DEFINE_THIS_MODULE(nx_match); +/* OXM headers. + * + * + * Standard OXM/NXM + * ================ + * + * The header is 32 bits long. It looks like this: + * + * |31 16 15 9| 8 7 0 + * +----------------------------------+---------------+--+------------------+ + * | oxm_class | oxm_field |hm| oxm_length | + * +----------------------------------+---------------+--+------------------+ + * + * where hm stands for oxm_hasmask. It is followed by oxm_length bytes of + * payload. When oxm_hasmask is 0, the payload is the value of the field + * identified by the header; when oxm_hasmask is 1, the payload is a value for + * the field followed by a mask of equal length. + * + * Internally, we represent a standard OXM header as a 64-bit integer with the + * above information in the most-significant bits. + * + * + * Experimenter OXM + * ================ + * + * The header is 64 bits long. It looks like the diagram above except that a + * 32-bit experimenter ID, which we call oxm_vendor and which identifies a + * vendor, is inserted just before the payload. Experimenter OXMs are + * identified by an all-1-bits oxm_class (OFPXMC12_EXPERIMENTER). The + * oxm_length value *includes* the experimenter ID, so that the real payload is + * only oxm_length - 4 bytes long. + * + * Internally, we represent an experimenter OXM header as a 64-bit integer with + * the standard header in the upper 32 bits and the experimenter ID in the + * lower 32 bits. (It would be more convenient to swap the positions of the + * two 32-bit words, but this would be more error-prone because experimenter + * OXMs are very rarely used, so accidentally passing one through a 32-bit type + * somewhere in the OVS code would be hard to find.) + */ + /* * OXM Class IDs. * The high order bit differentiate reserved classes from member classes. @@ -51,41 +91,82 @@ enum ofp12_oxm_class { OFPXMC12_EXPERIMENTER = 0xffff, /* Experimenter class */ }; -/* Functions for extracting fields from OXM/NXM headers. */ -static int nxm_class(uint32_t header) { return header >> 16; } -static int nxm_field(uint32_t header) { return (header >> 9) & 0x7f; } -static bool nxm_hasmask(uint32_t header) { return (header & 0x100) != 0; } -static int nxm_length(uint32_t header) { return header & 0xff; } +/* Functions for extracting raw field values from OXM/NXM headers. */ +static uint32_t nxm_vendor(uint64_t header) { return header; } +static int nxm_class(uint64_t header) { return header >> 48; } +static int nxm_field(uint64_t header) { return (header >> 41) & 0x7f; } +static bool nxm_hasmask(uint64_t header) { return (header >> 40) & 1; } +static int nxm_length(uint64_t header) { return (header >> 32) & 0xff; } -/* Returns true if 'header' is a legacy NXM header, false if it is an OXM - * header.*/ static bool -is_nxm_header(uint32_t header) +is_experimenter_oxm(uint64_t header) +{ + return nxm_class(header) == OFPXMC12_EXPERIMENTER; +} + +/* The OXM header "length" field is somewhat tricky: + * + * - For a standard OXM header, the length is the number of bytes of the + * payload, and the payload consists of just the value (and mask, if + * present). + * + * - For an experimenter OXM header, the length is the number of bytes in + * the payload plus 4 (the length of the experimenter ID). That is, the + * experimenter ID is included in oxm_length. + * + * This function returns the length of the experimenter ID field in 'header'. + * That is, for an experimenter OXM (when an experimenter ID is present), it + * returns 4, and for a standard OXM (when no experimenter ID is present), it + * returns 0. */ +static int +nxm_experimenter_len(uint64_t header) { - return nxm_class(header) <= 1; + return is_experimenter_oxm(header) ? 4 : 0; } -#define NXM_HEADER(CLASS, FIELD, HASMASK, LENGTH) \ - (((CLASS) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH)) +/* Returns the number of bytes that follow the header for an NXM/OXM entry + * with the given 'header'. */ +static int +nxm_payload_len(uint64_t header) +{ + return nxm_length(header) - nxm_experimenter_len(header); +} + +/* Returns the number of bytes in the header for an NXM/OXM entry with the + * given 'header'. */ +static int +nxm_header_len(uint64_t header) +{ + return 4 + nxm_experimenter_len(header); +} + +#define NXM_HEADER(VENDOR, CLASS, FIELD, HASMASK, LENGTH) \ + (((uint64_t) (CLASS) << 48) | \ + ((uint64_t) (FIELD) << 41) | \ + ((uint64_t) (HASMASK) << 40) | \ + ((uint64_t) (LENGTH) << 32) | \ + (VENDOR)) -#define NXM_HEADER_FMT "%d:%d:%d:%d" -#define NXM_HEADER_ARGS(HEADER) \ - nxm_class(HEADER), nxm_field(HEADER), \ +#define NXM_HEADER_FMT "%#"PRIx32":%d:%d:%d:%d" +#define NXM_HEADER_ARGS(HEADER) \ + nxm_vendor(HEADER), nxm_class(HEADER), nxm_field(HEADER), \ nxm_hasmask(HEADER), nxm_length(HEADER) /* Functions for turning the "hasmask" bit on or off. (This also requires * adjusting the length.) */ -static uint32_t -nxm_make_exact_header(uint32_t header) +static uint64_t +nxm_make_exact_header(uint64_t header) { - return NXM_HEADER(nxm_class(header), nxm_field(header), 0, - nxm_length(header) / 2); + int new_len = nxm_payload_len(header) / 2 + nxm_experimenter_len(header); + return NXM_HEADER(nxm_vendor(header), nxm_class(header), + nxm_field(header), 0, new_len); } -static uint32_t -nxm_make_wild_header(uint32_t header) +static uint64_t +nxm_make_wild_header(uint64_t header) { - return NXM_HEADER(nxm_class(header), nxm_field(header), 1, - nxm_length(header) * 2); + int new_len = nxm_payload_len(header) * 2 + nxm_experimenter_len(header); + return NXM_HEADER(nxm_vendor(header), nxm_class(header), + nxm_field(header), 1, new_len); } /* Flow cookie. @@ -95,23 +176,23 @@ nxm_make_wild_header(uint32_t header) * with specific cookies. See the "nx_flow_mod" and "nx_flow_stats_request" * structure definitions for more details. This match is otherwise not * allowed. */ -#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 0, 8) +#define NXM_NX_COOKIE NXM_HEADER (0, 0x0001, 30, 0, 8) #define NXM_NX_COOKIE_W nxm_make_wild_header(NXM_NX_COOKIE) struct nxm_field { - uint32_t header; + uint64_t header; enum ofp_version version; const char *name; /* e.g. "NXM_OF_IN_PORT". */ enum mf_field_id id; }; -static const struct nxm_field *nxm_field_by_header(uint32_t header); +static const struct nxm_field *nxm_field_by_header(uint64_t header); static const struct nxm_field *nxm_field_by_name(const char *name, size_t len); -static const struct nxm_field *nxm_field_by_mf_id(enum mf_field_id); -static const struct nxm_field *oxm_field_by_mf_id(enum mf_field_id); +static const struct nxm_field *nxm_field_by_mf_id(enum mf_field_id, + enum ofp_version); -static void nx_put_header__(struct ofpbuf *, uint32_t header, bool masked); +static void nx_put_header__(struct ofpbuf *, uint64_t header, bool masked); /* Rate limit for nx_match parse errors. These always indicate a bug in the * peer and so there's not much point in showing a lot of them. */ @@ -120,59 +201,61 @@ 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 +static uint64_t mf_oxm_header(enum mf_field_id id, enum ofp_version version) { - const struct nxm_field *f = nxm_field_from_mf_field(id, version); + const struct nxm_field *f = nxm_field_by_mf_id(id, version); return f ? f->header : 0; } +/* Returns the 32-bit OXM or NXM header to use for field 'id', preferring an + * NXM legacy header over any standardized OXM header. Returns 0 if field 'id' + * cannot be expressed with a 32-bit NXM or OXM header. + * + * Whenever possible, use nx_pull_header() instead of this function, because + * this function cannot support 64-bit experimenter OXM headers. */ +uint32_t +mf_nxm_header(enum mf_field_id id) +{ + uint64_t oxm = mf_oxm_header(id, 0); + return is_experimenter_oxm(oxm) ? 0 : oxm >> 32; +} + +static const struct mf_field * +mf_from_oxm_header(uint64_t header) +{ + const struct nxm_field *f = nxm_field_by_header(header); + return f ? mf_from_id(f->id) : NULL; +} + /* Returns the "struct mf_field" that corresponds to NXM or OXM header * 'header', or NULL if 'header' doesn't correspond to any known field. */ const struct mf_field * mf_from_nxm_header(uint32_t header) { - const struct nxm_field *f = nxm_field_by_header(header); - return f ? mf_from_id(f->id) : NULL; + return mf_from_oxm_header((uint64_t) header << 32); } /* Returns the width of the data for a field with the given 'header', in * bytes. */ static int -nxm_field_bytes(uint32_t header) +nxm_field_bytes(uint64_t header) { - unsigned int length = nxm_length(header); + unsigned int length = nxm_payload_len(header); return nxm_hasmask(header) ? length / 2 : length; } - -/* 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) -{ - const struct nxm_field *oxm = oxm_field_by_mf_id(id); - return oxm ? oxm->version : UINT8_MAX; -} - + /* nx_pull_match() and helpers. */ /* 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) +is_mask_consistent(uint64_t header, const uint8_t *value, const uint8_t *mask) { unsigned int width = nxm_field_bytes(header); unsigned int i; @@ -191,28 +274,37 @@ is_mask_consistent(uint32_t header, const uint8_t *value, const uint8_t *mask) } static bool -is_cookie_pseudoheader(uint32_t header) +is_cookie_pseudoheader(uint64_t header) { return header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W; } static enum ofperr -nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint32_t *header, +nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header, const struct mf_field **field) { if (ofpbuf_size(b) < 4) { - VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry", - ofpbuf_size(b)); - goto error; + goto bad_len; } - *header = ntohl(get_unaligned_be32(ofpbuf_pull(b, 4))); - if (nxm_length(*header) == 0) { - VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has zero length", - NXM_HEADER_ARGS(*header)); + + *header = ((uint64_t) ntohl(get_unaligned_be32(ofpbuf_data(b)))) << 32; + if (is_experimenter_oxm(*header)) { + if (ofpbuf_size(b) < 8) { + goto bad_len; + } + *header = ntohll(get_unaligned_be64(ofpbuf_data(b))); + } + if (nxm_length(*header) <= nxm_experimenter_len(*header)) { + VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has invalid length %d " + "(minimum is %d)", + NXM_HEADER_ARGS(*header), nxm_length(*header), + nxm_header_len(*header) + 1); goto error; } + ofpbuf_pull(b, nxm_header_len(*header)); + if (field) { - *field = mf_from_nxm_header(*header); + *field = mf_from_oxm_header(*header); if (!*field && !(allow_cookie && is_cookie_pseudoheader(*header))) { VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" is unknown", NXM_HEADER_ARGS(*header)); @@ -222,6 +314,9 @@ nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint32_t *header, return 0; +bad_len: + VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry", + ofpbuf_size(b)); error: *header = 0; *field = NULL; @@ -229,7 +324,7 @@ error: } static enum ofperr -nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint32_t *header, +nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header, const struct mf_field **field, union mf_value *value, union mf_value *mask) { @@ -243,7 +338,7 @@ nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint32_t *header, return header_error; } - payload_len = nxm_length(*header); + payload_len = nxm_payload_len(*header); payload = ofpbuf_try_pull(b, payload_len); if (!payload) { VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" calls for %u-byte " @@ -289,7 +384,7 @@ enum ofperr nx_pull_entry(struct ofpbuf *b, const struct mf_field **field, union mf_value *value, union mf_value *mask) { - uint32_t header; + uint64_t header; return nx_pull_entry__(b, false, &header, field, value, mask); } @@ -308,7 +403,7 @@ enum ofperr nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked) { enum ofperr error; - uint32_t header; + uint64_t header; error = nx_pull_header__(b, false, &header, field); if (masked) { @@ -325,7 +420,7 @@ nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie, union mf_value *value, union mf_value *mask) { enum ofperr error; - uint32_t header; + uint64_t header; error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask); if (error) { @@ -722,7 +817,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); /* Metadata. */ if (match->wc.masks.dp_hash) { @@ -744,6 +839,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, htons(ofp_to_u16(in_port))); } } + if (match->wc.masks.actset_output) { + nxm_put_32(b, MFF_ACTSET_OUTPUT, oxm, + ofputil_port_to_ofp11(flow->actset_output)); + } /* Ethernet. */ nxm_put_eth_masked(b, MFF_ETH_SRC, oxm, @@ -915,12 +1014,12 @@ oxm_put_match(struct ofpbuf *b, const struct match *match, } static void -nx_put_header__(struct ofpbuf *b, uint32_t header, bool masked) +nx_put_header__(struct ofpbuf *b, uint64_t header, bool masked) { - uint32_t masked_header = masked ? nxm_make_wild_header(header) : header; - ovs_be32 network_header = htonl(masked_header); + uint64_t masked_header = masked ? nxm_make_wild_header(header) : header; + ovs_be64 network_header = htonll(masked_header); - ofpbuf_put(b, &network_header, sizeof network_header); + ofpbuf_put(b, &network_header, nxm_header_len(header)); } void @@ -947,7 +1046,7 @@ nx_put_entry(struct ofpbuf *b, /* nx_match_to_string() and helpers. */ -static void format_nxm_field_name(struct ds *, uint32_t header); +static void format_nxm_field_name(struct ds *, uint64_t header); char * nx_match_to_string(const uint8_t *p, unsigned int match_len) @@ -965,7 +1064,7 @@ nx_match_to_string(const uint8_t *p, unsigned int match_len) union mf_value value; union mf_value mask; enum ofperr error; - uint32_t header; + uint64_t header; int value_len; error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask); @@ -1050,7 +1149,7 @@ nx_format_field_name(enum mf_field_id id, enum ofp_version version, } static void -format_nxm_field_name(struct ds *s, uint32_t header) +format_nxm_field_name(struct ds *s, uint64_t header) { const struct nxm_field *f = nxm_field_by_header(header); if (f) { @@ -1073,7 +1172,7 @@ streq_len(const char *a, size_t a_len, const char *b) return strlen(b) == a_len && !memcmp(a, b, a_len); } -static uint32_t +static uint64_t parse_nxm_field_name(const char *name, int name_len) { const struct nxm_field *f; @@ -1094,16 +1193,24 @@ parse_nxm_field_name(const char *name, int name_len) return NXM_NX_COOKIE_W; } - /* Check whether it's a 32-bit field header value as hex. + /* Check whether it's a field header value as hex. * (This isn't ordinarily useful except for testing error behavior.) */ if (name_len == 8) { - uint32_t header; + uint64_t header; bool ok; - header = hexits_value(name, name_len, &ok); + header = hexits_value(name, name_len, &ok) << 32; if (ok) { return header; } + } else if (name_len == 16) { + uint64_t header; + bool ok; + + header = hexits_value(name, name_len, &ok); + if (ok && is_experimenter_oxm(header)) { + return header; + } } return 0; @@ -1125,7 +1232,7 @@ nx_match_from_string_raw(const char *s, struct ofpbuf *b) for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) { const char *name; - uint32_t header; + uint64_t header; int name_len; size_t n; @@ -1201,7 +1308,7 @@ oxm_match_from_string(const char *s, struct ofpbuf *b) * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT +char * OVS_WARN_UNUSED_RESULT nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s) { const char *full_s = s; @@ -1250,7 +1357,7 @@ nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow) return error; } - return mf_check_dst(&move->dst, NULL); + return mf_check_dst(&move->dst, flow); } /* nxm_execute_reg_move(). */ @@ -1265,12 +1372,18 @@ nxm_execute_reg_move(const struct ofpact_reg_move *move, mf_mask_field_and_prereqs(move->dst.field, &wc->masks); mf_mask_field_and_prereqs(move->src.field, &wc->masks); - mf_get_value(move->dst.field, flow, &dst_value); - mf_get_value(move->src.field, flow, &src_value); - bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs, - &dst_value, move->dst.field->n_bytes, move->dst.ofs, - move->src.n_bits); - mf_set_flow_value(move->dst.field, &dst_value, flow); + /* A flow may wildcard nw_frag. Do nothing if setting a transport + * header field on a packet that does not have them. */ + if (mf_are_prereqs_ok(move->dst.field, flow) + && mf_are_prereqs_ok(move->src.field, flow)) { + + mf_get_value(move->dst.field, flow, &dst_value); + mf_get_value(move->src.field, flow, &src_value); + bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs, + &dst_value, move->dst.field->n_bytes, move->dst.ofs, + move->src.n_bits); + mf_set_flow_value(move->dst.field, &dst_value, flow); + } } void @@ -1297,7 +1410,7 @@ nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data, * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ -char * WARN_UNUSED_RESULT +char * OVS_WARN_UNUSED_RESULT nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s) { char *error; @@ -1413,7 +1526,7 @@ 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); + const struct nxm_field *f = nxm_field_by_mf_id(sf->field->id, 0); ds_put_cstr(s, f ? f->name : sf->field->name); } @@ -1447,7 +1560,7 @@ mf_parse_subfield_name(const char *name, int name_len, bool *wild) * 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 +char * OVS_WARN_UNUSED_RESULT mf_parse_subfield__(struct mf_subfield *sf, const char **sp) { const struct mf_field *field; @@ -1513,7 +1626,7 @@ mf_parse_subfield__(struct mf_subfield *sf, const char **sp) * 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 +char * OVS_WARN_UNUSED_RESULT mf_parse_subfield(struct mf_subfield *sf, const char *s) { char *error = mf_parse_subfield__(sf, &s); @@ -1537,7 +1650,7 @@ oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *fields, int i; BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) { - uint32_t oxm = mf_oxm_header(i, version); + uint64_t oxm = mf_oxm_header(i, version); uint32_t class = nxm_class(oxm); int field = nxm_field(oxm); @@ -1557,8 +1670,8 @@ 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); + uint64_t oxm = mf_oxm_header(id, version); + if (oxm && version >= nxm_field_by_header(oxm)->version) { uint32_t class = nxm_class(oxm); int field = nxm_field(oxm); @@ -1621,17 +1734,17 @@ oxm_maskable_fields(void) } struct nxm_field_index { - struct hmap_node header_node; - struct hmap_node name_node; - struct nxm_field nf; + struct hmap_node header_node; /* In nxm_header_map. */ + struct hmap_node name_node; /* In nxm_name_map. */ + struct ovs_list mf_node; /* In mf_mf_map[nf.id]. */ + const 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 struct ovs_list nxm_mf_map[MFF_N_IDS]; static void nxm_init(void) @@ -1640,24 +1753,23 @@ nxm_init(void) if (ovsthread_once_start(&once)) { hmap_init(&nxm_header_map); hmap_init(&nxm_name_map); + for (int i = 0; i < MFF_N_IDS; i++) { + list_init(&nxm_mf_map[i]); + } 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; - } + list_push_back(&nxm_mf_map[nfi->nf.id], &nfi->mf_node); } ovsthread_once_done(&once); } } static const struct nxm_field * -nxm_field_by_header(uint32_t header) +nxm_field_by_header(uint64_t header) { const struct nxm_field_index *nfi; @@ -1691,16 +1803,18 @@ nxm_field_by_name(const char *name, size_t len) } static const struct nxm_field * -nxm_field_by_mf_id(enum mf_field_id id) +nxm_field_by_mf_id(enum mf_field_id id, enum ofp_version version) { - nxm_init(); - return nxm_fields[id]; -} + const struct nxm_field_index *nfi; + const struct nxm_field *f; -static const struct nxm_field * -oxm_field_by_mf_id(enum mf_field_id id) -{ nxm_init(); - return oxm_fields[id]; -} + f = NULL; + LIST_FOR_EACH (nfi, mf_node, &nxm_mf_map[id]) { + if (!f || version >= nfi->nf.version) { + f = &nfi->nf; + } + } + return f; +}