diff options
Diffstat (limited to 'net/openvswitch/flow_netlink.c')
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 99 | 
1 files changed, 93 insertions, 6 deletions
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index cc282a58b75b..7176156d3844 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2317,6 +2317,62 @@ static struct sw_flow_actions *nla_alloc_flow_actions(int size)  	return sfa;  } +static void ovs_nla_free_nested_actions(const struct nlattr *actions, int len); + +static void ovs_nla_free_check_pkt_len_action(const struct nlattr *action) +{ +	const struct nlattr *a; +	int rem; + +	nla_for_each_nested(a, action, rem) { +		switch (nla_type(a)) { +		case OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL: +		case OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER: +			ovs_nla_free_nested_actions(nla_data(a), nla_len(a)); +			break; +		} +	} +} + +static void ovs_nla_free_clone_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); +	int rem = nla_len(action); + +	switch (nla_type(a)) { +	case OVS_CLONE_ATTR_EXEC: +		/* The real list of actions follows this attribute. */ +		a = nla_next(a, &rem); +		ovs_nla_free_nested_actions(a, rem); +		break; +	} +} + +static void ovs_nla_free_dec_ttl_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); + +	switch (nla_type(a)) { +	case OVS_DEC_TTL_ATTR_ACTION: +		ovs_nla_free_nested_actions(nla_data(a), nla_len(a)); +		break; +	} +} + +static void ovs_nla_free_sample_action(const struct nlattr *action) +{ +	const struct nlattr *a = nla_data(action); +	int rem = nla_len(action); + +	switch (nla_type(a)) { +	case OVS_SAMPLE_ATTR_ARG: +		/* The real list of actions follows this attribute. */ +		a = nla_next(a, &rem); +		ovs_nla_free_nested_actions(a, rem); +		break; +	} +} +  static void ovs_nla_free_set_action(const struct nlattr *a)  {  	const struct nlattr *ovs_key = nla_data(a); @@ -2330,25 +2386,54 @@ static void ovs_nla_free_set_action(const struct nlattr *a)  	}  } -void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) +static void ovs_nla_free_nested_actions(const struct nlattr *actions, int len)  {  	const struct nlattr *a;  	int rem; -	if (!sf_acts) +	/* Whenever new actions are added, the need to update this +	 * function should be considered. +	 */ +	BUILD_BUG_ON(OVS_ACTION_ATTR_MAX != 23); + +	if (!actions)  		return; -	nla_for_each_attr(a, sf_acts->actions, sf_acts->actions_len, rem) { +	nla_for_each_attr(a, actions, len, rem) {  		switch (nla_type(a)) { -		case OVS_ACTION_ATTR_SET: -			ovs_nla_free_set_action(a); +		case OVS_ACTION_ATTR_CHECK_PKT_LEN: +			ovs_nla_free_check_pkt_len_action(a); +			break; + +		case OVS_ACTION_ATTR_CLONE: +			ovs_nla_free_clone_action(a);  			break; +  		case OVS_ACTION_ATTR_CT:  			ovs_ct_free_action(a);  			break; + +		case OVS_ACTION_ATTR_DEC_TTL: +			ovs_nla_free_dec_ttl_action(a); +			break; + +		case OVS_ACTION_ATTR_SAMPLE: +			ovs_nla_free_sample_action(a); +			break; + +		case OVS_ACTION_ATTR_SET: +			ovs_nla_free_set_action(a); +			break;  		}  	} +} + +void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) +{ +	if (!sf_acts) +		return; +	ovs_nla_free_nested_actions(sf_acts->actions, sf_acts->actions_len);  	kfree(sf_acts);  } @@ -3458,7 +3543,9 @@ static int clone_action_to_attr(const struct nlattr *attr,  	if (!start)  		return -EMSGSIZE; -	err = ovs_nla_put_actions(nla_data(attr), rem, skb); +	/* Skipping the OVS_CLONE_ATTR_EXEC that is always the first attribute. */ +	attr = nla_next(nla_data(attr), &rem); +	err = ovs_nla_put_actions(attr, rem, skb);  	if (err)  		nla_nest_cancel(skb, start);  | 
