]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/uipc_mbuf2.c
xnu-1504.3.12.tar.gz
[apple/xnu.git] / bsd / kern / uipc_mbuf2.c
1 /*
2 * Copyright (c) 2000-2007 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 /* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */
29
30 /*
31 * Copyright (C) 1999 WIDE Project.
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. Neither the name of the project nor the names of its contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59 /*
60 * Copyright (c) 1982, 1986, 1988, 1991, 1993
61 * The Regents of the University of California. All rights reserved.
62 *
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
65 * are met:
66 * 1. Redistributions of source code must retain the above copyright
67 * notice, this list of conditions and the following disclaimer.
68 * 2. Redistributions in binary form must reproduce the above copyright
69 * notice, this list of conditions and the following disclaimer in the
70 * documentation and/or other materials provided with the distribution.
71 * 3. All advertising materials mentioning features or use of this software
72 * must display the following acknowledgement:
73 * This product includes software developed by the University of
74 * California, Berkeley and its contributors.
75 * 4. Neither the name of the University nor the names of its contributors
76 * may be used to endorse or promote products derived from this software
77 * without specific prior written permission.
78 *
79 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
80 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
81 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
82 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
83 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
84 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
85 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
86 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
87 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
88 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
89 * SUCH DAMAGE.
90 *
91 * @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95
92 */
93 /*
94 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
95 * support for mandatory and extensible security protections. This notice
96 * is included in support of clause 2.2 (b) of the Apple Public License,
97 * Version 2.0.
98 */
99
100
101 /*#define PULLDOWN_DEBUG*/
102
103 #include <sys/param.h>
104 #include <sys/systm.h>
105 #include <sys/proc_internal.h>
106 #include <sys/malloc.h>
107 #include <sys/mbuf.h>
108 #if defined(PULLDOWN_STAT) && defined(INET6)
109 #include <netinet/in.h>
110 #include <netinet/ip6.h>
111 #include <netinet6/ip6_var.h>
112 #endif
113
114 #if CONFIG_MACF_NET
115 #include <security/mac_framework.h>
116 #endif
117
118 /*
119 * ensure that [off, off + len) is contiguous on the mbuf chain "m".
120 * packet chain before "off" is kept untouched.
121 * if offp == NULL, the target will start at <retval, 0> on resulting chain.
122 * if offp != NULL, the target will start at <retval, *offp> on resulting chain.
123 *
124 * on error return (NULL return value), original "m" will be freed.
125 *
126 * XXX M_TRAILINGSPACE/M_LEADINGSPACE on shared cluster (sharedcluster)
127 */
128 struct mbuf *
129 m_pulldown(struct mbuf *m, int off, int len, int *offp)
130 {
131 struct mbuf *n, *o;
132 int hlen, tlen, olen;
133 int sharedcluster;
134 #if defined(PULLDOWN_STAT) && defined(INET6)
135 static struct mbuf *prev = NULL;
136 int prevlen = 0, prevmlen = 0;
137 #endif
138
139 /* check invalid arguments. */
140 if (m == NULL)
141 panic("m == NULL in m_pulldown()");
142 if (len > MCLBYTES) {
143 m_freem(m);
144 return NULL; /* impossible */
145 }
146
147 #if defined(PULLDOWN_STAT) && defined(INET6)
148 ip6stat.ip6s_pulldown++;
149 #endif
150
151 #if defined(PULLDOWN_STAT) && defined(INET6)
152 /* statistics for m_pullup */
153 ip6stat.ip6s_pullup++;
154 if (off + len > MHLEN)
155 ip6stat.ip6s_pullup_fail++;
156 else {
157 int dlen, mlen;
158
159 dlen = (prev == m) ? prevlen : m->m_len;
160 mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m);
161
162 if (dlen >= off + len)
163 ip6stat.ip6s_pullup--; /* call will not be made! */
164 else if ((m->m_flags & M_EXT) != 0) {
165 ip6stat.ip6s_pullup_alloc++;
166 ip6stat.ip6s_pullup_copy++;
167 } else {
168 if (mlen >= off + len)
169 ip6stat.ip6s_pullup_copy++;
170 else {
171 ip6stat.ip6s_pullup_alloc++;
172 ip6stat.ip6s_pullup_copy++;
173 }
174 }
175
176 prevlen = off + len;
177 prevmlen = MHLEN;
178 }
179
180 /* statistics for m_pullup2 */
181 ip6stat.ip6s_pullup2++;
182 if (off + len > MCLBYTES)
183 ip6stat.ip6s_pullup2_fail++;
184 else {
185 int dlen, mlen;
186
187 dlen = (prev == m) ? prevlen : m->m_len;
188 mlen = (prev == m) ? prevmlen : m->m_len + M_TRAILINGSPACE(m);
189 prevlen = off + len;
190 prevmlen = mlen;
191
192 if (dlen >= off + len)
193 ip6stat.ip6s_pullup2--; /* call will not be made! */
194 else if ((m->m_flags & M_EXT) != 0) {
195 ip6stat.ip6s_pullup2_alloc++;
196 ip6stat.ip6s_pullup2_copy++;
197 prevmlen = (off + len > MHLEN) ? MCLBYTES : MHLEN;
198 } else {
199 if (mlen >= off + len)
200 ip6stat.ip6s_pullup2_copy++;
201 else {
202 ip6stat.ip6s_pullup2_alloc++;
203 ip6stat.ip6s_pullup2_copy++;
204 prevmlen = (off + len > MHLEN) ? MCLBYTES
205 : MHLEN;
206 }
207 }
208 }
209
210 prev = m;
211 #endif
212
213 #ifdef PULLDOWN_DEBUG
214 {
215 struct mbuf *t;
216 printf("before:");
217 for (t = m; t; t = t->m_next)
218 printf(" %d", t->m_len);
219 printf("\n");
220 }
221 #endif
222 n = m;
223 while (n != NULL && off > 0) {
224 if (n->m_len > off)
225 break;
226 off -= n->m_len;
227 n = n->m_next;
228 }
229 /* be sure to point non-empty mbuf */
230 while (n != NULL && n->m_len == 0)
231 n = n->m_next;
232 if (!n) {
233 m_freem(m);
234 return NULL; /* mbuf chain too short */
235 }
236
237 /*
238 * the target data is on <n, off>.
239 * if we got enough data on the mbuf "n", we're done.
240 */
241 if ((off == 0 || offp) && len <= n->m_len - off)
242 goto ok;
243
244 #if defined(PULLDOWN_STAT) && defined(INET6)
245 ip6stat.ip6s_pulldown_copy++;
246 #endif
247
248 /*
249 * when len < n->m_len - off and off != 0, it is a special case.
250 * len bytes from <n, off> sits in single mbuf, but the caller does
251 * not like the starting position (off).
252 * chop the current mbuf into two pieces, set off to 0.
253 */
254 if (len < n->m_len - off) {
255 o = m_copym(n, off, n->m_len - off, M_DONTWAIT);
256 if (o == NULL) {
257 m_freem(m);
258 return NULL; /* ENOBUFS */
259 }
260 n->m_len = off;
261 o->m_next = n->m_next;
262 n->m_next = o;
263 n = n->m_next;
264 off = 0;
265 goto ok;
266 }
267
268 /*
269 * we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
270 * and construct contiguous mbuf with m_len == len.
271 * note that hlen + tlen == len, and tlen > 0.
272 */
273 hlen = n->m_len - off;
274 tlen = len - hlen;
275
276 /*
277 * ensure that we have enough trailing data on mbuf chain.
278 * if not, we can do nothing about the chain.
279 */
280 olen = 0;
281 for (o = n->m_next; o != NULL; o = o->m_next)
282 olen += o->m_len;
283 if (hlen + olen < len) {
284 m_freem(m);
285 return NULL; /* mbuf chain too short */
286 }
287
288 /*
289 * easy cases first.
290 * we need to use m_copydata() to get data from <n->m_next, 0>.
291 */
292 if ((n->m_flags & M_EXT) == 0)
293 sharedcluster = 0;
294 else {
295 if (n->m_ext.ext_free)
296 sharedcluster = 1;
297 else if (m_mclhasreference(n))
298 sharedcluster = 1;
299 else
300 sharedcluster = 0;
301 }
302 if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen
303 && !sharedcluster) {
304 m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
305 n->m_len += tlen;
306 m_adj(n->m_next, tlen);
307 goto ok;
308 }
309 if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen
310 && !sharedcluster) {
311 n->m_next->m_data -= hlen;
312 n->m_next->m_len += hlen;
313 bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen);
314 n->m_len -= hlen;
315 n = n->m_next;
316 off = 0;
317 goto ok;
318 }
319
320 /*
321 * now, we need to do the hard way. don't m_copy as there's no room
322 * on both end.
323 */
324 #if defined(PULLDOWN_STAT) && defined(INET6)
325 ip6stat.ip6s_pulldown_alloc++;
326 #endif
327 MGET(o, M_DONTWAIT, m->m_type);
328 if (o == NULL) {
329 m_freem(m);
330 return NULL; /* ENOBUFS */
331 }
332 if (len > MHLEN) { /* use MHLEN just for safety */
333 MCLGET(o, M_DONTWAIT);
334 if ((o->m_flags & M_EXT) == 0) {
335 m_freem(m);
336 m_free(o);
337 return NULL; /* ENOBUFS */
338 }
339 }
340 /* get hlen from <n, off> into <o, 0> */
341 o->m_len = hlen;
342 bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen);
343 n->m_len -= hlen;
344 /* get tlen from <n->m_next, 0> into <o, hlen> */
345 m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
346 o->m_len += tlen;
347 m_adj(n->m_next, tlen);
348 o->m_next = n->m_next;
349 n->m_next = o;
350 n = o;
351 off = 0;
352
353 ok:
354 #ifdef PULLDOWN_DEBUG
355 {
356 struct mbuf *t;
357 printf("after:");
358 for (t = m; t; t = t->m_next)
359 printf("%c%d", t == n ? '*' : ' ', t->m_len);
360 printf(" (off=%d)\n", off);
361 }
362 #endif
363 if (offp)
364 *offp = off;
365 return n;
366 }
367
368 /* Get a packet tag structure along with specified data following. */
369 struct m_tag *
370 m_tag_alloc(u_int32_t id, u_int16_t type, int len, int wait)
371 {
372 struct m_tag *t;
373
374 if (len < 0)
375 return NULL;
376 #if CONFIG_MBUF_TAGS_MALLOC
377 t = _MALLOC(len + sizeof (struct m_tag), M_TEMP, wait);
378 #else
379 if (len + sizeof(struct m_tag) <= MLEN) {
380 struct mbuf *m = m_get(wait, MT_TAG);
381 if (m == NULL)
382 return NULL;
383 t = mtod(m, struct m_tag *);
384 } else if (len + sizeof(struct m_tag) <= MCLBYTES) {
385 t = (struct m_tag *) m_mclalloc(wait);
386 } else
387 t = NULL;
388 #endif
389 if (t == NULL)
390 return NULL;
391 t->m_tag_type = type;
392 t->m_tag_len = len;
393 t->m_tag_id = id;
394 return t;
395 }
396
397
398 /* Free a packet tag. */
399 void
400 m_tag_free(struct m_tag *t)
401 {
402 #if CONFIG_MACF_NET
403 if (t != NULL &&
404 t->m_tag_id == KERNEL_MODULE_TAG_ID &&
405 t->m_tag_type == KERNEL_TAG_TYPE_MACLABEL)
406 mac_mbuf_tag_destroy(t);
407 #endif
408 #if CONFIG_MBUF_TAGS_MALLOC
409 _FREE(t, M_TEMP);
410 #else
411 if (t == NULL)
412 return;
413 if (t->m_tag_len + sizeof(struct m_tag) <= MLEN) {
414 struct mbuf * m = m_dtom(t);
415 m_free(m);
416 } else {
417 MCLFREE((caddr_t)t);
418 }
419 #endif
420 }
421
422 /* Prepend a packet tag. */
423 void
424 m_tag_prepend(struct mbuf *m, struct m_tag *t)
425 {
426 KASSERT(m && t, ("m_tag_prepend: null argument, m %p t %p", m, t));
427 SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link);
428 }
429
430 /* Unlink a packet tag. */
431 void
432 m_tag_unlink(struct mbuf *m, struct m_tag *t)
433 {
434 KASSERT(m && t, ("m_tag_unlink: null argument, m %p t %p", m, t));
435 SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link);
436 }
437
438 /* Unlink and free a packet tag. */
439 void
440 m_tag_delete(struct mbuf *m, struct m_tag *t)
441 {
442 KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", m, t));
443 m_tag_unlink(m, t);
444 m_tag_free(t);
445 }
446
447 /* Unlink and free a packet tag chain, starting from given tag. */
448 void
449 m_tag_delete_chain(struct mbuf *m, struct m_tag *t)
450 {
451 struct m_tag *p, *q;
452
453 KASSERT(m, ("m_tag_delete_chain: null mbuf"));
454 if (t != NULL)
455 p = t;
456 else
457 p = SLIST_FIRST(&m->m_pkthdr.tags);
458 if (p == NULL)
459 return;
460 while ((q = SLIST_NEXT(p, m_tag_link)) != NULL)
461 m_tag_delete(m, q);
462 m_tag_delete(m, p);
463 }
464
465 /* Find a tag, starting from a given position. */
466 struct m_tag *
467 m_tag_locate(struct mbuf *m, u_int32_t id, u_int16_t type, struct m_tag *t)
468 {
469 struct m_tag *p;
470
471 KASSERT(m, ("m_tag_find: null mbuf"));
472 if (t == NULL)
473 p = SLIST_FIRST(&m->m_pkthdr.tags);
474 else
475 p = SLIST_NEXT(t, m_tag_link);
476 while (p != NULL) {
477 if (p->m_tag_id == id && p->m_tag_type == type)
478 return p;
479 p = SLIST_NEXT(p, m_tag_link);
480 }
481 return NULL;
482 }
483
484 /* Copy a single tag. */
485 struct m_tag *
486 m_tag_copy(struct m_tag *t, int how)
487 {
488 struct m_tag *p;
489
490 KASSERT(t, ("m_tag_copy: null tag"));
491 p = m_tag_alloc(t->m_tag_id, t->m_tag_type, t->m_tag_len, how);
492 if (p == NULL)
493 return (NULL);
494 #if CONFIG_MACF_NET
495 /*
496 * XXXMAC: we should probably pass off the initialization, and
497 * copying here? can we hid that KERNEL_TAG_TYPE_MACLABEL is
498 * special from the mbuf code?
499 */
500 if (t != NULL &&
501 t->m_tag_id == KERNEL_MODULE_TAG_ID &&
502 t->m_tag_type == KERNEL_TAG_TYPE_MACLABEL) {
503 if (mac_mbuf_tag_init(p, how) != 0) {
504 m_tag_free(p);
505 return (NULL);
506 }
507 mac_mbuf_tag_copy(t, p);
508 } else
509 #endif
510 bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
511 return p;
512 }
513
514 /*
515 * Copy two tag chains. The destination mbuf (to) loses any attached
516 * tags even if the operation fails. This should not be a problem, as
517 * m_tag_copy_chain() is typically called with a newly-allocated
518 * destination mbuf.
519 */
520 int
521 m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
522 {
523 struct m_tag *p, *t, *tprev = NULL;
524
525 KASSERT(to && from,
526 ("m_tag_copy: null argument, to %p from %p", to, from));
527 m_tag_delete_chain(to, NULL);
528 SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
529 t = m_tag_copy(p, how);
530 if (t == NULL) {
531 m_tag_delete_chain(to, NULL);
532 return 0;
533 }
534 if (tprev == NULL)
535 SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link);
536 else {
537 SLIST_INSERT_AFTER(tprev, t, m_tag_link);
538 tprev = t;
539 }
540 }
541 return 1;
542 }
543
544 /* Initialize tags on an mbuf. */
545 void
546 m_tag_init(struct mbuf *m)
547 {
548 SLIST_INIT(&m->m_pkthdr.tags);
549 #if PF_PKTHDR
550 bzero(&m->m_pkthdr.pf_mtag, sizeof (m->m_pkthdr.pf_mtag));
551 #endif
552 }
553
554 /* Get first tag in chain. */
555 struct m_tag *
556 m_tag_first(struct mbuf *m)
557 {
558 return SLIST_FIRST(&m->m_pkthdr.tags);
559 }
560
561 /* Get next tag in chain. */
562 struct m_tag *
563 m_tag_next(__unused struct mbuf *m, struct m_tag *t)
564 {
565 return SLIST_NEXT(t, m_tag_link);
566 }