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