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: eccc1bb8d4 ("tunnel: drop packet if ECN present with not-ECT")
Reported-by: Bob Briscoe <ietf@bobbriscoe.net>
Reported-by: Olivier Tilmans <olivier.tilmans@nokia-bell-labs.com>
Cc: Dave Taht <dave.taht@gmail.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Toke Høiland-Jørgensen 2020-04-27 16:11:05 +02:00 коммит произвёл David S. Miller
Родитель 0ce205d466
Коммит b723748750
1 изменённых файлов: 55 добавлений и 2 удалений

Просмотреть файл

@ -99,6 +99,20 @@ static inline int IP_ECN_set_ce(struct iphdr *iph)
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)
{
iph->tos &= ~INET_ECN_MASK;
@ -134,6 +148,22 @@ static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
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)
{
dscp &= ~INET_ECN_MASK;
@ -159,6 +189,25 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
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
* 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,
int rc;
rc = __INET_ECN_decapsulate(outer, inner, &set_ce);
if (!rc && set_ce)
INET_ECN_set_ce(skb);
if (!rc) {
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;
}