1/*
2 * Copyright (c) 2011-2013 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
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/kpi_mbuf.h>
32#include <sys/socket.h>
33#include <sys/kern_control.h>
34#include <sys/mcache.h>
35#include <sys/socketvar.h>
36
37#include <kern/debug.h>
38
39#include <libkern/libkern.h>
40
41#include <net/if.h>
42#include <net/route.h>
43
44#include <netinet/in.h>
45#include <netinet/ip.h>
46#include <netinet/ip_var.h>
47#include <netinet/in_var.h>
48#include <netinet/ip6.h>
49#include <netinet6/ip6_var.h>
50
51#include <net/netsrc.h>
52
53static errno_t	netsrc_ctlsend(kern_ctl_ref, uint32_t, void *, mbuf_t, int);
54static errno_t	netsrc_ctlconnect(kern_ctl_ref, struct sockaddr_ctl *, void **);
55static errno_t	netsrc_ipv4(kern_ctl_ref, uint32_t, struct netsrc_req *);
56static errno_t	netsrc_ipv6(kern_ctl_ref, uint32_t, struct netsrc_req *);
57
58static kern_ctl_ref	netsrc_ctlref = NULL;
59
60__private_extern__ void
61netsrc_init(void)
62{
63	errno_t error;
64	struct kern_ctl_reg netsrc_ctl = {
65		.ctl_connect = netsrc_ctlconnect,
66		.ctl_send    = netsrc_ctlsend,
67	};
68
69	strlcpy(netsrc_ctl.ctl_name, NETSRC_CTLNAME, sizeof(NETSRC_CTLNAME));
70
71	if ((error = ctl_register(&netsrc_ctl, &netsrc_ctlref)))
72		printf("%s: ctl_register failed %d\n", __func__, error);
73}
74
75static errno_t
76netsrc_ctlconnect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo)
77{
78#pragma unused(kctl, sac, uinfo)
79
80	/*
81	 * We don't need to do anything here. This callback is only necessary
82	 * for ctl_register() to succeed.
83	 */
84	return (0);
85}
86
87static errno_t
88netsrc_ctlsend(kern_ctl_ref kctl, uint32_t unit, void *uinfo, mbuf_t m,
89    int flags)
90{
91#pragma unused(uinfo, flags)
92	errno_t error;
93	struct netsrc_req *nrq, storage;
94
95	if (mbuf_pkthdr_len(m) < sizeof(*nrq)) {
96		error = EINVAL;
97		goto out;
98	}
99	if (mbuf_len(m) >= sizeof(*nrq))
100		nrq = mbuf_data(m);
101	else {
102		mbuf_copydata(m, 0, sizeof(storage), &storage);
103		nrq = &storage;
104	}
105	/* We only have one version right now. */
106	if (nrq->nrq_ver != NETSRC_VERSION1) {
107		error = EINVAL;
108		goto out;
109	}
110	switch (nrq->nrq_sin.sin_family) {
111	case AF_INET:
112		error = netsrc_ipv4(kctl, unit, nrq);
113		break;
114	case AF_INET6:
115		error = netsrc_ipv6(kctl, unit, nrq);
116		break;
117	default:
118		printf("%s: invalid family\n", __func__);
119		error = EINVAL;
120	}
121out:
122	mbuf_freem(m);
123
124	return (error);
125
126}
127
128static errno_t
129netsrc_ipv4(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq)
130{
131	errno_t error = EHOSTUNREACH;
132	struct sockaddr_in *dstsin;
133	struct rtentry *rt;
134	struct in_ifaddr *ia;
135	struct netsrc_rep nrp;
136	struct sockaddr_in6 v4entry = {
137		.sin6_family = AF_INET6,
138		.sin6_len = sizeof(struct sockaddr_in6),
139		.sin6_addr = IN6ADDR_V4MAPPED_INIT,
140	};
141	struct in6_addrpolicy *policy;
142
143	dstsin = &nrq->nrq_sin;
144
145	if (dstsin->sin_len < sizeof (*dstsin) ||
146	    dstsin->sin_addr.s_addr == INADDR_ANY)
147		return (EINVAL);
148
149	lck_mtx_lock(rnh_lock);
150	rt = rt_lookup(TRUE, (struct sockaddr *)dstsin, NULL,
151	    rt_tables[AF_INET], nrq->nrq_ifscope);
152	lck_mtx_unlock(rnh_lock);
153	if (!rt)
154		return (EHOSTUNREACH);
155	lck_rw_lock_shared(in_ifaddr_rwlock);
156	TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
157		IFA_LOCK_SPIN(&ia->ia_ifa);
158		if (ia->ia_ifp == rt->rt_ifp) {
159			memset(&nrp, 0, sizeof(nrp));
160			memcpy(&nrp.nrp_sin, IA_SIN(ia), sizeof(nrp.nrp_sin));
161			IFA_UNLOCK(&ia->ia_ifa);
162			v4entry.sin6_addr.s6_addr32[3] =
163			    nrp.nrp_sin.sin_addr.s_addr;
164			policy = in6_addrsel_lookup_policy(&v4entry);
165			if (policy->label != -1) {
166				nrp.nrp_label = policy->label;
167				nrp.nrp_precedence = policy->preced;
168				/* XXX might not be true */
169				nrp.nrp_dstlabel = policy->label;
170				nrp.nrp_dstprecedence = policy->preced;
171			}
172			error = ctl_enqueuedata(kctl, unit, &nrp,
173			    sizeof(nrp), CTL_DATA_EOR);
174			break;
175		}
176		IFA_UNLOCK(&ia->ia_ifa);
177	}
178	lck_rw_done(in_ifaddr_rwlock);
179	if (rt)
180		rtfree(rt);
181
182	return (error);
183}
184
185static errno_t
186netsrc_ipv6(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq)
187{
188	struct sockaddr_in6 *dstsin6;
189	struct in6_addr *in6, storage;
190	struct in6_ifaddr *ia;
191	struct route_in6 ro;
192	int error = EHOSTUNREACH;
193	struct netsrc_rep nrp;
194
195	dstsin6 = &nrq->nrq_sin6;
196
197	if (dstsin6->sin6_len < sizeof (*dstsin6) ||
198	    IN6_IS_ADDR_UNSPECIFIED(&dstsin6->sin6_addr))
199		return (EINVAL);
200
201	memset(&ro, 0, sizeof(ro));
202	lck_mtx_lock(rnh_lock);
203	ro.ro_rt = rt_lookup(TRUE, (struct sockaddr *)dstsin6, NULL,
204	    rt_tables[AF_INET6], nrq->nrq_ifscope);
205	lck_mtx_unlock(rnh_lock);
206	if (!ro.ro_rt)
207		return (EHOSTUNREACH);
208	in6 = in6_selectsrc(dstsin6, NULL, NULL, &ro, NULL, &storage,
209	    nrq->nrq_ifscope, &error);
210	ROUTE_RELEASE(&ro);
211	if (!in6 || error)
212		return (error);
213	memset(&nrp, 0, sizeof(nrp));
214	nrp.nrp_sin6.sin6_family = AF_INET6;
215	nrp.nrp_sin6.sin6_len    = sizeof(nrp.nrp_sin6);
216	memcpy(&nrp.nrp_sin6.sin6_addr, in6, sizeof(nrp.nrp_sin6.sin6_addr));
217	lck_rw_lock_shared(&in6_ifaddr_rwlock);
218	for (ia = in6_ifaddrs; ia; ia = ia->ia_next) {
219		if (memcmp(&ia->ia_addr.sin6_addr, in6, sizeof(*in6)) == 0) {
220			struct sockaddr_in6 sin6;
221			struct in6_addrpolicy *policy;
222
223			if (ia->ia6_flags & IN6_IFF_TEMPORARY)
224				nrp.nrp_flags |= NETSRC_IP6_FLAG_TEMPORARY;
225			if (ia->ia6_flags & IN6_IFF_TENTATIVE)
226				nrp.nrp_flags |= NETSRC_IP6_FLAG_TENTATIVE;
227			if (ia->ia6_flags & IN6_IFF_DEPRECATED)
228				nrp.nrp_flags |= NETSRC_IP6_FLAG_DEPRECATED;
229			if (ia->ia6_flags & IN6_IFF_OPTIMISTIC)
230				nrp.nrp_flags |= NETSRC_IP6_FLAG_OPTIMISTIC;
231			if (ia->ia6_flags & IN6_IFF_SECURED)
232				nrp.nrp_flags |= NETSRC_IP6_FLAG_SECURED;
233			sin6.sin6_family = AF_INET6;
234			sin6.sin6_len    = sizeof(sin6);
235			memcpy(&sin6.sin6_addr, in6, sizeof(*in6));
236			policy = in6_addrsel_lookup_policy(&sin6);
237			if (policy->label != -1) {
238				nrp.nrp_label = policy->label;
239				nrp.nrp_precedence = policy->preced;
240			}
241			memcpy(&sin6.sin6_addr, &dstsin6->sin6_addr,
242			    sizeof(dstsin6->sin6_addr));
243			policy = in6_addrsel_lookup_policy(&sin6);
244			if (policy->label != -1) {
245				nrp.nrp_dstlabel = policy->label;
246				nrp.nrp_dstprecedence = policy->preced;
247			}
248			break;
249		}
250	}
251	lck_rw_done(&in6_ifaddr_rwlock);
252	error = ctl_enqueuedata(kctl, unit, &nrp, sizeof(nrp),
253	    CTL_DATA_EOR);
254
255	return (error);
256}
257