GNU Linux-libre 4.19.286-gnu1
[releases.git] / net / netfilter / utils.c
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>
8
9 #ifdef CONFIG_INET
10 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
11                        unsigned int dataoff, u8 protocol)
12 {
13         const struct iphdr *iph = ip_hdr(skb);
14         __sum16 csum = 0;
15
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;
26                 }
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);
36         }
37         return csum;
38 }
39 EXPORT_SYMBOL(nf_ip_checksum);
40 #endif
41
42 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
43                                       unsigned int dataoff, unsigned int len,
44                                       u8 protocol)
45 {
46         const struct iphdr *iph = ip_hdr(skb);
47         __sum16 csum = 0;
48
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);
59         }
60         return csum;
61 }
62
63 __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
64                         unsigned int dataoff, u8 protocol)
65 {
66         const struct ipv6hdr *ip6h = ipv6_hdr(skb);
67         __sum16 csum = 0;
68
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;
80                 }
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);
91         }
92         return csum;
93 }
94 EXPORT_SYMBOL(nf_ip6_checksum);
95
96 static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
97                                        unsigned int dataoff, unsigned int len,
98                                        u8 protocol)
99 {
100         const struct ipv6hdr *ip6h = ipv6_hdr(skb);
101         __wsum hsum;
102         __sum16 csum = 0;
103
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);
118         }
119         return csum;
120 };
121
122 __sum16 nf_checksum(struct sk_buff *skb, unsigned int hook,
123                     unsigned int dataoff, u8 protocol,
124                     unsigned short family)
125 {
126         __sum16 csum = 0;
127
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;
135         }
136
137         return csum;
138 }
139 EXPORT_SYMBOL_GPL(nf_checksum);
140
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)
144 {
145         __sum16 csum = 0;
146
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;
156         }
157
158         return csum;
159 }
160 EXPORT_SYMBOL_GPL(nf_checksum_partial);
161
162 int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
163              bool strict, unsigned short family)
164 {
165         const struct nf_ipv6_ops *v6ops;
166         int ret = 0;
167
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;
177         }
178
179         return ret;
180 }
181 EXPORT_SYMBOL_GPL(nf_route);
182
183 int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)
184 {
185         const struct nf_ipv6_ops *v6ops;
186         int ret = 0;
187
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;
197         }
198         return ret;
199 }