@@ -61,6 +61,8 @@ struct mirror {
/* Output (exactly one of out == NULL and out_vlan == -1 is true). */
struct mbundle *out; /* Output port or NULL. */
int out_vlan; /* Output VLAN or -1. */
+ uint16_t snaplen; /* Max per mirrored packet size in byte,
+ set to 0 equals 65535. */
mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */
/* Counters. */
@@ -203,6 +205,7 @@ mirror_set(struct mbridge *mbridge, void *aux, const char *name,
struct ofbundle **srcs, size_t n_srcs,
struct ofbundle **dsts, size_t n_dsts,
unsigned long *src_vlans, struct ofbundle *out_bundle,
+ uint16_t snaplen,
uint16_t out_vlan)
{
struct mbundle *mbundle, *out;
@@ -227,6 +230,7 @@ mirror_set(struct mbridge *mbridge, void *aux, const char *name,
mirror->idx = idx;
mirror->aux = aux;
mirror->out_vlan = -1;
+ mirror->snaplen = 0;
}
/* Get the new configuration. */
@@ -266,6 +270,7 @@ mirror_set(struct mbridge *mbridge, void *aux, const char *name,
mirror->out = out;
mirror->out_vlan = out_vlan;
+ mirror->snaplen = snaplen;
/* Update mbundles. */
mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
@@ -388,7 +393,8 @@ mirror_update_stats(struct mbridge *mbridge, mirror_mask_t mirrors,
* receives the output VLAN (if any). */
bool
mirror_get(struct mbridge *mbridge, int index, const unsigned long **vlans,
- mirror_mask_t *dup_mirrors, struct ofbundle **out, int *out_vlan)
+ mirror_mask_t *dup_mirrors, struct ofbundle **out,
+ int *snaplen, int *out_vlan)
{
struct mirror *mirror;
@@ -405,6 +411,7 @@ mirror_get(struct mbridge *mbridge, int index, const unsigned long **vlans,
*dup_mirrors = mirror->dup_mirrors;
*out = mirror->out ? mirror->out->ofbundle : NULL;
*out_vlan = mirror->out_vlan;
+ *snaplen = mirror->snaplen;
return true;
}
@@ -42,7 +42,7 @@ int mirror_set(struct mbridge *, void *aux, const char *name,
struct ofbundle **srcs, size_t n_srcs,
struct ofbundle **dsts, size_t n_dsts,
unsigned long *src_vlans, struct ofbundle *out_bundle,
- uint16_t out_vlan);
+ uint16_t snaplen, uint16_t out_vlan);
void mirror_destroy(struct mbridge *, void *aux);
int mirror_get_stats(struct mbridge *, void *aux, uint64_t *packets,
uint64_t *bytes);
@@ -50,6 +50,6 @@ void mirror_update_stats(struct mbridge*, mirror_mask_t, uint64_t packets,
uint64_t bytes);
bool mirror_get(struct mbridge *, int index, const unsigned long **vlans,
mirror_mask_t *dup_mirrors, struct ofbundle **out,
- int *out_vlan);
+ int *snaplen, int *out_vlan);
#endif /* ofproto-dpif-mirror.h */
@@ -238,6 +238,7 @@ struct xlate_ctx {
ofp_port_t nf_output_iface; /* Output interface index for NetFlow. */
bool exit; /* No further actions should be processed. */
mirror_mask_t mirrors; /* Bitmap of associated mirrors. */
+ int mirror_snaplen; /* Max size of a mirror packet in byte. */
/* Freezing Translation
* ====================
@@ -1702,12 +1703,15 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
mirror_mask_t dup_mirrors;
struct ofbundle *out;
int out_vlan;
+ int snaplen;
/* 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);
+ &vlans, &dup_mirrors,
+ &out, &snaplen, &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) {
@@ -1723,6 +1727,7 @@ mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle,
* done now to ensure that output_normal(), below, doesn't recursively
* output to the same mirrors. */
ctx->mirrors |= dup_mirrors;
+ ctx->mirror_snaplen = snaplen;
/* Send the packet to the mirror. */
if (out) {
@@ -1746,6 +1751,7 @@ 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;
+ ctx->mirror_snaplen = 0;
}
}
@@ -3010,6 +3016,9 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
} else if (xport->config & OFPUTIL_PC_NO_FWD) {
xlate_report(ctx, "OFPPC_NO_FWD set, skipping output");
return;
+ } else if (ctx->mirror_snaplen != 0 && xport->odp_port == ODPP_NONE) {
+ xlate_report(ctx, "Mirror truncate to ODPP_NONE, skipping output");
+ return;
} else if (check_stp) {
if (is_stp(&ctx->base_flow)) {
if (!xport_stp_should_forward_bpdu(xport) &&
@@ -3226,11 +3235,27 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
/* Tunnel push-pop action is not compatible with
* IPFIX action. */
compose_ipfix_action(ctx, out_port);
+
+ /* Handle truncation of the mirrored packet. */
+ if (ctx->mirror_snaplen > 0 &&
+ ctx->mirror_snaplen < UINT16_MAX) {
+ bool support_trunc = ctx->xbridge->support.trunc;
+ struct ovs_action_trunc *trunc;
+
+ trunc = nl_msg_put_unspec_uninit(ctx->odp_actions,
+ OVS_ACTION_ATTR_TRUNC,
+ sizeof *trunc);
+ trunc->max_len = ctx->mirror_snaplen;
+ if (!support_trunc) {
+ ctx->xout->slow |= SLOW_ACTION;
+ }
+ }
+
nl_msg_put_odp_port(ctx->odp_actions,
OVS_ACTION_ATTR_OUTPUT,
out_port);
- }
- }
+ }
+ }
}
ctx->sflow_odp_port = odp_port;
@@ -3216,7 +3216,8 @@ mirror_set__(struct ofproto *ofproto_, void *aux,
error = mirror_set(ofproto->mbridge, aux, s->name, srcs, s->n_srcs, dsts,
s->n_dsts, s->src_vlans,
- bundle_lookup(ofproto, s->out_bundle), s->out_vlan);
+ bundle_lookup(ofproto, s->out_bundle),
+ s->snaplen, s->out_vlan);
free(srcs);
free(dsts);
return error;
@@ -427,6 +427,8 @@ struct ofproto_mirror_settings {
/* Output (mutually exclusive). */
void *out_bundle; /* A registered ofbundle handle or NULL. */
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. */
};
int ofproto_mirror_register(struct ofproto *, void *aux,
@@ -4224,6 +4224,230 @@ AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: 4
OVS_VSWITCHD_STOP
AT_CLEANUP
+# Tests below verify the snaplen support for mirroring
+AT_SETUP([ofproto-dpif - mirroring, select_all with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 snaplen=100
+
+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])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,2
+])
+
+flow="in_port(2),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_src with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p1 get Port p1 -- --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror select_src_port=@p1 output_port=@p3 snaplen=100
+
+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])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,2
+])
+
+flow="in_port(2),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, OFPP_NONE ingress port with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p2 get Port p2 --\
+ --id=@m create Mirror name=mymirror select_all=true output_port=@p2 snaplen=100
+
+AT_CHECK([ovs-ofctl add-flow br0 action=output:1])
+
+# "in_port" defaults to OFPP_NONE if it's not specified.
+flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ttl=128,icmp_type=8,icmp_code=0"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 1,trunc(100),2
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_dst with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror select_dst_port=@p2 output_port=@p3 snaplen=100
+
+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])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 2,trunc(100),3
+])
+
+flow="in_port(2),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_vlan with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3 snaplen=100
+
+AT_DATA([flows.txt], [dnl
+in_port=1, actions=output:2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=10,pcp=0),encap(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))"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+flow="in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=11,pcp=0),encap(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))"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,2
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, output_port with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror select_all=true output_port=@p3 snaplen=100
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=mod_vlan_vid:17,output:2
+in_port=2 actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,push_vlan(vid=17,pcp=0),2
+])
+
+flow="in_port(2),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+AT_CHECK_UNQUOTED([tail -1 stdout], [0],
+ [Datapath actions: trunc(100),3,1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, output_vlan with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2
+ovs-vsctl \
+ set Bridge br0 mirrors=@m --\
+ --id=@m create Mirror name=mymirror select_all=true output_vlan=12 snaplen=100
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+in_port=2 actions=mod_vlan_vid:17,output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+flow="in_port(1),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+dnl Expect: "trunc(100),100,trunc(100),2,trunc(100),1", with different order
+AT_CHECK([echo "$actual" | sed -n 's/.*\(trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*\)/\1/p'], [0])
+
+flow="in_port(2),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)"
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$flow"], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+AT_CHECK([echo "$actual" | sed -n 's/.*\(trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*,trunc([0-9]*),[0-9]*\)/\1/p'], [0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - multiple VLAN output mirrors with snaplen])
+AT_KEYWORDS([mirror mirrors mirroring])
+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 snaplen=200 \
+ -- --id=@m2 create Mirror name=m2 select_all=true output_vlan=501 snaplen=300 \
+ -- 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 | egrep "trunc\(200\),2,trunc\(300\),3,100|trunc\(300\),3,trunc\(200\),2,100"], [0], [stdout])
+
+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).
@@ -4760,6 +4760,12 @@ mirror_configure(struct mirror *m)
return false;
}
+ if (cfg->snaplen) {
+ s.snaplen = *cfg->snaplen;
+ } else {
+ s.snaplen = 0;
+ }
+
/* Get port selection. */
if (cfg->select_all) {
size_t n_ports = hmap_count(&m->bridge->ports);
@@ -1,6 +1,6 @@
{"name": "Open_vSwitch",
"version": "7.13.0",
- "cksum": "2202834738 22577",
+ "cksum": "889248633 22774",
"tables": {
"Open_vSwitch": {
"columns": {
@@ -405,6 +405,11 @@
"minInteger": 1,
"maxInteger": 4095},
"min": 0, "max": 1}},
+ "snaplen": {
+ "type": {"key": {"type": "integer",
+ "minInteger": 14,
+ "maxInteger": 65535},
+ "min": 0, "max": 1}},
"statistics": {
"type": {"key": "string", "value": "integer",
"min": 0, "max": "unlimited"},
@@ -3692,6 +3692,15 @@
VLAN and should generally be preferred.
</p>
</column>
+
+ <column name="snaplen">
+ <p>Maximum per mirrored packet size in bytes.</p>
+ <p>A mirrored packet with size larger than <ref column="snaplen"/>
+ will be truncated in datapath to <ref column="snaplen"/> bytes
+ before sending to the mirror output port. A snaplen of 0 equals
+ 65535, which means no truncation applied.
+ </p>
+ </column>
</group>
<group title="Statistics: Mirror counters">
This patch adds a 'snaplen' config for mirroring table. A mirrored packet with size larger than snaplen bytes will be truncated in datapath before sending to the mirror output port. A snaplen of 0 equals 65535, which means no truncation applied. Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/141186839 Signed-off-by: William Tu <u9012063@gmail.com> --- v2->v3 - fix min and max number in schema http://openvswitch.org/pipermail/dev/2016-June/072630.html v1->v2 - use UINT16_MAX instead of (uint16_t) - 1 http://openvswitch.org/pipermail/dev/2016-June/072297.html --- ofproto/ofproto-dpif-mirror.c | 9 +- ofproto/ofproto-dpif-mirror.h | 4 +- ofproto/ofproto-dpif-xlate.c | 31 +++++- ofproto/ofproto-dpif.c | 3 +- ofproto/ofproto.h | 2 + tests/ofproto-dpif.at | 224 ++++++++++++++++++++++++++++++++++++++++++ vswitchd/bridge.c | 6 ++ vswitchd/vswitch.ovsschema | 7 +- vswitchd/vswitch.xml | 9 ++ 9 files changed, 287 insertions(+), 8 deletions(-)