Blame view

include/net/inet_ecn.h 6.06 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  #ifndef _INET_ECN_H_
  #define _INET_ECN_H_
  
  #include <linux/ip.h>
2566a509c   Thomas Graf   [NET]: Introduce ...
5
  #include <linux/skbuff.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
6
7
  
  #include <net/inet_sock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
16
  #include <net/dsfield.h>
  
  enum {
  	INET_ECN_NOT_ECT = 0,
  	INET_ECN_ECT_1 = 1,
  	INET_ECN_ECT_0 = 2,
  	INET_ECN_CE = 3,
  	INET_ECN_MASK = 3,
  };
eccc1bb8d   stephen hemminger   tunnel: drop pack...
17
  extern int sysctl_tunnel_ecn_log;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
26
27
28
29
  static inline int INET_ECN_is_ce(__u8 dsfield)
  {
  	return (dsfield & INET_ECN_MASK) == INET_ECN_CE;
  }
  
  static inline int INET_ECN_is_not_ect(__u8 dsfield)
  {
  	return (dsfield & INET_ECN_MASK) == INET_ECN_NOT_ECT;
  }
  
  static inline int INET_ECN_is_capable(__u8 dsfield)
  {
a02cec215   Eric Dumazet   net: return opera...
30
  	return dsfield & INET_ECN_ECT_0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  }
b5d9c9c28   Eric Dumazet   inet: add rfc 316...
32
33
34
35
36
37
38
39
  /*
   * RFC 3168 9.1.1
   *  The full-functionality option for ECN encapsulation is to copy the
   *  ECN codepoint of the inside header to the outside header on
   *  encapsulation if the inside header is not-ECT or ECT, and to set the
   *  ECN codepoint of the outside header to ECT(0) if the ECN codepoint of
   *  the inside header is CE.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
  static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
  {
  	outer &= ~INET_ECN_MASK;
  	outer |= !INET_ECN_is_ce(inner) ? (inner & INET_ECN_MASK) :
  					  INET_ECN_ECT_0;
  	return outer;
  }
ca0670702   Steinar H. Gunderson   ipv6: restore cor...
47
48
49
50
51
52
53
54
55
56
57
58
59
  static inline void INET_ECN_xmit(struct sock *sk)
  {
  	inet_sk(sk)->tos |= INET_ECN_ECT_0;
  	if (inet6_sk(sk) != NULL)
  		inet6_sk(sk)->tclass |= INET_ECN_ECT_0;
  }
  
  static inline void INET_ECN_dontxmit(struct sock *sk)
  {
  	inet_sk(sk)->tos &= ~INET_ECN_MASK;
  	if (inet6_sk(sk) != NULL)
  		inet6_sk(sk)->tclass &= ~INET_ECN_MASK;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
  
  #define IP6_ECN_flow_init(label) do {		\
        (label) &= ~htonl(INET_ECN_MASK << 20);	\
      } while (0)
  
  #define	IP6_ECN_flow_xmit(sk, label) do {				\
e9df2e8fd   YOSHIFUJI Hideaki   [IPV6]: Use appro...
66
  	if (INET_ECN_is_capable(inet6_sk(sk)->tclass))			\
95026cd24   Al Viro   [IPV6]: Fix ECN b...
67
  		(label) |= htonl(INET_ECN_ECT_0 << 20);			\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
      } while (0)
2566a509c   Thomas Graf   [NET]: Introduce ...
69
  static inline int IP_ECN_set_ce(struct iphdr *iph)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
5c78f275e   Al Viro   [NET]: IP header ...
71
  	u32 check = (__force u32)iph->check;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
81
  	u32 ecn = (iph->tos + 1) & INET_ECN_MASK;
  
  	/*
  	 * After the last operation we have (in binary):
  	 * INET_ECN_NOT_ECT => 01
  	 * INET_ECN_ECT_1   => 10
  	 * INET_ECN_ECT_0   => 11
  	 * INET_ECN_CE      => 00
  	 */
  	if (!(ecn & 2))
2566a509c   Thomas Graf   [NET]: Introduce ...
82
  		return !ecn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
  
  	/*
  	 * The following gives us:
  	 * INET_ECN_ECT_1 => check += htons(0xFFFD)
  	 * INET_ECN_ECT_0 => check += htons(0xFFFE)
  	 */
5c78f275e   Al Viro   [NET]: IP header ...
89
  	check += (__force u16)htons(0xFFFB) + (__force u16)htons(ecn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90

5c78f275e   Al Viro   [NET]: IP header ...
91
  	iph->check = (__force __sum16)(check + (check>=0xFFFF));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
  	iph->tos |= INET_ECN_CE;
2566a509c   Thomas Graf   [NET]: Introduce ...
93
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
  }
  
  static inline void IP_ECN_clear(struct iphdr *iph)
  {
  	iph->tos &= ~INET_ECN_MASK;
  }
29bb43b4e   Herbert Xu   [INET]: Give oute...
100
  static inline void ipv4_copy_dscp(unsigned int dscp, struct iphdr *inner)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
  {
29bb43b4e   Herbert Xu   [INET]: Give oute...
102
  	dscp &= ~INET_ECN_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
  	ipv4_change_dsfield(inner, INET_ECN_MASK, dscp);
  }
  
  struct ipv6hdr;
34ae6a1aa   Eric Dumazet   ipv6: update skb-...
107
108
109
110
111
112
113
  /* 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)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  {
34ae6a1aa   Eric Dumazet   ipv6: update skb-...
115
  	__be32 from, to;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  	if (INET_ECN_is_not_ect(ipv6_get_dsfield(iph)))
2566a509c   Thomas Graf   [NET]: Introduce ...
117
  		return 0;
34ae6a1aa   Eric Dumazet   ipv6: update skb-...
118
119
120
121
122
  
  	from = *(__be32 *)iph;
  	to = from | htonl(INET_ECN_CE << 20);
  	*(__be32 *)iph = to;
  	if (skb->ip_summed == CHECKSUM_COMPLETE)
c15c0ab12   Johannes Berg   ipv6: suppress sp...
123
124
  		skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from),
  				     (__force __wsum)to);
2566a509c   Thomas Graf   [NET]: Introduce ...
125
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
  }
  
  static inline void IP6_ECN_clear(struct ipv6hdr *iph)
  {
92d9ece7a   Al Viro   [INET]: annotate ...
130
  	*(__be32*)iph &= ~htonl(INET_ECN_MASK << 20);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  }
29bb43b4e   Herbert Xu   [INET]: Give oute...
132
  static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  {
29bb43b4e   Herbert Xu   [INET]: Give oute...
134
  	dscp &= ~INET_ECN_MASK;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  	ipv6_change_dsfield(inner, INET_ECN_MASK, dscp);
  }
2566a509c   Thomas Graf   [NET]: Introduce ...
137
138
139
  static inline int INET_ECN_set_ce(struct sk_buff *skb)
  {
  	switch (skb->protocol) {
f3a7c66b5   Harvey Harrison   net: replace __co...
140
  	case cpu_to_be16(ETH_P_IP):
ced14f680   Simon Horman   net: Correct comp...
141
142
  		if (skb_network_header(skb) + sizeof(struct iphdr) <=
  		    skb_tail_pointer(skb))
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
143
  			return IP_ECN_set_ce(ip_hdr(skb));
2566a509c   Thomas Graf   [NET]: Introduce ...
144
  		break;
f3a7c66b5   Harvey Harrison   net: replace __co...
145
  	case cpu_to_be16(ETH_P_IPV6):
ced14f680   Simon Horman   net: Correct comp...
146
147
  		if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
  		    skb_tail_pointer(skb))
34ae6a1aa   Eric Dumazet   ipv6: update skb-...
148
  			return IP6_ECN_set_ce(skb, ipv6_hdr(skb));
2566a509c   Thomas Graf   [NET]: Introduce ...
149
150
151
152
153
  		break;
  	}
  
  	return 0;
  }
eccc1bb8d   stephen hemminger   tunnel: drop pack...
154
  /*
d28071d10   Neal Cardwell   tunnel: fix RFC n...
155
   * RFC 6040 4.2
eccc1bb8d   stephen hemminger   tunnel: drop pack...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
   *  To decapsulate the inner header at the tunnel egress, a compliant
   *  tunnel egress MUST set the outgoing ECN field to the codepoint at the
   *  intersection of the appropriate arriving inner header (row) and outer
   *  header (column) in Figure 4
   *
   *      +---------+------------------------------------------------+
   *      |Arriving |            Arriving Outer Header               |
   *      |   Inner +---------+------------+------------+------------+
   *      |  Header | Not-ECT | ECT(0)     | ECT(1)     |     CE     |
   *      +---------+---------+------------+------------+------------+
   *      | Not-ECT | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)|
   *      |  ECT(0) |  ECT(0) | ECT(0)     | ECT(1)     |     CE     |
   *      |  ECT(1) |  ECT(1) | ECT(1) (!) | ECT(1)     |     CE     |
   *      |    CE   |      CE |     CE     |     CE(!!!)|     CE     |
   *      +---------+---------+------------+------------+------------+
   *
   *             Figure 4: New IP in IP Decapsulation Behaviour
   *
   *  returns 0 on success
   *          1 if something is broken and should be logged (!!! above)
   *          2 if packet should be dropped
   */
  static inline int INET_ECN_decapsulate(struct sk_buff *skb,
  				       __u8 outer, __u8 inner)
  {
  	if (INET_ECN_is_not_ect(inner)) {
  		switch (outer & INET_ECN_MASK) {
  		case INET_ECN_NOT_ECT:
  			return 0;
  		case INET_ECN_ECT_0:
  		case INET_ECN_ECT_1:
  			return 1;
  		case INET_ECN_CE:
  			return 2;
  		}
  	}
  
  	if (INET_ECN_is_ce(outer))
  		INET_ECN_set_ce(skb);
  
  	return 0;
  }
  
  static inline int IP_ECN_decapsulate(const struct iphdr *oiph,
  				     struct sk_buff *skb)
  {
  	__u8 inner;
  
  	if (skb->protocol == htons(ETH_P_IP))
  		inner = ip_hdr(skb)->tos;
  	else if (skb->protocol == htons(ETH_P_IPV6))
  		inner = ipv6_get_dsfield(ipv6_hdr(skb));
  	else
  		return 0;
  
  	return INET_ECN_decapsulate(skb, oiph->tos, inner);
  }
  
  static inline int IP6_ECN_decapsulate(const struct ipv6hdr *oipv6h,
  				      struct sk_buff *skb)
  {
  	__u8 inner;
  
  	if (skb->protocol == htons(ETH_P_IP))
  		inner = ip_hdr(skb)->tos;
  	else if (skb->protocol == htons(ETH_P_IPV6))
  		inner = ipv6_get_dsfield(ipv6_hdr(skb));
  	else
  		return 0;
  
  	return INET_ECN_decapsulate(skb, ipv6_get_dsfield(oipv6h), inner);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  #endif