netfilter: nf_tables: fix use-after-free when deleting compat expressions
[linux/fpc-iii.git] / net / netfilter / utils.c
blobe8da9a9bba73f04bbd016a4c26d9960fe5fc3c26
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>
9 #ifdef CONFIG_INET
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);
14 __sum16 csum = 0;
16 switch (skb->ip_summed) {
17 case CHECKSUM_COMPLETE:
18 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
19 break;
20 if ((protocol == 0 && !csum_fold(skb->csum)) ||
21 !csum_tcpudp_magic(iph->saddr, iph->daddr,
22 skb->len - dataoff, protocol,
23 skb->csum)) {
24 skb->ip_summed = CHECKSUM_UNNECESSARY;
25 break;
27 /* fall through */
28 case CHECKSUM_NONE:
29 if (protocol == 0)
30 skb->csum = 0;
31 else
32 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
33 skb->len - dataoff,
34 protocol, 0);
35 csum = __skb_checksum_complete(skb);
37 return csum;
39 EXPORT_SYMBOL(nf_ip_checksum);
40 #endif
42 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
43 unsigned int dataoff, unsigned int len,
44 u8 protocol)
46 const struct iphdr *iph = ip_hdr(skb);
47 __sum16 csum = 0;
49 switch (skb->ip_summed) {
50 case CHECKSUM_COMPLETE:
51 if (len == skb->len - dataoff)
52 return nf_ip_checksum(skb, hook, dataoff, protocol);
53 /* fall through */
54 case CHECKSUM_NONE:
55 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol,
56 skb->len - dataoff, 0);
57 skb->ip_summed = CHECKSUM_NONE;
58 return __skb_checksum_complete_head(skb, dataoff + len);
60 return csum;
63 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
64 unsigned int dataoff, u8 protocol)
66 const struct ipv6hdr *ip6h = ipv6_hdr(skb);
67 __sum16 csum = 0;
69 switch (skb->ip_summed) {
70 case CHECKSUM_COMPLETE:
71 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
72 break;
73 if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
74 skb->len - dataoff, protocol,
75 csum_sub(skb->csum,
76 skb_checksum(skb, 0,
77 dataoff, 0)))) {
78 skb->ip_summed = CHECKSUM_UNNECESSARY;
79 break;
81 /* fall through */
82 case CHECKSUM_NONE:
83 skb->csum = ~csum_unfold(
84 csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
85 skb->len - dataoff,
86 protocol,
87 csum_sub(0,
88 skb_checksum(skb, 0,
89 dataoff, 0))));
90 csum = __skb_checksum_complete(skb);
92 return csum;
94 EXPORT_SYMBOL(nf_ip6_checksum);
96 static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
97 unsigned int dataoff, unsigned int len,
98 u8 protocol)
100 const struct ipv6hdr *ip6h = ipv6_hdr(skb);
101 __wsum hsum;
102 __sum16 csum = 0;
104 switch (skb->ip_summed) {
105 case CHECKSUM_COMPLETE:
106 if (len == skb->len - dataoff)
107 return nf_ip6_checksum(skb, hook, dataoff, protocol);
108 /* fall through */
109 case CHECKSUM_NONE:
110 hsum = skb_checksum(skb, 0, dataoff, 0);
111 skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
112 &ip6h->daddr,
113 skb->len - dataoff,
114 protocol,
115 csum_sub(0, hsum)));
116 skb->ip_summed = CHECKSUM_NONE;
117 return __skb_checksum_complete_head(skb, dataoff + len);
119 return csum;
122 __sum16 nf_checksum(struct sk_buff *skb, unsigned int hook,
123 unsigned int dataoff, u8 protocol,
124 unsigned short family)
126 __sum16 csum = 0;
128 switch (family) {
129 case AF_INET:
130 csum = nf_ip_checksum(skb, hook, dataoff, protocol);
131 break;
132 case AF_INET6:
133 csum = nf_ip6_checksum(skb, hook, dataoff, protocol);
134 break;
137 return csum;
139 EXPORT_SYMBOL_GPL(nf_checksum);
141 __sum16 nf_checksum_partial(struct sk_buff *skb, unsigned int hook,
142 unsigned int dataoff, unsigned int len,
143 u8 protocol, unsigned short family)
145 __sum16 csum = 0;
147 switch (family) {
148 case AF_INET:
149 csum = nf_ip_checksum_partial(skb, hook, dataoff, len,
150 protocol);
151 break;
152 case AF_INET6:
153 csum = nf_ip6_checksum_partial(skb, hook, dataoff, len,
154 protocol);
155 break;
158 return csum;
160 EXPORT_SYMBOL_GPL(nf_checksum_partial);
162 int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
163 bool strict, unsigned short family)
165 const struct nf_ipv6_ops *v6ops;
166 int ret = 0;
168 switch (family) {
169 case AF_INET:
170 ret = nf_ip_route(net, dst, fl, strict);
171 break;
172 case AF_INET6:
173 v6ops = rcu_dereference(nf_ipv6_ops);
174 if (v6ops)
175 ret = v6ops->route(net, dst, fl, strict);
176 break;
179 return ret;
181 EXPORT_SYMBOL_GPL(nf_route);
183 int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)
185 const struct nf_ipv6_ops *v6ops;
186 int ret = 0;
188 switch (entry->state.pf) {
189 case AF_INET:
190 ret = nf_ip_reroute(skb, entry);
191 break;
192 case AF_INET6:
193 v6ops = rcu_dereference(nf_ipv6_ops);
194 if (v6ops)
195 ret = v6ops->reroute(skb, entry);
196 break;
198 return ret;