]>
Commit | Line | Data |
---|---|---|
73b09aff JS |
1 | /* |
2 | * Backported from upstream commit 5b490047240f | |
3 | * ("ipv6: Export nf_ct_frag6_gather()") | |
4 | * | |
5 | * IPv6 fragment reassembly for connection tracking | |
6 | * | |
7 | * Copyright (C)2004 USAGI/WIDE Project | |
8 | * | |
9 | * Author: | |
10 | * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | |
11 | * | |
12 | * Based on: net/ipv6/reassembly.c | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or | |
15 | * modify it under the terms of the GNU General Public License | |
16 | * as published by the Free Software Foundation; either version | |
17 | * 2 of the License, or (at your option) any later version. | |
18 | */ | |
19 | ||
20 | #define pr_fmt(fmt) "IPv6-nf: " fmt | |
21 | ||
22 | #include <linux/version.h> | |
23 | ||
73b09aff JS |
24 | #include <linux/errno.h> |
25 | #include <linux/types.h> | |
26 | #include <linux/string.h> | |
27 | #include <linux/socket.h> | |
28 | #include <linux/sockios.h> | |
29 | #include <linux/jiffies.h> | |
30 | #include <linux/net.h> | |
31 | #include <linux/list.h> | |
32 | #include <linux/netdevice.h> | |
33 | #include <linux/in6.h> | |
34 | #include <linux/ipv6.h> | |
35 | #include <linux/icmpv6.h> | |
36 | #include <linux/random.h> | |
37 | #include <linux/slab.h> | |
38 | ||
39 | #include <net/sock.h> | |
40 | #include <net/snmp.h> | |
41 | #include <net/inet_frag.h> | |
42 | ||
43 | #include <net/ipv6.h> | |
88a28bbe | 44 | #include <net/ipv6_frag.h> |
73b09aff JS |
45 | #include <net/protocol.h> |
46 | #include <net/transp_v6.h> | |
47 | #include <net/rawv6.h> | |
48 | #include <net/ndisc.h> | |
49 | #include <net/addrconf.h> | |
50 | #include <net/inet_ecn.h> | |
51 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | |
52 | #include <linux/netfilter.h> | |
53 | #include <linux/netfilter_ipv6.h> | |
54 | #include <linux/kernel.h> | |
55 | #include <linux/module.h> | |
56 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | |
7f4a5d68 | 57 | #include <net/netns/generic.h> |
58 | #include "datapath.h" | |
73b09aff | 59 | |
34c3c2b7 JS |
60 | #ifdef OVS_NF_DEFRAG6_BACKPORT |
61 | ||
63129291 | 62 | static const char nf_frags_cache_name[] = "ovs-frag6"; |
73b09aff JS |
63 | |
64 | struct nf_ct_frag6_skb_cb | |
65 | { | |
66 | struct inet6_skb_parm h; | |
67 | int offset; | |
73b09aff JS |
68 | }; |
69 | ||
70 | #define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb)) | |
71 | ||
72 | static struct inet_frags nf_frags; | |
73 | ||
7f4a5d68 | 74 | static struct netns_frags *get_netns_frags6_from_net(struct net *net) |
75 | { | |
76 | #ifdef HAVE_INET_FRAG_LRU_MOVE | |
77 | struct ovs_net *ovs_net = net_generic(net, ovs_net_id); | |
78 | return &(ovs_net->nf_frags); | |
79 | #else | |
80 | return &(net->nf_frag.frags); | |
81 | #endif | |
82 | } | |
83 | ||
84 | static struct net *get_net_from_netns_frags6(struct netns_frags *frags) | |
85 | { | |
86 | struct net *net; | |
87 | #ifdef HAVE_INET_FRAG_LRU_MOVE | |
88 | struct ovs_net *ovs_net; | |
89 | ||
90 | ovs_net = container_of(frags, struct ovs_net, nf_frags); | |
91 | net = ovs_net->net; | |
92 | #else | |
93 | net = container_of(frags, struct net, nf_frag.frags); | |
94 | #endif | |
95 | return net; | |
96 | } | |
97 | ||
73b09aff JS |
98 | static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) |
99 | { | |
100 | return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK); | |
101 | } | |
102 | ||
4a90b277 | 103 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff JS |
104 | static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr, |
105 | const struct in6_addr *daddr) | |
106 | { | |
107 | net_get_random_once(&nf_frags.rnd, sizeof(nf_frags.rnd)); | |
108 | return jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), | |
109 | (__force u32)id, nf_frags.rnd); | |
110 | } | |
ccd0a13b JS |
111 | /* fb3cfe6e75b9 ("inet: frag: remove hash size assumptions from callers") |
112 | * shifted this logic into inet_fragment, but prior kernels still need this. | |
113 | */ | |
114 | #if LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) | |
115 | #define nf_hash_frag(a, b, c) (nf_hash_frag(a, b, c) & (INETFRAGS_HASHSZ - 1)) | |
116 | #endif | |
73b09aff JS |
117 | |
118 | #ifdef HAVE_INET_FRAGS_CONST | |
119 | static unsigned int nf_hashfn(const struct inet_frag_queue *q) | |
120 | #else | |
121 | static unsigned int nf_hashfn(struct inet_frag_queue *q) | |
122 | #endif | |
123 | { | |
124 | const struct frag_queue *nq; | |
125 | ||
126 | nq = container_of(q, struct frag_queue, q); | |
127 | return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); | |
128 | } | |
129 | ||
4a90b277 | 130 | #endif /* HAVE_INET_FRAGS_RND */ |
73b09aff JS |
131 | static void nf_ct_frag6_expire(unsigned long data) |
132 | { | |
133 | struct frag_queue *fq; | |
134 | struct net *net; | |
135 | ||
136 | fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); | |
7f4a5d68 | 137 | net = get_net_from_netns_frags6(fq->q.net); |
73b09aff | 138 | |
4a90b277 | 139 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff | 140 | ip6_expire_frag_queue(net, fq, &nf_frags); |
88a28bbe YS |
141 | #else |
142 | #ifdef HAVE_IPV6_FRAG_H | |
143 | ip6frag_expire_frag_queue(net, fq); | |
4a90b277 GR |
144 | #else |
145 | ip6_expire_frag_queue(net, fq); | |
146 | #endif | |
88a28bbe | 147 | #endif |
73b09aff JS |
148 | } |
149 | ||
4a90b277 | 150 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff JS |
151 | /* Creation primitives. */ |
152 | static inline struct frag_queue *fq_find(struct net *net, __be32 id, | |
153 | u32 user, struct in6_addr *src, | |
154 | struct in6_addr *dst, u8 ecn) | |
155 | { | |
156 | struct inet_frag_queue *q; | |
157 | struct ip6_create_arg arg; | |
158 | unsigned int hash; | |
7f4a5d68 | 159 | struct netns_frags *frags; |
73b09aff JS |
160 | |
161 | arg.id = id; | |
162 | arg.user = user; | |
163 | arg.src = src; | |
164 | arg.dst = dst; | |
165 | arg.ecn = ecn; | |
166 | ||
ccd0a13b JS |
167 | #ifdef HAVE_INET_FRAGS_WITH_RWLOCK |
168 | read_lock_bh(&nf_frags.lock); | |
169 | #else | |
73b09aff | 170 | local_bh_disable(); |
ccd0a13b | 171 | #endif |
73b09aff JS |
172 | hash = nf_hash_frag(id, src, dst); |
173 | ||
7f4a5d68 | 174 | frags = get_netns_frags6_from_net(net); |
175 | q = inet_frag_find(frags, &nf_frags, &arg, hash); | |
73b09aff JS |
176 | local_bh_enable(); |
177 | if (IS_ERR_OR_NULL(q)) { | |
178 | inet_frag_maybe_warn_overflow(q, pr_fmt()); | |
179 | return NULL; | |
180 | } | |
181 | return container_of(q, struct frag_queue, q); | |
182 | } | |
4a90b277 GR |
183 | #else |
184 | static struct frag_queue *fq_find(struct net *net, __be32 id, u32 user, | |
185 | const struct ipv6hdr *hdr, int iif) | |
186 | { | |
187 | struct frag_v6_compare_key key = { | |
188 | .id = id, | |
189 | .saddr = hdr->saddr, | |
190 | .daddr = hdr->daddr, | |
191 | .user = user, | |
192 | .iif = iif, | |
193 | }; | |
194 | struct inet_frag_queue *q; | |
195 | ||
196 | q = inet_frag_find(&net->nf_frag.frags, &key); | |
197 | if (!q) | |
198 | return NULL; | |
199 | ||
200 | return container_of(q, struct frag_queue, q); | |
201 | } | |
73b09aff | 202 | |
4a90b277 | 203 | #endif /* HAVE_INET_FRAGS_RND */ |
73b09aff JS |
204 | |
205 | static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, | |
206 | const struct frag_hdr *fhdr, int nhoff) | |
207 | { | |
208 | struct sk_buff *prev, *next; | |
209 | unsigned int payload_len; | |
210 | int offset, end; | |
211 | u8 ecn; | |
212 | ||
213 | if (qp_flags(fq) & INET_FRAG_COMPLETE) { | |
214 | pr_debug("Already completed\n"); | |
215 | goto err; | |
216 | } | |
217 | ||
218 | payload_len = ntohs(ipv6_hdr(skb)->payload_len); | |
219 | ||
220 | offset = ntohs(fhdr->frag_off) & ~0x7; | |
221 | end = offset + (payload_len - | |
222 | ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); | |
223 | ||
224 | if ((unsigned int)end > IPV6_MAXPLEN) { | |
225 | pr_debug("offset is too large.\n"); | |
226 | return -1; | |
227 | } | |
228 | ||
229 | ecn = ip6_frag_ecn(ipv6_hdr(skb)); | |
230 | ||
231 | if (skb->ip_summed == CHECKSUM_COMPLETE) { | |
232 | const unsigned char *nh = skb_network_header(skb); | |
233 | skb->csum = csum_sub(skb->csum, | |
234 | csum_partial(nh, (u8 *)(fhdr + 1) - nh, | |
235 | 0)); | |
236 | } | |
237 | ||
238 | /* Is this the final fragment? */ | |
239 | if (!(fhdr->frag_off & htons(IP6_MF))) { | |
240 | /* If we already have some bits beyond end | |
241 | * or have different end, the segment is corrupted. | |
242 | */ | |
243 | if (end < fq->q.len || | |
244 | ((qp_flags(fq) & INET_FRAG_LAST_IN) && end != fq->q.len)) { | |
245 | pr_debug("already received last fragment\n"); | |
246 | goto err; | |
247 | } | |
248 | qp_flags(fq) |= INET_FRAG_LAST_IN; | |
249 | fq->q.len = end; | |
250 | } else { | |
251 | /* Check if the fragment is rounded to 8 bytes. | |
252 | * Required by the RFC. | |
253 | */ | |
254 | if (end & 0x7) { | |
255 | /* RFC2460 says always send parameter problem in | |
256 | * this case. -DaveM | |
257 | */ | |
258 | pr_debug("end of fragment not rounded to 8 bytes.\n"); | |
259 | return -1; | |
260 | } | |
261 | if (end > fq->q.len) { | |
262 | /* Some bits beyond end -> corruption. */ | |
263 | if (qp_flags(fq) & INET_FRAG_LAST_IN) { | |
264 | pr_debug("last packet already reached.\n"); | |
265 | goto err; | |
266 | } | |
267 | fq->q.len = end; | |
268 | } | |
269 | } | |
270 | ||
271 | if (end == offset) | |
272 | goto err; | |
273 | ||
274 | /* Point into the IP datagram 'data' part. */ | |
275 | if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) { | |
276 | pr_debug("queue: message is too short.\n"); | |
277 | goto err; | |
278 | } | |
279 | if (pskb_trim_rcsum(skb, end - offset)) { | |
280 | pr_debug("Can't trim\n"); | |
281 | goto err; | |
282 | } | |
283 | ||
284 | /* Find out which fragments are in front and at the back of us | |
285 | * in the chain of fragments so far. We must know where to put | |
286 | * this fragment, right? | |
287 | */ | |
288 | prev = fq->q.fragments_tail; | |
289 | if (!prev || NFCT_FRAG6_CB(prev)->offset < offset) { | |
290 | next = NULL; | |
291 | goto found; | |
292 | } | |
293 | prev = NULL; | |
294 | for (next = fq->q.fragments; next != NULL; next = next->next) { | |
295 | if (NFCT_FRAG6_CB(next)->offset >= offset) | |
296 | break; /* bingo! */ | |
297 | prev = next; | |
298 | } | |
299 | ||
300 | found: | |
301 | /* RFC5722, Section 4: | |
302 | * When reassembling an IPv6 datagram, if | |
303 | * one or more its constituent fragments is determined to be an | |
304 | * overlapping fragment, the entire datagram (and any constituent | |
305 | * fragments, including those not yet received) MUST be silently | |
306 | * discarded. | |
307 | */ | |
308 | ||
309 | /* Check for overlap with preceding fragment. */ | |
310 | if (prev && | |
311 | (NFCT_FRAG6_CB(prev)->offset + prev->len) > offset) | |
312 | goto discard_fq; | |
313 | ||
314 | /* Look for overlap with succeeding segment. */ | |
315 | if (next && NFCT_FRAG6_CB(next)->offset < end) | |
316 | goto discard_fq; | |
317 | ||
318 | NFCT_FRAG6_CB(skb)->offset = offset; | |
319 | ||
320 | /* Insert this fragment in the chain of fragments. */ | |
321 | skb->next = next; | |
322 | if (!next) | |
323 | fq->q.fragments_tail = skb; | |
324 | if (prev) | |
325 | prev->next = skb; | |
326 | else | |
327 | fq->q.fragments = skb; | |
328 | ||
329 | if (skb->dev) { | |
330 | fq->iif = skb->dev->ifindex; | |
331 | skb->dev = NULL; | |
332 | } | |
333 | fq->q.stamp = skb->tstamp; | |
334 | fq->q.meat += skb->len; | |
335 | fq->ecn |= ecn; | |
336 | if (payload_len > fq->q.max_size) | |
337 | fq->q.max_size = payload_len; | |
338 | add_frag_mem_limit(fq->q.net, skb->truesize); | |
339 | ||
340 | /* The first fragment. | |
341 | * nhoffset is obtained from the first fragment, of course. | |
342 | */ | |
343 | if (offset == 0) { | |
344 | fq->nhoffset = nhoff; | |
345 | qp_flags(fq) |= INET_FRAG_FIRST_IN; | |
346 | } | |
347 | ||
38f45380 | 348 | inet_frag_lru_move(&fq->q); |
73b09aff JS |
349 | return 0; |
350 | ||
351 | discard_fq: | |
4a90b277 | 352 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff | 353 | inet_frag_kill(&fq->q, &nf_frags); |
4a90b277 GR |
354 | #else |
355 | inet_frag_kill(&fq->q); | |
356 | #endif | |
73b09aff JS |
357 | err: |
358 | return -1; | |
359 | } | |
360 | ||
361 | /* | |
362 | * Check if this packet is complete. | |
73b09aff JS |
363 | * |
364 | * It is called with locked fq, and caller must check that | |
365 | * queue is eligible for reassembly i.e. it is not COMPLETE, | |
366 | * the last and the first frames arrived and all the bits are here. | |
2e602ea3 JS |
367 | * |
368 | * returns true if *prev skb has been transformed into the reassembled | |
369 | * skb, false otherwise. | |
73b09aff | 370 | */ |
2e602ea3 | 371 | static bool |
4b653331 | 372 | nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev) |
73b09aff | 373 | { |
4b653331 | 374 | struct sk_buff *fp, *head = fq->q.fragments; |
73b09aff JS |
375 | int payload_len; |
376 | u8 ecn; | |
377 | ||
4a90b277 | 378 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff | 379 | inet_frag_kill(&fq->q, &nf_frags); |
4a90b277 GR |
380 | #else |
381 | inet_frag_kill(&fq->q); | |
382 | #endif | |
73b09aff JS |
383 | |
384 | WARN_ON(head == NULL); | |
385 | WARN_ON(NFCT_FRAG6_CB(head)->offset != 0); | |
386 | ||
387 | ecn = ip_frag_ecn_table[fq->ecn]; | |
388 | if (unlikely(ecn == 0xff)) | |
2e602ea3 | 389 | return false; |
73b09aff JS |
390 | |
391 | /* Unfragmented part is taken from the first segment. */ | |
392 | payload_len = ((head->data - skb_network_header(head)) - | |
393 | sizeof(struct ipv6hdr) + fq->q.len - | |
394 | sizeof(struct frag_hdr)); | |
395 | if (payload_len > IPV6_MAXPLEN) { | |
2e602ea3 JS |
396 | net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n", |
397 | payload_len); | |
398 | return false; | |
73b09aff JS |
399 | } |
400 | ||
401 | /* Head of list must not be cloned. */ | |
2e602ea3 JS |
402 | if (skb_unclone(head, GFP_ATOMIC)) |
403 | return false; | |
73b09aff JS |
404 | |
405 | /* If the first fragment is fragmented itself, we split | |
406 | * it to two chunks: the first with data and paged part | |
407 | * and the second, holding only fragments. */ | |
408 | if (skb_has_frag_list(head)) { | |
409 | struct sk_buff *clone; | |
410 | int i, plen = 0; | |
411 | ||
412 | clone = alloc_skb(0, GFP_ATOMIC); | |
413 | if (clone == NULL) | |
2e602ea3 | 414 | return false; |
73b09aff JS |
415 | |
416 | clone->next = head->next; | |
417 | head->next = clone; | |
418 | skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; | |
419 | skb_frag_list_init(head); | |
420 | for (i = 0; i < skb_shinfo(head)->nr_frags; i++) | |
421 | plen += skb_frag_size(&skb_shinfo(head)->frags[i]); | |
422 | clone->len = clone->data_len = head->data_len - plen; | |
423 | head->data_len -= clone->len; | |
424 | head->len -= clone->len; | |
425 | clone->csum = 0; | |
426 | clone->ip_summed = head->ip_summed; | |
427 | ||
73b09aff JS |
428 | add_frag_mem_limit(fq->q.net, clone->truesize); |
429 | } | |
430 | ||
4b653331 JS |
431 | /* morph head into last received skb: prev. |
432 | * | |
433 | * This allows callers of ipv6 conntrack defrag to continue | |
434 | * to use the last skb(frag) passed into the reasm engine. | |
435 | * The last skb frag 'silently' turns into the full reassembled skb. | |
436 | * | |
437 | * Since prev is also part of q->fragments we have to clone it first. | |
438 | */ | |
439 | if (head != prev) { | |
440 | struct sk_buff *iter; | |
441 | ||
442 | fp = skb_clone(prev, GFP_ATOMIC); | |
443 | if (!fp) | |
2e602ea3 | 444 | return false; |
4b653331 JS |
445 | |
446 | fp->next = prev->next; | |
4a7649a8 JS |
447 | |
448 | iter = head; | |
449 | while (iter) { | |
450 | if (iter->next == prev) { | |
451 | iter->next = fp; | |
452 | break; | |
453 | } | |
454 | iter = iter->next; | |
4b653331 JS |
455 | } |
456 | ||
457 | skb_morph(prev, head); | |
458 | prev->next = head->next; | |
459 | consume_skb(head); | |
460 | head = prev; | |
461 | } | |
462 | ||
73b09aff JS |
463 | /* We have to remove fragment header from datagram and to relocate |
464 | * header in order to calculate ICV correctly. */ | |
465 | skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0]; | |
466 | memmove(head->head + sizeof(struct frag_hdr), head->head, | |
467 | (head->data - head->head) - sizeof(struct frag_hdr)); | |
468 | head->mac_header += sizeof(struct frag_hdr); | |
469 | head->network_header += sizeof(struct frag_hdr); | |
470 | ||
471 | skb_shinfo(head)->frag_list = head->next; | |
472 | skb_reset_transport_header(head); | |
473 | skb_push(head, head->data - skb_network_header(head)); | |
474 | ||
475 | for (fp=head->next; fp; fp = fp->next) { | |
476 | head->data_len += fp->len; | |
477 | head->len += fp->len; | |
478 | if (head->ip_summed != fp->ip_summed) | |
479 | head->ip_summed = CHECKSUM_NONE; | |
480 | else if (head->ip_summed == CHECKSUM_COMPLETE) | |
481 | head->csum = csum_add(head->csum, fp->csum); | |
482 | head->truesize += fp->truesize; | |
483 | } | |
484 | sub_frag_mem_limit(fq->q.net, head->truesize); | |
485 | ||
486 | head->ignore_df = 1; | |
487 | head->next = NULL; | |
488 | head->dev = dev; | |
489 | head->tstamp = fq->q.stamp; | |
490 | ipv6_hdr(head)->payload_len = htons(payload_len); | |
491 | ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); | |
492 | IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; | |
493 | ||
494 | /* Yes, and fold redundant checksum back. 8) */ | |
495 | if (head->ip_summed == CHECKSUM_COMPLETE) | |
496 | head->csum = csum_partial(skb_network_header(head), | |
497 | skb_network_header_len(head), | |
498 | head->csum); | |
499 | ||
500 | fq->q.fragments = NULL; | |
501 | fq->q.fragments_tail = NULL; | |
502 | ||
2e602ea3 | 503 | return true; |
73b09aff JS |
504 | } |
505 | ||
506 | /* | |
507 | * find the header just before Fragment Header. | |
508 | * | |
509 | * if success return 0 and set ... | |
510 | * (*prevhdrp): the value of "Next Header Field" in the header | |
511 | * just before Fragment Header. | |
512 | * (*prevhoff): the offset of "Next Header Field" in the header | |
513 | * just before Fragment Header. | |
514 | * (*fhoff) : the offset of Fragment Header. | |
515 | * | |
516 | * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c | |
517 | * | |
518 | */ | |
519 | static int | |
520 | find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) | |
521 | { | |
522 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | |
523 | const int netoff = skb_network_offset(skb); | |
524 | u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr); | |
525 | int start = netoff + sizeof(struct ipv6hdr); | |
526 | int len = skb->len - start; | |
527 | u8 prevhdr = NEXTHDR_IPV6; | |
528 | ||
529 | while (nexthdr != NEXTHDR_FRAGMENT) { | |
530 | struct ipv6_opt_hdr hdr; | |
531 | int hdrlen; | |
532 | ||
533 | if (!ipv6_ext_hdr(nexthdr)) { | |
534 | return -1; | |
535 | } | |
536 | if (nexthdr == NEXTHDR_NONE) { | |
537 | pr_debug("next header is none\n"); | |
538 | return -1; | |
539 | } | |
540 | if (len < (int)sizeof(struct ipv6_opt_hdr)) { | |
541 | pr_debug("too short\n"); | |
542 | return -1; | |
543 | } | |
544 | if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) | |
545 | BUG(); | |
546 | if (nexthdr == NEXTHDR_AUTH) | |
547 | hdrlen = (hdr.hdrlen+2)<<2; | |
548 | else | |
549 | hdrlen = ipv6_optlen(&hdr); | |
550 | ||
551 | prevhdr = nexthdr; | |
552 | prev_nhoff = start; | |
553 | ||
554 | nexthdr = hdr.nexthdr; | |
555 | len -= hdrlen; | |
556 | start += hdrlen; | |
557 | } | |
558 | ||
559 | if (len < 0) | |
560 | return -1; | |
561 | ||
562 | *prevhdrp = prevhdr; | |
563 | *prevhoff = prev_nhoff; | |
564 | *fhoff = start; | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
68d400d5 | 569 | int rpl_nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user) |
73b09aff | 570 | { |
73b09aff | 571 | struct net_device *dev = skb->dev; |
2e602ea3 | 572 | int fhoff, nhoff, ret; |
73b09aff JS |
573 | struct frag_hdr *fhdr; |
574 | struct frag_queue *fq; | |
575 | struct ipv6hdr *hdr; | |
73b09aff | 576 | u8 prevhdr; |
7f4a5d68 | 577 | struct netns_frags *frags; |
73b09aff JS |
578 | |
579 | /* Jumbo payload inhibits frag. header */ | |
580 | if (ipv6_hdr(skb)->payload_len == 0) { | |
581 | pr_debug("payload len = 0\n"); | |
2e602ea3 | 582 | return -EINVAL; |
73b09aff JS |
583 | } |
584 | ||
585 | if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) | |
2e602ea3 | 586 | return -EINVAL; |
73b09aff | 587 | |
4b653331 | 588 | if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr))) |
2e602ea3 | 589 | return -ENOMEM; |
73b09aff | 590 | |
4b653331 JS |
591 | skb_set_transport_header(skb, fhoff); |
592 | hdr = ipv6_hdr(skb); | |
593 | fhdr = (struct frag_hdr *)skb_transport_header(skb); | |
73b09aff | 594 | |
ccd0a13b | 595 | /* See ip_evictor(). */ |
7f4a5d68 | 596 | frags = get_netns_frags6_from_net(net); |
ccd0a13b JS |
597 | #ifdef HAVE_INET_FRAG_EVICTOR |
598 | local_bh_disable(); | |
7f4a5d68 | 599 | inet_frag_evictor(frags, &nf_frags, false); |
ccd0a13b JS |
600 | local_bh_enable(); |
601 | #endif | |
602 | ||
68d400d5 | 603 | skb_orphan(skb); |
4a90b277 | 604 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff JS |
605 | fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, |
606 | ip6_frag_ecn(hdr)); | |
4a90b277 GR |
607 | #else |
608 | fq = fq_find(net, fhdr->identification, user, hdr, | |
609 | skb->dev ? skb->dev->ifindex : 0); | |
610 | #endif | |
4b653331 | 611 | if (fq == NULL) |
2e602ea3 | 612 | return -ENOMEM; |
73b09aff JS |
613 | |
614 | spin_lock_bh(&fq->q.lock); | |
615 | ||
4b653331 | 616 | if (nf_ct_frag6_queue(fq, skb, fhdr, nhoff) < 0) { |
2e602ea3 JS |
617 | ret = -EINVAL; |
618 | goto out_unlock; | |
73b09aff JS |
619 | } |
620 | ||
2e602ea3 JS |
621 | /* after queue has assumed skb ownership, only 0 or -EINPROGRESS |
622 | * must be returned. | |
623 | */ | |
624 | ret = -EINPROGRESS; | |
73b09aff | 625 | if (qp_flags(fq) == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && |
2e602ea3 JS |
626 | fq->q.meat == fq->q.len && |
627 | nf_ct_frag6_reasm(fq, skb, dev)) | |
628 | ret = 0; | |
73b09aff | 629 | |
2e602ea3 JS |
630 | out_unlock: |
631 | spin_unlock_bh(&fq->q.lock); | |
4a90b277 | 632 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff | 633 | inet_frag_put(&fq->q, &nf_frags); |
4a90b277 GR |
634 | #else |
635 | inet_frag_put(&fq->q); | |
636 | #endif | |
2e602ea3 | 637 | return ret; |
73b09aff | 638 | } |
73b09aff | 639 | |
802d12b6 GR |
640 | #ifdef HAVE_DEFRAG_ENABLE_TAKES_NET |
641 | static int nf_ct_net_init(struct net *net) | |
642 | { | |
643 | return nf_defrag_ipv6_enable(net); | |
644 | } | |
645 | #endif | |
646 | ||
73b09aff JS |
647 | static void nf_ct_net_exit(struct net *net) |
648 | { | |
7f4a5d68 | 649 | } |
650 | ||
651 | void ovs_netns_frags6_init(struct net *net) | |
652 | { | |
653 | #ifdef HAVE_INET_FRAG_LRU_MOVE | |
654 | struct ovs_net *ovs_net = net_generic(net, ovs_net_id); | |
655 | ||
656 | ovs_net->nf_frags.high_thresh = IPV6_FRAG_HIGH_THRESH; | |
657 | ovs_net->nf_frags.low_thresh = IPV6_FRAG_LOW_THRESH; | |
658 | ovs_net->nf_frags.timeout = IPV6_FRAG_TIMEOUT; | |
659 | ||
660 | inet_frags_init_net(&(ovs_net->nf_frags)); | |
661 | #endif | |
662 | } | |
663 | ||
664 | void ovs_netns_frags6_exit(struct net *net) | |
665 | { | |
4a90b277 | 666 | #ifdef HAVE_INET_FRAGS_RND |
7f4a5d68 | 667 | struct netns_frags *frags; |
668 | ||
669 | frags = get_netns_frags6_from_net(net); | |
670 | inet_frags_exit_net(frags, &nf_frags); | |
4a90b277 | 671 | #endif |
73b09aff JS |
672 | } |
673 | ||
674 | static struct pernet_operations nf_ct_net_ops = { | |
802d12b6 GR |
675 | #ifdef HAVE_DEFRAG_ENABLE_TAKES_NET |
676 | .init = nf_ct_net_init, | |
677 | #endif | |
73b09aff JS |
678 | .exit = nf_ct_net_exit, |
679 | }; | |
680 | ||
88a28bbe YS |
681 | #ifdef HAVE_IPV6_FRAG_H |
682 | static const struct rhashtable_params nfct_rhash_params = { | |
683 | .head_offset = offsetof(struct inet_frag_queue, node), | |
684 | .hashfn = ip6frag_key_hashfn, | |
685 | .obj_hashfn = ip6frag_obj_hashfn, | |
686 | .obj_cmpfn = ip6frag_obj_cmpfn, | |
687 | .automatic_shrinking = true, | |
688 | }; | |
689 | #endif | |
690 | ||
73b09aff JS |
691 | int rpl_nf_ct_frag6_init(void) |
692 | { | |
693 | int ret = 0; | |
694 | ||
802d12b6 | 695 | #ifndef HAVE_DEFRAG_ENABLE_TAKES_NET |
5e9c7f2b | 696 | nf_defrag_ipv6_enable(); |
802d12b6 | 697 | #endif |
4a90b277 | 698 | #ifdef HAVE_INET_FRAGS_RND |
73b09aff | 699 | nf_frags.hashfn = nf_hashfn; |
4a90b277 | 700 | nf_frags.match = ip6_frag_match; |
88a28bbe YS |
701 | nf_frags.constructor = ip6_frag_init; |
702 | #else | |
703 | #ifdef HAVE_IPV6_FRAG_H | |
704 | nf_frags.rhash_params = nfct_rhash_params; | |
705 | nf_frags.constructor = ip6frag_init; | |
4a90b277 GR |
706 | #else |
707 | nf_frags.rhash_params = ip6_rhash_params; | |
fc00853c | 708 | nf_frags.constructor = ip6_frag_init; |
88a28bbe YS |
709 | #endif |
710 | #endif /* HAVE_INET_FRAGS_RND */ | |
73b09aff | 711 | nf_frags.destructor = NULL; |
73b09aff | 712 | nf_frags.qsize = sizeof(struct frag_queue); |
73b09aff | 713 | nf_frags.frag_expire = nf_ct_frag6_expire; |
4a90b277 | 714 | #if defined(HAVE_INET_FRAGS_WITH_FRAGS_WORK) || !defined(HAVE_INET_FRAGS_RND) |
73b09aff | 715 | nf_frags.frags_cache_name = nf_frags_cache_name; |
12190598 GR |
716 | #endif |
717 | #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0) | |
718 | nf_frags.secret_interval = 10 * 60 * HZ; | |
73b09aff JS |
719 | #endif |
720 | ret = inet_frags_init(&nf_frags); | |
721 | if (ret) | |
722 | goto out; | |
723 | ret = register_pernet_subsys(&nf_ct_net_ops); | |
724 | if (ret) | |
725 | inet_frags_fini(&nf_frags); | |
726 | ||
727 | out: | |
728 | return ret; | |
729 | } | |
730 | ||
731 | void rpl_nf_ct_frag6_cleanup(void) | |
732 | { | |
733 | unregister_pernet_subsys(&nf_ct_net_ops); | |
734 | inet_frags_fini(&nf_frags); | |
735 | } | |
736 | ||
34c3c2b7 | 737 | #endif /* OVS_NF_DEFRAG6_BACKPORT */ |