1/*
2 * Copyright (c) 2011-2017 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 netinet/in.h first. net/netsrc.h depends on netinet/in.h but
30// netinet/in.h doesn't work with -Wpadded, -Wpacked.
31#include <netinet/in.h>
32
33#pragma clang diagnostic push
34#pragma clang diagnostic error "-Wpadded"
35#pragma clang diagnostic error "-Wpacked"
36// This header defines structures shared with user space, so we need to ensure there is
37// no compiler inserted padding in case the user space process isn't using the same
38// architecture as the kernel (example: i386 process with x86_64 kernel).
39#include <net/netsrc.h>
40#pragma clang diagnostic pop
41
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/kpi_mbuf.h>
45#include <sys/socket.h>
46#include <sys/kern_control.h>
47#include <sys/mcache.h>
48#include <sys/socketvar.h>
49
50#include <kern/debug.h>
51
52#include <libkern/libkern.h>
53
54#include <net/if.h>
55#include <net/route.h>
56
57#include <netinet/in.h>
58#include <netinet/ip.h>
59#include <netinet/ip_var.h>
60#include <netinet/in_var.h>
61#include <netinet/ip6.h>
62#include <netinet6/ip6_var.h>
63
64#include <net/ntstat.h>
65
66static errno_t
67netsrc_ctlconnect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo)
68{
69#pragma unused(kctl, sac, uinfo)
70
71 /*
72 * We don't need to do anything here. This callback is only necessary
73 * for ctl_register() to succeed.
74 */
75 return 0;
76}
77
78static errno_t
79netsrc_reply(kern_ctl_ref kctl, uint32_t unit, unsigned int version,
80 struct netsrc_rep *reply)
81{
82 switch (version) {
83 case NETSRC_CURVERS:
84 return ctl_enqueuedata(kctlref: kctl, unit, data: reply,
85 len: sizeof(*reply), CTL_DATA_EOR);
86 case NETSRC_VERSION1: {
87 if ((reply->nrp_flags & NETSRC_FLAG_ROUTEABLE) == 0) {
88 return EHOSTUNREACH;
89 }
90#define NETSRC_FLAG_V1_MASK (NETSRC_IP6_FLAG_TENTATIVE | \
91 NETSRC_IP6_FLAG_TEMPORARY | \
92 NETSRC_IP6_FLAG_DEPRECATED | \
93 NETSRC_IP6_FLAG_OPTIMISTIC | \
94 NETSRC_IP6_FLAG_SECURED)
95 struct netsrc_repv1 v1 = {
96 .nrp_src = reply->nrp_src,
97 .nrp_flags = (reply->nrp_flags & NETSRC_FLAG_V1_MASK),
98 .nrp_label = reply->nrp_label,
99 .nrp_precedence = reply->nrp_precedence,
100 .nrp_dstlabel = reply->nrp_dstlabel,
101 .nrp_dstprecedence = reply->nrp_dstprecedence
102 };
103 return ctl_enqueuedata(kctlref: kctl, unit, data: &v1, len: sizeof(v1), CTL_DATA_EOR);
104 }
105 }
106 return EINVAL;
107}
108
109static void
110netsrc_common(struct rtentry *rt, struct netsrc_rep *reply)
111{
112 if (!rt) {
113 return;
114 }
115
116 // Gather statistics information
117 struct nstat_counts *rt_stats = rt->rt_stats;
118 if (rt_stats) {
119 reply->nrp_min_rtt = rt_stats->nstat_min_rtt;
120 reply->nrp_connection_attempts = rt_stats->nstat_connectattempts;
121 reply->nrp_connection_successes = rt_stats->nstat_connectsuccesses;
122 }
123
124 // If this route didn't have any stats, check its parent
125 if (reply->nrp_min_rtt == 0) {
126 // Is this lock necessary?
127 RT_LOCK(rt);
128 if (rt->rt_parent) {
129 rt_stats = rt->rt_parent->rt_stats;
130 if (rt_stats) {
131 reply->nrp_min_rtt = rt_stats->nstat_min_rtt;
132 reply->nrp_connection_attempts = rt_stats->nstat_connectattempts;
133 reply->nrp_connection_successes = rt_stats->nstat_connectsuccesses;
134 }
135 }
136 RT_UNLOCK(rt);
137 }
138 reply->nrp_ifindex = rt->rt_ifp ? rt->rt_ifp->if_index : 0;
139
140 if (rt->rt_ifp != NULL && (rt->rt_ifp->if_eflags & IFEF_AWDL)) {
141 reply->nrp_flags |= NETSRC_FLAG_AWDL;
142 }
143 if (rt->rt_flags & RTF_LOCAL) {
144 reply->nrp_flags |= NETSRC_FLAG_DIRECT;
145 } else if (!(rt->rt_flags & RTF_GATEWAY) &&
146 (rt->rt_ifa && rt->rt_ifa->ifa_ifp &&
147 !(rt->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) {
148 reply->nrp_flags |= NETSRC_FLAG_DIRECT;
149 }
150}
151
152static struct in6_addrpolicy *
153lookup_policy(struct sockaddr* sa)
154{
155 // alignment fun - if sa_family is AF_INET or AF_INET6, this is one of those
156 // addresses and it should be aligned, so this should be safe.
157 union sockaddr_in_4_6 *addr = (union sockaddr_in_4_6 *)(void*)sa;
158 if (addr->sa.sa_family == AF_INET6) {
159 return in6_addrsel_lookup_policy(&addr->sin6);
160 } else if (sa->sa_family == AF_INET) {
161 struct sockaddr_in6 mapped = {
162 .sin6_family = AF_INET6,
163 .sin6_len = sizeof(mapped),
164 .sin6_addr = IN6ADDR_V4MAPPED_INIT,
165 };
166 mapped.sin6_addr.s6_addr32[3] = addr->sin.sin_addr.s_addr;
167 return in6_addrsel_lookup_policy(&mapped);
168 }
169 return NULL;
170}
171
172static void
173netsrc_policy_common(struct netsrc_req *request, struct netsrc_rep *reply)
174{
175 // Destination policy
176 struct in6_addrpolicy *policy = lookup_policy(SA(&request->nrq_dst.sa));
177 if (policy != NULL && policy->label != -1) {
178 /* Explicit cast because both policy and netsrc are public APIs
179 * and apps might rely on it.
180 */
181 reply->nrp_dstlabel = (uint16_t)policy->label;
182 reply->nrp_dstprecedence = (uint16_t)policy->preced;
183 }
184
185 // Source policy
186 policy = lookup_policy(SA(&reply->nrp_src.sa));
187 if (policy != NULL && policy->label != -1) {
188 /* Explicit cast because both policy and netsrc are public APIs
189 * and apps might rely on it.
190 */
191 reply->nrp_label = (uint16_t)policy->label;
192 reply->nrp_precedence = (uint16_t)policy->preced;
193 }
194}
195
196static errno_t
197netsrc_ipv6(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *request)
198{
199 struct route_in6 ro = {
200 .ro_dst = request->nrq_sin6,
201 };
202
203 int error = 0;
204 struct in6_addr storage, *in6 = in6_selectsrc(&request->nrq_sin6, NULL,
205 NULL, &ro, NULL, &storage,
206 request->nrq_ifscope, &error);
207 struct netsrc_rep reply = {
208 .nrp_sin6.sin6_family = AF_INET6,
209 .nrp_sin6.sin6_len = sizeof(reply.nrp_sin6),
210 .nrp_sin6.sin6_addr = in6 ? *in6 : (struct in6_addr){},
211 };
212 netsrc_common(rt: ro.ro_rt, reply: &reply);
213 if (ro.ro_srcia == NULL && in6 != NULL) {
214 ro.ro_srcia = (struct ifaddr *)ifa_foraddr6_scoped(in6, reply.nrp_ifindex);
215 }
216 if (ro.ro_srcia) {
217 struct in6_ifaddr *ia = (struct in6_ifaddr *)ro.ro_srcia;
218#define IA_TO_NRP_FLAG(flag) \
219 if (ia->ia6_flags & IN6_IFF_##flag) { \
220 reply.nrp_flags |= NETSRC_FLAG_IP6_##flag; \
221 }
222 IA_TO_NRP_FLAG(TENTATIVE);
223 IA_TO_NRP_FLAG(TEMPORARY);
224 IA_TO_NRP_FLAG(DEPRECATED);
225 IA_TO_NRP_FLAG(OPTIMISTIC);
226 IA_TO_NRP_FLAG(SECURED);
227 IA_TO_NRP_FLAG(DYNAMIC);
228 IA_TO_NRP_FLAG(AUTOCONF);
229#undef IA_TO_NRP_FLAG
230 reply.nrp_flags |= NETSRC_FLAG_ROUTEABLE;
231 }
232 ROUTE_RELEASE(&ro);
233 netsrc_policy_common(request, reply: &reply);
234 return netsrc_reply(kctl, unit, version: request->nrq_ver, reply: &reply);
235}
236
237static errno_t
238netsrc_ipv4(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *request)
239{
240 // Unfortunately, IPv4 doesn't have a function like in6_selectsrc
241 // Look up the route
242 lck_mtx_lock(rnh_lock);
243 struct rtentry *rt = rt_lookup(TRUE, SA(&request->nrq_dst.sa),
244 NULL, rt_tables[AF_INET],
245 request->nrq_ifscope);
246 lck_mtx_unlock(rnh_lock);
247
248 // Look up the ifa
249 struct netsrc_rep reply = {};
250 if (rt) {
251 struct in_ifaddr *ia = NULL;
252 lck_rw_lock_shared(lck: &in_ifaddr_rwlock);
253 TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
254 IFA_LOCK_SPIN(&ia->ia_ifa);
255 if (ia->ia_ifp == rt->rt_ifp) {
256 ifa_addref(ifa: &ia->ia_ifa);
257 break;
258 }
259 IFA_UNLOCK(&ia->ia_ifa);
260 }
261 lck_rw_done(lck: &in_ifaddr_rwlock);
262
263 if (ia) {
264 reply.nrp_sin = *IA_SIN(ia);
265 IFA_UNLOCK(&ia->ia_ifa);
266 ifa_remref(ifa: &ia->ia_ifa);
267 reply.nrp_flags |= NETSRC_FLAG_ROUTEABLE;
268 }
269 netsrc_common(rt, reply: &reply);
270 rtfree(rt);
271 }
272 netsrc_policy_common(request, reply: &reply);
273 return netsrc_reply(kctl, unit, version: request->nrq_ver, reply: &reply);
274}
275
276static errno_t
277netsrc_ctlsend(kern_ctl_ref kctl, uint32_t unit, void *uinfo, mbuf_t m,
278 int flags)
279{
280#pragma unused(uinfo, flags)
281 errno_t error;
282 struct netsrc_req *nrq, storage;
283
284 if (mbuf_pkthdr_len(mbuf: m) < sizeof(*nrq)) {
285 error = EINVAL;
286 goto out;
287 }
288 if (mbuf_len(mbuf: m) >= sizeof(*nrq)) {
289 nrq = mbuf_data(mbuf: m);
290 } else {
291 mbuf_copydata(mbuf: m, offset: 0, length: sizeof(storage), out_data: &storage);
292 nrq = &storage;
293 }
294 if (nrq->nrq_ver > NETSRC_CURVERS) {
295 error = EINVAL;
296 goto out;
297 }
298 switch (nrq->nrq_sin.sin_family) {
299 case AF_INET:
300 if (nrq->nrq_sin.sin_len < sizeof(nrq->nrq_sin) ||
301 nrq->nrq_sin.sin_addr.s_addr == INADDR_ANY) {
302 error = EINVAL;
303 } else {
304 error = netsrc_ipv4(kctl, unit, request: nrq);
305 }
306 break;
307 case AF_INET6:
308 if (nrq->nrq_sin6.sin6_len < sizeof(nrq->nrq_sin6) ||
309 IN6_IS_ADDR_UNSPECIFIED(&nrq->nrq_sin6.sin6_addr)) {
310 error = EINVAL;
311 } else {
312 error = netsrc_ipv6(kctl, unit, request: nrq);
313 }
314 break;
315 default:
316 printf("%s: invalid family\n", __func__);
317 error = EINVAL;
318 }
319out:
320 mbuf_freem(mbuf: m);
321
322 return error;
323}
324
325__private_extern__ void
326netsrc_init(void)
327{
328 struct kern_ctl_reg netsrc_ctl = {
329 .ctl_connect = netsrc_ctlconnect,
330 .ctl_send = netsrc_ctlsend,
331 };
332
333 strlcpy(dst: netsrc_ctl.ctl_name, NETSRC_CTLNAME, n: sizeof(netsrc_ctl.ctl_name));
334
335 static kern_ctl_ref netsrc_ctlref = NULL;
336 errno_t error = ctl_register(userkctl: &netsrc_ctl, kctlref: &netsrc_ctlref);
337 if (error != 0) {
338 printf("%s: ctl_register failed %d\n", __func__, error);
339 }
340}
341