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) ...@@ -16,9 +16,9 @@ static inline int INET_ECN_is_ce(__u8 dsfield)
return (dsfield & INET_ECN_MASK) == INET_ECN_CE; 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) static inline int INET_ECN_is_capable(__u8 dsfield)
...@@ -29,8 +29,7 @@ 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) static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
{ {
outer &= ~INET_ECN_MASK; outer &= ~INET_ECN_MASK;
if (INET_ECN_is_capable(inner)) outer |= (inner & INET_ECN_MASK) ?: INET_ECN_ECT_0;
outer |= (inner & INET_ECN_MASK);
return outer; return outer;
} }
...@@ -50,7 +49,19 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner) ...@@ -50,7 +49,19 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
static inline void IP_ECN_set_ce(struct iphdr *iph) static inline void IP_ECN_set_ce(struct iphdr *iph)
{ {
u32 check = iph->check; u32 check = iph->check;
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); check += __constant_htons(0xFFFE);
break;
}
iph->check = check + (check>=0xFFFF); iph->check = check + (check>=0xFFFF);
iph->tos |= INET_ECN_CE; iph->tos |= INET_ECN_CE;
} }
...@@ -60,10 +71,14 @@ static inline void IP_ECN_clear(struct iphdr *iph) ...@@ -60,10 +71,14 @@ static inline void IP_ECN_clear(struct iphdr *iph)
iph->tos &= ~INET_ECN_MASK; iph->tos &= ~INET_ECN_MASK;
} }
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
struct ipv6hdr; struct ipv6hdr;
static inline void IP6_ECN_set_ce(struct ipv6hdr *iph) 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); *(u32*)iph |= htonl(INET_ECN_CE << 20);
} }
...@@ -72,6 +87,4 @@ static inline void IP6_ECN_clear(struct ipv6hdr *iph) ...@@ -72,6 +87,4 @@ static inline void IP6_ECN_clear(struct ipv6hdr *iph)
*(u32*)iph &= ~htonl(INET_ECN_MASK << 20); *(u32*)iph &= ~htonl(INET_ECN_MASK << 20);
} }
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
#endif #endif
...@@ -90,7 +90,7 @@ TCP_ECN_check_ce(struct tcp_opt *tp, struct sk_buff *skb) ...@@ -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, /* Funny extension: if ECT is not set on a segment,
* it is surely retransmit. It is not in ECN RFC, * it is surely retransmit. It is not in ECN RFC,
* but Linux follows this rule. */ * 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); tcp_enter_quickack_mode(tp);
} }
} }
......
...@@ -533,10 +533,8 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb) ...@@ -533,10 +533,8 @@ static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
{ {
if (INET_ECN_is_ce(iph->tos)) { if (INET_ECN_is_ce(iph->tos)) {
if (skb->protocol == htons(ETH_P_IP)) { 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)) { } 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 ...@@ -461,8 +461,7 @@ static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff
{ {
struct iphdr *inner_iph = skb->nh.iph; struct iphdr *inner_iph = skb->nh.iph;
if (INET_ECN_is_ce(outer_iph->tos) && if (INET_ECN_is_ce(outer_iph->tos))
INET_ECN_is_not_ce(inner_iph->tos))
IP_ECN_set_ce(inner_iph); IP_ECN_set_ce(inner_iph);
} }
......
...@@ -24,8 +24,7 @@ static inline void ipip_ecn_decapsulate(struct sk_buff *skb) ...@@ -24,8 +24,7 @@ static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
struct iphdr *outer_iph = skb->nh.iph; struct iphdr *outer_iph = skb->nh.iph;
struct iphdr *inner_iph = skb->h.ipiph; struct iphdr *inner_iph = skb->h.ipiph;
if (INET_ECN_is_ce(outer_iph->tos) && if (INET_ECN_is_ce(outer_iph->tos))
INET_ECN_is_not_ce(inner_iph->tos))
IP_ECN_set_ce(inner_iph); IP_ECN_set_ce(inner_iph);
} }
......
...@@ -360,8 +360,7 @@ static void ipip6_err(struct sk_buff *skb, u32 info) ...@@ -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) static inline void ipip6_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
{ {
if (INET_ECN_is_ce(iph->tos) && if (INET_ECN_is_ce(iph->tos))
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);
} }
......
...@@ -21,8 +21,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) ...@@ -21,8 +21,7 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
struct ipv6hdr *outer_iph = skb->nh.ipv6h; struct ipv6hdr *outer_iph = skb->nh.ipv6h;
struct ipv6hdr *inner_iph = skb->h.ipv6h; struct ipv6hdr *inner_iph = skb->h.ipv6h;
if (INET_ECN_is_ce(ip6_get_dsfield(outer_iph)) && if (INET_ECN_is_ce(ip6_get_dsfield(outer_iph)))
INET_ECN_is_not_ce(ip6_get_dsfield(inner_iph)))
IP6_ECN_set_ce(inner_iph); IP6_ECN_set_ce(inner_iph);
} }
......
...@@ -162,13 +162,12 @@ static int red_ecn_mark(struct sk_buff *skb) ...@@ -162,13 +162,12 @@ static int red_ecn_mark(struct sk_buff *skb)
switch (skb->protocol) { switch (skb->protocol) {
case __constant_htons(ETH_P_IP): 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; 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; return 1;
case __constant_htons(ETH_P_IPV6): 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; return 0;
IP6_ECN_set_ce(skb->nh.ipv6h); IP6_ECN_set_ce(skb->nh.ipv6h);
return 1; 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