]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/netsrc.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / net / netsrc.c
CommitLineData
6d2010ae 1/*
5ba3f43e 2 * Copyright (c) 2011-2017 Apple Inc. All rights reserved.
6d2010ae
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
6d2010ae
A
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.
0a7de745 14 *
6d2010ae
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
6d2010ae
A
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.
0a7de745 25 *
6d2010ae
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
5ba3f43e
A
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
6d2010ae
A
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
5ba3f43e 64#include <net/ntstat.h>
6d2010ae
A
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 */
0a7de745 75 return 0;
6d2010ae
A
76}
77
5ba3f43e
A
78static errno_t
79netsrc_reply(kern_ctl_ref kctl, uint32_t unit, uint16_t version,
0a7de745 80 struct netsrc_rep *reply)
5ba3f43e
A
81{
82 switch (version) {
0a7de745
A
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;
5ba3f43e 89 }
0a7de745
A
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 }
5ba3f43e
A
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
0a7de745 117 struct nstat_counts *rt_stats = rt->rt_stats;
5ba3f43e
A
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
cb323159 140 if (rt->rt_ifp != NULL && (rt->rt_ifp->if_eflags & IFEF_AWDL)) {
5ba3f43e
A
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) &&
0a7de745
A
146 (rt->rt_ifa && rt->rt_ifa->ifa_ifp &&
147 !(rt->rt_ifa->ifa_ifp->if_flags & IFF_POINTOPOINT))) {
5ba3f43e
A
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(&request->nrq_dst.sa);
177 if (policy != NULL && policy->label != -1) {
178 reply->nrp_dstlabel = policy->label;
179 reply->nrp_dstprecedence = policy->preced;
180 }
181
182 // Source policy
183 policy = lookup_policy(&reply->nrp_src.sa);
184 if (policy != NULL && policy->label != -1) {
185 reply->nrp_label = policy->label;
186 reply->nrp_precedence = policy->preced;
187 }
188}
189
190static errno_t
191netsrc_ipv6(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *request)
192{
193 struct route_in6 ro = {
194 .ro_dst = request->nrq_sin6,
195 };
196
197 int error = 0;
198 struct in6_addr storage, *in6 = in6_selectsrc(&request->nrq_sin6, NULL,
0a7de745
A
199 NULL, &ro, NULL, &storage,
200 request->nrq_ifscope, &error);
5ba3f43e
A
201 struct netsrc_rep reply = {
202 .nrp_sin6.sin6_family = AF_INET6,
203 .nrp_sin6.sin6_len = sizeof(reply.nrp_sin6),
204 .nrp_sin6.sin6_addr = in6 ? *in6 : (struct in6_addr){},
205 };
206 netsrc_common(ro.ro_rt, &reply);
207 if (ro.ro_srcia == NULL && in6 != NULL) {
208 ro.ro_srcia = (struct ifaddr *)ifa_foraddr6_scoped(in6, reply.nrp_ifindex);
209 }
210 if (ro.ro_srcia) {
211 struct in6_ifaddr *ia = (struct in6_ifaddr *)ro.ro_srcia;
0a7de745
A
212#define IA_TO_NRP_FLAG(flag) \
213 if (ia->ia6_flags & IN6_IFF_##flag) { \
214 reply.nrp_flags |= NETSRC_FLAG_IP6_##flag; \
215 }
5ba3f43e
A
216 IA_TO_NRP_FLAG(TENTATIVE);
217 IA_TO_NRP_FLAG(TEMPORARY);
218 IA_TO_NRP_FLAG(DEPRECATED);
219 IA_TO_NRP_FLAG(OPTIMISTIC);
220 IA_TO_NRP_FLAG(SECURED);
221 IA_TO_NRP_FLAG(DYNAMIC);
222 IA_TO_NRP_FLAG(AUTOCONF);
223#undef IA_TO_NRP_FLAG
224 reply.nrp_flags |= NETSRC_FLAG_ROUTEABLE;
225 }
226 ROUTE_RELEASE(&ro);
227 netsrc_policy_common(request, &reply);
228 return netsrc_reply(kctl, unit, request->nrq_ver, &reply);
229}
230
231static errno_t
232netsrc_ipv4(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *request)
233{
234 // Unfortunately, IPv4 doesn't have a function like in6_selectsrc
235 // Look up the route
236 lck_mtx_lock(rnh_lock);
237 struct rtentry *rt = rt_lookup(TRUE, &request->nrq_dst.sa,
0a7de745
A
238 NULL, rt_tables[AF_INET],
239 request->nrq_ifscope);
5ba3f43e
A
240 lck_mtx_unlock(rnh_lock);
241
242 // Look up the ifa
243 struct netsrc_rep reply = {};
244 if (rt) {
245 struct in_ifaddr *ia = NULL;
246 lck_rw_lock_shared(in_ifaddr_rwlock);
247 TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
248 IFA_LOCK_SPIN(&ia->ia_ifa);
249 if (ia->ia_ifp == rt->rt_ifp) {
250 IFA_ADDREF_LOCKED(&ia->ia_ifa);
251 break;
252 }
253 IFA_UNLOCK(&ia->ia_ifa);
254 }
255 lck_rw_done(in_ifaddr_rwlock);
256
257 if (ia) {
258 reply.nrp_sin = *IA_SIN(ia);
259 IFA_REMREF_LOCKED(&ia->ia_ifa);
260 IFA_UNLOCK(&ia->ia_ifa);
261 reply.nrp_flags |= NETSRC_FLAG_ROUTEABLE;
262 }
263 netsrc_common(rt, &reply);
264 rtfree(rt);
265 }
266 netsrc_policy_common(request, &reply);
267 return netsrc_reply(kctl, unit, request->nrq_ver, &reply);
268}
269
6d2010ae
A
270static errno_t
271netsrc_ctlsend(kern_ctl_ref kctl, uint32_t unit, void *uinfo, mbuf_t m,
272 int flags)
273{
274#pragma unused(uinfo, flags)
275 errno_t error;
276 struct netsrc_req *nrq, storage;
277
278 if (mbuf_pkthdr_len(m) < sizeof(*nrq)) {
279 error = EINVAL;
280 goto out;
281 }
0a7de745 282 if (mbuf_len(m) >= sizeof(*nrq)) {
6d2010ae 283 nrq = mbuf_data(m);
0a7de745 284 } else {
6d2010ae
A
285 mbuf_copydata(m, 0, sizeof(storage), &storage);
286 nrq = &storage;
287 }
5ba3f43e 288 if (nrq->nrq_ver > NETSRC_CURVERS) {
6d2010ae
A
289 error = EINVAL;
290 goto out;
291 }
292 switch (nrq->nrq_sin.sin_family) {
293 case AF_INET:
0a7de745
A
294 if (nrq->nrq_sin.sin_len < sizeof(nrq->nrq_sin) ||
295 nrq->nrq_sin.sin_addr.s_addr == INADDR_ANY) {
5ba3f43e
A
296 error = EINVAL;
297 } else {
298 error = netsrc_ipv4(kctl, unit, nrq);
299 }
6d2010ae
A
300 break;
301 case AF_INET6:
5ba3f43e 302 if (nrq->nrq_sin6.sin6_len < sizeof(nrq->nrq_sin6) ||
0a7de745 303 IN6_IS_ADDR_UNSPECIFIED(&nrq->nrq_sin6.sin6_addr)) {
5ba3f43e
A
304 error = EINVAL;
305 } else {
306 error = netsrc_ipv6(kctl, unit, nrq);
307 }
6d2010ae
A
308 break;
309 default:
310 printf("%s: invalid family\n", __func__);
311 error = EINVAL;
312 }
313out:
314 mbuf_freem(m);
315
0a7de745 316 return error;
6d2010ae
A
317}
318
5ba3f43e
A
319__private_extern__ void
320netsrc_init(void)
6d2010ae 321{
5ba3f43e
A
322 struct kern_ctl_reg netsrc_ctl = {
323 .ctl_connect = netsrc_ctlconnect,
324 .ctl_send = netsrc_ctlsend,
6d2010ae 325 };
6d2010ae 326
5ba3f43e 327 strlcpy(netsrc_ctl.ctl_name, NETSRC_CTLNAME, sizeof(netsrc_ctl.ctl_name));
6d2010ae 328
0a7de745 329 static kern_ctl_ref netsrc_ctlref = NULL;
5ba3f43e
A
330 errno_t error = ctl_register(&netsrc_ctl, &netsrc_ctlref);
331 if (error != 0) {
332 printf("%s: ctl_register failed %d\n", __func__, error);
6d2010ae 333 }
6d2010ae 334}