@@ -2169,7 +2169,7 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
struct nlattr *est,
struct fl_flow_tmplt *tmplt,
u32 flags, u32 fl_flags,
- struct netlink_ext_ack *extack)
+ bool *bound_to_filter, struct netlink_ext_ack *extack)
{
int err;
@@ -2185,18 +2185,20 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
tcf_bind_filter(tp, &f->res, base);
if (flags & TCA_ACT_FLAGS_NO_RTNL)
rtnl_unlock();
+ *bound_to_filter = true;
}
err = fl_set_key(net, tb, &f->key, &mask->key, extack);
if (err)
- return err;
+ goto unbind_filter;
fl_mask_update_range(mask);
fl_set_masked_key(&f->mkey, &f->key, mask);
if (!fl_mask_fits_tmplt(tmplt, mask)) {
NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template");
- return -EINVAL;
+ err = -EINVAL;
+ goto unbind_filter;
}
/* Enable tc skb extension if filter matches on data extracted from
@@ -2208,6 +2210,17 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp,
}
return 0;
+
+unbind_filter:
+ if (*bound_to_filter) {
+ if (flags & TCA_ACT_FLAGS_NO_RTNL)
+ rtnl_lock();
+ tcf_unbind_filter(tp, &f->res);
+ if (flags & TCA_ACT_FLAGS_NO_RTNL)
+ rtnl_unlock();
+ *bound_to_filter = false;
+ }
+ return err;
}
static int fl_ht_insert_unique(struct cls_fl_filter *fnew,
@@ -2241,6 +2254,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
struct cls_fl_head *head = fl_head_dereference(tp);
bool rtnl_held = !(flags & TCA_ACT_FLAGS_NO_RTNL);
struct cls_fl_filter *fold = *arg;
+ bool bound_to_filter = false;
struct cls_fl_filter *fnew;
struct fl_flow_mask *mask;
struct nlattr **tb;
@@ -2327,7 +2341,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE],
tp->chain->tmplt_priv, flags, fnew->flags,
- extack);
+ &bound_to_filter, extack);
if (err)
goto errout_idr;
@@ -2425,6 +2439,13 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
errout_mask:
fl_mask_put(head, fnew->mask);
errout_idr:
+ if (bound_to_filter) {
+ if (flags & TCA_ACT_FLAGS_NO_RTNL)
+ rtnl_lock();
+ tcf_unbind_filter(tp, &fnew->res);
+ if (flags & TCA_ACT_FLAGS_NO_RTNL)
+ rtnl_unlock();
+ }
if (!fold)
idr_remove(&head->handle_idr, fnew->handle);
__fl_put(fnew);