]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/uipc_mbuf2.c
xnu-1228.5.18.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 #ifndef __APPLE__
377 t = malloc(len + sizeof(struct m_tag), M_PACKET_TAGS, wait);
378 #else
379 /*MALLOC(t, struct m_tag *, len + sizeof(struct m_tag), M_TEMP, M_WAITOK);*/
380 if (len + sizeof(struct m_tag) <= MLEN) {
381 struct mbuf *m = m_get(wait, MT_TAG);
382 if (m == NULL)
383 return NULL;
384 t = mtod(m, struct m_tag *);
385 } else if (len + sizeof(struct m_tag) <= MCLBYTES) {
386 t = (struct m_tag *) m_mclalloc(wait);
387 } else
388 t = NULL;
389 #endif
390 if (t == NULL)
391 return NULL;
392 t->m_tag_type = type;
393 t->m_tag_len = len;
394 t->m_tag_id = id;
395 return t;
396 }
397
398
399 /* Free a packet tag. */
400 void
401 m_tag_free(struct m_tag *t)
402 {
403 #if CONFIG_MACF_NET
404 if (t != NULL &&
405 t->m_tag_id == KERNEL_MODULE_TAG_ID &&
406 t->m_tag_type == KERNEL_TAG_TYPE_MACLABEL)
407 mac_mbuf_tag_destroy(t);
408 #endif
409 #ifndef __APPLE__
410 free(t, M_PACKET_TAGS);
411 #else
412 /* FREE(t, M_TEMP); */
413 if (t == NULL)
414 return;
415 if (t->m_tag_len + sizeof(struct m_tag) <= MLEN) {
416 struct mbuf * m = m_dtom(t);
417 m_free(m);
418 } else {
419 MCLFREE((caddr_t)t);
420 }
421 #endif
422 }
423
424 /* Prepend a packet tag. */
425 void
426 m_tag_prepend(struct mbuf *m, struct m_tag *t)
427 {
428 KASSERT(m && t, ("m_tag_prepend: null argument, m %p t %p", m, t));
429 SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link);
430 }
431
432 /* Unlink a packet tag. */
433 void
434 m_tag_unlink(struct mbuf *m, struct m_tag *t)
435 {
436 KASSERT(m && t, ("m_tag_unlink: null argument, m %p t %p", m, t));
437 SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link);
438 }
439
440 /* Unlink and free a packet tag. */
441 void
442 m_tag_delete(struct mbuf *m, struct m_tag *t)
443 {
444 KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", m, t));
445 m_tag_unlink(m, t);
446 m_tag_free(t);
447 }
448
449 /* Unlink and free a packet tag chain, starting from given tag. */
450 void
451 m_tag_delete_chain(struct mbuf *m, struct m_tag *t)
452 {
453 struct m_tag *p, *q;
454
455 KASSERT(m, ("m_tag_delete_chain: null mbuf"));
456 if (t != NULL)
457 p = t;
458 else
459 p = SLIST_FIRST(&m->m_pkthdr.tags);
460 if (p == NULL)
461 return;
462 while ((q = SLIST_NEXT(p, m_tag_link)) != NULL)
463 m_tag_delete(m, q);
464 m_tag_delete(m, p);
465 }
466
467 /* Find a tag, starting from a given position. */
468 struct m_tag *
469 m_tag_locate(struct mbuf *m, u_int32_t id, u_int16_t type, struct m_tag *t)
470 {
471 struct m_tag *p;
472
473 KASSERT(m, ("m_tag_find: null mbuf"));
474 if (t == NULL)
475 p = SLIST_FIRST(&m->m_pkthdr.tags);
476 else
477 p = SLIST_NEXT(t, m_tag_link);
478 while (p != NULL) {
479 if (p->m_tag_id == id && p->m_tag_type == type)
480 return p;
481 p = SLIST_NEXT(p, m_tag_link);
482 }
483 return NULL;
484 }
485
486 /* Copy a single tag. */
487 struct m_tag *
488 m_tag_copy(struct m_tag *t, int how)
489 {
490 struct m_tag *p;
491
492 KASSERT(t, ("m_tag_copy: null tag"));
493 p = m_tag_alloc(t->m_tag_id, t->m_tag_type, t->m_tag_len, how);
494 if (p == NULL)
495 return (NULL);
496 #if CONFIG_MACF_NET
497 /*
498 * XXXMAC: we should probably pass off the initialization, and
499 * copying here? can we hid that KERNEL_TAG_TYPE_MACLABEL is
500 * special from the mbuf code?
501 */
502 if (t != NULL &&
503 t->m_tag_id == KERNEL_MODULE_TAG_ID &&
504 t->m_tag_type == KERNEL_TAG_TYPE_MACLABEL) {
505 if (mac_mbuf_tag_init(p, how) != 0) {
506 m_tag_free(p);
507 return (NULL);
508 }
509 mac_mbuf_tag_copy(t, p);
510 } else
511 #endif
512 bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
513 return p;
514 }
515
516 /*
517 * Copy two tag chains. The destination mbuf (to) loses any attached
518 * tags even if the operation fails. This should not be a problem, as
519 * m_tag_copy_chain() is typically called with a newly-allocated
520 * destination mbuf.
521 */
522 int
523 m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
524 {
525 struct m_tag *p, *t, *tprev = NULL;
526
527 KASSERT(to && from,
528 ("m_tag_copy: null argument, to %p from %p", to, from));
529 m_tag_delete_chain(to, NULL);
530 SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
531 t = m_tag_copy(p, how);
532 if (t == NULL) {
533 m_tag_delete_chain(to, NULL);
534 return 0;
535 }
536 if (tprev == NULL)
537 SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link);
538 else {
539 SLIST_INSERT_AFTER(tprev, t, m_tag_link);
540 tprev = t;
541 }
542 }
543 return 1;
544 }
545
546 /* Initialize tags on an mbuf. */
547 void
548 m_tag_init(struct mbuf *m)
549 {
550 SLIST_INIT(&m->m_pkthdr.tags);
551 }
552
553 /* Get first tag in chain. */
554 struct m_tag *
555 m_tag_first(struct mbuf *m)
556 {
557 return SLIST_FIRST(&m->m_pkthdr.tags);
558 }
559
560 /* Get next tag in chain. */
561 struct m_tag *
562 m_tag_next(__unused struct mbuf *m, struct m_tag *t)
563 {
564 return SLIST_NEXT(t, m_tag_link);
565 }