|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* ! 26: * Mach Operating System ! 27: * Copyright (c) 1989 Carnegie-Mellon University ! 28: * Copyright (c) 1988 Carnegie-Mellon University ! 29: * Copyright (c) 1987 Carnegie-Mellon University ! 30: * All rights reserved. The CMU software License Agreement specifies ! 31: * the terms and conditions for use and redistribution. ! 32: */ ! 33: /* ! 34: * HISTORY ! 35: * Revision 1.1.1.1 1997/09/30 02:44:31 wsanchez ! 36: * Import of kernel from umeshv/kernel ! 37: * ! 38: * 1-Mar-90 Gregg Kellogg (gk) at NeXT ! 39: * NeXT: Don't make the mach_net_kmsg_zone quite so big. ! 40: * Turn off optimization around netipc_listen(). ! 41: * ! 42: * Revision 2.11 89/02/25 18:05:43 gm0w ! 43: * Changes for cleanup. ! 44: * ! 45: * Revision 2.10 89/01/15 16:24:27 rpd ! 46: * Use decl_simple_lock_data. ! 47: * [89/01/15 15:02:07 rpd] ! 48: * ! 49: * Revision 2.8 88/10/11 10:19:58 rpd ! 50: * Removed SEND_KERNEL from the msg_queue operation. ! 51: * Removed redundant log messages. ! 52: * [88/10/09 08:46:23 rpd] ! 53: * ! 54: * Changed "struct KMsg" to "struct kern_msg". ! 55: * Removed redundant "#if MACH_NET/#endif" wrapper. ! 56: * [88/10/04 07:07:58 rpd] ! 57: * ! 58: * Revision 2.7 88/10/01 21:57:33 rpd ! 59: * Changed msg_remote_port to msg_local_port, because that is ! 60: * the destination port of a copied-in message now. ! 61: * [88/10/01 21:30:34 rpd] ! 62: * ! 63: * Revision 2.6 88/08/24 01:51:51 mwyoung ! 64: * Corrected include file references. ! 65: * [88/08/22 22:06:23 mwyoung] ! 66: * ! 67: * 22-Aug-88 Michael Young (mwyoung) at Carnegie-Mellon University ! 68: * Removed ancient history. ! 69: * ! 70: * 15-Aug-88 Avadis Tevanian (avie) at NeXT ! 71: * Changed number of allocated messages for mach_net from 8 to 4. ! 72: * ! 73: * Revision 2.5 88/08/06 18:22:03 rpd ! 74: * Eliminated use of kern/mach_ipc_defs.h. ! 75: * ! 76: * Revision 2.4 88/07/20 16:38:58 rpd ! 77: * When queueing a message, bump current and ip_data_grams ! 78: * ipc_statistics counters. ! 79: * ! 80: * 18-May-88 Daniel Julin (dpj) at Carnegie-Mellon University ! 81: * Added another access check for mach_net_server. Only privileged ! 82: * users should be allowed to send packets this way, because there ! 83: * is little or no check against false source addresses. ! 84: * ! 85: * 6-May-88 Daniel Julin (dpj) at Carnegie-Mellon University ! 86: * Added an access check for netipc_listen and ! 87: * (from code by rds). ! 88: * ! 89: * 3-Apr-88 Michael Young (mwyoung) at Carnegie-Mellon University ! 90: * De-linted. ! 91: * ! 92: * 1-Feb-88 David Golub (dbg) at Carnegie-Mellon University ! 93: * Receive_ip_datagram is called at splnet. It must drop priority ! 94: * to spl0 to call the IPC routines, and raise back to splnet when ! 95: * it exits. NOTE that this works ONLY if this is always called ! 96: * from the netisr thread - it will garble interrupt handling ! 97: * horribly. ! 98: * ! 99: * 8-Dec-87 David Black (dlb) at Carnegie-Mellon University ! 100: * Removed spl check code; interrupt disabling is no longer needed ! 101: * because all network code runs in a thread context. Removed TT ! 102: * conditionals. ! 103: * ! 104: * 9-Nov-87 David Golub (dbg) at Carnegie-Mellon University ! 105: * Since network routines now run in a thread context, use ! 106: * msg_queue to send message. ! 107: * ! 108: */ ! 109: ! 110: #import <mach_net.h> ! 111: ! 112: #import <sys/param.h> ! 113: #import <sys/systm.h> ! 114: #import <sys/mbuf.h> ! 115: #import <sys/socket.h> ! 116: ! 117: #import <net/if.h> ! 118: #import <net/route.h> ! 119: ! 120: #import <netinet/in.h> ! 121: #import <netinet/in_systm.h> ! 122: #import <netinet/ip.h> ! 123: #import <netinet/in_pcb.h> ! 124: ! 125: #import <netinet/ip.h> ! 126: #import <netinet/ip_var.h> ! 127: ! 128: #import <mach/mach_types.h> ! 129: #import <mach/mig_errors.h> ! 130: ! 131: #import <kern/zalloc.h> ! 132: ! 133: #import <sys/param.h> /* NBPG, CLBYTES, etc. */ ! 134: ! 135: #import <vm/vm_kern.h> /* kernel_map */ ! 136: #import <kern/parallel.h> ! 137: ! 138: #import <machine/spl.h> ! 139: ! 140: #if MACH_NET ! 141: ! 142: #define IP_MSG_ID 1959 ! 143: ! 144: static ! 145: void ! 146: mach_net_output( ! 147: ipc_kmsg_t kmsg ! 148: ) ! 149: { ! 150: struct ip_msg { ! 151: mach_msg_header_t header; ! 152: struct ip ip; ! 153: } *ip_msg = (struct ip_msg *) &kmsg->ikm_header; ! 154: ! 155: struct mbuf *top; ! 156: register struct mbuf **mp; ! 157: register int size; ! 158: register caddr_t cp; ! 159: static struct route cached_route = { 0 }; ! 160: ! 161: if (!(current_proc()->p_ucred->cr_uid == 0)) ! 162: return; ! 163: ! 164: kmsg->ikm_header.msgh_size += kmsg->ikm_delta; ! 165: /* ! 166: * Only accept simple messages large enough ! 167: * to contain an IP header. ! 168: */ ! 169: ! 170: if ((ip_msg->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) || ! 171: (ip_msg->header.msgh_size < sizeof (struct ip_msg))) ! 172: return; ! 173: ! 174: /* ! 175: * Check that user has supplied a reasonable IP datagram length. ! 176: */ ! 177: ! 178: size = ip_msg->ip.ip_len; ! 179: if ((size <= sizeof(struct ip)) || (size > (ip_msg->header.msgh_size - sizeof(mach_msg_header_t)))) ! 180: return; ! 181: ! 182: /* ! 183: * Remainder of this procedure must execute on master. ! 184: */ ! 185: unix_master(); ! 186: ! 187: /* ! 188: * Copy data to mbuf chain. ! 189: * This loop is similar to those in sosend() (in sys/uipc_socket.c) ! 190: * and if_ubaget() (in vaxif/if_uba.c). ! 191: */ ! 192: top = 0; ! 193: mp = ⊤ ! 194: cp = (caddr_t) &ip_msg->ip; ! 195: while (size > 0) { ! 196: register struct mbuf *m; ! 197: register unsigned int len; ! 198: ! 199: MGET(m, M_WAIT, MT_DATA); ! 200: if (m == 0) { ! 201: m_freem(top); ! 202: unix_release(); ! 203: return; ! 204: } ! 205: if (size >= NBPG/2) { /* heuristic */ ! 206: MCLGET(m); ! 207: if (m->m_len != MCLBYTES) ! 208: m->m_len = len = MIN(MLEN, size); ! 209: else ! 210: m->m_len = len = MIN(MCLBYTES, size); ! 211: } else ! 212: m->m_len = len = MIN(MLEN, size); ! 213: ! 214: bcopy(cp, mtod(m, caddr_t), len); ! 215: cp += len; ! 216: size -= len; ! 217: *mp = m; ! 218: mp = &m->m_next; ! 219: } ! 220: ! 221: /* ! 222: * Use cached route if the destination address is the same. ! 223: * Otherwise, zero the route so that ip_output() will find a new one. ! 224: * The use of the cached route decreases the total time to send a datagram ! 225: * by as much as 40%. ! 226: */ ! 227: if (((struct sockaddr_in *) &cached_route.ro_dst)->sin_addr.s_addr ! 228: != ip_msg->ip.ip_dst.s_addr) { ! 229: if (cached_route.ro_rt) ! 230: RTFREE(cached_route.ro_rt); ! 231: cached_route.ro_rt = 0; ! 232: } ! 233: ! 234: /* ! 235: * Send the mbuf chain. ! 236: * We use the IP_FORWARDING option because ! 237: * the user supplies a complete IP header. ! 238: * Deallocation of the mbuf chain is handled by ip_output(). ! 239: */ ! 240: if (ip_output(top, (struct mbuf *)0, ! 241: &cached_route, (IP_FORWARDING|IP_ALLOWBROADCAST)) != 0) { ! 242: unix_release(); ! 243: return; ! 244: } ! 245: unix_release(); ! 246: return; ! 247: } ! 248: ! 249: boolean_t ! 250: netipc_msg_send( ! 251: ipc_kmsg_t kmsg ! 252: ) ! 253: { ! 254: if (kmsg->ikm_header.msgh_id != IP_MSG_ID) ! 255: return (FALSE); ! 256: ! 257: mach_net_output(kmsg); ! 258: ! 259: return (TRUE); ! 260: } ! 261: ! 262: ! 263: /* ! 264: * Data structures and associated routines ! 265: * to register IPC ports for incoming datagrams. ! 266: * ! 267: * Listeners are organized into buckets by the (IP) protocol number ! 268: * for which they are listening. Within a bucket, the listeners ! 269: * are linked in a list that is maintained by a move-to-front strategy. ! 270: * ! 271: * A zero entry in any of the source or destination ! 272: * fields indicates a wildcard that matches the corresponding field ! 273: * in any incoming datagram. ! 274: * ! 275: * The lock field is used for mutual exclusion when adding listeners. ! 276: * Lookups do not require synchronization because the ipc_port field ! 277: * (used to tell whether a listener is present) is always updated last, ! 278: * in a single operation. ! 279: * ! 280: * The routine to add and remove listeners are called by the user ! 281: * via a Matchmaker interface. Remove_listener() is also called by ! 282: * port_deallocate(). ! 283: */ ! 284: ! 285: struct lbucket { ! 286: decl_simple_lock_data(, lock) ! 287: struct listener *head; ! 288: }; ! 289: ! 290: #define LBHSZ 16 /* kept small -- not many protocols */ ! 291: #define LBHASH(protocol) ((protocol) % LBHSZ) ! 292: struct lbucket listeners[LBHSZ]; /* indexed by hashed protocol */ ! 293: ! 294: struct listener { ! 295: struct listener *next; ! 296: long src_addr; ! 297: long dst_addr; ! 298: unsigned short src_port; ! 299: unsigned short dst_port; ! 300: port_t ipc_port; ! 301: }; ! 302: ! 303: #define LISTENER_MAX 100 ! 304: #define LISTENER_CHUNK 1 ! 305: ! 306: zone_t listener_zone; ! 307: ! 308: #define FOR_EACH_BUCKET(b) for((b)=listeners; (b) < &listeners[LBHSZ]; (b)++) ! 309: #define FOR_EACH_LISTENER(lp,b) for((lp)=(b)->head; (lp) != 0; (lp)=(lp)->next) ! 310: #define LOCK_BUCKET(b, ipl) { ipl = splnet(); simple_lock(&(b)->lock); } ! 311: #define UNLOCK_BUCKET(b, ipl) { simple_unlock(&(b)->lock); splx(ipl); } ! 312: ! 313: #endif ! 314: ! 315: /* ! 316: * Register a listener. ! 317: * No checking is done to prevent conflict with existing listeners. ! 318: */ ! 319: /*ARGSUSED*/ ! 320: #if m68k ! 321: /* ! 322: * Don't know why this pragme is here. My guess is an old M68K ! 323: * compiler bug which has probably been fixed. -PFK ! 324: */ ! 325: #pragma CC_OPT_OFF ! 326: #endif m68k ! 327: kern_return_t netipc_listen(server_port, _src_addr, _dst_addr, ! 328: _src_port, _dst_port, _protocol, ipc_port) ! 329: port_t server_port; /* unused */ ! 330: int _src_addr; ! 331: int _dst_addr; ! 332: int _src_port; ! 333: int _dst_port; ! 334: int _protocol; ! 335: port_t ipc_port; ! 336: { ! 337: #if MACH_NET ! 338: register struct listener *lp; ! 339: register struct lbucket *b; ! 340: int ipl; ! 341: ! 342: long src_addr = _src_addr; ! 343: long dst_addr = _dst_addr; ! 344: unsigned short src_port = _src_port; ! 345: unsigned short dst_port = _dst_port; ! 346: u_char protocol = _protocol; ! 347: ! 348: if (!(current_proc()->p_ucred->cr_uid == 0)) ! 349: return(KERN_NO_ACCESS); ! 350: ! 351: if (ipc_port == PORT_NULL) ! 352: return(KERN_INVALID_ARGUMENT); ! 353: lp = (struct listener *) zalloc(listener_zone); ! 354: lp->src_addr = src_addr; ! 355: lp->src_port = src_port; ! 356: lp->dst_addr = dst_addr; ! 357: lp->dst_port = dst_port; ! 358: ipc_port_reference((ipc_port_t)lp->ipc_port = ipc_port); ! 359: b = &listeners[LBHASH(protocol)]; ! 360: LOCK_BUCKET(b, ipl); ! 361: lp->next = b->head; ! 362: b->head = lp; ! 363: UNLOCK_BUCKET(b, ipl); ! 364: ipc_kobject_set((ipc_port_t)ipc_port, 0, IKOT_NETIPC); ! 365: return(KERN_SUCCESS); ! 366: #else ! 367: return (KERN_FAILURE); ! 368: #endif ! 369: } ! 370: #if m68k ! 371: /* ! 372: * See comment at beginning of routine. ! 373: */ ! 374: #pragma CC_OPT_RESTORE ! 375: #endif m68k ! 376: ! 377: /* ! 378: * Remove all listeners with the specified port. ! 379: */ ! 380: /*ARGSUSED*/ ! 381: kern_return_t netipc_ignore(server_port, ipc_port) ! 382: port_t server_port; /* unused */ ! 383: register port_t ipc_port; ! 384: { ! 385: #if MACH_NET ! 386: register struct lbucket *b; ! 387: register struct listener *prev, *lp; ! 388: kern_return_t result = KERN_FAILURE; ! 389: int ipl; ! 390: ! 391: if (ipc_port == PORT_NULL) ! 392: return KERN_INVALID_ARGUMENT; ! 393: FOR_EACH_BUCKET (b) { ! 394: LOCK_BUCKET(b, ipl); ! 395: prev = b->head; ! 396: FOR_EACH_LISTENER (lp, b) { ! 397: if (lp->ipc_port == ipc_port) { ! 398: result = KERN_SUCCESS; ! 399: if (lp == b->head) { ! 400: b->head = lp->next; ! 401: zfree(listener_zone, (vm_offset_t) lp); ! 402: if ((prev = lp = b->head) == 0) ! 403: break; ! 404: } else { ! 405: prev->next = lp->next; ! 406: zfree(listener_zone, (vm_offset_t) lp); ! 407: lp = prev; ! 408: } ! 409: ipc_port_release((ipc_port_t)ipc_port); ! 410: } ! 411: else { ! 412: prev = lp; ! 413: } ! 414: } ! 415: UNLOCK_BUCKET(b, ipl); ! 416: } ! 417: return(result); ! 418: #else ! 419: return (KERN_FAILURE); ! 420: #endif ! 421: } ! 422: ! 423: #if MACH_NET ! 424: /* ! 425: * Find a listener for the given source, destination, and protocol. ! 426: */ ! 427: port_t find_listener(src_addr, src_port, dst_addr, dst_port, protocol) ! 428: u_long src_addr; ! 429: u_short src_port; ! 430: u_long dst_addr; ! 431: u_short dst_port; ! 432: u_char protocol; ! 433: { ! 434: register struct lbucket *b; ! 435: register struct listener *prev, *lp; ! 436: ! 437: #define ADDR_MATCH(lp, addr) ((lp)->addr==INADDR_ANY || (lp)->addr==(addr)) ! 438: #define PORT_MATCH(lp, port) ((lp)->port==0 || (lp)->port ==(port)) ! 439: ! 440: b = &listeners[LBHASH(protocol)]; ! 441: prev = b->head; ! 442: FOR_EACH_LISTENER(lp, b) { ! 443: /* ! 444: * The following tests should be ordered ! 445: * in increasing likelihood of success. ! 446: */ ! 447: if (PORT_MATCH(lp, dst_port) && ! 448: PORT_MATCH(lp, src_port) && ! 449: ADDR_MATCH(lp, src_addr) && ! 450: ADDR_MATCH(lp, dst_addr)) { ! 451: if (lp != b->head) { ! 452: /* move to front */ ! 453: prev->next = lp->next; ! 454: lp->next = b->head; ! 455: b->head = lp; ! 456: } ! 457: return(lp->ipc_port); ! 458: } else ! 459: prev = lp; ! 460: } ! 461: return(PORT_NULL); ! 462: } ! 463: ! 464: /* ! 465: * IP header plus source and destination ports. ! 466: * Common to both UDP datagrams and TCP segments. ! 467: */ ! 468: struct ip_plus_ports { ! 469: struct ip ip_header; ! 470: u_short src_port; ! 471: u_short dst_port; ! 472: }; ! 473: ! 474: /* ! 475: * Skeletal IPC message header for fast initialization. ! 476: */ ! 477: static mach_msg_header_t ip_msg_template; ! 478: ! 479: #define MACH_NET_MSG_SIZE_MAX 2048 ! 480: zone_t mach_net_kmsg_zone; ! 481: ! 482: void mach_net_init() ! 483: { ! 484: register struct lbucket *b; ! 485: vm_offset_t zone_data; ! 486: ! 487: listener_zone = zinit( ! 488: sizeof(struct listener), ! 489: LISTENER_MAX * sizeof(struct listener), ! 490: LISTENER_CHUNK * sizeof(struct listener), ! 491: FALSE, ! 492: "net listener zone" ! 493: ); ! 494: ip_msg_template.msgh_bits = ! 495: MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0) | ! 496: MACH_MSGH_BITS_OLD_FORMAT; ! 497: ip_msg_template.msgh_size = MACH_NET_MSG_SIZE_MAX - IKM_OVERHEAD; ! 498: ip_msg_template.msgh_remote_port = MACH_PORT_NULL; ! 499: ip_msg_template.msgh_local_port = MACH_PORT_NULL; ! 500: ip_msg_template.msgh_id = IP_MSG_ID; ! 501: FOR_EACH_BUCKET (b) { ! 502: simple_lock_init(&b->lock); ! 503: } ! 504: ! 505: /* ! 506: * Create and allocate some space for our private kmsg zone. ! 507: */ ! 508: mach_net_kmsg_zone = zinit(MACH_NET_MSG_SIZE_MAX, 4 * MACH_NET_MSG_SIZE_MAX, ! 509: MACH_NET_MSG_SIZE_MAX, FALSE, "mach_net messages"); ! 510: zchange(mach_net_kmsg_zone, FALSE, FALSE, FALSE, FALSE); ! 511: (void) kmem_alloc_wired(kernel_map, ! 512: &zone_data, 4 * MACH_NET_MSG_SIZE_MAX); ! 513: zcram(mach_net_kmsg_zone, zone_data, ! 514: 4 * MACH_NET_MSG_SIZE_MAX); ! 515: } ! 516: ! 517: void ! 518: netipc_msg_release( ! 519: ipc_kmsg_t kmsg ! 520: ) ! 521: { ! 522: zfree(mach_net_kmsg_zone, kmsg); ! 523: } ! 524: ! 525: /* ! 526: * This routine is called from ipintr() (in netinet/ip_input.c) ! 527: * to filter incoming datagrams. ! 528: * Returns TRUE if a listener was found (or the packet was dropped), ! 529: * FALSE if the datagram should be passed on to socket code. ! 530: * ! 531: * This routine is always called at splnet, and must return that way. ! 532: * However, it must drop interrupt level to spl0 to use any of the MACH_IPC ! 533: * routines (which do not block interrupts). ! 534: */ ! 535: boolean_t receive_ip_datagram(mp) ! 536: struct mbuf **mp; ! 537: { ! 538: register struct mbuf *m = *mp; ! 539: register struct ip_plus_ports *ipp; ! 540: port_t port; ! 541: ipc_kmsg_t kmsg; ! 542: register caddr_t cp; ! 543: int space; ! 544: ! 545: /* ! 546: * Get IP header and ports (for UDP or TCP) together in first mbuf. ! 547: * Note: IP leaves IP header in first mbuf. ! 548: */ ! 549: ipp = mtod(m, struct ip_plus_ports *); ! 550: if (ipp->ip_header.ip_hl > (sizeof (struct ip) >> 2)) ! 551: ip_stripoptions(&ipp->ip_header, (struct mbuf *)0); ! 552: if (m->m_off > MMAXOFF || m->m_len < sizeof(struct ip_plus_ports)) { ! 553: if ((*mp = m = m_pullup(m, sizeof(struct ip_plus_ports))) == 0) ! 554: return(TRUE); ! 555: ipp = mtod(m, struct ip_plus_ports *); ! 556: } ! 557: ! 558: port = find_listener( ! 559: ipp->ip_header.ip_src.s_addr, ipp->src_port, /* source addr and port */ ! 560: ipp->ip_header.ip_dst.s_addr, ipp->dst_port, /* dest addr and port */ ! 561: ipp->ip_header.ip_p); /* protocol */ ! 562: ! 563: if (port == PORT_NULL) { ! 564: /* ! 565: * No one is listening for this datagram. ! 566: * Pass it on to the socket code. ! 567: */ ! 568: return(FALSE); ! 569: } ! 570: ! 571: /* ! 572: * Drop interrupt level to use MACH_IPC. ! 573: */ ! 574: (void) spl0(); ! 575: ! 576: if ((kmsg = (ipc_kmsg_t) zget(mach_net_kmsg_zone)) == IKM_NULL) { ! 577: /* ! 578: * Could not get a buffer - drop the datagram on the floor. ! 579: printf("receive_ip_datagram: kern_msg_allocate returns null.\n"); ! 580: */ ! 581: m_freem(m); ! 582: ! 583: /* ! 584: * Raise interrupt level to return. ! 585: */ ! 586: (void) splnet(); ! 587: return(TRUE); ! 588: } ! 589: ikm_init_special(kmsg, IKM_SIZE_NETIPC); ! 590: ! 591: /* ! 592: * Undo changes to IP header made by ipintr() ! 593: * before we were called. Unfortunately, the ! 594: * type of service field is gone for good. ! 595: */ ! 596: ipp->ip_header.ip_len += (ipp->ip_header.ip_hl << 2); ! 597: ipp->ip_header.ip_off >>= 3; ! 598: ! 599: /* ! 600: * Copy mbuf chain to kmsg buffer. ! 601: */ ! 602: space = MACH_NET_MSG_SIZE_MAX - sizeof (struct ipc_kmsg); ! 603: cp = (caddr_t)(&kmsg->ikm_header + 1); ! 604: while (m && space > 0) { ! 605: register unsigned int len; ! 606: ! 607: len = MIN(m->m_len, space); ! 608: bcopy(mtod(m, caddr_t), cp, len); ! 609: cp += len; ! 610: space -= len; ! 611: m = m_free(m); ! 612: } ! 613: kmsg->ikm_delta = space; ! 614: space &= ~3; ! 615: kmsg->ikm_delta = space - kmsg->ikm_delta; ! 616: /* ! 617: * Fill in message header and deliver it. ! 618: */ ! 619: kmsg->ikm_header = ip_msg_template; ! 620: kmsg->ikm_header.msgh_size -= space; ! 621: kmsg->ikm_header.msgh_remote_port = port; ! 622: ! 623: ipc_port_reference((ipc_port_t)port); ! 624: ipc_mqueue_send_always(kmsg); ! 625: ! 626: /* ! 627: * Raise interrupt level to return. ! 628: */ ! 629: (void) splnet(); ! 630: return(TRUE); ! 631: } ! 632: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.