Previously, mirrors only worked when using the "normal" action. This
commit performs mirroring even when mirroring is not used. It also adds
some unit tests.
---
NEWS | 1 +
ofproto/ofproto-dpif.c | 122 +++++++++++++++----------
ofproto/ofproto-provider.h | 8 +-
ofproto/ofproto.c | 5 +-
tests/ofproto-dpif.at | 215 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 296 insertions(+), 55 deletions(-)
diff --git a/NEWS b/NEWS
index aec514d..c718c56 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ post-v1.3.0
- Added ability to modify ECN bits in IPv4.
- Added ability to modify TTL in IPv4.
- ovs-vswitchd:
+ - Don't require the "normal" action to use mirrors.
- Track packet and byte statistics sent on mirrors.
- ovs-appctl:
- New "fdb/flush" command to flush bridge's MAC learning table.
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ab65fd5..7108213 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -136,8 +136,6 @@ static void update_mirror_stats(struct ofproto_dpif
*ofproto,
mirror_mask_t mirrors,
uint64_t packets, uint64_t bytes);
-/* A group of one or more OpenFlow ports. */
-#define OFBUNDLE_FLOOD ((struct ofbundle *) 1)
struct ofbundle {
struct ofproto_dpif *ofproto; /* Owning ofproto. */
struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
@@ -169,6 +167,8 @@ static void bundle_destroy(struct ofbundle *);
static void bundle_del_port(struct ofport_dpif *);
static void bundle_run(struct ofbundle *);
static void bundle_wait(struct ofbundle *);
+static struct ofport_dpif *lookup_input_bundle(struct ofproto_dpif *,
+ uint16_t in_port, bool warn);
static void stp_run(struct ofproto_dpif *ofproto);
static void stp_wait(struct ofproto_dpif *ofproto);
@@ -451,6 +451,8 @@ static int send_packet(struct ofproto_dpif *, uint32_t
odp_port,
static size_t
compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions,
const struct flow *, uint32_t odp_port);
+static void add_mirror_actions(struct action_xlate_ctx *ctx,
+ const struct flow *flow);
/* Global variables. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -3925,7 +3927,6 @@ compose_output_action__(struct action_xlate_ctx *ctx,
uint16_t ofp_port,
ctx->sflow_n_outputs++;
ctx->nf_output_iface = ofp_port;
ctx->flow.nw_tos = flow_nw_tos;
- ctx->mirrors = ofport->bundle->mirror_out;
}
static void
@@ -4447,6 +4448,8 @@ static struct ofpbuf *
xlate_actions(struct action_xlate_ctx *ctx,
const union ofp_action *in, size_t n_in)
{
+ struct flow orig_flow = ctx->flow;
+
COVERAGE_INC(ofproto_dpif_xlate);
ctx->odp_actions = ofpbuf_new(512);
@@ -4501,6 +4504,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
compose_output_action(ctx, OFPP_LOCAL);
}
}
+ add_mirror_actions(ctx, &orig_flow);
fix_sflow_action(ctx);
}
@@ -4677,34 +4681,6 @@ ofbundle_get_a_port(const struct ofbundle *bundle)
struct ofport_dpif, bundle_node);
}
-static mirror_mask_t
-compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan,
- const struct ofbundle *in_bundle,
- const struct ofbundle *out_bundle)
-{
- mirror_mask_t dst_mirrors = 0;
-
- if (out_bundle == OFBUNDLE_FLOOD) {
- struct ofbundle *bundle;
-
- HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
- if (bundle != in_bundle
- && ofbundle_includes_vlan(bundle, vlan)
- && bundle->floodable
- && !bundle->mirror_out) {
- output_normal(ctx, bundle, vlan);
- dst_mirrors |= bundle->dst_mirrors;
- }
- }
- ctx->nf_output_iface = NF_OUT_FLOOD;
- } else if (out_bundle) {
- output_normal(ctx, out_bundle, vlan);
- dst_mirrors = out_bundle->dst_mirrors;
- }
-
- return dst_mirrors;
-}
-
static bool
vlan_is_mirrored(const struct ofmirror *m, int vlan)
{
@@ -4753,18 +4729,69 @@ eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN])
}
static void
-output_mirrors(struct action_xlate_ctx *ctx,
- uint16_t vlan, const struct ofbundle *in_bundle,
- mirror_mask_t dst_mirrors)
+add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow)
{
struct ofproto_dpif *ofproto = ctx->ofproto;
mirror_mask_t mirrors;
+ struct ofport_dpif *in_port;
+ struct ofbundle *in_bundle;
+ uint16_t vlan;
+ uint16_t vid;
+ const struct nlattr *a;
+ size_t left;
+
+ /* Obtain in_port from orig_flow.in_port.
+ *
+ * lookup_input_bundle() also ensures that in_port belongs to a bundle. */
+ in_port = lookup_input_bundle(ctx->ofproto, orig_flow->in_port,
+ ctx->packet != NULL);
+ if (!in_port) {
+ return;
+ }
+ in_bundle = in_port->bundle;
+ mirrors = in_bundle->src_mirrors;
+
+ /* Drop frames on bundles reserved for mirroring. */
+ if (in_bundle->mirror_out) {
+ if (ctx->packet != NULL) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
+ "%s, which is reserved exclusively for mirroring",
+ ctx->ofproto->up.name, in_bundle->name);
+ }
+ return;
+ }
+
+ /* Check VLAN. */
+ vid = vlan_tci_to_vid(orig_flow->vlan_tci);
+ if (!input_vid_is_valid(vid, in_bundle, ctx->packet != NULL)) {
+ return;
+ }
+ vlan = input_vid_to_vlan(in_bundle, vid);
+
+ /* Look at the output ports to check for destination selections. */
+
+ NL_ATTR_FOR_EACH (a, left, ctx->odp_actions->data,
+ ctx->odp_actions->size) {
+ enum ovs_action_attr type = nl_attr_type(a);
+ struct ofport_dpif *ofport;
+
+ if (type != OVS_ACTION_ATTR_OUTPUT) {
+ continue;
+ }
+
+ ofport = get_odp_port(ofproto, nl_attr_get_u32(a));
+ mirrors |= ofport->bundle->dst_mirrors;
+ }
+ ctx->mirrors = mirrors;
- mirrors = in_bundle->src_mirrors | dst_mirrors;
if (!mirrors) {
return;
}
+ /* Restore the original packet before adding the mirror actions. */
+ ctx->flow = *orig_flow;
+
while (mirrors) {
struct ofmirror *m;
@@ -4778,7 +4805,7 @@ output_mirrors(struct action_xlate_ctx *ctx,
mirrors &= ~m->dup_mirrors;
if (m->out) {
output_normal(ctx, m->out, vlan);
- } else if (eth_dst_may_rspan(ctx->flow.dl_dst)
+ } else if (eth_dst_may_rspan(orig_flow->dl_dst)
&& vlan != m->out_vlan) {
struct ofbundle *bundle;
@@ -4954,10 +4981,8 @@ is_admissible(struct ofproto_dpif *ofproto, const struct
flow *flow,
static void
xlate_normal(struct action_xlate_ctx *ctx)
{
- mirror_mask_t dst_mirrors = 0;
struct ofport_dpif *in_port;
struct ofbundle *in_bundle;
- struct ofbundle *out_bundle;
struct mac_entry *mac;
uint16_t vlan;
uint16_t vid;
@@ -5006,7 +5031,6 @@ xlate_normal(struct action_xlate_ctx *ctx)
/* Check other admissibility requirements. */
if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) {
- output_mirrors(ctx, vlan, in_bundle, 0);
return;
}
@@ -5018,8 +5042,8 @@ xlate_normal(struct action_xlate_ctx *ctx)
/* Determine output bundle. */
mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
&ctx->tags);
- if (mac) {
- out_bundle = mac->port.p;
+ if (mac && mac->port.p != in_bundle) {
+ output_normal(ctx, mac->port.p, vlan);
} else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
/* If we are revalidating but don't have a learning entry then eject
* the flow. Installing a flow that floods packets opens up a window
@@ -5029,14 +5053,18 @@ xlate_normal(struct action_xlate_ctx *ctx)
ctx->may_set_up_flow = false;
return;
} else {
- out_bundle = OFBUNDLE_FLOOD;
- }
+ struct ofbundle *bundle;
- /* Don't send packets out their input bundles. */
- if (in_bundle != out_bundle) {
- dst_mirrors = compose_dsts(ctx, vlan, in_bundle, out_bundle);
+ HMAP_FOR_EACH (bundle, hmap_node, &ctx->ofproto->bundles) {
+ if (bundle != in_bundle
+ && ofbundle_includes_vlan(bundle, vlan)
+ && bundle->floodable
+ && !bundle->mirror_out) {
+ output_normal(ctx, bundle, vlan);
+ }
+ }
+ ctx->nf_output_iface = NF_OUT_FLOOD;
}
- output_mirrors(ctx, vlan, in_bundle, dst_mirrors);
}
/* Optimized flow revalidation.
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 34d18f2..dbb0101 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1011,10 +1011,10 @@ struct ofproto_class {
* 'ofproto' associated with client data pointer 'aux'. If no such mirror
* has been registered, this has no effect.
*
- * This function affects only the behavior of the OFPP_NORMAL action. An
- * implementation that does not support it at all may set it to NULL or
- * return EOPNOTSUPP. An implementation that supports only a subset of the
- * functionality should implement what it can and return 0. */
+ * An implementation that does not support it at all may set it to
+ * NULL or return EOPNOTSUPP. An implementation that supports only
+ * a subset of the functionality should implement what it can and
+ * return 0. */
int (*mirror_set)(struct ofproto *ofproto, void *aux,
const struct ofproto_mirror_settings *s);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 1f3a077..b9e3880 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -721,10 +721,7 @@ ofproto_bundle_unregister(struct ofproto *ofproto, void
*aux)
/* Registers a mirror associated with client data pointer 'aux' in 'ofproto'.
* If 'aux' is already registered then this function updates its configuration
- * to 's'. Otherwise, this function registers a new mirror.
- *
- * Mirrors affect only the treatment of packets output to the OFPP_NORMAL
- * port. */
+ * to 's'. Otherwise, this function registers a new mirror. */
int
ofproto_mirror_register(struct ofproto *ofproto, void *aux,
const struct ofproto_mirror_settings *s)
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 9080e7e..b56d157 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -376,3 +376,218 @@ AT_CHECK([tail -1 stdout], [0],
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_all])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ add-port br0 p3 -- set Interface p3 type=dummy --\
+ set Bridge br0 mirrors=@m --\
+ --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror \
+ select_all=true output_port=@p3 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+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])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2,3
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1,3
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_src])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ add-port br0 p3 -- set Interface p3 type=dummy --\
+ 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 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+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])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2,3
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_dst])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ add-port br0 p3 -- set Interface p3 type=dummy --\
+ 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 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+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])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2,3
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, select_vlan])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ add-port br0 p3 -- set Interface p3 type=dummy --\
+ 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 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+AT_DATA([flows.txt], [dnl
+in_port=1, actions=output:2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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))'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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))'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2,3
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, output_port])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ add-port br0 p3 -- set Interface p3 type=dummy --\
+ set Bridge br0 mirrors=@m --\
+ --id=@p3 get Port p3 --\
+ --id=@m create Mirror name=mymirror \
+ select_all=true output_port=@p3 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+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])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: push_vlan(vid=17,pcp=0),2,pop_vlan,3
+])
+AT_CHECK([ovs-appctl ofproto/trace br0
'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)'],
[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1,3
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - mirroring, output_vlan])
+dnl This test assumes that OpenFlow port numbers are allocated in order
+dnl starting from one. Unlike the previous tests, it does require that
+dnl they are allocated in the same order that they are named in the
+dnl database.
+OVS_VSWITCHD_START
+AT_CHECK([ovs-vsctl \
+ add-port br0 p1 -- set Interface p1 type=dummy --\
+ add-port br0 p2 -- set Interface p2 type=dummy --\
+ set Bridge br0 mirrors=@m --\
+ --id=@m create Mirror name=mymirror \
+ select_all=true output_vlan=12 ], [0], [stdout])
+AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], [dnl
+<0>
+])
+
+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 br0 $flow], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+AT_CHECK([ovs-dpctl normalize-actions "$flow"
"2,push_vlan(vid=12,pcp=0),0,1,2"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+
+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 br0 $flow], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+AT_CHECK([ovs-dpctl normalize-actions "$flow"
"push_vlan(vid=17,pcp=0),1,pop_vlan,push_vlan(vid=12,pcp=0),0,1,2"], [0],
[stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
--
1.7.4.1
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev