|
@@ -2460,6 +2460,40 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int validate_and_copy_clone(struct net *net,
|
|
|
+ const struct nlattr *attr,
|
|
|
+ const struct sw_flow_key *key,
|
|
|
+ struct sw_flow_actions **sfa,
|
|
|
+ __be16 eth_type, __be16 vlan_tci,
|
|
|
+ bool log, bool last)
|
|
|
+{
|
|
|
+ int start, err;
|
|
|
+ u32 exec;
|
|
|
+
|
|
|
+ if (nla_len(attr) && nla_len(attr) < NLA_HDRLEN)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ start = add_nested_action_start(sfa, OVS_ACTION_ATTR_CLONE, log);
|
|
|
+ if (start < 0)
|
|
|
+ return start;
|
|
|
+
|
|
|
+ exec = last || !actions_may_change_flow(attr);
|
|
|
+
|
|
|
+ err = ovs_nla_add_action(sfa, OVS_CLONE_ATTR_EXEC, &exec,
|
|
|
+ sizeof(exec), log);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = __ovs_nla_copy_actions(net, attr, key, sfa,
|
|
|
+ eth_type, vlan_tci, log);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ add_nested_action_end(*sfa, start);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
void ovs_match_init(struct sw_flow_match *match,
|
|
|
struct sw_flow_key *key,
|
|
|
bool reset_key,
|
|
@@ -2849,6 +2883,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|
|
[OVS_ACTION_ATTR_PUSH_NSH] = (u32)-1,
|
|
|
[OVS_ACTION_ATTR_POP_NSH] = 0,
|
|
|
[OVS_ACTION_ATTR_METER] = sizeof(u32),
|
|
|
+ [OVS_ACTION_ATTR_CLONE] = (u32)-1,
|
|
|
};
|
|
|
const struct ovs_action_push_vlan *vlan;
|
|
|
int type = nla_type(a);
|
|
@@ -3038,6 +3073,18 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|
|
/* Non-existent meters are simply ignored. */
|
|
|
break;
|
|
|
|
|
|
+ case OVS_ACTION_ATTR_CLONE: {
|
|
|
+ bool last = nla_is_last(a, rem);
|
|
|
+
|
|
|
+ err = validate_and_copy_clone(net, a, key, sfa,
|
|
|
+ eth_type, vlan_tci,
|
|
|
+ log, last);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ skip_copy = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
default:
|
|
|
OVS_NLERR(log, "Unknown Action type %d", type);
|
|
|
return -EINVAL;
|
|
@@ -3116,6 +3163,26 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int clone_action_to_attr(const struct nlattr *attr,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct nlattr *start;
|
|
|
+ int err = 0, rem = nla_len(attr);
|
|
|
+
|
|
|
+ start = nla_nest_start(skb, OVS_ACTION_ATTR_CLONE);
|
|
|
+ if (!start)
|
|
|
+ return -EMSGSIZE;
|
|
|
+
|
|
|
+ err = ovs_nla_put_actions(nla_data(attr), rem, skb);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ nla_nest_cancel(skb, start);
|
|
|
+ else
|
|
|
+ nla_nest_end(skb, start);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
|
|
|
{
|
|
|
const struct nlattr *ovs_key = nla_data(a);
|
|
@@ -3204,6 +3271,12 @@ int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb)
|
|
|
return err;
|
|
|
break;
|
|
|
|
|
|
+ case OVS_ACTION_ATTR_CLONE:
|
|
|
+ err = clone_action_to_attr(a, skb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
if (nla_put(skb, type, nla_len(a), nla_data(a)))
|
|
|
return -EMSGSIZE;
|