aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-05-16 04:37:37 -0400
committerDavid S. Miller <davem@davemloft.net>2011-05-16 14:49:14 -0400
commit5173cc057787560c127c6e9737f308c833dc4ff3 (patch)
tree3ac8450239e5e9ee99e1a4bbef1626401dd76be9 /net/ipv4
parent485bf569ba798b4702bc2efbfd3a355fe2c8db04 (diff)
ipv4: more compliant RFC 3168 support
Commit 6623e3b24a5e (ipv4: IP defragmentation must be ECN aware) was an attempt to not lose "Congestion Experienced" (CE) indications when performing datagram defragmentation. Stefanos Harhalakis raised the point that RFC 3168 requirements were not completely met by this commit. In particular, we MUST detect invalid combinations and eventually drop illegal frames. Reported-by: Stefanos Harhalakis <v13@v13.gr> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r--net/ipv4/ip_fragment.c60
1 files changed, 38 insertions, 22 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index b1d282f11be7..9e1458d3e465 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -77,22 +77,42 @@ struct ipq {
77 struct inet_peer *peer; 77 struct inet_peer *peer;
78}; 78};
79 79
80#define IPFRAG_ECN_CLEAR 0x01 /* one frag had INET_ECN_NOT_ECT */ 80/* RFC 3168 support :
81#define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */ 81 * We want to check ECN values of all fragments, do detect invalid combinations.
82 * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
83 */
84enum {
85 IPFRAG_ECN_NOT_ECT = 0x01, /* one frag had ECN_NOT_ECT */
86 IPFRAG_ECN_ECT_1 = 0x02, /* one frag had ECN_ECT_1 */
87 IPFRAG_ECN_ECT_0 = 0x04, /* one frag had ECN_ECT_0 */
88 IPFRAG_ECN_CE = 0x08, /* one frag had ECN_CE */
89};
82 90
83static inline u8 ip4_frag_ecn(u8 tos) 91static inline u8 ip4_frag_ecn(u8 tos)
84{ 92{
85 tos = (tos & INET_ECN_MASK) + 1; 93 return 1 << (tos & INET_ECN_MASK);
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} 94}
95 95
96/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
97 * Value : 0xff if frame should be dropped.
98 * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field
99 */
100static const u8 ip4_frag_ecn_table[16] = {
101 /* at least one fragment had CE, and others ECT_0 or ECT_1 */
102 [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = INET_ECN_CE,
103 [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = INET_ECN_CE,
104 [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = INET_ECN_CE,
105
106 /* invalid combinations : drop frame */
107 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff,
108 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff,
109 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff,
110 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
111 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff,
112 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff,
113 [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
114};
115
96static struct inet_frags ip4_frags; 116static struct inet_frags ip4_frags;
97 117
98int ip_frag_nqueues(struct net *net) 118int ip_frag_nqueues(struct net *net)
@@ -524,9 +544,15 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
524 int len; 544 int len;
525 int ihlen; 545 int ihlen;
526 int err; 546 int err;
547 u8 ecn;
527 548
528 ipq_kill(qp); 549 ipq_kill(qp);
529 550
551 ecn = ip4_frag_ecn_table[qp->ecn];
552 if (unlikely(ecn == 0xff)) {
553 err = -EINVAL;
554 goto out_fail;
555 }
530 /* Make the one we just received the head. */ 556 /* Make the one we just received the head. */
531 if (prev) { 557 if (prev) {
532 head = prev->next; 558 head = prev->next;
@@ -605,17 +631,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
605 iph = ip_hdr(head); 631 iph = ip_hdr(head);
606 iph->frag_off = 0; 632 iph->frag_off = 0;
607 iph->tot_len = htons(len); 633 iph->tot_len = htons(len);
608 /* RFC3168 5.3 Fragmentation support 634 iph->tos |= ecn;
609 * If one fragment had INET_ECN_NOT_ECT,
610 * reassembled frame also has INET_ECN_NOT_ECT
611 * Elif one fragment had INET_ECN_CE
612 * reassembled frame also has INET_ECN_CE
613 */
614 if (qp->ecn & IPFRAG_ECN_CLEAR)
615 iph->tos &= ~INET_ECN_MASK;
616 else if (qp->ecn & IPFRAG_ECN_SET_CE)
617 iph->tos |= INET_ECN_CE;
618
619 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); 635 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
620 qp->q.fragments = NULL; 636 qp->q.fragments = NULL;
621 qp->q.fragments_tail = NULL; 637 qp->q.fragments_tail = NULL;