2018-06-25 17:49:43 +02:00
// SPDX-License-Identifier: GPL-2.0
2017-11-27 21:55:14 +01:00
# include <linux/kernel.h>
# include <linux/netfilter.h>
# include <linux/netfilter_ipv4.h>
# include <linux/netfilter_ipv6.h>
2017-11-27 22:50:26 +01:00
# include <net/netfilter/nf_queue.h>
2018-06-25 17:49:59 +02:00
# include <net/ip6_checksum.h>
2017-11-27 21:55:14 +01:00
2018-06-25 17:49:43 +02:00
# ifdef CONFIG_INET
__sum16 nf_ip_checksum ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , u8 protocol )
{
const struct iphdr * iph = ip_hdr ( skb ) ;
__sum16 csum = 0 ;
switch ( skb - > ip_summed ) {
case CHECKSUM_COMPLETE :
if ( hook ! = NF_INET_PRE_ROUTING & & hook ! = NF_INET_LOCAL_IN )
break ;
2019-06-24 11:17:38 +08:00
if ( ( protocol ! = IPPROTO_TCP & & protocol ! = IPPROTO_UDP & &
! csum_fold ( skb - > csum ) ) | |
2018-06-25 17:49:43 +02:00
! csum_tcpudp_magic ( iph - > saddr , iph - > daddr ,
skb - > len - dataoff , protocol ,
skb - > csum ) ) {
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
break ;
}
2020-07-08 15:09:39 -05:00
fallthrough ;
2018-06-25 17:49:43 +02:00
case CHECKSUM_NONE :
2019-06-24 11:17:38 +08:00
if ( protocol ! = IPPROTO_TCP & & protocol ! = IPPROTO_UDP )
2018-06-25 17:49:43 +02:00
skb - > csum = 0 ;
else
skb - > csum = csum_tcpudp_nofold ( iph - > saddr , iph - > daddr ,
skb - > len - dataoff ,
protocol , 0 ) ;
csum = __skb_checksum_complete ( skb ) ;
}
return csum ;
}
EXPORT_SYMBOL ( nf_ip_checksum ) ;
# endif
static __sum16 nf_ip_checksum_partial ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , unsigned int len ,
u8 protocol )
{
const struct iphdr * iph = ip_hdr ( skb ) ;
__sum16 csum = 0 ;
switch ( skb - > ip_summed ) {
case CHECKSUM_COMPLETE :
if ( len = = skb - > len - dataoff )
return nf_ip_checksum ( skb , hook , dataoff , protocol ) ;
2020-07-08 15:09:39 -05:00
fallthrough ;
2018-06-25 17:49:43 +02:00
case CHECKSUM_NONE :
skb - > csum = csum_tcpudp_nofold ( iph - > saddr , iph - > daddr , protocol ,
skb - > len - dataoff , 0 ) ;
skb - > ip_summed = CHECKSUM_NONE ;
return __skb_checksum_complete_head ( skb , dataoff + len ) ;
}
return csum ;
}
2018-06-25 17:49:59 +02:00
__sum16 nf_ip6_checksum ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , u8 protocol )
{
const struct ipv6hdr * ip6h = ipv6_hdr ( skb ) ;
__sum16 csum = 0 ;
switch ( skb - > ip_summed ) {
case CHECKSUM_COMPLETE :
if ( hook ! = NF_INET_PRE_ROUTING & & hook ! = NF_INET_LOCAL_IN )
break ;
if ( ! csum_ipv6_magic ( & ip6h - > saddr , & ip6h - > daddr ,
skb - > len - dataoff , protocol ,
csum_sub ( skb - > csum ,
skb_checksum ( skb , 0 ,
dataoff , 0 ) ) ) ) {
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
break ;
}
2020-07-08 15:09:39 -05:00
fallthrough ;
2018-06-25 17:49:59 +02:00
case CHECKSUM_NONE :
skb - > csum = ~ csum_unfold (
csum_ipv6_magic ( & ip6h - > saddr , & ip6h - > daddr ,
skb - > len - dataoff ,
protocol ,
csum_sub ( 0 ,
skb_checksum ( skb , 0 ,
dataoff , 0 ) ) ) ) ;
csum = __skb_checksum_complete ( skb ) ;
}
return csum ;
}
EXPORT_SYMBOL ( nf_ip6_checksum ) ;
static __sum16 nf_ip6_checksum_partial ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , unsigned int len ,
u8 protocol )
{
const struct ipv6hdr * ip6h = ipv6_hdr ( skb ) ;
__wsum hsum ;
__sum16 csum = 0 ;
switch ( skb - > ip_summed ) {
case CHECKSUM_COMPLETE :
if ( len = = skb - > len - dataoff )
return nf_ip6_checksum ( skb , hook , dataoff , protocol ) ;
2020-07-08 15:09:39 -05:00
fallthrough ;
2018-06-25 17:49:59 +02:00
case CHECKSUM_NONE :
hsum = skb_checksum ( skb , 0 , dataoff , 0 ) ;
skb - > csum = ~ csum_unfold ( csum_ipv6_magic ( & ip6h - > saddr ,
& ip6h - > daddr ,
skb - > len - dataoff ,
protocol ,
csum_sub ( 0 , hsum ) ) ) ;
skb - > ip_summed = CHECKSUM_NONE ;
return __skb_checksum_complete_head ( skb , dataoff + len ) ;
}
return csum ;
} ;
2017-11-27 21:55:14 +01:00
__sum16 nf_checksum ( struct sk_buff * skb , unsigned int hook ,
2018-06-25 17:49:59 +02:00
unsigned int dataoff , u8 protocol ,
2017-11-27 21:55:14 +01:00
unsigned short family )
{
__sum16 csum = 0 ;
switch ( family ) {
case AF_INET :
csum = nf_ip_checksum ( skb , hook , dataoff , protocol ) ;
break ;
case AF_INET6 :
2018-06-25 17:49:59 +02:00
csum = nf_ip6_checksum ( skb , hook , dataoff , protocol ) ;
2017-11-27 21:55:14 +01:00
break ;
}
return csum ;
}
EXPORT_SYMBOL_GPL ( nf_checksum ) ;
2017-12-20 16:04:18 +01:00
__sum16 nf_checksum_partial ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , unsigned int len ,
2018-06-25 17:49:59 +02:00
u8 protocol , unsigned short family )
2017-12-20 16:04:18 +01:00
{
__sum16 csum = 0 ;
switch ( family ) {
case AF_INET :
csum = nf_ip_checksum_partial ( skb , hook , dataoff , len ,
protocol ) ;
break ;
case AF_INET6 :
2018-06-25 17:49:59 +02:00
csum = nf_ip6_checksum_partial ( skb , hook , dataoff , len ,
protocol ) ;
2017-12-20 16:04:18 +01:00
break ;
}
return csum ;
}
EXPORT_SYMBOL_GPL ( nf_checksum_partial ) ;
2017-11-27 22:29:52 +01:00
int nf_route ( struct net * net , struct dst_entry * * dst , struct flowi * fl ,
bool strict , unsigned short family )
{
2019-02-02 10:17:00 +01:00
const struct nf_ipv6_ops * v6ops __maybe_unused ;
2017-11-27 22:29:52 +01:00
int ret = 0 ;
switch ( family ) {
case AF_INET :
ret = nf_ip_route ( net , dst , fl , strict ) ;
break ;
case AF_INET6 :
2019-02-02 10:17:00 +01:00
ret = nf_ip6_route ( net , dst , fl , strict ) ;
2017-11-27 22:29:52 +01:00
break ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( nf_route ) ;
2017-11-27 22:50:26 +01:00
2023-03-07 16:31:30 -05:00
/* Only get and check the lengths, not do any hop-by-hop stuff. */
int nf_ip6_check_hbh_len ( struct sk_buff * skb , u32 * plen )
{
int len , off = sizeof ( struct ipv6hdr ) ;
unsigned char * nh ;
if ( ! pskb_may_pull ( skb , off + 8 ) )
return - ENOMEM ;
nh = ( unsigned char * ) ( ipv6_hdr ( skb ) + 1 ) ;
len = ( nh [ 1 ] + 1 ) < < 3 ;
if ( ! pskb_may_pull ( skb , off + len ) )
return - ENOMEM ;
nh = skb_network_header ( skb ) ;
off + = 2 ;
len - = 2 ;
while ( len > 0 ) {
int optlen ;
if ( nh [ off ] = = IPV6_TLV_PAD1 ) {
off + + ;
len - - ;
continue ;
}
if ( len < 2 )
return - EBADMSG ;
optlen = nh [ off + 1 ] + 2 ;
if ( optlen > len )
return - EBADMSG ;
if ( nh [ off ] = = IPV6_TLV_JUMBO ) {
u32 pkt_len ;
if ( nh [ off + 1 ] ! = 4 | | ( off & 3 ) ! = 2 )
return - EBADMSG ;
pkt_len = ntohl ( * ( __be32 * ) ( nh + off + 2 ) ) ;
if ( pkt_len < = IPV6_MAXPLEN | |
ipv6_hdr ( skb ) - > payload_len )
return - EBADMSG ;
if ( pkt_len > skb - > len - sizeof ( struct ipv6hdr ) )
return - EBADMSG ;
* plen = pkt_len ;
}
off + = optlen ;
len - = optlen ;
}
return len ? - EBADMSG : 0 ;
}
EXPORT_SYMBOL_GPL ( nf_ip6_check_hbh_len ) ;