/* * Copyright (c) 1998-2021 Apple Inc. All rights reserved. * * @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, 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) 1995 NeXT Computer, Inc. All Rights Reserved */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_domain.c 8.3 (Berkeley) 2/14/95 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_ptrcheck #include /* Needed by bound-checks-soft when enabled. */ #endif /* __has_ptrcheck */ /* Eventhandler context for protocol events */ struct eventhandler_lists_ctxt protoctl_evhdlr_ctxt; static void pr_init_old(struct protosw *, struct domain *); static void init_proto(struct protosw *, struct domain *); static void attach_proto(struct protosw *, struct domain *); static void detach_proto(struct protosw *, struct domain *); static void dom_init_old(struct domain *); static void init_domain(struct domain *); static void attach_domain(struct domain *); static void detach_domain(struct domain *); static struct protosw *pffindprotonotype_locked(int, int, int); static struct domain *pffinddomain_locked(int); static boolean_t domain_timeout_run; /* domain timer is scheduled to run */ static boolean_t domain_draining; static void domain_sched_timeout(void); static void domain_timeout(void *); static LCK_GRP_DECLARE(domain_proto_mtx_grp, "domain"); static LCK_ATTR_DECLARE(domain_proto_mtx_attr, 0, 0); static LCK_MTX_DECLARE_ATTR(domain_proto_mtx, &domain_proto_mtx_grp, &domain_proto_mtx_attr); static LCK_MTX_DECLARE_ATTR(domain_timeout_mtx, &domain_proto_mtx_grp, &domain_proto_mtx_attr); uint64_t _net_uptime; uint64_t _net_uptime_ms; uint64_t _net_uptime_us; #if (DEVELOPMENT || DEBUG) SYSCTL_DECL(_kern_ipc); static int sysctl_do_drain_domains SYSCTL_HANDLER_ARGS; SYSCTL_PROC(_kern_ipc, OID_AUTO, do_drain_domains, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0, sysctl_do_drain_domains, "I", "force manual drain domains"); #endif /* DEVELOPMENT || DEBUG */ static void pr_init_old(struct protosw *pp, struct domain *dp) { #pragma unused(dp) VERIFY(pp->pr_flags & PR_OLD); VERIFY(pp->pr_old != NULL); if (pp->pr_old->pr_init != NULL) { pp->pr_old->pr_init(); } } static void init_proto(struct protosw *pp, struct domain *dp) { VERIFY(pp->pr_flags & PR_ATTACHED); if (!(pp->pr_flags & PR_INITIALIZED)) { TAILQ_INIT(&pp->pr_filter_head); if (pp->pr_init != NULL) { pp->pr_init(pp, dp); } pp->pr_flags |= PR_INITIALIZED; } } static void attach_proto(struct protosw *pp, struct domain *dp) { domain_proto_mtx_lock_assert_held(); VERIFY(!(pp->pr_flags & PR_ATTACHED)); VERIFY(pp->pr_domain == NULL); VERIFY(pp->pr_protosw == NULL); TAILQ_INSERT_TAIL(&dp->dom_protosw, pp, pr_entry); pp->pr_flags |= PR_ATTACHED; pp->pr_domain = dp; pp->pr_protosw = pp; /* do some cleaning up on user request callbacks */ pru_sanitize(pp->pr_usrreqs); } static void detach_proto(struct protosw *pp, struct domain *dp) { domain_proto_mtx_lock_assert_held(); VERIFY(pp->pr_flags & PR_ATTACHED); VERIFY(pp->pr_domain == dp); VERIFY(pp->pr_protosw == pp); TAILQ_REMOVE(&dp->dom_protosw, pp, pr_entry); pp->pr_flags &= ~PR_ATTACHED; pp->pr_domain = NULL; pp->pr_protosw = NULL; } static void dom_init_old(struct domain *dp) { VERIFY(dp->dom_flags & DOM_OLD); VERIFY(dp->dom_old != NULL); if (dp->dom_old->dom_init != NULL) { dp->dom_old->dom_init(); } } static void init_domain(struct domain *dp) { VERIFY(dp->dom_flags & DOM_ATTACHED); if (!(dp->dom_flags & DOM_INITIALIZED)) { lck_mtx_init(&dp->dom_mtx_s, &domain_proto_mtx_grp, &domain_proto_mtx_attr); dp->dom_mtx = &dp->dom_mtx_s; TAILQ_INIT(&dp->dom_protosw); if (dp->dom_init != NULL) { dp->dom_init(dp); } dp->dom_flags |= DOM_INITIALIZED; } /* Recompute for new protocol */ if (max_linkhdr < 16) { /* XXX - Sheesh; everything's ether? */ max_linkhdr = 16; } max_linkhdr = (int)P2ROUNDUP(max_linkhdr, sizeof(uint32_t)); if (dp->dom_protohdrlen > max_protohdr) { max_protohdr = dp->dom_protohdrlen; } max_protohdr = (int)P2ROUNDUP(max_protohdr, sizeof(uint32_t)); max_hdr = max_linkhdr + max_protohdr; max_datalen = MHLEN - max_hdr; } static void attach_domain(struct domain *dp) { domain_proto_mtx_lock_assert_held(); VERIFY(!(dp->dom_flags & DOM_ATTACHED)); TAILQ_INSERT_TAIL(&domains, dp, dom_entry); dp->dom_flags |= DOM_ATTACHED; } static void detach_domain(struct domain *dp) { domain_proto_mtx_lock_assert_held(); VERIFY(dp->dom_flags & DOM_ATTACHED); TAILQ_REMOVE(&domains, dp, dom_entry); dp->dom_flags &= ~DOM_ATTACHED; if (dp->dom_flags & DOM_OLD) { struct domain_old *odp = dp->dom_old; VERIFY(odp != NULL); odp->dom_next = NULL; odp->dom_mtx = NULL; } } /* * Exported (private) routine, indirection of net_add_domain. */ void net_add_domain_old(struct domain_old *odp) { struct domain *dp; domain_guard_t guard __single; VERIFY(odp != NULL); guard = domain_guard_deploy(); if ((dp = pffinddomain_locked(odp->dom_family)) != NULL) { /* * There is really nothing better than to panic here, * as the caller would not have been able to handle * any failures otherwise. */ panic("%s: domain (%d,%s) already exists for %s", __func__, dp->dom_family, dp->dom_name, odp->dom_name); /* NOTREACHED */ } /* Make sure nothing is currently pointing to the odp. */ TAILQ_FOREACH(dp, &domains, dom_entry) { if (dp->dom_old == odp) { panic("%s: domain %p (%d,%s) is already " "associated with %p (%d,%s)\n", __func__, odp, odp->dom_family, odp->dom_name, dp, dp->dom_family, dp->dom_name); /* NOTREACHED */ } } if (odp->dom_protosw != NULL) { panic("%s: domain (%d,%s) protocols need to added " "via net_add_proto\n", __func__, odp->dom_family, odp->dom_name); /* NOTREACHED */ } dp = kalloc_type(struct domain, Z_WAITOK | Z_ZERO | Z_NOFAIL); /* Copy everything but dom_init, dom_mtx, dom_next and dom_refs */ dp->dom_family = odp->dom_family; dp->dom_flags = (odp->dom_flags & DOMF_USERFLAGS) | DOM_OLD; dp->dom_name = odp->dom_name; dp->dom_init = dom_init_old; dp->dom_externalize = odp->dom_externalize; dp->dom_dispose = odp->dom_dispose; dp->dom_rtattach = odp->dom_rtattach; dp->dom_rtoffset = odp->dom_rtoffset; dp->dom_maxrtkey = odp->dom_maxrtkey; dp->dom_protohdrlen = odp->dom_protohdrlen; dp->dom_old = odp; attach_domain(dp); init_domain(dp); /* Point the mutex back to the internal structure's */ odp->dom_mtx = dp->dom_mtx; domain_guard_release(guard); } /* * Exported (private) routine, indirection of net_del_domain. */ int net_del_domain_old(struct domain_old *odp) { struct domain *dp1 __single, *dp2 __single; int error = 0; domain_guard_t guard __single; VERIFY(odp != NULL); guard = domain_guard_deploy(); if (odp->dom_refs != 0) { error = EBUSY; goto done; } TAILQ_FOREACH_SAFE(dp1, &domains, dom_entry, dp2) { if (!(dp1->dom_flags & DOM_OLD)) { continue; } VERIFY(dp1->dom_old != NULL); if (odp == dp1->dom_old) { break; } } if (dp1 != NULL) { struct protosw *pp1 __single, *pp2 __single; VERIFY(dp1->dom_flags & DOM_OLD); VERIFY(dp1->dom_old == odp); /* Remove all protocols attached to this domain */ TAILQ_FOREACH_SAFE(pp1, &dp1->dom_protosw, pr_entry, pp2) { detach_proto(pp1, dp1); if (pp1->pr_usrreqs->pru_flags & PRUF_OLD) { kfree_type(struct pr_usrreqs, pp1->pr_usrreqs); } if (pp1->pr_flags & PR_OLD) { kfree_type(struct protosw, pp1); } } detach_domain(dp1); kfree_type(struct domain, dp1); } else { error = EPFNOSUPPORT; } done: domain_guard_release(guard); return error; } /* * Internal routine, not exported. * * net_add_proto - link a protosw into a domain's protosw chain * * NOTE: Caller must have acquired domain_proto_mtx */ int net_add_proto(struct protosw *pp, struct domain *dp, int doinit) { struct protosw *pp1; /* * This could be called as part of initializing the domain, * and thus DOM_INITIALIZED may not be set (yet). */ domain_proto_mtx_lock_assert_held(); VERIFY(!(pp->pr_flags & PR_ATTACHED)); /* pr_domain is set only after the protocol is attached */ if (pp->pr_domain != NULL) { panic("%s: domain (%d,%s), proto %d has non-NULL pr_domain!", __func__, dp->dom_family, dp->dom_name, pp->pr_protocol); /* NOTREACHED */ } if (pp->pr_usrreqs == NULL) { panic("%s: domain (%d,%s), proto %d has no usrreqs!", __func__, dp->dom_family, dp->dom_name, pp->pr_protocol); /* NOTREACHED */ } TAILQ_FOREACH(pp1, &dp->dom_protosw, pr_entry) { if (pp1->pr_type == pp->pr_type && pp1->pr_protocol == pp->pr_protocol) { return EEXIST; } } attach_proto(pp, dp); if (doinit) { net_init_proto(pp, dp); } return 0; } void net_init_proto(struct protosw *pp, struct domain *dp) { /* * This could be called as part of initializing the domain, * and thus DOM_INITIALIZED may not be set (yet). The protocol * must have been attached via net_addr_protosw() by now. */ domain_proto_mtx_lock_assert_held(); VERIFY(pp->pr_flags & PR_ATTACHED); init_proto(pp, dp); } /* * Exported (private) routine, indirection of net_add_proto. */ int net_add_proto_old(struct protosw_old *opp, struct domain_old *odp) { struct pr_usrreqs_old *opru; struct pr_usrreqs *pru __single = NULL; struct protosw *pp __single = NULL, *pp1; int error = 0; struct domain *dp; domain_guard_t guard __single; /* * This could be called as part of initializing the domain, * and thus DOM_INITIALIZED may not be set (yet). */ guard = domain_guard_deploy(); /* Make sure the domain has been added via net_add_domain */ TAILQ_FOREACH(dp, &domains, dom_entry) { if (!(dp->dom_flags & DOM_OLD)) { continue; } if (dp->dom_old == odp) { break; } } if (dp == NULL) { error = EINVAL; goto done; } TAILQ_FOREACH(pp1, &dp->dom_protosw, pr_entry) { if (pp1->pr_type == opp->pr_type && pp1->pr_protocol == opp->pr_protocol) { error = EEXIST; goto done; } } if ((opru = opp->pr_usrreqs) == NULL) { panic("%s: domain (%d,%s), proto %d has no usrreqs!", __func__, odp->dom_family, odp->dom_name, opp->pr_protocol); /* NOTREACHED */ } pru = kalloc_type(struct pr_usrreqs, Z_WAITOK | Z_ZERO | Z_NOFAIL); pru->pru_flags = PRUF_OLD; pru->pru_abort = opru->pru_abort; pru->pru_accept = opru->pru_accept; pru->pru_attach = opru->pru_attach; pru->pru_bind = opru->pru_bind; pru->pru_connect = opru->pru_connect; pru->pru_connect2 = opru->pru_connect2; pru->pru_control = opru->pru_control; pru->pru_detach = opru->pru_detach; pru->pru_disconnect = opru->pru_disconnect; pru->pru_listen = opru->pru_listen; pru->pru_peeraddr = opru->pru_peeraddr; pru->pru_rcvd = opru->pru_rcvd; pru->pru_rcvoob = opru->pru_rcvoob; pru->pru_send = opru->pru_send; pru->pru_sense = opru->pru_sense; pru->pru_shutdown = opru->pru_shutdown; pru->pru_sockaddr = opru->pru_sockaddr; pru->pru_sosend = opru->pru_sosend; pru->pru_soreceive = opru->pru_soreceive; pru->pru_sopoll = opru->pru_sopoll; pp = kalloc_type(struct protosw, Z_WAITOK | Z_ZERO | Z_NOFAIL); /* * Protocol fast and slow timers are now deprecated. */ if (opp->pr_unused != NULL) { printf("%s: domain (%d,%s), proto %d: pr_fasttimo is " "deprecated and won't be called\n", __func__, odp->dom_family, odp->dom_name, opp->pr_protocol); } if (opp->pr_unused2 != NULL) { printf("%s: domain (%d,%s), proto %d: pr_slowtimo is " "deprecated and won't be called\n", __func__, odp->dom_family, odp->dom_name, opp->pr_protocol); } /* Copy everything but pr_init, pr_next, pr_domain, pr_protosw */ pp->pr_type = opp->pr_type; pp->pr_protocol = opp->pr_protocol; pp->pr_flags = (opp->pr_flags & PRF_USERFLAGS) | PR_OLD; pp->pr_input = opp->pr_input; pp->pr_output = opp->pr_output; pp->pr_ctlinput = opp->pr_ctlinput; pp->pr_ctloutput = opp->pr_ctloutput; pp->pr_usrreqs = pru; pp->pr_init = pr_init_old; pp->pr_drain = opp->pr_drain; pp->pr_sysctl = opp->pr_sysctl; pp->pr_lock = opp->pr_lock; pp->pr_unlock = opp->pr_unlock; pp->pr_getlock = opp->pr_getlock; pp->pr_old = opp; /* attach as well as initialize */ attach_proto(pp, dp); net_init_proto(pp, dp); done: if (error != 0) { printf("%s: domain (%d,%s), proto %d: failed to attach, " "error %d\n", __func__, odp->dom_family, odp->dom_name, opp->pr_protocol, error); kfree_type(struct pr_usrreqs, pru); kfree_type(struct protosw, pp); } domain_guard_release(guard); return error; } /* * Internal routine, not exported. * * net_del_proto - remove a protosw from a domain's protosw chain. * Search the protosw chain for the element with matching data. * Then unlink and return. * * NOTE: Caller must have acquired domain_proto_mtx */ int net_del_proto(int type, int protocol, struct domain *dp) { struct protosw *pp __single; /* * This could be called as part of initializing the domain, * and thus DOM_INITIALIZED may not be set (yet). */ domain_proto_mtx_lock_assert_held(); TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_type == type && pp->pr_protocol == protocol) { break; } } if (pp == NULL) { return ENXIO; } detach_proto(pp, dp); if (pp->pr_usrreqs->pru_flags & PRUF_OLD) { kfree_type(struct pr_usrreqs, pp->pr_usrreqs); } if (pp->pr_flags & PR_OLD) { kfree_type(struct protosw, pp); } return 0; } /* * Exported (private) routine, indirection of net_del_proto. */ int net_del_proto_old(int type, int protocol, struct domain_old *odp) { int error = 0; struct protosw *pp __single; struct domain *dp; domain_guard_t guard __single; /* * This could be called as part of initializing the domain, * and thus DOM_INITIALIZED may not be set (yet). */ guard = domain_guard_deploy(); /* Make sure the domain has been added via net_add_domain */ TAILQ_FOREACH(dp, &domains, dom_entry) { if (!(dp->dom_flags & DOM_OLD)) { continue; } if (dp->dom_old == odp) { break; } } if (dp == NULL) { error = ENXIO; goto done; } TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_type == type && pp->pr_protocol == protocol) { break; } } if (pp == NULL) { error = ENXIO; goto done; } detach_proto(pp, dp); if (pp->pr_usrreqs->pru_flags & PRUF_OLD) { kfree_type(struct pr_usrreqs, pp->pr_usrreqs); } if (pp->pr_flags & PR_OLD) { kfree_type(struct protosw, pp); } done: domain_guard_release(guard); return error; } static void domain_sched_timeout(void) { LCK_MTX_ASSERT(&domain_timeout_mtx, LCK_MTX_ASSERT_OWNED); if (!domain_timeout_run && domain_draining) { domain_timeout_run = TRUE; timeout(domain_timeout, NULL, hz); } } void net_drain_domains(void) { lck_mtx_lock(&domain_timeout_mtx); domain_draining = TRUE; domain_sched_timeout(); lck_mtx_unlock(&domain_timeout_mtx); } extern struct domain inet6domain_s; #if IPSEC extern struct domain keydomain_s; #endif extern struct domain routedomain_s, ndrvdomain_s, inetdomain_s; extern struct domain systemdomain_s, localdomain_s; extern struct domain vsockdomain_s; #if MULTIPATH extern struct domain mpdomain_s; #endif /* MULTIPATH */ static void domain_timeout(void *arg) { #pragma unused(arg) struct protosw *pp; struct domain *dp; domain_guard_t guard __single; lck_mtx_lock(&domain_timeout_mtx); if (domain_draining) { domain_draining = FALSE; lck_mtx_unlock(&domain_timeout_mtx); guard = domain_guard_deploy(); TAILQ_FOREACH(dp, &domains, dom_entry) { TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_drain != NULL) { (*pp->pr_drain)(); } } } domain_guard_release(guard); lck_mtx_lock(&domain_timeout_mtx); } /* re-arm the timer if there's work to do */ domain_timeout_run = FALSE; domain_sched_timeout(); lck_mtx_unlock(&domain_timeout_mtx); } void domaininit(void) { struct domain *dp; domain_guard_t guard __single; eventhandler_lists_ctxt_init(&protoctl_evhdlr_ctxt); guard = domain_guard_deploy(); /* * Add all the static domains to the domains list. route domain * gets added and initialized last, since we need it to attach * rt_tables[] to everything that's already there. This also * means that domains added after this point won't get their * dom_rtattach() called on rt_tables[]. */ attach_domain(&inetdomain_s); attach_domain(&inet6domain_s); #if MULTIPATH attach_domain(&mpdomain_s); #endif /* MULTIPATH */ attach_domain(&systemdomain_s); attach_domain(&localdomain_s); #if IPSEC attach_domain(&keydomain_s); #endif /* IPSEC */ attach_domain(&ndrvdomain_s); attach_domain(&vsockdomain_s); attach_domain(&routedomain_s); /* must be last domain */ /* * Now ask them all to init (XXX including the routing domain, * see above) */ TAILQ_FOREACH(dp, &domains, dom_entry) init_domain(dp); domain_guard_release(guard); } static __inline__ struct domain * pffinddomain_locked(int pf) { struct domain *dp; domain_proto_mtx_lock_assert_held(); TAILQ_FOREACH(dp, &domains, dom_entry) { if (dp->dom_family == pf) { break; } } return dp; } struct protosw * pffindtype(int family, int type) { struct protosw *pp = NULL; struct domain *dp; domain_guard_t guard __single; guard = domain_guard_deploy(); if ((dp = pffinddomain_locked(family)) == NULL) { goto done; } TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_type != 0 && pp->pr_type == type) { goto done; } } done: domain_guard_release(guard); return pp; } /* * Internal routine, not exported. */ struct domain * pffinddomain(int pf) { struct domain *dp; domain_guard_t guard __single; guard = domain_guard_deploy(); dp = pffinddomain_locked(pf); domain_guard_release(guard); return dp; } /* * Exported (private) routine, indirection of pffinddomain. */ struct domain_old * pffinddomain_old(int pf) { struct domain_old *odp = NULL; struct domain *dp; domain_guard_t guard __single; guard = domain_guard_deploy(); if ((dp = pffinddomain_locked(pf)) != NULL && (dp->dom_flags & DOM_OLD)) { odp = dp->dom_old; } domain_guard_release(guard); return odp; } /* * Internal routine, not exported. */ struct protosw * pffindproto(int family, int protocol, int type) { struct protosw *pp; domain_guard_t guard __single; guard = domain_guard_deploy(); pp = pffindproto_locked(family, protocol, type); domain_guard_release(guard); return pp; } struct protosw * pffindproto_locked(int family, int protocol, int type) { struct protosw *maybe = NULL; struct protosw *pp; struct domain *dp; domain_proto_mtx_lock_assert_held(); if (family == 0) { return 0; } dp = pffinddomain_locked(family); if (dp == NULL) { return NULL; } TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if ((pp->pr_protocol == protocol) && (pp->pr_type == type)) { return pp; } if (type == SOCK_RAW && pp->pr_type == SOCK_RAW && pp->pr_protocol == 0 && maybe == NULL) { maybe = pp; } } return maybe; } /* * Exported (private) routine, indirection of pffindproto. */ struct protosw_old * pffindproto_old(int family, int protocol, int type) { struct protosw_old *opr = NULL; struct protosw *pp; domain_guard_t guard __single; guard = domain_guard_deploy(); if ((pp = pffindproto_locked(family, protocol, type)) != NULL && (pp->pr_flags & PR_OLD)) { opr = pp->pr_old; } domain_guard_release(guard); return opr; } static struct protosw * pffindprotonotype_locked(int family, int protocol, int type) { #pragma unused(type) struct domain *dp; struct protosw *pp; domain_proto_mtx_lock_assert_held(); if (family == 0) { return 0; } dp = pffinddomain_locked(family); if (dp == NULL) { return NULL; } TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_protocol == protocol) { return pp; } } return NULL; } struct protosw * pffindprotonotype(int family, int protocol) { struct protosw *pp; domain_guard_t guard __single; if (protocol == 0) { return NULL; } guard = domain_guard_deploy(); pp = pffindprotonotype_locked(family, protocol, 0); domain_guard_release(guard); return pp; } void pfctlinput(int cmd, struct sockaddr *sa) { pfctlinput2(cmd, sa, NULL); } void pfctlinput2(int cmd, struct sockaddr *sa, void *ctlparam) { struct domain *dp; struct protosw *pp; domain_guard_t guard __single; if (sa == NULL) { return; } guard = domain_guard_deploy(); TAILQ_FOREACH(dp, &domains, dom_entry) { TAILQ_FOREACH(pp, &dp->dom_protosw, pr_entry) { if (pp->pr_ctlinput != NULL) { (*pp->pr_ctlinput)(cmd, sa, ctlparam, NULL); } } } domain_guard_release(guard); } void net_update_uptime_with_time(const struct timeval *tvp) { uint64_t tmp; uint64_t seconds = tvp->tv_sec;; uint64_t milliseconds = ((uint64_t)tvp->tv_sec * 1000) + ((uint64_t)tvp->tv_usec / 1000); uint64_t microseconds = ((uint64_t)tvp->tv_sec * USEC_PER_SEC) + (uint64_t)tvp->tv_usec; /* * Round up the timer to the nearest integer value because otherwise * we might setup networking timers that are off by almost 1 second. */ if (tvp->tv_usec > 500000) { seconds++; } tmp = os_atomic_load(&_net_uptime, relaxed); if (tmp < seconds) { os_atomic_cmpxchg(&_net_uptime, tmp, seconds, relaxed); /* * No loop needed. If we are racing with another thread, let's give * the other one the priority. */ } /* update milliseconds variant */ tmp = os_atomic_load(&_net_uptime_ms, relaxed); if (tmp < milliseconds) { os_atomic_cmpxchg(&_net_uptime_ms, tmp, milliseconds, relaxed); } /* update microseconds variant */ tmp = os_atomic_load(&_net_uptime_us, relaxed); if (tmp < microseconds) { os_atomic_cmpxchg(&_net_uptime_us, tmp, microseconds, relaxed); } } void net_update_uptime(void) { struct timeval tv; microuptime(&tv); net_update_uptime_with_time(&tv); } /* * Convert our uin64_t net_uptime to a struct timeval. */ void net_uptime2timeval(struct timeval *tv) { if (tv == NULL) { return; } tv->tv_usec = 0; tv->tv_sec = (time_t)net_uptime(); } /* * An alternative way to obtain the coarse-grained uptime (in seconds) * for networking code which do not require high-precision timestamp, * as this is significantly cheaper than microuptime(). */ uint64_t net_uptime(void) { if (_net_uptime == 0) { net_update_uptime(); } return _net_uptime; } uint64_t net_uptime_ms(void) { if (_net_uptime_ms == 0) { net_update_uptime(); } return _net_uptime_ms; } uint64_t net_uptime_us(void) { if (_net_uptime_us == 0) { net_update_uptime(); } return _net_uptime_us; } void domain_proto_mtx_lock_assert_held(void) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_OWNED); } void domain_proto_mtx_lock_assert_notheld(void) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); } domain_guard_t domain_guard_deploy(void) { net_thread_marks_t marks __single; marks = net_thread_marks_push(NET_THREAD_HELD_DOMAIN); if (marks != net_thread_marks_none) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(&domain_proto_mtx); } else { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_OWNED); } return (domain_guard_t)(const void*)marks; } void domain_guard_release(domain_guard_t guard) { net_thread_marks_t marks __single = (net_thread_marks_t)(const void*)guard; if (marks != net_thread_marks_none) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_OWNED); lck_mtx_unlock(&domain_proto_mtx); net_thread_marks_pop(marks); } else { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); } } domain_unguard_t domain_unguard_deploy(void) { net_thread_marks_t marks __single; marks = net_thread_unmarks_push(NET_THREAD_HELD_DOMAIN); if (marks != net_thread_marks_none) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_OWNED); lck_mtx_unlock(&domain_proto_mtx); } else { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); } return (domain_unguard_t)(const void*)marks; } void domain_unguard_release(domain_unguard_t unguard) { net_thread_marks_t marks __single = (net_thread_marks_t)(const void*)unguard; if (marks != net_thread_marks_none) { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED); lck_mtx_lock(&domain_proto_mtx); net_thread_unmarks_pop(marks); } else { LCK_MTX_ASSERT(&domain_proto_mtx, LCK_MTX_ASSERT_OWNED); } } #if SKYWALK /* The following is used to enqueue work items for interface events */ struct protoctl_event { struct ifnet *ifp; union sockaddr_in_4_6 laddr; union sockaddr_in_4_6 raddr; uint32_t protoctl_event_code; struct protoctl_ev_val val; uint16_t lport; uint16_t rport; uint8_t protocol; }; struct protoctl_event_nwk_wq_entry { struct nwk_wq_entry nwk_wqe; struct protoctl_event protoctl_ev_arg; }; static void protoctl_event_callback(struct nwk_wq_entry *nwk_item) { struct protoctl_event_nwk_wq_entry *p_ev __single = NULL; p_ev = __unsafe_forge_single(struct protoctl_event_nwk_wq_entry *, __container_of(nwk_item, struct protoctl_event_nwk_wq_entry, nwk_wqe)); /* Call this before we walk the tree */ EVENTHANDLER_INVOKE(&protoctl_evhdlr_ctxt, protoctl_event, p_ev->protoctl_ev_arg.ifp, SA(&p_ev->protoctl_ev_arg.laddr), SA(&p_ev->protoctl_ev_arg.raddr), p_ev->protoctl_ev_arg.lport, p_ev->protoctl_ev_arg.rport, p_ev->protoctl_ev_arg.protocol, p_ev->protoctl_ev_arg.protoctl_event_code, &p_ev->protoctl_ev_arg.val); kfree_type(struct protoctl_event_nwk_wq_entry, p_ev); } /* XXX Some PRC events needs extra verification like sequence number checking */ void protoctl_event_enqueue_nwk_wq_entry(struct ifnet *ifp, struct sockaddr *p_laddr, struct sockaddr *p_raddr, uint16_t lport, uint16_t rport, uint8_t protocol, uint32_t protoctl_event_code, struct protoctl_ev_val *p_protoctl_ev_val) { struct protoctl_event_nwk_wq_entry *p_protoctl_ev = NULL; p_protoctl_ev = kalloc_type(struct protoctl_event_nwk_wq_entry, Z_WAITOK | Z_ZERO | Z_NOFAIL); p_protoctl_ev->protoctl_ev_arg.ifp = ifp; if (p_laddr != NULL) { VERIFY(p_laddr->sa_len <= sizeof(p_protoctl_ev->protoctl_ev_arg.laddr)); struct sockaddr_in6 *dst __single = &p_protoctl_ev->protoctl_ev_arg.laddr.sin6; SOCKADDR_COPY(SIN6(p_laddr), dst, p_laddr->sa_len); } if (p_raddr != NULL) { VERIFY(p_raddr->sa_len <= sizeof(p_protoctl_ev->protoctl_ev_arg.raddr)); struct sockaddr_in6 *dst __single = &p_protoctl_ev->protoctl_ev_arg.raddr.sin6; SOCKADDR_COPY(SIN6(p_raddr), dst, p_raddr->sa_len); } p_protoctl_ev->protoctl_ev_arg.lport = lport; p_protoctl_ev->protoctl_ev_arg.rport = rport; p_protoctl_ev->protoctl_ev_arg.protocol = protocol; p_protoctl_ev->protoctl_ev_arg.protoctl_event_code = protoctl_event_code; if (p_protoctl_ev_val != NULL) { bcopy(p_protoctl_ev_val, &(p_protoctl_ev->protoctl_ev_arg.val), sizeof(*p_protoctl_ev_val)); } p_protoctl_ev->nwk_wqe.func = protoctl_event_callback; nwk_wq_enqueue(&p_protoctl_ev->nwk_wqe); } #endif /* SKYWALK */ #if (DEVELOPMENT || DEBUG) static int sysctl_do_drain_domains SYSCTL_HANDLER_ARGS { #pragma unused(arg1, arg2) int error; int dummy = 0; error = sysctl_handle_int(oidp, &dummy, 0, req); if (error || req->newptr == USER_ADDR_NULL) { return error; } net_drain_domains(); return 0; } #endif /* DEVELOPMENT || DEBUG */