diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-02-02 23:22:20 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-02-02 23:22:20 -0300 |
commit | 5c545e1fb127a4b11ddc5f1a5ed066b853dd1a1a (patch) | |
tree | d4cd913bc79d37d32756a9bffbeedabf93e32579 /include/net/inet_ecn.h | |
parent | b4b7ff4b08e691656c9d77c758fc355833128ac0 (diff) |
Linux-libre 4.4.1-gnupck-4.4.1-gnu
Diffstat (limited to 'include/net/inet_ecn.h')
-rw-r--r-- | include/net/inet_ecn.h | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 84b20835b..0dc0a51da 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -111,11 +111,24 @@ static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner) struct ipv6hdr; -static inline int IP6_ECN_set_ce(struct ipv6hdr *iph) +/* Note: + * IP_ECN_set_ce() has to tweak IPV4 checksum when setting CE, + * meaning both changes have no effect on skb->csum if/when CHECKSUM_COMPLETE + * In IPv6 case, no checksum compensates the change in IPv6 header, + * so we have to update skb->csum. + */ +static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph) { + __be32 from, to; + if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph))) return 0; - *(__be32*)iph |= htonl(INET_ECN_CE << 20); + + from = *(__be32 *)iph; + to = from | htonl(INET_ECN_CE << 20); + *(__be32 *)iph = to; + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(csum_sub(skb->csum, from), to); return 1; } @@ -142,7 +155,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) case cpu_to_be16(ETH_P_IPV6): if (skb_network_header(skb) + sizeof(struct ipv6hdr) <= skb_tail_pointer(skb)) - return IP6_ECN_set_ce(ipv6_hdr(skb)); + return IP6_ECN_set_ce(skb, ipv6_hdr(skb)); break; } |