> On Feb 5, 2016, at 7:27 PM, Ben Pfaff <b...@ovn.org> wrote: > > On Fri, Feb 05, 2016 at 04:41:29PM -0800, Jarno Rajahalme wrote: >> >>> On Feb 5, 2016, at 3:30 PM, Ben Pfaff <b...@ovn.org> wrote: >>> >>> Mirroring is supposed to happen at most once for any destination on a given >>> packet, so the implementation keeps track of which mirrors have already >>> been used. However, until this commit it did that incorrectly: it >>> considered a mirror "used" even if it had been rejected on the basis of >>> VLAN. This commit fixes the problem. >> >> So even if a mirror has been rejected on the basis of a VLAN, it >> should still be considered for output (later)? Can you describe a >> scenario where this makes a difference? E.g., is there a case where a >> packet is not sent when it should have been sent, or did we get >> duplicate mirroring due to this, as tested against by the new test >> case? > > I think the best way to answer this is to rewrite the commit message. > Here's a new version. It also adds more comments to the code. >
Ben, Thanks for taking the time to explain this in detail. I guess we must have the documentary piece of the commit message in a document somewhere, but I had missed that so was not aware of it in this level of detail. It makes all sense now, Acked-by: Jarno Rajahalme <ja...@ovn.org> > --8<--------------------------cut here-------------------------->8-- > > From: Ben Pfaff <b...@ovn.org <mailto:b...@ovn.org>> > Date: Fri, 5 Feb 2016 19:16:01 -0800 > Subject: [PATCH] ofproto-dpif-xlate: Don't consider mirrors used when excluded > by VLAN. > > Mirrors can be configured to select packets for mirroring on the basis > of multiple criteria: input ports, output ports, and VLANs. A packet P > is to be mirrored if there exists a mirror M such that either: > > - P ingresses on an input port selected by M, or > > - P egresses on an output port selected by M > > AND P is in a VLAN selected by M. > > In addition, every mirror has a destination, which can be an output port > or an output VLAN. Either way, if a packet is mirrored to a particular > destination, it is done only once, even if different mirrors both select > a packet and have the same destination. > > Since commit efbc3b7c4006c (ofproto-dpif-xlate: Rewrite mirroring to better > fit flow translation.), these requirements have been implemented > incorrectly: if a packet satisfies one of the bulleted requirements > above for mirror M1, but not the VLAN selection requirement for M1, > then it was not sent to M's destination, but it was still considered > as having been sent to M1's destination for the purpose of avoid output > duplication. Thus, if P satisfied *all* of the requirements for a > second mirror M2, if M1 and M2 had the same destination, the packet was > still not mirrored. This commit fixes that problem. > > (The issue only occurred if M1 happened to have a smaller index than > M2 in OVS's internal data structures. That's just a matter of luck.) > > Reported-by: Huanle Han <hanxue...@gmail.com <mailto:hanxue...@gmail.com>> > Reported-at: http://openvswitch.org/pipermail/dev/2016-January/064531.html > <http://openvswitch.org/pipermail/dev/2016-January/064531.html> > Fixes: 7efbc3b7c4006c (ofproto-dpif-xlate: Rewrite mirroring to better fit > flow translation.) > Signed-off-by: Ben Pfaff <b...@ovn.org <mailto:b...@ovn.org>> > --- > ofproto/ofproto-dpif-xlate.c | 25 ++++++++++++++++++++----- > tests/ofproto-dpif.at <http://ofproto-dpif.at/> | 26 > ++++++++++++++++++++++++++ > 2 files changed, 46 insertions(+), 5 deletions(-) > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index a6ea067..7138c6c 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -1602,10 +1602,15 @@ lookup_input_bundle(const struct xbridge *xbridge, > ofp_port_t in_port, > return NULL; > } > > +/* Mirrors the packet represented by 'ctx' to appropriate mirror > destinations, > + * given the packet is ingressing or egressing on 'xbundle', which has > ingress > + * or egress (as appropriate) mirrors 'mirrors'. */ > static void > mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle, > mirror_mask_t mirrors) > { > + /* Figure out what VLAN the packet is in (because mirrors can select > + * packets on basis of VLAN). */ > bool warn = ctx->xin->packet != NULL; > uint16_t vid = vlan_tci_to_vid(ctx->xin->flow.vlan_tci); > if (!input_vid_is_valid(vid, xbundle, warn)) { > @@ -1621,9 +1626,6 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle > *xbundle, > return; > } > > - /* Record these mirrors so that we don't mirror to them again. */ > - ctx->mirrors |= mirrors; > - > if (ctx->xin->resubmit_stats) { > mirror_update_stats(xbridge->mbridge, mirrors, > ctx->xin->resubmit_stats->n_packets, > @@ -1637,27 +1639,36 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle > *xbundle, > entry->u.mirror.mirrors = mirrors; > } > > + /* 'mirrors' is a bit-mask of candidates for mirroring. Iterate as long > as > + * some candidates remain. */ > while (mirrors) { > const unsigned long *vlans; > mirror_mask_t dup_mirrors; > struct ofbundle *out; > int out_vlan; > > + /* Get the details of the mirror represented by the rightmost 1-bit. > */ > bool has_mirror = mirror_get(xbridge->mbridge, raw_ctz(mirrors), > &vlans, &dup_mirrors, &out, &out_vlan); > ovs_assert(has_mirror); > > + /* If this mirror selects on the basis of VLAN, and it does not > select > + * 'vlan', then discard this mirror and go on to the next one. */ > if (vlans) { > ctx->wc->masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK); > } > - > if (vlans && !bitmap_is_set(vlans, vlan)) { > mirrors = zero_rightmost_1bit(mirrors); > continue; > } > > - mirrors &= ~dup_mirrors; > + /* Record the mirror, and the mirrors that output to the same > + * destination, so that we don't mirror to them again. This must be > + * done now to ensure that output_normal(), below, doesn't > recursively > + * output to the same mirrors. */ > ctx->mirrors |= dup_mirrors; > + > + /* Send the packet to the mirror. */ > if (out) { > struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); > struct xbundle *out_xbundle = xbundle_lookup(xcfg, out); > @@ -1675,6 +1686,10 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle > *xbundle, > } > } > } > + > + /* output_normal() could have recursively output (to different > + * mirrors), so make sure that we don't send duplicates. */ > + mirrors &= ~ctx->mirrors; > } > } > > diff --git a/tests/ofproto-dpif.at <http://ofproto-dpif.at/> > b/tests/ofproto-dpif.at <http://ofproto-dpif.at/> > index a372d36..5fdf5e6 100644 > --- a/tests/ofproto-dpif.at <http://ofproto-dpif.at/> > +++ b/tests/ofproto-dpif.at <http://ofproto-dpif.at/> > @@ -4148,6 +4148,32 @@ AT_CHECK([ovs-dpctl normalize-actions "$flow" > "$actual"], [0], [expout]) > OVS_VSWITCHD_STOP > AT_CLEANUP > > +# This verifies that we don't get duplicate mirroring when mirror_packet() > +# might be invoked recursively, as a check against regression. > +AT_SETUP([ofproto-dpif - multiple VLAN output mirrors]) > +OVS_VSWITCHD_START > +add_of_ports br0 1 2 3 > +ovs-vsctl \ > + -- set Bridge br0 fail-mode=standalone mirrors=@m1,@m2 \ > + -- --id=@m1 create Mirror name=m1 select_all=true output_vlan=500 \ > + -- --id=@m2 create Mirror name=m2 select_all=true output_vlan=501 \ > + -- set Port br0 tag=0 \ > + -- set Port p1 tag=0 \ > + -- set Port p2 tag=500 \ > + -- set Port p3 tag=501 > + > +flow='in_port=1' > +AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout]) > +AT_CHECK([tail -1 stdout | sed 's/Datapath actions: // > +s/,/\ > +/g' | sort], [0], [100 > +2 > +3 > +]) > + > +OVS_VSWITCHD_STOP > +AT_CLEANUP > + > # This test verifies that mirror state is preserved across recirculation. > # > # Otherwise, post-recirculation the ingress and the output to port 4 > -- > 2.1.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev