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