From: Aaron Conole <aconole@redhat.com>
To: Minxi Hou <houminxi@gmail.com>
Cc: echaudro@redhat.com, i.maximets@ovn.org, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, shuah@kernel.org, netdev@vger.kernel.org,
dev@openvswitch.org, linux-kselftest@vger.kernel.org,
amorenoz@redhat.com
Subject: Re: [PATCH net-next] selftests/net/openvswitch: add flow modify test
Date: Fri, 05 Jun 2026 14:51:43 -0400 [thread overview]
Message-ID: <f7tv7bwhlc0.fsf@redhat.com> (raw)
In-Reply-To: <20260605124222.963815-1-houminxi@gmail.com> (Minxi Hou's message of "Fri, 5 Jun 2026 20:42:22 +0800")
Minxi Hou <houminxi@gmail.com> writes:
> Add mod_flow() and the mod-flow CLI command to ovs-dpctl.py, exercising
> OVS_FLOW_CMD_SET. Add test_flow_set which first modifies an existing
> flow with new actions and verifies the change via traffic, then modifies
> the same flow without actions and verifies the kernel handles the
> no-actions case gracefully.
>
> The no-actions path is unreachable from userspace OVS tools (dpctl
> mod-flow requires actions) but reachable via raw netlink. This is the
> code path where Adrian Moreno found a possible kfree_skb of ERR_PTR
> when reply allocation fails after locking.
>
> Make parse() skip OVS_FLOW_ATTR_ACTIONS when actstr is None so the
> kernel enters the post-lock allocation branch in ovs_flow_cmd_set().
>
> Suggested-by: Aaron Conole <aconole@redhat.com>
> Signed-off-by: Minxi Hou <houminxi@gmail.com>
> ---
Thanks for the really quick turnaround. I'll do a deeper review of the
code on Monday; I wanted to reply though because I was expecting to see
a splat in the selftest, but instead it passed. This confused me at
first, because I didn't realize that the net- targeted patches were also
applied in the net-next test run:
https://github.com/linux-netdev/testing/commit/30e78e3cb99d9abb5182c387abdf5ede29fb9589 ("net: openvswitch: fix possible kfree_skb of ERR_PTR")
But we will want to make sure that whenever this selftest would finally
get applied the above kfree_skb fix is also applied.
> .../selftests/net/openvswitch/openvswitch.sh | 72 +++++++++++++++++++
> .../selftests/net/openvswitch/ovs-dpctl.py | 39 +++++++++-
> 2 files changed, 108 insertions(+), 3 deletions(-)
>
> diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
> index a415e9dec8cd..71190bc662f9 100755
> --- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
> +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
> @@ -30,6 +30,7 @@ tests="
> drop_reason drop: test drop reasons are emitted
> pop_vlan vlan: POP_VLAN action strips tag
> dec_ttl ttl: dec_ttl decrements IP TTL
> + flow_set flow-set: Flow modify
> psample psample: Sampling packets with psample"
>
> info() {
> @@ -193,6 +194,23 @@ ovs_add_flow () {
> return 0
> }
>
> +ovs_mod_flow () {
> + if [ -n "$4" ]; then
> + info "Modifying flow: sbx:$1 br:$2 flow:$3 act:$4"
> + ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py \
> + mod-flow "$2" "$3" "$4"
> + else
> + info "Modifying flow (no actions): sbx:$1 br:$2 flow:$3"
> + ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py \
> + mod-flow "$2" "$3"
> + fi
> + if [ $? -ne 0 ]; then
> + info "Flow modify [ $3 ] failed"
> + return 1
> + fi
> + return 0
> +}
> +
> ovs_del_flows () {
> info "Deleting all flows from DP: sbx:$1 br:$2"
> ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py del-flows "$2"
> @@ -300,6 +318,60 @@ test_dec_ttl() {
> return 0
> }
>
> +test_flow_set() {
> + sbx_add "test_flow_set" || return $?
> + ovs_add_dp "test_flow_set" flowset || return 1
> +
> + info "create namespaces"
> + for ns in client server; do
> + ovs_add_netns_and_veths "test_flow_set" "flowset" "$ns" \
> + "${ns:0:1}0" "${ns:0:1}1" || return 1
> + done
> +
> + ip netns exec client ip addr add 10.0.0.1/24 dev c1
> + ip netns exec client ip link set c1 up
> + ip netns exec server ip addr add 10.0.0.2/24 dev s1
> + ip netns exec server ip link set s1 up
> +
> + ovs_add_flow "test_flow_set" flowset \
> + 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1
> + ovs_add_flow "test_flow_set" flowset \
> + 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1
> +
> + local fwd_flow="ufid:00000001-0002-0003-0004-000500060007"
> + fwd_flow="$fwd_flow,in_port(1),eth(),eth_type(0x0800),ipv4()"
> +
> + ovs_add_flow "test_flow_set" flowset "$fwd_flow" '2' \
> + || return 1
> + ovs_add_flow "test_flow_set" flowset \
> + 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1
> +
> + info "verify initial forwarding"
> + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \
> + 10.0.0.2 || return 1
> +
> + info "mod-flow with new actions (change to drop)"
> + ovs_mod_flow "test_flow_set" flowset "$fwd_flow" 'drop' \
> + || return 1
> +
> + info "verify traffic is now dropped"
> + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \
> + 10.0.0.2 >/dev/null 2>&1 \
> + && { info "FAIL: ping should fail after mod-flow to drop"
> + return 1; }
> +
> + info "mod-flow without actions"
> + ovs_mod_flow "test_flow_set" flowset "$fwd_flow" || return 1
> +
> + info "verify drop actions unchanged"
> + ovs_sbx "test_flow_set" ip netns exec client ping -c 1 -W 2 \
> + 10.0.0.2 >/dev/null 2>&1 \
> + && { info "FAIL: ping should still fail after no-actions set"
> + return 1; }
> +
> + return 0
> +}
> +
> # psample test
> # - use psample to observe packets
> test_psample() {
> diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> index 3342e295293d..9113473d425f 100644
> --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> @@ -2702,9 +2702,10 @@ class OvsFlow(GenericNetlinkSocket):
> self["attrs"].append(["OVS_FLOW_ATTR_KEY", k])
> self["attrs"].append(["OVS_FLOW_ATTR_MASK", m])
>
> - a = ovsactions()
> - a.parse(actstr)
> - self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])
> + if actstr is not None:
> + a = ovsactions()
> + a.parse(actstr)
> + self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])
>
> def __init__(self):
> GenericNetlinkSocket.__init__(self)
> @@ -2738,6 +2739,24 @@ class OvsFlow(GenericNetlinkSocket):
> raise ne
> return reply
>
> + def mod_flow(self, dpifindex, flowmsg):
> + flowmsg["cmd"] = OVS_FLOW_CMD_SET
> + flowmsg["version"] = OVS_DATAPATH_VERSION
> + flowmsg["reserved"] = 0
> + flowmsg["dpifindex"] = dpifindex
> +
> + try:
> + reply = self.nlm_request(
> + flowmsg,
> + msg_type=self.prid,
> + msg_flags=NLM_F_REQUEST | NLM_F_ACK,
> + )
> + reply = reply[0]
> + except NetlinkError as ne:
> + print(flowmsg)
> + raise ne
> + return reply
> +
> def del_flows(self, dpifindex):
> """
> Send a del message to the kernel that will drop all flows.
> @@ -3013,6 +3032,12 @@ def main(argv):
> addflcmd.add_argument("flow", help="Flow specification")
> addflcmd.add_argument("acts", help="Flow actions")
>
> + modflcmd = subparsers.add_parser("mod-flow")
> + modflcmd.add_argument("modbr", help="Datapath name")
> + modflcmd.add_argument("modflow", help="Flow specification")
> + modflcmd.add_argument("modacts", help="Flow actions",
> + nargs="?", default=None)
> +
> delfscmd = subparsers.add_parser("del-flows")
> delfscmd.add_argument("flsbr", help="Datapath name")
>
> @@ -3110,6 +3135,14 @@ def main(argv):
> flow = OvsFlow.ovs_flow_msg()
> flow.parse(args.flow, args.acts, rep["dpifindex"])
> ovsflow.add_flow(rep["dpifindex"], flow)
> + elif hasattr(args, "modbr"):
> + rep = ovsdp.info(args.modbr, 0)
> + if rep is None:
> + print("DP '%s' not found." % args.modbr)
> + return 1
> + flow = OvsFlow.ovs_flow_msg()
> + flow.parse(args.modflow, args.modacts, rep["dpifindex"])
> + ovsflow.mod_flow(rep["dpifindex"], flow)
> elif hasattr(args, "flsbr"):
> rep = ovsdp.info(args.flsbr, 0)
> if rep is None:
next prev parent reply other threads:[~2026-06-05 18:51 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-05 12:42 [PATCH net-next] selftests/net/openvswitch: add flow modify test Minxi Hou
2026-06-05 18:51 ` Aaron Conole [this message]
[not found] ` <CAJ0BgHcEHi0Bjv4569MnQmVbJukdLfghWMTDYk3k6dRp86OH_g@mail.gmail.com>
2026-06-06 1:33 ` Ilya Maximets
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f7tv7bwhlc0.fsf@redhat.com \
--to=aconole@redhat.com \
--cc=amorenoz@redhat.com \
--cc=davem@davemloft.net \
--cc=dev@openvswitch.org \
--cc=echaudro@redhat.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=houminxi@gmail.com \
--cc=i.maximets@ovn.org \
--cc=kuba@kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.