On Fri, Oct 13, 2023 at 9:06 AM Ilya Maximets <i.maxim...@ovn.org> wrote: > > On 10/8/23 06:21, Mike Pattrick wrote: > > Currently a bridge mirror will collect all packets and tools like > > ovs-tcpdump can apply additional filters after they have already been > > duplicated by vswitchd. This can result in inefficient collection. > > > > This patch adds support to apply pre-selection to bridge mirrors, which > > can limit which packets are mirrored based on flow metadata. This > > significantly improves overall vswitchd performance during mirroring if > > only a subset of traffic is required. > > > > I benchmarked this with two setups. A netlink based test with two veth > > pairs connected to a single bridge, and a netdev based test involving a > > mix of DPDK nics, and netdev-linux interfaces. Both tests involved > > saturating the link with iperf3 and then sending an icmp ping every > > second. I then measured the throughput on the link with no mirroring, > > icmp pre-selected mirroring, and full mirroring. The results, below, > > indicate a significant reduction to impact of mirroring when only a > > subset of the traffic on a port is selected for collection. > > > > Test No Mirror | No Filter | Filter | No Filter | Filter | > > +-----------+-----------+-----------+-----------+----------+ > > netlink | 39.0 Gbps | 36.1 Gbps | 38.2 Gbps | 7% | 2% | > > netdev | 7.39 Gbps | 4.95 Gbps | 6.24 Gbps | 33% | 15% | > > > > The ratios above are the percent reduction in total throughput when > > mirroring is used either with or without a filter. > > > > Signed-off-by: Mike Pattrick <m...@redhat.com> > > > > Hi, Mike. Thanks for the patch! > > Not a full review, but see some comments inline. > > Best regards, Ilya Maximets. > > > --- > > v3: > > - Added more tests > > - Refactored mirror wildcard modification out of mirror_get > > - Improved error handling > > v4: > > - Refactored mirror_set and mirror_get functions to reduce function > > parameters > > v5: > > - Fixed formating issues > > - Moved some code from patch 2 to patch 1 to fix compile > > v6: > > - Clarified filter format in documentation > > - Moved flow/mask operations to atomic set/get > > - Cleaned up and improved test > > > > --- > > Documentation/ref/ovs-tcpdump.8.rst | 8 +- > > NEWS | 4 + > > lib/flow.h | 9 +++ > > ofproto/ofproto-dpif-mirror.c | 68 +++++++++++++++- > > ofproto/ofproto-dpif-mirror.h | 8 +- > > ofproto/ofproto-dpif-xlate.c | 16 +++- > > ofproto/ofproto-dpif.c | 9 +-- > > ofproto/ofproto-dpif.h | 6 ++ > > ofproto/ofproto.h | 3 + > > tests/ofproto-dpif.at | 116 ++++++++++++++++++++++++++++ > > utilities/ovs-tcpdump.in | 13 +++- > > vswitchd/bridge.c | 13 +++- > > vswitchd/vswitch.ovsschema | 5 +- > > vswitchd/vswitch.xml | 13 ++++ > > 14 files changed, 273 insertions(+), 18 deletions(-) > > > > diff --git a/Documentation/ref/ovs-tcpdump.8.rst > > b/Documentation/ref/ovs-tcpdump.8.rst > > index b9f8cdf6f..e21e61211 100644 > > --- a/Documentation/ref/ovs-tcpdump.8.rst > > +++ b/Documentation/ref/ovs-tcpdump.8.rst > > @@ -61,8 +61,14 @@ Options > > > > If specified, mirror all ports (optional). > > > > +* ``--filter <flow>`` > > + > > + If specified, only mirror flows that match the provided OpenFlow filter. > > + The available fields are documented in ``ovs-fields(7)``. > > + > > See Also > > ======== > > > > ``ovs-appctl(8)``, ``ovs-vswitchd(8)``, ``ovs-pcap(1)``, > > -``ovs-tcpundump(1)``, ``tcpdump(8)``, ``wireshark(8)``. > > +``ovs-fields(7)``, ``ovs-tcpundump(1)``, ``tcpdump(8)``, > > +``wireshark(8)``. > > diff --git a/NEWS b/NEWS > > index 6b45492f1..e95033b50 100644 > > --- a/NEWS > > +++ b/NEWS > > @@ -6,6 +6,10 @@ Post-v3.2.0 > > from older version is supported but it may trigger more leader > > elections > > during the process, and error logs complaining unrecognized fields > > may > > be observed on old nodes. > > + * Added a new filter column in the Mirror table which can be used to > > + apply filters to mirror ports. > > + - ovs-tcpdump: > > + * Added new --filter parameter to apply pre-selection on mirrored > > flows. > > > > > > v3.2.0 - 17 Aug 2023 > > diff --git a/lib/flow.h b/lib/flow.h > > index a9d026e1c..f77d3553a 100644 > > --- a/lib/flow.h > > +++ b/lib/flow.h > > @@ -939,6 +939,15 @@ flow_union_with_miniflow(struct flow *dst, const > > struct miniflow *src) > > flow_union_with_miniflow_subset(dst, src, src->map); > > } > > > > +/* Perform a bitwise OR of minimask 'src' mask data with the equivalent > > + * fields in 'dst', storing the result in 'dst'. */ > > +static inline void > > +flow_wildcards_union_with_minimask(struct flow_wildcards *dst, > > + const struct minimask *src) > > +{ > > + flow_union_with_miniflow_subset(&dst->masks, &src->masks, > > src->masks.map); > > +} > > + > > static inline bool is_ct_valid(const struct flow *flow, > > const struct flow_wildcards *mask, > > struct flow_wildcards *wc) > > diff --git a/ofproto/ofproto-dpif-mirror.c b/ofproto/ofproto-dpif-mirror.c > > index 17ba6f799..792d3930c 100644 > > --- a/ofproto/ofproto-dpif-mirror.c > > +++ b/ofproto/ofproto-dpif-mirror.c > > @@ -21,6 +21,7 @@ > > #include "cmap.h" > > #include "hmapx.h" > > #include "ofproto.h" > > +#include "ofproto-dpif-trace.h" > > #include "vlan-bitmap.h" > > #include "openvswitch/vlog.h" > > > > @@ -57,6 +58,11 @@ struct mirror { > > struct hmapx srcs; /* Contains "struct mbundle*"s. */ > > struct hmapx dsts; /* Contains "struct mbundle*"s. */ > > > > + /* Filter criteria. */ > > + OVSRCU_TYPE(struct miniflow *) filter; > > + OVSRCU_TYPE(struct minimask *) mask; > > + char *filter_str; > > + > > /* This is accessed by handler threads assuming RCU protection (see > > * mirror_get()), but can be manipulated by mirror_set() without any > > * explicit synchronization. */ > > @@ -207,11 +213,13 @@ mirror_bundle_dst(struct mbridge *mbridge, struct > > ofbundle *ofbundle) > > } > > > > int > > -mirror_set(struct mbridge *mbridge, void *aux, > > +mirror_set(const struct ofproto *ofproto_, void *aux, > > const struct ofproto_mirror_settings *ms, struct ofbundle > > **srcs, > > struct ofbundle **dsts, struct ofbundle *bundle) > > > > { > > + const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); > > This looks like a layering violation. Can we pass mbridge here instead? > > > + struct mbridge *mbridge = ofproto->mbridge; > > struct mbundle *mbundle, *out; > > mirror_mask_t mirror_bit; > > struct mirror *mirror; > > @@ -265,7 +273,8 @@ mirror_set(struct mbridge *mbridge, void *aux, > > && vlan_bitmap_equal(vlans, ms->src_vlans) > > && mirror->out == out > > && mirror->out_vlan == out_vlan > > - && mirror->snaplen == ms->snaplen) > > + && mirror->snaplen == ms->snaplen > > + && nullable_string_is_equal(mirror->filter_str, ms->filter)) > > { > > hmapx_destroy(&srcs_map); > > hmapx_destroy(&dsts_map); > > @@ -289,6 +298,44 @@ mirror_set(struct mbridge *mbridge, void *aux, > > mirror->out_vlan = out_vlan; > > mirror->snaplen = ms->snaplen; > > > > + if (!nullable_string_is_equal(mirror->filter_str, ms->filter)) { > > THis check might not be enough. By allowing port names in the filter > there is a chance that ports will change their numbers without changing > their names. In this case the parsed filter flow should be invalidated.
Thanks for the review Ilya, I was going through these comments and I noticed some discrepancy with how flows are handled when port number changes. With the sample flow rule "in_port=p1 actions=output:p2" and after changing the port number with "ovs-vsctl set int p1 ofport_request=8", I see that the flow is not invalidated or updated, and instead retains the old now invalid port number: "in_port=1 actions=output:p2". Is this the intended behaviour? I think it makes some amount of sense for this filter to act in a similar fashion to flows. Thanks, Mike > > > + if (mirror->filter_str) { > > + ovsrcu_postpone(free, ovsrcu_get_protected(struct miniflow *, > > + &mirror->filter)); > > + ovsrcu_postpone(free, ovsrcu_get_protected(struct minimask *, > > + &mirror->mask)); > > + free(mirror->filter_str); > > + mirror->filter_str = NULL; > > + ovsrcu_set(&mirror->filter, NULL); > > + ovsrcu_set(&mirror->mask, NULL); > > + } > > + > > + if (ms->filter && strlen(ms->filter)) { > > + struct ofputil_port_map map = > > OFPUTIL_PORT_MAP_INITIALIZER(&map); > > + struct flow_wildcards wc; > > + struct flow flow; > > + char *err; > > + > > + ofproto_append_ports_to_map(&map, ofproto_->ports); > > + err = parse_ofp_exact_flow(&flow, &wc, > > + ofproto_get_tun_tab(ofproto_), > > + ms->filter, &map); > > + ofputil_port_map_destroy(&map); > > + if (err) { > > + VLOG_WARN("filter is invalid: %s", err); > > + free(err); > > + mirror_destroy(mbridge, mirror->aux); > > + return EINVAL; > > + } > > + > > + mirror->filter_str = xstrdup(ms->filter); > > + ovsrcu_set(&mirror->mask, minimask_create(&wc)); > > + ovsrcu_set(&mirror->filter, miniflow_create(&flow)); > > + } > > + } > > + > > + > > + > > Too many empty lines. > > > /* Update mbundles. */ > > mirror_bit = MIRROR_MASK_C(1) << mirror->idx; > > CMAP_FOR_EACH (mbundle, cmap_node, &mirror->mbridge->mbundles) { > > @@ -344,6 +391,17 @@ mirror_destroy(struct mbridge *mbridge, void *aux) > > ovsrcu_postpone(free, vlans); > > } > > > > + if (mirror->filter_str) { > > + ovsrcu_postpone(free, ovsrcu_get_protected(struct miniflow *, > > + &mirror->filter)); > > + ovsrcu_postpone(free, ovsrcu_get_protected(struct minimask *, > > + &mirror->mask)); > > + free(mirror->filter_str); > > + mirror->filter_str = NULL; > > + ovsrcu_set(&mirror->filter, NULL); > > + ovsrcu_set(&mirror->mask, NULL); > > + } > > + > > mbridge->mirrors[mirror->idx] = NULL; > > /* mirror_get() might have just read the pointer, so we must postpone > > the > > * free. */ > > @@ -415,7 +473,9 @@ mirror_update_stats(struct mbridge *mbridge, > > mirror_mask_t mirrors, > > * in which a 1-bit indicates that the mirror includes a particular VLAN, > > * 'mc->dup_mirrors' receives a bitmap of mirrors whose output duplicates > > * mirror 'index', 'mc->out' receives the output ofbundle (if any), > > - * and 'mc->out_vlan' receives the output VLAN (if any). > > + * and 'mc->out_vlan' receives the output VLAN (if any). In cases where the > > + * mirror has a filter configured 'mc->filter_flow' and 'mc->filter_mask' > > + * receives the flow and mask that this mirror should collect. > > * > > * Everything returned here is assumed to be RCU protected. > > */ > > @@ -441,6 +501,8 @@ mirror_get(struct mbridge *mbridge, int index, > > mc->bundle = mirror->out ? mirror->out->ofbundle : NULL; > > mc->out_vlan = mirror->out_vlan; > > mc->snaplen = mirror->snaplen; > > + mc->filter_flow = ovsrcu_get(struct miniflow *, &mirror->filter); > > + mc->filter_mask = ovsrcu_get(struct minimask *, &mirror->mask); > > return true; > > } > > > > diff --git a/ofproto/ofproto-dpif-mirror.h b/ofproto/ofproto-dpif-mirror.h > > index 9d5a6d680..faf92383e 100644 > > --- a/ofproto/ofproto-dpif-mirror.h > > +++ b/ofproto/ofproto-dpif-mirror.h > > @@ -25,6 +25,7 @@ typedef uint32_t mirror_mask_t; > > struct ofproto_mirror_settings; > > struct ofproto_dpif; > > struct ofbundle; > > +struct ofproto; > > > > struct mirror_config { > > /* A bitmap of mirrors that duplicate the current mirror */ > > @@ -33,6 +34,11 @@ struct mirror_config { > > /* VLANs of packets to select for mirroring. */ > > unsigned long *vlans; /* vlan_bitmap, NULL selects all VLANs. */ > > > > + /* The flow if a filter is used, or NULL. */ > > + struct miniflow *filter_flow; > > + /* The filter's flow mask, or NULL. */ > > + struct minimask *filter_mask; > > + > > /* Output (mutually exclusive). */ > > struct ofbundle *bundle; /* A registered ofbundle handle or NULL. */ > > uint16_t out_vlan; /* Output VLAN, only if out_bundle is > > NULL. */ > > @@ -66,7 +72,7 @@ bool mbridge_need_revalidate(struct mbridge *); > > void mbridge_register_bundle(struct mbridge *, struct ofbundle *); > > void mbridge_unregister_bundle(struct mbridge *, struct ofbundle *); > > > > -int mirror_set(struct mbridge *mbridge, void *aux, > > +int mirror_set(const struct ofproto *ofproto, void *aux, > > const struct ofproto_mirror_settings *ms, > > struct ofbundle **srcs, struct ofbundle **dsts, > > struct ofbundle *bundle); > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > > index 3746edf06..2ea4a0c6a 100644 > > --- a/ofproto/ofproto-dpif-xlate.c > > +++ b/ofproto/ofproto-dpif-xlate.c > > @@ -2218,7 +2218,8 @@ lookup_input_bundle(const struct xlate_ctx *ctx, > > > > /* 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'. */ > > + * or egress (as appropriate) mirrors 'mirrors'. In cases where a mirror is > > + * filtered, the current flows wildcard will be modified. */ > > static void > > mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle, > > mirror_mask_t mirrors) > > @@ -2269,6 +2270,19 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle > > *xbundle, > > continue; > > } > > > > + /* After the VLAN check, apply a flow mask if a filter is > > specified */ > > Period at the end of a comment. > > > + if (ctx->wc && mc.filter_flow) { > > + if (OVS_UNLIKELY( > > + miniflow_equal_flow_in_minimask(mc.filter_flow, > > + &ctx->xin->flow, > > + mc.filter_mask))) { > > + flow_wildcards_union_with_minimask(ctx->wc, > > mc.filter_mask); > > This doesn't look right. The fact that we're comparing the flow > means we're using the fileds to make a decision. And everything that > we use should become part of the mask. i.e. The mask should be > updated before comparison. > > Otherwise, if the first packet doesn't match the filter, installed > datapath flow will not have match on the filter fields, and subsequent > packets that may match the filter will pass without being mirrired. > > A test for this case would be nice to have. Some tests with more > cmplex filter would also be nice. > > This change will affect performance though significantly, because > non-filtered traffic will start exact matching on fields present > in the filter. > > > > + } else { > > + mirrors = zero_rightmost_1bit(mirrors); > > + continue; > > + } > > + } > > + > > /* We sent a packet to this mirror. */ > > used_mirrors |= rightmost_1bit(mirrors); > > > > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > > index 5a5a8ea86..15cb948c8 100644 > > --- a/ofproto/ofproto-dpif.c > > +++ b/ofproto/ofproto-dpif.c > > @@ -221,13 +221,6 @@ static void ct_zone_config_init(struct dpif_backer > > *backer); > > static void ct_zone_config_uninit(struct dpif_backer *backer); > > static void ct_zone_timeout_policy_sweep(struct dpif_backer *backer); > > > > -static inline struct ofproto_dpif * > > -ofproto_dpif_cast(const struct ofproto *ofproto) > > -{ > > - ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class); > > - return CONTAINER_OF(ofproto, struct ofproto_dpif, up); > > -} > > - > > /* Global variables. */ > > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > > > > @@ -3663,7 +3656,7 @@ mirror_set__(struct ofproto *ofproto_, void *aux, > > dsts[i] = bundle_lookup(ofproto, s->dsts[i]); > > } > > > > - error = mirror_set(ofproto->mbridge, aux, s, srcs, dsts, > > + error = mirror_set(ofproto_, aux, s, srcs, dsts, > > bundle_lookup(ofproto, s->out_bundle)); > > free(srcs); > > free(dsts); > > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > > index d8e0cd37a..9669f6827 100644 > > --- a/ofproto/ofproto-dpif.h > > +++ b/ofproto/ofproto-dpif.h > > @@ -357,6 +357,12 @@ struct ofproto_dpif { > > > > struct ofproto_dpif *ofproto_dpif_lookup_by_name(const char *name); > > struct ofproto_dpif *ofproto_dpif_lookup_by_uuid(const struct uuid *uuid); > > +static inline struct ofproto_dpif * > > +ofproto_dpif_cast(const struct ofproto *ofproto) > > +{ > > + ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class); > > + return CONTAINER_OF(ofproto, struct ofproto_dpif, up); > > +} > > > > ovs_version_t ofproto_dpif_get_tables_version(struct ofproto_dpif *); > > > > diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h > > index 8efdb20a0..372e0e3fc 100644 > > --- a/ofproto/ofproto.h > > +++ b/ofproto/ofproto.h > > @@ -497,6 +497,9 @@ struct ofproto_mirror_settings { > > uint16_t out_vlan; /* Output VLAN, only if out_bundle is > > NULL. */ > > uint16_t snaplen; /* Max packet size of a mirrored packet > > in byte, set to 0 equals 65535. */ > > + > > + /* Output filter */ > > Period. > > > + char *filter; > > }; > > > > int ofproto_mirror_register(struct ofproto *, void *aux, > > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > > index a39d0d3ae..f8d6ee52c 100644 > > --- a/tests/ofproto-dpif.at > > +++ b/tests/ofproto-dpif.at > > @@ -5182,6 +5182,122 @@ OVS_VSWITCHD_STOP > > AT_CLEANUP > > > > > > +AT_SETUP([ofproto-dpif - mirroring, filter]) > > +AT_KEYWORDS([mirror mirrors mirroring]) > > +OVS_VSWITCHD_START > > +add_of_ports br0 1 2 3 > > +AT_CHECK([ovs-vsctl \ > > + set Bridge br0 mirrors=@m -- \ > > + --id=@p3 get Port p3 -- \ > > + --id=@m create Mirror name=mymirror select_all=true > > output_port=@p3 filter="icmp"], [0], [ignore]) > > + > > +AT_DATA([flows.txt], [dnl > > +in_port=1 actions=output:2 > > +in_port=2 actions=output:1 > > +]) > > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) > > + > > +icmp_flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)" > > +tcp_flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128,frag=no),tcp()" > > + > > +dnl Check one direction, only icmp should mirror. > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 2 > > +]) > > + > > +dnl Check other direction, only icmp should mirror. > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,1 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 1 > > +]) > > + > > +dnl Change filter to tcp, only tcp should mirror. > > +AT_CHECK([ovs-vsctl set mirror mymirror filter="tcp"], [0]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 1 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,1 > > +]) > > + > > +dnl Invalid filter, nothing should mirror. > > +AT_CHECK([ovs-vsctl set mirror mymirror filter="invalid"], [0]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 1 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 1 > > +]) > > + > > +OVS_WAIT_UNTIL([test $(grep -Ec "filter is invalid|mirror mymirror > > configuration is invalid" ovs-vswitchd.log) -eq 2]) > > + > > +dnl Empty filter, all traffic should mirror. > > +AT_CHECK([ovs-vsctl clear mirror mymirror filter], [0]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(1),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,2 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$icmp_flow"], > > [0], [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,1 > > +]) > > + > > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port(2),$tcp_flow"], [0], > > [stdout]) > > +AT_CHECK_UNQUOTED([tail -1 stdout], [0], > > + [Datapath actions: 3,1 > > +]) > > + > > +OVS_VSWITCHD_STOP(["/filter is invalid: invalid: unknown field invalid/d > > +/mirror mymirror configuration is invalid/d"]) > > +AT_CLEANUP > > + > > + > > AT_SETUP([ofproto-dpif - mirroring, select_all]) > > AT_KEYWORDS([mirror mirrors mirroring]) > > OVS_VSWITCHD_START > > diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in > > index 4cbd9a5d3..7483b52ac 100755 > > --- a/utilities/ovs-tcpdump.in > > +++ b/utilities/ovs-tcpdump.in > > @@ -142,6 +142,7 @@ The following options are available: > > --mirror-to The name for the mirror port to use > > (optional) > > Default 'miINTERFACE' > > --span If specified, mirror all ports (optional) > > + --filter Set an OpenFlow formatted preselection filter > > """ % {'prog': sys.argv[0]}) > > sys.exit(0) > > > > @@ -354,7 +355,7 @@ class OVSDB(object): > > return result > > > > def bridge_mirror(self, intf_name, mirror_intf_name, br_name, > > - mirror_select_all=False): > > + mirror_select_all=False, mirror_filter=None): > > > > txn = self._start_txn() > > mirror = txn.insert(self.get_table('Mirror')) > > @@ -362,6 +363,9 @@ class OVSDB(object): > > > > mirror.select_all = mirror_select_all > > > > + if mirror_filter is not None: > > + mirror.filter = mirror_filter > > + > > mirrored_port = self._find_row_by_name('Port', intf_name) > > > > mirror.verify('select_dst_port') > > @@ -445,6 +449,7 @@ def main(): > > mirror_interface = None > > mirror_select_all = False > > dump_cmd = 'tcpdump' > > + mirror_filter = None > > > > for cur, nxt in argv_tuples(sys.argv[1:]): > > if skip_next: > > @@ -474,6 +479,10 @@ def main(): > > elif cur in ['--span']: > > mirror_select_all = True > > continue > > + elif cur in ['--filter']: > > + mirror_filter = nxt > > + skip_next = True > > + continue > > tcpdargs.append(cur) > > > > if interface is None: > > @@ -526,7 +535,7 @@ def main(): > > ovsdb.make_port(mirror_interface, ovsdb.port_bridge(interface)) > > ovsdb.bridge_mirror(interface, mirror_interface, > > ovsdb.port_bridge(interface), > > - mirror_select_all) > > + mirror_select_all, mirror_filter=mirror_filter) > > except OVSDBException as oe: > > print("ERROR: Unable to properly setup the mirror: %s." % str(oe)) > > sys.exit(1) > > diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c > > index e9110c1d8..6e53ab42a 100644 > > --- a/vswitchd/bridge.c > > +++ b/vswitchd/bridge.c > > @@ -5098,6 +5098,7 @@ mirror_configure(struct mirror *m) > > { > > const struct ovsrec_mirror *cfg = m->cfg; > > struct ofproto_mirror_settings s; > > + int ret; > > > > /* Set name. */ > > if (strcmp(cfg->name, m->name)) { > > @@ -5166,8 +5167,18 @@ mirror_configure(struct mirror *m) > > /* Get VLAN selection. */ > > s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, > > cfg->n_select_vlan); > > > > + /* Set the filter, mirror_set() will strdup this pointer. */ > > + s.filter = cfg->filter; > > + > > /* Configure. */ > > - ofproto_mirror_register(m->bridge->ofproto, m, &s); > > + ret = ofproto_mirror_register(m->bridge->ofproto, m, &s); > > + if (ret == EOPNOTSUPP) { > > + VLOG_ERR("ofproto %s: does not support mirroring", > > + m->bridge->ofproto->name); > > + } else if (ret) { > > + VLOG_ERR("bridge %s: mirror %s configuration is invalid", > > + m->bridge->name, m->name); > > + } > > > > /* Clean up. */ > > if (s.srcs != s.dsts) { > > diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema > > index 2d395ff95..24c6fcdbf 100644 > > --- a/vswitchd/vswitch.ovsschema > > +++ b/vswitchd/vswitch.ovsschema > > @@ -1,6 +1,6 @@ > > {"name": "Open_vSwitch", > > "version": "8.4.0", > > - "cksum": "2738838700 27127", > > + "cksum": "1474151405 27231", > > "tables": { > > "Open_vSwitch": { > > "columns": { > > @@ -461,6 +461,9 @@ > > "type": {"key": "string", "value": "integer", > > "min": 0, "max": "unlimited"}, > > "ephemeral": true}, > > + "filter": { > > + "type": {"key": {"type": "string"}, > > + "min": 0, "max": 1}}, > > "external_ids": { > > "type": {"key": "string", "value": "string", > > "min": 0, "max": "unlimited"}}}}, > > diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml > > index cfcde34ff..b5327c96c 100644 > > --- a/vswitchd/vswitch.xml > > +++ b/vswitchd/vswitch.xml > > @@ -5160,6 +5160,19 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 > > type=patch options:peer=p1 \ > > VLANs on which packets are selected for mirroring. An empty set > > selects packets on all VLANs. > > </column> > > + <column name="filter"> > > + <p> > > + Filter to apply to mirror. Flows that match <ref > > column="filter"/> > > + will flow through this mirror, flows that do not match will be > > + ignored. The <ref column="filter"/> syntax is described in > > + <code>ovs-fields</code>(7). > > + </p> > > + <p> > > + This filter is applied after <ref column="select_all"/>, <ref > > + column="select_dst_port"/>, <ref column="select_src_port"/>, > > and > > + <ref column="select_vlan"/>. > > + </p> > > + </column> > > </group> > > > > <group title="Mirroring Destination Configuration"> > > -- > > 2.39.3 > > > > _______________________________________________ > > dev mailing list > > d...@openvswitch.org > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev