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:
prev parent reply other threads:[~2026-06-05 18:51 UTC|newest]
Thread overview: 2+ 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]
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox