From patchwork Wed Apr 24 13:50:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Adri=C3=A1n_Moreno?= X-Patchwork-Id: 1927180 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dFCdlXqG; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VPgTz0GChz1yZy for ; Wed, 24 Apr 2024 23:55:03 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp2.osuosl.org (Postfix) with ESMTP id 69749415C4; Wed, 24 Apr 2024 13:55:01 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp2.osuosl.org ([127.0.0.1]) by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id vx74S9ltQJlD; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp2.osuosl.org C48EB408AF Authentication-Results: smtp2.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dFCdlXqG Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp2.osuosl.org (Postfix) with ESMTPS id C48EB408AF; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id A45B0C0077; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 6FEEAC0037 for ; Wed, 24 Apr 2024 13:54:58 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 5FCD96062D for ; Wed, 24 Apr 2024 13:54:58 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id Jptmc24pWqgk for ; Wed, 24 Apr 2024 13:54:57 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=amorenoz@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org 2B1B9605A0 Authentication-Results: smtp3.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org 2B1B9605A0 Authentication-Results: smtp3.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dFCdlXqG Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp3.osuosl.org (Postfix) with ESMTPS id 2B1B9605A0 for ; Wed, 24 Apr 2024 13:54:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1713966896; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jIPqwhfGK1wcmX33k6tBJ0wJeSbk/ZBhsvCTykKHqkw=; b=dFCdlXqGm3yn5Llb5DmQqmGsULBFBskO0SNpm2/e4/qLxRUo1mbYutpPMRadqpHRCLexnW 0x+SgnAnAY+j/Bu+ZcnIG8VrW4D2lUQFcqPh8Cpjnw5GpE8lOEZonMbZjBURhAHFAzWGFz 4TmG6PxY4o1eBNzRpihUjBGrXNgHwIo= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-70-_LhlJPW2PiOSXqOiu7ySag-1; Wed, 24 Apr 2024 09:54:51 -0400 X-MC-Unique: _LhlJPW2PiOSXqOiu7ySag-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 35ABB3C02B54; Wed, 24 Apr 2024 13:54:51 +0000 (UTC) Received: from antares.redhat.com (unknown [10.39.193.98]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1D8CE1C06798; Wed, 24 Apr 2024 13:54:48 +0000 (UTC) From: Adrian Moreno To: netdev@vger.kernel.org Date: Wed, 24 Apr 2024 15:50:53 +0200 Message-ID: <20240424135109.3524355-7-amorenoz@redhat.com> In-Reply-To: <20240424135109.3524355-1-amorenoz@redhat.com> References: <20240424135109.3524355-1-amorenoz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 Cc: dev@openvswitch.org, Paolo Abeni , Donald Hunter , linux-kernel@vger.kernel.org, i.maximets@ovn.org, Eric Dumazet , horms@kernel.org, Jakub Kicinski , "David S. Miller" Subject: [ovs-dev] [PATCH net-next 6/8] net:openvswitch: add psample support X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Add support for psample sampling via two new attributes to the OVS_ACTION_ATTR_SAMPLE action. OVS_SAMPLE_ATTR_PSAMPLE_GROUP used to pass an integer psample group_id. OVS_SAMPLE_ATTR_PSAMPLE_COOKIE used to pass a variable-length binary cookie that will be forwared to psample. The maximum length of the user-defined cookie is set to 16, same as tc_cookie, to discourage using cookies that will not be offloadable. In order to simplify the internal processing of the action and given the maximum size of the cookie is relatively small, add both fields to the internal-only struct sample_arg. The presence of a group_id mandates that the action shall called the psample module to multicast the packet with such group_id and the user-provided cookie if present. This behavior is orthonogal to also executing the nested actions if present. Signed-off-by: Adrian Moreno --- Documentation/netlink/specs/ovs_flow.yaml | 6 ++ include/uapi/linux/openvswitch.h | 49 ++++++++++---- net/openvswitch/actions.c | 51 +++++++++++++-- net/openvswitch/flow_netlink.c | 80 ++++++++++++++++++----- 4 files changed, 153 insertions(+), 33 deletions(-) diff --git a/Documentation/netlink/specs/ovs_flow.yaml b/Documentation/netlink/specs/ovs_flow.yaml index 4fdfc6b5cae9..5543c2937225 100644 --- a/Documentation/netlink/specs/ovs_flow.yaml +++ b/Documentation/netlink/specs/ovs_flow.yaml @@ -825,6 +825,12 @@ attribute-sets: name: actions type: nest nested-attributes: action-attrs + - + name: psample_group + type: u32 + - + name: psample_cookie + type: binary - name: userspace-attrs enum-name: ovs-userspace-attr diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index efc82c318fa2..e9cd6f3a952d 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -639,6 +639,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 @@ -646,15 +647,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__ @@ -665,13 +678,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 set, 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/net/openvswitch/actions.c b/net/openvswitch/actions.c index 6fcd7e2ca81f..eb3166986fd2 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "datapath.h" @@ -1025,6 +1026,34 @@ static int dec_ttl_exception_handler(struct datapath *dp, struct sk_buff *skb, return 0; } +static int ovs_psample_packet(struct datapath *dp, struct sw_flow_key *key, + const struct sample_arg *arg, + struct sk_buff *skb) +{ + struct psample_group psample_group = {}; + struct psample_metadata md = {}; + struct vport *input_vport; + u32 rate; + + psample_group.group_num = arg->group_id; + psample_group.net = ovs_dp_get_net(dp); + + input_vport = ovs_vport_rcu(dp, key->phy.in_port); + if (!input_vport) + input_vport = ovs_vport_rcu(dp, OVSP_LOCAL); + + md.in_ifindex = input_vport->dev->ifindex; + md.user_cookie = arg->cookie_len ? &arg->cookie[0] : NULL; + md.user_cookie_len = arg->cookie_len; + md.trunc_size = skb->len; + + rate = arg->probability ? U32_MAX / arg->probability : 0; + + psample_sample_packet(&psample_group, skb, rate, &md); + + return 0; +} + /* When 'last' is true, sample() should always consume the 'skb'. * Otherwise, sample() should keep 'skb' intact regardless what * actions are executed within sample(). @@ -1033,11 +1062,12 @@ static int sample(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, bool last) { - struct nlattr *actions; + const struct sample_arg *arg; struct nlattr *sample_arg; int rem = nla_len(attr); - const struct sample_arg *arg; + struct nlattr *actions; bool clone_flow_key; + int ret; /* The first action is always 'OVS_SAMPLE_ATTR_ARG'. */ sample_arg = nla_data(attr); @@ -1051,9 +1081,20 @@ static int sample(struct datapath *dp, struct sk_buff *skb, return 0; } - clone_flow_key = !arg->exec; - return clone_execute(dp, skb, key, 0, actions, rem, last, - clone_flow_key); + if (arg->flags & OVS_SAMPLE_ARG_FLAG_PSAMPLE) { + ret = ovs_psample_packet(dp, key, arg, skb); + if (ret) + return ret; + } + + if (nla_ok(actions, rem)) { + clone_flow_key = !(arg->flags & OVS_SAMPLE_ARG_FLAG_EXEC); + ret = clone_execute(dp, skb, key, 0, actions, rem, last, + clone_flow_key); + } else if (last) { + ovs_kfree_skb_reason(skb, OVS_DROP_LAST_ACTION); + } + return ret; } /* When 'last' is true, clone() should always consume the 'skb'. diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index f224d9bcea5e..1a348d3905fc 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2561,6 +2561,9 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, u32 mpls_label_count, bool log, u32 depth); +static int copy_action(const struct nlattr *from, + struct sw_flow_actions **sfa, bool log); + static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, struct sw_flow_actions **sfa, @@ -2569,10 +2572,10 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, u32 depth) { const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; - const struct nlattr *probability, *actions; + const struct nlattr *probability, *actions, *group, *cookie; + struct sample_arg arg = {}; const struct nlattr *a; int rem, start, err; - struct sample_arg arg; memset(attrs, 0, sizeof(attrs)); nla_for_each_nested(a, attr, rem) { @@ -2589,7 +2592,19 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, return -EINVAL; actions = attrs[OVS_SAMPLE_ATTR_ACTIONS]; - if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) + if (actions && (!nla_len(actions) || nla_len(actions) < NLA_HDRLEN)) + return -EINVAL; + + group = attrs[OVS_SAMPLE_ATTR_PSAMPLE_GROUP]; + if (group && nla_len(group) != sizeof(u32)) + return -EINVAL; + + cookie = attrs[OVS_SAMPLE_ATTR_PSAMPLE_COOKIE]; + if (cookie && + (!group || nla_len(cookie) > OVS_PSAMPLE_COOKIE_MAX_SIZE)) + return -EINVAL; + + if (!group && !actions) return -EINVAL; /* validation done, copy sample action. */ @@ -2608,7 +2623,19 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, * If the sample is the last action, it can always be excuted * rather than deferred. */ - arg.exec = last || !actions_may_change_flow(actions); + if (actions && (last || !actions_may_change_flow(actions))) + arg.flags |= OVS_SAMPLE_ARG_FLAG_EXEC; + + if (group) { + arg.flags |= OVS_SAMPLE_ARG_FLAG_PSAMPLE; + arg.group_id = nla_get_u32(group); + } + + if (cookie) { + memcpy(&arg.cookie[0], nla_data(cookie), nla_len(cookie)); + arg.cookie_len = nla_len(cookie); + } + arg.probability = nla_get_u32(probability); err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg), @@ -2616,12 +2643,13 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, if (err) return err; - err = __ovs_nla_copy_actions(net, actions, key, sfa, - eth_type, vlan_tci, mpls_label_count, log, - depth + 1); - - if (err) - return err; + if (actions) { + err = __ovs_nla_copy_actions(net, actions, key, sfa, + eth_type, vlan_tci, + mpls_label_count, log, depth + 1); + if (err) + return err; + } add_nested_action_end(*sfa, start); @@ -3553,20 +3581,38 @@ static int sample_action_to_attr(const struct nlattr *attr, goto out; } - ac_start = nla_nest_start_noflag(skb, OVS_SAMPLE_ATTR_ACTIONS); - if (!ac_start) { - err = -EMSGSIZE; - goto out; + if (arg->flags & OVS_SAMPLE_ARG_FLAG_PSAMPLE) { + if (nla_put_u32(skb, OVS_SAMPLE_ATTR_PSAMPLE_GROUP, + arg->group_id)) { + err = -EMSGSIZE; + goto out; + } + + if (arg->cookie_len && + nla_put(skb, OVS_SAMPLE_ATTR_PSAMPLE_COOKIE, + arg->cookie_len, &arg->cookie[0])) { + err = -EMSGSIZE; + goto out; + } } - err = ovs_nla_put_actions(actions, rem, skb); + if (nla_ok(actions, rem)) { + ac_start = nla_nest_start_noflag(skb, OVS_SAMPLE_ATTR_ACTIONS); + if (!ac_start) { + err = -EMSGSIZE; + goto out; + } + err = ovs_nla_put_actions(actions, rem, skb); + } out: if (err) { - nla_nest_cancel(skb, ac_start); + if (ac_start) + nla_nest_cancel(skb, ac_start); nla_nest_cancel(skb, start); } else { - nla_nest_end(skb, ac_start); + if (ac_start) + nla_nest_end(skb, ac_start); nla_nest_end(skb, start); } From patchwork Wed Apr 24 13:50:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Adri=C3=A1n_Moreno?= X-Patchwork-Id: 1927181 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Je8uenkB; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::137; helo=smtp4.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VPgV16vPtz23tY for ; Wed, 24 Apr 2024 23:55:05 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp4.osuosl.org (Postfix) with ESMTP id 25216408FF; Wed, 24 Apr 2024 13:55:04 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp4.osuosl.org ([127.0.0.1]) by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 7269aPyzQezC; Wed, 24 Apr 2024 13:55:02 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp4.osuosl.org 316D64087D Authentication-Results: smtp4.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Je8uenkB Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [IPv6:2605:bc80:3010:104::8cd3:938]) by smtp4.osuosl.org (Postfix) with ESMTPS id 316D64087D; Wed, 24 Apr 2024 13:55:02 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id DE20BC008E; Wed, 24 Apr 2024 13:55:01 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) by lists.linuxfoundation.org (Postfix) with ESMTP id C7B26C0DD2 for ; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id 9E3F96062D for ; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 7kj_PDu03tMw for ; Wed, 24 Apr 2024 13:54:59 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=amorenoz@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp3.osuosl.org D0D56605A0 Authentication-Results: smtp3.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org D0D56605A0 Authentication-Results: smtp3.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Je8uenkB Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by smtp3.osuosl.org (Postfix) with ESMTPS id D0D56605A0 for ; Wed, 24 Apr 2024 13:54:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1713966897; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=z8Yzpk3KTnFdMHRu4agPz/+OCowB+XjwxhNrbJUlANE=; b=Je8uenkBT/1lxDYl3GRx0Q82aXUMwiYo41v8StN2qNrpq7S8YFpmKFfUy0QNwhWcaPScuv eQo3bSgZir5GmhEB0B/+lnqe637AudijjYafODMLJ9xccix+jWJXkJGn+CikuUaURabssp 49QsQg1L0JoThWyUhAfMOgTkZpWltnc= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-436-gVewJ_sWPXmhNDPqLA0nCg-1; Wed, 24 Apr 2024 09:54:54 -0400 X-MC-Unique: gVewJ_sWPXmhNDPqLA0nCg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id DC6F43C02B49; Wed, 24 Apr 2024 13:54:53 +0000 (UTC) Received: from antares.redhat.com (unknown [10.39.193.98]) by smtp.corp.redhat.com (Postfix) with ESMTP id ACE0E1C060D0; Wed, 24 Apr 2024 13:54:51 +0000 (UTC) From: Adrian Moreno To: netdev@vger.kernel.org Date: Wed, 24 Apr 2024 15:50:54 +0200 Message-ID: <20240424135109.3524355-8-amorenoz@redhat.com> In-Reply-To: <20240424135109.3524355-1-amorenoz@redhat.com> References: <20240424135109.3524355-1-amorenoz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 Cc: dev@openvswitch.org, Paolo Abeni , linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, i.maximets@ovn.org, Eric Dumazet , horms@kernel.org, Jakub Kicinski , Shuah Khan , "David S. Miller" Subject: [ovs-dev] [PATCH net-next 7/8] selftests: openvswitch: add sample action. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Add sample action support to ovs-dpctl.py. Signed-off-by: Adrian Moreno --- .../selftests/net/openvswitch/ovs-dpctl.py | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index 1dd057afd3fb..3a2dddc57e42 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -8,6 +8,7 @@ import argparse import errno import ipaddress import logging +import math import multiprocessing import re import struct @@ -58,6 +59,7 @@ OVS_FLOW_CMD_DEL = 2 OVS_FLOW_CMD_GET = 3 OVS_FLOW_CMD_SET = 4 +UINT32_MAX = 0xFFFFFFFF def macstr(mac): outstr = ":".join(["%02X" % i for i in mac]) @@ -285,7 +287,7 @@ class ovsactions(nla): ("OVS_ACTION_ATTR_SET", "none"), ("OVS_ACTION_ATTR_PUSH_VLAN", "none"), ("OVS_ACTION_ATTR_POP_VLAN", "flag"), - ("OVS_ACTION_ATTR_SAMPLE", "none"), + ("OVS_ACTION_ATTR_SAMPLE", "sample"), ("OVS_ACTION_ATTR_RECIRC", "uint32"), ("OVS_ACTION_ATTR_HASH", "none"), ("OVS_ACTION_ATTR_PUSH_MPLS", "none"), @@ -306,6 +308,91 @@ class ovsactions(nla): ("OVS_ACTION_ATTR_DROP", "uint32"), ) + class sample(nla): + nla_flags = NLA_F_NESTED + + nla_map = ( + ("OVS_SAMPLE_ATTR_UNSPEC", "none"), + ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"), + ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"), + ("OVS_SAMPLE_ATTR_PSAMPLE_GROUP", "uint32"), + ("OVS_SAMPLE_ATTR_PSAMPLE_COOKIE", "array(uint8)"), + ) + + def dpstr(self, more=False): + args = [] + + args.append("sample={:.2f}%".format( + 100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") / + UINT32_MAX)) + + group = self.get_attr("OVS_SAMPLE_ATTR_PSAMPLE_GROUP") + cookie = self.get_attr("OVS_SAMPLE_ATTR_PSAMPLE_COOKIE") + actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS") + + if group: + args.append("group_id=%d" % group) + if cookie: + args.append("cookie=%s" % + "".join(format(x, "02x") for x in cookie)) + if actions: + args.append("actions(%s)" % actions.dpstr(more)) + + return "sample(%s)" % ",".join(args) + + def parse(self, actstr): + """ Parses the input action string and populates the internal + attributes. The input string must start with "sample(" + + Returns the remaining action string. + Raises ValueError if the action string has invalid content. + """ + + def parse_nested_actions(actstr): + subacts = ovsactions() + parsed_len = subacts.parse(actstr) + return subacts, parsed_len + + def percent_to_rate(percent): + percent = float(percent.strip('%')) + return int(math.floor(UINT32_MAX * (percent / 100.0) + .5)) + + for (key, attr, func) in ( + ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate), + ("group_id", "OVS_SAMPLE_ATTR_PSAMPLE_GROUP", int), + ("cookie", "OVS_SAMPLE_ATTR_PSAMPLE_COOKIE", + lambda x: list(bytearray.fromhex(x))), + ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions), + ): + if not actstr.startswith(key): + continue + + actstr = actstr[len(key) :] + + if not func: + self["attrs"].append([attr, None]) + continue + + # The length of complex attributes cannot be determined + # beforehand and must be reported by the parsing func. + delim = actstr[0] + actstr = actstr[1:] + if delim == "=": + pos = strcspn(actstr, ",)") + datum = func(actstr[:pos]) + elif delim == "(": + datum, pos = func(actstr) + + self["attrs"].append([attr, datum]) + actstr = actstr[pos:] + actstr = actstr[strspn(actstr, ", ") :] + + if actstr[0] != ")": + raise ValueError("Action str: '%s' unbalanced" % actstr) + + return actstr[1:] + + class ctact(nla): nla_flags = NLA_F_NESTED @@ -637,6 +724,13 @@ class ovsactions(nla): self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact]) parsed = True + elif parse_starts_block(actstr, "sample(", False): + sampleact = self.sample() + actstr = sampleact.parse(actstr[len("sample(") : ]) + self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact]) + parsed = True + + actstr = actstr[strspn(actstr, ", ") :] while parencount > 0: parencount -= 1 From patchwork Wed Apr 24 13:50:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Adri=C3=A1n_Moreno?= X-Patchwork-Id: 1927182 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VVjqahxr; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org) Received: from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VPgV74Fqlz1yZr for ; Wed, 24 Apr 2024 23:55:11 +1000 (AEST) Received: from localhost (localhost [127.0.0.1]) by smtp3.osuosl.org (Postfix) with ESMTP id A5BAB60BF9; Wed, 24 Apr 2024 13:55:09 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp3.osuosl.org ([127.0.0.1]) by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id q3tXNjB9PLBn; Wed, 24 Apr 2024 13:55:07 +0000 (UTC) X-Comment: SPF check N/A for local connections - client-ip=140.211.9.56; helo=lists.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver= DKIM-Filter: OpenDKIM Filter v2.11.0 smtp3.osuosl.org C772460BF1 Authentication-Results: smtp3.osuosl.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VVjqahxr Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by smtp3.osuosl.org (Postfix) with ESMTPS id C772460BF1; Wed, 24 Apr 2024 13:55:07 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id AC241C0077; Wed, 24 Apr 2024 13:55:07 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2AE43C0DD0 for ; Wed, 24 Apr 2024 13:55:06 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by smtp1.osuosl.org (Postfix) with ESMTP id D10FD82079 for ; Wed, 24 Apr 2024 13:55:05 +0000 (UTC) X-Virus-Scanned: amavis at osuosl.org Received: from smtp1.osuosl.org ([127.0.0.1]) by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP id fvw5-BTFxiSd for ; Wed, 24 Apr 2024 13:55:04 +0000 (UTC) Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=170.10.129.124; helo=us-smtp-delivery-124.mimecast.com; envelope-from=amorenoz@redhat.com; receiver= DMARC-Filter: OpenDMARC Filter v1.4.2 smtp1.osuosl.org 2237B8209F Authentication-Results: smtp1.osuosl.org; dmarc=pass (p=none dis=none) header.from=redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 smtp1.osuosl.org 2237B8209F Authentication-Results: smtp1.osuosl.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=VVjqahxr Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by smtp1.osuosl.org (Postfix) with ESMTPS id 2237B8209F for ; Wed, 24 Apr 2024 13:55:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1713966902; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NRfG44Ph4wnW5G/uRG2RMT/4P6AMh9CGQOP3sRR/P0s=; b=VVjqahxrK5JEM2KM00vJse80quo7SZjEn4qCKwxp8zKkY/FOiFksvNrSzcRTwC+/J6vvL3 ex69n5Dd21XfQ/NCt2y1ygfqVho7HOmC+SBPzy2JtERu+gPx/xQjAyUsVydGy6g4qySmzf fJq1zf1Jc2q+adcP2KoM4ukIbtSwAW0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-411-aHlfTTb1N_y3PKS5ssXMKg-1; Wed, 24 Apr 2024 09:54:57 -0400 X-MC-Unique: aHlfTTb1N_y3PKS5ssXMKg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id BFC47823F1D; Wed, 24 Apr 2024 13:54:56 +0000 (UTC) Received: from antares.redhat.com (unknown [10.39.193.98]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8AB1C1C060D0; Wed, 24 Apr 2024 13:54:54 +0000 (UTC) From: Adrian Moreno To: netdev@vger.kernel.org Date: Wed, 24 Apr 2024 15:50:55 +0200 Message-ID: <20240424135109.3524355-9-amorenoz@redhat.com> In-Reply-To: <20240424135109.3524355-1-amorenoz@redhat.com> References: <20240424135109.3524355-1-amorenoz@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.7 Cc: dev@openvswitch.org, Paolo Abeni , linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, i.maximets@ovn.org, Eric Dumazet , horms@kernel.org, Jakub Kicinski , Shuah Khan , "David S. Miller" Subject: [ovs-dev] [PATCH net-next 8/8] selftests: openvswitch: add psample test X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Add a test to verify sampling packets via psample works. In order to do that, create a subcommand in ovs-dpctl.py to listen to on the psample multicast group and print samples. In order to also test simultaneous sFlow and psample actions, add missing parsing support for "userspace" action (via refactoring the one in sample). Signed-off-by: Adrian Moreno --- .../selftests/net/openvswitch/openvswitch.sh | 97 +++++++++- .../selftests/net/openvswitch/ovs-dpctl.py | 167 ++++++++++++++---- 2 files changed, 231 insertions(+), 33 deletions(-) diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh index 5cae53543849..7a2307a384a9 100755 --- a/tools/testing/selftests/net/openvswitch/openvswitch.sh +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh @@ -20,7 +20,8 @@ tests=" nat_related_v4 ip4-nat-related: ICMP related matches work with SNAT netlink_checks ovsnl: validate netlink attrs and settings upcall_interfaces ovs: test the upcall interfaces - drop_reason drop: test drop reasons are emitted" + drop_reason drop: test drop reasons are emitted + psample psample: Sampling packets with psample" info() { [ $VERBOSE = 0 ] || echo $* @@ -170,6 +171,19 @@ ovs_drop_reason_count() return `echo "$perf_output" | grep "$pattern" | wc -l` } +ovs_test_flow_fails () { + ERR_MSG="Flow actions may not be safe on all matching packets" + + PRE_TEST=$(dmesg | grep -c "${ERR_MSG}") + ovs_add_flow $@ &> /dev/null $@ && return 1 + POST_TEST=$(dmesg | grep -c "${ERR_MSG}") + + if [ "$PRE_TEST" == "$POST_TEST" ]; then + return 1 + fi + return 0 +} + usage() { echo echo "$0 [OPTIONS] [TEST]..." @@ -184,6 +198,87 @@ usage() { exit 1 } + +# psample test +# - samples packets with psample +test_psample() { + sbx_add "test_psample" || return $? + + # Add a datapath with per-vport dispatching. + ovs_add_dp "test_psample" psample -V 2:1 || return 1 + + info "create namespaces" + ovs_add_netns_and_veths "test_psample" "psample" \ + client c0 c1 172.31.110.10/24 -u || return 1 + ovs_add_netns_and_veths "test_psample" "psample" \ + server s0 s1 172.31.110.20/24 -u || return 1 + + # Check if psample actions can be configured. + ovs_add_flow "test_psample" psample \ + 'in_port(1),eth(),eth_type(0x0806),arp()' 'sample(sample=100%,group_id=1,cookie=0102)' + if [ $? == 1 ]; then + info "no support for psample - skipping" + ovs_exit_sig + return $ksft_skip + fi + + ovs_del_flows "test_psample" psample + + # Allow ARP + ovs_add_flow "test_psample" psample \ + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1 + ovs_add_flow "test_psample" psample \ + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1 + + # Test action verification. + OLDIFS=$IFS + IFS='*' + min_key='in_port(1),eth(),eth_type(0x800),ipv4()' + for testcase in \ + "cookie to large"*"sample(sample=100%,group_id=1,cookie=1615141312111009080706050403020100)" \ + "no group or action"*"sample(sample=100%)" \ + "no group or action with cookie"*"sample(sample=100%,cookie=deadbeef)"; + do + set -- $testcase; + ovs_test_flow_fails "test_psample" psample $min_key $2 + if [ $? == 1 ]; then + info "failed - $1" + return 1 + fi + done + IFS=$OLDIFS + + # Sample all traffic. In this case the sample action only has psample + # arguments. + ovs_add_flow "test_psample" psample \ + "in_port(1),eth(),eth_type(0x0800),ipv4(src=172.31.110.10,proto=1),icmp()" "sample(sample=100%,group_id=1,cookie=c0ffee),2" + + # Sample all traffic. In this case the sample action has both psample + # arguments and an upcall emulating simultaneous psample and + # sFlow / IPFIX. + nlpid=$(grep -E "listening on upcall packet handler" $ovs_dir/s0.out | cut -d ":" -f 2 | tr -d ' ') + ovs_add_flow "test_psample" psample \ + "in_port(2),eth(),eth_type(0x0800),ipv4(src=172.31.110.20,proto=1),icmp()" "sample(sample=100%,group_id=2,cookie=eeff0c,actions(userspace(pid=${nlpid},userdata=eeff0c))),1" + + # Record psample data. + python3 $ovs_base/ovs-dpctl.py psample >$ovs_dir/psample.out 2>$ovs_dir/psample.err & + pid=$! + on_exit "ovs_sbx test_psample kill -TERM $pid 2>/dev/null" + + # Send a single ping. + sleep 1 + ovs_sbx "test_psample" ip netns exec client ping -I c1 172.31.110.20 -c 1 || return 1 + sleep 1 + + # We should have received one userspace action upcall and 2 psample packets. + grep -E "userspace action command" $ovs_dir/s0.out >/dev/null 2>&1 || return 1 + + grep -E "rate:1,group:1,cookie:c0ffee" $ovs_dir/psample.out >/dev/null 2>&1 || return 1 + grep -E "rate:1,group:2,cookie:eeff0c" $ovs_dir/psample.out >/dev/null 2>&1 || return 1 + + return 0 +} + # drop_reason test # - drop packets and verify the right drop reason is reported test_drop_reason() { diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py index 3a2dddc57e42..2fb5bcfe9c36 100644 --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py @@ -27,8 +27,10 @@ try: from pyroute2.netlink import genlmsg from pyroute2.netlink import nla from pyroute2.netlink import nlmsg_atoms - from pyroute2.netlink.exceptions import NetlinkError + from pyroute2.netlink.event import EventSocket from pyroute2.netlink.generic import GenericNetlinkSocket + from pyroute2.netlink.nlsocket import Marshal + from pyroute2.netlink.exceptions import NetlinkError import pyroute2 except ModuleNotFoundError: @@ -269,6 +271,47 @@ def parse_extract_field( return str_skipped, data +def parse_attributes(actstr, attributes): + """Parses actstr according to attribute description. attributes must be + a list of tuples (name, attribute, parse_func), e.g: + ("pid", OVS_USERSPACE_ATTR_PID, int) + + Returns a list of parsed attributes followed by the remaining string. + """ + attrs = [] + for (key, attr, func) in attributes: + if not actstr.startswith(key): + continue + + actstr = actstr[len(key) :] + + if not func: + attrs.append([attr]) + continue + + # The length of complex attributes cannot be determined + # beforehand and must be reported by the parsing func. + delim = actstr[0] + actstr = actstr[1:] + if delim == "=": + pos = strcspn(actstr, ",)") + datum = func(actstr[:pos]) + elif delim == "(": + datum, pos = func(actstr) + + attrs.append([attr, datum]) + actstr = actstr[pos:] + + if delim == "(": + actstr = actstr[1:] + + actstr = actstr[strspn(actstr, ", ") :] + + if actstr[0] != ")": + raise ValueError("Action str: '%s' unbalanced" % actstr) + + return attrs, actstr[1:] + class ovs_dp_msg(genlmsg): # include the OVS version # We need a custom header rather than just being able to rely on @@ -357,41 +400,19 @@ class ovsactions(nla): percent = float(percent.strip('%')) return int(math.floor(UINT32_MAX * (percent / 100.0) + .5)) - for (key, attr, func) in ( + attrs_desc = ( ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate), ("group_id", "OVS_SAMPLE_ATTR_PSAMPLE_GROUP", int), ("cookie", "OVS_SAMPLE_ATTR_PSAMPLE_COOKIE", lambda x: list(bytearray.fromhex(x))), ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions), - ): - if not actstr.startswith(key): - continue - - actstr = actstr[len(key) :] - - if not func: - self["attrs"].append([attr, None]) - continue - - # The length of complex attributes cannot be determined - # beforehand and must be reported by the parsing func. - delim = actstr[0] - actstr = actstr[1:] - if delim == "=": - pos = strcspn(actstr, ",)") - datum = func(actstr[:pos]) - elif delim == "(": - datum, pos = func(actstr) - - self["attrs"].append([attr, datum]) - actstr = actstr[pos:] - actstr = actstr[strspn(actstr, ", ") :] - - if actstr[0] != ")": - raise ValueError("Action str: '%s' unbalanced" % actstr) + ) - return actstr[1:] + attrs, actstr = parse_attributes(actstr, attrs_desc) + for attr in attrs: + self["attrs"].append(attr) + return actstr class ctact(nla): nla_flags = NLA_F_NESTED @@ -521,6 +542,18 @@ class ovsactions(nla): print_str += ")" return print_str + def parse(self, actstr): + attrs_desc = ( + ("pid", "OVS_USERSPACE_ATTR_PID", int), + ("userdata", "OVS_USERSPACE_ATTR_USERDATA", + lambda x: list(bytearray.fromhex(x))), + ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int) + ) + attrs, actstr = parse_attributes(actstr, attrs_desc) + for attr in attrs: + self["attrs"].append(attr) + return actstr + def dpstr(self, more=False): print_str = "" @@ -730,6 +763,11 @@ class ovsactions(nla): self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact]) parsed = True + elif parse_starts_block(actstr, "userspace(", False): + uact = self.userspace() + actstr = uact.parse(actstr[len("userpsace(") : ]) + self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact]) + parsed = True actstr = actstr[strspn(actstr, ", ") :] while parencount > 0: @@ -2112,10 +2150,70 @@ class OvsFlow(GenericNetlinkSocket): print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True) def execute(self, packetmsg): - print("userspace execute command") + print("userspace execute command", flush=True) def action(self, packetmsg): - print("userspace action command") + print("userspace action command", flush=True) + + +class psample_sample(genlmsg): + nla_map = ( + ("PSAMPLE_ATTR_IIFINDEX", "none"), + ("PSAMPLE_ATTR_OIFINDEX", "none"), + ("PSAMPLE_ATTR_ORIGSIZE", "none"), + ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"), + ("PSAMPLE_ATTR_GROUP_SEQ", "none"), + ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"), + ("PSAMPLE_ATTR_DATA", "array(uint8)"), + ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"), + ("PSAMPLE_ATTR_TUNNEL", "none"), + ("PSAMPLE_ATTR_PAD", "none"), + ("PSAMPLE_ATTR_OUT_TC", "none"), + ("PSAMPLE_ATTR_OUT_TC_OCC", "none"), + ("PSAMPLE_ATTR_LATENCY", "none"), + ("PSAMPLE_ATTR_TIMESTAMP", "none"), + ("PSAMPLE_ATTR_PROTO", "none"), + ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"), + ) + + def dpstr(self): + fields = [] + data = "" + for (attr, value) in self["attrs"]: + if attr == "PSAMPLE_ATTR_SAMPLE_GROUP": + fields.append("group:%d" % value) + if attr == "PSAMPLE_ATTR_SAMPLE_RATE": + fields.append("rate:%d" % value) + if attr == "PSAMPLE_ATTR_USER_COOKIE": + value = "".join(format(x, "02x") for x in value) + fields.append("cookie:%s" % value) + if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0: + data = "data:%s" % "".join(format(x, "02x") for x in value) + + return ("%s %s" % (",".join(fields), data)).strip() + + +class psample_msg(Marshal): + PSAMPLE_CMD_SAMPLE = 0 + PSAMPLE_CMD_GET_GROUP = 1 + PSAMPLE_CMD_NEW_GROUP = 2 + PSAMPLE_CMD_DEL_GROUP = 3 + PSAMPLE_CMD_SET_FILTER = 4 + msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample} + + +class Psample(EventSocket): + genl_family = "psample" + mcast_groups = ["packets"] + marshal_class = psample_msg + + def read_samples(self): + while True: + try: + for msg in self.get(): + print(msg.dpstr(), flush=True) + except NetlinkError as ne: + raise ne def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): @@ -2175,7 +2273,7 @@ def main(argv): help="Increment 'verbose' output counter.", default=0, ) - subparsers = parser.add_subparsers() + subparsers = parser.add_subparsers(dest="subcommand") showdpcmd = subparsers.add_parser("show") showdpcmd.add_argument( @@ -2232,6 +2330,8 @@ def main(argv): delfscmd = subparsers.add_parser("del-flows") delfscmd.add_argument("flsbr", help="Datapath name") + subparsers.add_parser("psample") + args = parser.parse_args() if args.verbose > 0: @@ -2246,6 +2346,9 @@ def main(argv): sys.setrecursionlimit(100000) + if args.subcommand == "psample": + Psample().read_samples() + if hasattr(args, "showdp"): found = False for iface in ndb.interfaces: