Commit b7237487 authored by Toke Høiland-Jørgensen's avatar Toke Høiland-Jørgensen Committed by David S. Miller

tunnel: Propagate ECT(1) when decapsulating as recommended by RFC6040

RFC 6040 recommends propagating an ECT(1) mark from an outer tunnel header
to the inner header if that inner header is already marked as ECT(0). When
RFC 6040 decapsulation was implemented, this case of propagation was not
added. This simply appears to be an oversight, so let's fix that.

Fixes: eccc1bb8 ("tunnel: drop packet if ECN present with not-ECT")
Reported-by: default avatarBob Briscoe <ietf@bobbriscoe.net>
Reported-by: default avatarOlivier Tilmans <olivier.tilmans@nokia-bell-labs.com>
Cc: Dave Taht <dave.taht@gmail.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: default avatarToke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0ce205d4
...@@ -99,6 +99,20 @@ static inline int IP_ECN_set_ce(struct iphdr *iph) ...@@ -99,6 +99,20 @@ static inline int IP_ECN_set_ce(struct iphdr *iph)
return 1; return 1;
} }
static inline int IP_ECN_set_ect1(struct iphdr *iph)
{
u32 check = (__force u32)iph->check;
if ((iph->tos & INET_ECN_MASK) != INET_ECN_ECT_0)
return 0;
check += (__force u16)htons(0x100);
iph->check = (__force __sum16)(check + (check>=0xFFFF));
iph->tos ^= INET_ECN_MASK;
return 1;
}
static inline void IP_ECN_clear(struct iphdr *iph) static inline void IP_ECN_clear(struct iphdr *iph)
{ {
iph->tos &= ~INET_ECN_MASK; iph->tos &= ~INET_ECN_MASK;
...@@ -134,6 +148,22 @@ static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph) ...@@ -134,6 +148,22 @@ static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
return 1; return 1;
} }
static inline int IP6_ECN_set_ect1(struct sk_buff *skb, struct ipv6hdr *iph)
{
__be32 from, to;
if ((ipv6_get_dsfield(iph) & INET_ECN_MASK) != INET_ECN_ECT_0)
return 0;
from = *(__be32 *)iph;
to = from ^ htonl(INET_ECN_MASK << 20);
*(__be32 *)iph = to;
if (skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from),
(__force __wsum)to);
return 1;
}
static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner) static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner)
{ {
dscp &= ~INET_ECN_MASK; dscp &= ~INET_ECN_MASK;
...@@ -159,6 +189,25 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) ...@@ -159,6 +189,25 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
return 0; return 0;
} }
static inline int INET_ECN_set_ect1(struct sk_buff *skb)
{
switch (skb->protocol) {
case cpu_to_be16(ETH_P_IP):
if (skb_network_header(skb) + sizeof(struct iphdr) <=
skb_tail_pointer(skb))
return IP_ECN_set_ect1(ip_hdr(skb));
break;
case cpu_to_be16(ETH_P_IPV6):
if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
skb_tail_pointer(skb))
return IP6_ECN_set_ect1(skb, ipv6_hdr(skb));
break;
}
return 0;
}
/* /*
* RFC 6040 4.2 * RFC 6040 4.2
* To decapsulate the inner header at the tunnel egress, a compliant * To decapsulate the inner header at the tunnel egress, a compliant
...@@ -208,8 +257,12 @@ static inline int INET_ECN_decapsulate(struct sk_buff *skb, ...@@ -208,8 +257,12 @@ static inline int INET_ECN_decapsulate(struct sk_buff *skb,
int rc; int rc;
rc = __INET_ECN_decapsulate(outer, inner, &set_ce); rc = __INET_ECN_decapsulate(outer, inner, &set_ce);
if (!rc && set_ce) if (!rc) {
INET_ECN_set_ce(skb); if (set_ce)
INET_ECN_set_ce(skb);
else if ((outer & INET_ECN_MASK) == INET_ECN_ECT_1)
INET_ECN_set_ect1(skb);
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment