Skip to content

Commit 369609f

Browse files
tohojoPaolo Abeni
authored andcommitted
tc: Ensure we have enough buffer space when sending filter netlink notifications
The tfilter_notify() and tfilter_del_notify() functions assume that NLMSG_GOODSIZE is always enough to dump the filter chain. This is not always the case, which can lead to silent notify failures (because the return code of tfilter_notify() is not always checked). In particular, this can lead to NLM_F_ECHO not being honoured even though an action succeeds, which forces userspace to create workarounds[0]. Fix this by increasing the message size if dumping the filter chain into the allocated skb fails. Use the size of the incoming skb as a size hint if set, so we can start at a larger value when appropriate. To trigger this, run the following commands: # ip link add type veth # tc qdisc replace dev veth0 root handle 1: fq_codel # tc -echo filter add dev veth0 parent 1: u32 match u32 0 0 $(for i in $(seq 32); do echo action pedit munge ip dport set 22; done) Before this fix, tc just returns: Not a filter(cmd 2) After the fix, we get the correct echo: added filter dev veth0 parent 1: protocol all pref 49152 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 terminal flowid not_in_hw match 00000000/00000000 at 0 action order 1: pedit action pass keys 1 index 1 ref 1 bind 1 key #0 at 20: val 00000016 mask ffff0000 [repeated 32 times] [0] openvswitch/ovs@106ef21 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Frode Nordahl <frode.nordahl@canonical.com> Closes: https://bugs.launchpad.net/ubuntu/+source/openvswitch/+bug/2018500 Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com> Reviewed-by: Jiri Pirko <jiri@nvidia.com> Link: https://patch.msgid.link/20250407105542.16601-1-toke@redhat.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 13e7d72 commit 369609f

File tree

1 file changed

+45
-21
lines changed

1 file changed

+45
-21
lines changed

net/sched/cls_api.c

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,7 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
20572057
struct tcmsg *tcm;
20582058
struct nlmsghdr *nlh;
20592059
unsigned char *b = skb_tail_pointer(skb);
2060+
int ret = -EMSGSIZE;
20602061

20612062
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
20622063
if (!nlh)
@@ -2101,11 +2102,45 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
21012102

21022103
return skb->len;
21032104

2105+
cls_op_not_supp:
2106+
ret = -EOPNOTSUPP;
21042107
out_nlmsg_trim:
21052108
nla_put_failure:
2106-
cls_op_not_supp:
21072109
nlmsg_trim(skb, b);
2108-
return -1;
2110+
return ret;
2111+
}
2112+
2113+
static struct sk_buff *tfilter_notify_prep(struct net *net,
2114+
struct sk_buff *oskb,
2115+
struct nlmsghdr *n,
2116+
struct tcf_proto *tp,
2117+
struct tcf_block *block,
2118+
struct Qdisc *q, u32 parent,
2119+
void *fh, int event,
2120+
u32 portid, bool rtnl_held,
2121+
struct netlink_ext_ack *extack)
2122+
{
2123+
unsigned int size = oskb ? max(NLMSG_GOODSIZE, oskb->len) : NLMSG_GOODSIZE;
2124+
struct sk_buff *skb;
2125+
int ret;
2126+
2127+
retry:
2128+
skb = alloc_skb(size, GFP_KERNEL);
2129+
if (!skb)
2130+
return ERR_PTR(-ENOBUFS);
2131+
2132+
ret = tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
2133+
n->nlmsg_seq, n->nlmsg_flags, event, false,
2134+
rtnl_held, extack);
2135+
if (ret <= 0) {
2136+
kfree_skb(skb);
2137+
if (ret == -EMSGSIZE) {
2138+
size += NLMSG_GOODSIZE;
2139+
goto retry;
2140+
}
2141+
return ERR_PTR(-EINVAL);
2142+
}
2143+
return skb;
21092144
}
21102145

21112146
static int tfilter_notify(struct net *net, struct sk_buff *oskb,
@@ -2121,16 +2156,10 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
21212156
if (!unicast && !rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
21222157
return 0;
21232158

2124-
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2125-
if (!skb)
2126-
return -ENOBUFS;
2127-
2128-
if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
2129-
n->nlmsg_seq, n->nlmsg_flags, event,
2130-
false, rtnl_held, extack) <= 0) {
2131-
kfree_skb(skb);
2132-
return -EINVAL;
2133-
}
2159+
skb = tfilter_notify_prep(net, oskb, n, tp, block, q, parent, fh, event,
2160+
portid, rtnl_held, extack);
2161+
if (IS_ERR(skb))
2162+
return PTR_ERR(skb);
21342163

21352164
if (unicast)
21362165
err = rtnl_unicast(skb, net, portid);
@@ -2153,16 +2182,11 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
21532182
if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
21542183
return tp->ops->delete(tp, fh, last, rtnl_held, extack);
21552184

2156-
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2157-
if (!skb)
2158-
return -ENOBUFS;
2159-
2160-
if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
2161-
n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
2162-
false, rtnl_held, extack) <= 0) {
2185+
skb = tfilter_notify_prep(net, oskb, n, tp, block, q, parent, fh,
2186+
RTM_DELTFILTER, portid, rtnl_held, extack);
2187+
if (IS_ERR(skb)) {
21632188
NL_SET_ERR_MSG(extack, "Failed to build del event notification");
2164-
kfree_skb(skb);
2165-
return -EINVAL;
2189+
return PTR_ERR(skb);
21662190
}
21672191

21682192
err = tp->ops->delete(tp, fh, last, rtnl_held, extack);

0 commit comments

Comments
 (0)