X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..eee3565979933af707c711411001ba11fe406a3c:/bsd/net/if_loop.c diff --git a/bsd/net/if_loop.c b/bsd/net/if_loop.c index 422303b87..3baa27434 100644 --- a/bsd/net/if_loop.c +++ b/bsd/net/if_loop.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1982, 1986, 1993 @@ -52,6 +58,13 @@ * SUCH DAMAGE. * * @(#)if_loop.c 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/net/if_loop.c,v 1.47.2.5 2001/07/03 11:01:41 ume Exp $ + */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. */ /* @@ -60,410 +73,487 @@ #include "loop.h" #if NLOOP > 0 +#if NLOOP != 1 +#error "More than one loopback interface is not supported." +#endif + #include #include #include #include #include #include +#include +#include #include #include -#include #include #include #include -#if INET +#if INET #include #include #endif -#if IPX -#include -#include -#endif - #if INET6 -#ifndef INET +#if !INET #include #endif #include #include #endif -#if NS -#include -#include -#endif +#include +#include -#if ISO -#include -#include +#if CONFIG_MACF_NET +#include #endif -#include +#include -#if NETAT -extern struct ifqueue atalkintrq; -#endif +#define LOMTU 16384 +#define LOSNDQ_MAXLEN 256 -#include "bpfilter.h" -#if NBPFILTER > 0 -#include -#endif +#define LO_BPF_TAP_OUT(_m) { \ + if (lo_statics[0].bpf_callback != NULL) { \ + bpf_tap_out(lo_ifp, DLT_NULL, _m, \ + &((struct loopback_header *)_m->m_pkthdr.pkt_hdr)-> \ + protocol, sizeof (u_int32_t)); \ + } \ +} -#define NLOOP_ATTACHMENTS (NLOOP * 12) +#define LO_BPF_TAP_OUT_MULTI(_m) { \ + if (lo_statics[0].bpf_callback != NULL) { \ + struct mbuf *_n; \ + for (_n = _m; _n != NULL; _n = _n->m_nextpkt) \ + LO_BPF_TAP_OUT(_n); \ + } \ +} struct lo_statics_str { - int bpf_mode; - int (*bpf_callback)(struct ifnet *, struct mbuf *); + int bpf_mode; + bpf_packet_func bpf_callback; }; -static struct if_proto *lo_array[NLOOP_ATTACHMENTS]; static struct lo_statics_str lo_statics[NLOOP]; -static lo_count = 0; - - -#ifdef TINY_LOMTU -#define LOMTU (1024+512) -#else -#define LOMTU 16384 -#endif +static int lo_txstart = 0; -struct ifnet loif[NLOOP]; +struct ifnet *lo_ifp = NULL; -void lo_reg_if_mods(); +struct loopback_header { + protocol_family_t protocol; +}; +/* Local forward declerations */ +void loopattach(void); +static errno_t lo_demux(struct ifnet *, struct mbuf *, char *, + protocol_family_t *); +static errno_t +lo_framer(struct ifnet *, struct mbuf **, const struct sockaddr *, + const char *, const char *, u_int32_t *, u_int32_t *); +static errno_t lo_add_proto(struct ifnet *, protocol_family_t, + const struct ifnet_demux_desc *, u_int32_t); +static errno_t lo_del_proto(struct ifnet *, protocol_family_t); +static int lo_output(struct ifnet *, struct mbuf *); +static errno_t lo_pre_enqueue(struct ifnet *, struct mbuf *); +static void lo_start(struct ifnet *); +static errno_t lo_pre_output(struct ifnet *, protocol_family_t, struct mbuf **, + const struct sockaddr *, void *, char *, char *); +static errno_t lo_input(struct ifnet *, protocol_family_t, struct mbuf *); +static void lo_rtrequest(int, struct rtentry *, struct sockaddr *); +static errno_t lo_ioctl(struct ifnet *, u_long, void *); +static errno_t lo_attach_proto(struct ifnet *, protocol_family_t); +static void lo_reg_if_mods(void); +static errno_t lo_set_bpf_tap(struct ifnet *, bpf_tap_mode, bpf_packet_func); +static int sysctl_dequeue_max SYSCTL_HANDLER_ARGS; +static int sysctl_sched_model SYSCTL_HANDLER_ARGS; +static int sysctl_dequeue_scidx SYSCTL_HANDLER_ARGS; + +SYSCTL_DECL(_net_link); + +SYSCTL_NODE(_net_link, OID_AUTO, loopback, CTLFLAG_RW | CTLFLAG_LOCKED, 0, + "loopback interface"); + +#define LO_BW_SLEEP 10 +static u_int32_t lo_bw_sleep_usec = LO_BW_SLEEP; +SYSCTL_UINT(_net_link_loopback, OID_AUTO, bw_sleep_usec, + CTLFLAG_RW | CTLFLAG_LOCKED, &lo_bw_sleep_usec, LO_BW_SLEEP, ""); + +static u_int32_t lo_bw_measure = 0; +SYSCTL_UINT(_net_link_loopback, OID_AUTO, bw_measure, + CTLFLAG_RW | CTLFLAG_LOCKED, &lo_bw_measure, 0, ""); + +static u_int32_t lo_dequeue_max = LOSNDQ_MAXLEN; +SYSCTL_PROC(_net_link_loopback, OID_AUTO, max_dequeue, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_dequeue_max, LOSNDQ_MAXLEN, + sysctl_dequeue_max, "I", "Maximum number of packets dequeued at a time"); + +static u_int32_t lo_sched_model = IFNET_SCHED_MODEL_NORMAL; +SYSCTL_PROC(_net_link_loopback, OID_AUTO, sched_model, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_sched_model, + IFNET_SCHED_MODEL_NORMAL, sysctl_sched_model, "I", "Scheduling model"); + +static u_int32_t lo_dequeue_sc = MBUF_SC_BE; +static int lo_dequeue_scidx = MBUF_SCIDX(MBUF_SC_BE); +SYSCTL_PROC(_net_link_loopback, OID_AUTO, dequeue_sc, + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_dequeue_scidx, + MBUF_SC_BE, sysctl_dequeue_scidx, "I", "Dequeue a specific SC index"); + +static errno_t +lo_demux(struct ifnet *ifp, struct mbuf *m, char *frame_header, + protocol_family_t *protocol_family) +{ +#pragma unused(ifp, m) + struct loopback_header *header = + (struct loopback_header *)(void *)frame_header; + *protocol_family = header->protocol; + return (0); +} -int lo_demux(ifp, m, frame_header, proto) - struct ifnet *ifp; - struct mbuf *m; - char *frame_header; - struct if_proto **proto; +static errno_t +lo_framer(struct ifnet *ifp, struct mbuf **m, const struct sockaddr *dest, + const char *dest_linkaddr, const char *frame_type, + u_int32_t *prepend_len, u_int32_t *postpend_len) { - int i; - struct if_proto **proto_ptr; +#pragma unused(ifp, dest, dest_linkaddr) + struct loopback_header *header; - proto_ptr = mtod(m, struct if_proto **); - *proto = *proto_ptr; - m_adj(m, sizeof(u_long)); - return 0; -} + M_PREPEND(*m, sizeof (struct loopback_header), M_WAITOK, 1); + if (*m == NULL) { + /* Tell caller not to try to free passed-in mbuf */ + return (EJUSTRETURN); + } + if (prepend_len != NULL) + *prepend_len = sizeof (struct loopback_header); + if (postpend_len != NULL) + *postpend_len = 0; -int lo_framer(ifp, m, dest, dest_linkaddr, frame_type) - struct ifnet *ifp; - struct mbuf **m; - struct sockaddr *dest; - char *dest_linkaddr; - char *frame_type; + header = mtod(*m, struct loopback_header *); + bcopy(frame_type, &header->protocol, sizeof (u_int32_t)); + return (0); +} +static errno_t +lo_add_proto(struct ifnet *interface, protocol_family_t protocol_family, + const struct ifnet_demux_desc *demux_array, u_int32_t demux_count) { - char *to_ptr; - - M_PREPEND(*m, (4 * sizeof(u_long)), M_WAITOK); - to_ptr = mtod(*m, char *); - bcopy(dest_linkaddr, to_ptr, (4 * sizeof(u_long))); - return 0; +#pragma unused(interface, protocol_family, demux_array, demux_count) + return (0); } -static -int lo_add_if(struct ifnet *ifp) +static errno_t +lo_del_proto(struct ifnet *ifp, protocol_family_t protocol) { - ifp->if_demux = lo_demux; - ifp->if_framer = lo_framer; - ifp->if_event = 0; - return 0; +#pragma unused(ifp, protocol) + return (0); } -static -int lo_del_if(struct ifnet *ifp) +static void +lo_tx_compl(struct ifnet *ifp, struct mbuf *m) { - return 0; -} + errno_t error; + + if ((ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) { + boolean_t requested; + + error = mbuf_get_timestamp_requested(m, &requested); + if (requested) { + struct timespec now; + u_int64_t ts; + nanouptime(&now); + net_timernsec(&now, &ts); + error = mbuf_set_timestamp(m, ts, TRUE); + if (error != 0) + printf("%s: mbuf_set_timestamp() failed %d\n", + __func__, error); + } + } + error = mbuf_set_status(m, KERN_SUCCESS); + if (error != 0) + printf("%s: mbuf_set_status() failed %d\n", + __func__, error); + ifnet_tx_compl(ifp, m); +} -static -int lo_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag) +/* + * Output callback. + * + * This routine is called only when lo_txstart is disabled. + */ +static int +lo_output(struct ifnet *ifp, struct mbuf *m_list) { - int i; + struct mbuf *m, *m_tail = NULL; + struct ifnet_stat_increment_param s; + u_int32_t cnt = 0, len = 0; - for (i=0; i < lo_count; i++) - if (lo_array[i] == 0) { - lo_array[lo_count] = proto; - return 0; - } + bzero(&s, sizeof(s)); - if ((i == lo_count) && (lo_count == NLOOP_ATTACHMENTS)) - panic("lo_add_proto -- Too many attachments\n"); + for (m = m_list; m; m = m->m_nextpkt) { + VERIFY(m->m_flags & M_PKTHDR); + cnt++; + len += m->m_pkthdr.len; - lo_array[lo_count++] = proto; - return 0; -} + /* + * Don't overwrite the rcvif field if it is in use. + * This is used to match multicast packets, sent looping + * back, with the appropriate group record on input. + */ + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.pkt_flags |= PKTF_LOOP; + m->m_pkthdr.pkt_hdr = mtod(m, char *); -static -int lo_del_proto(struct if_proto *proto, u_long dl_tag) -{ - int i; + /* loopback checksums are always OK */ + m->m_pkthdr.csum_data = 0xffff; + m->m_pkthdr.csum_flags = + CSUM_DATA_VALID | CSUM_PSEUDO_HDR | + CSUM_IP_CHECKED | CSUM_IP_VALID; + + m_adj(m, sizeof (struct loopback_header)); - for (i=0; i < lo_count; i++) - if (lo_array[i] == proto) { - lo_array[i] = 0; - return 0; + LO_BPF_TAP_OUT(m); + if (m->m_nextpkt == NULL) { + m_tail = m; + } + lo_tx_compl(ifp, m); } - return ENOENT; + s.packets_in = cnt; + s.packets_out = cnt; + s.bytes_in = len; + s.bytes_out = len; + + return (ifnet_input_extended(ifp, m_list, m_tail, &s)); } -static int -lo_output(ifp, m) - struct ifnet *ifp; - register struct mbuf *m; -{ u_int *prepend_ptr; - u_int af; - u_long saved_header[3]; - - if ((m->m_flags & M_PKTHDR) == 0) - panic("lo_output: no HDR"); - - /* - * Don't overwrite the rcvif field if it is in use. - * This is used to match multicast packets, sent looping - * back, with the appropriate group record on input. - */ - if (m->m_pkthdr.rcvif == NULL) - m->m_pkthdr.rcvif = ifp; - prepend_ptr = mtod(m, u_int *); - af = *prepend_ptr; - m_adj(m, sizeof(u_int)); - - -#if NBPFILTER > 0 - if (lo_statics[ifp->if_unit].bpf_mode != BPF_TAP_DISABLE) { - struct mbuf m0, *n; - - bcopy(mtod(m, caddr_t), &saved_header[0], (3 * sizeof(u_long))); - m_adj(m, (3 * sizeof(u_long))); - - n = m; - if (ifp->if_bpf->bif_dlt == DLT_NULL) { - /* - * We need to prepend the address family as - * a four byte field. Cons up a dummy header - * to pacify bpf. This is safe because bpf - * will only read from the mbuf (i.e., it won't - * try to free it or keep a pointer a to it). - */ - m0.m_next = m; - m0.m_len = 4; - m0.m_data = (char *)⁡ - n = &m0; - } +/* + * Pre-enqueue callback. + * + * This routine is called only when lo_txstart is enabled. + */ +static errno_t +lo_pre_enqueue(struct ifnet *ifp, struct mbuf *m0) +{ + struct mbuf *m = m0, *n; + int error = 0; - (*lo_statics[ifp->if_unit].bpf_callback)(ifp, n); + while (m != NULL) { + VERIFY(m->m_flags & M_PKTHDR); - M_PREPEND(m, (3 * sizeof(u_long)), M_WAITOK); - bcopy(&saved_header[0], mtod(m, caddr_t), (3 * sizeof(u_long))); + n = m->m_nextpkt; + m->m_nextpkt = NULL; - } -#endif + /* + * Don't overwrite the rcvif field if it is in use. + * This is used to match multicast packets, sent looping + * back, with the appropriate group record on input. + */ + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = ifp; - ifp->if_ibytes += m->m_pkthdr.len; - ifp->if_obytes += m->m_pkthdr.len; + m->m_pkthdr.pkt_flags |= PKTF_LOOP; + m->m_pkthdr.pkt_hdr = mtod(m, char *); - ifp->if_opackets++; - ifp->if_ipackets++; + /* loopback checksums are always OK */ + m->m_pkthdr.csum_data = 0xffff; + m->m_pkthdr.csum_flags = + CSUM_DATA_VALID | CSUM_PSEUDO_HDR | + CSUM_IP_CHECKED | CSUM_IP_VALID; - /* WARNING - * This won't work for loopbacked multicast - */ - m->m_pkthdr.header = mtod(m, char *); - m->m_pkthdr.aux = ifp; /* HACKERY */ - return dlil_input(ifp, m, m); -} + m_adj(m, sizeof (struct loopback_header)); + /* + * Let the callee free it in case of error, + * and perform any necessary accounting. + */ + (void) ifnet_enqueue(ifp, m); -/* - * This is a common pre-output route used by INET, AT, etc. This could - * (should?) be split into separate pre-output routines for each protocol. - */ + m = n; + } -static int -lo_pre_output(ifp, m, dst, route, frame_type, dst_addr, dl_tag) - struct ifnet *ifp; - register struct mbuf **m; - struct sockaddr *dst; - void *route; - char *frame_type; - char *dst_addr; - u_long dl_tag; + return (error); +} +/* + * Start output callback. + * + * This routine is invoked by the start worker thread; because we never call + * it directly, there is no need do deploy any serialization mechanism other + * than what's already used by the worker thread, i.e. this is already single + * threaded. + * + * This routine is called only when lo_txstart is enabled. + */ +static void +lo_start(struct ifnet *ifp) { - int s, isr; - register struct ifqueue *ifq = 0; - u_long *prepend_ptr; - register struct rtentry *rt = (struct rtentry *) route; - - prepend_ptr = (u_long *) dst_addr; - if (((*m)->m_flags & M_PKTHDR) == 0) - panic("looutput no HDR"); - - if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) { - if (rt->rt_flags & RTF_BLACKHOLE) { - m_freem(*m); - return EJUSTRETURN; - } - else - return ((rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH); - } + struct ifnet_stat_increment_param s; + + bzero(&s, sizeof (s)); + + for (;;) { + struct mbuf *m = NULL, *m_tail = NULL; + u_int32_t cnt, len = 0; + int sleep_chan = 0; + struct timespec ts; + + if (lo_sched_model == IFNET_SCHED_MODEL_NORMAL) { + if (ifnet_dequeue_multi(ifp, lo_dequeue_max, &m, + &m_tail, &cnt, &len) != 0) + break; + } else { + if (ifnet_dequeue_service_class_multi(ifp, + lo_dequeue_sc, lo_dequeue_max, &m, + &m_tail, &cnt, &len) != 0) + break; + } - switch (dst->sa_family) { -#if INET - case AF_INET: - ifq = &ipintrq; - isr = NETISR_IP; - break; -#endif -#if INET6 - case AF_INET6: - ifq = &ip6intrq; - isr = NETISR_IPV6; - break; -#endif -#if IPX - case AF_IPX: - ifq = &ipxintrq; - isr = NETISR_IPX; - break; -#endif -#if NS - case AF_NS: - ifq = &nsintrq; - isr = NETISR_NS; - break; -#endif -#if ISO - case AF_ISO: - ifq = &clnlintrq; - isr = NETISR_ISO; - break; -#endif -#if NETAT - case AF_APPLETALK: - ifq = &atalkintrq; - isr = NETISR_APPLETALK; - break; -#endif NETAT - default: - return (EAFNOSUPPORT); - } + LO_BPF_TAP_OUT_MULTI(m); + + if (lo_bw_measure) { + if (cnt >= if_bw_measure_size) + ifnet_transmit_burst_start(ifp, m); + if (lo_bw_sleep_usec > 0) { + bzero(&ts, sizeof(ts)); + ts.tv_nsec = (lo_bw_sleep_usec << 10) * cnt; + + /* Add msleep with timeout */ + (void) msleep(&sleep_chan, NULL, + PSOCK, "lo_start", &ts); + } + if (cnt >= if_bw_measure_size) + ifnet_transmit_burst_end(ifp, m_tail); + } + lo_tx_compl(ifp, m); - *prepend_ptr++ = dst->sa_family; /* For lo_output(BPF) */ - *prepend_ptr++ = dlttoproto(dl_tag); /* For lo_demux */ - *prepend_ptr++ = (u_long) ifq; /* For lo_input */ - *prepend_ptr = isr; /* For lo_input */ + /* stats are required for extended variant */ + s.packets_in = cnt; + s.packets_out = cnt; + s.bytes_in = len; + s.bytes_out = len; - return 0; + (void) ifnet_input_extended(ifp, m, m_tail, &s); + } } +/* + * This is a common pre-output route used by INET and INET6. This could + * (should?) be split into separate pre-output routines for each protocol. + */ +static errno_t +lo_pre_output(struct ifnet *ifp, protocol_family_t protocol_family, + struct mbuf **m, const struct sockaddr *dst, void *route, char *frame_type, + char *dst_addr) +{ +#pragma unused(ifp, dst, dst_addr) + struct rtentry *rt = route; + + VERIFY((*m)->m_flags & M_PKTHDR); + + (*m)->m_flags |= M_LOOP; + + if (rt != NULL) { + u_int32_t rt_flags = rt->rt_flags; + if (rt_flags & (RTF_REJECT | RTF_BLACKHOLE)) { + if (rt_flags & RTF_BLACKHOLE) { + m_freem(*m); + return (EJUSTRETURN); + } else { + return ((rt_flags & RTF_HOST) ? + EHOSTUNREACH : ENETUNREACH); + } + } + } + bcopy(&protocol_family, frame_type, sizeof (protocol_family)); + return (0); +} /* * lo_input - This should work for all attached protocols that use the * ifq/schednetisr input mechanism. */ +static errno_t +lo_input(struct ifnet *ifp, protocol_family_t protocol_family, struct mbuf *m) +{ +#pragma unused(ifp, protocol_family) + if ((ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) { + errno_t error; + struct timespec now; + u_int64_t ts; -int -lo_input(m, fh, ifp, dl_tag, sync_ok) - register struct mbuf *m; - char *fh; - struct ifnet *ifp; - u_long dl_tag; - int sync_ok; + nanouptime(&now); + net_timernsec(&now, &ts); -{ - u_long *prepend_ptr; - int s, isr; - register struct ifqueue *ifq = 0; - - prepend_ptr = mtod(m, u_long *); - ifq = (struct ifqueue *) *prepend_ptr++; - isr = *prepend_ptr; - m_adj(m, (2 * sizeof(u_long))); - - s = splimp(); - if (IF_QFULL(ifq)) { - IF_DROP(ifq); - m_freem(m); - splx(s); - return (EJUSTRETURN); + error = mbuf_set_timestamp(m, ts, TRUE); + if (error != 0) + printf("%s: mbuf_set_timestamp() failed %d\n", + __func__, error); } - IF_ENQUEUE(ifq, m); - schednetisr(isr); - splx(s); + if (proto_input(protocol_family, m) != 0) + m_freem(m); return (0); } - - - /* ARGSUSED */ static void -lortrequest(cmd, rt, sa) - int cmd; - struct rtentry *rt; - struct sockaddr *sa; +lo_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa) { - if (rt) { +#pragma unused(cmd, sa) + if (rt != NULL) { + RT_LOCK_ASSERT_HELD(rt); rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */ /* * For optimal performance, the send and receive buffers * should be at least twice the MTU plus a little more for * overhead. */ - rt->rt_rmx.rmx_recvpipe = - rt->rt_rmx.rmx_sendpipe = 3 * LOMTU; + rt->rt_rmx.rmx_recvpipe = rt->rt_rmx.rmx_sendpipe = 3 * LOMTU; } } /* * Process an ioctl request. */ -/* ARGSUSED */ -static int -loioctl(dl_tag, ifp, cmd, data) - u_long dl_tag; - register struct ifnet *ifp; - u_long cmd; - void *data; +static errno_t +lo_ioctl(struct ifnet *ifp, u_long cmd, void *data) { - register struct ifaddr *ifa; - register struct ifreq *ifr = (struct ifreq *)data; - register int error = 0; + int error = 0; switch (cmd) { - case SIOCSIFADDR: - ifp->if_flags |= IFF_UP | IFF_RUNNING; - ifa = (struct ifaddr *)data; - ifa->ifa_rtrequest = lortrequest; + case SIOCSIFADDR: { /* struct ifaddr pointer */ + struct ifaddr *ifa = data; + + ifnet_set_flags(ifp, IFF_UP|IFF_RUNNING, IFF_UP|IFF_RUNNING); + IFA_LOCK_SPIN(ifa); + ifa->ifa_rtrequest = lo_rtrequest; + IFA_UNLOCK(ifa); /* * Everything else is done at a higher level. */ break; + } + + case SIOCADDMULTI: /* struct ifreq */ + case SIOCDELMULTI: { /* struct ifreq */ + struct ifreq *ifr = data; - case SIOCADDMULTI: - case SIOCDELMULTI: - if (ifr == 0) { + if (ifr == NULL) { error = EAFNOSUPPORT; /* XXX */ break; } @@ -483,132 +573,233 @@ loioctl(dl_tag, ifp, cmd, data) break; } break; + } + + case SIOCSIFMTU: { /* struct ifreq */ + struct ifreq *ifr = data; - case SIOCSIFMTU: - ifp->if_mtu = ifr->ifr_mtu; + bcopy(&ifr->ifr_mtu, &ifp->if_mtu, sizeof (int)); break; + } - case SIOCSIFFLAGS: + case SIOCSIFFLAGS: /* struct ifreq */ + case SIOCSIFTIMESTAMPENABLE: + case SIOCSIFTIMESTAMPDISABLE: break; default: error = EOPNOTSUPP; + break; } return (error); } #endif /* NLOOP > 0 */ -int lo_shutdown() +static errno_t +lo_attach_proto(struct ifnet *ifp, protocol_family_t protocol_family) { - return 0; -} + struct ifnet_attach_proto_param_v2 proto; + errno_t result = 0; + bzero(&proto, sizeof (proto)); + proto.input = lo_input; + proto.pre_output = lo_pre_output; -void lo_reg_if_mods() -{ - struct dlil_ifmod_reg_str lo_ifmod; + result = ifnet_attach_protocol_v2(ifp, protocol_family, &proto); - lo_ifmod.add_if = lo_add_if; - lo_ifmod.del_if = lo_del_if; - lo_ifmod.add_proto = lo_add_proto; - lo_ifmod.del_proto = lo_del_proto; - lo_ifmod.ifmod_ioctl = 0; - lo_ifmod.shutdown = lo_shutdown; + if (result && result != EEXIST) { + printf("lo_attach_proto: ifnet_attach_protocol for %u " + "returned=%d\n", protocol_family, result); + } - if (dlil_reg_if_modules(APPLE_IF_FAM_LOOPBACK, &lo_ifmod)) - panic("Couldn't register lo modules\n"); + return (result); } - -u_long lo_attach_inet(struct ifnet *ifp) +static void +lo_reg_if_mods(void) { - struct dlil_proto_reg_str reg; - struct dlil_demux_desc desc; - u_long dl_tag=0; - short native=0; - int stat; - int i; - - for (i=0; i < lo_count; i++) { - if ((lo_array[i]) && (lo_array[i]->ifp == ifp)) { - if (lo_array[i]->protocol_family == PF_INET) - return lo_array[i]->dl_tag; - } - } - - TAILQ_INIT(®.demux_desc_head); - desc.type = DLIL_DESC_RAW; - desc.variants.bitmask.proto_id_length = 0; - desc.variants.bitmask.proto_id = 0; - desc.variants.bitmask.proto_id_mask = 0; - desc.native_type = (char *) &native; - TAILQ_INSERT_TAIL(®.demux_desc_head, &desc, next); - reg.interface_family = ifp->if_family; - reg.unit_number = ifp->if_unit; - reg.input = lo_input; - reg.pre_output = lo_pre_output; - reg.event = 0; - reg.offer = 0; - reg.ioctl = loioctl; - reg.default_proto = 0; - reg.protocol_family = PF_INET; - - stat = dlil_attach_protocol(®, &dl_tag); - if (stat) { - panic("lo_attach_inet can't attach interface\n"); - } - - return dl_tag; + int error; + + /* Register protocol registration functions */ + if ((error = proto_register_plumber(PF_INET, + APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0) + printf("proto_register_plumber failed for AF_INET " + "error=%d\n", error); + + if ((error = proto_register_plumber(PF_INET6, + APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0) + printf("proto_register_plumber failed for AF_INET6 " + "error=%d\n", error); } - -int lo_set_bpf_tap(struct ifnet *ifp, int mode, int (*bpf_callback)(struct ifnet *, struct mbuf *)) +static errno_t +lo_set_bpf_tap(struct ifnet *ifp, bpf_tap_mode mode, + bpf_packet_func bpf_callback) { + VERIFY(ifp == lo_ifp); - /* - * NEED MUTEX HERE XXX - */ - if (mode == BPF_TAP_DISABLE) { - lo_statics[ifp->if_unit].bpf_mode = mode; - lo_statics[ifp->if_unit].bpf_callback = bpf_callback; - } - else { - lo_statics[ifp->if_unit].bpf_callback = bpf_callback; - lo_statics[ifp->if_unit].bpf_mode = mode; + lo_statics[0].bpf_mode = mode; + + switch (mode) { + case BPF_TAP_DISABLE: + case BPF_TAP_INPUT: + lo_statics[0].bpf_callback = NULL; + break; + + case BPF_TAP_OUTPUT: + case BPF_TAP_INPUT_OUTPUT: + lo_statics[0].bpf_callback = bpf_callback; + break; } - return 0; + return (0); } - /* ARGSUSED */ void -loopattach(dummy) - void *dummy; +loopattach(void) { - register struct ifnet *ifp; - register int i = 0; + struct ifnet_init_eparams lo_init; + errno_t result = 0; + + PE_parse_boot_argn("lo_txstart", &lo_txstart, sizeof (lo_txstart)); - thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL); lo_reg_if_mods(); - for (ifp = loif; i < NLOOP; ifp++) { - lo_statics[i].bpf_callback = 0; - lo_statics[i].bpf_mode = BPF_TAP_DISABLE; - ifp->if_name = "lo"; - ifp->if_family = APPLE_IF_FAM_LOOPBACK; - ifp->if_unit = i++; - ifp->if_mtu = LOMTU; - ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST; - ifp->if_ioctl = 0; - ifp->if_set_bpf_tap = lo_set_bpf_tap; - ifp->if_output = lo_output; - ifp->if_type = IFT_LOOP; - dlil_if_attach(ifp); -#if NBPFILTER > 0 - bpfattach(ifp, DLT_NULL, sizeof(u_int)); + lo_statics[0].bpf_callback = NULL; + lo_statics[0].bpf_mode = BPF_TAP_DISABLE; + + bzero(&lo_init, sizeof (lo_init)); + lo_init.ver = IFNET_INIT_CURRENT_VERSION; + lo_init.len = sizeof (lo_init); + lo_init.sndq_maxlen = LOSNDQ_MAXLEN; + if (lo_txstart) { + lo_init.flags = 0; + lo_init.pre_enqueue = lo_pre_enqueue; + lo_init.start = lo_start; + lo_init.output_sched_model = lo_sched_model; + } else { + lo_init.flags = IFNET_INIT_LEGACY; + lo_init.output = lo_output; + } + lo_init.name = "lo"; + lo_init.unit = 0; + lo_init.family = IFNET_FAMILY_LOOPBACK; + lo_init.type = IFT_LOOP; + lo_init.demux = lo_demux; + lo_init.add_proto = lo_add_proto; + lo_init.del_proto = lo_del_proto; + lo_init.framer_extended = lo_framer; + lo_init.softc = &lo_statics[0]; + lo_init.ioctl = lo_ioctl; + lo_init.set_bpf_tap = lo_set_bpf_tap; + + result = ifnet_allocate_extended(&lo_init, &lo_ifp); + if (result != 0) { + panic("%s: couldn't allocate loopback ifnet (%d)\n", + __func__, result); + /* NOTREACHED */ + } + + ifnet_set_mtu(lo_ifp, LOMTU); + ifnet_set_flags(lo_ifp, IFF_LOOPBACK | IFF_MULTICAST, + IFF_LOOPBACK | IFF_MULTICAST); + ifnet_set_offload(lo_ifp, + IFNET_CSUM_IP | IFNET_CSUM_TCP | IFNET_CSUM_UDP | + IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6 | IFNET_IPV6_FRAGMENT | + IFNET_CSUM_FRAGMENT | IFNET_IP_FRAGMENT | IFNET_MULTIPAGES | + IFNET_TX_STATUS | IFNET_SW_TIMESTAMP); + ifnet_set_hdrlen(lo_ifp, sizeof (struct loopback_header)); + ifnet_set_eflags(lo_ifp, IFEF_SENDLIST, IFEF_SENDLIST); + +#if CONFIG_MACF_NET + mac_ifnet_label_init(ifp); #endif + + result = ifnet_attach(lo_ifp, NULL); + if (result != 0) { + panic("%s: couldn't attach loopback ifnet (%d)\n", + __func__, result); + /* NOTREACHED */ } - thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL); + bpfattach(lo_ifp, DLT_NULL, sizeof (u_int32_t)); +} + +static int +sysctl_dequeue_max SYSCTL_HANDLER_ARGS +{ +#pragma unused(arg1, arg2) + u_int32_t i; + int err; + + i = lo_dequeue_max; + + err = sysctl_handle_int(oidp, &i, 0, req); + if (err != 0 || req->newptr == USER_ADDR_NULL) + return (err); + + if (i < 1) + i = 1; + else if (i > LOSNDQ_MAXLEN) + i = LOSNDQ_MAXLEN; + + lo_dequeue_max = i; + + return (err); +} + +static int +sysctl_sched_model SYSCTL_HANDLER_ARGS +{ +#pragma unused(arg1, arg2) + u_int32_t i; + int err; + + i = lo_sched_model; + + err = sysctl_handle_int(oidp, &i, 0, req); + if (err != 0 || req->newptr == USER_ADDR_NULL) + return (err); + + switch (i) { + case IFNET_SCHED_MODEL_NORMAL: + case IFNET_SCHED_MODEL_DRIVER_MANAGED: + case IFNET_SCHED_MODEL_FQ_CODEL: + break; + + default: + err = EINVAL; + break; + } + + if (err == 0 && (err = ifnet_set_output_sched_model(lo_ifp, i)) == 0) + lo_sched_model = i; + + return (err); +} + +static int +sysctl_dequeue_scidx SYSCTL_HANDLER_ARGS +{ +#pragma unused(arg1, arg2) + u_int32_t i; + int err; + + i = lo_dequeue_scidx; + + err = sysctl_handle_int(oidp, &i, 0, req); + if (err != 0 || req->newptr == USER_ADDR_NULL) + return (err); + + if (!MBUF_VALID_SCIDX(i)) + return (EINVAL); + + if (lo_sched_model != IFNET_SCHED_MODEL_DRIVER_MANAGED) + return (ENODEV); + + lo_dequeue_sc = m_service_class_from_idx(i); + lo_dequeue_scidx = MBUF_SCIDX(lo_dequeue_sc); + + return (err); }