Commit 3c4bb45a authored by Paolo Abeni's avatar Paolo Abeni

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.deSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 6feb37b3 93d75d47
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/tc_act/tc_ipt.h> #include <linux/tc_act/tc_ipt.h>
#include <net/tc_act/tc_ipt.h> #include <net/tc_act/tc_ipt.h>
#include <net/tc_wrapper.h> #include <net/tc_wrapper.h>
#include <net/ip.h>
#include <linux/netfilter_ipv4/ip_tables.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, ...@@ -48,7 +49,7 @@ static int ipt_init_target(struct net *net, struct xt_entry_target *t,
par.entryinfo = &e; par.entryinfo = &e;
par.target = target; par.target = target;
par.targinfo = t->data; par.targinfo = t->data;
par.hook_mask = hook; par.hook_mask = 1 << hook;
par.family = NFPROTO_IPV4; par.family = NFPROTO_IPV4;
ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); 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) ...@@ -85,7 +86,8 @@ static void tcf_ipt_release(struct tc_action *a)
static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
[TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ }, [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_INDEX] = { .type = NLA_U32 },
[TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) }, [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, ...@@ -158,15 +160,27 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
return -EEXIST; return -EEXIST;
} }
} }
err = -EINVAL;
hook = nla_get_u32(tb[TCA_IPT_HOOK]); 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;
}
err = -ENOMEM; tname = kstrdup("mangle", GFP_KERNEL);
tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
if (unlikely(!tname)) if (unlikely(!tname))
goto err1; 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); t = kmemdup(td, td->u.target_size, GFP_KERNEL);
if (unlikely(!t)) if (unlikely(!t))
...@@ -217,10 +231,31 @@ static int tcf_xt_init(struct net *net, struct nlattr *nla, ...@@ -217,10 +231,31 @@ static int tcf_xt_init(struct net *net, struct nlattr *nla,
a, &act_xt_ops, tp, flags); 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, TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
const struct tc_action *a, const struct tc_action *a,
struct tcf_result *res) struct tcf_result *res)
{ {
char saved_cb[sizeof_field(struct sk_buff, cb)];
int ret = 0, result = 0; int ret = 0, result = 0;
struct tcf_ipt *ipt = to_ipt(a); struct tcf_ipt *ipt = to_ipt(a);
struct xt_action_param par; struct xt_action_param par;
...@@ -231,9 +266,24 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb, ...@@ -231,9 +266,24 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
}; };
if (skb_protocol(skb, false) != htons(ETH_P_IP))
return TC_ACT_UNSPEC;
if (skb_unclone(skb, GFP_ATOMIC)) if (skb_unclone(skb, GFP_ATOMIC))
return TC_ACT_UNSPEC; 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); spin_lock(&ipt->tcf_lock);
tcf_lastuse_update(&ipt->tcf_tm); tcf_lastuse_update(&ipt->tcf_tm);
...@@ -246,6 +296,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb, ...@@ -246,6 +296,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
par.state = &state; par.state = &state;
par.target = ipt->tcfi_t->u.kernel.target; par.target = ipt->tcfi_t->u.kernel.target;
par.targinfo = ipt->tcfi_t->data; par.targinfo = ipt->tcfi_t->data;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
ret = par.target->target(skb, &par); ret = par.target->target(skb, &par);
switch (ret) { switch (ret) {
...@@ -266,6 +319,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb, ...@@ -266,6 +319,9 @@ TC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
break; break;
} }
spin_unlock(&ipt->tcf_lock); spin_unlock(&ipt->tcf_lock);
memcpy(skb->cb, saved_cb, sizeof(skb->cb));
return result; return result;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment