]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/netsrc.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / bsd / net / netsrc.c
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
66 static errno_t
67 netsrc_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
78 static errno_t
79 netsrc_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(kctl, unit, reply,
85 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(kctl, unit, &v1, sizeof(v1), CTL_DATA_EOR);
104 }
105 }
106 return EINVAL;
107 }
108
109 static void
110 netsrc_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
152 static struct in6_addrpolicy *
153 lookup_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
172 static void
173 netsrc_policy_common(struct netsrc_req *request, struct netsrc_rep *reply)
174 {
175 // Destination policy
176 struct in6_addrpolicy *policy = lookup_policy(&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(&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
196 static errno_t
197 netsrc_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(ro.ro_rt, &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);
234 return netsrc_reply(kctl, unit, request->nrq_ver, &reply);
235 }
236
237 static errno_t
238 netsrc_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, &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(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_LOCKED(&ia->ia_ifa);
257 break;
258 }
259 IFA_UNLOCK(&ia->ia_ifa);
260 }
261 lck_rw_done(in_ifaddr_rwlock);
262
263 if (ia) {
264 reply.nrp_sin = *IA_SIN(ia);
265 IFA_REMREF_LOCKED(&ia->ia_ifa);
266 IFA_UNLOCK(&ia->ia_ifa);
267 reply.nrp_flags |= NETSRC_FLAG_ROUTEABLE;
268 }
269 netsrc_common(rt, &reply);
270 rtfree(rt);
271 }
272 netsrc_policy_common(request, &reply);
273 return netsrc_reply(kctl, unit, request->nrq_ver, &reply);
274 }
275
276 static errno_t
277 netsrc_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(m) < sizeof(*nrq)) {
285 error = EINVAL;
286 goto out;
287 }
288 if (mbuf_len(m) >= sizeof(*nrq)) {
289 nrq = mbuf_data(m);
290 } else {
291 mbuf_copydata(m, 0, sizeof(storage), &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, 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, nrq);
313 }
314 break;
315 default:
316 printf("%s: invalid family\n", __func__);
317 error = EINVAL;
318 }
319 out:
320 mbuf_freem(m);
321
322 return error;
323 }
324
325 __private_extern__ void
326 netsrc_init(void)
327 {
328 struct kern_ctl_reg netsrc_ctl = {
329 .ctl_connect = netsrc_ctlconnect,
330 .ctl_send = netsrc_ctlsend,
331 };
332
333 strlcpy(netsrc_ctl.ctl_name, NETSRC_CTLNAME, sizeof(netsrc_ctl.ctl_name));
334
335 static kern_ctl_ref netsrc_ctlref = NULL;
336 errno_t error = ctl_register(&netsrc_ctl, &netsrc_ctlref);
337 if (error != 0) {
338 printf("%s: ctl_register failed %d\n", __func__, error);
339 }
340 }