Branch data Line data Source code
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 : : 11 : : enum { 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 : : 19 : : extern int sysctl_tunnel_ecn_log; 20 : : 21 : 0 : static inline int INET_ECN_is_ce(__u8 dsfield) 22 : : { 23 [ # # # # ]: 0 : return (dsfield & INET_ECN_MASK) == INET_ECN_CE; 24 : : } 25 : : 26 : 0 : static inline int INET_ECN_is_not_ect(__u8 dsfield) 27 : : { 28 [ # # ]: 0 : return (dsfield & INET_ECN_MASK) == INET_ECN_NOT_ECT; 29 : : } 30 : : 31 : 0 : static inline int INET_ECN_is_capable(__u8 dsfield) 32 : : { 33 [ # # ]: 0 : 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 : : */ 44 : 0 : static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner) 45 : : { 46 : 0 : outer &= ~INET_ECN_MASK; 47 [ # # # # ]: 0 : outer |= !INET_ECN_is_ce(inner) ? (inner & INET_ECN_MASK) : 48 : : INET_ECN_ECT_0; 49 [ # # # # ]: 0 : return outer; 50 : : } 51 : : 52 : 0 : static inline void INET_ECN_xmit(struct sock *sk) 53 : : { 54 [ # # ]: 0 : inet_sk(sk)->tos |= INET_ECN_ECT_0; 55 [ # # # # ]: 0 : if (inet6_sk(sk) != NULL) 56 [ # # ]: 0 : inet6_sk(sk)->tclass |= INET_ECN_ECT_0; 57 : 0 : } 58 : : 59 : 56 : static inline void INET_ECN_dontxmit(struct sock *sk) 60 : : { 61 [ + - ]: 56 : inet_sk(sk)->tos &= ~INET_ECN_MASK; 62 [ + - + + ]: 56 : if (inet6_sk(sk) != NULL) 63 [ + - ]: 56 : inet6_sk(sk)->tclass &= ~INET_ECN_MASK; 64 : 56 : } 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 : : 75 : 0 : static inline int IP_ECN_set_ce(struct iphdr *iph) 76 : : { 77 : 0 : u32 check = (__force u32)iph->check; 78 : 0 : 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 [ # # ]: 0 : if (!(ecn & 2)) 88 : 0 : 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 : 0 : check += (__force u16)htons(0xFFFB) + (__force u16)htons(ecn); 96 : : 97 : 0 : iph->check = (__force __sum16)(check + (check>=0xFFFF)); 98 : 0 : iph->tos |= INET_ECN_CE; 99 : 0 : return 1; 100 : : } 101 : : 102 : 0 : static inline void IP_ECN_clear(struct iphdr *iph) 103 : : { 104 : 0 : iph->tos &= ~INET_ECN_MASK; 105 : 0 : } 106 : : 107 : : static 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 : : 113 : : struct 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 : : */ 121 : 0 : static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph) 122 : : { 123 : 0 : __be32 from, to; 124 : : 125 [ # # ]: 0 : if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph))) 126 : : return 0; 127 : : 128 : 0 : from = *(__be32 *)iph; 129 : 0 : to = from | htonl(INET_ECN_CE << 20); 130 : 0 : *(__be32 *)iph = to; 131 [ # # ]: 0 : if (skb->ip_summed == CHECKSUM_COMPLETE) 132 : 0 : skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from), 133 : : (__force __wsum)to); 134 : : return 1; 135 : : } 136 : : 137 : : static 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 : : 143 : 0 : static inline int INET_ECN_set_ce(struct sk_buff *skb) 144 : : { 145 [ # # # ]: 0 : switch (skb->protocol) { 146 : 0 : case cpu_to_be16(ETH_P_IP): 147 [ # # ]: 0 : if (skb_network_header(skb) + sizeof(struct iphdr) <= 148 [ # # ]: 0 : skb_tail_pointer(skb)) 149 [ # # ]: 0 : return IP_ECN_set_ce(ip_hdr(skb)); 150 : : break; 151 : : 152 : 0 : case cpu_to_be16(ETH_P_IPV6): 153 [ # # ]: 0 : if (skb_network_header(skb) + sizeof(struct ipv6hdr) <= 154 [ # # ]: 0 : skb_tail_pointer(skb)) 155 [ # # ]: 0 : 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 : : */ 186 : 0 : static inline int __INET_ECN_decapsulate(__u8 outer, __u8 inner, bool *set_ce) 187 : : { 188 : 0 : if (INET_ECN_is_not_ect(inner)) { 189 [ # # # ]: 0 : 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 : 0 : *set_ce = INET_ECN_is_ce(outer); 201 : 0 : return 0; 202 : : } 203 : : 204 : 0 : static inline int INET_ECN_decapsulate(struct sk_buff *skb, 205 : : __u8 outer, __u8 inner) 206 : : { 207 : 0 : bool set_ce = false; 208 : 0 : int rc; 209 : : 210 [ # # ]: 0 : rc = __INET_ECN_decapsulate(outer, inner, &set_ce); 211 [ # # ]: 0 : if (!rc && set_ce) 212 : 0 : INET_ECN_set_ce(skb); 213 : : 214 : 0 : return rc; 215 : : } 216 : : 217 : : static 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 : : 232 : : static 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