]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
6d2010ae | 2 | * Copyright (c) 2000-2011 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * Copyright (c) 1982, 1986, 1991, 1993 | |
30 | * The Regents of the University of California. All rights reserved. | |
31 | * | |
32 | * Redistribution and use in source and binary forms, with or without | |
33 | * modification, are permitted provided that the following conditions | |
34 | * are met: | |
35 | * 1. Redistributions of source code must retain the above copyright | |
36 | * notice, this list of conditions and the following disclaimer. | |
37 | * 2. Redistributions in binary form must reproduce the above copyright | |
38 | * notice, this list of conditions and the following disclaimer in the | |
39 | * documentation and/or other materials provided with the distribution. | |
40 | * 3. All advertising materials mentioning features or use of this software | |
41 | * must display the following acknowledgement: | |
42 | * This product includes software developed by the University of | |
43 | * California, Berkeley and its contributors. | |
44 | * 4. Neither the name of the University nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | * | |
60 | * @(#)in.c 8.4 (Berkeley) 1/9/95 | |
9bccf70c | 61 | * $FreeBSD: src/sys/netinet/in.c,v 1.44.2.5 2001/08/13 16:26:17 ume Exp $ |
1c79356b A |
62 | */ |
63 | ||
64 | #include <sys/param.h> | |
65 | #include <sys/systm.h> | |
66 | #include <sys/sockio.h> | |
67 | #include <sys/socketvar.h> | |
68 | #include <sys/malloc.h> | |
69 | #include <sys/proc.h> | |
70 | #include <sys/socket.h> | |
71 | #include <sys/kernel.h> | |
72 | #include <sys/sysctl.h> | |
73 | #include <sys/kern_event.h> | |
91447636 | 74 | #include <sys/syslog.h> |
6d2010ae | 75 | #include <sys/mcache.h> |
b0d623f7 | 76 | #include <kern/zalloc.h> |
1c79356b | 77 | |
2d21ac55 A |
78 | #include <pexpert/pexpert.h> |
79 | ||
1c79356b | 80 | #include <net/if.h> |
1c79356b | 81 | #include <net/if_types.h> |
9bccf70c | 82 | #include <net/route.h> |
2d21ac55 | 83 | #include <net/kpi_protocol.h> |
1c79356b A |
84 | |
85 | #include <netinet/in.h> | |
86 | #include <netinet/in_var.h> | |
87 | #include <netinet/in_pcb.h> | |
88 | ||
89 | #include <netinet/igmp_var.h> | |
90 | #include <net/dlil.h> | |
91 | ||
92 | #include <netinet/ip_var.h> | |
93 | ||
94 | #include <netinet/tcp.h> | |
95 | #include <netinet/tcp_timer.h> | |
96 | #include <netinet/tcp_var.h> | |
97 | ||
98 | #include <sys/file.h> | |
99 | ||
b0d623f7 A |
100 | #if PF |
101 | #include <net/pfvar.h> | |
102 | #endif /* PF */ | |
1c79356b | 103 | |
91447636 A |
104 | static int in_mask2len(struct in_addr *); |
105 | static void in_len2mask(struct in_addr *, int); | |
106 | static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, | |
107 | struct ifnet *, struct proc *); | |
1c79356b | 108 | |
91447636 A |
109 | static void in_socktrim(struct sockaddr_in *); |
110 | static int in_ifinit(struct ifnet *, | |
111 | struct in_ifaddr *, struct sockaddr_in *, int); | |
1c79356b | 112 | |
b0d623f7 A |
113 | #define IA_HASH_INIT(ia) { \ |
114 | (ia)->ia_hash.tqe_next = (void *)(uintptr_t)-1; \ | |
115 | (ia)->ia_hash.tqe_prev = (void *)(uintptr_t)-1; \ | |
116 | } | |
117 | ||
118 | #define IA_IS_HASHED(ia) \ | |
119 | (!((ia)->ia_hash.tqe_next == (void *)(uintptr_t)-1 || \ | |
120 | (ia)->ia_hash.tqe_prev == (void *)(uintptr_t)-1)) | |
121 | ||
122 | static void in_iahash_remove(struct in_ifaddr *); | |
123 | static void in_iahash_insert(struct in_ifaddr *); | |
124 | static void in_iahash_insert_ptp(struct in_ifaddr *); | |
125 | static struct in_ifaddr *in_ifaddr_alloc(int); | |
6d2010ae A |
126 | static void in_ifaddr_attached(struct ifaddr *); |
127 | static void in_ifaddr_detached(struct ifaddr *); | |
b0d623f7 A |
128 | static void in_ifaddr_free(struct ifaddr *); |
129 | static void in_ifaddr_trace(struct ifaddr *, int); | |
130 | ||
1c79356b | 131 | static int subnetsarelocal = 0; |
6d2010ae | 132 | SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW | CTLFLAG_LOCKED, |
1c79356b A |
133 | &subnetsarelocal, 0, ""); |
134 | ||
91447636 A |
135 | /* Track whether or not the SIOCARPIPLL ioctl has been called */ |
136 | __private_extern__ u_int32_t ipv4_ll_arp_aware = 0; | |
1c79356b | 137 | |
6d2010ae A |
138 | #define INIFA_TRACE_HIST_SIZE 32 /* size of trace history */ |
139 | ||
140 | /* For gdb */ | |
141 | __private_extern__ unsigned int inifa_trace_hist_size = INIFA_TRACE_HIST_SIZE; | |
142 | ||
b0d623f7 A |
143 | struct in_ifaddr_dbg { |
144 | struct in_ifaddr inifa; /* in_ifaddr */ | |
145 | struct in_ifaddr inifa_old; /* saved in_ifaddr */ | |
6d2010ae A |
146 | u_int16_t inifa_refhold_cnt; /* # of IFA_ADDREF */ |
147 | u_int16_t inifa_refrele_cnt; /* # of IFA_REMREF */ | |
b0d623f7 A |
148 | /* |
149 | * Alloc and free callers. | |
150 | */ | |
151 | ctrace_t inifa_alloc; | |
152 | ctrace_t inifa_free; | |
153 | /* | |
6d2010ae | 154 | * Circular lists of IFA_ADDREF and IFA_REMREF callers. |
b0d623f7 | 155 | */ |
6d2010ae A |
156 | ctrace_t inifa_refhold[INIFA_TRACE_HIST_SIZE]; |
157 | ctrace_t inifa_refrele[INIFA_TRACE_HIST_SIZE]; | |
158 | /* | |
159 | * Trash list linkage | |
160 | */ | |
161 | TAILQ_ENTRY(in_ifaddr_dbg) inifa_trash_link; | |
b0d623f7 A |
162 | }; |
163 | ||
6d2010ae A |
164 | /* List of trash in_ifaddr entries protected by inifa_trash_lock */ |
165 | static TAILQ_HEAD(, in_ifaddr_dbg) inifa_trash_head; | |
166 | static decl_lck_mtx_data(, inifa_trash_lock); | |
167 | ||
168 | #if DEBUG | |
169 | static unsigned int inifa_debug = 1; /* debugging (enabled) */ | |
170 | #else | |
171 | static unsigned int inifa_debug; /* debugging (disabled) */ | |
172 | #endif /* !DEBUG */ | |
b0d623f7 A |
173 | static unsigned int inifa_size; /* size of zone element */ |
174 | static struct zone *inifa_zone; /* zone for in_ifaddr */ | |
175 | ||
176 | #define INIFA_ZONE_MAX 64 /* maximum elements in zone */ | |
177 | #define INIFA_ZONE_NAME "in_ifaddr" /* zone name */ | |
178 | ||
6d2010ae A |
179 | /* |
180 | * Return 1 if the address is | |
181 | * - loopback | |
182 | * - unicast or multicast link local | |
183 | * - routed via a link level gateway | |
184 | * - belongs to a directly connected (sub)net | |
185 | */ | |
2d21ac55 A |
186 | int |
187 | inaddr_local(struct in_addr in) | |
188 | { | |
189 | struct rtentry *rt; | |
190 | struct sockaddr_in sin; | |
191 | int local = 0; | |
192 | ||
6d2010ae A |
193 | if (ntohl(in.s_addr) == INADDR_LOOPBACK || IN_LINKLOCAL(ntohl(in.s_addr))) { |
194 | local = 1; | |
195 | } else if (ntohl(in.s_addr) >= INADDR_UNSPEC_GROUP && | |
196 | ntohl(in.s_addr) <= INADDR_MAX_LOCAL_GROUP) { | |
2d21ac55 | 197 | local = 1; |
2d21ac55 | 198 | } else { |
6d2010ae A |
199 | sin.sin_family = AF_INET; |
200 | sin.sin_len = sizeof (sin); | |
201 | sin.sin_addr = in; | |
202 | rt = rtalloc1((struct sockaddr *)&sin, 0, 0); | |
203 | ||
204 | if (rt != NULL) { | |
205 | RT_LOCK_SPIN(rt); | |
206 | if (rt->rt_gateway->sa_family == AF_LINK || | |
207 | (rt->rt_ifp->if_flags & IFF_LOOPBACK)) | |
208 | local = 1; | |
209 | RT_UNLOCK(rt); | |
210 | rtfree(rt); | |
211 | } else { | |
212 | local = in_localaddr(in); | |
213 | } | |
2d21ac55 A |
214 | } |
215 | return (local); | |
216 | } | |
217 | ||
1c79356b A |
218 | /* |
219 | * Return 1 if an internet address is for a ``local'' host | |
220 | * (one to which we have a connection). If subnetsarelocal | |
221 | * is true, this includes other subnets of the local net. | |
222 | * Otherwise, it includes only the directly-connected (sub)nets. | |
223 | */ | |
224 | int | |
2d21ac55 | 225 | in_localaddr(struct in_addr in) |
1c79356b | 226 | { |
b0d623f7 | 227 | u_int32_t i = ntohl(in.s_addr); |
91447636 | 228 | struct in_ifaddr *ia; |
1c79356b A |
229 | |
230 | if (subnetsarelocal) { | |
b0d623f7 | 231 | lck_rw_lock_shared(in_ifaddr_rwlock); |
1c79356b | 232 | for (ia = in_ifaddrhead.tqh_first; ia; |
6d2010ae A |
233 | ia = ia->ia_link.tqe_next) { |
234 | IFA_LOCK(&ia->ia_ifa); | |
91447636 | 235 | if ((i & ia->ia_netmask) == ia->ia_net) { |
6d2010ae | 236 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 | 237 | lck_rw_done(in_ifaddr_rwlock); |
1c79356b | 238 | return (1); |
91447636 | 239 | } |
6d2010ae A |
240 | IFA_UNLOCK(&ia->ia_ifa); |
241 | } | |
b0d623f7 | 242 | lck_rw_done(in_ifaddr_rwlock); |
1c79356b | 243 | } else { |
b0d623f7 | 244 | lck_rw_lock_shared(in_ifaddr_rwlock); |
1c79356b | 245 | for (ia = in_ifaddrhead.tqh_first; ia; |
6d2010ae A |
246 | ia = ia->ia_link.tqe_next) { |
247 | IFA_LOCK(&ia->ia_ifa); | |
91447636 | 248 | if ((i & ia->ia_subnetmask) == ia->ia_subnet) { |
6d2010ae | 249 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 | 250 | lck_rw_done(in_ifaddr_rwlock); |
1c79356b | 251 | return (1); |
91447636 | 252 | } |
6d2010ae A |
253 | IFA_UNLOCK(&ia->ia_ifa); |
254 | } | |
b0d623f7 | 255 | lck_rw_done(in_ifaddr_rwlock); |
1c79356b A |
256 | } |
257 | return (0); | |
258 | } | |
259 | ||
260 | /* | |
261 | * Determine whether an IP address is in a reserved set of addresses | |
262 | * that may not be forwarded, or whether datagrams to that destination | |
263 | * may be forwarded. | |
264 | */ | |
265 | int | |
2d21ac55 | 266 | in_canforward(struct in_addr in) |
1c79356b | 267 | { |
b0d623f7 A |
268 | u_int32_t i = ntohl(in.s_addr); |
269 | u_int32_t net; | |
1c79356b A |
270 | |
271 | if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) | |
272 | return (0); | |
273 | if (IN_CLASSA(i)) { | |
274 | net = i & IN_CLASSA_NET; | |
275 | if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) | |
276 | return (0); | |
277 | } | |
278 | return (1); | |
279 | } | |
280 | ||
281 | /* | |
282 | * Trim a mask in a sockaddr | |
283 | */ | |
284 | static void | |
2d21ac55 | 285 | in_socktrim(struct sockaddr_in *ap) |
1c79356b | 286 | { |
91447636 A |
287 | char *cplim = (char *) &ap->sin_addr; |
288 | char *cp = (char *) (&ap->sin_addr + 1); | |
1c79356b A |
289 | |
290 | ap->sin_len = 0; | |
291 | while (--cp >= cplim) | |
292 | if (*cp) { | |
293 | (ap)->sin_len = cp - (char *) (ap) + 1; | |
294 | break; | |
295 | } | |
296 | } | |
297 | ||
298 | static int | |
2d21ac55 | 299 | in_mask2len(struct in_addr *mask) |
1c79356b | 300 | { |
2d21ac55 | 301 | size_t x, y; |
1c79356b A |
302 | u_char *p; |
303 | ||
304 | p = (u_char *)mask; | |
305 | for (x = 0; x < sizeof(*mask); x++) { | |
306 | if (p[x] != 0xff) | |
307 | break; | |
308 | } | |
309 | y = 0; | |
310 | if (x < sizeof(*mask)) { | |
311 | for (y = 0; y < 8; y++) { | |
312 | if ((p[x] & (0x80 >> y)) == 0) | |
313 | break; | |
314 | } | |
315 | } | |
316 | return x * 8 + y; | |
317 | } | |
318 | ||
319 | static void | |
2d21ac55 | 320 | in_len2mask(struct in_addr *mask, int len) |
1c79356b A |
321 | { |
322 | int i; | |
323 | u_char *p; | |
324 | ||
325 | p = (u_char *)mask; | |
326 | bzero(mask, sizeof(*mask)); | |
327 | for (i = 0; i < len / 8; i++) | |
328 | p[i] = 0xff; | |
329 | if (len % 8) | |
330 | p[i] = (0xff00 >> (len % 8)) & 0xff; | |
331 | } | |
332 | ||
333 | static int in_interfaces; /* number of external internet interfaces */ | |
334 | ||
6d2010ae A |
335 | static int |
336 | in_domifattach(struct ifnet *ifp) | |
337 | { | |
338 | int error; | |
339 | ||
340 | if ((error = proto_plumb(PF_INET, ifp)) && error != EEXIST) | |
341 | log(LOG_ERR, "%s: proto_plumb returned %d if=%s%d\n", | |
342 | __func__, error, ifp->if_name, ifp->if_unit); | |
343 | ||
344 | return (error); | |
345 | } | |
346 | ||
1c79356b A |
347 | /* |
348 | * Generic internet control operations (ioctl's). | |
349 | * Ifp is 0 if not an interface-specific ioctl. | |
2d21ac55 A |
350 | * |
351 | * Returns: 0 Success | |
352 | * EINVAL | |
353 | * EADDRNOTAVAIL | |
354 | * EDESTADDRREQ | |
355 | * EPERM | |
356 | * ENOBUFS | |
357 | * EBUSY | |
358 | * EOPNOTSUPP | |
359 | * proc_suser:EPERM | |
360 | * suser:EPERM | |
361 | * in_lifaddr_ioctl:??? | |
362 | * dlil_ioctl:??? | |
363 | * in_ifinit:??? | |
364 | * dlil_plumb_protocol:??? | |
365 | * dlil_unplumb_protocol:??? | |
1c79356b A |
366 | */ |
367 | /* ARGSUSED */ | |
368 | int | |
91447636 A |
369 | in_control( |
370 | struct socket *so, | |
371 | u_long cmd, | |
372 | caddr_t data, | |
373 | struct ifnet *ifp, | |
374 | struct proc *p) | |
1c79356b | 375 | { |
91447636 | 376 | struct ifreq *ifr = (struct ifreq *)data; |
2d21ac55 | 377 | struct in_ifaddr *ia = NULL, *iap; |
91447636 | 378 | struct ifaddr *ifa; |
1c79356b A |
379 | struct in_aliasreq *ifra = (struct in_aliasreq *)data; |
380 | struct sockaddr_in oldaddr; | |
2d21ac55 A |
381 | int error = 0; |
382 | int hostIsNew, maskIsNew; | |
1c79356b A |
383 | struct kev_msg ev_msg; |
384 | struct kev_in_data in_event_data; | |
385 | ||
6d2010ae A |
386 | bzero(&in_event_data, sizeof(struct kev_in_data)); |
387 | bzero(&ev_msg, sizeof(struct kev_msg)); | |
1c79356b A |
388 | switch (cmd) { |
389 | case SIOCALIFADDR: | |
390 | case SIOCDLIFADDR: | |
b0d623f7 | 391 | if ((error = proc_suser(p)) != 0) |
1c79356b | 392 | return error; |
1c79356b A |
393 | /*fall through*/ |
394 | case SIOCGLIFADDR: | |
395 | if (!ifp) | |
396 | return EINVAL; | |
397 | return in_lifaddr_ioctl(so, cmd, data, ifp, p); | |
398 | } | |
399 | ||
400 | /* | |
401 | * Find address for this interface, if it exists. | |
402 | * | |
403 | * If an alias address was specified, find that one instead of | |
404 | * the first one on the interface. | |
405 | */ | |
91447636 | 406 | if (ifp) { |
b0d623f7 | 407 | lck_rw_lock_shared(in_ifaddr_rwlock); |
1c79356b A |
408 | for (iap = in_ifaddrhead.tqh_first; iap; |
409 | iap = iap->ia_link.tqe_next) | |
410 | if (iap->ia_ifp == ifp) { | |
6d2010ae | 411 | IFA_LOCK(&iap->ia_ifa); |
1c79356b A |
412 | if (((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr == |
413 | iap->ia_addr.sin_addr.s_addr) { | |
414 | ia = iap; | |
6d2010ae | 415 | IFA_UNLOCK(&iap->ia_ifa); |
1c79356b A |
416 | break; |
417 | } else if (ia == NULL) { | |
418 | ia = iap; | |
6d2010ae A |
419 | if (ifr->ifr_addr.sa_family != AF_INET) { |
420 | IFA_UNLOCK(&iap->ia_ifa); | |
1c79356b | 421 | break; |
6d2010ae | 422 | } |
1c79356b | 423 | } |
6d2010ae | 424 | IFA_UNLOCK(&iap->ia_ifa); |
1c79356b | 425 | } |
b0d623f7 | 426 | /* take a reference on ia before releasing lock */ |
2d21ac55 | 427 | if (ia != NULL) { |
6d2010ae | 428 | IFA_ADDREF(&ia->ia_ifa); |
2d21ac55 | 429 | } |
b0d623f7 | 430 | lck_rw_done(in_ifaddr_rwlock); |
91447636 | 431 | } |
1c79356b | 432 | switch (cmd) { |
0b4e3aa0 | 433 | case SIOCAUTOADDR: |
91447636 | 434 | case SIOCARPIPLL: |
b0d623f7 | 435 | if ((error = proc_suser(p)) != 0) { |
2d21ac55 A |
436 | goto done; |
437 | } | |
438 | if (ifp == 0) { | |
439 | error = EADDRNOTAVAIL; | |
440 | goto done; | |
441 | } | |
0b4e3aa0 | 442 | break; |
1c79356b A |
443 | |
444 | case SIOCAIFADDR: | |
445 | case SIOCDIFADDR: | |
2d21ac55 A |
446 | if (ifp == 0) { |
447 | error = EADDRNOTAVAIL; | |
448 | goto done; | |
449 | } | |
1c79356b | 450 | if (ifra->ifra_addr.sin_family == AF_INET) { |
2d21ac55 A |
451 | struct in_ifaddr *oia; |
452 | ||
b0d623f7 | 453 | lck_rw_lock_shared(in_ifaddr_rwlock); |
1c79356b | 454 | for (oia = ia; ia; ia = ia->ia_link.tqe_next) { |
6d2010ae | 455 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
456 | if (ia->ia_ifp == ifp && |
457 | ia->ia_addr.sin_addr.s_addr == | |
6d2010ae A |
458 | ifra->ifra_addr.sin_addr.s_addr) { |
459 | IFA_ADDREF_LOCKED(&ia->ia_ifa); | |
460 | IFA_UNLOCK(&ia->ia_ifa); | |
1c79356b | 461 | break; |
6d2010ae A |
462 | } |
463 | IFA_UNLOCK(&ia->ia_ifa); | |
2d21ac55 | 464 | } |
b0d623f7 | 465 | lck_rw_done(in_ifaddr_rwlock); |
6d2010ae A |
466 | if (oia != NULL) |
467 | IFA_REMREF(&oia->ia_ifa); | |
1c79356b A |
468 | if ((ifp->if_flags & IFF_POINTOPOINT) |
469 | && (cmd == SIOCAIFADDR) | |
470 | && (ifra->ifra_dstaddr.sin_addr.s_addr | |
471 | == INADDR_ANY)) { | |
2d21ac55 A |
472 | error = EDESTADDRREQ; |
473 | goto done; | |
1c79356b A |
474 | } |
475 | } | |
2d21ac55 A |
476 | else if (cmd == SIOCAIFADDR) { |
477 | error = EINVAL; | |
478 | goto done; | |
479 | } | |
480 | if (cmd == SIOCDIFADDR && ia == 0) { | |
481 | error = EADDRNOTAVAIL; | |
482 | goto done; | |
483 | } | |
1c79356b A |
484 | /* FALLTHROUGH */ |
485 | case SIOCSIFADDR: | |
486 | case SIOCSIFNETMASK: | |
487 | case SIOCSIFDSTADDR: | |
6d2010ae A |
488 | /* socket is NULL if called from in_purgeaddrs() */ |
489 | if (so != NULL && (so->so_state & SS_PRIV) == 0) { | |
490 | error = EPERM; | |
491 | goto done; | |
492 | } | |
493 | /* in case it's NULL, make sure it came from the kernel */ | |
494 | if (so == NULL && p != kernproc) { | |
2d21ac55 A |
495 | error = EPERM; |
496 | goto done; | |
497 | } | |
498 | if (ifp == 0) { | |
499 | error = EADDRNOTAVAIL; | |
500 | goto done; | |
501 | } | |
502 | if (ifra->ifra_addr.sin_family != AF_INET | |
503 | && cmd == SIOCSIFADDR) { | |
504 | error = EINVAL; | |
505 | goto done; | |
506 | } | |
6d2010ae | 507 | if (ia == NULL) { |
b0d623f7 | 508 | ia = in_ifaddr_alloc(M_WAITOK); |
6d2010ae | 509 | if (ia == NULL) { |
2d21ac55 A |
510 | error = ENOBUFS; |
511 | goto done; | |
512 | } | |
6d2010ae | 513 | ifnet_lock_exclusive(ifp); |
1c79356b | 514 | ifa = &ia->ia_ifa; |
6d2010ae | 515 | IFA_LOCK(ifa); |
b0d623f7 | 516 | /* Hold a reference for this routine */ |
6d2010ae A |
517 | IFA_ADDREF_LOCKED(ifa); |
518 | IA_HASH_INIT(ia); | |
1c79356b A |
519 | ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; |
520 | ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; | |
521 | ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; | |
522 | ia->ia_sockmask.sin_len = 8; | |
523 | if (ifp->if_flags & IFF_BROADCAST) { | |
524 | ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); | |
525 | ia->ia_broadaddr.sin_family = AF_INET; | |
526 | } | |
527 | ia->ia_ifp = ifp; | |
528 | if (!(ifp->if_flags & IFF_LOOPBACK)) | |
529 | in_interfaces++; | |
b0d623f7 | 530 | /* if_attach_ifa() holds a reference for ifa_link */ |
91447636 | 531 | if_attach_ifa(ifp, ifa); |
6d2010ae A |
532 | /* |
533 | * If we have to go through in_ifinit(), make sure | |
534 | * to avoid installing route(s) based on this address | |
535 | * via PFC_IFUP event, before the link resolver (ARP) | |
536 | * initializes it. | |
537 | */ | |
538 | if (cmd == SIOCAIFADDR || cmd == SIOCSIFADDR) | |
539 | ifa->ifa_debug |= IFD_NOTREADY; | |
540 | IFA_UNLOCK(ifa); | |
91447636 | 541 | ifnet_lock_done(ifp); |
b0d623f7 A |
542 | lck_rw_lock_exclusive(in_ifaddr_rwlock); |
543 | /* Hold a reference for ia_link */ | |
6d2010ae | 544 | IFA_ADDREF(ifa); |
91447636 | 545 | TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); |
b0d623f7 | 546 | lck_rw_done(in_ifaddr_rwlock); |
6d2010ae A |
547 | error = in_domifattach(ifp); |
548 | /* discard error,can be cold with unsupported interfaces */ | |
549 | if (error) | |
550 | error = 0; | |
1c79356b A |
551 | } |
552 | break; | |
553 | ||
0b4e3aa0 A |
554 | case SIOCPROTOATTACH: |
555 | case SIOCPROTODETACH: | |
b0d623f7 | 556 | if ((error = proc_suser(p)) != 0) { |
2d21ac55 A |
557 | goto done; |
558 | } | |
559 | if (ifp == 0) { | |
560 | error = EADDRNOTAVAIL; | |
561 | goto done; | |
562 | } | |
9bccf70c | 563 | break; |
b0d623f7 | 564 | |
1c79356b | 565 | case SIOCSIFBRDADDR: |
2d21ac55 A |
566 | if ((so->so_state & SS_PRIV) == 0) { |
567 | error = EPERM; | |
568 | goto done; | |
569 | } | |
1c79356b A |
570 | /* FALLTHROUGH */ |
571 | ||
572 | case SIOCGIFADDR: | |
573 | case SIOCGIFNETMASK: | |
574 | case SIOCGIFDSTADDR: | |
575 | case SIOCGIFBRDADDR: | |
2d21ac55 A |
576 | if (ia == (struct in_ifaddr *)0) { |
577 | error = EADDRNOTAVAIL; | |
578 | goto done; | |
579 | } | |
1c79356b A |
580 | break; |
581 | } | |
582 | switch (cmd) { | |
0b4e3aa0 | 583 | case SIOCAUTOADDR: |
91447636 A |
584 | ifnet_lock_exclusive(ifp); |
585 | if (ifr->ifr_intval) | |
0b4e3aa0 A |
586 | ifp->if_eflags |= IFEF_AUTOCONFIGURING; |
587 | else | |
588 | ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; | |
91447636 A |
589 | ifnet_lock_done(ifp); |
590 | break; | |
591 | ||
592 | case SIOCARPIPLL: | |
91447636 A |
593 | ipv4_ll_arp_aware = 1; |
594 | ifnet_lock_exclusive(ifp); | |
595 | if (ifr->ifr_data) | |
596 | ifp->if_eflags |= IFEF_ARPLL; | |
597 | else | |
598 | ifp->if_eflags &= ~IFEF_ARPLL; | |
599 | ifnet_lock_done(ifp); | |
0b4e3aa0 | 600 | break; |
1c79356b A |
601 | |
602 | case SIOCGIFADDR: | |
6d2010ae | 603 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 604 | *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; |
6d2010ae | 605 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
606 | break; |
607 | ||
608 | case SIOCGIFBRDADDR: | |
2d21ac55 A |
609 | if ((ifp->if_flags & IFF_BROADCAST) == 0) { |
610 | error = EINVAL; | |
611 | break; | |
612 | } | |
6d2010ae | 613 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 614 | *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; |
6d2010ae | 615 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
616 | break; |
617 | ||
618 | case SIOCGIFDSTADDR: | |
2d21ac55 A |
619 | if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { |
620 | error = EINVAL; | |
621 | break; | |
622 | } | |
6d2010ae | 623 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 624 | *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; |
6d2010ae | 625 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
626 | break; |
627 | ||
628 | case SIOCGIFNETMASK: | |
6d2010ae | 629 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 630 | *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; |
6d2010ae | 631 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
632 | break; |
633 | ||
634 | case SIOCSIFDSTADDR: | |
2d21ac55 A |
635 | if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { |
636 | error = EINVAL; | |
637 | break; | |
638 | } | |
6d2010ae | 639 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
640 | oldaddr = ia->ia_dstaddr; |
641 | ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; | |
b0d623f7 A |
642 | if (ia->ia_dstaddr.sin_family == AF_INET) |
643 | ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); | |
6d2010ae | 644 | IFA_UNLOCK(&ia->ia_ifa); |
2d21ac55 | 645 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFDSTADDR, ia); |
6d2010ae | 646 | IFA_LOCK(&ia->ia_ifa); |
2d21ac55 A |
647 | if (error == EOPNOTSUPP) { |
648 | error = 0; | |
649 | } | |
1c79356b | 650 | if (error) { |
2d21ac55 | 651 | ia->ia_dstaddr = oldaddr; |
6d2010ae | 652 | IFA_UNLOCK(&ia->ia_ifa); |
2d21ac55 | 653 | break; |
1c79356b | 654 | } |
6d2010ae | 655 | IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); |
1c79356b A |
656 | |
657 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
658 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
659 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
6d2010ae | 660 | |
1c79356b A |
661 | ev_msg.event_code = KEV_INET_SIFDSTADDR; |
662 | ||
663 | if (ia->ia_ifa.ifa_dstaddr) | |
664 | in_event_data.ia_dstaddr = | |
665 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
666 | else | |
667 | in_event_data.ia_dstaddr.s_addr = 0; | |
668 | ||
669 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
670 | in_event_data.ia_net = ia->ia_net; | |
671 | in_event_data.ia_netmask = ia->ia_netmask; | |
672 | in_event_data.ia_subnet = ia->ia_subnet; | |
673 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
674 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
6d2010ae | 675 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
676 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); |
677 | in_event_data.link_data.if_family = ifp->if_family; | |
b0d623f7 | 678 | in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; |
1c79356b A |
679 | |
680 | ev_msg.dv[0].data_ptr = &in_event_data; | |
681 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
682 | ev_msg.dv[1].data_length = 0; | |
683 | ||
684 | kev_post_msg(&ev_msg); | |
685 | ||
6d2010ae A |
686 | lck_mtx_lock(rnh_lock); |
687 | IFA_LOCK(&ia->ia_ifa); | |
1c79356b A |
688 | if (ia->ia_flags & IFA_ROUTE) { |
689 | ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; | |
6d2010ae A |
690 | IFA_UNLOCK(&ia->ia_ifa); |
691 | rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); | |
692 | IFA_LOCK(&ia->ia_ifa); | |
1c79356b | 693 | ia->ia_ifa.ifa_dstaddr = |
6d2010ae A |
694 | (struct sockaddr *)&ia->ia_dstaddr; |
695 | IFA_UNLOCK(&ia->ia_ifa); | |
696 | rtinit_locked(&(ia->ia_ifa), (int)RTM_ADD, | |
697 | RTF_HOST|RTF_UP); | |
698 | } else { | |
699 | IFA_UNLOCK(&ia->ia_ifa); | |
1c79356b | 700 | } |
6d2010ae | 701 | lck_mtx_unlock(rnh_lock); |
1c79356b A |
702 | break; |
703 | ||
704 | case SIOCSIFBRDADDR: | |
2d21ac55 A |
705 | if ((ifp->if_flags & IFF_BROADCAST) == 0) { |
706 | error = EINVAL; | |
707 | break; | |
708 | } | |
6d2010ae | 709 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
710 | ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; |
711 | ||
712 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
713 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
714 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
6d2010ae | 715 | |
1c79356b A |
716 | ev_msg.event_code = KEV_INET_SIFBRDADDR; |
717 | ||
718 | if (ia->ia_ifa.ifa_dstaddr) | |
719 | in_event_data.ia_dstaddr = | |
720 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
721 | else | |
722 | in_event_data.ia_dstaddr.s_addr = 0; | |
723 | ||
724 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
725 | in_event_data.ia_net = ia->ia_net; | |
726 | in_event_data.ia_netmask = ia->ia_netmask; | |
727 | in_event_data.ia_subnet = ia->ia_subnet; | |
728 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
729 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
6d2010ae | 730 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
731 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); |
732 | in_event_data.link_data.if_family = ifp->if_family; | |
b0d623f7 | 733 | in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; |
1c79356b A |
734 | |
735 | ev_msg.dv[0].data_ptr = &in_event_data; | |
736 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
737 | ev_msg.dv[1].data_length = 0; | |
738 | ||
739 | kev_post_msg(&ev_msg); | |
740 | ||
741 | break; | |
742 | ||
743 | case SIOCSIFADDR: | |
b0d623f7 A |
744 | /* |
745 | * If this is a new address, the reference count for the | |
746 | * hash table has been taken at creation time above. | |
747 | */ | |
748 | error = in_ifinit(ifp, ia, | |
749 | (struct sockaddr_in *)&ifr->ifr_addr, 1); | |
750 | #if PF | |
751 | if (!error) | |
752 | (void) pf_ifaddr_hook(ifp, cmd); | |
753 | #endif /* PF */ | |
2d21ac55 | 754 | break; |
1c79356b | 755 | |
0b4e3aa0 | 756 | case SIOCPROTOATTACH: |
6d2010ae | 757 | error = in_domifattach(ifp); |
2d21ac55 | 758 | break; |
6d2010ae | 759 | |
0b4e3aa0 | 760 | case SIOCPROTODETACH: |
6d2010ae A |
761 | /* |
762 | * If an IPv4 address is still present, refuse to detach. | |
763 | */ | |
91447636 | 764 | ifnet_lock_shared(ifp); |
6d2010ae A |
765 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { |
766 | IFA_LOCK(ifa); | |
767 | if (ifa->ifa_addr->sa_family == AF_INET) { | |
768 | IFA_UNLOCK(ifa); | |
91447636 | 769 | break; |
6d2010ae A |
770 | } |
771 | IFA_UNLOCK(ifa); | |
772 | } | |
91447636 | 773 | ifnet_lock_done(ifp); |
6d2010ae | 774 | if (ifa != NULL) { |
2d21ac55 A |
775 | error = EBUSY; |
776 | break; | |
777 | } | |
55e303ae | 778 | |
2d21ac55 | 779 | error = proto_unplumb(PF_INET, ifp); |
9bccf70c | 780 | break; |
0b4e3aa0 | 781 | |
2d21ac55 A |
782 | case SIOCSIFNETMASK: { |
783 | u_long i; | |
6d2010ae | 784 | |
1c79356b | 785 | i = ifra->ifra_addr.sin_addr.s_addr; |
6d2010ae | 786 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
787 | ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i); |
788 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
789 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
790 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
6d2010ae | 791 | |
1c79356b A |
792 | ev_msg.event_code = KEV_INET_SIFNETMASK; |
793 | ||
794 | if (ia->ia_ifa.ifa_dstaddr) | |
795 | in_event_data.ia_dstaddr = | |
796 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
797 | else | |
798 | in_event_data.ia_dstaddr.s_addr = 0; | |
799 | ||
800 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
801 | in_event_data.ia_net = ia->ia_net; | |
802 | in_event_data.ia_netmask = ia->ia_netmask; | |
803 | in_event_data.ia_subnet = ia->ia_subnet; | |
804 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
805 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
6d2010ae | 806 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
807 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); |
808 | in_event_data.link_data.if_family = ifp->if_family; | |
b0d623f7 | 809 | in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; |
1c79356b A |
810 | |
811 | ev_msg.dv[0].data_ptr = &in_event_data; | |
812 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
813 | ev_msg.dv[1].data_length = 0; | |
814 | ||
815 | kev_post_msg(&ev_msg); | |
816 | ||
817 | break; | |
2d21ac55 | 818 | } |
1c79356b A |
819 | case SIOCAIFADDR: |
820 | maskIsNew = 0; | |
821 | hostIsNew = 1; | |
822 | error = 0; | |
b0d623f7 | 823 | |
6d2010ae | 824 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
825 | if (ia->ia_addr.sin_family == AF_INET) { |
826 | if (ifra->ifra_addr.sin_len == 0) { | |
827 | ifra->ifra_addr = ia->ia_addr; | |
828 | hostIsNew = 0; | |
829 | } else if (ifra->ifra_addr.sin_addr.s_addr == | |
830 | ia->ia_addr.sin_addr.s_addr) | |
831 | hostIsNew = 0; | |
832 | } | |
833 | if (ifra->ifra_mask.sin_len) { | |
6d2010ae | 834 | IFA_UNLOCK(&ia->ia_ifa); |
91447636 | 835 | in_ifscrub(ifp, ia, 0); |
6d2010ae | 836 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
837 | ia->ia_sockmask = ifra->ifra_mask; |
838 | ia->ia_subnetmask = | |
839 | ntohl(ia->ia_sockmask.sin_addr.s_addr); | |
840 | maskIsNew = 1; | |
841 | } | |
842 | if ((ifp->if_flags & IFF_POINTOPOINT) && | |
843 | (ifra->ifra_dstaddr.sin_family == AF_INET)) { | |
6d2010ae | 844 | IFA_UNLOCK(&ia->ia_ifa); |
91447636 | 845 | in_ifscrub(ifp, ia, 0); |
6d2010ae | 846 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 847 | ia->ia_dstaddr = ifra->ifra_dstaddr; |
b0d623f7 | 848 | ia->ia_dstaddr.sin_len = sizeof (struct sockaddr_in); |
1c79356b A |
849 | maskIsNew = 1; /* We lie; but the effect's the same */ |
850 | } | |
851 | if (ifra->ifra_addr.sin_family == AF_INET && | |
852 | (hostIsNew || maskIsNew)) { | |
6d2010ae | 853 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b | 854 | error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); |
6d2010ae A |
855 | } else { |
856 | IFA_UNLOCK(&ia->ia_ifa); | |
1c79356b | 857 | } |
b0d623f7 A |
858 | #if PF |
859 | if (!error) | |
860 | (void) pf_ifaddr_hook(ifp, cmd); | |
861 | #endif /* PF */ | |
6d2010ae | 862 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
863 | if ((ifp->if_flags & IFF_BROADCAST) && |
864 | (ifra->ifra_broadaddr.sin_family == AF_INET)) | |
865 | ia->ia_broadaddr = ifra->ifra_broadaddr; | |
866 | ||
867 | /* | |
868 | * Report event. | |
869 | */ | |
870 | ||
9bccf70c | 871 | if ((error == 0) || (error == EEXIST)) { |
1c79356b A |
872 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
873 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
874 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
875 | ||
876 | if (hostIsNew) | |
877 | ev_msg.event_code = KEV_INET_NEW_ADDR; | |
878 | else | |
879 | ev_msg.event_code = KEV_INET_CHANGED_ADDR; | |
880 | ||
881 | if (ia->ia_ifa.ifa_dstaddr) | |
882 | in_event_data.ia_dstaddr = | |
883 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
884 | else | |
885 | in_event_data.ia_dstaddr.s_addr = 0; | |
886 | ||
887 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
888 | in_event_data.ia_net = ia->ia_net; | |
889 | in_event_data.ia_netmask = ia->ia_netmask; | |
890 | in_event_data.ia_subnet = ia->ia_subnet; | |
891 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
892 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
6d2010ae | 893 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
894 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); |
895 | in_event_data.link_data.if_family = ifp->if_family; | |
b0d623f7 | 896 | in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; |
1c79356b A |
897 | |
898 | ev_msg.dv[0].data_ptr = &in_event_data; | |
899 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
900 | ev_msg.dv[1].data_length = 0; | |
901 | ||
902 | kev_post_msg(&ev_msg); | |
6d2010ae A |
903 | } else { |
904 | IFA_UNLOCK(&ia->ia_ifa); | |
1c79356b | 905 | } |
2d21ac55 | 906 | break; |
1c79356b A |
907 | |
908 | case SIOCDIFADDR: | |
2d21ac55 | 909 | error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia); |
91447636 A |
910 | if (error == EOPNOTSUPP) |
911 | error = 0; | |
2d21ac55 A |
912 | if (error != 0) { |
913 | break; | |
914 | } | |
9bccf70c | 915 | |
91447636 | 916 | /* Fill out the kernel event information */ |
1c79356b A |
917 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
918 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
919 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
6d2010ae | 920 | |
1c79356b A |
921 | ev_msg.event_code = KEV_INET_ADDR_DELETED; |
922 | ||
6d2010ae | 923 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
924 | if (ia->ia_ifa.ifa_dstaddr) |
925 | in_event_data.ia_dstaddr = | |
926 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
927 | else | |
928 | in_event_data.ia_dstaddr.s_addr = 0; | |
929 | ||
930 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
931 | in_event_data.ia_net = ia->ia_net; | |
932 | in_event_data.ia_netmask = ia->ia_netmask; | |
933 | in_event_data.ia_subnet = ia->ia_subnet; | |
934 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
935 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
6d2010ae | 936 | IFA_UNLOCK(&ia->ia_ifa); |
1c79356b A |
937 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); |
938 | in_event_data.link_data.if_family = ifp->if_family; | |
b0d623f7 | 939 | in_event_data.link_data.if_unit = (u_int32_t) ifp->if_unit; |
1c79356b A |
940 | |
941 | ev_msg.dv[0].data_ptr = &in_event_data; | |
91447636 | 942 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); |
1c79356b A |
943 | ev_msg.dv[1].data_length = 0; |
944 | ||
b0d623f7 A |
945 | ifa = &ia->ia_ifa; |
946 | lck_rw_lock_exclusive(in_ifaddr_rwlock); | |
947 | /* Release ia_link reference */ | |
6d2010ae | 948 | IFA_REMREF(ifa); |
91447636 | 949 | TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); |
6d2010ae | 950 | IFA_LOCK(ifa); |
b0d623f7 A |
951 | if (IA_IS_HASHED(ia)) |
952 | in_iahash_remove(ia); | |
6d2010ae | 953 | IFA_UNLOCK(ifa); |
b0d623f7 A |
954 | lck_rw_done(in_ifaddr_rwlock); |
955 | ||
9bccf70c A |
956 | /* |
957 | * in_ifscrub kills the interface route. | |
958 | */ | |
b0d623f7 | 959 | in_ifscrub(ifp, ia, 0); |
91447636 | 960 | ifnet_lock_exclusive(ifp); |
6d2010ae | 961 | IFA_LOCK(ifa); |
b0d623f7 | 962 | /* if_detach_ifa() releases ifa_link reference */ |
91447636 | 963 | if_detach_ifa(ifp, ifa); |
6d2010ae A |
964 | /* Our reference to this address is dropped at the bottom */ |
965 | IFA_UNLOCK(ifa); | |
966 | ||
b0d623f7 A |
967 | /* |
968 | * If the interface supports multicast, and no address is left, | |
969 | * remove the "all hosts" multicast group from that interface. | |
970 | */ | |
6d2010ae A |
971 | if ((ifp->if_flags & IFF_MULTICAST) != 0 || |
972 | ifp->if_allhostsinm != NULL ) { | |
9bccf70c | 973 | |
6d2010ae A |
974 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { |
975 | IFA_LOCK(ifa); | |
976 | if (ifa->ifa_addr->sa_family == AF_INET) { | |
977 | IFA_UNLOCK(ifa); | |
9bccf70c | 978 | break; |
6d2010ae A |
979 | } |
980 | IFA_UNLOCK(ifa); | |
9bccf70c | 981 | } |
91447636 | 982 | ifnet_lock_done(ifp); |
6d2010ae A |
983 | |
984 | lck_mtx_lock(&ifp->if_addrconfig_lock); | |
985 | if (ifa == NULL && ifp->if_allhostsinm != NULL) { | |
986 | struct in_multi *inm = ifp->if_allhostsinm; | |
987 | ifp->if_allhostsinm = NULL; | |
988 | ||
989 | in_delmulti(inm); | |
990 | /* release the reference for allhostsinm pointer */ | |
991 | INM_REMREF(inm); | |
992 | } | |
993 | lck_mtx_unlock(&ifp->if_addrconfig_lock); | |
994 | } else { | |
91447636 | 995 | ifnet_lock_done(ifp); |
6d2010ae | 996 | } |
91447636 A |
997 | |
998 | /* Post the kernel event */ | |
999 | kev_post_msg(&ev_msg); | |
2d21ac55 A |
1000 | |
1001 | /* | |
1002 | * See if there is any IPV4 address left and if so, | |
1003 | * reconfigure KDP to use current primary address. | |
1004 | */ | |
1005 | ifa = ifa_ifpgetprimary(ifp, AF_INET); | |
1006 | if (ifa != NULL) { | |
1007 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa); | |
1008 | if (error == EOPNOTSUPP) | |
1009 | error = 0; | |
1010 | ||
1011 | /* Release reference from ifa_ifpgetprimary() */ | |
6d2010ae | 1012 | IFA_REMREF(ifa); |
2d21ac55 | 1013 | } |
b0d623f7 A |
1014 | #if PF |
1015 | (void) pf_ifaddr_hook(ifp, cmd); | |
1016 | #endif /* PF */ | |
1c79356b A |
1017 | break; |
1018 | ||
9bccf70c | 1019 | #ifdef __APPLE__ |
1c79356b A |
1020 | case SIOCSETOT: { |
1021 | /* | |
1022 | * Inspiration from tcp_ctloutput() and ip_ctloutput() | |
9bccf70c | 1023 | * Special ioctl for OpenTransport sockets |
1c79356b A |
1024 | */ |
1025 | struct inpcb *inp, *cloned_inp; | |
91447636 | 1026 | int error2 = 0; |
1c79356b A |
1027 | int cloned_fd = *(int *)data; |
1028 | ||
1c79356b A |
1029 | inp = sotoinpcb(so); |
1030 | if (inp == NULL) { | |
1c79356b A |
1031 | break; |
1032 | } | |
1033 | ||
1034 | /* let's make sure it's either -1 or a valid file descriptor */ | |
1035 | if (cloned_fd != -1) { | |
1036 | struct socket *cloned_so; | |
91447636 A |
1037 | error2 = file_socket(cloned_fd, &cloned_so); |
1038 | if (error2){ | |
1c79356b A |
1039 | break; |
1040 | } | |
1c79356b | 1041 | cloned_inp = sotoinpcb(cloned_so); |
91447636 | 1042 | file_drop(cloned_fd); |
1c79356b A |
1043 | } else { |
1044 | cloned_inp = NULL; | |
1045 | } | |
1046 | ||
1047 | if (cloned_inp == NULL) { | |
1048 | /* OT always uses IP_PORTRANGE_HIGH */ | |
1049 | inp->inp_flags &= ~(INP_LOWPORT); | |
1050 | inp->inp_flags |= INP_HIGHPORT; | |
1051 | /* For UDP, OT allows broadcast by default */ | |
1052 | if (so->so_type == SOCK_DGRAM) | |
1053 | so->so_options |= SO_BROADCAST; | |
1054 | /* For TCP we want to see MSG_OOB when receive urgent data */ | |
1055 | else if (so->so_type == SOCK_STREAM) | |
1056 | so->so_options |= SO_WANTOOBFLAG; | |
1057 | } else { | |
1058 | inp->inp_ip_tos = cloned_inp->inp_ip_tos; | |
1059 | inp->inp_ip_ttl = cloned_inp->inp_ip_ttl; | |
1060 | inp->inp_flags = cloned_inp->inp_flags; | |
1061 | ||
1062 | /* Multicast options */ | |
1063 | if (cloned_inp->inp_moptions != NULL) { | |
1c79356b A |
1064 | struct ip_moptions *cloned_imo = cloned_inp->inp_moptions; |
1065 | struct ip_moptions *imo = inp->inp_moptions; | |
1066 | ||
1067 | if (imo == NULL) { | |
1068 | /* | |
1069 | * No multicast option buffer attached to the pcb; | |
1070 | * allocate one. | |
1071 | */ | |
6d2010ae | 1072 | imo = ip_allocmoptions(M_WAITOK); |
1c79356b | 1073 | if (imo == NULL) { |
91447636 | 1074 | error2 = ENOBUFS; |
1c79356b A |
1075 | break; |
1076 | } | |
1c79356b A |
1077 | inp->inp_moptions = imo; |
1078 | } | |
6d2010ae A |
1079 | |
1080 | error2 = imo_clone(cloned_imo, imo); | |
1c79356b A |
1081 | } |
1082 | } | |
1c79356b A |
1083 | break; |
1084 | } | |
9bccf70c | 1085 | #endif /* __APPLE__ */ |
1c79356b A |
1086 | |
1087 | default: | |
2d21ac55 | 1088 | error = EOPNOTSUPP; |
1c79356b | 1089 | } |
2d21ac55 A |
1090 | done: |
1091 | if (ia != NULL) { | |
6d2010ae | 1092 | IFA_REMREF(&ia->ia_ifa); |
2d21ac55 A |
1093 | } |
1094 | return (error); | |
1c79356b A |
1095 | } |
1096 | ||
1097 | /* | |
1098 | * SIOC[GAD]LIFADDR. | |
9bccf70c | 1099 | * SIOCGLIFADDR: get first address. (?!?) |
1c79356b A |
1100 | * SIOCGLIFADDR with IFLR_PREFIX: |
1101 | * get first address that matches the specified prefix. | |
1102 | * SIOCALIFADDR: add the specified address. | |
1103 | * SIOCALIFADDR with IFLR_PREFIX: | |
1104 | * EINVAL since we can't deduce hostid part of the address. | |
1105 | * SIOCDLIFADDR: delete the specified address. | |
1106 | * SIOCDLIFADDR with IFLR_PREFIX: | |
1107 | * delete the first address that matches the specified prefix. | |
1108 | * return values: | |
1109 | * EINVAL on invalid parameters | |
1110 | * EADDRNOTAVAIL on prefix match failed/specified address not found | |
1111 | * other values may be returned from in_ioctl() | |
1112 | */ | |
1113 | static int | |
91447636 A |
1114 | in_lifaddr_ioctl( |
1115 | struct socket *so, | |
1116 | u_long cmd, | |
1117 | caddr_t data, | |
1118 | struct ifnet *ifp, | |
1119 | struct proc *p) | |
1c79356b A |
1120 | { |
1121 | struct if_laddrreq *iflr = (struct if_laddrreq *)data; | |
1122 | struct ifaddr *ifa; | |
1123 | ||
1124 | /* sanity checks */ | |
1125 | if (!data || !ifp) { | |
1126 | panic("invalid argument to in_lifaddr_ioctl"); | |
6d2010ae | 1127 | /*NOTREACHED*/ |
1c79356b A |
1128 | } |
1129 | ||
1130 | switch (cmd) { | |
1131 | case SIOCGLIFADDR: | |
1132 | /* address must be specified on GET with IFLR_PREFIX */ | |
1133 | if ((iflr->flags & IFLR_PREFIX) == 0) | |
1134 | break; | |
1135 | /*FALLTHROUGH*/ | |
1136 | case SIOCALIFADDR: | |
1137 | case SIOCDLIFADDR: | |
1138 | /* address must be specified on ADD and DELETE */ | |
1139 | if (iflr->addr.ss_family != AF_INET) | |
1140 | return EINVAL; | |
1141 | if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) | |
1142 | return EINVAL; | |
1143 | /* XXX need improvement */ | |
1144 | if (iflr->dstaddr.ss_family | |
1145 | && iflr->dstaddr.ss_family != AF_INET) | |
1146 | return EINVAL; | |
1147 | if (iflr->dstaddr.ss_family | |
1148 | && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) | |
1149 | return EINVAL; | |
1150 | break; | |
1151 | default: /*shouldn't happen*/ | |
1c79356b | 1152 | return EOPNOTSUPP; |
1c79356b A |
1153 | } |
1154 | if (sizeof(struct in_addr) * 8 < iflr->prefixlen) | |
1155 | return EINVAL; | |
1156 | ||
1157 | switch (cmd) { | |
1158 | case SIOCALIFADDR: | |
1159 | { | |
1160 | struct in_aliasreq ifra; | |
1161 | ||
1162 | if (iflr->flags & IFLR_PREFIX) | |
1163 | return EINVAL; | |
1164 | ||
1165 | /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ | |
1166 | bzero(&ifra, sizeof(ifra)); | |
1167 | bcopy(iflr->iflr_name, ifra.ifra_name, | |
1168 | sizeof(ifra.ifra_name)); | |
1169 | ||
1170 | bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); | |
1171 | ||
1172 | if (iflr->dstaddr.ss_family) { /*XXX*/ | |
1173 | bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, | |
1174 | iflr->dstaddr.ss_len); | |
1175 | } | |
1176 | ||
1177 | ifra.ifra_mask.sin_family = AF_INET; | |
1178 | ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); | |
1179 | in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); | |
1180 | ||
1181 | return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, p); | |
1182 | } | |
1183 | case SIOCGLIFADDR: | |
1184 | case SIOCDLIFADDR: | |
1185 | { | |
1186 | struct in_ifaddr *ia; | |
2d21ac55 A |
1187 | struct in_addr mask, candidate; |
1188 | struct in_addr match = { 0 }; | |
1c79356b A |
1189 | struct sockaddr_in *sin; |
1190 | int cmp; | |
1191 | ||
1192 | bzero(&mask, sizeof(mask)); | |
1193 | if (iflr->flags & IFLR_PREFIX) { | |
1194 | /* lookup a prefix rather than address. */ | |
1195 | in_len2mask(&mask, iflr->prefixlen); | |
1196 | ||
1197 | sin = (struct sockaddr_in *)&iflr->addr; | |
1198 | match.s_addr = sin->sin_addr.s_addr; | |
1199 | match.s_addr &= mask.s_addr; | |
1200 | ||
1201 | /* if you set extra bits, that's wrong */ | |
1202 | if (match.s_addr != sin->sin_addr.s_addr) | |
1203 | return EINVAL; | |
1204 | ||
1205 | cmp = 1; | |
1206 | } else { | |
1207 | if (cmd == SIOCGLIFADDR) { | |
1208 | /* on getting an address, take the 1st match */ | |
1209 | cmp = 0; /*XXX*/ | |
1210 | } else { | |
1211 | /* on deleting an address, do exact match */ | |
1212 | in_len2mask(&mask, 32); | |
1213 | sin = (struct sockaddr_in *)&iflr->addr; | |
1214 | match.s_addr = sin->sin_addr.s_addr; | |
1215 | ||
1216 | cmp = 1; | |
1217 | } | |
1218 | } | |
1219 | ||
91447636 | 1220 | ifnet_lock_shared(ifp); |
1c79356b | 1221 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { |
6d2010ae A |
1222 | IFA_LOCK(ifa); |
1223 | if (ifa->ifa_addr->sa_family != AF_INET6) { | |
1224 | IFA_UNLOCK(ifa); | |
1c79356b | 1225 | continue; |
6d2010ae A |
1226 | } |
1227 | if (!cmp) { | |
1228 | IFA_UNLOCK(ifa); | |
1c79356b | 1229 | break; |
6d2010ae | 1230 | } |
1c79356b A |
1231 | candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; |
1232 | candidate.s_addr &= mask.s_addr; | |
6d2010ae | 1233 | IFA_UNLOCK(ifa); |
1c79356b A |
1234 | if (candidate.s_addr == match.s_addr) |
1235 | break; | |
1236 | } | |
6d2010ae A |
1237 | if (ifa != NULL) |
1238 | IFA_ADDREF(ifa); | |
91447636 | 1239 | ifnet_lock_done(ifp); |
1c79356b A |
1240 | if (!ifa) |
1241 | return EADDRNOTAVAIL; | |
1242 | ia = (struct in_ifaddr *)ifa; | |
1243 | ||
1244 | if (cmd == SIOCGLIFADDR) { | |
6d2010ae | 1245 | IFA_LOCK(ifa); |
1c79356b A |
1246 | /* fill in the if_laddrreq structure */ |
1247 | bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); | |
1248 | ||
1249 | if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { | |
1250 | bcopy(&ia->ia_dstaddr, &iflr->dstaddr, | |
1251 | ia->ia_dstaddr.sin_len); | |
1252 | } else | |
1253 | bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); | |
1254 | ||
1255 | iflr->prefixlen = | |
1256 | in_mask2len(&ia->ia_sockmask.sin_addr); | |
1257 | ||
1258 | iflr->flags = 0; /*XXX*/ | |
1259 | ||
6d2010ae A |
1260 | IFA_UNLOCK(ifa); |
1261 | IFA_REMREF(ifa); | |
1c79356b A |
1262 | return 0; |
1263 | } else { | |
1264 | struct in_aliasreq ifra; | |
1265 | ||
1266 | /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ | |
1267 | bzero(&ifra, sizeof(ifra)); | |
1268 | bcopy(iflr->iflr_name, ifra.ifra_name, | |
1269 | sizeof(ifra.ifra_name)); | |
1270 | ||
6d2010ae | 1271 | IFA_LOCK(ifa); |
1c79356b A |
1272 | bcopy(&ia->ia_addr, &ifra.ifra_addr, |
1273 | ia->ia_addr.sin_len); | |
1274 | if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { | |
1275 | bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, | |
1276 | ia->ia_dstaddr.sin_len); | |
1277 | } | |
1278 | bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, | |
1279 | ia->ia_sockmask.sin_len); | |
6d2010ae A |
1280 | IFA_UNLOCK(ifa); |
1281 | IFA_REMREF(ifa); | |
1c79356b A |
1282 | return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, |
1283 | ifp, p); | |
1284 | } | |
1285 | } | |
1286 | } | |
1287 | ||
1288 | return EOPNOTSUPP; /*just for safety*/ | |
1289 | } | |
1290 | ||
1291 | /* | |
1292 | * Delete any existing route for an interface. | |
1293 | */ | |
1294 | void | |
6d2010ae | 1295 | in_ifscrub(struct ifnet *ifp, struct in_ifaddr *ia, int locked) |
1c79356b | 1296 | { |
6d2010ae A |
1297 | IFA_LOCK(&ia->ia_ifa); |
1298 | if ((ia->ia_flags & IFA_ROUTE) == 0) { | |
1299 | IFA_UNLOCK(&ia->ia_ifa); | |
1c79356b | 1300 | return; |
6d2010ae A |
1301 | } |
1302 | IFA_UNLOCK(&ia->ia_ifa); | |
91447636 | 1303 | if (!locked) |
b0d623f7 | 1304 | lck_mtx_lock(rnh_lock); |
1c79356b | 1305 | if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) |
91447636 | 1306 | rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); |
1c79356b | 1307 | else |
91447636 | 1308 | rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, 0); |
6d2010ae | 1309 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 1310 | ia->ia_flags &= ~IFA_ROUTE; |
6d2010ae | 1311 | IFA_UNLOCK(&ia->ia_ifa); |
91447636 | 1312 | if (!locked) |
b0d623f7 A |
1313 | lck_mtx_unlock(rnh_lock); |
1314 | } | |
1315 | ||
1316 | /* | |
1317 | * Caller must hold in_ifaddr_rwlock as writer. | |
1318 | */ | |
1319 | static void | |
1320 | in_iahash_remove(struct in_ifaddr *ia) | |
1321 | { | |
6d2010ae A |
1322 | lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); |
1323 | IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); | |
b0d623f7 | 1324 | |
6d2010ae A |
1325 | if (!IA_IS_HASHED(ia)) { |
1326 | panic("attempt to remove wrong ia %p from hash table\n", ia); | |
1327 | /* NOTREACHED */ | |
1328 | } | |
b0d623f7 A |
1329 | TAILQ_REMOVE(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); |
1330 | IA_HASH_INIT(ia); | |
6d2010ae A |
1331 | if (IFA_REMREF_LOCKED(&ia->ia_ifa) == NULL) { |
1332 | panic("%s: unexpected (missing) refcnt ifa=%p", __func__, | |
1333 | &ia->ia_ifa); | |
1334 | /* NOTREACHED */ | |
1335 | } | |
b0d623f7 A |
1336 | } |
1337 | ||
1338 | /* | |
1339 | * Caller must hold in_ifaddr_rwlock as writer. | |
1340 | */ | |
1341 | static void | |
1342 | in_iahash_insert(struct in_ifaddr *ia) | |
1343 | { | |
6d2010ae A |
1344 | lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); |
1345 | IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); | |
1346 | ||
1347 | if (ia->ia_addr.sin_family != AF_INET) { | |
b0d623f7 | 1348 | panic("attempt to insert wrong ia %p into hash table\n", ia); |
6d2010ae A |
1349 | /* NOTREACHED */ |
1350 | } else if (IA_IS_HASHED(ia)) { | |
b0d623f7 | 1351 | panic("attempt to double-insert ia %p into hash table\n", ia); |
6d2010ae A |
1352 | /* NOTREACHED */ |
1353 | } | |
b0d623f7 | 1354 | TAILQ_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); |
6d2010ae | 1355 | IFA_ADDREF_LOCKED(&ia->ia_ifa); |
b0d623f7 A |
1356 | } |
1357 | ||
1358 | /* | |
1359 | * Some point to point interfaces that are tunnels | |
1360 | * borrow the address from an underlying interface (e.g. | |
1361 | * VPN server). In order for source address selection logic to | |
1362 | * find the underlying interface first, we add the address | |
1363 | * of borrowing point to point interfaces at the end of the list. | |
1364 | * (see rdar://6733789) | |
1365 | * | |
1366 | * Caller must hold in_ifaddr_rwlock as writer. | |
1367 | */ | |
1368 | static void | |
1369 | in_iahash_insert_ptp(struct in_ifaddr *ia) | |
1370 | { | |
1371 | struct in_ifaddr *tmp_ifa; | |
1372 | struct ifnet *tmp_ifp; | |
1373 | ||
6d2010ae A |
1374 | lck_rw_assert(in_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); |
1375 | IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); | |
1376 | ||
1377 | if (ia->ia_addr.sin_family != AF_INET) { | |
b0d623f7 | 1378 | panic("attempt to insert wrong ia %p into hash table\n", ia); |
6d2010ae A |
1379 | /* NOTREACHED */ |
1380 | } else if (IA_IS_HASHED(ia)) { | |
b0d623f7 | 1381 | panic("attempt to double-insert ia %p into hash table\n", ia); |
6d2010ae A |
1382 | /* NOTREACHED */ |
1383 | } | |
1384 | IFA_UNLOCK(&ia->ia_ifa); | |
1385 | TAILQ_FOREACH(tmp_ifa, INADDR_HASH(ia->ia_addr.sin_addr.s_addr), | |
1386 | ia_hash) { | |
1387 | IFA_LOCK(&tmp_ifa->ia_ifa); | |
1388 | /* ia->ia_addr won't change, so check without lock */ | |
1389 | if (IA_SIN(tmp_ifa)->sin_addr.s_addr == | |
1390 | ia->ia_addr.sin_addr.s_addr) { | |
1391 | IFA_UNLOCK(&tmp_ifa->ia_ifa); | |
b0d623f7 | 1392 | break; |
6d2010ae A |
1393 | } |
1394 | IFA_UNLOCK(&tmp_ifa->ia_ifa); | |
1395 | } | |
b0d623f7 A |
1396 | tmp_ifp = (tmp_ifa == NULL) ? NULL : tmp_ifa->ia_ifp; |
1397 | ||
6d2010ae A |
1398 | IFA_LOCK(&ia->ia_ifa); |
1399 | if (tmp_ifp == NULL) { | |
1400 | TAILQ_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), | |
1401 | ia, ia_hash); | |
1402 | } else { | |
1403 | TAILQ_INSERT_TAIL(INADDR_HASH(ia->ia_addr.sin_addr.s_addr), | |
1404 | ia, ia_hash); | |
1405 | } | |
1406 | IFA_ADDREF_LOCKED(&ia->ia_ifa); | |
1c79356b A |
1407 | } |
1408 | ||
1409 | /* | |
1410 | * Initialize an interface's internet address | |
1411 | * and routing table entry. | |
1412 | */ | |
1413 | static int | |
91447636 A |
1414 | in_ifinit( |
1415 | struct ifnet *ifp, | |
1416 | struct in_ifaddr *ia, | |
1417 | struct sockaddr_in *sin, | |
1418 | int scrub) | |
1c79356b | 1419 | { |
b0d623f7 | 1420 | u_int32_t i = ntohl(sin->sin_addr.s_addr); |
1c79356b | 1421 | struct sockaddr_in oldaddr; |
91447636 | 1422 | int flags = RTF_UP, error; |
2d21ac55 A |
1423 | struct ifaddr *ifa0; |
1424 | unsigned int cmd; | |
b0d623f7 A |
1425 | int oldremoved = 0; |
1426 | ||
1427 | /* Take an extra reference for this routine */ | |
6d2010ae | 1428 | IFA_ADDREF(&ia->ia_ifa); |
1c79356b | 1429 | |
b0d623f7 | 1430 | lck_rw_lock_exclusive(in_ifaddr_rwlock); |
6d2010ae | 1431 | IFA_LOCK(&ia->ia_ifa); |
1c79356b | 1432 | oldaddr = ia->ia_addr; |
b0d623f7 A |
1433 | if (IA_IS_HASHED(ia)) { |
1434 | oldremoved = 1; | |
1435 | in_iahash_remove(ia); | |
1436 | } | |
1c79356b | 1437 | ia->ia_addr = *sin; |
b0d623f7 A |
1438 | ia->ia_addr.sin_len = sizeof (*sin); |
1439 | if ((ifp->if_flags & IFF_POINTOPOINT)) | |
1440 | in_iahash_insert_ptp(ia); | |
6d2010ae | 1441 | else |
b0d623f7 | 1442 | in_iahash_insert(ia); |
6d2010ae | 1443 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 | 1444 | lck_rw_done(in_ifaddr_rwlock); |
1c79356b | 1445 | |
9bccf70c | 1446 | /* |
2d21ac55 A |
1447 | * Give the interface a chance to initialize if this is its first |
1448 | * address, and to validate the address if necessary. Send down | |
1449 | * SIOCSIFADDR for first address, and SIOCAIFADDR for alias(es). | |
1450 | * We find the first IPV4 address assigned to it and check if this | |
1451 | * is the same as the one passed into this routine. | |
9bccf70c | 1452 | */ |
2d21ac55 A |
1453 | ifa0 = ifa_ifpgetprimary(ifp, AF_INET); |
1454 | cmd = (&ia->ia_ifa == ifa0) ? SIOCSIFADDR : SIOCAIFADDR; | |
1455 | error = ifnet_ioctl(ifp, PF_INET, cmd, ia); | |
1c79356b | 1456 | if (error == EOPNOTSUPP) |
2d21ac55 A |
1457 | error = 0; |
1458 | /* | |
1459 | * If we've just sent down SIOCAIFADDR, send another ioctl down | |
1460 | * for SIOCSIFADDR for the first IPV4 address of the interface, | |
1461 | * because an address change on one of the addresses will result | |
1462 | * in the removal of the previous first IPV4 address. KDP needs | |
1463 | * be reconfigured with the current primary IPV4 address. | |
1464 | */ | |
1465 | if (error == 0 && cmd == SIOCAIFADDR) { | |
1466 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa0); | |
1467 | if (error == EOPNOTSUPP) | |
1468 | error = 0; | |
1469 | } | |
1470 | ||
1471 | /* Release reference from ifa_ifpgetprimary() */ | |
6d2010ae | 1472 | IFA_REMREF(ifa0); |
2d21ac55 | 1473 | |
1c79356b | 1474 | if (error) { |
b0d623f7 | 1475 | lck_rw_lock_exclusive(in_ifaddr_rwlock); |
6d2010ae | 1476 | IFA_LOCK(&ia->ia_ifa); |
b0d623f7 A |
1477 | if (IA_IS_HASHED(ia)) |
1478 | in_iahash_remove(ia); | |
1c79356b | 1479 | ia->ia_addr = oldaddr; |
b0d623f7 A |
1480 | if (oldremoved) { |
1481 | if ((ifp->if_flags & IFF_POINTOPOINT)) | |
1482 | in_iahash_insert_ptp(ia); | |
1483 | else | |
1484 | in_iahash_insert(ia); | |
1485 | } | |
6d2010ae | 1486 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 A |
1487 | lck_rw_done(in_ifaddr_rwlock); |
1488 | /* Release extra reference taken above */ | |
6d2010ae | 1489 | IFA_REMREF(&ia->ia_ifa); |
1c79356b A |
1490 | return (error); |
1491 | } | |
b0d623f7 | 1492 | lck_mtx_lock(rnh_lock); |
6d2010ae A |
1493 | IFA_LOCK(&ia->ia_ifa); |
1494 | /* | |
1495 | * Address has been initialized by the link resolver (ARP) | |
1496 | * via ifnet_ioctl() above; it may now generate route(s). | |
1497 | */ | |
1498 | ia->ia_ifa.ifa_debug &= ~IFD_NOTREADY; | |
1c79356b A |
1499 | if (scrub) { |
1500 | ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; | |
6d2010ae | 1501 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 | 1502 | in_ifscrub(ifp, ia, 1); |
6d2010ae | 1503 | IFA_LOCK(&ia->ia_ifa); |
1c79356b A |
1504 | ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; |
1505 | } | |
6d2010ae | 1506 | IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); |
1c79356b A |
1507 | if (IN_CLASSA(i)) |
1508 | ia->ia_netmask = IN_CLASSA_NET; | |
1509 | else if (IN_CLASSB(i)) | |
1510 | ia->ia_netmask = IN_CLASSB_NET; | |
1511 | else | |
1512 | ia->ia_netmask = IN_CLASSC_NET; | |
1513 | /* | |
1514 | * The subnet mask usually includes at least the standard network part, | |
1515 | * but may may be smaller in the case of supernetting. | |
1516 | * If it is set, we believe it. | |
1517 | */ | |
1518 | if (ia->ia_subnetmask == 0) { | |
1519 | ia->ia_subnetmask = ia->ia_netmask; | |
1520 | ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); | |
1521 | } else | |
1522 | ia->ia_netmask &= ia->ia_subnetmask; | |
1523 | ia->ia_net = i & ia->ia_netmask; | |
1524 | ia->ia_subnet = i & ia->ia_subnetmask; | |
1525 | in_socktrim(&ia->ia_sockmask); | |
1526 | /* | |
1527 | * Add route for the network. | |
1528 | */ | |
1529 | ia->ia_ifa.ifa_metric = ifp->if_metric; | |
1530 | if (ifp->if_flags & IFF_BROADCAST) { | |
1531 | ia->ia_broadaddr.sin_addr.s_addr = | |
1532 | htonl(ia->ia_subnet | ~ia->ia_subnetmask); | |
1533 | ia->ia_netbroadcast.s_addr = | |
1534 | htonl(ia->ia_net | ~ ia->ia_netmask); | |
1535 | } else if (ifp->if_flags & IFF_LOOPBACK) { | |
1536 | ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; | |
1537 | flags |= RTF_HOST; | |
1538 | } else if (ifp->if_flags & IFF_POINTOPOINT) { | |
b0d623f7 | 1539 | if (ia->ia_dstaddr.sin_family != AF_INET) { |
6d2010ae | 1540 | IFA_UNLOCK(&ia->ia_ifa); |
b0d623f7 A |
1541 | lck_mtx_unlock(rnh_lock); |
1542 | /* Release extra reference taken above */ | |
6d2010ae | 1543 | IFA_REMREF(&ia->ia_ifa); |
1c79356b | 1544 | return (0); |
b0d623f7 A |
1545 | } |
1546 | ia->ia_dstaddr.sin_len = sizeof (*sin); | |
1c79356b A |
1547 | flags |= RTF_HOST; |
1548 | } | |
6d2010ae A |
1549 | IFA_UNLOCK(&ia->ia_ifa); |
1550 | if ((error = rtinit_locked(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) { | |
1551 | IFA_LOCK(&ia->ia_ifa); | |
1c79356b | 1552 | ia->ia_flags |= IFA_ROUTE; |
6d2010ae A |
1553 | IFA_UNLOCK(&ia->ia_ifa); |
1554 | } | |
b0d623f7 A |
1555 | lck_mtx_unlock(rnh_lock); |
1556 | ||
9bccf70c A |
1557 | /* XXX check if the subnet route points to the same interface */ |
1558 | if (error == EEXIST) | |
1559 | error = 0; | |
1c79356b A |
1560 | |
1561 | /* | |
1562 | * If the interface supports multicast, join the "all hosts" | |
1563 | * multicast group on that interface. | |
1564 | */ | |
1565 | if (ifp->if_flags & IFF_MULTICAST) { | |
1566 | struct in_addr addr; | |
1567 | ||
6d2010ae | 1568 | lck_mtx_lock(&ifp->if_addrconfig_lock); |
1c79356b | 1569 | addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); |
6d2010ae A |
1570 | if (ifp->if_allhostsinm == NULL) { |
1571 | struct in_multi *inm; | |
1572 | inm = in_addmulti(&addr, ifp); | |
1573 | ||
1574 | if (inm != NULL) { | |
1575 | /* keep the reference on inm added by | |
1576 | * in_addmulti above for storing the | |
1577 | * pointer in allhostsinm | |
1578 | */ | |
1579 | ifp->if_allhostsinm = inm; | |
1580 | } else { | |
1581 | printf("Failed to add membership to all-hosts multicast address on interface %s%d\n", ifp->if_name, ifp->if_unit); | |
1582 | } | |
1583 | } | |
1584 | lck_mtx_unlock(&ifp->if_addrconfig_lock); | |
1c79356b | 1585 | } |
b0d623f7 A |
1586 | |
1587 | /* Release extra reference taken above */ | |
6d2010ae | 1588 | IFA_REMREF(&ia->ia_ifa); |
1c79356b A |
1589 | return (error); |
1590 | } | |
1591 | ||
1592 | ||
1593 | /* | |
1594 | * Return 1 if the address might be a local broadcast address. | |
1595 | */ | |
1596 | int | |
6d2010ae | 1597 | in_broadcast(struct in_addr in, struct ifnet *ifp) |
1c79356b | 1598 | { |
91447636 | 1599 | struct ifaddr *ifa; |
b0d623f7 | 1600 | u_int32_t t; |
1c79356b | 1601 | |
6d2010ae A |
1602 | if (in.s_addr == INADDR_BROADCAST || in.s_addr == INADDR_ANY) |
1603 | return (1); | |
1c79356b | 1604 | if ((ifp->if_flags & IFF_BROADCAST) == 0) |
6d2010ae | 1605 | return (0); |
1c79356b A |
1606 | t = ntohl(in.s_addr); |
1607 | /* | |
1608 | * Look through the list of addresses for a match | |
1609 | * with a broadcast address. | |
1610 | */ | |
1611 | #define ia ((struct in_ifaddr *)ifa) | |
91447636 A |
1612 | ifnet_lock_shared(ifp); |
1613 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |
6d2010ae | 1614 | IFA_LOCK(ifa); |
1c79356b A |
1615 | if (ifa->ifa_addr->sa_family == AF_INET && |
1616 | (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || | |
1617 | in.s_addr == ia->ia_netbroadcast.s_addr || | |
1618 | /* | |
1619 | * Check for old-style (host 0) broadcast. | |
1620 | */ | |
1621 | t == ia->ia_subnet || t == ia->ia_net) && | |
1622 | /* | |
1623 | * Check for an all one subnetmask. These | |
1624 | * only exist when an interface gets a secondary | |
1625 | * address. | |
1626 | */ | |
b0d623f7 | 1627 | ia->ia_subnetmask != (u_int32_t)0xffffffff) { |
6d2010ae | 1628 | IFA_UNLOCK(ifa); |
91447636 | 1629 | ifnet_lock_done(ifp); |
6d2010ae | 1630 | return (1); |
91447636 | 1631 | } |
6d2010ae | 1632 | IFA_UNLOCK(ifa); |
0b4e3aa0 | 1633 | } |
91447636 | 1634 | ifnet_lock_done(ifp); |
1c79356b A |
1635 | return (0); |
1636 | #undef ia | |
1637 | } | |
91447636 | 1638 | |
6d2010ae A |
1639 | void |
1640 | in_purgeaddrs(struct ifnet *ifp) | |
1c79356b | 1641 | { |
6d2010ae A |
1642 | struct ifaddr **ifap; |
1643 | int err, i; | |
1c79356b A |
1644 | |
1645 | /* | |
6d2010ae A |
1646 | * Be nice, and try the civilized way first. If we can't get |
1647 | * rid of them this way, then do it the rough way. We must | |
1648 | * only get here during detach time, after the ifnet has been | |
1649 | * removed from the global list and arrays. | |
1c79356b | 1650 | */ |
6d2010ae A |
1651 | err = ifnet_get_address_list_family_internal(ifp, &ifap, AF_INET, 1, |
1652 | M_WAITOK); | |
1653 | if (err == 0 && ifap != NULL) { | |
1654 | for (i = 0; ifap[i] != NULL; i++) { | |
1655 | struct ifaliasreq ifr; | |
1656 | struct ifaddr *ifa; | |
1657 | ||
1658 | ifa = ifap[i]; | |
1659 | bzero(&ifr, sizeof (ifr)); | |
1660 | IFA_LOCK(ifa); | |
1661 | ifr.ifra_addr = *ifa->ifa_addr; | |
1662 | if (ifa->ifa_dstaddr != NULL) | |
1663 | ifr.ifra_broadaddr = *ifa->ifa_dstaddr; | |
1664 | IFA_UNLOCK(ifa); | |
1665 | err = in_control(NULL, SIOCDIFADDR, (caddr_t)&ifr, ifp, | |
1666 | kernproc); | |
1667 | /* if we lost the race, ignore it */ | |
1668 | if (err == EADDRNOTAVAIL) | |
1669 | err = 0; | |
1670 | if (err != 0) { | |
1671 | char s_addr[MAX_IPv4_STR_LEN]; | |
1672 | char s_dstaddr[MAX_IPv4_STR_LEN]; | |
1673 | struct in_addr *s, *d; | |
1674 | ||
1675 | IFA_LOCK(ifa); | |
1676 | s = &((struct sockaddr_in *) | |
1677 | ifa->ifa_addr)->sin_addr; | |
1678 | d = &((struct sockaddr_in *) | |
1679 | ifa->ifa_dstaddr)->sin_addr; | |
1680 | (void) inet_ntop(AF_INET, &s->s_addr, s_addr, | |
1681 | sizeof (s_addr)); | |
1682 | (void) inet_ntop(AF_INET, &d->s_addr, s_dstaddr, | |
1683 | sizeof (s_dstaddr)); | |
1684 | IFA_UNLOCK(ifa); | |
1685 | ||
1686 | printf("%s: SIOCDIFADDR ifp=%p ifa_addr=%s " | |
1687 | "ifa_dstaddr=%s (err=%d)\n", __func__, ifp, | |
1688 | s_addr, s_dstaddr, err); | |
1689 | } | |
1690 | } | |
1691 | ifnet_free_address_list(ifap); | |
1692 | } else if (err != 0 && err != ENXIO) { | |
1693 | printf("%s: error retrieving list of AF_INET addresses for " | |
1694 | "ifp=%p (err=%d)\n", __func__, ifp, err); | |
1c79356b | 1695 | } |
1c79356b | 1696 | } |
91447636 | 1697 | |
2d21ac55 | 1698 | int inet_aton(char *cp, struct in_addr *pin); |
91447636 A |
1699 | int |
1700 | inet_aton(char * cp, struct in_addr * pin) | |
1701 | { | |
2d21ac55 | 1702 | u_char * b = (unsigned char *)pin; |
91447636 A |
1703 | int i; |
1704 | char * p; | |
1705 | ||
1706 | for (p = cp, i = 0; i < 4; i++) { | |
b0d623f7 | 1707 | u_int32_t l = strtoul(p, 0, 0); |
91447636 A |
1708 | if (l > 255) |
1709 | return (FALSE); | |
1710 | b[i] = l; | |
1711 | p = strchr(p, '.'); | |
1712 | if (i < 3 && p == NULL) | |
1713 | return (FALSE); | |
1714 | p++; | |
1715 | } | |
1716 | return (TRUE); | |
1717 | } | |
6d2010ae A |
1718 | |
1719 | int inet_ntoa2(struct in_addr * pin, char * cp, const int len); | |
1720 | int inet_ntoa2(struct in_addr * pin, char * cp, const int len) | |
1721 | { | |
1722 | int ret; | |
1723 | ||
1724 | /* address is in network byte order */ | |
1725 | ret = snprintf(cp, len, "%u.%u.%u.%u", pin->s_addr & 0xFF, | |
1726 | (pin->s_addr >> 8) & 0xFF, (pin->s_addr >> 16) & 0xFF, | |
1727 | (pin->s_addr >> 24) & 0xFF); | |
1728 | ||
1729 | return ret < len ? TRUE : FALSE; | |
1730 | } | |
b0d623f7 A |
1731 | |
1732 | /* | |
1733 | * Called as part of ip_init | |
1734 | */ | |
1735 | void | |
1736 | in_ifaddr_init(void) | |
1737 | { | |
6d2010ae A |
1738 | in_multi_init(); |
1739 | ||
b0d623f7 A |
1740 | PE_parse_boot_argn("ifa_debug", &inifa_debug, sizeof (inifa_debug)); |
1741 | ||
1742 | inifa_size = (inifa_debug == 0) ? sizeof (struct in_ifaddr) : | |
1743 | sizeof (struct in_ifaddr_dbg); | |
1744 | ||
1745 | inifa_zone = zinit(inifa_size, INIFA_ZONE_MAX * inifa_size, | |
1746 | 0, INIFA_ZONE_NAME); | |
6d2010ae | 1747 | if (inifa_zone == NULL) { |
b0d623f7 | 1748 | panic("%s: failed allocating %s", __func__, INIFA_ZONE_NAME); |
6d2010ae A |
1749 | /* NOTREACHED */ |
1750 | } | |
b0d623f7 | 1751 | zone_change(inifa_zone, Z_EXPAND, TRUE); |
6d2010ae A |
1752 | zone_change(inifa_zone, Z_CALLERACCT, FALSE); |
1753 | ||
1754 | lck_mtx_init(&inifa_trash_lock, ifa_mtx_grp, ifa_mtx_attr); | |
1755 | TAILQ_INIT(&inifa_trash_head); | |
b0d623f7 A |
1756 | } |
1757 | ||
1758 | static struct in_ifaddr * | |
1759 | in_ifaddr_alloc(int how) | |
1760 | { | |
1761 | struct in_ifaddr *inifa; | |
1762 | ||
1763 | inifa = (how == M_WAITOK) ? zalloc(inifa_zone) : | |
1764 | zalloc_noblock(inifa_zone); | |
1765 | if (inifa != NULL) { | |
1766 | bzero(inifa, inifa_size); | |
1767 | inifa->ia_ifa.ifa_free = in_ifaddr_free; | |
1768 | inifa->ia_ifa.ifa_debug |= IFD_ALLOC; | |
6d2010ae | 1769 | ifa_lock_init(&inifa->ia_ifa); |
b0d623f7 A |
1770 | if (inifa_debug != 0) { |
1771 | struct in_ifaddr_dbg *inifa_dbg = | |
1772 | (struct in_ifaddr_dbg *)inifa; | |
1773 | inifa->ia_ifa.ifa_debug |= IFD_DEBUG; | |
1774 | inifa->ia_ifa.ifa_trace = in_ifaddr_trace; | |
6d2010ae A |
1775 | inifa->ia_ifa.ifa_attached = in_ifaddr_attached; |
1776 | inifa->ia_ifa.ifa_detached = in_ifaddr_detached; | |
b0d623f7 A |
1777 | ctrace_record(&inifa_dbg->inifa_alloc); |
1778 | } | |
1779 | } | |
1780 | return (inifa); | |
1781 | } | |
1782 | ||
1783 | static void | |
1784 | in_ifaddr_free(struct ifaddr *ifa) | |
1785 | { | |
6d2010ae A |
1786 | IFA_LOCK_ASSERT_HELD(ifa); |
1787 | ||
1788 | if (ifa->ifa_refcnt != 0) { | |
b0d623f7 | 1789 | panic("%s: ifa %p bad ref cnt", __func__, ifa); |
6d2010ae A |
1790 | /* NOTREACHED */ |
1791 | } if (!(ifa->ifa_debug & IFD_ALLOC)) { | |
b0d623f7 | 1792 | panic("%s: ifa %p cannot be freed", __func__, ifa); |
6d2010ae A |
1793 | /* NOTREACHED */ |
1794 | } | |
b0d623f7 A |
1795 | if (ifa->ifa_debug & IFD_DEBUG) { |
1796 | struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; | |
1797 | ctrace_record(&inifa_dbg->inifa_free); | |
1798 | bcopy(&inifa_dbg->inifa, &inifa_dbg->inifa_old, | |
1799 | sizeof (struct in_ifaddr)); | |
6d2010ae A |
1800 | if (ifa->ifa_debug & IFD_TRASHED) { |
1801 | /* Become a regular mutex, just in case */ | |
1802 | IFA_CONVERT_LOCK(ifa); | |
1803 | lck_mtx_lock(&inifa_trash_lock); | |
1804 | TAILQ_REMOVE(&inifa_trash_head, inifa_dbg, | |
1805 | inifa_trash_link); | |
1806 | lck_mtx_unlock(&inifa_trash_lock); | |
1807 | ifa->ifa_debug &= ~IFD_TRASHED; | |
1808 | } | |
b0d623f7 | 1809 | } |
6d2010ae A |
1810 | IFA_UNLOCK(ifa); |
1811 | ifa_lock_destroy(ifa); | |
b0d623f7 A |
1812 | bzero(ifa, sizeof (struct in_ifaddr)); |
1813 | zfree(inifa_zone, ifa); | |
1814 | } | |
1815 | ||
6d2010ae A |
1816 | static void |
1817 | in_ifaddr_attached(struct ifaddr *ifa) | |
1818 | { | |
1819 | struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; | |
1820 | ||
1821 | IFA_LOCK_ASSERT_HELD(ifa); | |
1822 | ||
1823 | if (!(ifa->ifa_debug & IFD_DEBUG)) { | |
1824 | panic("%s: ifa %p has no debug structure", __func__, ifa); | |
1825 | /* NOTREACHED */ | |
1826 | } | |
1827 | if (ifa->ifa_debug & IFD_TRASHED) { | |
1828 | /* Become a regular mutex, just in case */ | |
1829 | IFA_CONVERT_LOCK(ifa); | |
1830 | lck_mtx_lock(&inifa_trash_lock); | |
1831 | TAILQ_REMOVE(&inifa_trash_head, inifa_dbg, inifa_trash_link); | |
1832 | lck_mtx_unlock(&inifa_trash_lock); | |
1833 | ifa->ifa_debug &= ~IFD_TRASHED; | |
1834 | } | |
1835 | } | |
1836 | ||
1837 | static void | |
1838 | in_ifaddr_detached(struct ifaddr *ifa) | |
1839 | { | |
1840 | struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; | |
1841 | ||
1842 | IFA_LOCK_ASSERT_HELD(ifa); | |
1843 | ||
1844 | if (!(ifa->ifa_debug & IFD_DEBUG)) { | |
1845 | panic("%s: ifa %p has no debug structure", __func__, ifa); | |
1846 | /* NOTREACHED */ | |
1847 | } else if (ifa->ifa_debug & IFD_TRASHED) { | |
1848 | panic("%s: ifa %p is already in trash list", __func__, ifa); | |
1849 | /* NOTREACHED */ | |
1850 | } | |
1851 | ifa->ifa_debug |= IFD_TRASHED; | |
1852 | /* Become a regular mutex, just in case */ | |
1853 | IFA_CONVERT_LOCK(ifa); | |
1854 | lck_mtx_lock(&inifa_trash_lock); | |
1855 | TAILQ_INSERT_TAIL(&inifa_trash_head, inifa_dbg, inifa_trash_link); | |
1856 | lck_mtx_unlock(&inifa_trash_lock); | |
1857 | } | |
1858 | ||
b0d623f7 A |
1859 | static void |
1860 | in_ifaddr_trace(struct ifaddr *ifa, int refhold) | |
1861 | { | |
1862 | struct in_ifaddr_dbg *inifa_dbg = (struct in_ifaddr_dbg *)ifa; | |
1863 | ctrace_t *tr; | |
1864 | u_int32_t idx; | |
1865 | u_int16_t *cnt; | |
1866 | ||
6d2010ae | 1867 | if (!(ifa->ifa_debug & IFD_DEBUG)) { |
b0d623f7 | 1868 | panic("%s: ifa %p has no debug structure", __func__, ifa); |
6d2010ae A |
1869 | /* NOTREACHED */ |
1870 | } | |
b0d623f7 A |
1871 | if (refhold) { |
1872 | cnt = &inifa_dbg->inifa_refhold_cnt; | |
1873 | tr = inifa_dbg->inifa_refhold; | |
1874 | } else { | |
1875 | cnt = &inifa_dbg->inifa_refrele_cnt; | |
1876 | tr = inifa_dbg->inifa_refrele; | |
1877 | } | |
1878 | ||
6d2010ae | 1879 | idx = atomic_add_16_ov(cnt, 1) % INIFA_TRACE_HIST_SIZE; |
b0d623f7 A |
1880 | ctrace_record(&tr[idx]); |
1881 | } |