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
, uint16_t 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
->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                 reply
->nrp_dstlabel 
= policy
->label
; 
 179                 reply
->nrp_dstprecedence 
= policy
->preced
; 
 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
; 
 191 netsrc_ipv6(kern_ctl_ref kctl
, uint32_t unit
, struct netsrc_req 
*request
) 
 193         struct route_in6 ro 
= { 
 194                 .ro_dst 
= request
->nrq_sin6
, 
 198         struct in6_addr storage
, *in6 
= in6_selectsrc(&request
->nrq_sin6
, NULL
, 
 199                                                                                                   NULL
, &ro
, NULL
, &storage
, 
 200                                                                                                   request
->nrq_ifscope
, &error
); 
 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
){}, 
 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
); 
 211                 struct in6_ifaddr 
*ia 
= (struct in6_ifaddr 
*)ro
.ro_srcia
; 
 212 #define IA_TO_NRP_FLAG(flag)    \ 
 213                 if (ia->ia6_flags & IN6_IFF_##flag) {                   \ 
 214                         reply.nrp_flags |= NETSRC_FLAG_IP6_##flag;      \ 
 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
; 
 227         netsrc_policy_common(request
, &reply
); 
 228         return netsrc_reply(kctl
, unit
, request
->nrq_ver
, &reply
); 
 232 netsrc_ipv4(kern_ctl_ref kctl
, uint32_t unit
, struct netsrc_req 
*request
) 
 234         // Unfortunately, IPv4 doesn't have a function like in6_selectsrc 
 236         lck_mtx_lock(rnh_lock
); 
 237         struct rtentry 
*rt 
= rt_lookup(TRUE
, &request
->nrq_dst
.sa
, 
 238                                                                    NULL
, rt_tables
[AF_INET
], 
 239                                                                    request
->nrq_ifscope
); 
 240         lck_mtx_unlock(rnh_lock
); 
 243         struct netsrc_rep reply 
= {}; 
 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
); 
 253                         IFA_UNLOCK(&ia
->ia_ifa
); 
 255                 lck_rw_done(in_ifaddr_rwlock
); 
 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
; 
 263                 netsrc_common(rt
, &reply
); 
 266         netsrc_policy_common(request
, &reply
); 
 267         return netsrc_reply(kctl
, unit
, request
->nrq_ver
, &reply
); 
 271 netsrc_ctlsend(kern_ctl_ref kctl
, uint32_t unit
, void *uinfo
, mbuf_t m
, 
 274 #pragma unused(uinfo, flags) 
 276         struct netsrc_req 
*nrq
, storage
; 
 278         if (mbuf_pkthdr_len(m
) < sizeof(*nrq
)) { 
 282         if (mbuf_len(m
) >= sizeof(*nrq
)) 
 285                 mbuf_copydata(m
, 0, sizeof(storage
), &storage
); 
 288         if (nrq
->nrq_ver 
> NETSRC_CURVERS
) { 
 292         switch (nrq
->nrq_sin
.sin_family
) { 
 294                 if (nrq
->nrq_sin
.sin_len 
< sizeof (nrq
->nrq_sin
) || 
 295                         nrq
->nrq_sin
.sin_addr
.s_addr 
== INADDR_ANY
) { 
 298                         error 
= netsrc_ipv4(kctl
, unit
, nrq
); 
 302                 if (nrq
->nrq_sin6
.sin6_len 
< sizeof(nrq
->nrq_sin6
) || 
 303                         IN6_IS_ADDR_UNSPECIFIED(&nrq
->nrq_sin6
.sin6_addr
)) { 
 306                         error 
= netsrc_ipv6(kctl
, unit
, nrq
); 
 310                 printf("%s: invalid family\n", __func__
); 
 320 __private_extern__ 
void 
 323         struct kern_ctl_reg netsrc_ctl 
= { 
 324                 .ctl_connect 
= netsrc_ctlconnect
, 
 325                 .ctl_send    
= netsrc_ctlsend
, 
 328         strlcpy(netsrc_ctl
.ctl_name
, NETSRC_CTLNAME
, sizeof(netsrc_ctl
.ctl_name
)); 
 330         static kern_ctl_ref     netsrc_ctlref 
= NULL
; 
 331         errno_t error 
= ctl_register(&netsrc_ctl
, &netsrc_ctlref
); 
 333                 printf("%s: ctl_register failed %d\n", __func__
, error
);