Skip to content

Commit

Permalink
ofp-parse: Allow match field names in actions and brackets in matches.
Browse files Browse the repository at this point in the history
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn').  Allow also leaving out the
trailing '[]' to indicate full field.  These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.

Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation.  For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:

... reg0[14..15]=2 ...

instead of

... reg0=0x8000/0xc000 ...

Note that only contiguous masks can be specified with the bracket
notation.

Signed-off-by: Jarno Rajahalme <[email protected]>
Acked-by: Ben Pfaff <[email protected]>
  • Loading branch information
Jarno Rajahalme committed Jan 5, 2017
1 parent e7dce33 commit 21b2fa6
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 138 deletions.
1 change: 1 addition & 0 deletions include/openvswitch/meta-flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,7 @@ struct field_array {

/* Finding mf_fields. */
const struct mf_field *mf_from_name(const char *name);
const struct mf_field *mf_from_name_len(const char *name, size_t len);

static inline const struct mf_field *
mf_from_id(enum mf_field_id id)
Expand Down
145 changes: 89 additions & 56 deletions lib/learn.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,24 +187,12 @@ learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc)
/* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
static char * OVS_WARN_UNUSED_RESULT
learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec,
learn_parse_load_immediate(union mf_subvalue *imm, const char *s,
const char *full_s, struct ofpact_learn_spec *spec,
struct ofpbuf *ofpacts)
{
const char *full_s = s;
struct mf_subfield dst;
union mf_subvalue imm;
char *error;
int err;

err = parse_int_string(s, imm.u8, sizeof imm.u8, (char **) &s);
if (err) {
return xasprintf("%s: too many bits in immediate value", full_s);
}

if (strncmp(s, "->", 2)) {
return xasprintf("%s: missing `->' following value", full_s);
}
s += 2;

error = mf_parse_subfield(&dst, s);
if (error) {
Expand All @@ -215,8 +203,8 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec,
full_s, s);
}

if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
(8 * sizeof imm) - dst.n_bits)) {
if (!bitwise_is_all_zeros(imm, sizeof *imm, dst.n_bits,
(8 * sizeof *imm) - dst.n_bits)) {
return xasprintf("%s: value does not fit into %u bits",
full_s, dst.n_bits);
}
Expand All @@ -229,7 +217,7 @@ learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec,
/* Push value last, as this may reallocate 'spec'! */
unsigned int n_bytes = DIV_ROUND_UP(dst.n_bits, 8);
uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(n_bytes));
memcpy(src_imm, &imm.u8[sizeof imm.u8 - n_bytes], n_bytes);
memcpy(src_imm, &imm->u8[sizeof imm->u8 - n_bytes], n_bytes);

return NULL;
}
Expand All @@ -241,50 +229,78 @@ learn_parse_spec(const char *orig, char *name, char *value,
struct ofpact_learn_spec *spec,
struct ofpbuf *ofpacts, struct match *match)
{
if (mf_from_name(name)) {
const struct mf_field *dst = mf_from_name(name);
union mf_value imm;
char *error;

error = mf_parse_value(dst, value, &imm);
if (error) {
return error;
}
/* Parse destination and check prerequisites. */
struct mf_subfield dst;
char *error;

spec->n_bits = dst->n_bits;
spec->src_type = NX_LEARN_SRC_IMMEDIATE;
spec->dst_type = NX_LEARN_DST_MATCH;
spec->dst.field = dst;
spec->dst.ofs = 0;
spec->dst.n_bits = dst->n_bits;

/* Update 'match' to allow for satisfying destination
* prerequisites. */
mf_set_value(dst, &imm, match, NULL);

/* Push value last, as this may reallocate 'spec'! */
uint8_t *src_imm = ofpbuf_put_zeros(ofpacts,
OFPACT_ALIGN(dst->n_bytes));
memcpy(src_imm, &imm, dst->n_bytes);
} else if (strchr(name, '[')) {
/* Parse destination and check prerequisites. */
char *error;

error = mf_parse_subfield(&spec->dst, name);
if (error) {
return error;
}
if (!mf_nxm_header(spec->dst.field->id)) {
error = mf_parse_subfield(&dst, name);
if (!error) {
if (!mf_nxm_header(dst.field->id)) {
return xasprintf("%s: experimenter OXM field '%s' not supported",
orig, name);
}
spec->dst = dst;
spec->n_bits = dst.n_bits;
spec->dst_type = NX_LEARN_DST_MATCH;

/* Parse source and check prerequisites. */
if (value[0] != '\0') {
error = mf_parse_subfield(&spec->src, value);
struct mf_subfield src;
error = mf_parse_subfield(&src, value);
if (error) {
return error;
union mf_value imm;
char *imm_error = NULL;

/* Try an immediate value. */
if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) {
/* Full field value. */
imm_error = mf_parse_value(dst.field, value, &imm);
} else {
char *tail;
/* Partial field value. */
if (parse_int_string(value, (uint8_t *)&imm,
dst.field->n_bytes, &tail)
|| *tail != 0) {
imm_error = xasprintf("%s: cannot parse integer value", orig);
}

if (!imm_error &&
!bitwise_is_all_zeros(&imm, dst.field->n_bytes,
dst.n_bits,
dst.field->n_bytes * 8 - dst.n_bits)) {
struct ds ds;

ds_init(&ds);
mf_format(dst.field, &imm, NULL, &ds);
imm_error = xasprintf("%s: value %s does not fit into %d bits",
orig, ds_cstr(&ds), dst.n_bits);
ds_destroy(&ds);
}
}
if (imm_error) {
char *err = xasprintf("%s: %s value %s cannot be parsed as a subfield (%s) or an immediate value (%s)",
orig, name, value, error, imm_error);
free(error);
free(imm_error);
return err;
}

spec->src_type = NX_LEARN_SRC_IMMEDIATE;

/* Update 'match' to allow for satisfying destination
* prerequisites. */
mf_write_subfield_value(&dst, &imm, match);

/* Push value last, as this may reallocate 'spec'! */
unsigned int imm_bytes = DIV_ROUND_UP(dst.n_bits, 8);
uint8_t *src_imm = ofpbuf_put_zeros(ofpacts,
OFPACT_ALIGN(imm_bytes));
memcpy(src_imm, &imm, imm_bytes);

free(error);
return NULL;
}
spec->src = src;
if (spec->src.n_bits != spec->dst.n_bits) {
return xasprintf("%s: bit widths of %s (%u) and %s (%u) "
"differ", orig, name, spec->src.n_bits, value,
Expand All @@ -294,12 +310,29 @@ learn_parse_spec(const char *orig, char *name, char *value,
spec->src = spec->dst;
}

spec->n_bits = spec->src.n_bits;
spec->src_type = NX_LEARN_SRC_FIELD;
spec->dst_type = NX_LEARN_DST_MATCH;
} else if (!strcmp(name, "load")) {
if (value[strcspn(value, "[-")] == '-') {
char *error = learn_parse_load_immediate(value, spec, ofpacts);
union mf_subvalue imm;
char *tail;
char *dst_value = strstr(value, "->");

if (dst_value == value) {
return xasprintf("%s: missing source before `->' in `%s'", name,
value);
}
if (!dst_value) {
return xasprintf("%s: missing `->' in `%s'", name, value);
}

if (!parse_int_string(value, imm.u8, sizeof imm.u8, (char **) &tail)
&& tail != value) {
if (tail != dst_value) {
return xasprintf("%s: garbage before `->' in `%s'",
name, value);
}

char *error = learn_parse_load_immediate(&imm, dst_value + 2, value, spec,
ofpacts);
if (error) {
return error;
}
Expand Down
11 changes: 11 additions & 0 deletions lib/meta-flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ mf_from_name(const char *name)
return shash_find_data(&mf_by_name, name);
}

/* Returns the field with the given 'name' (which is 'len' bytes long), or a
* null pointer if no field has that name. */
const struct mf_field *
mf_from_name_len(const char *name, size_t len)
{
nxm_init();

struct shash_node *node = shash_find_len(&mf_by_name, name, len);
return node ? node->data : NULL;
}

static void
nxm_do_init(void)
{
Expand Down
37 changes: 19 additions & 18 deletions lib/nx-match.c
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,7 @@ mf_parse_subfield_name(const char *name, int name_len, bool *wild)
char * OVS_WARN_UNUSED_RESULT
mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
{
const struct mf_field *field;
const struct mf_field *field = NULL;
const struct nxm_field *f;
const char *name;
int start, end;
Expand All @@ -1822,30 +1822,31 @@ mf_parse_subfield__(struct mf_subfield *sf, const char **sp)

s = *sp;
name = s;
name_len = strcspn(s, "[");
if (s[name_len] != '[') {
return xasprintf("%s: missing [ looking for field name", *sp);
}
name_len = strcspn(s, "[-");

f = mf_parse_subfield_name(name, name_len, &wild);
if (!f) {
field = f ? mf_from_id(f->id) : mf_from_name_len(name, name_len);
if (!field) {
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);
/* Assume full field. */
start = 0;
end = field->n_bits - 1;
if (*s == '[') {
if (!strncmp(s, "[]", 2)) {
/* Nothing to do. */
} else if (ovs_scan(s, "[%d..%d]", &start, &end)) {
/* Nothing to do. */
} else if (ovs_scan(s, "[%d]", &start)) {
end = start;
} else {
return xasprintf("%s: syntax error expecting [] or [<bit>] or "
"[<start>..<end>]", *sp);
}
s = strchr(s, ']') + 1;
}
s = strchr(s, ']') + 1;

if (start > end) {
return xasprintf("%s: starting bit %d is after ending bit %d",
Expand Down
52 changes: 18 additions & 34 deletions lib/ofp-actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -609,25 +609,30 @@ static char * OVS_WARN_UNUSED_RESULT
parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
if (strchr(arg, '[')) {
struct ofpact_output_reg *output_reg;

output_reg = ofpact_put_OUTPUT_REG(ofpacts);
output_reg->max_len = UINT16_MAX;
return mf_parse_subfield(&output_reg->src, arg);
} else if (strstr(arg, "port") && strstr(arg, "max_len")) {
if (strstr(arg, "port") && strstr(arg, "max_len")) {
struct ofpact_output_trunc *output_trunc;

output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
return parse_truncate_subfield(output_trunc, arg);
} else {
struct ofpact_output *output;
struct mf_subfield src;
char *error = mf_parse_subfield(&src, arg);
if (!error) {
struct ofpact_output_reg *output_reg;

output = ofpact_put_OUTPUT(ofpacts);
if (!ofputil_port_from_string(arg, &output->port)) {
return xasprintf("%s: output to unknown port", arg);
output_reg = ofpact_put_OUTPUT_REG(ofpacts);
output_reg->max_len = UINT16_MAX;
output_reg->src = src;
} else {
free(error);
struct ofpact_output *output;

output = ofpact_put_OUTPUT(ofpacts);
if (!ofputil_port_from_string(arg, &output->port)) {
return xasprintf("%s: output to unknown port", arg);
}
output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
}
output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
return NULL;
}
}
Expand Down Expand Up @@ -2375,28 +2380,7 @@ parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
const char *full_arg = arg;
char *error;

error = mf_parse_subfield__(&move->src, &arg);
if (error) {
return error;
}
if (strncmp(arg, "->", 2)) {
return xasprintf("%s: missing `->' following source", full_arg);
}
arg += 2;
error = mf_parse_subfield(&move->dst, arg);
if (error) {
return error;
}

if (move->src.n_bits != move->dst.n_bits) {
return xasprintf("%s: source field is %d bits wide but destination is "
"%d bits wide", full_arg,
move->src.n_bits, move->dst.n_bits);
}
return NULL;
return nxm_parse_reg_move(move, arg);
}

static void
Expand Down
Loading

0 comments on commit 21b2fa6

Please sign in to comment.