From: Florian Westphal fw@strlen.de
[ Upstream commit b2dc32dcba08bf55cec600caa76f4afd2e3614df ]
Netfilter targets make assumptions on the skb state, for example iphdr is supposed to be in the linear area.
This is normally done by IP stack, but in act_ipt case no such checks are made.
Some targets can even assume that skb_dst will be valid. Make a minimum effort to check for this:
- Don't call the targets eval function for non-ipv4 skbs. - Don't call the targets eval function for POSTROUTING emulation when the skb has no dst set.
v3: use skb_protocol helper (Davide Caratti)
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Florian Westphal fw@strlen.de Reviewed-by: Simon Horman simon.horman@corigine.com Acked-by: Jamal Hadi Salim jhs@mojatatu.com Signed-off-by: Paolo Abeni pabeni@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org --- net/sched/act_ipt.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
--- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -229,6 +229,26 @@ static int tcf_xt_init(struct net *net, 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); +} + static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { @@ -242,9 +262,22 @@ static int tcf_ipt_act(struct sk_buff *s .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; + } + spin_lock(&ipt->tcf_lock);
tcf_lastuse_update(&ipt->tcf_tm);