@@ -109,6 +109,7 @@
OFPACT(CT, ofpact_conntrack, ofpact, "ct") \
OFPACT(NAT, ofpact_nat, ofpact, "nat") \
OFPACT(OUTPUT_TRUNC, ofpact_output_trunc,ofpact, "output_trunc") \
+ OFPACT(CLONE, ofpact_clone, ofpact, "clone") \
\
/* Debugging actions. \
* \
@@ -868,6 +869,28 @@ struct ofpact_sample {
enum nx_action_sample_direction direction;
};
+/* OFPACT_CLONE.
+ *
+ * Used for cloning the packet and execute actions
+ * in the clone envelope. */
+struct ofpact_clone {
+ OFPACT_PADDED_MEMBERS(
+ struct ofpact ofpact;
+ );
+ struct ofpact actions[0];
+};
+
+BUILD_ASSERT_DECL(offsetof(struct ofpact_clone, actions)
+ % OFPACT_ALIGNTO == 0);
+BUILD_ASSERT_DECL(offsetof(struct ofpact_clone, actions)
+ == sizeof(struct ofpact_clone));
+
+static inline size_t
+ofpact_clone_get_action_len(const struct ofpact_clone *oc)
+{
+ return oc->ofpact.len - offsetof(struct ofpact_clone, actions);
+}
+
/* OFPACT_DEC_TTL.
*
* Used for OFPAT11_DEC_NW_TTL, NXAST_DEC_TTL and NXAST_DEC_TTL_CNT_IDS. */
@@ -306,6 +306,9 @@ enum ofp_raw_action_type {
/* NX1.0+(39): struct nx_action_output_trunc. */
NXAST_RAW_OUTPUT_TRUNC,
+ /* NX1.0+(42): struct nx_action_clone, ... */
+ NXAST_RAW_CLONE,
+
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -386,6 +389,7 @@ ofpact_next_flattened(const struct ofpact *ofpact)
switch (ofpact->type) {
case OFPACT_OUTPUT:
case OFPACT_GROUP:
+ case OFPACT_CLONE:
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_OUTPUT_REG:
@@ -4801,6 +4805,90 @@ format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a, struct ds *s)
colors.paren, colors.end);
}
+
+struct nx_action_clone {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is at least 16. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_CLONE. */
+ ovs_be16 pad0;
+ ovs_be32 pad1;
+ /* Followed by a sequence of zero or more OpenFlow actions. The length
+ * of these is included in 'len'. */
+};
+OFP_ASSERT(sizeof(struct nx_action_clone) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_CLONE(const struct nx_action_clone *nac,
+ enum ofp_version ofp_version,
+ struct ofpbuf *out)
+{
+ int error;
+ struct ofpbuf openflow;
+ const size_t clone_offset = ofpacts_pull(out);
+ struct ofpact_clone *clone = ofpact_put_CLONE(out);
+
+ /* decode action list */
+ ofpbuf_pull(out, sizeof(*clone));
+ openflow = ofpbuf_const_initializer(
+ nac + 1, ntohs(nac->len) - sizeof(*nac));
+ error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
+ ofp_version,
+ 1u << OVSINST_OFPIT11_APPLY_ACTIONS,
+ out, OFPACT_CLONE);
+ clone = ofpbuf_push_uninit(out, sizeof(*clone));
+ out->header = &clone->ofpact;
+ ofpact_finish_CLONE(out, &clone);
+ ofpbuf_push_uninit(out, clone_offset);
+ return error;
+}
+
+static void
+encode_CLONE(const struct ofpact_clone *clone,
+ enum ofp_version ofp_version, struct ofpbuf *out)
+{
+ size_t len;
+ const size_t ofs = out->size;
+ struct nx_action_clone *nac;
+
+ nac = put_NXAST_CLONE(out);
+ len = ofpacts_put_openflow_actions(clone->actions,
+ ofpact_clone_get_action_len(clone),
+ out, ofp_version);
+ len += sizeof(*nac);
+ nac = ofpbuf_at(out, ofs, sizeof(*nac));
+ nac->len = htons(len);
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_CLONE(char *arg, struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols)
+{
+
+ const size_t ct_offset = ofpacts_pull(ofpacts);
+ struct ofpact_clone *clone = ofpact_put_CLONE(ofpacts);
+ char *error;
+
+ ofpbuf_pull(ofpacts, sizeof(*clone));
+ error = ofpacts_parse_copy(arg, ofpacts, usable_protocols,
+ false, OFPACT_CLONE);
+ /* header points to the action list */
+ ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof(*clone));
+ clone = ofpacts->header;
+
+ ofpact_finish_CLONE(ofpacts, &clone);
+ ofpbuf_push_uninit(ofpacts, ct_offset);
+ return error;
+}
+
+static void
+format_CLONE(const struct ofpact_clone *a, struct ds *s)
+{
+ ds_put_format(s, "%sclone(%s", colors.paren, colors.end);
+ ofpacts_format(a->actions, ofpact_clone_get_action_len(a), s);
+ ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
/* Action structure for NXAST_SAMPLE.
*
* Samples matching packets with the given probability and sends them
@@ -6212,6 +6300,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
case OFPACT_BUNDLE:
case OFPACT_CLEAR_ACTIONS:
case OFPACT_CT:
+ case OFPACT_CLONE:
case OFPACT_NAT:
case OFPACT_CONTROLLER:
case OFPACT_DEC_MPLS_TTL:
@@ -6288,6 +6377,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
* the specification. Thus the order in which they should be applied
* in the action set is undefined. */
case OFPACT_BUNDLE:
+ case OFPACT_CLONE:
case OFPACT_CONTROLLER:
case OFPACT_CT:
case OFPACT_NAT:
@@ -6480,6 +6570,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
return OVSINST_OFPIT11_GOTO_TABLE;
case OFPACT_OUTPUT:
case OFPACT_GROUP:
+ case OFPACT_CLONE:
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_OUTPUT_REG:
@@ -7080,6 +7171,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_SAMPLE:
return 0;
+ case OFPACT_CLONE:
+ return 0;
+
case OFPACT_CT: {
struct ofpact_conntrack *oc = ofpact_get_CT(a);
@@ -7271,7 +7365,7 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
if (outer_action) {
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
- || outer_action == OFPACT_CT);
+ || outer_action == OFPACT_CT || outer_action == OFPACT_CLONE);
if (outer_action == OFPACT_CT) {
if (!field) {
@@ -7282,6 +7376,10 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
return OFPERR_OFPBAC_BAD_ARGUMENT;
}
}
+ if (outer_action == OFPACT_CLONE) {
+ /* add some constraints. */
+ return 0;
+ }
}
return 0;
@@ -7635,6 +7733,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
case OFPACT_POP_MPLS:
case OFPACT_SAMPLE:
case OFPACT_CLEAR_ACTIONS:
+ case OFPACT_CLONE:
case OFPACT_WRITE_ACTIONS:
case OFPACT_GOTO_TABLE:
case OFPACT_METER:
@@ -4289,6 +4289,35 @@ xlate_sample_action(struct xlate_ctx *ctx,
tunnel_out_port, false);
}
+static void
+compose_clone_action(struct xlate_ctx *ctx,
+ const struct ofpact_clone *oc)
+{
+ struct flow old_flow, old_base_flow;
+ size_t actions_offset;
+ size_t sample_offset = nl_msg_start_nested(ctx->odp_actions,
+ OVS_ACTION_ATTR_SAMPLE);
+
+ /* Ensure that any prior actions are applied. */
+ xlate_commit_actions(ctx);
+
+ /* clone might change the flow key, store the original. */
+ old_flow = ctx->xin->flow;
+ old_base_flow = ctx->base_flow;
+
+ /* Sample with 100% Probability */
+ nl_msg_put_u32(ctx->odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, UINT32_MAX);
+ actions_offset = nl_msg_start_nested(ctx->odp_actions,
+ OVS_SAMPLE_ATTR_ACTIONS);
+ do_xlate_actions(oc->actions, ofpact_clone_get_action_len(oc), ctx);
+ nl_msg_end_nested(ctx->odp_actions, actions_offset);
+ nl_msg_end_nested(ctx->odp_actions, sample_offset);
+
+ /* restore flow key */
+ ctx->xin->flow = old_flow;
+ ctx->base_flow = old_base_flow;
+}
+
static bool
may_receive(const struct xport *xport, struct xlate_ctx *ctx)
{
@@ -4447,6 +4476,7 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
case OFPACT_WRITE_ACTIONS:
case OFPACT_METER:
case OFPACT_SAMPLE:
+ case OFPACT_CLONE:
case OFPACT_DEBUG_RECIRC:
case OFPACT_CT:
case OFPACT_NAT:
@@ -4695,6 +4725,7 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
case OFPACT_NOTE:
case OFPACT_EXIT:
case OFPACT_SAMPLE:
+ case OFPACT_CLONE:
case OFPACT_UNROLL_XLATE:
case OFPACT_CT:
case OFPACT_NAT:
@@ -5054,6 +5085,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
break;
+ case OFPACT_CLONE:
+ compose_clone_action(ctx, ofpact_get_CLONE(a));
+ break;
+
case OFPACT_CT:
compose_conntrack_action(ctx, ofpact_get_CT(a));
break;
@@ -6388,6 +6388,25 @@ AT_CHECK([ovs-vsctl destroy Flow_Sample_Collector_Set 1], [0], [ignore])
OVS_VSWITCHD_STOP
AT_CLEANUP
+dnl OpenFlow Clone action using Sample datapath action
+AT_SETUP([ofproto-dpif - clone action])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3 4
+
+AT_DATA([flows.txt], [dnl
+in_port=1, actions=clone(set_field:192.168.3.3->ip_src),clone(set_field:192.168.4.4->ip_dst,output:2),clone(mod_dl_src:80:81:81:81:81:81,set_field:192.168.5.5->ip_dst,output:3),output:4
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt], [0], [ignore])
+
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.10.10.2,dst=10.10.10.1,proto=1,tos=1,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: sample(sample=100.0%,actions(drop)),sample(sample=100.0%,actions(set(ipv4(src=10.10.10.2,dst=192.168.4.4)),2)),sample(sample=100.0%,actions(set(eth(src=80:81:81:81:81:81)),set(ipv4(src=10.10.10.2,dst=192.168.5.5)),3)),4
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
dnl Flow based IPFIX statistics check
AT_SETUP([ofproto-dpif - Flow IPFIX statistics check])
OVS_VSWITCHD_START
@@ -337,6 +337,35 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PI
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([datapath - clone action])
+OVS_TRAFFIC_VSWITCHD_START()
+
+ADD_NAMESPACES(at_ns0, at_ns1, at_ns2)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24")
+
+AT_CHECK([ovs-vsctl -- set interface ovs-p0 ofport_request=1])
+AT_CHECK([ovs-vsctl -- set interface ovs-p1 ofport_request=2])
+AT_CHECK([ovs-vsctl -- set interface ovs-p2 ofport_request=3])
+
+dnl verify that the clone(...) won't affect the original packet, so ping still works OK
+dnl without 'output' in 'clone()'
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=1,actions=clone(mod_dl_dst(50:54:00:00:00:0a),set_field:192.168.3.3->ip_dst), output:2"])
+dnl with 'output' in 'clone()'
+AT_CHECK([ovs-ofctl add-flow br0 "in_port=2,actions=clone(mod_dl_dst(50:54:00:00:00:0b),set_field:192.168.4.4->ip_dst, output:3), output:1"])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-l7.py]], [http0.pid])
+NS_CHECK_EXEC([at_ns0], [wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0.log])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([datapath - basic truncate action])
OVS_TRAFFIC_VSWITCHD_START()
AT_CHECK([ovs-ofctl del-flows br0])