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