]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet/in_arp.c
cdef93335a661b5746a3997dec7efe0ef99302cb
[apple/xnu.git] / bsd / netinet / in_arp.c
1 /*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Copyright (c) 1982, 1989, 1993
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 */
56
57 #include <kern/debug.h>
58 #include <netinet/in_arp.h>
59 #include <sys/types.h>
60 #include <sys/param.h>
61 #include <sys/kernel_types.h>
62 #include <sys/syslog.h>
63 #include <sys/systm.h>
64 #include <sys/time.h>
65 #include <sys/kernel.h>
66 #include <sys/mbuf.h>
67 #include <sys/sysctl.h>
68 #include <string.h>
69 #include <net/if_arp.h>
70 #include <net/if_dl.h>
71 #include <net/dlil.h>
72 #include <net/route.h>
73 #include <netinet/if_ether.h>
74 #include <netinet/in_var.h>
75
76 #define SIN(s) ((struct sockaddr_in *)s)
77 #define CONST_LLADDR(s) ((const u_char*)((s)->sdl_data + (s)->sdl_nlen))
78 #define rt_expire rt_rmx.rmx_expire
79
80 static const size_t MAX_HW_LEN = 10;
81
82 SYSCTL_DECL(_net_link_ether);
83 SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, "");
84
85 /* timer values */
86 static int arpt_prune = (5*60*1); /* walk list every 5 minutes */
87 static int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
88 static int arpt_down = 20; /* once declared down, don't send for 20 sec */
89
90 /* Apple Hardware SUM16 checksuming */
91 int apple_hwcksum_tx = 1;
92 int apple_hwcksum_rx = 1;
93
94 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, prune_intvl, CTLFLAG_RW,
95 &arpt_prune, 0, "");
96 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_RW,
97 &arpt_keep, 0, "");
98 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, host_down_time, CTLFLAG_RW,
99 &arpt_down, 0, "");
100 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, apple_hwcksum_tx, CTLFLAG_RW,
101 &apple_hwcksum_tx, 0, "");
102 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, apple_hwcksum_rx, CTLFLAG_RW,
103 &apple_hwcksum_rx, 0, "");
104
105 struct llinfo_arp {
106 LIST_ENTRY(llinfo_arp) la_le;
107 struct rtentry *la_rt;
108 struct mbuf *la_hold; /* last packet until resolved/timeout */
109 long la_asked; /* last time we QUERIED for this addr */
110 };
111
112 static LIST_HEAD(, llinfo_arp) llinfo_arp;
113
114 static int arp_inuse, arp_allocated;
115
116 static int arp_maxtries = 5;
117 static int useloopback = 1; /* use loopback interface for local traffic */
118 static int arp_proxyall = 0;
119
120 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_RW,
121 &arp_maxtries, 0, "");
122 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, useloopback, CTLFLAG_RW,
123 &useloopback, 0, "");
124 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
125 &arp_proxyall, 0, "");
126
127 static int log_arp_warnings = 0;
128
129 SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_warnings, CTLFLAG_RW,
130 &log_arp_warnings, 0,
131 "log arp warning messages");
132
133 extern u_int32_t ipv4_ll_arp_aware;
134
135 /*
136 * Free an arp entry.
137 */
138 static void
139 arptfree(
140 struct llinfo_arp *la)
141 {
142 struct rtentry *rt = la->la_rt;
143 struct sockaddr_dl *sdl;
144 lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
145 if (rt == 0)
146 panic("arptfree");
147 if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
148 sdl->sdl_family == AF_LINK) {
149 sdl->sdl_alen = 0;
150 la->la_asked = 0;
151 rt->rt_flags &= ~RTF_REJECT;
152 return;
153 }
154 rtrequest_locked(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt),
155 0, (struct rtentry **)0);
156 }
157
158 /*
159 * Timeout routine. Age arp_tab entries periodically.
160 */
161 /* ARGSUSED */
162 static void
163 arptimer(
164 __unused void *ignored_arg)
165 {
166 struct llinfo_arp *la = llinfo_arp.lh_first;
167 struct llinfo_arp *ola;
168 struct timeval timenow;
169
170 lck_mtx_lock(rt_mtx);
171 getmicrotime(&timenow);
172 while ((ola = la) != 0) {
173 struct rtentry *rt = la->la_rt;
174 la = la->la_le.le_next;
175 if (rt->rt_expire && rt->rt_expire <= timenow.tv_sec)
176 arptfree(ola); /* timer has expired, clear */
177 }
178 lck_mtx_unlock(rt_mtx);
179 timeout(arptimer, (caddr_t)0, arpt_prune * hz);
180 }
181
182 /*
183 * Parallel to llc_rtrequest.
184 */
185 static void
186 arp_rtrequest(
187 int req,
188 struct rtentry *rt,
189 __unused struct sockaddr *sa)
190 {
191 struct sockaddr *gate = rt->rt_gateway;
192 struct llinfo_arp *la = (struct llinfo_arp *)rt->rt_llinfo;
193 static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK, 0, 0, 0, 0, 0, {0}};
194 static int arpinit_done;
195 struct timeval timenow;
196
197 if (!arpinit_done) {
198 arpinit_done = 1;
199 LIST_INIT(&llinfo_arp);
200 timeout(arptimer, (caddr_t)0, hz);
201 }
202 lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
203
204 if (rt->rt_flags & RTF_GATEWAY)
205 return;
206 getmicrotime(&timenow);
207 switch (req) {
208
209 case RTM_ADD:
210 /*
211 * XXX: If this is a manually added route to interface
212 * such as older version of routed or gated might provide,
213 * restore cloning bit.
214 */
215 if ((rt->rt_flags & RTF_HOST) == 0 &&
216 SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
217 rt->rt_flags |= RTF_CLONING;
218 if (rt->rt_flags & RTF_CLONING) {
219 /*
220 * Case 1: This route should come from a route to iface.
221 */
222 rt_setgate(rt, rt_key(rt),
223 (struct sockaddr *)&null_sdl);
224 gate = rt->rt_gateway;
225 SDL(gate)->sdl_type = rt->rt_ifp->if_type;
226 SDL(gate)->sdl_index = rt->rt_ifp->if_index;
227 rt->rt_expire = timenow.tv_sec;
228 break;
229 }
230 /* Announce a new entry if requested. */
231 if (rt->rt_flags & RTF_ANNOUNCE)
232 dlil_send_arp(rt->rt_ifp, ARPOP_REQUEST, SDL(gate), rt_key(rt), (struct sockaddr_dl *)rt_key(rt), NULL);
233 /*FALLTHROUGH*/
234 case RTM_RESOLVE:
235 if (gate->sa_family != AF_LINK ||
236 gate->sa_len < sizeof(null_sdl)) {
237 if (log_arp_warnings)
238 log(LOG_DEBUG, "arp_rtrequest: bad gateway value\n");
239 break;
240 }
241 SDL(gate)->sdl_type = rt->rt_ifp->if_type;
242 SDL(gate)->sdl_index = rt->rt_ifp->if_index;
243 if (la != 0)
244 break; /* This happens on a route change */
245 /*
246 * Case 2: This route may come from cloning, or a manual route
247 * add with a LL address.
248 */
249 R_Malloc(la, struct llinfo_arp *, sizeof(*la));
250 rt->rt_llinfo = (caddr_t)la;
251 if (la == 0) {
252 if ( log_arp_warnings)
253 log(LOG_DEBUG, "arp_rtrequest: malloc failed\n");
254 break;
255 }
256 arp_inuse++, arp_allocated++;
257 Bzero(la, sizeof(*la));
258 la->la_rt = rt;
259 rt->rt_flags |= RTF_LLINFO;
260 LIST_INSERT_HEAD(&llinfo_arp, la, la_le);
261
262 #if INET
263 /*
264 * This keeps the multicast addresses from showing up
265 * in `arp -a' listings as unresolved. It's not actually
266 * functional. Then the same for broadcast.
267 */
268 if (IN_MULTICAST(ntohl(SIN(rt_key(rt))->sin_addr.s_addr))) {
269 dlil_resolve_multi(rt->rt_ifp, rt_key(rt), gate, sizeof(struct sockaddr_dl));
270 rt->rt_expire = 0;
271 }
272 else if (in_broadcast(SIN(rt_key(rt))->sin_addr, rt->rt_ifp)) {
273 struct sockaddr_dl *gate_ll = SDL(gate);
274 size_t broadcast_len;
275 ifnet_llbroadcast_copy_bytes(rt->rt_ifp, LLADDR(gate_ll),
276 sizeof(gate_ll->sdl_data),
277 &broadcast_len);
278 gate_ll->sdl_alen = broadcast_len;
279 gate_ll->sdl_family = AF_LINK;
280 gate_ll->sdl_len = sizeof(struct sockaddr_dl);
281 rt->rt_expire = timenow.tv_sec;
282 }
283 #endif
284
285 if (SIN(rt_key(rt))->sin_addr.s_addr ==
286 (IA_SIN(rt->rt_ifa))->sin_addr.s_addr) {
287 /*
288 * This test used to be
289 * if (loif.if_flags & IFF_UP)
290 * It allowed local traffic to be forced
291 * through the hardware by configuring the loopback down.
292 * However, it causes problems during network configuration
293 * for boards that can't receive packets they send.
294 * It is now necessary to clear "useloopback" and remove
295 * the route to force traffic out to the hardware.
296 */
297 rt->rt_expire = 0;
298 ifnet_lladdr_copy_bytes(rt->rt_ifp, LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6);
299 if (useloopback)
300 rt->rt_ifp = loif;
301
302 }
303 break;
304
305 case RTM_DELETE:
306 if (la == 0)
307 break;
308 arp_inuse--;
309 LIST_REMOVE(la, la_le);
310 rt->rt_llinfo = 0;
311 rt->rt_flags &= ~RTF_LLINFO;
312 if (la->la_hold) {
313 m_freem(la->la_hold);
314 }
315 la->la_hold = NULL;
316 R_Free((caddr_t)la);
317 }
318 }
319
320 /*
321 * convert hardware address to hex string for logging errors.
322 */
323 static const char *
324 sdl_addr_to_hex(const struct sockaddr_dl *sdl, char * orig_buf, int buflen)
325 {
326 char * buf = orig_buf;
327 int i;
328 const u_char * lladdr = sdl->sdl_data;
329 int maxbytes = buflen / 3;
330
331 if (maxbytes > sdl->sdl_alen) {
332 maxbytes = sdl->sdl_alen;
333 }
334 *buf = '\0';
335 for (i = 0; i < maxbytes; i++) {
336 snprintf(buf, 3, "%02x", lladdr[i]);
337 buf += 2;
338 *buf = (i == maxbytes - 1) ? '\0' : ':';
339 buf++;
340 }
341 return (orig_buf);
342 }
343
344 /*
345 * arp_lookup_route will lookup the route for a given address.
346 *
347 * The routing lock must be held. The address must be for a
348 * host on a local network on this interface.
349 */
350 static errno_t
351 arp_lookup_route(
352 const struct in_addr *addr,
353 int create,
354 int proxy,
355 route_t *route)
356 {
357 struct sockaddr_inarp sin = {sizeof(sin), AF_INET, 0, {0}, {0}, 0, 0};
358 const char *why = 0;
359 errno_t error = 0;
360
361 // Caller is responsible for taking the routing lock
362 lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED);
363
364 sin.sin_addr.s_addr = addr->s_addr;
365 sin.sin_other = proxy ? SIN_PROXY : 0;
366
367 *route = rtalloc1_locked((const struct sockaddr*)&sin, create, 0);
368 if (*route == NULL)
369 return ENETUNREACH;
370
371 rtunref(*route);
372
373 if ((*route)->rt_flags & RTF_GATEWAY) {
374 why = "host is not on local network";
375
376 /* If there are no references to this route, purge it */
377 if ((*route)->rt_refcnt <= 0 && ((*route)->rt_flags & RTF_WASCLONED) != 0) {
378 rtrequest_locked(RTM_DELETE,
379 (struct sockaddr *)rt_key(*route),
380 (*route)->rt_gateway, rt_mask(*route),
381 (*route)->rt_flags, 0);
382 }
383 *route = NULL;
384 error = ENETUNREACH;
385 }
386 else if (((*route)->rt_flags & RTF_LLINFO) == 0) {
387 why = "could not allocate llinfo";
388 *route = NULL;
389 error = ENOMEM;
390 }
391 else if ((*route)->rt_gateway->sa_family != AF_LINK) {
392 why = "gateway route is not ours";
393 *route = NULL;
394 error = EPROTONOSUPPORT;
395 }
396
397 if (why && create && log_arp_warnings) {
398 char tmp[MAX_IPv4_STR_LEN];
399 log(LOG_DEBUG, "arplookup %s failed: %s\n",
400 inet_ntop(AF_INET, addr, tmp, sizeof(tmp)), why);
401 }
402
403 return error;
404 }
405
406
407 __private_extern__ errno_t
408 arp_route_to_gateway_route(
409 const struct sockaddr *net_dest,
410 route_t hint,
411 route_t *out_route);
412 /*
413 * arp_route_to_gateway_route will find the gateway route for a given route.
414 *
415 * If the route is down, look the route up again.
416 * If the route goes through a gateway, get the route to the gateway.
417 * If the gateway route is down, look it up again.
418 * If the route is set to reject, verify it hasn't expired.
419 */
420 __private_extern__ errno_t
421 arp_route_to_gateway_route(
422 const struct sockaddr *net_dest,
423 route_t hint,
424 route_t *out_route)
425 {
426 route_t route = hint;
427 *out_route = NULL;
428 struct timeval timenow;
429
430 /* If we got a hint from the higher layers, check it out */
431 if (route) {
432 lck_mtx_lock(rt_mtx);
433
434 if ((route->rt_flags & RTF_UP) == 0) {
435 /* route is down, find a new one */
436 hint = route = rtalloc1_locked(net_dest, 1, 0);
437 if (hint) {
438 rtunref(hint);
439 }
440 else {
441 /* No route to host */
442 lck_mtx_unlock(rt_mtx);
443 return EHOSTUNREACH;
444 }
445 }
446
447 if (route->rt_flags & RTF_GATEWAY) {
448 /*
449 * We need the gateway route. If it is NULL or down,
450 * look it up.
451 */
452 if (route->rt_gwroute == 0 ||
453 (route->rt_gwroute->rt_flags & RTF_UP) == 0) {
454 if (route->rt_gwroute != 0)
455 rtfree_locked(route->rt_gwroute);
456
457 route->rt_gwroute = rtalloc1_locked(route->rt_gateway, 1, 0);
458 if (route->rt_gwroute == 0) {
459 lck_mtx_unlock(rt_mtx);
460 return EHOSTUNREACH;
461 }
462 }
463
464 route = route->rt_gwroute;
465 }
466
467 if (route->rt_flags & RTF_REJECT) {
468 getmicrotime(&timenow);
469 if (route->rt_rmx.rmx_expire == 0 ||
470 timenow.tv_sec < route->rt_rmx.rmx_expire) {
471 lck_mtx_unlock(rt_mtx);
472 return route == hint ? EHOSTDOWN : EHOSTUNREACH;
473 }
474 }
475
476 lck_mtx_unlock(rt_mtx);
477 }
478
479 *out_route = route;
480 return 0;
481 }
482
483 errno_t
484 arp_lookup_ip(
485 ifnet_t ifp,
486 const struct sockaddr_in *net_dest,
487 struct sockaddr_dl *ll_dest,
488 size_t ll_dest_len,
489 route_t hint,
490 mbuf_t packet)
491 {
492 route_t route = NULL;
493 errno_t result = 0;
494 struct sockaddr_dl *gateway;
495 struct llinfo_arp *llinfo;
496 struct timeval timenow;
497
498 if (net_dest->sin_family != AF_INET)
499 return EAFNOSUPPORT;
500
501 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
502 return ENETDOWN;
503
504 /*
505 * If we were given a route, verify the route and grab the gateway
506 */
507 if (hint) {
508 result = arp_route_to_gateway_route((const struct sockaddr*)net_dest,
509 hint, &route);
510 if (result != 0)
511 return result;
512 }
513
514 if (packet->m_flags & M_BCAST) {
515 u_long broadcast_len;
516 bzero(ll_dest, ll_dest_len);
517 result = ifnet_llbroadcast_copy_bytes(ifp, LLADDR(ll_dest), ll_dest_len
518 - offsetof(struct sockaddr_dl,
519 sdl_data), &broadcast_len);
520 if (result != 0) {
521 return result;
522 }
523
524 ll_dest->sdl_alen = broadcast_len;
525 ll_dest->sdl_family = AF_LINK;
526 ll_dest->sdl_len = sizeof(struct sockaddr_dl);
527
528 return 0;
529 }
530 if (packet->m_flags & M_MCAST) {
531 return dlil_resolve_multi(ifp, (const struct sockaddr*)net_dest,
532 (struct sockaddr*)ll_dest, ll_dest_len);
533 }
534
535 lck_mtx_lock(rt_mtx);
536
537 /*
538 * If we didn't find a route, or the route doesn't have
539 * link layer information, trigger the creation of the
540 * route and link layer information.
541 */
542 if (route == NULL || route->rt_llinfo == NULL)
543 result = arp_lookup_route(&net_dest->sin_addr, 1, 0, &route);
544
545 if (result || route == NULL || route->rt_llinfo == NULL) {
546 char tmp[MAX_IPv4_STR_LEN];
547 lck_mtx_unlock(rt_mtx);
548 if (log_arp_warnings)
549 log(LOG_DEBUG, "arpresolve: can't allocate llinfo for %s\n",
550 inet_ntop(AF_INET, &net_dest->sin_addr, tmp, sizeof(tmp)));
551 return result;
552 }
553
554 /*
555 * Now that we have the right route, is it filled in?
556 */
557 gateway = SDL(route->rt_gateway);
558 getmicrotime(&timenow);
559 if ((route->rt_rmx.rmx_expire == 0 || route->rt_rmx.rmx_expire > timenow.tv_sec) &&
560 gateway != NULL && gateway->sdl_family == AF_LINK && gateway->sdl_alen != 0) {
561 bcopy(gateway, ll_dest, MIN(gateway->sdl_len, ll_dest_len));
562 lck_mtx_unlock(rt_mtx);
563 return 0;
564 }
565
566 /*
567 * Route wasn't complete/valid. We need to arp.
568 */
569 if (ifp->if_flags & IFF_NOARP) {
570 lck_mtx_unlock(rt_mtx);
571 return ENOTSUP;
572 }
573
574 llinfo = (struct llinfo_arp*)route->rt_llinfo;
575 if (packet) {
576 if (llinfo->la_hold) {
577 m_freem(llinfo->la_hold);
578 }
579 llinfo->la_hold = packet;
580 }
581
582 if (route->rt_rmx.rmx_expire) {
583 route->rt_flags &= ~RTF_REJECT;
584 if (llinfo->la_asked == 0 || route->rt_rmx.rmx_expire != timenow.tv_sec) {
585 route->rt_rmx.rmx_expire = timenow.tv_sec;
586 if (llinfo->la_asked++ < arp_maxtries) {
587 lck_mtx_unlock(rt_mtx);
588 dlil_send_arp(ifp, ARPOP_REQUEST, NULL, route->rt_ifa->ifa_addr,
589 NULL, (const struct sockaddr*)net_dest);
590 return EJUSTRETURN;
591 }
592 else {
593 route->rt_flags |= RTF_REJECT;
594 route->rt_rmx.rmx_expire += arpt_down;
595 llinfo->la_asked = 0;
596 llinfo->la_hold = 0;
597 lck_mtx_unlock(rt_mtx);
598 return EHOSTUNREACH;
599 }
600 }
601 }
602 lck_mtx_unlock(rt_mtx);
603
604 return EJUSTRETURN;
605 }
606
607 errno_t
608 arp_ip_handle_input(
609 ifnet_t ifp,
610 u_short arpop,
611 const struct sockaddr_dl *sender_hw,
612 const struct sockaddr_in *sender_ip,
613 const struct sockaddr_in *target_ip)
614 {
615 char ipv4str[MAX_IPv4_STR_LEN];
616 struct sockaddr_dl *gateway;
617 struct in_ifaddr *ia;
618 struct in_ifaddr *best_ia = NULL;
619 route_t route = NULL;
620 char buf[3 * MAX_HW_LEN]; // enough for MAX_HW_LEN byte hw address
621 struct llinfo_arp *llinfo;
622 struct timeval timenow;
623 errno_t error;
624
625 /* Do not respond to requests for 0.0.0.0 */
626 if (target_ip->sin_addr.s_addr == 0 && arpop == ARPOP_REQUEST) {
627 return 0;
628 }
629
630 /*
631 * Determine if this ARP is for us
632 */
633 lck_mtx_lock(rt_mtx);
634 for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) {
635 /* do_bridge should be tested here for bridging */
636 if (ia->ia_ifp == ifp) {
637 best_ia = ia;
638 if (target_ip->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr ||
639 sender_ip->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr) {
640 break;
641 }
642 }
643 }
644
645 /* If we don't have an IP address on this interface, ignore the packet */
646 if (best_ia == 0) {
647 lck_mtx_unlock(rt_mtx);
648 return 0;
649 }
650
651 /* If the packet is from this interface, ignore the packet */
652 if (!bcmp(CONST_LLADDR(sender_hw), ifnet_lladdr(ifp), sender_hw->sdl_len)) {
653 lck_mtx_unlock(rt_mtx);
654 return 0;
655 }
656
657 /* Check for a conflict */
658 if (sender_ip->sin_addr.s_addr == best_ia->ia_addr.sin_addr.s_addr) {
659 struct kev_msg ev_msg;
660 struct kev_in_collision *in_collision;
661 u_char storage[sizeof(struct kev_in_collision) + MAX_HW_LEN];
662 in_collision = (struct kev_in_collision*)storage;
663 log(LOG_ERR, "%s%d duplicate IP address %s sent from address %s\n",
664 ifp->if_name, ifp->if_unit,
665 inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str, sizeof(ipv4str)),
666 sdl_addr_to_hex(sender_hw, buf, sizeof(buf)));
667
668 /* Send a kernel event so anyone can learn of the conflict */
669 in_collision->link_data.if_family = ifp->if_family;
670 in_collision->link_data.if_unit = ifp->if_unit;
671 strncpy(&in_collision->link_data.if_name[0], ifp->if_name, IFNAMSIZ);
672 in_collision->ia_ipaddr = sender_ip->sin_addr;
673 in_collision->hw_len = sender_hw->sdl_alen < MAX_HW_LEN ? sender_hw->sdl_alen : MAX_HW_LEN;
674 bcopy(CONST_LLADDR(sender_hw), (caddr_t)in_collision->hw_addr, in_collision->hw_len);
675 ev_msg.vendor_code = KEV_VENDOR_APPLE;
676 ev_msg.kev_class = KEV_NETWORK_CLASS;
677 ev_msg.kev_subclass = KEV_INET_SUBCLASS;
678 ev_msg.event_code = KEV_INET_ARPCOLLISION;
679 ev_msg.dv[0].data_ptr = in_collision;
680 ev_msg.dv[0].data_length = sizeof(struct kev_in_collision) + in_collision->hw_len;
681 ev_msg.dv[1].data_length = 0;
682 kev_post_msg(&ev_msg);
683
684 goto respond;
685 }
686
687 /*
688 * Look up the routing entry. If it doesn't exist and we are the
689 * target, go ahead and create one.
690 */
691 error = arp_lookup_route(&sender_ip->sin_addr, (target_ip->sin_addr.s_addr ==
692 best_ia->ia_addr.sin_addr.s_addr), 0, &route);
693
694 if (error || route == 0 || route->rt_gateway == 0) {
695 if (ipv4_ll_arp_aware != 0 && IN_LINKLOCAL(target_ip->sin_addr.s_addr)
696 && arpop == ARPOP_REQUEST && sender_ip->sin_addr.s_addr == 0) {
697 /*
698 * Verify this ARP probe doesn't conflict with an IPv4LL we know of
699 * on another interface.
700 */
701 error = arp_lookup_route(&target_ip->sin_addr, 0, 0, &route);
702 if (error == 0 && route && route->rt_gateway) {
703 gateway = SDL(route->rt_gateway);
704 if (route->rt_ifp != ifp &&
705 (gateway->sdl_alen != sender_hw->sdl_alen ||
706 bcmp(CONST_LLADDR(gateway), CONST_LLADDR(sender_hw),
707 gateway->sdl_alen) != 0)) {
708 /*
709 * A node is probing for an IPv4LL we know exists on a
710 * different interface. We respond with a conflicting probe
711 * to force the new device to pick a different IPv4LL
712 * address.
713 */
714 log(LOG_INFO,
715 "arp: %s on %s%d sent probe for %s, already on %s%d\n",
716 sdl_addr_to_hex(sender_hw, buf, sizeof(buf)),
717 ifp->if_name, ifp->if_unit,
718 inet_ntop(AF_INET, &target_ip->sin_addr, ipv4str,
719 sizeof(ipv4str)),
720 route->rt_ifp->if_name, route->rt_ifp->if_unit);
721 log(LOG_INFO,
722 "arp: sending conflicting probe to %s on %s%d\n",
723 sdl_addr_to_hex(sender_hw, buf, sizeof(buf)),
724 ifp->if_name, ifp->if_unit);
725
726 /*
727 * Send a conservative unicast "ARP probe".
728 * This should force the other device to pick a new number.
729 * This will not force the device to pick a new number if the device
730 * has already assigned that number.
731 * This will not imply to the device that we own that address.
732 */
733 dlil_send_arp_internal(ifp, ARPOP_REQUEST,
734 (struct sockaddr_dl*)TAILQ_FIRST(&ifp->if_addrhead)->ifa_addr,
735 (const struct sockaddr*)sender_ip, sender_hw,
736 (const struct sockaddr*)target_ip);
737 }
738 }
739 }
740
741 goto respond;
742 }
743
744 gateway = SDL(route->rt_gateway);
745 if (route->rt_ifp != ifp) {
746 if (!IN_LINKLOCAL(sender_ip->sin_addr.s_addr) || (ifp->if_eflags & IFEF_ARPLL) == 0) {
747 if (log_arp_warnings)
748 log(LOG_ERR, "arp: %s is on %s%d but got reply from %s on %s%d\n",
749 inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str,
750 sizeof(ipv4str)),
751 route->rt_ifp->if_name,
752 route->rt_ifp->if_unit,
753 sdl_addr_to_hex(sender_hw, buf, sizeof(buf)),
754 ifp->if_name, ifp->if_unit);
755 goto respond;
756 }
757 else {
758 /* Don't change a permanent address */
759 if (route->rt_rmx.rmx_expire == 0) {
760 goto respond;
761 }
762
763 /*
764 * Don't change the cloned route away from the parent's interface
765 * if the address did resolve.
766 */
767 if (gateway->sdl_alen != 0 && route->rt_parent &&
768 route->rt_parent->rt_ifp == route->rt_ifp) {
769 goto respond;
770 }
771
772 /* Change the interface when the existing route is on */
773 route->rt_ifp = ifp;
774 rtsetifa(route, &best_ia->ia_ifa);
775 gateway->sdl_index = ifp->if_index;
776 }
777 }
778
779 if (gateway->sdl_alen && bcmp(LLADDR(gateway), CONST_LLADDR(sender_hw), gateway->sdl_alen)) {
780 if (route->rt_rmx.rmx_expire) {
781 char buf2[3 * MAX_HW_LEN];
782 log(LOG_INFO, "arp: %s moved from %s to %s on %s%d\n",
783 inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str,
784 sizeof(ipv4str)),
785 sdl_addr_to_hex(gateway, buf, sizeof(buf)),
786 sdl_addr_to_hex(sender_hw, buf2, sizeof(buf2)), ifp->if_name,
787 ifp->if_unit);
788 }
789 else {
790 log(LOG_ERR,
791 "arp: %s attempts to modify permanent entry for %s on %s%d\n",
792 sdl_addr_to_hex(sender_hw, buf, sizeof(buf)),
793 inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str,
794 sizeof(ipv4str)),
795 ifp->if_name, ifp->if_unit);
796 goto respond;
797 }
798 }
799
800 /* Copy the sender hardware address in to the route's gateway address */
801 gateway->sdl_alen = sender_hw->sdl_alen;
802 bcopy(CONST_LLADDR(sender_hw), LLADDR(gateway), gateway->sdl_alen);
803
804 /* Update the expire time for the route and clear the reject flag */
805 getmicrotime(&timenow);
806 if (route->rt_rmx.rmx_expire)
807 route->rt_rmx.rmx_expire = timenow.tv_sec + arpt_keep;
808 route->rt_flags &= ~RTF_REJECT;
809
810 /* update the llinfo, send a queued packet if there is one */
811 llinfo = (struct llinfo_arp*)route->rt_llinfo;
812 llinfo->la_asked = 0;
813 if (llinfo->la_hold) {
814 struct mbuf *m0;
815 m0 = llinfo->la_hold;
816 llinfo->la_hold = 0;
817
818 /* Should we a reference on the route first? */
819 lck_mtx_unlock(rt_mtx);
820 dlil_output(ifp, PF_INET, m0, (caddr_t)route, rt_key(route), 0);
821 lck_mtx_lock(rt_mtx);
822 }
823
824 respond:
825 if (arpop != ARPOP_REQUEST) {
826 lck_mtx_unlock(rt_mtx);
827 return 0;
828 }
829
830 /* If we are not the target, check if we should proxy */
831 if (target_ip->sin_addr.s_addr != best_ia->ia_addr.sin_addr.s_addr) {
832
833 /* Find a proxy route */
834 error = arp_lookup_route(&target_ip->sin_addr, 0, SIN_PROXY, &route);
835 if (error || route == NULL) {
836
837 /* We don't have a route entry indicating we should use proxy */
838 /* If we aren't supposed to proxy all, we are done */
839 if (!arp_proxyall) {
840 lck_mtx_unlock(rt_mtx);
841 return 0;
842 }
843
844 /* See if we have a route to the target ip before we proxy it */
845 route = rtalloc1_locked((const struct sockaddr*)target_ip, 0, 0);
846 if (!route) {
847 lck_mtx_unlock(rt_mtx);
848 return 0;
849 }
850
851 /*
852 * Don't proxy for hosts already on the same interface.
853 */
854 if (route->rt_ifp == ifp) {
855 rtfree_locked(route);
856 lck_mtx_unlock(rt_mtx);
857 return 0;
858 }
859 }
860 }
861 lck_mtx_unlock(rt_mtx);
862
863 dlil_send_arp(ifp, ARPOP_REPLY, NULL, (const struct sockaddr*)target_ip,
864 sender_hw, (const struct sockaddr*)sender_ip);
865
866 return 0;
867 }
868
869 void
870 arp_ifinit(
871 struct ifnet *ifp,
872 struct ifaddr *ifa)
873 {
874 ifa->ifa_rtrequest = arp_rtrequest;
875 ifa->ifa_flags |= RTF_CLONING;
876 dlil_send_arp(ifp, ARPOP_REQUEST, NULL, ifa->ifa_addr, NULL, ifa->ifa_addr);
877 }