xref: /xnu-11417.121.6/bsd/net/classq/classq_util.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
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  */
87 u_int8_t
read_dsfield(struct mbuf * m,struct pf_mtag * t)88 read_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 
139 void
write_dsfield(struct mbuf * m,struct pf_mtag * t,u_int8_t dsfield)140 write_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  */
211 int
mark_ecn(struct mbuf * m,struct pf_mtag * t,int flags)212 mark_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