]>
Commit | Line | Data |
---|---|---|
6d2010ae | 1 | /* |
39236c6e | 2 | * Copyright (c) 2011-2013 Apple Inc. All rights reserved. |
6d2010ae A |
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 <sys/param.h> | |
30 | #include <sys/types.h> | |
31 | #include <sys/kpi_mbuf.h> | |
32 | #include <sys/socket.h> | |
33 | #include <sys/kern_control.h> | |
34 | #include <sys/mcache.h> | |
35 | #include <sys/socketvar.h> | |
36 | ||
37 | #include <kern/debug.h> | |
38 | ||
39 | #include <libkern/libkern.h> | |
40 | ||
41 | #include <net/if.h> | |
42 | #include <net/route.h> | |
43 | ||
44 | #include <netinet/in.h> | |
45 | #include <netinet/ip.h> | |
46 | #include <netinet/ip_var.h> | |
47 | #include <netinet/in_var.h> | |
48 | #include <netinet/ip6.h> | |
49 | #include <netinet6/ip6_var.h> | |
50 | ||
51 | #include <net/netsrc.h> | |
52 | ||
53 | static errno_t netsrc_ctlsend(kern_ctl_ref, uint32_t, void *, mbuf_t, int); | |
54 | static errno_t netsrc_ctlconnect(kern_ctl_ref, struct sockaddr_ctl *, void **); | |
55 | static errno_t netsrc_ipv4(kern_ctl_ref, uint32_t, struct netsrc_req *); | |
56 | static errno_t netsrc_ipv6(kern_ctl_ref, uint32_t, struct netsrc_req *); | |
57 | ||
58 | static kern_ctl_ref netsrc_ctlref = NULL; | |
59 | ||
60 | __private_extern__ void | |
61 | netsrc_init(void) | |
62 | { | |
63 | errno_t error; | |
64 | struct kern_ctl_reg netsrc_ctl = { | |
65 | .ctl_connect = netsrc_ctlconnect, | |
66 | .ctl_send = netsrc_ctlsend, | |
67 | }; | |
68 | ||
69 | strlcpy(netsrc_ctl.ctl_name, NETSRC_CTLNAME, sizeof(NETSRC_CTLNAME)); | |
70 | ||
71 | if ((error = ctl_register(&netsrc_ctl, &netsrc_ctlref))) | |
72 | printf("%s: ctl_register failed %d\n", __func__, error); | |
73 | } | |
74 | ||
75 | static errno_t | |
76 | netsrc_ctlconnect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo) | |
77 | { | |
78 | #pragma unused(kctl, sac, uinfo) | |
79 | ||
80 | /* | |
81 | * We don't need to do anything here. This callback is only necessary | |
82 | * for ctl_register() to succeed. | |
83 | */ | |
84 | return (0); | |
85 | } | |
86 | ||
87 | static errno_t | |
88 | netsrc_ctlsend(kern_ctl_ref kctl, uint32_t unit, void *uinfo, mbuf_t m, | |
89 | int flags) | |
90 | { | |
91 | #pragma unused(uinfo, flags) | |
92 | errno_t error; | |
93 | struct netsrc_req *nrq, storage; | |
94 | ||
95 | if (mbuf_pkthdr_len(m) < sizeof(*nrq)) { | |
96 | error = EINVAL; | |
97 | goto out; | |
98 | } | |
99 | if (mbuf_len(m) >= sizeof(*nrq)) | |
100 | nrq = mbuf_data(m); | |
101 | else { | |
102 | mbuf_copydata(m, 0, sizeof(storage), &storage); | |
103 | nrq = &storage; | |
104 | } | |
105 | /* We only have one version right now. */ | |
106 | if (nrq->nrq_ver != NETSRC_VERSION1) { | |
107 | error = EINVAL; | |
108 | goto out; | |
109 | } | |
110 | switch (nrq->nrq_sin.sin_family) { | |
111 | case AF_INET: | |
112 | error = netsrc_ipv4(kctl, unit, nrq); | |
113 | break; | |
114 | case AF_INET6: | |
115 | error = netsrc_ipv6(kctl, unit, nrq); | |
116 | break; | |
117 | default: | |
118 | printf("%s: invalid family\n", __func__); | |
119 | error = EINVAL; | |
120 | } | |
121 | out: | |
122 | mbuf_freem(m); | |
123 | ||
124 | return (error); | |
125 | ||
126 | } | |
127 | ||
128 | static errno_t | |
129 | netsrc_ipv4(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq) | |
130 | { | |
131 | errno_t error = EHOSTUNREACH; | |
132 | struct sockaddr_in *dstsin; | |
133 | struct rtentry *rt; | |
134 | struct in_ifaddr *ia; | |
135 | struct netsrc_rep nrp; | |
136 | struct sockaddr_in6 v4entry = { | |
137 | .sin6_family = AF_INET6, | |
138 | .sin6_len = sizeof(struct sockaddr_in6), | |
139 | .sin6_addr = IN6ADDR_V4MAPPED_INIT, | |
140 | }; | |
141 | struct in6_addrpolicy *policy; | |
142 | ||
143 | dstsin = &nrq->nrq_sin; | |
144 | ||
145 | if (dstsin->sin_len < sizeof (*dstsin) || | |
146 | dstsin->sin_addr.s_addr == INADDR_ANY) | |
147 | return (EINVAL); | |
148 | ||
149 | lck_mtx_lock(rnh_lock); | |
150 | rt = rt_lookup(TRUE, (struct sockaddr *)dstsin, NULL, | |
151 | rt_tables[AF_INET], nrq->nrq_ifscope); | |
152 | lck_mtx_unlock(rnh_lock); | |
153 | if (!rt) | |
154 | return (EHOSTUNREACH); | |
155 | lck_rw_lock_shared(in_ifaddr_rwlock); | |
156 | TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) { | |
157 | IFA_LOCK_SPIN(&ia->ia_ifa); | |
158 | if (ia->ia_ifp == rt->rt_ifp) { | |
159 | memset(&nrp, 0, sizeof(nrp)); | |
160 | memcpy(&nrp.nrp_sin, IA_SIN(ia), sizeof(nrp.nrp_sin)); | |
161 | IFA_UNLOCK(&ia->ia_ifa); | |
162 | v4entry.sin6_addr.s6_addr32[3] = | |
163 | nrp.nrp_sin.sin_addr.s_addr; | |
164 | policy = in6_addrsel_lookup_policy(&v4entry); | |
165 | if (policy->label != -1) { | |
166 | nrp.nrp_label = policy->label; | |
167 | nrp.nrp_precedence = policy->preced; | |
168 | /* XXX might not be true */ | |
169 | nrp.nrp_dstlabel = policy->label; | |
170 | nrp.nrp_dstprecedence = policy->preced; | |
171 | } | |
172 | error = ctl_enqueuedata(kctl, unit, &nrp, | |
173 | sizeof(nrp), CTL_DATA_EOR); | |
174 | break; | |
175 | } | |
176 | IFA_UNLOCK(&ia->ia_ifa); | |
177 | } | |
178 | lck_rw_done(in_ifaddr_rwlock); | |
179 | if (rt) | |
180 | rtfree(rt); | |
181 | ||
182 | return (error); | |
183 | } | |
184 | ||
185 | static errno_t | |
186 | netsrc_ipv6(kern_ctl_ref kctl, uint32_t unit, struct netsrc_req *nrq) | |
187 | { | |
188 | struct sockaddr_in6 *dstsin6; | |
189 | struct in6_addr *in6, storage; | |
190 | struct in6_ifaddr *ia; | |
191 | struct route_in6 ro; | |
192 | int error = EHOSTUNREACH; | |
193 | struct netsrc_rep nrp; | |
194 | ||
195 | dstsin6 = &nrq->nrq_sin6; | |
196 | ||
197 | if (dstsin6->sin6_len < sizeof (*dstsin6) || | |
198 | IN6_IS_ADDR_UNSPECIFIED(&dstsin6->sin6_addr)) | |
199 | return (EINVAL); | |
200 | ||
201 | memset(&ro, 0, sizeof(ro)); | |
202 | lck_mtx_lock(rnh_lock); | |
203 | ro.ro_rt = rt_lookup(TRUE, (struct sockaddr *)dstsin6, NULL, | |
204 | rt_tables[AF_INET6], nrq->nrq_ifscope); | |
205 | lck_mtx_unlock(rnh_lock); | |
206 | if (!ro.ro_rt) | |
207 | return (EHOSTUNREACH); | |
208 | in6 = in6_selectsrc(dstsin6, NULL, NULL, &ro, NULL, &storage, | |
209 | nrq->nrq_ifscope, &error); | |
39236c6e | 210 | ROUTE_RELEASE(&ro); |
6d2010ae A |
211 | if (!in6 || error) |
212 | return (error); | |
213 | memset(&nrp, 0, sizeof(nrp)); | |
214 | nrp.nrp_sin6.sin6_family = AF_INET6; | |
215 | nrp.nrp_sin6.sin6_len = sizeof(nrp.nrp_sin6); | |
216 | memcpy(&nrp.nrp_sin6.sin6_addr, in6, sizeof(nrp.nrp_sin6.sin6_addr)); | |
217 | lck_rw_lock_shared(&in6_ifaddr_rwlock); | |
218 | for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { | |
219 | if (memcmp(&ia->ia_addr.sin6_addr, in6, sizeof(*in6)) == 0) { | |
220 | struct sockaddr_in6 sin6; | |
221 | struct in6_addrpolicy *policy; | |
222 | ||
223 | if (ia->ia6_flags & IN6_IFF_TEMPORARY) | |
224 | nrp.nrp_flags |= NETSRC_IP6_FLAG_TEMPORARY; | |
225 | if (ia->ia6_flags & IN6_IFF_TENTATIVE) | |
226 | nrp.nrp_flags |= NETSRC_IP6_FLAG_TENTATIVE; | |
227 | if (ia->ia6_flags & IN6_IFF_DEPRECATED) | |
228 | nrp.nrp_flags |= NETSRC_IP6_FLAG_DEPRECATED; | |
316670eb A |
229 | if (ia->ia6_flags & IN6_IFF_OPTIMISTIC) |
230 | nrp.nrp_flags |= NETSRC_IP6_FLAG_OPTIMISTIC; | |
39236c6e A |
231 | if (ia->ia6_flags & IN6_IFF_SECURED) |
232 | nrp.nrp_flags |= NETSRC_IP6_FLAG_SECURED; | |
6d2010ae A |
233 | sin6.sin6_family = AF_INET6; |
234 | sin6.sin6_len = sizeof(sin6); | |
235 | memcpy(&sin6.sin6_addr, in6, sizeof(*in6)); | |
236 | policy = in6_addrsel_lookup_policy(&sin6); | |
237 | if (policy->label != -1) { | |
238 | nrp.nrp_label = policy->label; | |
239 | nrp.nrp_precedence = policy->preced; | |
240 | } | |
241 | memcpy(&sin6.sin6_addr, &dstsin6->sin6_addr, | |
242 | sizeof(dstsin6->sin6_addr)); | |
243 | policy = in6_addrsel_lookup_policy(&sin6); | |
244 | if (policy->label != -1) { | |
245 | nrp.nrp_dstlabel = policy->label; | |
246 | nrp.nrp_dstprecedence = policy->preced; | |
247 | } | |
248 | break; | |
249 | } | |
250 | } | |
251 | lck_rw_done(&in6_ifaddr_rwlock); | |
252 | error = ctl_enqueuedata(kctl, unit, &nrp, sizeof(nrp), | |
253 | CTL_DATA_EOR); | |
254 | ||
255 | return (error); | |
256 | } |