On 2/1/26 12:52 AM, Jun Gu wrote:
> In the current implementation, mirror_packet() is invoked at the end of
> compose_output_action__(). This logic is flawed because it triggers
> mirroring even when the packet is not actually being output to a port,
> but is instead being recirculated (e.g., for bond member selection).
> 
> This causes two major issues:
> 
> 1. Redundant Mirrors: If a packet is mirrored during the recirculation
>    phase and then mirrored again when it finally hits an actual output
>    action, the mirror destination receives duplicate traffic.
> 
> 2. False Mirroring on Dropped Packets: Mirroring should only occur on
>    actual output. If for some reason the post-recurculation rules are
>    not available, we should not be mirroring the packet. For example,
>    if a bond is removed while we have the first datapath flow installed,
>    but the packet after recirculation goes to userspace via MISS upcall
>    and gets dropped, then we should not mirror that packet.
> 
> This patch refactors compose_output_action__() to ensure mirror_packet()
> is only called within the actual output.
> 
> Signed-off-by: Jun Gu <[email protected]>
> ---

Hi.  Thanks for the new version!  It mostly looks good to me.
See a few small comments below.

Best regards, Ilya Maximets.

>  ofproto/ofproto-dpif-xlate.c | 21 ++++++++++++++-----
>  tests/ofproto-dpif.at        | 40 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 56 insertions(+), 5 deletions(-)
> 
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 32ee49e7e..335a7e68f 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -4560,6 +4560,9 @@ compose_output_action__(struct xlate_ctx *ctx, 
> ofp_port_t ofp_port,
>          /* Commit accumulated flow updates before output. */
>          xlate_commit_actions(ctx);
>  
> +        /* Flag to decide whether the output should be mirrored. */
> +        bool mirrored = true;

The name is in the past tense, so it suggests that the packet was already
mirrored, which is not the case.  Something like 'skip_mirrors' might be
better with a default value of 'false'.  The comment for it here is also
not necessary, as it will be self-explanatory.  And we'll need to move
the declaration to the top of the function, see the comment below.

> +
>          if (xr && bond_use_lb_output_action(xport->xbundle->bond)) {
>              /*
>               * If bond mode is balance-tcp and optimize balance tcp is 
> enabled
> @@ -4589,6 +4592,9 @@ compose_output_action__(struct xlate_ctx *ctx, 
> ofp_port_t ofp_port,
>              /* Recirc action. */
>              nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_RECIRC,
>                             xr->recirc_id);
> +
> +            /* Avoid mirroring on recirculation. */
> +            mirrored = false;
>          } else if (is_native_tunnel) {
>              /* Output to native tunnel port. */
>              native_tunnel_output(ctx, xport, flow, odp_port, truncate,
> @@ -4629,12 +4635,17 @@ compose_output_action__(struct xlate_ctx *ctx, 
> ofp_port_t ofp_port,
>          ctx->sflow_odp_port = odp_port;
>          ctx->sflow_n_outputs++;
>          ctx->nf_output_iface = ofp_port;
> -    }
>  
> -    if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) {
> -        mirror_packet(ctx, xport->xbundle,
> -                      xbundle_mirror_dst(xport->xbundle->xbridge,
> -                                         xport->xbundle));
> +        /* Mirror only when we actually perform an output action.
> +         * Recirculation does not send the packet anywhere, so it
> +         * should not trigger mirroring.
> +         */

This comment doesn't make a lot of sense here.  It's talking about
recirculation, but at this point in code we're far from the recirculation
handling.  We don't a comment here, but part of it can be moved to the
place where the boolean value is set.

> +        if (mirrored && mbridge_has_mirrors(ctx->xbridge->mbridge)
> +            && xport->xbundle) {
> +            mirror_packet(ctx, xport->xbundle,
> +                          xbundle_mirror_dst(xport->xbundle->xbridge,
> +                                             xport->xbundle));
> +        }

I'd suggest we keep this outside of the 'if (out_port != ODPP_NONE)'
check.  The reason is that some virtual ports may not have an actual
datapath port number, but we may still want to mirror when traffic
goes through them.  I don't think we have any such port today beside
the patch port, but it's better if we keep the logic more generic,
even if it's not a possible condition right now.

>      }
>  
>  out:
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index b791bf4e4..86ade6066 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -6112,6 +6112,46 @@ AT_CHECK([tail -1 stdout | grep -E 
> "trunc\(200\),2,trunc\(300\),3,100|trunc\(300
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  
> +AT_SETUP([ofproto-dpif - mirroring on balance-tcp bonding with dp_hash])
> +AT_KEYWORDS([mirror mirrors mirroring])
> +OVS_VSWITCHD_START(
> +  [add-bond br0 bond0 p1 p2 bond_mode=balance-tcp lacp=active \
> +       other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- 
> \
> +   set interface p1 type=dummy options:pstream=punix:$OVS_RUNDIR/p1.sock 
> ofport_request=1 -- \
> +   set interface p2 type=dummy options:pstream=punix:$OVS_RUNDIR/p2.sock 
> ofport_request=2 -- \
> +   add-br br1 -- \
> +   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
> +   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
> +       fail-mode=standalone -- \
> +   add-bond br1 bond1 p3 p4 bond_mode=balance-tcp lacp=active \
> +       other-config:lacp-time=fast other-config:bond-rebalance-interval=0 -- 
> \
> +   set interface p3 type=dummy options:stream=unix:$OVS_RUNDIR/p1.sock 
> ofport_request=3 -- \
> +   set interface p4 type=dummy options:stream=unix:$OVS_RUNDIR/p2.sock 
> ofport_request=4 -- \
> +   add-port br1 p5 -- set interface p5 ofport_request=5 type=dummy

nit: Addition of the p5 can be moved below to the add_of_ports, i.e.
  add_of_ports br1 5 6

> +])
> +AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
> +])

nit: This call is not needed, ports are UP by default.

> +
> +# Waits for all ifaces enabled.
> +OVS_WAIT_UNTIL([test `ovs-appctl bond/show | grep -c "may_enable: true"` -ge 
> 4])
> +
> +add_of_ports br1 6
> +AT_CHECK([ovs-vsctl \
> +            set Bridge br1 mirrors=@m --\
> +            --id=@bond1 get Port bond1 -- --id=@p6 get Port p6 --\
> +            --id=@m create Mirror name=mymirror select_dst_port=@bond1 
> select_src_port=@bond1 output_port=@p6],
> +          [0], [ignore])
> +
> +# Sends a packet to trigger recirculation.
> +AT_CHECK([ovs-appctl netdev-dummy/receive p5 
> "in_port(5),eth(src=50:54:00:00:00:05,dst=50:54:00:00:01:00),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1)"])
> +
> +AT_CHECK([ovs-appctl dpctl/dump-flows --names | grep 'in_port(p5)' | \
> +          sed 's/.*actions://' | grep -c p6 | tr -d '\n'], [0], [1])
> +
> +OVS_VSWITCHD_STOP()
> +AT_CLEANUP
> +
> +
>  # This test verifies that the table ID is preserved across recirculation
>  # when a resubmit action requires it (because the action is relative to
>  # the current table rather than specifying a table).

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

Reply via email to