The new odp sample attributes allow userspace to specify a group_id and user-defined cookie to be passed down to psample.
Add support for parsing and formatting such action. Signed-off-by: Adrian Moreno <amore...@redhat.com> --- include/linux/openvswitch.h | 49 +++++++++--- lib/odp-execute.c | 3 + lib/odp-util.c | 150 ++++++++++++++++++++++++++--------- ofproto/ofproto-dpif-ipfix.c | 2 + python/ovs/flow/odp.py | 2 + tests/odp.at | 5 ++ 6 files changed, 163 insertions(+), 48 deletions(-) diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index d9fb991ef..3e748405c 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -696,6 +696,7 @@ enum ovs_flow_attr { #define OVS_UFID_F_OMIT_MASK (1 << 1) #define OVS_UFID_F_OMIT_ACTIONS (1 << 2) +#define OVS_PSAMPLE_COOKIE_MAX_SIZE 16 /** * enum ovs_sample_attr - Attributes for %OVS_ACTION_ATTR_SAMPLE action. * @OVS_SAMPLE_ATTR_PROBABILITY: 32-bit fraction of packets to sample with @@ -703,15 +704,27 @@ enum ovs_flow_attr { * %UINT32_MAX samples all packets and intermediate values sample intermediate * fractions of packets. * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event. - * Actions are passed as nested attributes. + * Actions are passed as nested attributes. Optional if + * OVS_SAMPLE_ATTR_PSAMPLE_GROUP is set. + * @OVS_SAMPLE_ATTR_PSAMPLE_GROUP: A 32-bit number to be used as psample group. + * Optional if OVS_SAMPLE_ATTR_ACTIONS is set. + * @OVS_SAMPLE_ATTR_PSAMPLE_COOKIE: A variable-length binary cookie that, if + * provided, will be copied to the psample cookie. * - * Executes the specified actions with the given probability on a per-packet - * basis. + * Either OVS_SAMPLE_ATTR_PSAMPLE_GROUP or OVS_SAMPLE_ATTR_ACTIONS must be + * specified. + * + * Executes the specified actions and/or sends the packet to psample + * with the given probability on a per-packet basis. */ enum ovs_sample_attr { OVS_SAMPLE_ATTR_UNSPEC, - OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */ - OVS_SAMPLE_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ + OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */ + OVS_SAMPLE_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_ + * attributes. + */ + OVS_SAMPLE_ATTR_PSAMPLE_GROUP, /* u32 number */ + OVS_SAMPLE_ATTR_PSAMPLE_COOKIE, /* binary */ __OVS_SAMPLE_ATTR_MAX, #ifdef __KERNEL__ @@ -722,13 +735,27 @@ enum ovs_sample_attr { #define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1) #ifdef __KERNEL__ + +/* Definition for flags in struct sample_arg. */ +enum { + /* When actions in sample will not change the flows. */ + OVS_SAMPLE_ARG_FLAG_EXEC = 1 << 0, + /* When set, the packet will be sent to psample. */ + OVS_SAMPLE_ARG_FLAG_PSAMPLE = 1 << 1, +}; + struct sample_arg { - bool exec; /* When true, actions in sample will not - * change flow keys. False otherwise. - */ - u32 probability; /* Same value as - * 'OVS_SAMPLE_ATTR_PROBABILITY'. - */ + u16 flags; /* Flags that modify the behavior of the + * action. See SAMPLE_ARG_FLAG_* + */ + u32 probability; /* Same value as + * 'OVS_SAMPLE_ATTR_PROBABILITY'. + */ + u32 group_id; /* Same value as + * 'OVS_SAMPLE_ATTR_PSAMPLE_GROUP'. + */ + u8 cookie_len; /* Length of psample cookie */ + char cookie[OVS_PSAMPLE_COOKIE_MAX_SIZE]; /* psample cookie data. */ }; #endif diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 081e4d432..d8ee93429 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -716,6 +716,9 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal, subactions = a; break; + /* Ignored in userspace datapath. */ + case OVS_SAMPLE_ATTR_PSAMPLE_GROUP: + case OVS_SAMPLE_ATTR_PSAMPLE_COOKIE: case OVS_SAMPLE_ATTR_UNSPEC: case __OVS_SAMPLE_ATTR_MAX: default: diff --git a/lib/odp-util.c b/lib/odp-util.c index 21f34d955..611b5229e 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -227,12 +227,16 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr, { static const struct nl_policy ovs_sample_policy[] = { [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 }, - [OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED } + [OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED, + .optional = true }, + [OVS_SAMPLE_ATTR_PSAMPLE_GROUP] = { .type = NL_A_U32, + .optional = true }, + [OVS_SAMPLE_ATTR_PSAMPLE_COOKIE] = { .type = NL_A_UNSPEC, + .optional = true } }; struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)]; + const struct nlattr *nla; double percentage; - const struct nlattr *nla_acts; - int len; ds_put_cstr(ds, "sample"); @@ -244,13 +248,33 @@ format_odp_sample_action(struct ds *ds, const struct nlattr *attr, percentage = (100.0 * nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY])) / UINT32_MAX; - ds_put_format(ds, "(sample=%.1f%%,", percentage); + ds_put_format(ds, "(sample=%.1f%%", percentage); - ds_put_cstr(ds, "actions("); - nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]); - len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]); - format_odp_actions(ds, nla_acts, len, portno_names); - ds_put_format(ds, "))"); + nla = a[OVS_SAMPLE_ATTR_PSAMPLE_GROUP]; + if (nla) { + ds_put_format(ds, ",group_id=%d", nl_attr_get_u32(nla)); + + nla = a[OVS_SAMPLE_ATTR_PSAMPLE_COOKIE]; + + if (nla) { + size_t i; + const uint8_t *c = nl_attr_get(nla); + ds_put_cstr(ds, ",cookie="); + for (i = 0; i < nl_attr_get_size(nla); i++) { + ds_put_format(ds, "%02x", c[i]); + } + } + } + + nla = a[OVS_SAMPLE_ATTR_ACTIONS]; + if (nla) { + ds_put_cstr(ds, ",actions("); + format_odp_actions(ds, nl_attr_get(nla), nl_attr_get_size(nla), + portno_names); + ds_put_format(ds, "))"); + } else { + ds_put_format(ds, ")"); + } } static void @@ -1348,6 +1372,84 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions, } } +static int +parse_action_list(struct parse_odp_context *context, const char *s, + struct ofpbuf *actions); +static int +parse_odp_sample_action(const char *s, struct parse_odp_context *context, + struct ofpbuf *actions) +{ + double percentage; + uint32_t group_id; + size_t sample_ofs, actions_ofs; + double probability; + int parsed = 0; + int n; + + if (!ovs_scan(s, "sample(sample=%lf%%,%n", &percentage, &parsed)) { + return -EINVAL; + } + + probability = floor(UINT32_MAX * (percentage / 100.0) + .5); + sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE); + nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY, + (probability <= 0 ? 0 + : probability >= UINT32_MAX ? UINT32_MAX + : probability)); + + if (ovs_scan(s + parsed, "group_id=%"PRIu32"%n", &group_id, &n)) { + parsed += n; + + nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PSAMPLE_GROUP, group_id); + + if (ovs_scan(s + parsed, ",cookie=%n", &n)) { + struct ofpbuf buf; + size_t size; + char *end; + + parsed += n; + ofpbuf_init(&buf, OVS_PSAMPLE_COOKIE_MAX_SIZE); + + end = ofpbuf_put_hex(&buf, s + parsed, &size); + if (!end || + size > OVS_PSAMPLE_COOKIE_MAX_SIZE || + (end[0] != ')' && end[0] != ',')) { + ofpbuf_uninit(&buf); + return -EINVAL; + } + + nl_msg_put_unspec(actions, OVS_SAMPLE_ATTR_PSAMPLE_COOKIE, + buf.data, buf.size); + + ofpbuf_uninit(&buf); + parsed = end - s; + } + if (s[parsed] == ')') { + nl_msg_end_nested(actions, sample_ofs); + return parsed + 1; + } else if (s[parsed] == ',') { + parsed += 1; + } else { + return -EINVAL; + } + } + + if (ovs_scan(s + parsed, "actions(%n", &n)) { + parsed += n; + actions_ofs = nl_msg_start_nested(actions, OVS_SAMPLE_ATTR_ACTIONS); + int retval = parse_action_list(context, s + parsed, actions); + if (retval < 0) { + return retval; + } + parsed += retval; + nl_msg_end_nested(actions, actions_ofs); + nl_msg_end_nested(actions, sample_ofs); + return s[parsed + 1] == ')' ? parsed + 2 : -EINVAL; + } + + return -EINVAL; +} + /* Separate out parse_odp_userspace_action() function. */ static int parse_odp_userspace_action(const char *s, struct ofpbuf *actions) @@ -2561,34 +2663,8 @@ parse_odp_action__(struct parse_odp_context *context, const char *s, } { - double percentage; - int n = -1; - - if (ovs_scan(s, "sample(sample=%lf%%,actions(%n", &percentage, &n) - && percentage >= 0. && percentage <= 100.0) { - size_t sample_ofs, actions_ofs; - double probability; - - probability = floor(UINT32_MAX * (percentage / 100.0) + .5); - sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE); - nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY, - (probability <= 0 ? 0 - : probability >= UINT32_MAX ? UINT32_MAX - : probability)); - - actions_ofs = nl_msg_start_nested(actions, - OVS_SAMPLE_ATTR_ACTIONS); - int retval = parse_action_list(context, s + n, actions); - if (retval < 0) { - return retval; - } - - - n += retval; - nl_msg_end_nested(actions, actions_ofs); - nl_msg_end_nested(actions, sample_ofs); - - return s[n + 1] == ')' ? n + 2 : -EINVAL; + if (ovs_scan(s, "sample(")) { + return parse_odp_sample_action(s, context, actions); } } diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index cd65dae7e..407b2b38a 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -3064,6 +3064,8 @@ dpif_ipfix_read_sample_actions(const struct flow *flow, &sample_actions); break; + case OVS_SAMPLE_ATTR_PSAMPLE_GROUP: + case OVS_SAMPLE_ATTR_PSAMPLE_COOKIE: case OVS_SAMPLE_ATTR_UNSPEC: case __OVS_SAMPLE_ATTR_MAX: default: diff --git a/python/ovs/flow/odp.py b/python/ovs/flow/odp.py index 7d9b165d4..bc0495eb4 100644 --- a/python/ovs/flow/odp.py +++ b/python/ovs/flow/odp.py @@ -349,6 +349,8 @@ class ODPFlow(Flow): KVDecoders( { "sample": (lambda x: float(x.strip("%"))), + "group_id": decode_int, + "cookie": decode_default, "actions": nested_kv_decoder( KVDecoders( decoders=_decoders, diff --git a/tests/odp.at b/tests/odp.at index ba20604e4..4a2435c97 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -327,6 +327,9 @@ push_vlan(tpid=0x9100,vid=13,pcp=5) push_vlan(tpid=0x9100,vid=13,pcp=5,cfi=0) pop_vlan sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2))) +sample(sample=9.7%,group_id=25) +sample(sample=9.7%,group_id=12,cookie=0102) +sample(sample=9.7%,group_id=12,cookie=0102030405060708090a0b0c0d0e0f,actions(1,2,3,push_vlan(vid=1,pcp=2))) set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(df|csum|key))) set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key))) tnl_pop(4) @@ -406,11 +409,13 @@ AT_DATA([actions.txt], [dnl encap_nsh@:{@ tnl_push(tnl_port(6),header(size=94,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=2,segs(2001:cafe::90,2001:cafe::91))),out_port(1)) tnl_push(tnl_port(6),header(size=126,type=112,eth(dst=f8:bc:12:44:34:b6,src=f8:bc:12:46:58:e0,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2001:cafe::92,label=0,proto=43,tclass=0x0,hlimit=64),srv6(segments_left=2,segs(2001:cafe::90,2001:cafe::91,2001:cafe::92,2001:cafe::93))),out_port(1)) +sample(sample=9.7%,group_id=12,cookie=0102030405060708090a0b0c0d0e0f0f0f) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [dnl odp_actions_from_string: error odp_actions_from_string: error odp_actions_from_string: error +odp_actions_from_string: error ]) AT_CLEANUP -- 2.44.0 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev