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-of-by: Jarno Rajahalme <ja...@ovn.org> Signed-off-by: Jarno Rajahalme <ja...@ovn.org> --- lib/learn.c | 145 +++++++++++++++++++++++++++++------------------ lib/nx-match.c | 47 +++++++++------ lib/ofp-actions.c | 52 ++++++----------- lib/ofp-parse.c | 54 +++++++++++++++++- tests/learn.at | 4 +- tests/ovs-ofctl.at | 28 +++++++-- utilities/ovs-ofctl.8.in | 40 ++++++++----- 7 files changed, 236 insertions(+), 134 deletions(-) diff --git a/lib/learn.c b/lib/learn.c index 9cab759..ce52c35 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -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) { @@ -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); } @@ -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; } @@ -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, @@ -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; } diff --git a/lib/nx-match.c b/lib/nx-match.c index 9201aae..9c769c0 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -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; @@ -1822,30 +1822,41 @@ 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) { + if (f) { + field = mf_from_id(f->id); + } else if (name_len < 256) { + char name_buf[256]; + if (s[name_len] != '\0') { + memcpy(name_buf, name, name_len); + name_buf[name_len] = '\0'; + name = name_buf; + } + field = mf_from_name(name); + } + 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", diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 7507558..2ff9abf 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -584,25 +584,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; } } @@ -2360,28 +2365,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 diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 553991c..af19fae 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -250,6 +250,52 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match, return error; } +/* Parses 'str_value' as the value of subfield 'name', and updates + * 'match' appropriately. Restricts the set of usable protocols to ones + * supporting the parsed field. + * + * 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 +parse_subfield(const char *name, const char *str_value, struct match *match, + enum ofputil_protocol *usable_protocols) +{ + struct mf_subfield sf; + char *error; + + error = mf_parse_subfield(&sf, name); + if (!error) { + union mf_value val; + char *tail; + if (parse_int_string(str_value, (uint8_t *)&val, sf.field->n_bytes, + &tail) || *tail != 0) { + return xasprintf("%s: cannot parse integer value: %s", name, + str_value); + } + if (!bitwise_is_all_zeros(&val, sf.field->n_bytes, sf.n_bits, + sf.field->n_bytes * 8 - sf.n_bits)) { + struct ds ds; + + ds_init(&ds); + mf_format(sf.field, &val, NULL, &ds); + error = xasprintf("%s: value %s does not fit into %d bits", + name, ds_cstr(&ds), sf.n_bits); + ds_destroy(&ds); + return error; + } + + const struct mf_field *field = sf.field; + union mf_value value, mask; + unsigned int size = DIV_ROUND_UP(sf.n_bits, 8); + + mf_get(field, match, &value, &mask); + bitwise_copy(&val, size, 0, &value, field->n_bytes, sf.ofs, sf.n_bits); + bitwise_one ( &mask, field->n_bytes, sf.ofs, sf.n_bits); + *usable_protocols &= mf_set(field, &value, &mask, match, &error); + } + return error; +} + static char * extract_actions(char *s) { @@ -356,6 +402,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, while (ofputil_parse_key_value(&string, &name, &value)) { const struct protocol *p; + const struct mf_field *mf; char *error = NULL; if (parse_protocol(name, &p)) { @@ -379,9 +426,10 @@ 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)) { - error = parse_field(mf_from_name(name), value, &fm->match, - usable_protocols); + } else if ((mf = mf_from_name(name)) != NULL) { + error = parse_field(mf, value, &fm->match, usable_protocols); + } else if (strchr(name, '[')) { + error = parse_subfield(name, value, &fm->match, usable_protocols); } else { if (!*value) { return xasprintf("field %s missing value", name); diff --git a/tests/learn.at b/tests/learn.at index 3e72dff..3f6fb5a 100644 --- a/tests/learn.at +++ b/tests/learn.at @@ -7,7 +7,7 @@ actions=learn(send_flow_rem) actions=learn(delete_learned) actions=learn(send_flow_rem,delete_learned) actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10]) -actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) +actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,eth_dst=eth_src,load:in_port->reg1[16..31]) ]]) AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: any @@ -26,7 +26,7 @@ AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(table=1, in_port_oxm=123456)']], [1], [], [stderr]) AT_CHECK([sed -e 's/.*|ofp_util|WARN|//' < stderr], [0], [[port 123456 is outside the supported range 0 through ffff or 0xffffff00 through 0xffffffff -ovs-ofctl: 123456: port value out of range for in_port_oxm +ovs-ofctl: table=1, in_port_oxm=123456: in_port_oxm value 123456 cannot be parsed as a subfield (123456: unknown field `123456') or an immediate value (123456: port value out of range for in_port_oxm) ]], [[]]) AT_CLEANUP diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index f2ee970..8c74bdb 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -79,6 +79,9 @@ for test_case in \ 'xxreg2=2/1 NXM,OXM' \ 'xxreg3=3 NXM,OXM' \ 'xxreg3=3/1 NXM,OXM' \ + 'xxreg3[[0..0]]=1 NXM,OXM' \ + 'xxreg3[[126..127]]=3 NXM,OXM' \ + 'reg3[[]]=3 NXM,OXM' \ 'dl_src=00:11:22:33:44:55 any' \ 'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM,OpenFlow11' \ 'dl_dst=00:11:22:33:44:55 any' \ @@ -147,6 +150,7 @@ for test_case in \ 'udp,udp_dst=0x1000/0x1000 NXM,OXM' \ 'udp6,udp_dst=80 NXM,OXM' \ 'udp6,udp_dst=0x1000/0x1000 NXM,OXM' \ + 'udp6,udp_dst[[12]]=1 NXM,OXM' \ 'icmp,icmp_type=1 any' \ 'icmp,icmp_code=2 any' \ 'icmp6,icmpv6_type=1 NXM,OXM' \ @@ -395,7 +399,7 @@ cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tcp,tp_src=0x1230/0xfff0,tun_id=0x1234,cookie=0x5678,actions=flood actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789 -actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, NXM_NX_REG0[0..12]) +actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, reg0[0..12]) table=1,actions=drop tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop @@ -403,13 +407,13 @@ actions=bundle(eth_src,50,active_backup,ofport,slaves:1) actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3) actions=bundle(symmetric_l4,60,hrw,ofport,slaves:) actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2 -actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1) +actions=bundle_load(eth_src,50,active_backup,ofport,reg0,slaves:1) actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3) -actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:[2,3]) +actions=bundle_load(symmetric_l4,60,hrw,ofport,reg0[0..15],slaves:[2,3]) actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:) actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3) -send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3 +send_flow_rem,actions=output:1,output:NXM_NX_REG0,output:2,output:reg1[16..31],output:3 check_overlap,actions=output:1,exit,output:2 tcp,actions=fin_timeout(idle_timeout=5,hard_timeout=15) actions=controller(max_len=123,reason=invalid_ttl,id=555) @@ -419,6 +423,9 @@ ip,actions=ct(commit,zone=5) ip,actions=ct(commit,exec(load(1->NXM_NX_CT_MARK[]))) ip,actions=ct(commit,exec(load(0x1->NXM_NX_CT_LABEL[]))) ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->NXM_NX_CT_LABEL[32..95]))) +ip,actions=ct(commit,exec(load(1->ct_mark))) +ip,actions=ct(commit,exec(load(0x1->ct_label[]))) +ip,actions=ct(commit,exec(load(0x1234567890ABCDEF->ct_label[32..95]))) ip,actions=ct(commit,exec(set_field(0x1->ct_label))) ip,ct_state=+trk,ct_label=0x1234567890abcdef12345678,actions=ct(commit) actions=output(max_len=100,port=123) @@ -466,6 +473,9 @@ NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,zone=5) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95])) +NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_MARK[])) +NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) +NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1234567890abcdef->NXM_NX_CT_LABEL[32..95])) NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[0..63],load:0->NXM_NX_CT_LABEL[64..127])) NXT_FLOW_MOD: ADD table:255 ct_state=+trk,ct_label=0x1234567890abcdef12345678,ip actions=ct(commit) NXT_FLOW_MOD: ADD table:255 actions=output(port=123,max_len=100) @@ -579,9 +589,12 @@ cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note tun_id=0x1234,cookie=0x5678,actions=flood actions=drop -reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] +reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[] -actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[] +actions=push:reg0[0..31],pop:NXM_NX_REG0[] +reg0=123,actions=move:reg0[0..5]->reg1[26..31],load:55->reg2,move:reg0->tun_id[0..31],move:reg0[0..15]->vlan_tci +actions=move:eth_dst->eth_src[] +actions=push:reg0[0..31],pop:reg0 vlan_tci=0x1123/0x1fff,actions=drop actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) @@ -619,6 +632,9 @@ NXT_FLOW_MOD: ADD <any> actions=drop NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[] NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] +NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] +NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[] +NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678) NXT_FLOW_MOD: ADD <any> actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678,sampling_port=56789) diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 96135ea..e52a3cf 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1545,7 +1545,9 @@ is the packet's input port, the packet is not output. . .IP \fBoutput:\fIsrc\fB[\fIstart\fB..\fIend\fB] Outputs the packet to the OpenFlow port number read from \fIsrc\fR, -which must be an NXM field as described above. For example, +which must be an NXM field as described above. Also the match field +name can be used, for example, instead of 'NXM_NX_REG0' the +name 'reg0' can be used. For example, \fBoutput:NXM_NX_REG0[16..31]\fR outputs to the OpenFlow port number written in the upper half of register 0. If the port number is the packet's input port, the packet is not output. @@ -2009,16 +2011,18 @@ bytes with value 0 to make the total number 6 more than a multiple of Copies the named bits from field \fIsrc\fR to field \fIdst\fR. \fIsrc\fR and \fIdst\fR must be NXM field names as defined in \fBnicira\-ext.h\fR, e.g. \fBNXM_OF_UDP_SRC\fR or \fBNXM_NX_REG0\fR. -Each \fIstart\fR and \fIend\fR pair, which are inclusive, must specify -the same number of bits and must fit within its respective field. +Also the corresponding match field names can be used, for example, +instead of 'NXM_NX_REG0' the name 'reg0' can be used. Each +\fIstart\fR and \fIend\fR pair, which are inclusive, must specify the +same number of bits and must fit within its respective field. Shorthands for \fB[\fIstart\fB..\fIend\fB]\fR exist: use \fB[\fIbit\fB]\fR to specify a single bit or \fB[]\fR to specify an -entire field. +entire field (in the latter case the brackets can also be left off). .IP Examples: \fBmove:NXM_NX_REG0[0..5]\->NXM_NX_REG1[26..31]\fR copies the six bits numbered 0 through 5, inclusive, in register 0 into bits 26 through 31, inclusive; -\fBmove:NXM_NX_REG0[0..15]\->NXM_OF_VLAN_TCI[]\fR copies the least +\fBmove:reg0[0..15]\->vlan_tci\fR copies the least significant 16 bits of register 0 into the VLAN TCI field. .IP In OpenFlow 1.0 through 1.4, \fBmove\fR ordinarily uses an Open @@ -2037,9 +2041,9 @@ the customary syntax for field \fIdst\fR, which is expressed as a field name. For example, \fBset_field:00:11:22:33:44:55->eth_src\fR sets the Ethernet source address to 00:11:22:33:44:55. With \fBload\fR, \fIvalue\fR must be an integer value (in decimal or -prefixed by \fB0x\fR for hexadecimal) and \fIdst\fR is the NXM or OXM -name for the field. For example, -\fBload:0x001122334455->OXM_OF_ETH_DST[]\fR has the same effect as the +prefixed by \fB0x\fR for hexadecimal) and \fIdst\fR can also be the +NXM or OXM name for the field. For example, +\fBload:0x001122334455->OXM_OF_ETH_SRC[]\fR has the same effect as the prior \fBset_field\fR example. .IP The two forms exist for historical reasons. Open vSwitch 1.1 @@ -2059,8 +2063,9 @@ subfield, \fBOFPAT_SET_FIELD\fR otherwise; and OpenFlow 1.5 and later, Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields on top of the stack. .IP -Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register -2 bits 0 through 5, inclusive, on to the internal stack. +Example: \fBpush:NXM_NX_REG2[0..5]\fR or \fBpush:reg2[0..5]\fR push +the value stored in register 2 bits 0 through 5, inclusive, on to the +internal stack. . .IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]" Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits @@ -2068,9 +2073,9 @@ inclusive, from the value popped and store them into the corresponding bits in \fIdst\fR. . .IP -Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack. -Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the -value just popped. +Example: \fBpop:NXM_NX_REG2[0..5]\fR or \fBpop:reg2[0..5]\fR pops the +value from top of the stack. Set register 2 bits 0 through 5, +inclusive, based on bits 0 through 5 from the value just popped. . . .IP "\fBmultipath(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIn_links\fB, \fIarg\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR" @@ -2137,7 +2142,10 @@ above. Example: \fBbundle_load(eth_src, 0, hrw, ofport, NXM_NX_REG0[], slaves:4, 8)\fR uses an Ethernet source hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest Random Weight -algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR. +algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR. Also the +match field name can be used, for example, instead of 'NXM_NX_REG0' +the name 'reg0' can be used. When the while field is indicated the +empty brackets can also be left off. .IP Refer to \fBnicira\-ext.h\fR for more details. . @@ -3280,7 +3288,9 @@ Implements a level 2 MAC learning switch using the learn. \fBovs\-ofctl add\-flow br0 'table=0,priority=0 actions=load:3->NXM_NX_REG0[0..15],learn(table=0,priority=1,idle_timeout=10,NXM_OF_ETH_SRC[],NXM_OF_VLAN_TCI[0..11],output:NXM_NX_REG0[0..15]),output:2\fR In this use of a learn action, the first packet from each source MAC will be sent to port 2. Subsequent packets will be output to port 3, -with an idle timeout of 10 seconds. +with an idle timeout of 10 seconds. Also the match field names can be +used, for example, instead of 'NXM_NX_REG0' the name 'reg0' can be +used. Empty brackets can also be left off. . Additional examples may be found documented as part of related sections. . -- 2.1.4 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev