1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/kernel.h>
3 #include <linux/netfilter.h>
4 #include <linux/netfilter_ipv4.h>
5 #include <linux/netfilter_ipv6.h>
6 #include <net/netfilter/nf_queue.h>
7 #include <net/ip6_checksum.h>
10 __sum16
nf_ip_checksum(struct sk_buff
*skb
, unsigned int hook
,
11 unsigned int dataoff
, u8 protocol
)
13 const struct iphdr
*iph
= ip_hdr(skb
);
16 switch (skb
->ip_summed
) {
17 case CHECKSUM_COMPLETE
:
18 if (hook
!= NF_INET_PRE_ROUTING
&& hook
!= NF_INET_LOCAL_IN
)
20 if ((protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
&&
21 !csum_fold(skb
->csum
)) ||
22 !csum_tcpudp_magic(iph
->saddr
, iph
->daddr
,
23 skb
->len
- dataoff
, protocol
,
25 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
30 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
)
33 skb
->csum
= csum_tcpudp_nofold(iph
->saddr
, iph
->daddr
,
36 csum
= __skb_checksum_complete(skb
);
40 EXPORT_SYMBOL(nf_ip_checksum
);
43 static __sum16
nf_ip_checksum_partial(struct sk_buff
*skb
, unsigned int hook
,
44 unsigned int dataoff
, unsigned int len
,
47 const struct iphdr
*iph
= ip_hdr(skb
);
50 switch (skb
->ip_summed
) {
51 case CHECKSUM_COMPLETE
:
52 if (len
== skb
->len
- dataoff
)
53 return nf_ip_checksum(skb
, hook
, dataoff
, protocol
);
56 skb
->csum
= csum_tcpudp_nofold(iph
->saddr
, iph
->daddr
, protocol
,
57 skb
->len
- dataoff
, 0);
58 skb
->ip_summed
= CHECKSUM_NONE
;
59 return __skb_checksum_complete_head(skb
, dataoff
+ len
);
64 __sum16
nf_ip6_checksum(struct sk_buff
*skb
, unsigned int hook
,
65 unsigned int dataoff
, u8 protocol
)
67 const struct ipv6hdr
*ip6h
= ipv6_hdr(skb
);
70 switch (skb
->ip_summed
) {
71 case CHECKSUM_COMPLETE
:
72 if (hook
!= NF_INET_PRE_ROUTING
&& hook
!= NF_INET_LOCAL_IN
)
74 if (!csum_ipv6_magic(&ip6h
->saddr
, &ip6h
->daddr
,
75 skb
->len
- dataoff
, protocol
,
79 skb
->ip_summed
= CHECKSUM_UNNECESSARY
;
84 skb
->csum
= ~csum_unfold(
85 csum_ipv6_magic(&ip6h
->saddr
, &ip6h
->daddr
,
91 csum
= __skb_checksum_complete(skb
);
95 EXPORT_SYMBOL(nf_ip6_checksum
);
97 static __sum16
nf_ip6_checksum_partial(struct sk_buff
*skb
, unsigned int hook
,
98 unsigned int dataoff
, unsigned int len
,
101 const struct ipv6hdr
*ip6h
= ipv6_hdr(skb
);
105 switch (skb
->ip_summed
) {
106 case CHECKSUM_COMPLETE
:
107 if (len
== skb
->len
- dataoff
)
108 return nf_ip6_checksum(skb
, hook
, dataoff
, protocol
);
111 hsum
= skb_checksum(skb
, 0, dataoff
, 0);
112 skb
->csum
= ~csum_unfold(csum_ipv6_magic(&ip6h
->saddr
,
117 skb
->ip_summed
= CHECKSUM_NONE
;
118 return __skb_checksum_complete_head(skb
, dataoff
+ len
);
123 __sum16
nf_checksum(struct sk_buff
*skb
, unsigned int hook
,
124 unsigned int dataoff
, u8 protocol
,
125 unsigned short family
)
131 csum
= nf_ip_checksum(skb
, hook
, dataoff
, protocol
);
134 csum
= nf_ip6_checksum(skb
, hook
, dataoff
, protocol
);
140 EXPORT_SYMBOL_GPL(nf_checksum
);
142 __sum16
nf_checksum_partial(struct sk_buff
*skb
, unsigned int hook
,
143 unsigned int dataoff
, unsigned int len
,
144 u8 protocol
, unsigned short family
)
150 csum
= nf_ip_checksum_partial(skb
, hook
, dataoff
, len
,
154 csum
= nf_ip6_checksum_partial(skb
, hook
, dataoff
, len
,
161 EXPORT_SYMBOL_GPL(nf_checksum_partial
);
163 int nf_route(struct net
*net
, struct dst_entry
**dst
, struct flowi
*fl
,
164 bool strict
, unsigned short family
)
166 const struct nf_ipv6_ops
*v6ops __maybe_unused
;
171 ret
= nf_ip_route(net
, dst
, fl
, strict
);
174 ret
= nf_ip6_route(net
, dst
, fl
, strict
);
180 EXPORT_SYMBOL_GPL(nf_route
);
182 /* Only get and check the lengths, not do any hop-by-hop stuff. */
183 int nf_ip6_check_hbh_len(struct sk_buff
*skb
, u32
*plen
)
185 int len
, off
= sizeof(struct ipv6hdr
);
188 if (!pskb_may_pull(skb
, off
+ 8))
190 nh
= (unsigned char *)(ipv6_hdr(skb
) + 1);
191 len
= (nh
[1] + 1) << 3;
193 if (!pskb_may_pull(skb
, off
+ len
))
195 nh
= skb_network_header(skb
);
202 if (nh
[off
] == IPV6_TLV_PAD1
) {
209 optlen
= nh
[off
+ 1] + 2;
213 if (nh
[off
] == IPV6_TLV_JUMBO
) {
216 if (nh
[off
+ 1] != 4 || (off
& 3) != 2)
218 pkt_len
= ntohl(*(__be32
*)(nh
+ off
+ 2));
219 if (pkt_len
<= IPV6_MAXPLEN
||
220 ipv6_hdr(skb
)->payload_len
)
222 if (pkt_len
> skb
->len
- sizeof(struct ipv6hdr
))
230 return len
? -EBADMSG
: 0;
232 EXPORT_SYMBOL_GPL(nf_ip6_check_hbh_len
);