20ab0ed5041fb8791a40418e544d124cc001f930
[openwrt/openwrt.git] /
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Sat, 17 Feb 2018 11:55:51 +0100
3 Subject: [PATCH] netfilter: nf_flow_table: move ipv6 offload hook code to
4 nf_flow_table
5
6 Useful as preparation for adding iptables support for offload
7
8 Signed-off-by: Felix Fietkau <nbd@nbd.name>
9 ---
10
11 --- a/net/ipv6/netfilter/nf_flow_table_ipv6.c
12 +++ b/net/ipv6/netfilter/nf_flow_table_ipv6.c
13 @@ -3,240 +3,8 @@
14 #include <linux/module.h>
15 #include <linux/netfilter.h>
16 #include <linux/rhashtable.h>
17 -#include <linux/ipv6.h>
18 -#include <linux/netdevice.h>
19 -#include <net/ipv6.h>
20 -#include <net/ip6_route.h>
21 -#include <net/neighbour.h>
22 #include <net/netfilter/nf_flow_table.h>
23 #include <net/netfilter/nf_tables.h>
24 -/* For layer 4 checksum field offset. */
25 -#include <linux/tcp.h>
26 -#include <linux/udp.h>
27 -
28 -static int nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
29 - struct in6_addr *addr,
30 - struct in6_addr *new_addr)
31 -{
32 - struct tcphdr *tcph;
33 -
34 - if (!pskb_may_pull(skb, thoff + sizeof(*tcph)) ||
35 - skb_try_make_writable(skb, thoff + sizeof(*tcph)))
36 - return -1;
37 -
38 - tcph = (void *)(skb_network_header(skb) + thoff);
39 - inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
40 - new_addr->s6_addr32, true);
41 -
42 - return 0;
43 -}
44 -
45 -static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
46 - struct in6_addr *addr,
47 - struct in6_addr *new_addr)
48 -{
49 - struct udphdr *udph;
50 -
51 - if (!pskb_may_pull(skb, thoff + sizeof(*udph)) ||
52 - skb_try_make_writable(skb, thoff + sizeof(*udph)))
53 - return -1;
54 -
55 - udph = (void *)(skb_network_header(skb) + thoff);
56 - if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
57 - inet_proto_csum_replace16(&udph->check, skb, addr->s6_addr32,
58 - new_addr->s6_addr32, true);
59 - if (!udph->check)
60 - udph->check = CSUM_MANGLED_0;
61 - }
62 -
63 - return 0;
64 -}
65 -
66 -static int nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
67 - unsigned int thoff, struct in6_addr *addr,
68 - struct in6_addr *new_addr)
69 -{
70 - switch (ip6h->nexthdr) {
71 - case IPPROTO_TCP:
72 - if (nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr) < 0)
73 - return NF_DROP;
74 - break;
75 - case IPPROTO_UDP:
76 - if (nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr) < 0)
77 - return NF_DROP;
78 - break;
79 - }
80 -
81 - return 0;
82 -}
83 -
84 -static int nf_flow_snat_ipv6(const struct flow_offload *flow,
85 - struct sk_buff *skb, struct ipv6hdr *ip6h,
86 - unsigned int thoff,
87 - enum flow_offload_tuple_dir dir)
88 -{
89 - struct in6_addr addr, new_addr;
90 -
91 - switch (dir) {
92 - case FLOW_OFFLOAD_DIR_ORIGINAL:
93 - addr = ip6h->saddr;
94 - new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6;
95 - ip6h->saddr = new_addr;
96 - break;
97 - case FLOW_OFFLOAD_DIR_REPLY:
98 - addr = ip6h->daddr;
99 - new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6;
100 - ip6h->daddr = new_addr;
101 - break;
102 - default:
103 - return -1;
104 - }
105 -
106 - return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
107 -}
108 -
109 -static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
110 - struct sk_buff *skb, struct ipv6hdr *ip6h,
111 - unsigned int thoff,
112 - enum flow_offload_tuple_dir dir)
113 -{
114 - struct in6_addr addr, new_addr;
115 -
116 - switch (dir) {
117 - case FLOW_OFFLOAD_DIR_ORIGINAL:
118 - addr = ip6h->daddr;
119 - new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6;
120 - ip6h->daddr = new_addr;
121 - break;
122 - case FLOW_OFFLOAD_DIR_REPLY:
123 - addr = ip6h->saddr;
124 - new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6;
125 - ip6h->saddr = new_addr;
126 - break;
127 - default:
128 - return -1;
129 - }
130 -
131 - return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
132 -}
133 -
134 -static int nf_flow_nat_ipv6(const struct flow_offload *flow,
135 - struct sk_buff *skb,
136 - enum flow_offload_tuple_dir dir)
137 -{
138 - struct ipv6hdr *ip6h = ipv6_hdr(skb);
139 - unsigned int thoff = sizeof(*ip6h);
140 -
141 - if (flow->flags & FLOW_OFFLOAD_SNAT &&
142 - (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
143 - nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
144 - return -1;
145 - if (flow->flags & FLOW_OFFLOAD_DNAT &&
146 - (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
147 - nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
148 - return -1;
149 -
150 - return 0;
151 -}
152 -
153 -static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
154 - struct flow_offload_tuple *tuple)
155 -{
156 - struct flow_ports *ports;
157 - struct ipv6hdr *ip6h;
158 - unsigned int thoff;
159 -
160 - if (!pskb_may_pull(skb, sizeof(*ip6h)))
161 - return -1;
162 -
163 - ip6h = ipv6_hdr(skb);
164 -
165 - if (ip6h->nexthdr != IPPROTO_TCP &&
166 - ip6h->nexthdr != IPPROTO_UDP)
167 - return -1;
168 -
169 - thoff = sizeof(*ip6h);
170 - if (!pskb_may_pull(skb, thoff + sizeof(*ports)))
171 - return -1;
172 -
173 - ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
174 -
175 - tuple->src_v6 = ip6h->saddr;
176 - tuple->dst_v6 = ip6h->daddr;
177 - tuple->src_port = ports->source;
178 - tuple->dst_port = ports->dest;
179 - tuple->l3proto = AF_INET6;
180 - tuple->l4proto = ip6h->nexthdr;
181 - tuple->iifidx = dev->ifindex;
182 -
183 - return 0;
184 -}
185 -
186 -/* Based on ip_exceeds_mtu(). */
187 -static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
188 -{
189 - if (skb->len <= mtu)
190 - return false;
191 -
192 - if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
193 - return false;
194 -
195 - return true;
196 -}
197 -
198 -unsigned int
199 -nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
200 - const struct nf_hook_state *state)
201 -{
202 - struct flow_offload_tuple_rhash *tuplehash;
203 - struct nf_flowtable *flow_table = priv;
204 - struct flow_offload_tuple tuple = {};
205 - enum flow_offload_tuple_dir dir;
206 - struct flow_offload *flow;
207 - struct net_device *outdev;
208 - struct in6_addr *nexthop;
209 - struct ipv6hdr *ip6h;
210 - struct rt6_info *rt;
211 -
212 - if (skb->protocol != htons(ETH_P_IPV6))
213 - return NF_ACCEPT;
214 -
215 - if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
216 - return NF_ACCEPT;
217 -
218 - tuplehash = flow_offload_lookup(flow_table, &tuple);
219 - if (tuplehash == NULL)
220 - return NF_ACCEPT;
221 -
222 - outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.oifidx);
223 - if (!outdev)
224 - return NF_ACCEPT;
225 -
226 - dir = tuplehash->tuple.dir;
227 - flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
228 - rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
229 -
230 - if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
231 - return NF_ACCEPT;
232 -
233 - if (skb_try_make_writable(skb, sizeof(*ip6h)))
234 - return NF_DROP;
235 -
236 - if (flow->flags & (FLOW_OFFLOAD_SNAT | FLOW_OFFLOAD_DNAT) &&
237 - nf_flow_nat_ipv6(flow, skb, dir) < 0)
238 - return NF_DROP;
239 -
240 - flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
241 - ip6h = ipv6_hdr(skb);
242 - ip6h->hop_limit--;
243 -
244 - skb->dev = outdev;
245 - nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
246 - neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
247 -
248 - return NF_STOLEN;
249 -}
250 -EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
251
252 static struct nf_flowtable_type flowtable_ipv6 = {
253 .family = NFPROTO_IPV6,
254 --- a/net/netfilter/nf_flow_table_ip.c
255 +++ b/net/netfilter/nf_flow_table_ip.c
256 @@ -4,8 +4,11 @@
257 #include <linux/netfilter.h>
258 #include <linux/rhashtable.h>
259 #include <linux/ip.h>
260 +#include <linux/ipv6.h>
261 #include <linux/netdevice.h>
262 #include <net/ip.h>
263 +#include <net/ipv6.h>
264 +#include <net/ip6_route.h>
265 #include <net/neighbour.h>
266 #include <net/netfilter/nf_flow_table.h>
267 /* For layer 4 checksum field offset. */
268 @@ -241,3 +244,215 @@ nf_flow_offload_ip_hook(void *priv, stru
269 return NF_STOLEN;
270 }
271 EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
272 +
273 +static int nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
274 + struct in6_addr *addr,
275 + struct in6_addr *new_addr)
276 +{
277 + struct tcphdr *tcph;
278 +
279 + if (!pskb_may_pull(skb, thoff + sizeof(*tcph)) ||
280 + skb_try_make_writable(skb, thoff + sizeof(*tcph)))
281 + return -1;
282 +
283 + tcph = (void *)(skb_network_header(skb) + thoff);
284 + inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
285 + new_addr->s6_addr32, true);
286 +
287 + return 0;
288 +}
289 +
290 +static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
291 + struct in6_addr *addr,
292 + struct in6_addr *new_addr)
293 +{
294 + struct udphdr *udph;
295 +
296 + if (!pskb_may_pull(skb, thoff + sizeof(*udph)) ||
297 + skb_try_make_writable(skb, thoff + sizeof(*udph)))
298 + return -1;
299 +
300 + udph = (void *)(skb_network_header(skb) + thoff);
301 + if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
302 + inet_proto_csum_replace16(&udph->check, skb, addr->s6_addr32,
303 + new_addr->s6_addr32, true);
304 + if (!udph->check)
305 + udph->check = CSUM_MANGLED_0;
306 + }
307 +
308 + return 0;
309 +}
310 +
311 +static int nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
312 + unsigned int thoff, struct in6_addr *addr,
313 + struct in6_addr *new_addr)
314 +{
315 + switch (ip6h->nexthdr) {
316 + case IPPROTO_TCP:
317 + if (nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr) < 0)
318 + return NF_DROP;
319 + break;
320 + case IPPROTO_UDP:
321 + if (nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr) < 0)
322 + return NF_DROP;
323 + break;
324 + }
325 +
326 + return 0;
327 +}
328 +
329 +static int nf_flow_snat_ipv6(const struct flow_offload *flow,
330 + struct sk_buff *skb, struct ipv6hdr *ip6h,
331 + unsigned int thoff,
332 + enum flow_offload_tuple_dir dir)
333 +{
334 + struct in6_addr addr, new_addr;
335 +
336 + switch (dir) {
337 + case FLOW_OFFLOAD_DIR_ORIGINAL:
338 + addr = ip6h->saddr;
339 + new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v6;
340 + ip6h->saddr = new_addr;
341 + break;
342 + case FLOW_OFFLOAD_DIR_REPLY:
343 + addr = ip6h->daddr;
344 + new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6;
345 + ip6h->daddr = new_addr;
346 + break;
347 + default:
348 + return -1;
349 + }
350 +
351 + return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
352 +}
353 +
354 +static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
355 + struct sk_buff *skb, struct ipv6hdr *ip6h,
356 + unsigned int thoff,
357 + enum flow_offload_tuple_dir dir)
358 +{
359 + struct in6_addr addr, new_addr;
360 +
361 + switch (dir) {
362 + case FLOW_OFFLOAD_DIR_ORIGINAL:
363 + addr = ip6h->daddr;
364 + new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v6;
365 + ip6h->daddr = new_addr;
366 + break;
367 + case FLOW_OFFLOAD_DIR_REPLY:
368 + addr = ip6h->saddr;
369 + new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6;
370 + ip6h->saddr = new_addr;
371 + break;
372 + default:
373 + return -1;
374 + }
375 +
376 + return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
377 +}
378 +
379 +static int nf_flow_nat_ipv6(const struct flow_offload *flow,
380 + struct sk_buff *skb,
381 + enum flow_offload_tuple_dir dir)
382 +{
383 + struct ipv6hdr *ip6h = ipv6_hdr(skb);
384 + unsigned int thoff = sizeof(*ip6h);
385 +
386 + if (flow->flags & FLOW_OFFLOAD_SNAT &&
387 + (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
388 + nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
389 + return -1;
390 + if (flow->flags & FLOW_OFFLOAD_DNAT &&
391 + (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
392 + nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
393 + return -1;
394 +
395 + return 0;
396 +}
397 +
398 +static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
399 + struct flow_offload_tuple *tuple)
400 +{
401 + struct flow_ports *ports;
402 + struct ipv6hdr *ip6h;
403 + unsigned int thoff;
404 +
405 + if (!pskb_may_pull(skb, sizeof(*ip6h)))
406 + return -1;
407 +
408 + ip6h = ipv6_hdr(skb);
409 +
410 + if (ip6h->nexthdr != IPPROTO_TCP &&
411 + ip6h->nexthdr != IPPROTO_UDP)
412 + return -1;
413 +
414 + thoff = sizeof(*ip6h);
415 + if (!pskb_may_pull(skb, thoff + sizeof(*ports)))
416 + return -1;
417 +
418 + ports = (struct flow_ports *)(skb_network_header(skb) + thoff);
419 +
420 + tuple->src_v6 = ip6h->saddr;
421 + tuple->dst_v6 = ip6h->daddr;
422 + tuple->src_port = ports->source;
423 + tuple->dst_port = ports->dest;
424 + tuple->l3proto = AF_INET6;
425 + tuple->l4proto = ip6h->nexthdr;
426 + tuple->iifidx = dev->ifindex;
427 +
428 + return 0;
429 +}
430 +
431 +unsigned int
432 +nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
433 + const struct nf_hook_state *state)
434 +{
435 + struct flow_offload_tuple_rhash *tuplehash;
436 + struct nf_flowtable *flow_table = priv;
437 + struct flow_offload_tuple tuple = {};
438 + enum flow_offload_tuple_dir dir;
439 + struct flow_offload *flow;
440 + struct net_device *outdev;
441 + struct in6_addr *nexthop;
442 + struct ipv6hdr *ip6h;
443 + struct rt6_info *rt;
444 +
445 + if (skb->protocol != htons(ETH_P_IPV6))
446 + return NF_ACCEPT;
447 +
448 + if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
449 + return NF_ACCEPT;
450 +
451 + tuplehash = flow_offload_lookup(flow_table, &tuple);
452 + if (tuplehash == NULL)
453 + return NF_ACCEPT;
454 +
455 + outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.oifidx);
456 + if (!outdev)
457 + return NF_ACCEPT;
458 +
459 + dir = tuplehash->tuple.dir;
460 + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
461 + rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
462 +
463 + if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
464 + return NF_ACCEPT;
465 +
466 + if (skb_try_make_writable(skb, sizeof(*ip6h)))
467 + return NF_DROP;
468 +
469 + if (flow->flags & (FLOW_OFFLOAD_SNAT | FLOW_OFFLOAD_DNAT) &&
470 + nf_flow_nat_ipv6(flow, skb, dir) < 0)
471 + return NF_DROP;
472 +
473 + flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
474 + ip6h = ipv6_hdr(skb);
475 + ip6h->hop_limit--;
476 +
477 + skb->dev = outdev;
478 + nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
479 + neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb);
480 +
481 + return NF_STOLEN;
482 +}
483 +EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);