> On May 9, 2025, at 6:03 PM, Aaron Conole <[email protected]> wrote:
> 
> Daniel Ding <[email protected] <mailto:[email protected]>> writes:
> 
>> In order to collect meters since the last meter action
>> to the current action in the dpif_execute_helper_cb,
>> the next entry is simply obtained using nl_attr_next, and
>> not considering the wrong nested nl without end_nested.
>> This will cause memory access to be overflowed.
>> 
>> Gdb debug looks like this:
>> 
>>  (gdb) print a
>>  $6 = (const struct nlattr *) 0xaaaad175a69c
>>  (gdb) n
>>  1206                                a = nl_attr_next(a);
>>  (gdb) n
>>  1207                            } while (a != action &&
>>  (gdb) n
>>  1206                                a = nl_attr_next(a);
>>  (gdb) print a
>>  $7 = (const struct nlattr *) 0xaaaad175a69c
>>  (gdb) print *a
>>  $8 = {nla_len = 0, nla_type = 0}
>>  (gdb)
>> 
>> Fixes: 076caa2fb077 ("ofproto: Meter translation.")
>> Signed-off-by: Daniel Ding <[email protected]>
>> ---
> 
> Hi Daniel,
> 
> Thanks for continuing to work on this.
> 
> As noted by the robot, and by my own testing, there is an error applying
> for the test you add:
> 
>  Applying: dpif: Fix collecting meters overflows memory.
>  error: patch failed: tests/ofproto-dpif.at:12063 
> <http://ofproto-dpif.at:12063/>
>  error: tests/ofproto-dpif.at <http://ofproto-dpif.at/>: patch does not apply
>  Patch failed at 0001 dpif: Fix collecting meters overflows memory.
> 
>> lib/dpif.c              | 20 +++----------
>> tests/ofproto-dpif.at   | 29 ++++++++++++++++++
>> tests/ofproto-macros.at | 66 +++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 99 insertions(+), 16 deletions(-)
>> 
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index d07241f1e..02429a30c 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -71,6 +71,7 @@ COVERAGE_DEFINE(dpif_meter_set);
>> COVERAGE_DEFINE(dpif_port_add);
>> COVERAGE_DEFINE(dpif_port_del);
>> COVERAGE_DEFINE(dpif_purge);
>> +COVERAGE_DEFINE(dpif_execute_meters_discard);
>> 
>> static const struct dpif_class *base_dpif_classes[] = {
>> #if defined(__linux__) || defined(_WIN32)
>> @@ -1182,6 +1183,8 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>         /* Maintain a pointer to the first meter action seen. */
>>         if (!aux->meter_action) {
>>             aux->meter_action = action;
>> +        } else {
>> +           COVERAGE_INC(dpif_execute_meters_discard);
>>         }
>>         break;
>> 
>> @@ -1202,22 +1205,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>> 
>>             if (aux->meter_action) {
>>                 const struct nlattr *a = aux->meter_action;
>> -
>> -                /* XXX: This code collects meter actions since the last 
>> action
>> -                 * execution via the datapath to be executed right before 
>> the
>> -                 * current action that needs to be executed by the datapath.
>> -                 * This is only an approximation, but better than nothing.
>> -                 * Fundamentally, we should have a mechanism by which the
>> -                 * datapath could return the result of the meter action so 
>> that
>> -                 * we could execute them at the right order. */
>> -                do {
>> -                    ofpbuf_put(&execute_actions, a, NLA_ALIGN(a->nla_len));
>> -                    /* Find next meter action before 'action', if any. */
>> -                    do {
>> -                        a = nl_attr_next(a);
>> -                    } while (a != action &&
>> -                             nl_attr_type(a) != OVS_ACTION_ATTR_METER);
>> -                } while (a != action);
>> +                ofpbuf_put(&execute_actions, a, NLA_ALIGN(a->nla_len));
>>             }
>> 
>>             /* The Linux kernel datapath throws away the tunnel information
>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>> index a1393f7f8..f5f665e45 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -12063,3 +12063,32 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap 
>> | wc -l`])
>> 
>> OVS_VSWITCHD_STOP
>> AT_CLEANUP
>> +
>> +
>> +AT_SETUP([ofproto-dpif - collecting meters])
>> +OVS_NETDEV_VSWITCHD_START
>> +
>> +AT_CHECK([ovs-vsctl add-br br-vm -- set Bridge br-vm datapath_type=netdev])
>> +AT_CHECK([ovs-vsctl add-br br-phy -- set Bridge br-phy 
>> datapath_type=netdev])
>> +AT_CHECK([ovs-vsctl add-port br-vm vx1 -- set Interface vx1 type=vxlan 
>> options:remote_ip=1.1.1.1 options:local_ip=1.1.1.2])
>> +
>> +dnl Active vxlan egress network
>> +ADD_NAMESPACES(ns1)
>> +ADD_VETH(p1, ns1, br-phy, "1.1.1.1/24")
>> +AT_CHECK([ip link set up dev br-phy])
>> +AT_CHECK([ip addr add 1.1.1.2/24 dev br-phy])
>> +
>> +dnl Force packet-out into collecting meters
>> +AT_CHECK([ovs-ofctl -O OpenFlow13 add-meter br-vm 'meter=1 pktps 
>> bands=type=drop rate=1'])
>> +AT_CHECK([ovs-ofctl -O OpenFlow13 add-flow br-vm 'table=2,arp 
>> actions=meter:1,set_field:2->arp_op,vx1,br-vm'])
>> +
>> +dnl Wait egress network ready
>> +AT_CHECK([ping -c 3 1.1.1.1], [0], [stdout])
>> +OVS_WAIT_UNTIL([test -n "`ovs-appctl tnl/arp/show | grep 1.1.1.1`"])
>> +OVS_WAIT_UNTIL([test -n "`ovs-appctl ovs/route/show | grep 1.1.1.0`"])
>> +
>> +dnl Send a arp packet-out message
>> +AT_CHECK([ovs-ofctl packet-out br-vm 'in_port=controller 
>> packet=ffffffffffff000000010102080600010800060400010000000101020101010300000000000001010102
>>  actions=resubmit(,2)'])
>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
> 
> This test will need to be reworked so that it can function via the dummy
> datapath.  The reason we do that is so that the test can be run even
> without super user privileges.  This test is checking a part of the code
> that can be tested independently of having live traffic passing, so
> please implement it that way.
> 

Hi Aaron,
I don’t think this test needs to be added to the dummy datapath. This issue

only occurs in the netdev datapath, which clones actions when encountering

a tunnel output and the action is not the last one.

Maybe, ofproto-dpif.at is not the appropriate position for this test.

>> \ No newline at end of file
>> diff --git a/tests/ofproto-macros.at <http://ofproto-macros.at/> 
>> b/tests/ofproto-macros.at <http://ofproto-macros.at/>
>> index c22fb3c79..50512f08b 100644
>> --- a/tests/ofproto-macros.at <http://ofproto-macros.at/>
>> +++ b/tests/ofproto-macros.at <http://ofproto-macros.at/>
> 
> This introduction here is inappropriate for the ofproto-dpif suite.  The
> point of this suite is to do testing via the dummy datapath rather than
> a datapath that will require real ports from the system.
> 
>> @@ -384,3 +384,69 @@ m4_define([WAIT_FOR_DUMMY_PORTS], \
>>                   | grep 'unknown\|disconnected'])])])
>> 
>> 
>> +# DEL_NAMESPACES(ns [, ns ... ])
>> +#
>> +# Delete namespaces from the running OS
>> +m4_define([DEL_NAMESPACES],
>> +   [m4_foreach([ns], [$@],
>> +               [ip netns del ns
>> +])
>> +   ]
>> +)
>> +
>> +
>> +# ADD_NAMESPACES(ns [, ns ... ])
>> +#
>> +# Add new namespaces, if ns exists, the old one
>> +# will be remove before new ones are installed.
>> +m4_define([ADD_NAMESPACES],
>> +   [m4_foreach([ns], [$@],
>> +               [DEL_NAMESPACES(ns)
>> +                AT_CHECK([ip netns add ns || return 77])
>> +                on_exit 'DEL_NAMESPACES(ns)'
>> +                ip netns exec ns sysctl -w 
>> net.netfilter.nf_conntrack_helper=0
>> +               ])
>> +   ]
>> +)
>> +# NS_EXEC([namespace], [command])
>> +#
>> +# Execute 'command' in 'namespace'
>> +m4_define([NS_EXEC],
>> +    [ip netns exec $1 sh << NS_EXEC_HEREDOC
>> +$2
>> +NS_EXEC_HEREDOC])
>> +
>> +# NS_CHECK_EXEC([namespace], [command], other_params...)
>> +#
>> +# Wrapper for AT_CHECK that executes 'command' inside 'namespace'.
>> +# 'other_params' as passed as they are to AT_CHECK.
>> +m4_define([NS_CHECK_EXEC],
>> +    [ AT_CHECK([NS_EXEC([$1], [$2])], m4_shift(m4_shift($@))) ]
>> +)
>> +
>> +m4_define([ADD_VETH],
>> +    [ AT_CHECK([ip link add $1 type veth peer name ovs-$1 || return 77])
>> +      AT_CHECK([ip link set $1 netns $2])
>> +      AT_CHECK([ip link set dev ovs-$1 up])
>> +      AT_CHECK([ovs-vsctl add-port $3 ovs-$1])
>> +      NS_CHECK_EXEC([$2], [ip addr add $4 dev $1 $7])
>> +      NS_CHECK_EXEC([$2], [ip link set dev $1 up])
>> +      if test -n "$5"; then
>> +        NS_CHECK_EXEC([$2], [ip link set dev $1 address $5])
>> +      fi
>> +      if test -n "$6"; then
>> +        NS_CHECK_EXEC([$2], [ip route add default via $6])
>> +      fi
>> +      on_exit 'ip link del ovs-$1'
>> +    ]
>> +)
>> +
>> +# OVS_NETDEV_VSWITCHD_START([dbinit-aux-args])
>> +#
>> +# Creates a database and starts ovsdb-server, starts ovs-vswitchd
>> +# connected to that database.
>> +# 'dbinit-aux-args' are passed as additional commands to 'ovs-vsctl init'
>> +# before starting ovs-vswitchd.
>> +m4_define([OVS_NETDEV_VSWITCHD_START],
>> +  [_OVS_VSWITCHD_START([--disable-system], [$1])
>> +])
>> \ No newline at end of file

Regards,
Daniel Ding

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to