2 * Copyright (c) 2011-2017 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
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
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>
50 #include <kern/debug.h>
52 #include <libkern/libkern.h>
55 #include <net/route.h>
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>
64 #include <net/ntstat.h>
67 netsrc_ctlconnect(kern_ctl_ref kctl
, struct sockaddr_ctl
*sac
, void **uinfo
)
69 #pragma unused(kctl, sac, uinfo)
72 * We don't need to do anything here. This callback is only necessary
73 * for ctl_register() to succeed.
79 netsrc_reply(kern_ctl_ref kctl
, uint32_t unit
, unsigned int version
,
80 struct netsrc_rep
*reply
)
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) {
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
103 return ctl_enqueuedata(kctl
, unit
, &v1
, sizeof(v1
), CTL_DATA_EOR
);
110 netsrc_common(struct rtentry
*rt
, struct netsrc_rep
*reply
)
116 // Gather statistics information
117 struct nstat_counts
*rt_stats
= rt
->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
;
124 // If this route didn't have any stats, check its parent
125 if (reply
->nrp_min_rtt
== 0) {
126 // Is this lock necessary?
129 rt_stats
= rt
->rt_parent
->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
;
138 reply
->nrp_ifindex
= rt
->rt_ifp
? rt
->rt_ifp
->if_index
: 0;
140 if (rt
->rt_ifp
!= NULL
&& (rt
->rt_ifp
->if_eflags
& IFEF_AWDL
)) {
141 reply
->nrp_flags
|= NETSRC_FLAG_AWDL
;
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
;
152 static struct in6_addrpolicy
*
153 lookup_policy(struct sockaddr
* sa
)
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
,
166 mapped
.sin6_addr
.s6_addr32
[3] = addr
->sin
.sin_addr
.s_addr
;
167 return in6_addrsel_lookup_policy(&mapped
);
173 netsrc_policy_common(struct netsrc_req
*request
, struct netsrc_rep
*reply
)
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.
181 reply
->nrp_dstlabel
= (uint16_t)policy
->label
;
182 reply
->nrp_dstprecedence
= (uint16_t)policy
->preced
;
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.
191 reply
->nrp_label
= (uint16_t)policy
->label
;
192 reply
->nrp_precedence
= (uint16_t)policy
->preced
;
197 netsrc_ipv6(kern_ctl_ref kctl
, uint32_t unit
, struct netsrc_req
*request
)
199 struct route_in6 ro
= {
200 .ro_dst
= request
->nrq_sin6
,
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
){},
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
);
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; \
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
;
233 netsrc_policy_common(request
, &reply
);
234 return netsrc_reply(kctl
, unit
, request
->nrq_ver
, &reply
);
238 netsrc_ipv4(kern_ctl_ref kctl
, uint32_t unit
, struct netsrc_req
*request
)
240 // Unfortunately, IPv4 doesn't have a function like in6_selectsrc
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
);
249 struct netsrc_rep reply
= {};
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
);
259 IFA_UNLOCK(&ia
->ia_ifa
);
261 lck_rw_done(in_ifaddr_rwlock
);
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
;
269 netsrc_common(rt
, &reply
);
272 netsrc_policy_common(request
, &reply
);
273 return netsrc_reply(kctl
, unit
, request
->nrq_ver
, &reply
);
277 netsrc_ctlsend(kern_ctl_ref kctl
, uint32_t unit
, void *uinfo
, mbuf_t m
,
280 #pragma unused(uinfo, flags)
282 struct netsrc_req
*nrq
, storage
;
284 if (mbuf_pkthdr_len(m
) < sizeof(*nrq
)) {
288 if (mbuf_len(m
) >= sizeof(*nrq
)) {
291 mbuf_copydata(m
, 0, sizeof(storage
), &storage
);
294 if (nrq
->nrq_ver
> NETSRC_CURVERS
) {
298 switch (nrq
->nrq_sin
.sin_family
) {
300 if (nrq
->nrq_sin
.sin_len
< sizeof(nrq
->nrq_sin
) ||
301 nrq
->nrq_sin
.sin_addr
.s_addr
== INADDR_ANY
) {
304 error
= netsrc_ipv4(kctl
, unit
, nrq
);
308 if (nrq
->nrq_sin6
.sin6_len
< sizeof(nrq
->nrq_sin6
) ||
309 IN6_IS_ADDR_UNSPECIFIED(&nrq
->nrq_sin6
.sin6_addr
)) {
312 error
= netsrc_ipv6(kctl
, unit
, nrq
);
316 printf("%s: invalid family\n", __func__
);
325 __private_extern__
void
328 struct kern_ctl_reg netsrc_ctl
= {
329 .ctl_connect
= netsrc_ctlconnect
,
330 .ctl_send
= netsrc_ctlsend
,
333 strlcpy(netsrc_ctl
.ctl_name
, NETSRC_CTLNAME
, sizeof(netsrc_ctl
.ctl_name
));
335 static kern_ctl_ref netsrc_ctlref
= NULL
;
336 errno_t error
= ctl_register(&netsrc_ctl
, &netsrc_ctlref
);
338 printf("%s: ctl_register failed %d\n", __func__
, error
);