Merge branch 'net-sched-act_ipt-bug-fixes'

Florian Westphal says:

====================
net/sched: act_ipt bug fixes

v3: prefer skb_header() helper in patch 2.  No other changes.
I've retained Acks and RvB-Tags of v2.

While checking if netfilter could be updated to replace selected
instances of NF_DROP with kfree_skb_reason+NF_STOLEN to improve
debugging info via drop monitor I found that act_ipt is incompatible
with such an approach.  Moreover, it lacks multiple sanity checks
to avoid certain code paths that make assumptions that the tc layer
doesn't meet, such as header sanity checks, availability of skb_dst,
skb_nfct() and so on.

act_ipt test in the tc selftest still pass with this applied.

I think that we should consider removal of this module, while
this should take care of all problems, its ipv4 only and I don't
think there are any netfilter targets that lack a native tc
equivalent, even when ignoring bpf.
====================

Link: https://lore.kernel.org/r/20230627123813.3036-1-fw@strlen.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2023-06-29 12:10:39 +02:00
commit 3c4bb45ab2
1 changed files with 64 additions and 8 deletions

View File

@ -21,6 +21,7 @@
#include <linux/tc_act/tc_ipt.h>
#include <net/tc_act/tc_ipt.h>
#include <net/tc_wrapper.h>
#include <net/ip.h>
#include <linux/netfilter_ipv4/ip_tables.h>
@ -48,7 +49,7 @@ static int ipt_init_target(struct net *net, struct xt_entry_target *t,
par.entryinfo = &e;
par.target = target;
par.targinfo = t->data;
par.hook_mask = hook;
par.hook_mask = 1 << hook;
par.family = NFPROTO_IPV4;
ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false);
@ -85,7 +86,8 @@ static void tcf_ipt_release(struct tc_action *a)
static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
[TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ },
[TCA_IPT_HOOK] = { .type = NLA_U32 },
[TCA_IPT_HOOK] = NLA_POLICY_RANGE(NLA_U32, NF_INET_PRE_ROUTING,
NF_INET_NUMHOOKS),
[TCA_IPT_INDEX] = { .type = NLA_U32 },
[TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) },
};
@ -158,15 +160,27 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
return -EEXIST;
}
}
hook = nla_get_u32(tb[TCA_IPT_HOOK]);
err = -ENOMEM;
tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
err = -EINVAL;
hook = nla_get_u32(tb[TCA_IPT_HOOK]);
switch (hook) {
case NF_INET_PRE_ROUTING:
break;
case NF_INET_POST_ROUTING:
break;
default:
goto err1;
}
if (tb[TCA_IPT_TABLE]) {
/* mangle only for now */
if (nla_strcmp(tb[TCA_IPT_TABLE], "mangle"))
goto err1;
}
tname = kstrdup("mangle", GFP_KERNEL);
if (unlikely(!tname))
goto err1;
if (tb[TCA_IPT_TABLE] == NULL ||
nla_strscpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ)
strcpy(tname, "mangle");
t = kmemdup(td, td->u.target_size, GFP_KERNEL);
if (unlikely(!t))
@ -217,10 +231,31 @@ static int tcf_xt_init(struct net *net, struct nlattr *nla,
a, &act_xt_ops, tp, flags);
}
static bool tcf_ipt_act_check(struct sk_buff *skb)
{
const struct iphdr *iph;
unsigned int nhoff, len;
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
return false;
nhoff = skb_network_offset(skb);
iph = ip_hdr(skb);
if (iph->ihl < 5 || iph->version != 4)
return false;
len = skb_ip_totlen(skb);
if (skb->len < nhoff + len || len < (iph->ihl * 4u))
return false;
return pskb_may_pull(skb, iph->ihl * 4u);
}
TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
const struct tc_action *a,
struct tcf_result *res)
{
char saved_cb[sizeof_field(struct sk_buff, cb)];
int ret = 0, result = 0;
struct tcf_ipt *ipt = to_ipt(a);
struct xt_action_param par;
@ -231,9 +266,24 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
.pf = NFPROTO_IPV4,
};
if (skb_protocol(skb, false) != htons(ETH_P_IP))
return TC_ACT_UNSPEC;
if (skb_unclone(skb, GFP_ATOMIC))
return TC_ACT_UNSPEC;
if (!tcf_ipt_act_check(skb))
return TC_ACT_UNSPEC;
if (state.hook == NF_INET_POST_ROUTING) {
if (!skb_dst(skb))
return TC_ACT_UNSPEC;
state.out = skb->dev;
}
memcpy(saved_cb, skb->cb, sizeof(saved_cb));
spin_lock(&ipt->tcf_lock);
tcf_lastuse_update(&ipt->tcf_tm);
@ -246,6 +296,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
par.state = &state;
par.target = ipt->tcfi_t->u.kernel.target;
par.targinfo = ipt->tcfi_t->data;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
ret = par.target->target(skb, &par);
switch (ret) {
@ -266,6 +319,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
break;
}
spin_unlock(&ipt->tcf_lock);
memcpy(skb->cb, saved_cb, sizeof(skb->cb));
return result;
}