diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/ip_fragment.c | 60 |
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 | */ | ||
84 | enum { | ||
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 | ||
83 | static inline u8 ip4_frag_ecn(u8 tos) | 91 | static 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 | */ | ||
100 | static 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 | |||
96 | static struct inet_frags ip4_frags; | 116 | static struct inet_frags ip4_frags; |
97 | 117 | ||
98 | int ip_frag_nqueues(struct net *net) | 118 | int 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; |