|
@@ -77,22 +77,42 @@ struct ipq {
|
|
|
struct inet_peer *peer;
|
|
|
};
|
|
|
|
|
|
-#define IPFRAG_ECN_CLEAR 0x01 /* one frag had INET_ECN_NOT_ECT */
|
|
|
-#define IPFRAG_ECN_SET_CE 0x04 /* one frag had INET_ECN_CE */
|
|
|
+/* RFC 3168 support :
|
|
|
+ * We want to check ECN values of all fragments, do detect invalid combinations.
|
|
|
+ * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value.
|
|
|
+ */
|
|
|
+enum {
|
|
|
+ IPFRAG_ECN_NOT_ECT = 0x01, /* one frag had ECN_NOT_ECT */
|
|
|
+ IPFRAG_ECN_ECT_1 = 0x02, /* one frag had ECN_ECT_1 */
|
|
|
+ IPFRAG_ECN_ECT_0 = 0x04, /* one frag had ECN_ECT_0 */
|
|
|
+ IPFRAG_ECN_CE = 0x08, /* one frag had ECN_CE */
|
|
|
+};
|
|
|
|
|
|
static inline u8 ip4_frag_ecn(u8 tos)
|
|
|
{
|
|
|
- tos = (tos & INET_ECN_MASK) + 1;
|
|
|
- /*
|
|
|
- * After the last operation we have (in binary):
|
|
|
- * INET_ECN_NOT_ECT => 001
|
|
|
- * INET_ECN_ECT_1 => 010
|
|
|
- * INET_ECN_ECT_0 => 011
|
|
|
- * INET_ECN_CE => 100
|
|
|
- */
|
|
|
- return (tos & 2) ? 0 : tos;
|
|
|
+ return 1 << (tos & INET_ECN_MASK);
|
|
|
}
|
|
|
|
|
|
+/* Given the OR values of all fragments, apply RFC 3168 5.3 requirements
|
|
|
+ * Value : 0xff if frame should be dropped.
|
|
|
+ * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field
|
|
|
+ */
|
|
|
+static const u8 ip4_frag_ecn_table[16] = {
|
|
|
+ /* at least one fragment had CE, and others ECT_0 or ECT_1 */
|
|
|
+ [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = INET_ECN_CE,
|
|
|
+ [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = INET_ECN_CE,
|
|
|
+ [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = INET_ECN_CE,
|
|
|
+
|
|
|
+ /* invalid combinations : drop frame */
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff,
|
|
|
+ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff,
|
|
|
+};
|
|
|
+
|
|
|
static struct inet_frags ip4_frags;
|
|
|
|
|
|
int ip_frag_nqueues(struct net *net)
|
|
@@ -524,9 +544,15 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
|
|
|
int len;
|
|
|
int ihlen;
|
|
|
int err;
|
|
|
+ u8 ecn;
|
|
|
|
|
|
ipq_kill(qp);
|
|
|
|
|
|
+ ecn = ip4_frag_ecn_table[qp->ecn];
|
|
|
+ if (unlikely(ecn == 0xff)) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_fail;
|
|
|
+ }
|
|
|
/* Make the one we just received the head. */
|
|
|
if (prev) {
|
|
|
head = prev->next;
|
|
@@ -605,17 +631,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
|
|
|
iph = ip_hdr(head);
|
|
|
iph->frag_off = 0;
|
|
|
iph->tot_len = htons(len);
|
|
|
- /* RFC3168 5.3 Fragmentation support
|
|
|
- * If one fragment had INET_ECN_NOT_ECT,
|
|
|
- * reassembled frame also has INET_ECN_NOT_ECT
|
|
|
- * Elif one fragment had INET_ECN_CE
|
|
|
- * reassembled frame also has INET_ECN_CE
|
|
|
- */
|
|
|
- if (qp->ecn & IPFRAG_ECN_CLEAR)
|
|
|
- iph->tos &= ~INET_ECN_MASK;
|
|
|
- else if (qp->ecn & IPFRAG_ECN_SET_CE)
|
|
|
- iph->tos |= INET_ECN_CE;
|
|
|
-
|
|
|
+ iph->tos |= ecn;
|
|
|
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
|
|
|
qp->q.fragments = NULL;
|
|
|
qp->q.fragments_tail = NULL;
|