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