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