Commit 3fa10563 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-sched-action-bind'



Pedro Tammela says:

====================
net/sched: fix action bind logic

Some actions are not handling the case where an action can be created and bound to a
filter independently. These actions are checking for parameters only passed
in the netlink message for create/change/replace, which then errors out
for valid uses like:
tc filter ... action pedit index 1

In the iproute2 side, we saw a couple of actions with their parsers
broken when passing "index 1" as the only action argument, while the kernel
side accepted it correctly. We fixed those as well.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents aaa3c08e 4a20056a
Loading
Loading
Loading
Loading
+37 −29
Original line number Original line Diff line number Diff line
@@ -190,40 +190,67 @@ static int tcf_mpls_init(struct net *net, struct nlattr *nla,
	parm = nla_data(tb[TCA_MPLS_PARMS]);
	parm = nla_data(tb[TCA_MPLS_PARMS]);
	index = parm->index;
	index = parm->index;


	err = tcf_idr_check_alloc(tn, &index, a, bind);
	if (err < 0)
		return err;
	exists = err;
	if (exists && bind)
		return 0;

	if (!exists) {
		ret = tcf_idr_create(tn, index, est, a, &act_mpls_ops, bind,
				     true, flags);
		if (ret) {
			tcf_idr_cleanup(tn, index);
			return ret;
		}

		ret = ACT_P_CREATED;
	} else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
		tcf_idr_release(*a, bind);
		return -EEXIST;
	}

	/* Verify parameters against action type. */
	/* Verify parameters against action type. */
	switch (parm->m_action) {
	switch (parm->m_action) {
	case TCA_MPLS_ACT_POP:
	case TCA_MPLS_ACT_POP:
		if (!tb[TCA_MPLS_PROTO]) {
		if (!tb[TCA_MPLS_PROTO]) {
			NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop");
			NL_SET_ERR_MSG_MOD(extack, "Protocol must be set for MPLS pop");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
		if (!eth_proto_is_802_3(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
			NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop");
			NL_SET_ERR_MSG_MOD(extack, "Invalid protocol type for MPLS pop");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] ||
		if (tb[TCA_MPLS_LABEL] || tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] ||
		    tb[TCA_MPLS_BOS]) {
		    tb[TCA_MPLS_BOS]) {
			NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop");
			NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC or BOS cannot be used with MPLS pop");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		break;
		break;
	case TCA_MPLS_ACT_DEC_TTL:
	case TCA_MPLS_ACT_DEC_TTL:
		if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] ||
		if (tb[TCA_MPLS_PROTO] || tb[TCA_MPLS_LABEL] ||
		    tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) {
		    tb[TCA_MPLS_TTL] || tb[TCA_MPLS_TC] || tb[TCA_MPLS_BOS]) {
			NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl");
			NL_SET_ERR_MSG_MOD(extack, "Label, TTL, TC, BOS or protocol cannot be used with MPLS dec_ttl");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		break;
		break;
	case TCA_MPLS_ACT_PUSH:
	case TCA_MPLS_ACT_PUSH:
	case TCA_MPLS_ACT_MAC_PUSH:
	case TCA_MPLS_ACT_MAC_PUSH:
		if (!tb[TCA_MPLS_LABEL]) {
		if (!tb[TCA_MPLS_LABEL]) {
			NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push");
			NL_SET_ERR_MSG_MOD(extack, "Label is required for MPLS push");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		if (tb[TCA_MPLS_PROTO] &&
		if (tb[TCA_MPLS_PROTO] &&
		    !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
		    !eth_p_mpls(nla_get_be16(tb[TCA_MPLS_PROTO]))) {
			NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push");
			NL_SET_ERR_MSG_MOD(extack, "Protocol must be an MPLS type for MPLS push");
			return -EPROTONOSUPPORT;
			err = -EPROTONOSUPPORT;
			goto release_idr;
		}
		}
		/* Push needs a TTL - if not specified, set a default value. */
		/* Push needs a TTL - if not specified, set a default value. */
		if (!tb[TCA_MPLS_TTL]) {
		if (!tb[TCA_MPLS_TTL]) {
@@ -238,33 +265,14 @@ static int tcf_mpls_init(struct net *net, struct nlattr *nla,
	case TCA_MPLS_ACT_MODIFY:
	case TCA_MPLS_ACT_MODIFY:
		if (tb[TCA_MPLS_PROTO]) {
		if (tb[TCA_MPLS_PROTO]) {
			NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify");
			NL_SET_ERR_MSG_MOD(extack, "Protocol cannot be used with MPLS modify");
			return -EINVAL;
			err = -EINVAL;
			goto release_idr;
		}
		}
		break;
		break;
	default:
	default:
		NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action");
		NL_SET_ERR_MSG_MOD(extack, "Unknown MPLS action");
		return -EINVAL;
		err = -EINVAL;
	}
		goto release_idr;

	err = tcf_idr_check_alloc(tn, &index, a, bind);
	if (err < 0)
		return err;
	exists = err;
	if (exists && bind)
		return 0;

	if (!exists) {
		ret = tcf_idr_create(tn, index, est, a,
				     &act_mpls_ops, bind, true, flags);
		if (ret) {
			tcf_idr_cleanup(tn, index);
			return ret;
		}

		ret = ACT_P_CREATED;
	} else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
		tcf_idr_release(*a, bind);
		return -EEXIST;
	}
	}


	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
+31 −27
Original line number Original line Diff line number Diff line
@@ -181,26 +181,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
	}
	}


	parm = nla_data(pattr);
	parm = nla_data(pattr);
	if (!parm->nkeys) {
		NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
		return -EINVAL;
	}
	ksize = parm->nkeys * sizeof(struct tc_pedit_key);
	if (nla_len(pattr) < sizeof(*parm) + ksize) {
		NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
		return -EINVAL;
	}

	nparms = kzalloc(sizeof(*nparms), GFP_KERNEL);
	if (!nparms)
		return -ENOMEM;

	nparms->tcfp_keys_ex =
		tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
	if (IS_ERR(nparms->tcfp_keys_ex)) {
		ret = PTR_ERR(nparms->tcfp_keys_ex);
		goto out_free;
	}


	index = parm->index;
	index = parm->index;
	err = tcf_idr_check_alloc(tn, &index, a, bind);
	err = tcf_idr_check_alloc(tn, &index, a, bind);
@@ -209,25 +189,49 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
						&act_pedit_ops, bind, flags);
						&act_pedit_ops, bind, flags);
		if (ret) {
		if (ret) {
			tcf_idr_cleanup(tn, index);
			tcf_idr_cleanup(tn, index);
			goto out_free_ex;
			return ret;
		}
		}
		ret = ACT_P_CREATED;
		ret = ACT_P_CREATED;
	} else if (err > 0) {
	} else if (err > 0) {
		if (bind)
		if (bind)
			goto out_free;
			return 0;
		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
			ret = -EEXIST;
			ret = -EEXIST;
			goto out_release;
			goto out_release;
		}
		}
	} else {
	} else {
		ret = err;
		return err;
		goto out_free_ex;
	}

	if (!parm->nkeys) {
		NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
		ret = -EINVAL;
		goto out_release;
	}
	ksize = parm->nkeys * sizeof(struct tc_pedit_key);
	if (nla_len(pattr) < sizeof(*parm) + ksize) {
		NL_SET_ERR_MSG_ATTR(extack, pattr, "Length of TCA_PEDIT_PARMS or TCA_PEDIT_PARMS_EX pedit attribute is invalid");
		ret = -EINVAL;
		goto out_release;
	}

	nparms = kzalloc(sizeof(*nparms), GFP_KERNEL);
	if (!nparms) {
		ret = -ENOMEM;
		goto out_release;
	}

	nparms->tcfp_keys_ex =
		tcf_pedit_keys_ex_parse(tb[TCA_PEDIT_KEYS_EX], parm->nkeys);
	if (IS_ERR(nparms->tcfp_keys_ex)) {
		ret = PTR_ERR(nparms->tcfp_keys_ex);
		goto out_free;
	}
	}


	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
	if (err < 0) {
	if (err < 0) {
		ret = err;
		ret = err;
		goto out_release;
		goto out_free_ex;
	}
	}


	nparms->tcfp_off_max_hint = 0;
	nparms->tcfp_off_max_hint = 0;
@@ -278,12 +282,12 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
put_chain:
put_chain:
	if (goto_ch)
	if (goto_ch)
		tcf_chain_put_by_act(goto_ch);
		tcf_chain_put_by_act(goto_ch);
out_release:
	tcf_idr_release(*a, bind);
out_free_ex:
out_free_ex:
	kfree(nparms->tcfp_keys_ex);
	kfree(nparms->tcfp_keys_ex);
out_free:
out_free:
	kfree(nparms);
	kfree(nparms);
out_release:
	tcf_idr_release(*a, bind);
	return ret;
	return ret;
}
}


+9 −2
Original line number Original line Diff line number Diff line
@@ -55,8 +55,8 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
					  sample_policy, NULL);
					  sample_policy, NULL);
	if (ret < 0)
	if (ret < 0)
		return ret;
		return ret;
	if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] ||

	    !tb[TCA_SAMPLE_PSAMPLE_GROUP])
	if (!tb[TCA_SAMPLE_PARMS])
		return -EINVAL;
		return -EINVAL;


	parm = nla_data(tb[TCA_SAMPLE_PARMS]);
	parm = nla_data(tb[TCA_SAMPLE_PARMS]);
@@ -80,6 +80,13 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
		tcf_idr_release(*a, bind);
		tcf_idr_release(*a, bind);
		return -EEXIST;
		return -EEXIST;
	}
	}

	if (!tb[TCA_SAMPLE_RATE] || !tb[TCA_SAMPLE_PSAMPLE_GROUP]) {
		NL_SET_ERR_MSG(extack, "sample rate and group are required");
		err = -EINVAL;
		goto release_idr;
	}

	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
	if (err < 0)
	if (err < 0)
		goto release_idr;
		goto release_idr;