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)
        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;
        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;