aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
diff options
context:
space:
mode:
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;