aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-01-05 02:52:55 -0500
committerDavid S. Miller <davem@davemloft.net>2011-01-06 14:21:30 -0500
commit6623e3b24a5ebb07e81648c478d286a1329ab891 (patch)
treee3e558391cbad217925baab7f926ec7250a810d9 /net
parent9c86c0f4ba49b39f909d7f18731b91e563e07065 (diff)
ipv4: IP defragmentation must be ECN aware
RFC3168 (The Addition of Explicit Congestion Notification to IP) states : 5.3. Fragmentation ECN-capable packets MAY have the DF (Don't Fragment) bit set. Reassembly of a fragmented packet MUST NOT lose indications of congestion. In other words, if any fragment of an IP packet to be reassembled has the CE codepoint set, then one of two actions MUST be taken: * Set the CE codepoint on the reassembled packet. However, this MUST NOT occur if any of the other fragments contributing to this reassembly carries the Not-ECT codepoint. * The packet is dropped, instead of being reassembled, for any other reason. This patch implements this requirement for IPv4, choosing the first action : If one fragment had NO-ECT codepoint reassembled frame has NO-ECT ElIf one fragment had CE codepoint reassembled frame has CE Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/ip_fragment.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index e6215bdd96c0..a1151b8adf3c 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -45,6 +45,7 @@
45#include <linux/udp.h> 45#include <linux/udp.h>
46#include <linux/inet.h> 46#include <linux/inet.h>
47#include <linux/netfilter_ipv4.h> 47#include <linux/netfilter_ipv4.h>
48#include <net/inet_ecn.h>
48 49
49/* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 50/* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6
50 * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c 51 * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c
@@ -70,11 +71,28 @@ struct ipq {
70 __be32 daddr; 71 __be32 daddr;
71 __be16 id; 72 __be16 id;
72 u8 protocol; 73 u8 protocol;
74 u8 ecn; /* RFC3168 support */
73 int iif; 75 int iif;
74 unsigned int rid; 76 unsigned int rid;
75 struct inet_peer *peer; 77 struct inet_peer *peer;
76}; 78};
77 79
80#define IPFRAG_ECN_CLEAR 0x01 /* one frag had INET_ECN_NOT_ECT */
81#define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */
82
83static inline u8 ip4_frag_ecn(u8 tos)
84{
85 tos = (tos & INET_ECN_MASK) + 1;
86 /*
87 * After the last operation we have (in binary):
88 * INET_ECN_NOT_ECT => 001
89 * INET_ECN_ECT_1 => 010
90 * INET_ECN_ECT_0 => 011
91 * INET_ECN_CE => 100
92 */
93 return (tos & 2) ? 0 : tos;
94}
95
78static struct inet_frags ip4_frags; 96static struct inet_frags ip4_frags;
79 97
80int ip_frag_nqueues(struct net *net) 98int ip_frag_nqueues(struct net *net)
@@ -137,6 +155,7 @@ static void ip4_frag_init(struct inet_frag_queue *q, void *a)
137 155
138 qp->protocol = arg->iph->protocol; 156 qp->protocol = arg->iph->protocol;
139 qp->id = arg->iph->id; 157 qp->id = arg->iph->id;
158 qp->ecn = ip4_frag_ecn(arg->iph->tos);
140 qp->saddr = arg->iph->saddr; 159 qp->saddr = arg->iph->saddr;
141 qp->daddr = arg->iph->daddr; 160 qp->daddr = arg->iph->daddr;
142 qp->user = arg->user; 161 qp->user = arg->user;
@@ -316,6 +335,7 @@ static int ip_frag_reinit(struct ipq *qp)
316 qp->q.fragments = NULL; 335 qp->q.fragments = NULL;
317 qp->q.fragments_tail = NULL; 336 qp->q.fragments_tail = NULL;
318 qp->iif = 0; 337 qp->iif = 0;
338 qp->ecn = 0;
319 339
320 return 0; 340 return 0;
321} 341}
@@ -328,6 +348,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
328 int flags, offset; 348 int flags, offset;
329 int ihl, end; 349 int ihl, end;
330 int err = -ENOENT; 350 int err = -ENOENT;
351 u8 ecn;
331 352
332 if (qp->q.last_in & INET_FRAG_COMPLETE) 353 if (qp->q.last_in & INET_FRAG_COMPLETE)
333 goto err; 354 goto err;
@@ -339,6 +360,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb)
339 goto err; 360 goto err;
340 } 361 }
341 362
363 ecn = ip4_frag_ecn(ip_hdr(skb)->tos);
342 offset = ntohs(ip_hdr(skb)->frag_off); 364 offset = ntohs(ip_hdr(skb)->frag_off);
343 flags = offset & ~IP_OFFSET; 365 flags = offset & ~IP_OFFSET;
344 offset &= IP_OFFSET; 366 offset &= IP_OFFSET;
@@ -472,6 +494,7 @@ found:
472 } 494 }
473 qp->q.stamp = skb->tstamp; 495 qp->q.stamp = skb->tstamp;
474 qp->q.meat += skb->len; 496 qp->q.meat += skb->len;
497 qp->ecn |= ecn;
475 atomic_add(skb->truesize, &qp->q.net->mem); 498 atomic_add(skb->truesize, &qp->q.net->mem);
476 if (offset == 0) 499 if (offset == 0)
477 qp->q.last_in |= INET_FRAG_FIRST_IN; 500 qp->q.last_in |= INET_FRAG_FIRST_IN;
@@ -583,6 +606,17 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
583 iph = ip_hdr(head); 606 iph = ip_hdr(head);
584 iph->frag_off = 0; 607 iph->frag_off = 0;
585 iph->tot_len = htons(len); 608 iph->tot_len = htons(len);
609 /* RFC3168 5.3 Fragmentation support
610 * If one fragment had INET_ECN_NOT_ECT,
611 * reassembled frame also has INET_ECN_NOT_ECT
612 * Elif one fragment had INET_ECN_CE
613 * reassembled frame also has INET_ECN_CE
614 */
615 if (qp->ecn & IPFRAG_ECN_CLEAR)
616 iph->tos &= ~INET_ECN_MASK;
617 else if (qp->ecn & IPFRAG_ECN_SET_CE)
618 iph->tos |= INET_ECN_CE;
619
586 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); 620 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
587 qp->q.fragments = NULL; 621 qp->q.fragments = NULL;
588 qp->q.fragments_tail = NULL; 622 qp->q.fragments_tail = NULL;