1/*
2 * Copyright (c) 2007-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * Copyright (c) 1991-1997 Regents of the University of California.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the Network Research
44 * Group at Lawrence Berkeley Laboratory.
45 * 4. Neither the name of the University nor of the Laboratory may be used
46 * to endorse or promote products derived from this software without
47 * specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63#include <sys/param.h>
64#include <sys/mbuf.h>
65#include <sys/errno.h>
66#include <sys/random.h>
67#include <sys/kernel_types.h>
68#include <sys/sysctl.h>
69
70#include <net/if.h>
71#include <net/net_osdep.h>
72#include <net/classq/classq.h>
73
74#include <netinet/in.h>
75#include <netinet/in_systm.h>
76#include <netinet/ip.h>
77#include <netinet/ip6.h>
78#include <netinet/tcp.h>
79#include <netinet/udp.h>
80
81#include <libkern/libkern.h>
82
83#if PF_ECN
84/*
85 * read and write diffserv field in IPv4 or IPv6 header
86 */
87u_int8_t
88read_dsfield(struct mbuf *m, struct pf_mtag *t)
89{
90 struct mbuf *m0;
91 u_int8_t ds_field = 0;
92
93 if (t->pftag_hdr == NULL ||
94 !(t->pftag_flags & (PF_TAG_HDR_INET | PF_TAG_HDR_INET6))) {
95 return (u_int8_t)0;
96 }
97
98 /* verify that hdr is within the mbuf data */
99 for (m0 = m; m0 != NULL; m0 = m0->m_next) {
100 if (((caddr_t)t->pftag_hdr >= m0->m_data) &&
101 ((caddr_t)t->pftag_hdr < m0->m_data + m0->m_len)) {
102 break;
103 }
104 }
105 if (m0 == NULL) {
106 /* ick, tag info is stale */
107 printf("%s: can't locate header!\n", __func__);
108 return (u_int8_t)0;
109 }
110
111 if (t->pftag_flags & PF_TAG_HDR_INET) {
112 struct ip *ip = (struct ip *)(void *)t->pftag_hdr;
113
114 if (((uintptr_t)ip + sizeof(*ip)) >
115 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
116 return 0; /* out of bounds */
117 }
118 if (ip->ip_v != 4) {
119 return (u_int8_t)0; /* version mismatch! */
120 }
121 ds_field = ip->ip_tos;
122 } else if (t->pftag_flags & PF_TAG_HDR_INET6) {
123 struct ip6_hdr *ip6 = (struct ip6_hdr *)(void *)t->pftag_hdr;
124 u_int32_t flowlabel;
125
126 if (((uintptr_t)ip6 + sizeof(*ip6)) >
127 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
128 return 0; /* out of bounds */
129 }
130 flowlabel = ntohl(ip6->ip6_flow);
131 if ((flowlabel >> 28) != 6) {
132 return (u_int8_t)0; /* version mismatch! */
133 }
134 ds_field = (flowlabel >> 20) & 0xff;
135 }
136 return ds_field;
137}
138
139void
140write_dsfield(struct mbuf *m, struct pf_mtag *t, u_int8_t dsfield)
141{
142 struct mbuf *m0;
143
144 if (t->pftag_hdr == NULL ||
145 !(t->pftag_flags & (PF_TAG_HDR_INET | PF_TAG_HDR_INET6))) {
146 return;
147 }
148
149 /* verify that hdr is within the mbuf data */
150 for (m0 = m; m0 != NULL; m0 = m0->m_next) {
151 if (((caddr_t)t->pftag_hdr >= m0->m_data) &&
152 ((caddr_t)t->pftag_hdr < m0->m_data + m0->m_len)) {
153 break;
154 }
155 }
156 if (m0 == NULL) {
157 /* ick, tag info is stale */
158 printf("%s: can't locate header!\n", __func__);
159 return;
160 }
161
162 if (t->pftag_flags & PF_TAG_HDR_INET) {
163 struct ip *ip = (struct ip *)(void *)t->pftag_hdr;
164 u_int8_t old;
165 int32_t sum;
166
167 if (((uintptr_t)ip + sizeof(*ip)) >
168 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
169 return; /* out of bounds */
170 }
171 if (ip->ip_v != 4) {
172 return; /* version mismatch! */
173 }
174 old = ip->ip_tos;
175 dsfield |= old & 3; /* leave CU bits */
176 if (old == dsfield) {
177 return;
178 }
179 ip->ip_tos = dsfield;
180 /*
181 * update checksum (from RFC1624)
182 * HC' = ~(~HC + ~m + m')
183 */
184 sum = ~ntohs(ip->ip_sum) & 0xffff;
185 sum += 0xff00 + (~old & 0xff) + dsfield;
186 sum = (sum >> 16) + (sum & 0xffff);
187 sum += (sum >> 16); /* add carry */
188
189 ip->ip_sum = htons(~sum & 0xffff);
190 } else if (t->pftag_flags & PF_TAG_HDR_INET6) {
191 struct ip6_hdr *ip6 = (struct ip6_hdr *)t->pftag_hdr;
192 u_int32_t flowlabel;
193
194 if (((uintptr_t)ip6 + sizeof(*ip6)) >
195 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
196 return; /* out of bounds */
197 }
198 flowlabel = ntohl(ip6->ip6_flow);
199 if ((flowlabel >> 28) != 6) {
200 return; /* version mismatch! */
201 }
202 flowlabel = (flowlabel & 0xf03fffff) | (dsfield << 20);
203 ip6->ip6_flow = htonl(flowlabel);
204 }
205}
206
207/*
208 * try to mark CE bit to the packet.
209 * returns 1 if successfully marked, 0 otherwise.
210 */
211int
212mark_ecn(struct mbuf *m, struct pf_mtag *t, int flags)
213{
214 struct mbuf *m0;
215 void *hdr;
216 int af;
217
218 if ((hdr = t->pftag_hdr) == NULL ||
219 !(t->pftag_flags & (PF_TAG_HDR_INET | PF_TAG_HDR_INET6))) {
220 return 0;
221 }
222
223 /* verify that hdr is within the mbuf data */
224 for (m0 = m; m0 != NULL; m0 = m0->m_next) {
225 if (((caddr_t)hdr >= m0->m_data) &&
226 ((caddr_t)hdr < m0->m_data + m0->m_len)) {
227 break;
228 }
229 }
230 if (m0 == NULL) {
231 /* ick, tag info is stale */
232 printf("%s: can't locate header!\n", __func__);
233 return 0;
234 }
235
236 if (t->pftag_flags & PF_TAG_HDR_INET) {
237 af = AF_INET;
238 } else if (t->pftag_flags & PF_TAG_HDR_INET6) {
239 af = AF_INET6;
240 } else {
241 af = AF_UNSPEC;
242 }
243
244 switch (af) {
245 case AF_INET:
246 if (flags & CLASSQF_ECN4) { /* REDF_ECN4 == BLUEF_ECN4 */
247 struct ip *ip = hdr;
248 u_int8_t otos;
249 int sum;
250
251 if (((uintptr_t)ip + sizeof(*ip)) >
252 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
253 return 0; /* out of bounds */
254 }
255 if (ip->ip_v != 4) {
256 return 0; /* version mismatch! */
257 }
258 if ((ip->ip_tos & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT) {
259 return 0; /* not-ECT */
260 }
261 if ((ip->ip_tos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
262 return 1; /* already marked */
263 }
264 /*
265 * ecn-capable but not marked,
266 * mark CE and update checksum
267 */
268 otos = ip->ip_tos;
269 ip->ip_tos |= IPTOS_ECN_CE;
270 /*
271 * update checksum (from RFC1624) only if hw
272 * checksum is not supported.
273 * HC' = ~(~HC + ~m + m')
274 */
275 if (!(m->m_pkthdr.csum_flags & CSUM_DELAY_IP)) {
276 sum = ~ntohs(ip->ip_sum) & 0xffff;
277 sum += (~otos & 0xffff) + ip->ip_tos;
278 sum = (sum >> 16) + (sum & 0xffff);
279 sum += (sum >> 16); /* add carry */
280 ip->ip_sum = htons(~sum & 0xffff);
281 }
282 return 1;
283 }
284 break;
285 case AF_INET6:
286 if (flags & CLASSQF_ECN6) { /* REDF_ECN6 == BLUEF_ECN6 */
287 struct ip6_hdr *ip6 = hdr;
288 u_int32_t flowlabel;
289
290 if (((uintptr_t)ip6 + sizeof(*ip6)) >
291 ((uintptr_t)mbuf_datastart(m0) + mbuf_maxlen(m0))) {
292 return 0; /* out of bounds */
293 }
294 flowlabel = ntohl(ip6->ip6_flow);
295 if ((flowlabel >> 28) != 6) {
296 return 0; /* version mismatch! */
297 }
298 if ((flowlabel & (IPTOS_ECN_MASK << 20)) ==
299 (IPTOS_ECN_NOTECT << 20)) {
300 return 0; /* not-ECT */
301 }
302 if ((flowlabel & (IPTOS_ECN_MASK << 20)) ==
303 (IPTOS_ECN_CE << 20)) {
304 return 1; /* already marked */
305 }
306 /*
307 * ecn-capable but not marked, mark CE
308 */
309 flowlabel |= (IPTOS_ECN_CE << 20);
310 ip6->ip6_flow = htonl(flowlabel);
311 return 1;
312 }
313 break;
314 }
315
316 /* not marked */
317 return 0;
318}
319#endif /* PF_ECN */
320