mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 00:10:09 +00:00
a13fbf5ed5
There are also quite some places in netfilter that may process IPv4 TCP GSO packets, we need to replace them too. In length_mt(), we have to use u_int32_t/int to accept skb_ip_totlen() return value, otherwise it may overflow and mismatch. This change will also help us add selftest for IPv4 BIG TCP in the following patch. Note that we don't need to replace the one in tcpmss_tg4(), as it will return if there is data after tcphdr in tcpmss_mangle_packet(). The same in mangle_contents() in nf_nat_helper.c, it returns false when skb->len + extra > 65535 in enlarge_skb(). Signed-off-by: Xin Long <lucien.xin@gmail.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
90 lines
1.9 KiB
C
90 lines
1.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _NF_TABLES_IPV4_H_
|
|
#define _NF_TABLES_IPV4_H_
|
|
|
|
#include <net/netfilter/nf_tables.h>
|
|
#include <net/ip.h>
|
|
|
|
static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt)
|
|
{
|
|
struct iphdr *ip;
|
|
|
|
ip = ip_hdr(pkt->skb);
|
|
pkt->flags = NFT_PKTINFO_L4PROTO;
|
|
pkt->tprot = ip->protocol;
|
|
pkt->thoff = ip_hdrlen(pkt->skb);
|
|
pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET;
|
|
}
|
|
|
|
static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
|
|
{
|
|
struct iphdr *iph, _iph;
|
|
u32 len, thoff;
|
|
|
|
iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
|
|
sizeof(*iph), &_iph);
|
|
if (!iph)
|
|
return -1;
|
|
|
|
if (iph->ihl < 5 || iph->version != 4)
|
|
return -1;
|
|
|
|
len = iph_totlen(pkt->skb, iph);
|
|
thoff = iph->ihl * 4;
|
|
if (pkt->skb->len < len)
|
|
return -1;
|
|
else if (len < thoff)
|
|
return -1;
|
|
else if (thoff < sizeof(*iph))
|
|
return -1;
|
|
|
|
pkt->flags = NFT_PKTINFO_L4PROTO;
|
|
pkt->tprot = iph->protocol;
|
|
pkt->thoff = thoff;
|
|
pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
|
|
{
|
|
if (__nft_set_pktinfo_ipv4_validate(pkt) < 0)
|
|
nft_set_pktinfo_unspec(pkt);
|
|
}
|
|
|
|
static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
|
|
{
|
|
struct iphdr *iph;
|
|
u32 len, thoff;
|
|
|
|
if (!pskb_may_pull(pkt->skb, sizeof(*iph)))
|
|
return -1;
|
|
|
|
iph = ip_hdr(pkt->skb);
|
|
if (iph->ihl < 5 || iph->version != 4)
|
|
goto inhdr_error;
|
|
|
|
len = iph_totlen(pkt->skb, iph);
|
|
thoff = iph->ihl * 4;
|
|
if (pkt->skb->len < len) {
|
|
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
return -1;
|
|
} else if (len < thoff) {
|
|
goto inhdr_error;
|
|
} else if (thoff < sizeof(*iph)) {
|
|
return -1;
|
|
}
|
|
|
|
pkt->flags = NFT_PKTINFO_L4PROTO;
|
|
pkt->tprot = iph->protocol;
|
|
pkt->thoff = thoff;
|
|
pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
|
|
|
|
return 0;
|
|
|
|
inhdr_error:
|
|
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INHDRERRORS);
|
|
return -1;
|
|
}
|
|
|
|
#endif
|