X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/060df5ea7c632b1ac8cc8aac1fb59758165c2084..6d2010ae8f7a6078e10b361c6962983bab233e0f:/bsd/net/netsrc.c?ds=sidebyside diff --git a/bsd/net/netsrc.c b/bsd/net/netsrc.c new file mode 100644 index 000000000..2c1037c26 --- /dev/null +++ b/bsd/net/netsrc.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2011 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@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +static errno_t netsrc_ctlsend(kern_ctl_ref, uint32_t, void *, mbuf_t, int); +static errno_t netsrc_ctlconnect(kern_ctl_ref, struct sockaddr_ctl *, void **); +static errno_t netsrc_ipv4(kern_ctl_ref, uint32_t, struct netsrc_req *); +static errno_t netsrc_ipv6(kern_ctl_ref, uint32_t, struct netsrc_req *); + +static kern_ctl_ref netsrc_ctlref = NULL; + +__private_extern__ void +netsrc_init(void) +{ + errno_t error; + struct kern_ctl_reg netsrc_ctl = { + .ctl_connect = netsrc_ctlconnect, + .ctl_send = netsrc_ctlsend, + }; + + strlcpy(netsrc_ctl.ctl_name, NETSRC_CTLNAME, sizeof(NETSRC_CTLNAME)); + + if ((error = ctl_register(&netsrc_ctl, &netsrc_ctlref))) + printf("%s: ctl_register failed %d\n", __func__, error); +} + +static errno_t +netsrc_ctlconnect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo) +{ +#pragma unused(kctl, sac, uinfo) + + /* + * We don't need to do anything here. This callback is only necessary + * for ctl_register() to succeed. + */ + return (0); +} + +static errno_t +netsrc_ctlsend(kern_ctl_ref kctl, uint32_t unit, void *uinfo, mbuf_t m, + int flags) +{ +#pragma unused(uinfo, flags) + errno_t error; + struct netsrc_req *nrq, storage; + + if (mbuf_pkthdr_len(m) < sizeof(*nrq)) { + error = EINVAL; + goto out; + } + if (mbuf_len(m) >= sizeof(*nrq)) + nrq = mbuf_data(m); + else { + mbuf_copydata(m, 0, sizeof(storage), &storage); + nrq = &storage; + } + /* We only have one version right now. */ + if (nrq->nrq_ver != NETSRC_VERSION1) { + error = EINVAL; + goto out; + } + switch (nrq->nrq_sin.sin_family) { + case AF_INET: + error = netsrc_ipv4(kctl, unit, nrq); + break; + case AF_INET6: + error = netsrc_ipv6(kctl, unit, nrq); + break; + default: + printf("%s: invalid family\n", __func__); + error = EINVAL; + } +out: + mbuf_freem(m); + + return (error); + +} + +static errno_t +netsrc_ipv4(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq) +{ + errno_t error = EHOSTUNREACH; + struct sockaddr_in *dstsin; + struct rtentry *rt; + struct in_ifaddr *ia; + struct netsrc_rep nrp; + struct sockaddr_in6 v4entry = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = IN6ADDR_V4MAPPED_INIT, + }; + struct in6_addrpolicy *policy; + + dstsin = &nrq->nrq_sin; + + if (dstsin->sin_len < sizeof (*dstsin) || + dstsin->sin_addr.s_addr == INADDR_ANY) + return (EINVAL); + + lck_mtx_lock(rnh_lock); + rt = rt_lookup(TRUE, (struct sockaddr *)dstsin, NULL, + rt_tables[AF_INET], nrq->nrq_ifscope); + lck_mtx_unlock(rnh_lock); + if (!rt) + return (EHOSTUNREACH); + lck_rw_lock_shared(in_ifaddr_rwlock); + TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { + IFA_LOCK_SPIN(&ia->ia_ifa); + if (ia->ia_ifp == rt->rt_ifp) { + memset(&nrp, 0, sizeof(nrp)); + memcpy(&nrp.nrp_sin, IA_SIN(ia), sizeof(nrp.nrp_sin)); + IFA_UNLOCK(&ia->ia_ifa); + v4entry.sin6_addr.s6_addr32[3] = + nrp.nrp_sin.sin_addr.s_addr; + policy = in6_addrsel_lookup_policy(&v4entry); + if (policy->label != -1) { + nrp.nrp_label = policy->label; + nrp.nrp_precedence = policy->preced; + /* XXX might not be true */ + nrp.nrp_dstlabel = policy->label; + nrp.nrp_dstprecedence = policy->preced; + } + error = ctl_enqueuedata(kctl, unit, &nrp, + sizeof(nrp), CTL_DATA_EOR); + break; + } + IFA_UNLOCK(&ia->ia_ifa); + } + lck_rw_done(in_ifaddr_rwlock); + if (rt) + rtfree(rt); + + return (error); +} + +static errno_t +netsrc_ipv6(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq) +{ + struct sockaddr_in6 *dstsin6; + struct in6_addr *in6, storage; + struct in6_ifaddr *ia; + struct route_in6 ro; + int error = EHOSTUNREACH; + struct netsrc_rep nrp; + + dstsin6 = &nrq->nrq_sin6; + + if (dstsin6->sin6_len < sizeof (*dstsin6) || + IN6_IS_ADDR_UNSPECIFIED(&dstsin6->sin6_addr)) + return (EINVAL); + + memset(&ro, 0, sizeof(ro)); + lck_mtx_lock(rnh_lock); + ro.ro_rt = rt_lookup(TRUE, (struct sockaddr *)dstsin6, NULL, + rt_tables[AF_INET6], nrq->nrq_ifscope); + lck_mtx_unlock(rnh_lock); + if (!ro.ro_rt) + return (EHOSTUNREACH); + in6 = in6_selectsrc(dstsin6, NULL, NULL, &ro, NULL, &storage, + nrq->nrq_ifscope, &error); + if (ro.ro_rt) + rtfree(ro.ro_rt); + if (!in6 || error) + return (error); + memset(&nrp, 0, sizeof(nrp)); + nrp.nrp_sin6.sin6_family = AF_INET6; + nrp.nrp_sin6.sin6_len = sizeof(nrp.nrp_sin6); + memcpy(&nrp.nrp_sin6.sin6_addr, in6, sizeof(nrp.nrp_sin6.sin6_addr)); + lck_rw_lock_shared(&in6_ifaddr_rwlock); + for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + if (memcmp(&ia->ia_addr.sin6_addr, in6, sizeof(*in6)) == 0) { + struct sockaddr_in6 sin6; + struct in6_addrpolicy *policy; + + if (ia->ia6_flags & IN6_IFF_TEMPORARY) + nrp.nrp_flags |= NETSRC_IP6_FLAG_TEMPORARY; + if (ia->ia6_flags & IN6_IFF_TENTATIVE) + nrp.nrp_flags |= NETSRC_IP6_FLAG_TENTATIVE; + if (ia->ia6_flags & IN6_IFF_DEPRECATED) + nrp.nrp_flags |= NETSRC_IP6_FLAG_DEPRECATED; + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(sin6); + memcpy(&sin6.sin6_addr, in6, sizeof(*in6)); + policy = in6_addrsel_lookup_policy(&sin6); + if (policy->label != -1) { + nrp.nrp_label = policy->label; + nrp.nrp_precedence = policy->preced; + } + memcpy(&sin6.sin6_addr, &dstsin6->sin6_addr, + sizeof(dstsin6->sin6_addr)); + policy = in6_addrsel_lookup_policy(&sin6); + if (policy->label != -1) { + nrp.nrp_dstlabel = policy->label; + nrp.nrp_dstprecedence = policy->preced; + } + break; + } + } + lck_rw_done(&in6_ifaddr_rwlock); + error = ctl_enqueuedata(kctl, unit, &nrp, sizeof(nrp), + CTL_DATA_EOR); + + return (error); +}