Skip to content

Commit

Permalink
tunnel: Support matching on the presence of Geneve options.
Browse files Browse the repository at this point in the history
Sometimes it is useful to match only on whether a Geneve option
is present even if the specific value is unimportant. A special
case of this is zero length options where there is no value at all
and the only information conveyed is whether the option was included
in the packet.

This operation was partially supported before but it was not consistent -
in particular, options were never serialized through NXM/OXM unless
they had a non-zero mask. Furthermore, zero length options were rejected
altogether when they were installed through the Geneve map OpenFlow
command.

This adds support for these types of matches by making any NXM/OXM for
tunnel metadata force a match on that field. In the case of a zero length
option, both the value and mask of the NXM are ignored.

Signed-off-by: Jesse Gross <[email protected]>
Acked-by: Jarno Rajahalme <[email protected]>
  • Loading branch information
jessegross committed Aug 29, 2015
1 parent 14dd55a commit 1cb2009
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 76 deletions.
3 changes: 3 additions & 0 deletions lib/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,7 @@ bitmap_is_all_zeros(const unsigned long *bitmap, size_t n)
#define ULLONG_SET0(MAP, OFFSET) ((MAP) &= ~(1ULL << (OFFSET)))
#define ULLONG_SET1(MAP, OFFSET) ((MAP) |= 1ULL << (OFFSET))

/* Returns the value of a bit in a map as a bool. */
#define ULLONG_GET(MAP, OFFSET) !!((MAP) & (1ULL << (OFFSET)))

#endif /* bitmap.h */
51 changes: 36 additions & 15 deletions lib/meta-flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,9 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
return !wc->masks.tunnel.gbp_id;
case MFF_TUN_GBP_FLAGS:
return !wc->masks.tunnel.gbp_flags;
CASE_MFF_TUN_METADATA: {
union mf_value value;

tun_metadata_read(&wc->masks.tunnel, mf, &value);
return is_all_zeros(&value.tun_metadata, mf->n_bytes);
}
CASE_MFF_TUN_METADATA:
return !ULLONG_GET(wc->masks.tunnel.metadata.present.map,
mf->id - MFF_TUN_METADATA0);
case MFF_METADATA:
return !wc->masks.metadata;
case MFF_IN_PORT:
Expand Down Expand Up @@ -1063,19 +1060,28 @@ field_len(const struct mf_field *mf, const union mf_value *value_)
/* Returns the effective length of the field. For fixed length fields,
* this is just the defined length. For variable length fields, it is
* the minimum size encoding that retains the same meaning (i.e.
* discarding leading zeros). */
* discarding leading zeros).
*
* 'is_masked' returns (if non-NULL) whether the original contained
* a mask. Otherwise, a mask that is the same length as the value
* might be misinterpreted as an exact match. */
int
mf_field_len(const struct mf_field *mf, const union mf_value *value,
const union mf_value *mask)
const union mf_value *mask, bool *is_masked_)
{
int len, mask_len;
bool is_masked = mask && !is_all_ones(mask, mf->n_bytes);

len = field_len(mf, value);
if (mask && !is_all_ones(mask, mf->n_bytes)) {
if (is_masked) {
mask_len = field_len(mf, mask);
len = MAX(len, mask_len);
}

if (is_masked_) {
*is_masked_ = is_masked;
}

return len;
}

Expand Down Expand Up @@ -1329,17 +1335,30 @@ mf_set_flow_value_masked(const struct mf_field *field,
mf_set_flow_value(field, &tmp, flow);
}

/* Returns true if 'mf' has a zero value in 'flow', false if it is nonzero.
bool
mf_is_tun_metadata(const struct mf_field *mf)
{
return mf->id >= MFF_TUN_METADATA0 &&
mf->id < MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS;
}

/* Returns true if 'mf' has previously been set in 'flow', false if
* it contains a non-default value.
*
* The caller is responsible for ensuring that 'flow' meets 'mf''s
* prerequisites. */
bool
mf_is_zero(const struct mf_field *mf, const struct flow *flow)
mf_is_set(const struct mf_field *mf, const struct flow *flow)
{
union mf_value value;
if (!mf_is_tun_metadata(mf)) {
union mf_value value;

mf_get_value(mf, flow, &value);
return is_all_zeros(&value, mf->n_bytes);
mf_get_value(mf, flow, &value);
return !is_all_zeros(&value, mf->n_bytes);
} else {
return ULLONG_GET(flow->tunnel.metadata.present.map,
mf->id - MFF_TUN_METADATA0);
}
}

/* Makes 'match' wildcard field 'mf'.
Expand Down Expand Up @@ -1585,7 +1604,9 @@ mf_set(const struct mf_field *mf,
if (!mask || is_all_ones(mask, mf->n_bytes)) {
mf_set_value(mf, value, match);
return mf->usable_protocols_exact;
} else if (is_all_zeros(mask, mf->n_bytes)) {
} else if (is_all_zeros(mask, mf->n_bytes) && !mf_is_tun_metadata(mf)) {
/* Tunnel metadata matches on the existence of the field itself, so
* it still needs to be encoded even if the value is wildcarded. */
mf_set_wild(mf, match);
return OFPUTIL_P_ANY;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/meta-flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -1851,10 +1851,11 @@ void mf_set_flow_value_masked(const struct mf_field *,
const union mf_value *value,
const union mf_value *mask,
struct flow *);
bool mf_is_zero(const struct mf_field *, const struct flow *);
bool mf_is_tun_metadata(const struct mf_field *);
bool mf_is_set(const struct mf_field *, const struct flow *);
void mf_mask_field(const struct mf_field *, struct flow *);
int mf_field_len(const struct mf_field *, const union mf_value *value,
const union mf_value *mask);
const union mf_value *mask, bool *is_masked);

void mf_get(const struct mf_field *, const struct match *,
union mf_value *value, union mf_value *mask);
Expand Down
43 changes: 21 additions & 22 deletions lib/nx-match.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,11 @@ nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header,
}
*header = ntohll(get_unaligned_be64(b->data));
}
if (nxm_length(*header) <= nxm_experimenter_len(*header)) {
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);
nxm_header_len(*header));
goto error;
}
ofpbuf_pull(b, nxm_header_len(*header));
Expand Down Expand Up @@ -674,26 +674,25 @@ oxm_pull_field_array(const void *fields_data, size_t fields_len,
* 'put' functions whose names end in 'm' add a field that might be wildcarded.
* Other 'put' functions add exact-match fields.
*/

static void
nxm_put_unmasked(struct ofpbuf *b, enum mf_field_id field,
enum ofp_version version, const void *value, size_t n_bytes)
void
nxm_put__(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
const void *value, const void *mask, size_t n_bytes)
{
nx_put_header_len(b, field, version, false, n_bytes);
nx_put_header_len(b, field, version, !!mask, n_bytes);
ofpbuf_put(b, value, n_bytes);
if (mask) {
ofpbuf_put(b, mask, n_bytes);
}

}

void
static void
nxm_put(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
const void *value, const void *mask, size_t n_bytes)
{
if (!is_all_zeros(mask, n_bytes)) {
bool masked = !is_all_ones(mask, n_bytes);
nx_put_header_len(b, field, version, masked, n_bytes);
ofpbuf_put(b, value, n_bytes);
if (masked) {
ofpbuf_put(b, mask, n_bytes);
}
nxm_put__(b, field, version, value, masked ? mask : NULL, n_bytes);
}
}

Expand All @@ -708,7 +707,7 @@ static void
nxm_put_8(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
uint8_t value)
{
nxm_put_unmasked(b, field, version, &value, sizeof value);
nxm_put__(b, field, version, &value, NULL, sizeof value);
}

static void
Expand All @@ -722,7 +721,7 @@ static void
nxm_put_16(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
ovs_be16 value)
{
nxm_put_unmasked(b, field, version, &value, sizeof value);
nxm_put__(b, field, version, &value, NULL, sizeof value);
}

static void
Expand All @@ -736,7 +735,7 @@ static void
nxm_put_32(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
ovs_be32 value)
{
nxm_put_unmasked(b, field, version, &value, sizeof value);
nxm_put__(b, field, version, &value, NULL, sizeof value);
}

static void
Expand Down Expand Up @@ -1171,10 +1170,10 @@ oxm_put_field_array(struct ofpbuf *b, const struct field_array *fa,

for (i = 0; i < MFF_N_IDS; i++) {
if (bitmap_is_set(fa->used.bm, i)) {
int len = mf_field_len(mf_from_id(i), &fa->value[i], NULL);
nxm_put_unmasked(b, i, version,
&fa->value[i].u8 + mf_from_id(i)->n_bytes - len,
len);
int len = mf_field_len(mf_from_id(i), &fa->value[i], NULL, NULL);
nxm_put__(b, i, version,
&fa->value[i].u8 + mf_from_id(i)->n_bytes - len, NULL,
len);
}
}

Expand Down Expand Up @@ -1216,10 +1215,10 @@ nx_put_entry(struct ofpbuf *b,
const union mf_value *value, const union mf_value *mask)
{
const struct mf_field *mf = mf_from_id(field);
bool masked = mask && !is_all_ones(mask, mf->n_bytes);
bool masked;
int len, offset;

len = mf_field_len(mf, value, mask);
len = mf_field_len(mf, value, mask, &masked);
offset = mf->n_bytes - len;

nx_put_header_len(b, field, version, masked, len);
Expand Down
6 changes: 3 additions & 3 deletions lib/nx-match.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ 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 nxm_put(struct ofpbuf *b, enum mf_field_id field,
enum ofp_version version, const void *value,
const void *mask, size_t n_bytes);
void nxm_put__(struct ofpbuf *b, enum mf_field_id field,
enum ofp_version version, const void *value,
const void *mask, size_t n_bytes);
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,
Expand Down
3 changes: 2 additions & 1 deletion lib/odp-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1845,7 +1845,8 @@ format_geneve_opts(const struct geneve_opt *opt,
verbose);
format_u8x(ds, "type", opt->type, MASK(mask, type), verbose);
format_u8u(ds, "len", data_len, mask ? &data_len_mask : NULL, verbose);
if (verbose || !mask || !is_all_zeros(mask + 1, data_len)) {
if (data_len &&
(verbose || !mask || !is_all_zeros(mask + 1, data_len))) {
ds_put_hex(ds, opt + 1, data_len);
if (mask && !is_all_ones(mask + 1, data_len)) {
ds_put_char(ds, '/');
Expand Down
23 changes: 18 additions & 5 deletions lib/ofp-parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,21 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
} else if (!strcmp(name, "no_readonly_table")
|| !strcmp(name, "allow_hidden_fields")) {
/* ignore these fields. */
} else if (mf_from_name(name)) {
char *value;

value = strtok_r(NULL, ", \t\r\n", &save_ptr);
if (!value) {
/* If there's no value, we're just trying to match on the
* existence of the field, so use a no-op value. */
value = "0/0";
}

error = parse_field(mf_from_name(name), value, &fm->match,
usable_protocols);
if (error) {
return error;
}
} else {
char *value;

Expand Down Expand Up @@ -424,9 +439,6 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
error = str_to_be64(value, &fm->new_cookie);
fm->modify_cookie = true;
}
} else if (mf_from_name(name)) {
error = parse_field(mf_from_name(name), value, &fm->match,
usable_protocols);
} else if (!strcmp(name, "duration")
|| !strcmp(name, "n_packets")
|| !strcmp(name, "n_bytes")
Expand Down Expand Up @@ -1098,7 +1110,7 @@ parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s,
goto exit;
}

if (!mf_is_zero(mf, flow)) {
if (mf_is_set(mf, flow)) {
error = xasprintf("%s: field %s set multiple times", s, key);
goto exit;
}
Expand Down Expand Up @@ -1243,7 +1255,8 @@ parse_select_group_field(char *s, struct field_array *fa,
}

/* The mask cannot be all-zeros */
if (is_all_zeros(&value, mf->n_bytes)) {
if (!mf_is_tun_metadata(mf) &&
is_all_zeros(&value, mf->n_bytes)) {
return xasprintf("%s: values are wildcards here "
"and must not be all-zeros", s);
}
Expand Down
3 changes: 1 addition & 2 deletions lib/ofp-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -9185,8 +9185,7 @@ decode_geneve_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
map->option_type = nx_map->option_type;

map->option_len = nx_map->option_len;
if (map->option_len == 0 || map->option_len % 4 ||
map->option_len > GENEVE_MAX_OPT_SIZE) {
if (map->option_len % 4 || map->option_len > GENEVE_MAX_OPT_SIZE) {
VLOG_WARN_RL(&bad_ofmsg_rl,
"geneve table option length (%u) is not a valid option size",
map->option_len);
Expand Down
Loading

0 comments on commit 1cb2009

Please sign in to comment.