Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f7ef2f8 authored by Cong Wang's avatar Cong Wang Committed by David S. Miller
Browse files

sched, cls: check if we could overwrite actions when changing a filter



When actions are attached to a filter, they are a part of the filter
itself, so when changing a filter we should allow to overwrite the actions
inside as well.

In my specific case, when I tried to _append_ a new action to an existing
filter which already has an action, I got EEXIST since kernel refused
to overwrite the existing one in kernel.

This patch checks if we are changing the filter checking NLM_F_CREATE flag
(Sigh, filters don't use NLM_F_REPLACE...) and then passes the boolean down
to actions. This fixes the problem above.

Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: default avatarCong Wang <cwang@twopensource.com>
Signed-off-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4940b8cd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,

int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
		      struct nlattr **tb, struct nlattr *rate_tlv,
		      struct tcf_exts *exts);
		      struct tcf_exts *exts, bool ovr);
void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts);
void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
		     struct tcf_exts *src);
+1 −1
Original line number Diff line number Diff line
@@ -199,7 +199,7 @@ struct tcf_proto_ops {
	int			(*change)(struct net *net, struct sk_buff *,
					struct tcf_proto*, unsigned long,
					u32 handle, struct nlattr **,
					unsigned long *);
					unsigned long *, bool);
	int			(*delete)(struct tcf_proto*, unsigned long);
	void			(*walk)(struct tcf_proto*, struct tcf_walker *arg);

+5 −4
Original line number Diff line number Diff line
@@ -317,7 +317,8 @@ replay:
		}
	}

	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh);
	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
			      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
	if (err == 0) {
		if (tp_created) {
			spin_lock_bh(root_lock);
@@ -504,7 +505,7 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
EXPORT_SYMBOL(tcf_exts_destroy);

int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
		  struct nlattr *rate_tlv, struct tcf_exts *exts)
		  struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
{
#ifdef CONFIG_NET_CLS_ACT
	{
@@ -513,7 +514,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
		INIT_LIST_HEAD(&exts->actions);
		if (exts->police && tb[exts->police]) {
			act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
						"police", TCA_ACT_NOREPLACE,
						"police", ovr,
						TCA_ACT_BIND);
			if (IS_ERR(act))
				return PTR_ERR(act);
@@ -523,7 +524,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
		} else if (exts->action && tb[exts->action]) {
			int err;
			err = tcf_action_init(net, tb[exts->action], rate_tlv,
					      NULL, TCA_ACT_NOREPLACE,
					      NULL, ovr,
					      TCA_ACT_BIND, &exts->actions);
			if (err)
				return err;
+5 −5
Original line number Diff line number Diff line
@@ -130,14 +130,14 @@ static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
static int basic_set_parms(struct net *net, struct tcf_proto *tp,
			   struct basic_filter *f, unsigned long base,
			   struct nlattr **tb,
			   struct nlattr *est)
			   struct nlattr *est, bool ovr)
{
	int err;
	struct tcf_exts e;
	struct tcf_ematch_tree t;

	tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	err = tcf_exts_validate(net, tp, tb, est, &e);
	err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
	if (err < 0)
		return err;

@@ -161,7 +161,7 @@ errout:

static int basic_change(struct net *net, struct sk_buff *in_skb,
			struct tcf_proto *tp, unsigned long base, u32 handle,
			struct nlattr **tca, unsigned long *arg)
			struct nlattr **tca, unsigned long *arg, bool ovr)
{
	int err;
	struct basic_head *head = tp->root;
@@ -179,7 +179,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
	if (f != NULL) {
		if (handle && f->handle != handle)
			return -EINVAL;
		return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
		return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
	}

	err = -ENOBUFS;
@@ -206,7 +206,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
		f->handle = head->hgenerator;
	}

	err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
	err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
	if (err < 0)
		goto errout;

+5 −5
Original line number Diff line number Diff line
@@ -156,7 +156,7 @@ static void cls_bpf_put(struct tcf_proto *tp, unsigned long f)
static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
				   struct cls_bpf_prog *prog,
				   unsigned long base, struct nlattr **tb,
				   struct nlattr *est)
				   struct nlattr *est, bool ovr)
{
	struct sock_filter *bpf_ops, *bpf_old;
	struct tcf_exts exts;
@@ -170,7 +170,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
		return -EINVAL;

	tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
	ret = tcf_exts_validate(net, tp, tb, est, &exts);
	ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
	if (ret < 0)
		return ret;

@@ -242,7 +242,7 @@ static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
			  struct tcf_proto *tp, unsigned long base,
			  u32 handle, struct nlattr **tca,
			  unsigned long *arg)
			  unsigned long *arg, bool ovr)
{
	struct cls_bpf_head *head = tp->root;
	struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg;
@@ -260,7 +260,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
		if (handle && prog->handle != handle)
			return -EINVAL;
		return cls_bpf_modify_existing(net, tp, prog, base, tb,
					       tca[TCA_RATE]);
					       tca[TCA_RATE], ovr);
	}

	prog = kzalloc(sizeof(*prog), GFP_KERNEL);
@@ -277,7 +277,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
		goto errout;
	}

	ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]);
	ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE], ovr);
	if (ret < 0)
		goto errout;

Loading