@@ -173,6 +173,8 @@ struct xport {
struct lldp *lldp; /* LLDP handle or null. */
};
+struct xlate_backup_data;
+
struct xlate_ctx {
struct xlate_in *xin;
struct xlate_out *xout;
@@ -390,6 +392,10 @@ struct xlate_ctx {
struct ofpbuf action_set; /* Action set. */
enum xlate_error error; /* Translation failed. */
+
+ struct xlate_backup_data *xlate_backup_data; /* To restore flow data if
+ something goes wrong
+ during xlate. */
};
/* Structure to track VLAN manipulation */
@@ -6150,6 +6156,118 @@ xlate_ofpact_unroll_xlate(struct xlate_ctx *ctx,
"cookie=%#"PRIx64, a->rule_table_id, a->rule_cookie);
}
+struct xlate_backup_data {
+ size_t odp_actions_size;
+ struct flow wc_masks;
+};
+
+static void
+store_ctx_xlate_data(struct xlate_ctx *ctx)
+{
+ uint64_t *wc = (uint64_t *) &ctx->wc->masks;
+ uint64_t *backup_wc = (uint64_t *) &ctx->xlate_backup_data->wc_masks;
+ int i;
+
+ ctx->xlate_backup_data->odp_actions_size = ctx->odp_actions->size;
+ for (i = 0; i < FLOW_U64S; i++) {
+ if (wc[i]) {
+ backup_wc[i] |= wc[i];
+ }
+ }
+}
+
+static void
+restore_ctx_xlate_data(struct xlate_ctx *ctx)
+{
+ uint64_t *flow = (uint64_t *) &ctx->xin->flow;
+ uint64_t *base_flow = (uint64_t *) &ctx->base_flow;
+ uint64_t *wc = (uint64_t *) &ctx->wc->masks;
+ uint64_t *backup_wc = (uint64_t *) &ctx->xlate_backup_data->wc_masks;
+ int i;
+
+ ctx->odp_actions->size = ctx->xlate_backup_data->odp_actions_size;
+ for (i = 0; i < FLOW_U64S; i++) {
+ if (wc[i]) {
+ wc[i] &= backup_wc[i];
+ flow[i] = base_flow[i];
+ }
+ }
+}
+
+static bool
+ofpact_validates_dp_actions(const struct ofpact *a)
+{
+ switch (a->type) {
+ /* Output. */
+ case OFPACT_OUTPUT:
+ case OFPACT_GROUP:
+ case OFPACT_CONTROLLER:
+ case OFPACT_ENQUEUE:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_BUNDLE:
+ /* Metadata. */
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_FIN_TIMEOUT:
+ /* Flow table interaction. */
+ case OFPACT_RESUBMIT:
+ case OFPACT_LEARN:
+ case OFPACT_CONJUNCTION:
+ /* Arithmetic. */
+ case OFPACT_MULTIPATH:
+ /* Other. */
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ case OFPACT_SAMPLE:
+ case OFPACT_UNROLL_XLATE:
+ case OFPACT_CT:
+ case OFPACT_CT_CLEAR:
+ case OFPACT_NAT:
+ case OFPACT_OUTPUT_TRUNC:
+ case OFPACT_CLONE:
+ /* Instructions. */
+ case OFPACT_METER:
+ case OFPACT_CLEAR_ACTIONS:
+ case OFPACT_WRITE_ACTIONS:
+ case OFPACT_WRITE_METADATA:
+ case OFPACT_GOTO_TABLE:
+ return true;
+ /* Header changes. */
+ case OFPACT_SET_FIELD:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IP_DSCP:
+ case OFPACT_SET_IP_ECN:
+ case OFPACT_SET_IP_TTL:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_REG_MOVE:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STACK_POP:
+ case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
+ /* Generic encap & decap */
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
+ /* Debugging actions. */
+ case OFPACT_DEBUG_RECIRC:
+ default:
+ return false;
+ }
+}
+
static void
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct xlate_ctx *ctx, bool is_last_action)
@@ -6168,6 +6286,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
return;
}
+ /* Initialize xlate_data. */
+ store_ctx_xlate_data(ctx);
+
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
struct ofpact_controller *controller;
const struct ofpact_metadata *metadata;
@@ -6175,8 +6296,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
const struct mf_field *mf;
bool last = is_last_action && ofpact_last(a, ofpacts, ofpacts_len)
&& ctx->action_set.size;
+ size_t old_size;
if (ctx->error) {
+ /* Restore xlate data. */
+ restore_ctx_xlate_data(ctx);
break;
}
@@ -6199,6 +6323,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
ds_destroy(&s);
}
+ old_size = ctx->odp_actions->size;
+
switch (a->type) {
case OFPACT_OUTPUT:
xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
@@ -6558,6 +6684,12 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
break;
}
+ /* Store xlate data. */
+ if (ofpact_validates_dp_actions(a) && !ctx->error
+ && old_size != ctx->odp_actions->size) {
+ store_ctx_xlate_data(ctx);
+ }
+
/* Check if need to store this and the remaining actions for later
* execution. */
if (!ctx->error && ctx->exit && ctx_first_frozen_action(ctx)) {
@@ -6898,6 +7030,9 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
.action_set = OFPBUF_STUB_INITIALIZER(action_set_stub),
};
+ /* Allocate memory for xlate backup data. */
+ ctx.xlate_backup_data = xzalloc(sizeof(struct xlate_backup_data));
+
/* 'base_flow' reflects the packet as it came in, but we need it to reflect
* the packet as the datapath will treat it for output actions. Our
* datapath doesn't retain tunneling information without us re-setting
@@ -7221,6 +7356,8 @@ exit:
ofpbuf_uninit(&scratch_actions);
ofpbuf_delete(ctx.encap_data);
+ free(ctx.xlate_backup_data);
+
/* Make sure we return a "drop flow" in case of an error. */
if (ctx.error) {
xout->slow = 0;
@@ -9954,3 +9954,182 @@ AT_CHECK([grep flow_del ovs-vswitchd.log], [1])
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - xlate error - rollback])
+
+# ->-+
+# | p1 LOCAL
+# +-o-------+ +---------+ +---------+ +-------o-+
+# | br0 | | br1 | | br2 | | br3 |
+# +-------o-+ +-o-----o-+ +-o-----o-+ +-o-------+
+# p01 | p10 | | p12 p21 | | p23 p32 |
+# +-----------+ +-----------+ +-----------+
+
+OVS_VSWITCHD_START([dnl
+ -- add-br br1 -- set bridge br1 datapath_type=dummy \
+ -- add-br br2 -- set bridge br2 datapath_type=dummy \
+ -- add-br br3 -- set bridge br3 datapath_type=dummy \
+ -- add-port br0 p01 \
+ -- set interface p01 type=patch options:peer=p10 ofport_request=10 \
+ -- add-port br1 p10 \
+ -- set interface p10 type=patch options:peer=p01 ofport_request=20 \
+ -- add-port br1 p12 \
+ -- set interface p12 type=patch options:peer=p21 ofport_request=30 \
+ -- add-port br2 p21 \
+ -- set interface p21 type=patch options:peer=p12 ofport_request=40 \
+ -- add-port br2 p23 \
+ -- set interface p23 type=patch options:peer=p32 ofport_request=50 \
+ -- add-port br3 p32 \
+ -- set interface p32 type=patch options:peer=p23 ofport_request=60
+])
+
+AT_CHECK([
+ ovs-vsctl add-port br0 p1 -- set interface p1 type=dummy ofport_request=1
+])
+
+AT_CHECK([
+ ovs-appctl dpif/show | grep -v hit | sed "s/$(printf \\t)/ /g" | sed 's./[[0-9]]\{1,\}..'
+], [0], [dnl
+ br0:
+ br0 65534: (dummy-internal)
+ p01 10/none: (patch: peer=p10)
+ p1 1: (dummy)
+ br1:
+ br1 65534: (dummy-internal)
+ p10 20/none: (patch: peer=p01)
+ p12 30/none: (patch: peer=p21)
+ br2:
+ br2 65534: (dummy-internal)
+ p21 40/none: (patch: peer=p12)
+ p23 50/none: (patch: peer=p32)
+ br3:
+ br3 65534: (dummy-internal)
+ p32 60/none: (patch: peer=p23)
+])
+
+AT_CHECK([
+ ovs-ofctl del-flows br0
+ ovs-ofctl del-flows br1
+ ovs-ofctl del-flows br2
+ ovs-ofctl del-flows br3
+], [0])
+
+# Error due to pushing too many MPLS labels.
+AT_CHECK([
+ ovs-ofctl add-flow -OOpenFlow13 br0 "in_port=1,ip,actions=dec_ttl,push_mpls:0x8847,output:p01"
+ ovs-ofctl add-flow -OOpenFlow13 br1 "in_port=p10,mpls,actions=mod_dl_src:aa:aa:aa:bb:bb:f0,push_mpls:0x8847,output:p12"
+ ovs-ofctl add-flow -OOpenFlow13 br2 "in_port=p21,mpls,actions=mod_dl_dst:aa:aa:aa:00:00:01,push_mpls:0x8847,output:p23"
+ ovs-ofctl add-flow -OOpenFlow13 br3 "in_port=p32,mpls,actions=push_mpls:0x8847,output:LOCAL"
+], [0])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,ip,nw_ttl=255'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: drop
+])
+
+AT_CHECK([
+ ovs-ofctl del-flows br0
+ ovs-ofctl del-flows br1
+ ovs-ofctl del-flows br2
+ ovs-ofctl del-flows br3
+], [0])
+
+# Error due to applying encap(Ethernet) to an Ethernet packet.
+AT_CHECK([
+ovs-ofctl add-flow br0 "in_port=1,actions=dec_ttl,output:p01"
+ovs-ofctl add-flow br1 in_port=p10,actions=p12
+ovs-ofctl add-flow br2 -OOpenFlow13 "in_port=p21,ip,actions=dec_ttl,dec_ttl,dec_ttl,dec_ttl,output:LOCAL,dec_ttl,mod_dl_src:aa:aa:aa:bb:bb:ff,encap(ethernet),dec_ttl,output:p23"
+ovs-ofctl add-flow br3 "in_port=p32,actions=LOCAL"
+], [0])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,ip,nw_ttl=255'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 102
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - xlate error - rollback2])
+
+# ->-+
+# | p1 LOCAL LOCAL
+# +-o-------+ +-------o-+ +---------+ +-------o-+
+# | br0 | | br1 | | br2 | | br3 |
+# +-------o-+ +-o----o-o+ +-o-----o-+ +-o-o-----+
+# p01 | p10 | p13| |p12 p21 | | p23 p32 | | p31
+# +-----------+ | +----------+ +-----------+ |
+# +--------------------------------+
+
+OVS_VSWITCHD_START([dnl
+ -- add-br br1 -- set bridge br1 datapath_type=dummy \
+ -- add-br br2 -- set bridge br2 datapath_type=dummy \
+ -- add-br br3 -- set bridge br3 datapath_type=dummy \
+ -- add-port br0 p01 \
+ -- set interface p01 type=patch options:peer=p10 ofport_request=10 \
+ -- add-port br1 p10 \
+ -- set interface p10 type=patch options:peer=p01 ofport_request=20 \
+ -- add-port br1 p12 \
+ -- set interface p12 type=patch options:peer=p21 ofport_request=30 \
+ -- add-port br2 p21 \
+ -- set interface p21 type=patch options:peer=p12 ofport_request=40 \
+ -- add-port br2 p23 \
+ -- set interface p23 type=patch options:peer=p32 ofport_request=50 \
+ -- add-port br3 p32 \
+ -- set interface p32 type=patch options:peer=p23 ofport_request=60 \
+ -- add-port br1 p13 \
+ -- set interface p13 type=patch options:peer=p31 ofport_request=70 \
+ -- add-port br3 p31 \
+ -- set interface p31 type=patch options:peer=p13 ofport_request=80
+])
+
+AT_CHECK([
+ ovs-vsctl add-port br0 p1 -- set interface p1 type=dummy ofport_request=1
+])
+
+AT_CHECK([
+ ovs-appctl dpif/show | grep -v hit | sed "s/$(printf \\t)/ /g" | sed 's./[[0-9]]\{1,\}..'
+], [0], [dnl
+ br0:
+ br0 65534: (dummy-internal)
+ p01 10/none: (patch: peer=p10)
+ p1 1: (dummy)
+ br1:
+ br1 65534: (dummy-internal)
+ p10 20/none: (patch: peer=p01)
+ p12 30/none: (patch: peer=p21)
+ p13 70/none: (patch: peer=p31)
+ br2:
+ br2 65534: (dummy-internal)
+ p21 40/none: (patch: peer=p12)
+ p23 50/none: (patch: peer=p32)
+ br3:
+ br3 65534: (dummy-internal)
+ p31 80/none: (patch: peer=p13)
+ p32 60/none: (patch: peer=p23)
+])
+
+
+AT_CHECK([
+ ovs-ofctl del-flows br0
+ ovs-ofctl del-flows br1
+ ovs-ofctl del-flows br2
+ ovs-ofctl del-flows br3
+], [0])
+
+# Drop packet due to xlate error on one path (br0-br1-br2-b3) but forward it via another one (br0-br1-br3).
+AT_CHECK([
+ ovs-ofctl add-flow br0 "in_port=1,actions=dec_ttl,output:p01"
+ ovs-ofctl add-flow br1 in_port=p10,ip,actions=dec_ttl,dec_ttl,dec_ttl,dec_ttl,output:LOCAL,mod_dl_dst:ee:ee:ee:ff:ff:01,output:p12,p13
+ ovs-ofctl add-flow br2 -OOpenFlow13 "in_port=p21,ip,actions=dec_ttl,dec_ttl,dec_ttl,dec_ttl,dec_ttl,mod_dl_src:aa:aa:aa:bb:bb:ff,encap(ethernet),dec_ttl,output:p23"
+ ovs-ofctl add-flow br3 "in_port=p32,actions=LOCAL"
+ ovs-ofctl add-flow br3 "in_port=p31,actions=LOCAL"
+], [0])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,ip,nw_ttl=255'], [0], [stdout])
+AT_CHECK([grep "Datapath actions" stdout], [0],
+ [Datapath actions: 101,set(eth(dst=ee:ee:ee:ff:ff:01)),103
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP