Commit b6eeb1e5 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPV4/IPV6]: Update ECN handling.

This patch brings the IP ECN handling up-to-date with repsect to
RFC 3168.  Mostly this means treating ECT(1) in the same way as
ECT(0).
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2cb5183d
......@@ -16,9 +16,9 @@ static inline int INET_ECN_is_ce(__u8 dsfield)
return (dsfield & INET_ECN_MASK) == INET_ECN_CE;
}
static inline int INET_ECN_is_not_ce(__u8 dsfield)
static inline int INET_ECN_is_not_ect(__u8 dsfield)
{
return (dsfield & INET_ECN_MASK) == INET_ECN_ECT_0;
return (dsfield & INET_ECN_MASK) == INET_ECN_NOT_ECT;
}
static inline int INET_ECN_is_capable(__u8 dsfield)
......@@ -29,8 +29,7 @@ static inline int INET_ECN_is_capable(__u8 dsfield)
static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
{
outer &= ~INET_ECN_MASK;
if (INET_ECN_is_capable(inner))
outer |= (inner & INET_ECN_MASK);
outer |= (inner & INET_ECN_MASK) ?: INET_ECN_ECT_0;
return outer;
}
......@@ -50,7 +49,19 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
static inline void IP_ECN_set_ce(struct iphdr *iph)
{
u32 check = iph->check;
check += __constant_htons(0xFFFE);
switch (iph->tos & INET_ECN_MASK) {
default:
case INET_ECN_NOT_ECT:
case INET_ECN_CE:
return;
case INET_ECN_ECT_1:
check += __constant_htons(0xFFFD);
break;
case INET_ECN_ECT_0:
check += __constant_htons(0xFFFE);
break;
}
iph->check = check + (check>=0xFFFF);
iph->tos |= INET_ECN_CE;
}
......@@ -60,10 +71,14 @@ static inline void IP_ECN_clear(struct iphdr *iph)
iph->tos &= ~INET_ECN_MASK;
}
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
struct ipv6hdr;
static inline void IP6_ECN_set_ce(struct ipv6hdr *iph)
{
if (INET_ECN_is_not_ect(ip6_get_dsfield(iph)))
return;
*(u32*)iph |= htonl(INET_ECN_CE << 20);
}
......@@ -72,6 +87,4 @@ static inline void IP6_ECN_clear(struct ipv6hdr *iph)
*(u32*)iph &= ~htonl(INET_ECN_MASK << 20);
}
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
#endif
......@@ -90,7 +90,7 @@ TCP_ECN_check_ce(struct tcp_opt *tp, struct sk_buff *skb)
/* Funny extension: if ECT is not set on a segment,
* it is surely retransmit. It is not in ECN RFC,
* but Linux follows this rule. */
else if (!INET_ECN_is_capable((TCP_SKB_CB(skb)->flags)))
else if (INET_ECN_is_not_ect((TCP_SKB_CB(skb)->flags)))
tcp_enter_quickack_mode(tp);
}
}
......
......@@ -533,11 +533,9 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
{
if (INET_ECN_is_ce(iph->tos)) {
if (skb->protocol == htons(ETH_P_IP)) {
if (INET_ECN_is_not_ce(skb->nh.iph->tos))
IP_ECN_set_ce(skb->nh.iph);
IP_ECN_set_ce(skb->nh.iph);
} else if (skb->protocol == htons(ETH_P_IPV6)) {
if (INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
IP6_ECN_set_ce(skb->nh.ipv6h);
IP6_ECN_set_ce(skb->nh.ipv6h);
}
}
}
......
......@@ -461,8 +461,7 @@ static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff
{
struct iphdr *inner_iph = skb->nh.iph;
if (INET_ECN_is_ce(outer_iph->tos) &&
INET_ECN_is_not_ce(inner_iph->tos))
if (INET_ECN_is_ce(outer_iph->tos))
IP_ECN_set_ce(inner_iph);
}
......
......@@ -24,8 +24,7 @@ static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
struct iphdr *outer_iph = skb->nh.iph;
struct iphdr *inner_iph = skb->h.ipiph;
if (INET_ECN_is_ce(outer_iph->tos) &&
INET_ECN_is_not_ce(inner_iph->tos))
if (INET_ECN_is_ce(outer_iph->tos))
IP_ECN_set_ce(inner_iph);
}
......
......@@ -360,8 +360,7 @@ static void ipip6_err(struct sk_buff *skb, u32 info)
static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
{
if (INET_ECN_is_ce(iph->tos) &&
INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
if (INET_ECN_is_ce(iph->tos))
IP6_ECN_set_ce(skb->nh.ipv6h);
}
......
......@@ -21,8 +21,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
struct ipv6hdr *outer_iph = skb->nh.ipv6h;
struct ipv6hdr *inner_iph = skb->h.ipv6h;
if (INET_ECN_is_ce(ip6_get_dsfield(outer_iph)) &&
INET_ECN_is_not_ce(ip6_get_dsfield(inner_iph)))
if (INET_ECN_is_ce(ip6_get_dsfield(outer_iph)))
IP6_ECN_set_ce(inner_iph);
}
......
......@@ -162,13 +162,12 @@ static int red_ecn_mark(struct sk_buff *skb)
switch (skb->protocol) {
case __constant_htons(ETH_P_IP):
if (!INET_ECN_is_capable(skb->nh.iph->tos))
if (INET_ECN_is_not_ect(skb->nh.iph->tos))
return 0;
if (INET_ECN_is_not_ce(skb->nh.iph->tos))
IP_ECN_set_ce(skb->nh.iph);
IP_ECN_set_ce(skb->nh.iph);
return 1;
case __constant_htons(ETH_P_IPV6):
if (!INET_ECN_is_capable(ip6_get_dsfield(skb->nh.ipv6h)))
if (INET_ECN_is_not_ect(ip6_get_dsfield(skb->nh.ipv6h)))
return 0;
IP6_ECN_set_ce(skb->nh.ipv6h);
return 1;
......
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