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