]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
c910b4d9 | 2 | * Copyright (c) 2000-2008 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> |
1c79356b | 75 | |
2d21ac55 A |
76 | #include <pexpert/pexpert.h> |
77 | ||
1c79356b | 78 | #include <net/if.h> |
1c79356b | 79 | #include <net/if_types.h> |
9bccf70c | 80 | #include <net/route.h> |
2d21ac55 | 81 | #include <net/kpi_protocol.h> |
1c79356b A |
82 | |
83 | #include <netinet/in.h> | |
84 | #include <netinet/in_var.h> | |
85 | #include <netinet/in_pcb.h> | |
86 | ||
87 | #include <netinet/igmp_var.h> | |
88 | #include <net/dlil.h> | |
89 | ||
90 | #include <netinet/ip_var.h> | |
91 | ||
92 | #include <netinet/tcp.h> | |
93 | #include <netinet/tcp_timer.h> | |
94 | #include <netinet/tcp_var.h> | |
95 | ||
96 | #include <sys/file.h> | |
97 | ||
98 | ||
91447636 A |
99 | static int in_mask2len(struct in_addr *); |
100 | static void in_len2mask(struct in_addr *, int); | |
101 | static int in_lifaddr_ioctl(struct socket *, u_long, caddr_t, | |
102 | struct ifnet *, struct proc *); | |
1c79356b | 103 | |
91447636 A |
104 | static void in_socktrim(struct sockaddr_in *); |
105 | static int in_ifinit(struct ifnet *, | |
106 | struct in_ifaddr *, struct sockaddr_in *, int); | |
1c79356b A |
107 | |
108 | static int subnetsarelocal = 0; | |
109 | SYSCTL_INT(_net_inet_ip, OID_AUTO, subnets_are_local, CTLFLAG_RW, | |
110 | &subnetsarelocal, 0, ""); | |
111 | ||
112 | struct in_multihead in_multihead; /* XXX BSS initialization */ | |
113 | ||
91447636 A |
114 | /* Track whether or not the SIOCARPIPLL ioctl has been called */ |
115 | __private_extern__ u_int32_t ipv4_ll_arp_aware = 0; | |
1c79356b | 116 | |
2d21ac55 A |
117 | int |
118 | inaddr_local(struct in_addr in) | |
119 | { | |
120 | struct rtentry *rt; | |
121 | struct sockaddr_in sin; | |
122 | int local = 0; | |
123 | ||
124 | sin.sin_family = AF_INET; | |
125 | sin.sin_len = sizeof (sin); | |
126 | sin.sin_addr = in; | |
127 | rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); | |
128 | ||
129 | if (rt != NULL) { | |
130 | if (rt->rt_gateway->sa_family == AF_LINK || | |
131 | (rt->rt_ifp->if_flags & IFF_LOOPBACK)) | |
132 | local = 1; | |
133 | rtfree(rt); | |
134 | } else { | |
135 | local = in_localaddr(in); | |
136 | } | |
137 | return (local); | |
138 | } | |
139 | ||
1c79356b A |
140 | /* |
141 | * Return 1 if an internet address is for a ``local'' host | |
142 | * (one to which we have a connection). If subnetsarelocal | |
143 | * is true, this includes other subnets of the local net. | |
144 | * Otherwise, it includes only the directly-connected (sub)nets. | |
145 | */ | |
146 | int | |
2d21ac55 | 147 | in_localaddr(struct in_addr in) |
1c79356b | 148 | { |
91447636 A |
149 | u_long i = ntohl(in.s_addr); |
150 | struct in_ifaddr *ia; | |
1c79356b A |
151 | |
152 | if (subnetsarelocal) { | |
91447636 | 153 | lck_mtx_lock(rt_mtx); |
1c79356b A |
154 | for (ia = in_ifaddrhead.tqh_first; ia; |
155 | ia = ia->ia_link.tqe_next) | |
91447636 A |
156 | if ((i & ia->ia_netmask) == ia->ia_net) { |
157 | lck_mtx_unlock(rt_mtx); | |
1c79356b | 158 | return (1); |
91447636 A |
159 | } |
160 | lck_mtx_unlock(rt_mtx); | |
1c79356b | 161 | } else { |
91447636 | 162 | lck_mtx_lock(rt_mtx); |
1c79356b A |
163 | for (ia = in_ifaddrhead.tqh_first; ia; |
164 | ia = ia->ia_link.tqe_next) | |
91447636 A |
165 | if ((i & ia->ia_subnetmask) == ia->ia_subnet) { |
166 | lck_mtx_unlock(rt_mtx); | |
1c79356b | 167 | return (1); |
91447636 A |
168 | } |
169 | lck_mtx_unlock(rt_mtx); | |
1c79356b A |
170 | } |
171 | return (0); | |
172 | } | |
173 | ||
174 | /* | |
175 | * Determine whether an IP address is in a reserved set of addresses | |
176 | * that may not be forwarded, or whether datagrams to that destination | |
177 | * may be forwarded. | |
178 | */ | |
179 | int | |
2d21ac55 | 180 | in_canforward(struct in_addr in) |
1c79356b | 181 | { |
91447636 A |
182 | u_long i = ntohl(in.s_addr); |
183 | u_long net; | |
1c79356b A |
184 | |
185 | if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i)) | |
186 | return (0); | |
187 | if (IN_CLASSA(i)) { | |
188 | net = i & IN_CLASSA_NET; | |
189 | if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT)) | |
190 | return (0); | |
191 | } | |
192 | return (1); | |
193 | } | |
194 | ||
195 | /* | |
196 | * Trim a mask in a sockaddr | |
197 | */ | |
198 | static void | |
2d21ac55 | 199 | in_socktrim(struct sockaddr_in *ap) |
1c79356b | 200 | { |
91447636 A |
201 | char *cplim = (char *) &ap->sin_addr; |
202 | char *cp = (char *) (&ap->sin_addr + 1); | |
1c79356b A |
203 | |
204 | ap->sin_len = 0; | |
205 | while (--cp >= cplim) | |
206 | if (*cp) { | |
207 | (ap)->sin_len = cp - (char *) (ap) + 1; | |
208 | break; | |
209 | } | |
210 | } | |
211 | ||
212 | static int | |
2d21ac55 | 213 | in_mask2len(struct in_addr *mask) |
1c79356b | 214 | { |
2d21ac55 | 215 | size_t x, y; |
1c79356b A |
216 | u_char *p; |
217 | ||
218 | p = (u_char *)mask; | |
219 | for (x = 0; x < sizeof(*mask); x++) { | |
220 | if (p[x] != 0xff) | |
221 | break; | |
222 | } | |
223 | y = 0; | |
224 | if (x < sizeof(*mask)) { | |
225 | for (y = 0; y < 8; y++) { | |
226 | if ((p[x] & (0x80 >> y)) == 0) | |
227 | break; | |
228 | } | |
229 | } | |
230 | return x * 8 + y; | |
231 | } | |
232 | ||
233 | static void | |
2d21ac55 | 234 | in_len2mask(struct in_addr *mask, int len) |
1c79356b A |
235 | { |
236 | int i; | |
237 | u_char *p; | |
238 | ||
239 | p = (u_char *)mask; | |
240 | bzero(mask, sizeof(*mask)); | |
241 | for (i = 0; i < len / 8; i++) | |
242 | p[i] = 0xff; | |
243 | if (len % 8) | |
244 | p[i] = (0xff00 >> (len % 8)) & 0xff; | |
245 | } | |
246 | ||
247 | static int in_interfaces; /* number of external internet interfaces */ | |
248 | ||
249 | /* | |
250 | * Generic internet control operations (ioctl's). | |
251 | * Ifp is 0 if not an interface-specific ioctl. | |
2d21ac55 A |
252 | * |
253 | * Returns: 0 Success | |
254 | * EINVAL | |
255 | * EADDRNOTAVAIL | |
256 | * EDESTADDRREQ | |
257 | * EPERM | |
258 | * ENOBUFS | |
259 | * EBUSY | |
260 | * EOPNOTSUPP | |
261 | * proc_suser:EPERM | |
262 | * suser:EPERM | |
263 | * in_lifaddr_ioctl:??? | |
264 | * dlil_ioctl:??? | |
265 | * in_ifinit:??? | |
266 | * dlil_plumb_protocol:??? | |
267 | * dlil_unplumb_protocol:??? | |
1c79356b A |
268 | */ |
269 | /* ARGSUSED */ | |
270 | int | |
91447636 A |
271 | in_control( |
272 | struct socket *so, | |
273 | u_long cmd, | |
274 | caddr_t data, | |
275 | struct ifnet *ifp, | |
276 | struct proc *p) | |
1c79356b | 277 | { |
91447636 | 278 | struct ifreq *ifr = (struct ifreq *)data; |
2d21ac55 | 279 | struct in_ifaddr *ia = NULL, *iap; |
91447636 | 280 | struct ifaddr *ifa; |
1c79356b A |
281 | struct in_aliasreq *ifra = (struct in_aliasreq *)data; |
282 | struct sockaddr_in oldaddr; | |
2d21ac55 A |
283 | int error = 0; |
284 | int hostIsNew, maskIsNew; | |
1c79356b A |
285 | struct kev_msg ev_msg; |
286 | struct kev_in_data in_event_data; | |
287 | ||
1c79356b A |
288 | |
289 | switch (cmd) { | |
290 | case SIOCALIFADDR: | |
291 | case SIOCDLIFADDR: | |
91447636 | 292 | if (p && (error = proc_suser(p)) != 0) |
1c79356b | 293 | return error; |
1c79356b A |
294 | /*fall through*/ |
295 | case SIOCGLIFADDR: | |
296 | if (!ifp) | |
297 | return EINVAL; | |
298 | return in_lifaddr_ioctl(so, cmd, data, ifp, p); | |
299 | } | |
300 | ||
301 | /* | |
302 | * Find address for this interface, if it exists. | |
303 | * | |
304 | * If an alias address was specified, find that one instead of | |
305 | * the first one on the interface. | |
306 | */ | |
91447636 A |
307 | if (ifp) { |
308 | lck_mtx_lock(rt_mtx); | |
1c79356b A |
309 | for (iap = in_ifaddrhead.tqh_first; iap; |
310 | iap = iap->ia_link.tqe_next) | |
311 | if (iap->ia_ifp == ifp) { | |
312 | if (((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr == | |
313 | iap->ia_addr.sin_addr.s_addr) { | |
314 | ia = iap; | |
315 | break; | |
316 | } else if (ia == NULL) { | |
317 | ia = iap; | |
318 | if (ifr->ifr_addr.sa_family != AF_INET) | |
319 | break; | |
320 | } | |
321 | } | |
2d21ac55 A |
322 | /* take a reference on ia before releasing mutex */ |
323 | if (ia != NULL) { | |
324 | ifaref(&ia->ia_ifa); | |
325 | } | |
91447636 A |
326 | lck_mtx_unlock(rt_mtx); |
327 | } | |
1c79356b | 328 | switch (cmd) { |
0b4e3aa0 | 329 | case SIOCAUTOADDR: |
91447636 | 330 | case SIOCARPIPLL: |
2d21ac55 A |
331 | if (p && (error = proc_suser(p)) != 0) { |
332 | goto done; | |
333 | } | |
334 | if (ifp == 0) { | |
335 | error = EADDRNOTAVAIL; | |
336 | goto done; | |
337 | } | |
0b4e3aa0 | 338 | break; |
1c79356b A |
339 | |
340 | case SIOCAIFADDR: | |
341 | case SIOCDIFADDR: | |
2d21ac55 A |
342 | if (ifp == 0) { |
343 | error = EADDRNOTAVAIL; | |
344 | goto done; | |
345 | } | |
1c79356b | 346 | if (ifra->ifra_addr.sin_family == AF_INET) { |
2d21ac55 A |
347 | struct in_ifaddr *oia; |
348 | ||
91447636 | 349 | lck_mtx_lock(rt_mtx); |
1c79356b A |
350 | for (oia = ia; ia; ia = ia->ia_link.tqe_next) { |
351 | if (ia->ia_ifp == ifp && | |
352 | ia->ia_addr.sin_addr.s_addr == | |
353 | ifra->ifra_addr.sin_addr.s_addr) | |
354 | break; | |
355 | } | |
2d21ac55 A |
356 | /* take a reference on ia before releasing mutex */ |
357 | if (ia != NULL && ia != oia) { | |
358 | ifaref(&ia->ia_ifa); | |
359 | } | |
91447636 | 360 | lck_mtx_unlock(rt_mtx); |
2d21ac55 A |
361 | if (oia != NULL && oia != ia) { |
362 | ifafree(&oia->ia_ifa); | |
363 | } | |
1c79356b A |
364 | if ((ifp->if_flags & IFF_POINTOPOINT) |
365 | && (cmd == SIOCAIFADDR) | |
366 | && (ifra->ifra_dstaddr.sin_addr.s_addr | |
367 | == INADDR_ANY)) { | |
2d21ac55 A |
368 | error = EDESTADDRREQ; |
369 | goto done; | |
1c79356b A |
370 | } |
371 | } | |
2d21ac55 A |
372 | else if (cmd == SIOCAIFADDR) { |
373 | error = EINVAL; | |
374 | goto done; | |
375 | } | |
376 | if (cmd == SIOCDIFADDR && ia == 0) { | |
377 | error = EADDRNOTAVAIL; | |
378 | goto done; | |
379 | } | |
1c79356b A |
380 | /* FALLTHROUGH */ |
381 | case SIOCSIFADDR: | |
382 | case SIOCSIFNETMASK: | |
383 | case SIOCSIFDSTADDR: | |
2d21ac55 A |
384 | if ((so->so_state & SS_PRIV) == 0) { |
385 | error = EPERM; | |
386 | goto done; | |
387 | } | |
388 | if (ifp == 0) { | |
389 | error = EADDRNOTAVAIL; | |
390 | goto done; | |
391 | } | |
392 | if (ifra->ifra_addr.sin_family != AF_INET | |
393 | && cmd == SIOCSIFADDR) { | |
394 | error = EINVAL; | |
395 | goto done; | |
396 | } | |
1c79356b A |
397 | if (ia == (struct in_ifaddr *)0) { |
398 | ia = (struct in_ifaddr *) | |
399 | _MALLOC(sizeof *ia, M_IFADDR, M_WAITOK); | |
2d21ac55 A |
400 | if (ia == (struct in_ifaddr *)NULL) { |
401 | error = ENOBUFS; | |
402 | goto done; | |
403 | } | |
1c79356b A |
404 | bzero((caddr_t)ia, sizeof *ia); |
405 | /* | |
406 | * Protect from ipintr() traversing address list | |
407 | * while we're modifying it. | |
408 | */ | |
1c79356b | 409 | |
1c79356b | 410 | ifa = &ia->ia_ifa; |
1c79356b | 411 | |
2d21ac55 A |
412 | ia->ia_addr.sin_family = AF_INET; |
413 | ia->ia_addr.sin_len = sizeof (ia->ia_addr); | |
1c79356b A |
414 | ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; |
415 | ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; | |
416 | ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; | |
417 | ia->ia_sockmask.sin_len = 8; | |
91447636 | 418 | ifnet_lock_exclusive(ifp); |
1c79356b A |
419 | if (ifp->if_flags & IFF_BROADCAST) { |
420 | ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr); | |
421 | ia->ia_broadaddr.sin_family = AF_INET; | |
422 | } | |
423 | ia->ia_ifp = ifp; | |
424 | if (!(ifp->if_flags & IFF_LOOPBACK)) | |
425 | in_interfaces++; | |
91447636 A |
426 | if_attach_ifa(ifp, ifa); |
427 | ifnet_lock_done(ifp); | |
428 | ||
429 | lck_mtx_lock(rt_mtx); | |
2d21ac55 | 430 | ifaref(&ia->ia_ifa); |
91447636 A |
431 | TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_link); |
432 | lck_mtx_unlock(rt_mtx); | |
433 | ||
434 | /* Generic protocol plumbing */ | |
435 | ||
2d21ac55 A |
436 | if ((error = proto_plumb(PF_INET, ifp))) { |
437 | if (error != EEXIST) { | |
438 | kprintf("in.c: warning can't plumb proto if=%s%d type %d error=%d\n", | |
439 | ifp->if_name, ifp->if_unit, ifp->if_type, error); | |
440 | } | |
91447636 A |
441 | error = 0; /*discard error, can be cold with unsupported interfaces */ |
442 | } | |
443 | ||
1c79356b A |
444 | } |
445 | break; | |
446 | ||
0b4e3aa0 A |
447 | case SIOCPROTOATTACH: |
448 | case SIOCPROTODETACH: | |
2d21ac55 A |
449 | if (p && (error = proc_suser(p)) != 0) { |
450 | goto done; | |
451 | } | |
452 | if (ifp == 0) { | |
453 | error = EADDRNOTAVAIL; | |
454 | goto done; | |
455 | } | |
9bccf70c | 456 | break; |
0b4e3aa0 | 457 | |
1c79356b | 458 | case SIOCSIFBRDADDR: |
9bccf70c | 459 | #ifdef __APPLE__ |
2d21ac55 A |
460 | if ((so->so_state & SS_PRIV) == 0) { |
461 | error = EPERM; | |
462 | goto done; | |
463 | } | |
9bccf70c | 464 | #else |
2d21ac55 A |
465 | if (p && (error = suser(p)) != 0) { |
466 | goto done; | |
467 | } | |
1c79356b A |
468 | #endif |
469 | /* FALLTHROUGH */ | |
470 | ||
471 | case SIOCGIFADDR: | |
472 | case SIOCGIFNETMASK: | |
473 | case SIOCGIFDSTADDR: | |
474 | case SIOCGIFBRDADDR: | |
2d21ac55 A |
475 | if (ia == (struct in_ifaddr *)0) { |
476 | error = EADDRNOTAVAIL; | |
477 | goto done; | |
478 | } | |
1c79356b A |
479 | break; |
480 | } | |
481 | switch (cmd) { | |
0b4e3aa0 | 482 | case SIOCAUTOADDR: |
91447636 A |
483 | ifnet_lock_exclusive(ifp); |
484 | if (ifr->ifr_intval) | |
0b4e3aa0 A |
485 | ifp->if_eflags |= IFEF_AUTOCONFIGURING; |
486 | else | |
487 | ifp->if_eflags &= ~IFEF_AUTOCONFIGURING; | |
91447636 A |
488 | ifnet_lock_done(ifp); |
489 | break; | |
490 | ||
491 | case SIOCARPIPLL: | |
91447636 A |
492 | ipv4_ll_arp_aware = 1; |
493 | ifnet_lock_exclusive(ifp); | |
494 | if (ifr->ifr_data) | |
495 | ifp->if_eflags |= IFEF_ARPLL; | |
496 | else | |
497 | ifp->if_eflags &= ~IFEF_ARPLL; | |
498 | ifnet_lock_done(ifp); | |
0b4e3aa0 | 499 | break; |
1c79356b A |
500 | |
501 | case SIOCGIFADDR: | |
502 | *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_addr; | |
503 | break; | |
504 | ||
505 | case SIOCGIFBRDADDR: | |
2d21ac55 A |
506 | if ((ifp->if_flags & IFF_BROADCAST) == 0) { |
507 | error = EINVAL; | |
508 | break; | |
509 | } | |
1c79356b A |
510 | *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_broadaddr; |
511 | break; | |
512 | ||
513 | case SIOCGIFDSTADDR: | |
2d21ac55 A |
514 | if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { |
515 | error = EINVAL; | |
516 | break; | |
517 | } | |
1c79356b A |
518 | *((struct sockaddr_in *)&ifr->ifr_dstaddr) = ia->ia_dstaddr; |
519 | break; | |
520 | ||
521 | case SIOCGIFNETMASK: | |
522 | *((struct sockaddr_in *)&ifr->ifr_addr) = ia->ia_sockmask; | |
523 | break; | |
524 | ||
525 | case SIOCSIFDSTADDR: | |
2d21ac55 A |
526 | if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { |
527 | error = EINVAL; | |
528 | break; | |
529 | } | |
1c79356b A |
530 | oldaddr = ia->ia_dstaddr; |
531 | ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; | |
2d21ac55 A |
532 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFDSTADDR, ia); |
533 | if (error == EOPNOTSUPP) { | |
534 | error = 0; | |
535 | } | |
1c79356b | 536 | if (error) { |
2d21ac55 A |
537 | ia->ia_dstaddr = oldaddr; |
538 | break; | |
1c79356b A |
539 | } |
540 | ||
541 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
542 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
543 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
544 | ||
545 | ev_msg.event_code = KEV_INET_SIFDSTADDR; | |
546 | ||
547 | if (ia->ia_ifa.ifa_dstaddr) | |
548 | in_event_data.ia_dstaddr = | |
549 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
550 | else | |
551 | in_event_data.ia_dstaddr.s_addr = 0; | |
552 | ||
553 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
554 | in_event_data.ia_net = ia->ia_net; | |
555 | in_event_data.ia_netmask = ia->ia_netmask; | |
556 | in_event_data.ia_subnet = ia->ia_subnet; | |
557 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
558 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
559 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
560 | in_event_data.link_data.if_family = ifp->if_family; | |
561 | in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; | |
562 | ||
563 | ev_msg.dv[0].data_ptr = &in_event_data; | |
564 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
565 | ev_msg.dv[1].data_length = 0; | |
566 | ||
567 | kev_post_msg(&ev_msg); | |
568 | ||
569 | ||
570 | if (ia->ia_flags & IFA_ROUTE) { | |
571 | ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&oldaddr; | |
572 | rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); | |
573 | ia->ia_ifa.ifa_dstaddr = | |
574 | (struct sockaddr *)&ia->ia_dstaddr; | |
575 | rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_HOST|RTF_UP); | |
576 | } | |
577 | break; | |
578 | ||
579 | case SIOCSIFBRDADDR: | |
2d21ac55 A |
580 | if ((ifp->if_flags & IFF_BROADCAST) == 0) { |
581 | error = EINVAL; | |
582 | break; | |
583 | } | |
1c79356b A |
584 | ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; |
585 | ||
586 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
587 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
588 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
589 | ||
590 | ev_msg.event_code = KEV_INET_SIFBRDADDR; | |
591 | ||
592 | if (ia->ia_ifa.ifa_dstaddr) | |
593 | in_event_data.ia_dstaddr = | |
594 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
595 | else | |
596 | in_event_data.ia_dstaddr.s_addr = 0; | |
597 | ||
598 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
599 | in_event_data.ia_net = ia->ia_net; | |
600 | in_event_data.ia_netmask = ia->ia_netmask; | |
601 | in_event_data.ia_subnet = ia->ia_subnet; | |
602 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
603 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
604 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
605 | in_event_data.link_data.if_family = ifp->if_family; | |
606 | in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; | |
607 | ||
608 | ev_msg.dv[0].data_ptr = &in_event_data; | |
609 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
610 | ev_msg.dv[1].data_length = 0; | |
611 | ||
612 | kev_post_msg(&ev_msg); | |
613 | ||
614 | break; | |
615 | ||
616 | case SIOCSIFADDR: | |
2d21ac55 A |
617 | error = in_ifinit(ifp, ia, (struct sockaddr_in *) &ifr->ifr_addr, 1); |
618 | break; | |
1c79356b | 619 | |
0b4e3aa0 | 620 | case SIOCPROTOATTACH: |
2d21ac55 A |
621 | error = proto_plumb(PF_INET, ifp); |
622 | break; | |
0b4e3aa0 A |
623 | |
624 | case SIOCPROTODETACH: | |
625 | // if an ip address is still present, refuse to detach | |
91447636 A |
626 | ifnet_lock_shared(ifp); |
627 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) | |
628 | if (ifa->ifa_addr->sa_family == AF_INET) | |
629 | break; | |
630 | ifnet_lock_done(ifp); | |
2d21ac55 A |
631 | if (ifa != 0) { |
632 | error = EBUSY; | |
633 | break; | |
634 | } | |
55e303ae | 635 | |
2d21ac55 | 636 | error = proto_unplumb(PF_INET, ifp); |
9bccf70c A |
637 | break; |
638 | ||
0b4e3aa0 | 639 | |
2d21ac55 A |
640 | case SIOCSIFNETMASK: { |
641 | u_long i; | |
642 | ||
1c79356b A |
643 | i = ifra->ifra_addr.sin_addr.s_addr; |
644 | ia->ia_subnetmask = ntohl(ia->ia_sockmask.sin_addr.s_addr = i); | |
645 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
646 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
647 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
648 | ||
649 | ev_msg.event_code = KEV_INET_SIFNETMASK; | |
650 | ||
651 | if (ia->ia_ifa.ifa_dstaddr) | |
652 | in_event_data.ia_dstaddr = | |
653 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
654 | else | |
655 | in_event_data.ia_dstaddr.s_addr = 0; | |
656 | ||
657 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
658 | in_event_data.ia_net = ia->ia_net; | |
659 | in_event_data.ia_netmask = ia->ia_netmask; | |
660 | in_event_data.ia_subnet = ia->ia_subnet; | |
661 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
662 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
663 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
664 | in_event_data.link_data.if_family = ifp->if_family; | |
665 | in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; | |
666 | ||
667 | ev_msg.dv[0].data_ptr = &in_event_data; | |
668 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
669 | ev_msg.dv[1].data_length = 0; | |
670 | ||
671 | kev_post_msg(&ev_msg); | |
672 | ||
673 | break; | |
2d21ac55 | 674 | } |
1c79356b A |
675 | case SIOCAIFADDR: |
676 | maskIsNew = 0; | |
677 | hostIsNew = 1; | |
678 | error = 0; | |
679 | if (ia->ia_addr.sin_family == AF_INET) { | |
680 | if (ifra->ifra_addr.sin_len == 0) { | |
681 | ifra->ifra_addr = ia->ia_addr; | |
682 | hostIsNew = 0; | |
683 | } else if (ifra->ifra_addr.sin_addr.s_addr == | |
684 | ia->ia_addr.sin_addr.s_addr) | |
685 | hostIsNew = 0; | |
686 | } | |
687 | if (ifra->ifra_mask.sin_len) { | |
91447636 | 688 | in_ifscrub(ifp, ia, 0); |
1c79356b A |
689 | ia->ia_sockmask = ifra->ifra_mask; |
690 | ia->ia_subnetmask = | |
691 | ntohl(ia->ia_sockmask.sin_addr.s_addr); | |
692 | maskIsNew = 1; | |
693 | } | |
694 | if ((ifp->if_flags & IFF_POINTOPOINT) && | |
695 | (ifra->ifra_dstaddr.sin_family == AF_INET)) { | |
91447636 | 696 | in_ifscrub(ifp, ia, 0); |
1c79356b A |
697 | ia->ia_dstaddr = ifra->ifra_dstaddr; |
698 | maskIsNew = 1; /* We lie; but the effect's the same */ | |
699 | } | |
700 | if (ifra->ifra_addr.sin_family == AF_INET && | |
701 | (hostIsNew || maskIsNew)) { | |
702 | error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0); | |
703 | } | |
704 | if ((ifp->if_flags & IFF_BROADCAST) && | |
705 | (ifra->ifra_broadaddr.sin_family == AF_INET)) | |
706 | ia->ia_broadaddr = ifra->ifra_broadaddr; | |
707 | ||
708 | /* | |
709 | * Report event. | |
710 | */ | |
711 | ||
9bccf70c | 712 | if ((error == 0) || (error == EEXIST)) { |
1c79356b A |
713 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
714 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
715 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
716 | ||
717 | if (hostIsNew) | |
718 | ev_msg.event_code = KEV_INET_NEW_ADDR; | |
719 | else | |
720 | ev_msg.event_code = KEV_INET_CHANGED_ADDR; | |
721 | ||
722 | if (ia->ia_ifa.ifa_dstaddr) | |
723 | in_event_data.ia_dstaddr = | |
724 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
725 | else | |
726 | in_event_data.ia_dstaddr.s_addr = 0; | |
727 | ||
728 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
729 | in_event_data.ia_net = ia->ia_net; | |
730 | in_event_data.ia_netmask = ia->ia_netmask; | |
731 | in_event_data.ia_subnet = ia->ia_subnet; | |
732 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
733 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
734 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
735 | in_event_data.link_data.if_family = ifp->if_family; | |
736 | in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; | |
737 | ||
738 | ev_msg.dv[0].data_ptr = &in_event_data; | |
739 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); | |
740 | ev_msg.dv[1].data_length = 0; | |
741 | ||
742 | kev_post_msg(&ev_msg); | |
743 | } | |
2d21ac55 | 744 | break; |
1c79356b A |
745 | |
746 | case SIOCDIFADDR: | |
2d21ac55 | 747 | error = ifnet_ioctl(ifp, PF_INET, SIOCDIFADDR, ia); |
91447636 A |
748 | if (error == EOPNOTSUPP) |
749 | error = 0; | |
2d21ac55 A |
750 | if (error != 0) { |
751 | break; | |
752 | } | |
9bccf70c | 753 | |
91447636 | 754 | /* Fill out the kernel event information */ |
1c79356b A |
755 | ev_msg.vendor_code = KEV_VENDOR_APPLE; |
756 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
757 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
758 | ||
759 | ev_msg.event_code = KEV_INET_ADDR_DELETED; | |
760 | ||
761 | if (ia->ia_ifa.ifa_dstaddr) | |
762 | in_event_data.ia_dstaddr = | |
763 | ((struct sockaddr_in *)ia->ia_ifa.ifa_dstaddr)->sin_addr; | |
764 | else | |
765 | in_event_data.ia_dstaddr.s_addr = 0; | |
766 | ||
767 | in_event_data.ia_addr = ia->ia_addr.sin_addr; | |
768 | in_event_data.ia_net = ia->ia_net; | |
769 | in_event_data.ia_netmask = ia->ia_netmask; | |
770 | in_event_data.ia_subnet = ia->ia_subnet; | |
771 | in_event_data.ia_subnetmask = ia->ia_subnetmask; | |
772 | in_event_data.ia_netbroadcast = ia->ia_netbroadcast; | |
773 | strncpy(&in_event_data.link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
774 | in_event_data.link_data.if_family = ifp->if_family; | |
775 | in_event_data.link_data.if_unit = (unsigned long) ifp->if_unit; | |
776 | ||
777 | ev_msg.dv[0].data_ptr = &in_event_data; | |
91447636 | 778 | ev_msg.dv[0].data_length = sizeof(struct kev_in_data); |
1c79356b A |
779 | ev_msg.dv[1].data_length = 0; |
780 | ||
91447636 A |
781 | lck_mtx_lock(rt_mtx); |
782 | TAILQ_REMOVE(&in_ifaddrhead, ia, ia_link); | |
9bccf70c A |
783 | /* |
784 | * in_ifscrub kills the interface route. | |
785 | */ | |
91447636 | 786 | in_ifscrub(ifp, ia, 1); |
1c79356b | 787 | ifa = &ia->ia_ifa; |
91447636 A |
788 | lck_mtx_unlock(rt_mtx); |
789 | ifnet_lock_exclusive(ifp); | |
790 | if_detach_ifa(ifp, ifa); | |
9bccf70c A |
791 | |
792 | #ifdef __APPLE__ | |
91447636 | 793 | /* |
9bccf70c A |
794 | * If the interface supports multicast, and no address is left, |
795 | * remove the "all hosts" multicast group from that interface. | |
796 | */ | |
797 | if (ifp->if_flags & IFF_MULTICAST) { | |
798 | struct in_addr addr; | |
0c530ab8 | 799 | struct in_multi *inm = NULL; |
9bccf70c A |
800 | |
801 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) | |
802 | if (ifa->ifa_addr->sa_family == AF_INET) | |
803 | break; | |
804 | ||
805 | if (ifa == 0) { | |
806 | addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); | |
807 | IN_LOOKUP_MULTI(addr, ifp, inm); | |
9bccf70c | 808 | } |
91447636 A |
809 | ifnet_lock_done(ifp); |
810 | if (inm) | |
811 | in_delmulti(&inm); | |
812 | } else | |
813 | ifnet_lock_done(ifp); | |
9bccf70c | 814 | #endif |
91447636 A |
815 | |
816 | /* Post the kernel event */ | |
817 | kev_post_msg(&ev_msg); | |
2d21ac55 A |
818 | |
819 | /* | |
820 | * See if there is any IPV4 address left and if so, | |
821 | * reconfigure KDP to use current primary address. | |
822 | */ | |
823 | ifa = ifa_ifpgetprimary(ifp, AF_INET); | |
824 | if (ifa != NULL) { | |
825 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa); | |
826 | if (error == EOPNOTSUPP) | |
827 | error = 0; | |
828 | ||
829 | /* Release reference from ifa_ifpgetprimary() */ | |
830 | ifafree(ifa); | |
831 | } | |
1c79356b A |
832 | break; |
833 | ||
9bccf70c | 834 | #ifdef __APPLE__ |
1c79356b A |
835 | case SIOCSETOT: { |
836 | /* | |
837 | * Inspiration from tcp_ctloutput() and ip_ctloutput() | |
9bccf70c | 838 | * Special ioctl for OpenTransport sockets |
1c79356b A |
839 | */ |
840 | struct inpcb *inp, *cloned_inp; | |
91447636 | 841 | int error2 = 0; |
1c79356b A |
842 | int cloned_fd = *(int *)data; |
843 | ||
1c79356b A |
844 | inp = sotoinpcb(so); |
845 | if (inp == NULL) { | |
1c79356b A |
846 | break; |
847 | } | |
848 | ||
849 | /* let's make sure it's either -1 or a valid file descriptor */ | |
850 | if (cloned_fd != -1) { | |
851 | struct socket *cloned_so; | |
91447636 A |
852 | error2 = file_socket(cloned_fd, &cloned_so); |
853 | if (error2){ | |
1c79356b A |
854 | break; |
855 | } | |
1c79356b | 856 | cloned_inp = sotoinpcb(cloned_so); |
91447636 | 857 | file_drop(cloned_fd); |
1c79356b A |
858 | } else { |
859 | cloned_inp = NULL; | |
860 | } | |
861 | ||
862 | if (cloned_inp == NULL) { | |
863 | /* OT always uses IP_PORTRANGE_HIGH */ | |
864 | inp->inp_flags &= ~(INP_LOWPORT); | |
865 | inp->inp_flags |= INP_HIGHPORT; | |
866 | /* For UDP, OT allows broadcast by default */ | |
867 | if (so->so_type == SOCK_DGRAM) | |
868 | so->so_options |= SO_BROADCAST; | |
869 | /* For TCP we want to see MSG_OOB when receive urgent data */ | |
870 | else if (so->so_type == SOCK_STREAM) | |
871 | so->so_options |= SO_WANTOOBFLAG; | |
872 | } else { | |
873 | inp->inp_ip_tos = cloned_inp->inp_ip_tos; | |
874 | inp->inp_ip_ttl = cloned_inp->inp_ip_ttl; | |
875 | inp->inp_flags = cloned_inp->inp_flags; | |
876 | ||
877 | /* Multicast options */ | |
878 | if (cloned_inp->inp_moptions != NULL) { | |
91447636 | 879 | int i; |
1c79356b A |
880 | struct ip_moptions *cloned_imo = cloned_inp->inp_moptions; |
881 | struct ip_moptions *imo = inp->inp_moptions; | |
882 | ||
883 | if (imo == NULL) { | |
884 | /* | |
885 | * No multicast option buffer attached to the pcb; | |
886 | * allocate one. | |
887 | */ | |
1c79356b A |
888 | imo = (struct ip_moptions*) |
889 | _MALLOC(sizeof(*imo), M_IPMOPTS, M_WAITOK); | |
890 | if (imo == NULL) { | |
91447636 | 891 | error2 = ENOBUFS; |
1c79356b A |
892 | break; |
893 | } | |
1c79356b A |
894 | inp->inp_moptions = imo; |
895 | } | |
896 | imo->imo_multicast_ifp = cloned_imo->imo_multicast_ifp; | |
897 | imo->imo_multicast_vif = cloned_imo->imo_multicast_vif; | |
898 | imo->imo_multicast_ttl = cloned_imo->imo_multicast_ttl; | |
899 | imo->imo_multicast_loop = cloned_imo->imo_multicast_loop; | |
900 | imo->imo_num_memberships = cloned_imo->imo_num_memberships; | |
901 | for (i = 0; i < cloned_imo->imo_num_memberships; i++) { | |
902 | imo->imo_membership[i] = | |
903 | in_addmulti(&cloned_imo->imo_membership[i]->inm_addr, | |
904 | cloned_imo->imo_membership[i]->inm_ifp); | |
9bccf70c | 905 | if (imo->imo_membership[i] == NULL) { |
91447636 | 906 | error2 = ENOBUFS; |
9bccf70c A |
907 | break; |
908 | } | |
909 | } | |
910 | if (i < cloned_imo->imo_num_memberships) { | |
911 | /* Failed, perform cleanup */ | |
912 | for (i--; i >= 0; i--) | |
91447636 A |
913 | in_delmulti(&imo->imo_membership[i]); |
914 | imo->imo_num_memberships = 0; | |
9bccf70c | 915 | break; |
1c79356b A |
916 | } |
917 | } | |
918 | } | |
1c79356b A |
919 | break; |
920 | } | |
9bccf70c | 921 | #endif /* __APPLE__ */ |
1c79356b A |
922 | |
923 | default: | |
2d21ac55 | 924 | error = EOPNOTSUPP; |
1c79356b | 925 | } |
2d21ac55 A |
926 | done: |
927 | if (ia != NULL) { | |
928 | ifafree(&ia->ia_ifa); | |
929 | } | |
930 | return (error); | |
1c79356b A |
931 | } |
932 | ||
933 | /* | |
934 | * SIOC[GAD]LIFADDR. | |
9bccf70c | 935 | * SIOCGLIFADDR: get first address. (?!?) |
1c79356b A |
936 | * SIOCGLIFADDR with IFLR_PREFIX: |
937 | * get first address that matches the specified prefix. | |
938 | * SIOCALIFADDR: add the specified address. | |
939 | * SIOCALIFADDR with IFLR_PREFIX: | |
940 | * EINVAL since we can't deduce hostid part of the address. | |
941 | * SIOCDLIFADDR: delete the specified address. | |
942 | * SIOCDLIFADDR with IFLR_PREFIX: | |
943 | * delete the first address that matches the specified prefix. | |
944 | * return values: | |
945 | * EINVAL on invalid parameters | |
946 | * EADDRNOTAVAIL on prefix match failed/specified address not found | |
947 | * other values may be returned from in_ioctl() | |
948 | */ | |
949 | static int | |
91447636 A |
950 | in_lifaddr_ioctl( |
951 | struct socket *so, | |
952 | u_long cmd, | |
953 | caddr_t data, | |
954 | struct ifnet *ifp, | |
955 | struct proc *p) | |
1c79356b A |
956 | { |
957 | struct if_laddrreq *iflr = (struct if_laddrreq *)data; | |
958 | struct ifaddr *ifa; | |
959 | ||
960 | /* sanity checks */ | |
961 | if (!data || !ifp) { | |
962 | panic("invalid argument to in_lifaddr_ioctl"); | |
963 | /*NOTRECHED*/ | |
964 | } | |
965 | ||
966 | switch (cmd) { | |
967 | case SIOCGLIFADDR: | |
968 | /* address must be specified on GET with IFLR_PREFIX */ | |
969 | if ((iflr->flags & IFLR_PREFIX) == 0) | |
970 | break; | |
971 | /*FALLTHROUGH*/ | |
972 | case SIOCALIFADDR: | |
973 | case SIOCDLIFADDR: | |
974 | /* address must be specified on ADD and DELETE */ | |
975 | if (iflr->addr.ss_family != AF_INET) | |
976 | return EINVAL; | |
977 | if (iflr->addr.ss_len != sizeof(struct sockaddr_in)) | |
978 | return EINVAL; | |
979 | /* XXX need improvement */ | |
980 | if (iflr->dstaddr.ss_family | |
981 | && iflr->dstaddr.ss_family != AF_INET) | |
982 | return EINVAL; | |
983 | if (iflr->dstaddr.ss_family | |
984 | && iflr->dstaddr.ss_len != sizeof(struct sockaddr_in)) | |
985 | return EINVAL; | |
986 | break; | |
987 | default: /*shouldn't happen*/ | |
1c79356b | 988 | return EOPNOTSUPP; |
1c79356b A |
989 | } |
990 | if (sizeof(struct in_addr) * 8 < iflr->prefixlen) | |
991 | return EINVAL; | |
992 | ||
993 | switch (cmd) { | |
994 | case SIOCALIFADDR: | |
995 | { | |
996 | struct in_aliasreq ifra; | |
997 | ||
998 | if (iflr->flags & IFLR_PREFIX) | |
999 | return EINVAL; | |
1000 | ||
1001 | /* copy args to in_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ | |
1002 | bzero(&ifra, sizeof(ifra)); | |
1003 | bcopy(iflr->iflr_name, ifra.ifra_name, | |
1004 | sizeof(ifra.ifra_name)); | |
1005 | ||
1006 | bcopy(&iflr->addr, &ifra.ifra_addr, iflr->addr.ss_len); | |
1007 | ||
1008 | if (iflr->dstaddr.ss_family) { /*XXX*/ | |
1009 | bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, | |
1010 | iflr->dstaddr.ss_len); | |
1011 | } | |
1012 | ||
1013 | ifra.ifra_mask.sin_family = AF_INET; | |
1014 | ifra.ifra_mask.sin_len = sizeof(struct sockaddr_in); | |
1015 | in_len2mask(&ifra.ifra_mask.sin_addr, iflr->prefixlen); | |
1016 | ||
1017 | return in_control(so, SIOCAIFADDR, (caddr_t)&ifra, ifp, p); | |
1018 | } | |
1019 | case SIOCGLIFADDR: | |
1020 | case SIOCDLIFADDR: | |
1021 | { | |
1022 | struct in_ifaddr *ia; | |
2d21ac55 A |
1023 | struct in_addr mask, candidate; |
1024 | struct in_addr match = { 0 }; | |
1c79356b A |
1025 | struct sockaddr_in *sin; |
1026 | int cmp; | |
1027 | ||
1028 | bzero(&mask, sizeof(mask)); | |
1029 | if (iflr->flags & IFLR_PREFIX) { | |
1030 | /* lookup a prefix rather than address. */ | |
1031 | in_len2mask(&mask, iflr->prefixlen); | |
1032 | ||
1033 | sin = (struct sockaddr_in *)&iflr->addr; | |
1034 | match.s_addr = sin->sin_addr.s_addr; | |
1035 | match.s_addr &= mask.s_addr; | |
1036 | ||
1037 | /* if you set extra bits, that's wrong */ | |
1038 | if (match.s_addr != sin->sin_addr.s_addr) | |
1039 | return EINVAL; | |
1040 | ||
1041 | cmp = 1; | |
1042 | } else { | |
1043 | if (cmd == SIOCGLIFADDR) { | |
1044 | /* on getting an address, take the 1st match */ | |
1045 | cmp = 0; /*XXX*/ | |
1046 | } else { | |
1047 | /* on deleting an address, do exact match */ | |
1048 | in_len2mask(&mask, 32); | |
1049 | sin = (struct sockaddr_in *)&iflr->addr; | |
1050 | match.s_addr = sin->sin_addr.s_addr; | |
1051 | ||
1052 | cmp = 1; | |
1053 | } | |
1054 | } | |
1055 | ||
91447636 | 1056 | ifnet_lock_shared(ifp); |
1c79356b A |
1057 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { |
1058 | if (ifa->ifa_addr->sa_family != AF_INET6) | |
1059 | continue; | |
1060 | if (!cmp) | |
1061 | break; | |
1062 | candidate.s_addr = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; | |
1063 | candidate.s_addr &= mask.s_addr; | |
1064 | if (candidate.s_addr == match.s_addr) | |
1065 | break; | |
1066 | } | |
91447636 | 1067 | ifnet_lock_done(ifp); |
1c79356b A |
1068 | if (!ifa) |
1069 | return EADDRNOTAVAIL; | |
1070 | ia = (struct in_ifaddr *)ifa; | |
1071 | ||
1072 | if (cmd == SIOCGLIFADDR) { | |
1073 | /* fill in the if_laddrreq structure */ | |
1074 | bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); | |
1075 | ||
1076 | if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { | |
1077 | bcopy(&ia->ia_dstaddr, &iflr->dstaddr, | |
1078 | ia->ia_dstaddr.sin_len); | |
1079 | } else | |
1080 | bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); | |
1081 | ||
1082 | iflr->prefixlen = | |
1083 | in_mask2len(&ia->ia_sockmask.sin_addr); | |
1084 | ||
1085 | iflr->flags = 0; /*XXX*/ | |
1086 | ||
1087 | return 0; | |
1088 | } else { | |
1089 | struct in_aliasreq ifra; | |
1090 | ||
1091 | /* fill in_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ | |
1092 | bzero(&ifra, sizeof(ifra)); | |
1093 | bcopy(iflr->iflr_name, ifra.ifra_name, | |
1094 | sizeof(ifra.ifra_name)); | |
1095 | ||
1096 | bcopy(&ia->ia_addr, &ifra.ifra_addr, | |
1097 | ia->ia_addr.sin_len); | |
1098 | if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { | |
1099 | bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, | |
1100 | ia->ia_dstaddr.sin_len); | |
1101 | } | |
1102 | bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, | |
1103 | ia->ia_sockmask.sin_len); | |
1104 | ||
1105 | return in_control(so, SIOCDIFADDR, (caddr_t)&ifra, | |
1106 | ifp, p); | |
1107 | } | |
1108 | } | |
1109 | } | |
1110 | ||
1111 | return EOPNOTSUPP; /*just for safety*/ | |
1112 | } | |
1113 | ||
1114 | /* | |
1115 | * Delete any existing route for an interface. | |
1116 | */ | |
1117 | void | |
91447636 A |
1118 | in_ifscrub( |
1119 | struct ifnet *ifp, | |
1120 | struct in_ifaddr *ia, | |
1121 | int locked) | |
1c79356b A |
1122 | { |
1123 | ||
1124 | if ((ia->ia_flags & IFA_ROUTE) == 0) | |
1125 | return; | |
91447636 A |
1126 | if (!locked) |
1127 | lck_mtx_lock(rt_mtx); | |
1c79356b | 1128 | if (ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) |
91447636 | 1129 | rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST); |
1c79356b | 1130 | else |
91447636 | 1131 | rtinit_locked(&(ia->ia_ifa), (int)RTM_DELETE, 0); |
1c79356b | 1132 | ia->ia_flags &= ~IFA_ROUTE; |
91447636 A |
1133 | if (!locked) |
1134 | lck_mtx_unlock(rt_mtx); | |
1c79356b A |
1135 | } |
1136 | ||
1137 | /* | |
1138 | * Initialize an interface's internet address | |
1139 | * and routing table entry. | |
1140 | */ | |
1141 | static int | |
91447636 A |
1142 | in_ifinit( |
1143 | struct ifnet *ifp, | |
1144 | struct in_ifaddr *ia, | |
1145 | struct sockaddr_in *sin, | |
1146 | int scrub) | |
1c79356b | 1147 | { |
91447636 | 1148 | u_long i = ntohl(sin->sin_addr.s_addr); |
1c79356b | 1149 | struct sockaddr_in oldaddr; |
91447636 | 1150 | int flags = RTF_UP, error; |
2d21ac55 A |
1151 | struct ifaddr *ifa0; |
1152 | unsigned int cmd; | |
1c79356b | 1153 | |
1c79356b A |
1154 | oldaddr = ia->ia_addr; |
1155 | ia->ia_addr = *sin; | |
1156 | ||
9bccf70c | 1157 | /* |
2d21ac55 A |
1158 | * Give the interface a chance to initialize if this is its first |
1159 | * address, and to validate the address if necessary. Send down | |
1160 | * SIOCSIFADDR for first address, and SIOCAIFADDR for alias(es). | |
1161 | * We find the first IPV4 address assigned to it and check if this | |
1162 | * is the same as the one passed into this routine. | |
9bccf70c | 1163 | */ |
2d21ac55 A |
1164 | ifa0 = ifa_ifpgetprimary(ifp, AF_INET); |
1165 | cmd = (&ia->ia_ifa == ifa0) ? SIOCSIFADDR : SIOCAIFADDR; | |
1166 | error = ifnet_ioctl(ifp, PF_INET, cmd, ia); | |
1c79356b | 1167 | if (error == EOPNOTSUPP) |
2d21ac55 A |
1168 | error = 0; |
1169 | /* | |
1170 | * If we've just sent down SIOCAIFADDR, send another ioctl down | |
1171 | * for SIOCSIFADDR for the first IPV4 address of the interface, | |
1172 | * because an address change on one of the addresses will result | |
1173 | * in the removal of the previous first IPV4 address. KDP needs | |
1174 | * be reconfigured with the current primary IPV4 address. | |
1175 | */ | |
1176 | if (error == 0 && cmd == SIOCAIFADDR) { | |
1177 | error = ifnet_ioctl(ifp, PF_INET, SIOCSIFADDR, ifa0); | |
1178 | if (error == EOPNOTSUPP) | |
1179 | error = 0; | |
1180 | } | |
1181 | ||
1182 | /* Release reference from ifa_ifpgetprimary() */ | |
1183 | ifafree(ifa0); | |
1184 | ||
1c79356b | 1185 | if (error) { |
1c79356b A |
1186 | ia->ia_addr = oldaddr; |
1187 | return (error); | |
1188 | } | |
1c79356b A |
1189 | if (scrub) { |
1190 | ia->ia_ifa.ifa_addr = (struct sockaddr *)&oldaddr; | |
91447636 | 1191 | in_ifscrub(ifp, ia, 0); |
1c79356b A |
1192 | ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; |
1193 | } | |
1194 | if (IN_CLASSA(i)) | |
1195 | ia->ia_netmask = IN_CLASSA_NET; | |
1196 | else if (IN_CLASSB(i)) | |
1197 | ia->ia_netmask = IN_CLASSB_NET; | |
1198 | else | |
1199 | ia->ia_netmask = IN_CLASSC_NET; | |
1200 | /* | |
1201 | * The subnet mask usually includes at least the standard network part, | |
1202 | * but may may be smaller in the case of supernetting. | |
1203 | * If it is set, we believe it. | |
1204 | */ | |
1205 | if (ia->ia_subnetmask == 0) { | |
1206 | ia->ia_subnetmask = ia->ia_netmask; | |
1207 | ia->ia_sockmask.sin_addr.s_addr = htonl(ia->ia_subnetmask); | |
1208 | } else | |
1209 | ia->ia_netmask &= ia->ia_subnetmask; | |
1210 | ia->ia_net = i & ia->ia_netmask; | |
1211 | ia->ia_subnet = i & ia->ia_subnetmask; | |
1212 | in_socktrim(&ia->ia_sockmask); | |
1213 | /* | |
1214 | * Add route for the network. | |
1215 | */ | |
1216 | ia->ia_ifa.ifa_metric = ifp->if_metric; | |
1217 | if (ifp->if_flags & IFF_BROADCAST) { | |
1218 | ia->ia_broadaddr.sin_addr.s_addr = | |
1219 | htonl(ia->ia_subnet | ~ia->ia_subnetmask); | |
1220 | ia->ia_netbroadcast.s_addr = | |
1221 | htonl(ia->ia_net | ~ ia->ia_netmask); | |
1222 | } else if (ifp->if_flags & IFF_LOOPBACK) { | |
1223 | ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr; | |
1224 | flags |= RTF_HOST; | |
1225 | } else if (ifp->if_flags & IFF_POINTOPOINT) { | |
1226 | if (ia->ia_dstaddr.sin_family != AF_INET) | |
1227 | return (0); | |
1228 | flags |= RTF_HOST; | |
1229 | } | |
1230 | if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0) | |
1231 | ia->ia_flags |= IFA_ROUTE; | |
9bccf70c A |
1232 | /* XXX check if the subnet route points to the same interface */ |
1233 | if (error == EEXIST) | |
1234 | error = 0; | |
1c79356b A |
1235 | |
1236 | /* | |
1237 | * If the interface supports multicast, join the "all hosts" | |
1238 | * multicast group on that interface. | |
1239 | */ | |
1240 | if (ifp->if_flags & IFF_MULTICAST) { | |
9bccf70c | 1241 | struct in_multi *inm; |
1c79356b A |
1242 | struct in_addr addr; |
1243 | ||
1244 | addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); | |
91447636 | 1245 | ifnet_lock_shared(ifp); |
9bccf70c | 1246 | IN_LOOKUP_MULTI(addr, ifp, inm); |
91447636 | 1247 | ifnet_lock_done(ifp); |
9bccf70c A |
1248 | if (inm == 0) |
1249 | in_addmulti(&addr, ifp); | |
1c79356b A |
1250 | } |
1251 | return (error); | |
1252 | } | |
1253 | ||
1254 | ||
1255 | /* | |
1256 | * Return 1 if the address might be a local broadcast address. | |
1257 | */ | |
1258 | int | |
91447636 A |
1259 | in_broadcast( |
1260 | struct in_addr in, | |
1261 | struct ifnet *ifp) | |
1c79356b | 1262 | { |
91447636 | 1263 | struct ifaddr *ifa; |
1c79356b A |
1264 | u_long t; |
1265 | ||
1266 | if (in.s_addr == INADDR_BROADCAST || | |
1267 | in.s_addr == INADDR_ANY) | |
1268 | return 1; | |
1269 | if ((ifp->if_flags & IFF_BROADCAST) == 0) | |
1270 | return 0; | |
1271 | t = ntohl(in.s_addr); | |
1272 | /* | |
1273 | * Look through the list of addresses for a match | |
1274 | * with a broadcast address. | |
1275 | */ | |
1276 | #define ia ((struct in_ifaddr *)ifa) | |
91447636 A |
1277 | ifnet_lock_shared(ifp); |
1278 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |
1279 | if (ifa->ifa_addr == NULL) { | |
1280 | ifnet_lock_done(ifp); | |
0b4e3aa0 | 1281 | return (0); |
91447636 | 1282 | } |
1c79356b A |
1283 | if (ifa->ifa_addr->sa_family == AF_INET && |
1284 | (in.s_addr == ia->ia_broadaddr.sin_addr.s_addr || | |
1285 | in.s_addr == ia->ia_netbroadcast.s_addr || | |
1286 | /* | |
1287 | * Check for old-style (host 0) broadcast. | |
1288 | */ | |
1289 | t == ia->ia_subnet || t == ia->ia_net) && | |
1290 | /* | |
1291 | * Check for an all one subnetmask. These | |
1292 | * only exist when an interface gets a secondary | |
1293 | * address. | |
1294 | */ | |
91447636 A |
1295 | ia->ia_subnetmask != (u_long)0xffffffff) { |
1296 | ifnet_lock_done(ifp); | |
1297 | return 1; | |
1298 | } | |
0b4e3aa0 | 1299 | } |
91447636 | 1300 | ifnet_lock_done(ifp); |
1c79356b A |
1301 | return (0); |
1302 | #undef ia | |
1303 | } | |
91447636 A |
1304 | |
1305 | static void | |
1306 | in_free_inm( | |
1307 | void* ifma_protospec) | |
1308 | { | |
1309 | struct in_multi *inm = ifma_protospec; | |
1310 | ||
1311 | /* | |
1312 | * No remaining claims to this record; let IGMP know that | |
1313 | * we are leaving the multicast group. | |
1314 | */ | |
1315 | igmp_leavegroup(inm); | |
1316 | lck_mtx_lock(rt_mtx); | |
1317 | LIST_REMOVE(inm, inm_link); | |
1318 | lck_mtx_unlock(rt_mtx); | |
1319 | FREE(inm, M_IPMADDR); | |
1320 | } | |
1321 | ||
1c79356b A |
1322 | /* |
1323 | * Add an address to the list of IP multicast addresses for a given interface. | |
1324 | */ | |
1325 | struct in_multi * | |
91447636 A |
1326 | in_addmulti( |
1327 | struct in_addr *ap, | |
1328 | struct ifnet *ifp) | |
1c79356b | 1329 | { |
91447636 | 1330 | struct in_multi *inm; |
1c79356b A |
1331 | int error; |
1332 | struct sockaddr_in sin; | |
1333 | struct ifmultiaddr *ifma; | |
1c79356b A |
1334 | |
1335 | /* | |
1336 | * Call generic routine to add membership or increment | |
1337 | * refcount. It wants addresses in the form of a sockaddr, | |
1338 | * so we build one here (being careful to zero the unused bytes). | |
1339 | */ | |
1340 | bzero(&sin, sizeof sin); | |
1341 | sin.sin_family = AF_INET; | |
1342 | sin.sin_len = sizeof sin; | |
1343 | sin.sin_addr = *ap; | |
1344 | error = if_addmulti(ifp, (struct sockaddr *)&sin, &ifma); | |
1345 | if (error) { | |
1c79356b A |
1346 | return 0; |
1347 | } | |
1348 | ||
1349 | /* | |
1350 | * If ifma->ifma_protospec is null, then if_addmulti() created | |
1351 | * a new record. Otherwise, we are done. | |
1352 | */ | |
9bccf70c | 1353 | if (ifma->ifma_protospec != 0) { |
1c79356b | 1354 | return ifma->ifma_protospec; |
9bccf70c | 1355 | } |
1c79356b | 1356 | |
0b4e3aa0 | 1357 | inm = (struct in_multi *) _MALLOC(sizeof(*inm), M_IPMADDR, M_WAITOK); |
1c79356b | 1358 | if (inm == NULL) { |
1c79356b A |
1359 | return (NULL); |
1360 | } | |
1361 | ||
1362 | bzero(inm, sizeof *inm); | |
1363 | inm->inm_addr = *ap; | |
1364 | inm->inm_ifp = ifp; | |
1365 | inm->inm_ifma = ifma; | |
91447636 A |
1366 | lck_mtx_lock(rt_mtx); |
1367 | if (ifma->ifma_protospec == NULL) { | |
1368 | ifma->ifma_protospec = inm; | |
1369 | ifma->ifma_free = in_free_inm; | |
1370 | LIST_INSERT_HEAD(&in_multihead, inm, inm_link); | |
1371 | } | |
1372 | lck_mtx_unlock(rt_mtx); | |
1373 | ||
1374 | if (ifma->ifma_protospec != inm) { | |
1375 | _FREE(inm, M_IPMADDR); | |
1376 | return ifma->ifma_protospec; | |
1377 | } | |
1c79356b A |
1378 | |
1379 | /* | |
1380 | * Let IGMP know that we have joined a new IP multicast group. | |
1381 | */ | |
55e303ae A |
1382 | error = igmp_joingroup(inm); |
1383 | if (error) { | |
91447636 A |
1384 | char addrbuf[16]; |
1385 | ||
1386 | /* | |
1387 | * We can't free the inm because someone else may already be | |
1388 | * using it. Once we put it in to ifma->ifma_protospec, it | |
1389 | * must exist as long as the ifma does. Might be nice to flag | |
1390 | * the error so we can try igmp_joingroup the next time through. | |
1391 | */ | |
1392 | log(LOG_ERR, "igmp_joingroup error %d joining multicast %s on %s%d\n", | |
1393 | error, inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf)), | |
1394 | ifp->if_name, ifp->if_unit); | |
55e303ae | 1395 | } |
91447636 | 1396 | |
1c79356b A |
1397 | return (inm); |
1398 | } | |
1399 | ||
1400 | /* | |
1401 | * Delete a multicast address record. | |
1402 | */ | |
1403 | void | |
91447636 A |
1404 | in_delmulti( |
1405 | struct in_multi **inm) | |
1c79356b | 1406 | { |
91447636 A |
1407 | struct in_multi *inm2; |
1408 | ||
1409 | lck_mtx_lock(rt_mtx); | |
1410 | LIST_FOREACH(inm2, &in_multihead, inm_link) { | |
1411 | if (inm2 == *inm) | |
1412 | break; | |
1413 | } | |
1414 | if (inm2 != *inm) { | |
1415 | lck_mtx_unlock(rt_mtx); | |
2d21ac55 | 1416 | printf("in_delmulti - ignoring invalid inm (%p)\n", *inm); |
91447636 A |
1417 | return; |
1418 | } | |
1419 | lck_mtx_unlock(rt_mtx); | |
9bccf70c A |
1420 | |
1421 | /* We intentionally do this a bit differently than BSD */ | |
91447636 A |
1422 | if ((*inm)->inm_ifma) { |
1423 | if_delmultiaddr((*inm)->inm_ifma, 0); | |
1424 | ifma_release((*inm)->inm_ifma); | |
1c79356b | 1425 | } |
91447636 | 1426 | *inm = NULL; |
1c79356b | 1427 | } |
91447636 A |
1428 | |
1429 | #if !NFSCLIENT | |
2d21ac55 | 1430 | int inet_aton(char *cp, struct in_addr *pin); |
91447636 A |
1431 | int |
1432 | inet_aton(char * cp, struct in_addr * pin) | |
1433 | { | |
2d21ac55 | 1434 | u_char * b = (unsigned char *)pin; |
91447636 A |
1435 | int i; |
1436 | char * p; | |
1437 | ||
1438 | for (p = cp, i = 0; i < 4; i++) { | |
1439 | u_long l = strtoul(p, 0, 0); | |
1440 | if (l > 255) | |
1441 | return (FALSE); | |
1442 | b[i] = l; | |
1443 | p = strchr(p, '.'); | |
1444 | if (i < 3 && p == NULL) | |
1445 | return (FALSE); | |
1446 | p++; | |
1447 | } | |
1448 | return (TRUE); | |
1449 | } | |
1450 | #endif |