linux/include/net/inet_ecn.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#ifndef _INET_ECN_H_
   3#define _INET_ECN_H_
   4
   5#include <linux/ip.h>
   6#include <linux/skbuff.h>
   7
   8#include <net/inet_sock.h>
   9#include <net/dsfield.h>
  10
  11enum {
  12        INET_ECN_NOT_ECT = 0,
  13        INET_ECN_ECT_1 = 1,
  14        INET_ECN_ECT_0 = 2,
  15        INET_ECN_CE = 3,
  16        INET_ECN_MASK = 3,
  17};
  18
  19extern int sysctl_tunnel_ecn_log;
  20
  21static inline int INET_ECN_is_ce(__u8 dsfield)
  22{
  23        return (dsfield & INET_ECN_MASK) == INET_ECN_CE;
  24}
  25
  26static inline int INET_ECN_is_not_ect(__u8 dsfield)
  27{
  28        return (dsfield & INET_ECN_MASK) == INET_ECN_NOT_ECT;
  29}
  30
  31static inline int INET_ECN_is_capable(__u8 dsfield)
  32{
  33        return dsfield & INET_ECN_ECT_0;
  34}
  35
  36/*
  37 * RFC 3168 9.1.1
  38 *  The full-functionality option for ECN encapsulation is to copy the
  39 *  ECN codepoint of the inside header to the outside header on
  40 *  encapsulation if the inside header is not-ECT or ECT, and to set the
  41 *  ECN codepoint of the outside header to ECT(0) if the ECN codepoint of
  42 *  the inside header is CE.
  43 */
  44static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
  45{
  46        outer &= ~INET_ECN_MASK;
  47        outer |= !INET_ECN_is_ce(inner) ? (inner & INET_ECN_MASK) :
  48                                          INET_ECN_ECT_0;
  49        return outer;
  50}
  51
  52static inline void INET_ECN_xmit(struct sock *sk)
  53{
  54        inet_sk(sk)->tos |= INET_ECN_ECT_0;
  55        if (inet6_sk(sk) != NULL)
  56                inet6_sk(sk)->tclass |= INET_ECN_ECT_0;
  57}
  58
  59static inline void INET_ECN_dontxmit(struct sock *sk)
  60{
  61        inet_sk(sk)->tos &= ~INET_ECN_MASK;
  62        if (inet6_sk(sk) != NULL)
  63                inet6_sk(sk)->tclass &= ~INET_ECN_MASK;
  64}
  65
  66#define IP6_ECN_flow_init(label) do {           \
  67      (label) &= ~htonl(INET_ECN_MASK << 20);   \
  68    } while (0)
  69
  70#define IP6_ECN_flow_xmit(sk, label) do {                               \
  71        if (INET_ECN_is_capable(inet6_sk(sk)->tclass))                  \
  72                (label) |= htonl(INET_ECN_ECT_0 << 20);                 \
  73    } while (0)
  74
  75static inline int IP_ECN_set_ce(struct iphdr *iph)
  76{
  77        u32 check = (__force u32)iph->check;
  78        u32 ecn = (iph->tos + 1) & INET_ECN_MASK;
  79
  80        /*
  81         * After the last operation we have (in binary):
  82         * INET_ECN_NOT_ECT => 01
  83         * INET_ECN_ECT_1   => 10
  84         * INET_ECN_ECT_0   => 11
  85         * INET_ECN_CE      => 00
  86         */
  87        if (!(ecn & 2))
  88                return !ecn;
  89
  90        /*
  91         * The following gives us:
  92         * INET_ECN_ECT_1 => check += htons(0xFFFD)
  93         * INET_ECN_ECT_0 => check += htons(0xFFFE)
  94         */
  95        check += (__force u16)htons(0xFFFB) + (__force u16)htons(ecn);
  96
  97        iph->check = (__force __sum16)(check + (check>=0xFFFF));
  98        iph->tos |= INET_ECN_CE;
  99        return 1;
 100}
 101
 102static inline void IP_ECN_clear(struct iphdr *iph)
 103{
 104        iph->tos &= ~INET_ECN_MASK;
 105}
 106
 107static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner)
 108{
 109        dscp &= ~INET_ECN_MASK;
 110        ipv4_change_dsfield(inner, INET_ECN_MASK, dscp);
 111}
 112
 113struct ipv6hdr;
 114
 115/* Note:
 116 * IP_ECN_set_ce() has to tweak IPV4 checksum when setting CE,
 117 * meaning both changes have no effect on skb->csum if/when CHECKSUM_COMPLETE
 118 * In IPv6 case, no checksum compensates the change in IPv6 header,
 119 * so we have to update skb->csum.
 120 */
 121static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
 122{
 123        __be32 from, to;
 124
 125        if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph)))
 126                return 0;
 127
 128        from = *(__be32 *)iph;
 129        to = from | htonl(INET_ECN_CE << 20);
 130        *(__be32 *)iph = to;
 131        if (skb->ip_summed == CHECKSUM_COMPLETE)
 132                skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from),
 133                                     (__force __wsum)to);
 134        return 1;
 135}
 136
 137static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner)
 138{
 139        dscp &= ~INET_ECN_MASK;
 140        ipv6_change_dsfield(inner, INET_ECN_MASK, dscp);
 141}
 142
 143static inline int INET_ECN_set_ce(struct sk_buff *skb)
 144{
 145        switch (skb->protocol) {
 146        case cpu_to_be16(ETH_P_IP):
 147                if (skb_network_header(skb) + sizeof(struct iphdr) <=
 148                    skb_tail_pointer(skb))
 149                        return IP_ECN_set_ce(ip_hdr(skb));
 150                break;
 151
 152        case cpu_to_be16(ETH_P_IPV6):
 153                if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
 154                    skb_tail_pointer(skb))
 155                        return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
 156                break;
 157        }
 158
 159        return 0;
 160}
 161
 162/*
 163 * RFC 6040 4.2
 164 *  To decapsulate the inner header at the tunnel egress, a compliant
 165 *  tunnel egress MUST set the outgoing ECN field to the codepoint at the
 166 *  intersection of the appropriate arriving inner header (row) and outer
 167 *  header (column) in Figure 4
 168 *
 169 *      +---------+------------------------------------------------+
 170 *      |Arriving |            Arriving Outer Header               |
 171 *      |   Inner +---------+------------+------------+------------+
 172 *      |  Header | Not-ECT | ECT(0)     | ECT(1)     |     CE     |
 173 *      +---------+---------+------------+------------+------------+
 174 *      | Not-ECT | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)|
 175 *      |  ECT(0) |  ECT(0) | ECT(0)     | ECT(1)     |     CE     |
 176 *      |  ECT(1) |  ECT(1) | ECT(1) (!) | ECT(1)     |     CE     |
 177 *      |    CE   |      CE |     CE     |     CE(!!!)|     CE     |
 178 *      +---------+---------+------------+------------+------------+
 179 *
 180 *             Figure 4: New IP in IP Decapsulation Behaviour
 181 *
 182 *  returns 0 on success
 183 *          1 if something is broken and should be logged (!!! above)
 184 *          2 if packet should be dropped
 185 */
 186static inline int __INET_ECN_decapsulate(__u8 outer, __u8 inner, bool *set_ce)
 187{
 188        if (INET_ECN_is_not_ect(inner)) {
 189                switch (outer & INET_ECN_MASK) {
 190                case INET_ECN_NOT_ECT:
 191                        return 0;
 192                case INET_ECN_ECT_0:
 193                case INET_ECN_ECT_1:
 194                        return 1;
 195                case INET_ECN_CE:
 196                        return 2;
 197                }
 198        }
 199
 200        *set_ce = INET_ECN_is_ce(outer);
 201        return 0;
 202}
 203
 204static inline int INET_ECN_decapsulate(struct sk_buff *skb,
 205                                       __u8 outer, __u8 inner)
 206{
 207        bool set_ce = false;
 208        int rc;
 209
 210        rc = __INET_ECN_decapsulate(outer, inner, &set_ce);
 211        if (!rc && set_ce)
 212                INET_ECN_set_ce(skb);
 213
 214        return rc;
 215}
 216
 217static inline int IP_ECN_decapsulate(const struct iphdr *oiph,
 218                                     struct sk_buff *skb)
 219{
 220        __u8 inner;
 221
 222        if (skb->protocol == htons(ETH_P_IP))
 223                inner = ip_hdr(skb)->tos;
 224        else if (skb->protocol == htons(ETH_P_IPV6))
 225                inner = ipv6_get_dsfield(ipv6_hdr(skb));
 226        else
 227                return 0;
 228
 229        return INET_ECN_decapsulate(skb, oiph->tos, inner);
 230}
 231
 232static inline int IP6_ECN_decapsulate(const struct ipv6hdr *oipv6h,
 233                                      struct sk_buff *skb)
 234{
 235        __u8 inner;
 236
 237        if (skb->protocol == htons(ETH_P_IP))
 238                inner = ip_hdr(skb)->tos;
 239        else if (skb->protocol == htons(ETH_P_IPV6))
 240                inner = ipv6_get_dsfield(ipv6_hdr(skb));
 241        else
 242                return 0;
 243
 244        return INET_ECN_decapsulate(skb, ipv6_get_dsfield(oipv6h), inner);
 245}
 246#endif
 247