diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-03-24 11:20:51 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-04-06 07:06:37 -0400 |
commit | b8dd6a223eb86d537c2c6d8d28916c1f0ba3ea3c (patch) | |
tree | d7c2e2a7a34357f3fcaa9f5aa93a2281523736c3 /net/ipv6 | |
parent | 12202fa7573d32aa0915cde6e8fab4c86b63ca2c (diff) |
netfilter: implement RFC3168 5.3 (ecn protection) for ipv6 fragmentation handling
This change brings netfilter reassembly logic on par with
reassembly.c. The corresponding change in net-next is
(eec2e61 ipv6: implement RFC3168 5.3 (ecn protection) for
ipv6 fragmentation handling)
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Jesper Dangaard Brouer <jbrouer@redhat.com>
Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 6700069949dd..dffdc1a389c5 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <net/rawv6.h> | 41 | #include <net/rawv6.h> |
42 | #include <net/ndisc.h> | 42 | #include <net/ndisc.h> |
43 | #include <net/addrconf.h> | 43 | #include <net/addrconf.h> |
44 | #include <net/inet_ecn.h> | ||
44 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | 45 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
45 | #include <linux/sysctl.h> | 46 | #include <linux/sysctl.h> |
46 | #include <linux/netfilter.h> | 47 | #include <linux/netfilter.h> |
@@ -138,6 +139,11 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net) | |||
138 | } | 139 | } |
139 | #endif | 140 | #endif |
140 | 141 | ||
142 | static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) | ||
143 | { | ||
144 | return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK); | ||
145 | } | ||
146 | |||
141 | static unsigned int nf_hashfn(struct inet_frag_queue *q) | 147 | static unsigned int nf_hashfn(struct inet_frag_queue *q) |
142 | { | 148 | { |
143 | const struct frag_queue *nq; | 149 | const struct frag_queue *nq; |
@@ -166,7 +172,7 @@ static void nf_ct_frag6_expire(unsigned long data) | |||
166 | /* Creation primitives. */ | 172 | /* Creation primitives. */ |
167 | static inline struct frag_queue *fq_find(struct net *net, __be32 id, | 173 | static inline struct frag_queue *fq_find(struct net *net, __be32 id, |
168 | u32 user, struct in6_addr *src, | 174 | u32 user, struct in6_addr *src, |
169 | struct in6_addr *dst) | 175 | struct in6_addr *dst, u8 ecn) |
170 | { | 176 | { |
171 | struct inet_frag_queue *q; | 177 | struct inet_frag_queue *q; |
172 | struct ip6_create_arg arg; | 178 | struct ip6_create_arg arg; |
@@ -176,6 +182,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id, | |||
176 | arg.user = user; | 182 | arg.user = user; |
177 | arg.src = src; | 183 | arg.src = src; |
178 | arg.dst = dst; | 184 | arg.dst = dst; |
185 | arg.ecn = ecn; | ||
179 | 186 | ||
180 | read_lock_bh(&nf_frags.lock); | 187 | read_lock_bh(&nf_frags.lock); |
181 | hash = inet6_hash_frag(id, src, dst, nf_frags.rnd); | 188 | hash = inet6_hash_frag(id, src, dst, nf_frags.rnd); |
@@ -196,6 +203,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
196 | struct sk_buff *prev, *next; | 203 | struct sk_buff *prev, *next; |
197 | unsigned int payload_len; | 204 | unsigned int payload_len; |
198 | int offset, end; | 205 | int offset, end; |
206 | u8 ecn; | ||
199 | 207 | ||
200 | if (fq->q.last_in & INET_FRAG_COMPLETE) { | 208 | if (fq->q.last_in & INET_FRAG_COMPLETE) { |
201 | pr_debug("Already completed\n"); | 209 | pr_debug("Already completed\n"); |
@@ -213,6 +221,8 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
213 | return -1; | 221 | return -1; |
214 | } | 222 | } |
215 | 223 | ||
224 | ecn = ip6_frag_ecn(ipv6_hdr(skb)); | ||
225 | |||
216 | if (skb->ip_summed == CHECKSUM_COMPLETE) { | 226 | if (skb->ip_summed == CHECKSUM_COMPLETE) { |
217 | const unsigned char *nh = skb_network_header(skb); | 227 | const unsigned char *nh = skb_network_header(skb); |
218 | skb->csum = csum_sub(skb->csum, | 228 | skb->csum = csum_sub(skb->csum, |
@@ -317,6 +327,7 @@ found: | |||
317 | } | 327 | } |
318 | fq->q.stamp = skb->tstamp; | 328 | fq->q.stamp = skb->tstamp; |
319 | fq->q.meat += skb->len; | 329 | fq->q.meat += skb->len; |
330 | fq->ecn |= ecn; | ||
320 | if (payload_len > fq->q.max_size) | 331 | if (payload_len > fq->q.max_size) |
321 | fq->q.max_size = payload_len; | 332 | fq->q.max_size = payload_len; |
322 | add_frag_mem_limit(&fq->q, skb->truesize); | 333 | add_frag_mem_limit(&fq->q, skb->truesize); |
@@ -352,12 +363,17 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) | |||
352 | { | 363 | { |
353 | struct sk_buff *fp, *op, *head = fq->q.fragments; | 364 | struct sk_buff *fp, *op, *head = fq->q.fragments; |
354 | int payload_len; | 365 | int payload_len; |
366 | u8 ecn; | ||
355 | 367 | ||
356 | inet_frag_kill(&fq->q, &nf_frags); | 368 | inet_frag_kill(&fq->q, &nf_frags); |
357 | 369 | ||
358 | WARN_ON(head == NULL); | 370 | WARN_ON(head == NULL); |
359 | WARN_ON(NFCT_FRAG6_CB(head)->offset != 0); | 371 | WARN_ON(NFCT_FRAG6_CB(head)->offset != 0); |
360 | 372 | ||
373 | ecn = ip_frag_ecn_table[fq->ecn]; | ||
374 | if (unlikely(ecn == 0xff)) | ||
375 | goto out_fail; | ||
376 | |||
361 | /* Unfragmented part is taken from the first segment. */ | 377 | /* Unfragmented part is taken from the first segment. */ |
362 | payload_len = ((head->data - skb_network_header(head)) - | 378 | payload_len = ((head->data - skb_network_header(head)) - |
363 | sizeof(struct ipv6hdr) + fq->q.len - | 379 | sizeof(struct ipv6hdr) + fq->q.len - |
@@ -428,6 +444,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) | |||
428 | head->dev = dev; | 444 | head->dev = dev; |
429 | head->tstamp = fq->q.stamp; | 445 | head->tstamp = fq->q.stamp; |
430 | ipv6_hdr(head)->payload_len = htons(payload_len); | 446 | ipv6_hdr(head)->payload_len = htons(payload_len); |
447 | ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); | ||
431 | IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; | 448 | IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; |
432 | 449 | ||
433 | /* Yes, and fold redundant checksum back. 8) */ | 450 | /* Yes, and fold redundant checksum back. 8) */ |
@@ -572,7 +589,8 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user) | |||
572 | inet_frag_evictor(&net->nf_frag.frags, &nf_frags, false); | 589 | inet_frag_evictor(&net->nf_frag.frags, &nf_frags, false); |
573 | local_bh_enable(); | 590 | local_bh_enable(); |
574 | 591 | ||
575 | fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr); | 592 | fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, |
593 | ip6_frag_ecn(hdr)); | ||
576 | if (fq == NULL) { | 594 | if (fq == NULL) { |
577 | pr_debug("Can't find and can't create new queue\n"); | 595 | pr_debug("Can't find and can't create new queue\n"); |
578 | goto ret_orig; | 596 | goto ret_orig; |