bool valid; /* True if allocated to a class and type. */
};
-/* Maps from Geneve option class+type to positions in a struct tun_metadata's
+/* Maps from TLV option class+type to positions in a struct tun_metadata's
* 'opts' array. */
struct tun_table {
/* TUN_METADATA<i> is stored in element <i>. */
static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx)
OVS_REQUIRES(tab_mutex);
static void memcpy_to_metadata(struct tun_metadata *dst, const void *src,
- const struct tun_metadata_loc *);
+ const struct tun_metadata_loc *,
+ unsigned int idx);
static void memcpy_from_metadata(void *dst, const struct tun_metadata *src,
const struct tun_metadata_loc *);
tun_metadata_del_entry(map, entry - map->entries);
}
+ hmap_destroy(&map->key_hmap);
free(map);
}
}
enum ofperr
-tun_metadata_table_mod(struct ofputil_geneve_table_mod *gtm)
+tun_metadata_table_mod(struct ofputil_tlv_table_mod *ttm)
{
struct tun_table *old_map, *new_map;
- struct ofputil_geneve_map *ofp_map;
+ struct ofputil_tlv_map *ofp_map;
enum ofperr err = 0;
ovs_mutex_lock(&tab_mutex);
old_map = ovsrcu_get_protected(struct tun_table *, &metadata_tab);
- switch (gtm->command) {
- case NXGTMC_ADD:
+ switch (ttm->command) {
+ case NXTTMC_ADD:
new_map = table_alloc(old_map);
- LIST_FOR_EACH (ofp_map, list_node, >m->mappings) {
+ LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
err = tun_metadata_add_entry(new_map, ofp_map->index,
ofp_map->option_class,
ofp_map->option_type,
}
break;
- case NXGTMC_DELETE:
+ case NXTTMC_DELETE:
new_map = table_alloc(old_map);
- LIST_FOR_EACH (ofp_map, list_node, >m->mappings) {
+ LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) {
tun_metadata_del_entry(new_map, ofp_map->index);
}
break;
- case NXGTMC_CLEAR:
+ case NXTTMC_CLEAR:
new_map = table_alloc(NULL);
break;
}
void
-tun_metadata_table_request(struct ofputil_geneve_table_reply *gtr)
+tun_metadata_table_request(struct ofputil_tlv_table_reply *ttr)
{
struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
int i;
- gtr->max_option_space = TUN_METADATA_TOT_OPT_SIZE;
- gtr->max_fields = TUN_METADATA_NUM_OPTS;
- list_init(>r->mappings);
+ ttr->max_option_space = TUN_METADATA_TOT_OPT_SIZE;
+ ttr->max_fields = TUN_METADATA_NUM_OPTS;
+ list_init(&ttr->mappings);
for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) {
struct tun_meta_entry *entry = &map->entries[i];
- struct ofputil_geneve_map *map;
+ struct ofputil_tlv_map *map;
if (!entry->valid) {
continue;
map->option_len = entry->loc.len;
map->index = i;
- list_push_back(>r->mappings, &map->list_node);
+ list_push_back(&ttr->mappings, &map->list_node);
}
}
}
loc = &map->entries[idx].loc;
-
- ULLONG_SET1(tnl->metadata.present.map, idx);
memcpy_to_metadata(&tnl->metadata,
- value->tun_metadata + mf->n_bytes - loc->len, loc);
+ value->tun_metadata + mf->n_bytes - loc->len, loc, idx);
}
static const struct tun_metadata_loc *
metadata_loc_from_match(struct tun_table *map, struct match *match,
- unsigned int idx, unsigned int field_len)
+ const char *name, unsigned int idx,
+ unsigned int field_len, bool masked, char **err_str)
{
ovs_assert(idx < TUN_METADATA_NUM_OPTS);
+ if (err_str) {
+ *err_str = NULL;
+ }
+
if (map) {
if (map->entries[idx].valid) {
return &map->entries[idx].loc;
}
}
- if (match->tun_md.alloc_offset + field_len >= TUN_METADATA_TOT_OPT_SIZE ||
- match->tun_md.loc[idx].len) {
+ if (match->tun_md.alloc_offset + field_len > TUN_METADATA_TOT_OPT_SIZE) {
+ if (err_str) {
+ *err_str = xasprintf("field %s exceeds maximum size for tunnel "
+ "metadata (used %d, max %d)", name,
+ match->tun_md.alloc_offset + field_len,
+ TUN_METADATA_TOT_OPT_SIZE);
+ }
+
+ return NULL;
+ }
+
+ if (ULLONG_GET(match->wc.masks.tunnel.metadata.present.map, idx)) {
+ if (err_str) {
+ *err_str = xasprintf("field %s set multiple times", name);
+ }
+
return NULL;
}
- match->tun_md.loc[idx].len = field_len;
- match->tun_md.loc[idx].c.offset = match->tun_md.alloc_offset;
- match->tun_md.loc[idx].c.len = field_len;
- match->tun_md.loc[idx].c.next = NULL;
+ match->tun_md.entry[idx].loc.len = field_len;
+ match->tun_md.entry[idx].loc.c.offset = match->tun_md.alloc_offset;
+ match->tun_md.entry[idx].loc.c.len = field_len;
+ match->tun_md.entry[idx].loc.c.next = NULL;
+ match->tun_md.entry[idx].masked = masked;
match->tun_md.alloc_offset += field_len;
match->tun_md.valid = true;
- return &match->tun_md.loc[idx];
+ return &match->tun_md.entry[idx].loc;
}
/* Makes 'match' match 'value'/'mask' on field 'mf'.
* value.
*
* 'mask' may be NULL; if so, then 'mf' is made exact-match.
+ *
+ * If non-NULL, 'err_str' returns a malloc'ed string describing any errors
+ * with the request or NULL if there is no error. The caller is reponsible
+ * for freeing the string.
*/
void
tun_metadata_set_match(const struct mf_field *mf, const union mf_value *value,
- const union mf_value *mask, struct match *match)
+ const union mf_value *mask, struct match *match,
+ char **err_str)
{
struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab);
const struct tun_metadata_loc *loc;
unsigned int idx = mf->id - MFF_TUN_METADATA0;
unsigned int field_len;
+ bool is_masked;
unsigned int data_offset;
union mf_value data;
ovs_assert(!(match->flow.tunnel.flags & FLOW_TNL_F_UDPIF));
- field_len = mf_field_len(mf, value, mask);
- loc = metadata_loc_from_match(map, match, idx, field_len);
+ field_len = mf_field_len(mf, value, mask, &is_masked);
+ loc = metadata_loc_from_match(map, match, mf->name, idx, field_len,
+ is_masked, err_str);
if (!loc) {
return;
}
mask->tun_metadata[data_offset + i];
}
}
- ULLONG_SET1(match->flow.tunnel.metadata.present.map, idx);
- memcpy_to_metadata(&match->flow.tunnel.metadata, data.tun_metadata, loc);
+ memcpy_to_metadata(&match->flow.tunnel.metadata, data.tun_metadata,
+ loc, idx);
if (!value) {
memset(data.tun_metadata, 0, loc->len);
} else {
memcpy(data.tun_metadata, mask->tun_metadata + data_offset, loc->len);
}
- ULLONG_SET1(match->wc.masks.tunnel.metadata.present.map, idx);
- memcpy_to_metadata(&match->wc.masks.tunnel.metadata, data.tun_metadata, loc);
+ memcpy_to_metadata(&match->wc.masks.tunnel.metadata, data.tun_metadata,
+ loc, idx);
}
static bool
const struct tun_metadata_loc *old_loc = &flow.metadata.tab->entries[i].loc;
const struct tun_metadata_loc *new_loc;
- new_loc = metadata_loc_from_match(NULL, flow_metadata, i, old_loc->len);
+ new_loc = metadata_loc_from_match(NULL, flow_metadata, NULL, i,
+ old_loc->len, false, NULL);
memcpy_from_metadata(opts.tun_metadata, &flow.metadata, old_loc);
memcpy_to_metadata(&flow_metadata->flow.tunnel.metadata,
- opts.tun_metadata, new_loc);
+ opts.tun_metadata, new_loc, i);
memset(opts.tun_metadata, 0xff, old_loc->len);
memcpy_to_metadata(&flow_metadata->wc.masks.tunnel.metadata,
- opts.tun_metadata, new_loc);
+ opts.tun_metadata, new_loc, i);
}
}
static void
memcpy_to_metadata(struct tun_metadata *dst, const void *src,
- const struct tun_metadata_loc *loc)
+ const struct tun_metadata_loc *loc, unsigned int idx)
{
const struct tun_metadata_loc_chain *chain = &loc->c;
int addr = 0;
addr += chain->len;
chain = chain->next;
}
+
+ ULLONG_SET1(dst->present.map, idx);
}
static void
entry = &map->entries[idx];
if (entry->valid) {
- return OFPERR_NXGTMFC_ALREADY_MAPPED;
+ return OFPERR_NXTTMFC_ALREADY_MAPPED;
}
entry->key = tun_meta_key(htons(opt_class), type);
if (tun_meta_find_key(&map->key_hmap, entry->key)) {
- return OFPERR_NXGTMFC_DUP_ENTRY;
+ return OFPERR_NXTTMFC_DUP_ENTRY;
}
entry->valid = true;
if (!cur_chain) {
cur_chain = xzalloc(sizeof *cur_chain);
+ prev_chain->next = cur_chain;
}
err = tun_metadata_alloc_chain(map, len, cur_chain);
if (err) {
tun_metadata_del_entry(map, idx);
- return OFPERR_NXGTMFC_TABLE_FULL;
+ return OFPERR_NXTTMFC_TABLE_FULL;
}
len -= cur_chain->len;
- if (prev_chain) {
- prev_chain->next = cur_chain;
- }
prev_chain = cur_chain;
cur_chain = NULL;
}
flow_opt->type));
if (entry) {
if (entry->loc.len == flow_opt->length * 4) {
- memcpy_to_metadata(metadata, opt + 1, &entry->loc);
- ULLONG_SET1(metadata->present.map, entry - map->entries);
+ memcpy_to_metadata(metadata, opt + 1, &entry->loc,
+ entry - map->entries);
} else {
return EINVAL;
}
ovs_assert(!(flow->flags & FLOW_TNL_F_UDPIF));
- ofpbuf_use_stack(&b, opts, GENEVE_TOT_OPT_SIZE);
+ ofpbuf_use_stack(&b, opts, TLV_TOT_OPT_SIZE);
tun_metadata_to_geneve__(&flow->metadata, &b, crit_opt);
return b.size;
static const struct tun_metadata_loc *
metadata_loc_from_match_read(struct tun_table *map, const struct match *match,
- unsigned int idx)
+ unsigned int idx, struct flow_tnl *mask,
+ bool *is_masked)
{
+ union mf_value mask_opts;
+
if (match->tun_md.valid) {
- return &match->tun_md.loc[idx];
+ *is_masked = match->tun_md.entry[idx].masked;
+ return &match->tun_md.entry[idx].loc;
}
+ memcpy_from_metadata(mask_opts.tun_metadata, &mask->metadata,
+ &map->entries[idx].loc);
+
+ *is_masked = map->entries[idx].loc.len == 0 ||
+ !is_all_ones(mask_opts.tun_metadata,
+ map->entries[idx].loc.len);
return &map->entries[idx].loc;
}
ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) {
const struct tun_metadata_loc *loc;
+ bool is_masked;
union mf_value opts;
union mf_value mask_opts;
- loc = metadata_loc_from_match_read(flow.metadata.tab, match, i);
+ loc = metadata_loc_from_match_read(flow.metadata.tab, match, i,
+ &mask, &is_masked);
memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc);
memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc);
- nxm_put(b, MFF_TUN_METADATA0 + i, oxm, opts.tun_metadata,
- mask_opts.tun_metadata, loc->len);
+ nxm_put__(b, MFF_TUN_METADATA0 + i, oxm, opts.tun_metadata,
+ is_masked ? mask_opts.tun_metadata : NULL, loc->len);
}
}
ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) {
const struct tun_metadata_loc *loc;
- union mf_value opts;
+ bool is_masked;
+ union mf_value opts, mask_opts;
- loc = metadata_loc_from_match_read(flow.metadata.tab, match, i);
+ loc = metadata_loc_from_match_read(flow.metadata.tab, match, i,
+ &mask, &is_masked);
- ds_put_format(s, "tun_metadata%u=", i);
- memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc);
- ds_put_hex(s, opts.tun_metadata, loc->len);
+ ds_put_format(s, "tun_metadata%u", i);
+ memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc);
+
+ if (!ULLONG_GET(flow.metadata.present.map, i)) {
+ /* Indicate that we are matching on the field being not present. */
+ ds_put_cstr(s, "=NP");
+ } else if (!(is_masked &&
+ is_all_zeros(mask_opts.tun_metadata, loc->len))) {
+ ds_put_char(s, '=');
- memcpy_from_metadata(opts.tun_metadata, &mask.metadata, loc);
- if (!is_all_ones(opts.tun_metadata, loc->len)) {
- ds_put_char(s, '/');
+ memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc);
ds_put_hex(s, opts.tun_metadata, loc->len);
+
+ if (!is_all_ones(mask_opts.tun_metadata, loc->len)) {
+ ds_put_char(s, '/');
+ ds_put_hex(s, mask_opts.tun_metadata, loc->len);
+ }
}
ds_put_char(s, ',');
}