]>
Commit | Line | Data |
---|---|---|
91447636 | 1 | /* |
c910b4d9 | 2 | * Copyright (c) 2004-2008 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> | |
b0d623f7 | 80 | #include <kern/zalloc.h> |
91447636 | 81 | |
b0d623f7 | 82 | #define SA(p) ((struct sockaddr *)(p)) |
91447636 A |
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 | |
b0d623f7 | 86 | #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) |
91447636 A |
87 | |
88 | static const size_t MAX_HW_LEN = 10; | |
89 | ||
90 | SYSCTL_DECL(_net_link_ether); | |
2d21ac55 | 91 | SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW|CTLFLAG_LOCKED, 0, ""); |
91447636 A |
92 | |
93 | /* timer values */ | |
94 | static int arpt_prune = (5*60*1); /* walk list every 5 minutes */ | |
95 | static int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */ | |
96 | static int arpt_down = 20; /* once declared down, don't send for 20 sec */ | |
97 | ||
98 | /* Apple Hardware SUM16 checksuming */ | |
99 | int apple_hwcksum_tx = 1; | |
100 | int apple_hwcksum_rx = 1; | |
101 | ||
102 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, prune_intvl, CTLFLAG_RW, | |
103 | &arpt_prune, 0, ""); | |
104 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_RW, | |
105 | &arpt_keep, 0, ""); | |
106 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, host_down_time, CTLFLAG_RW, | |
107 | &arpt_down, 0, ""); | |
108 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, apple_hwcksum_tx, CTLFLAG_RW, | |
109 | &apple_hwcksum_tx, 0, ""); | |
110 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, apple_hwcksum_rx, CTLFLAG_RW, | |
111 | &apple_hwcksum_rx, 0, ""); | |
112 | ||
113 | struct llinfo_arp { | |
b0d623f7 A |
114 | /* |
115 | * The following are protected by rnh_lock | |
116 | */ | |
91447636 A |
117 | LIST_ENTRY(llinfo_arp) la_le; |
118 | struct rtentry *la_rt; | |
b0d623f7 A |
119 | /* |
120 | * The following are protected by rt_lock | |
121 | */ | |
91447636 | 122 | struct mbuf *la_hold; /* last packet until resolved/timeout */ |
b0d623f7 | 123 | int32_t la_asked; /* last time we QUERIED for this addr */ |
91447636 A |
124 | }; |
125 | ||
b0d623f7 A |
126 | /* |
127 | * Synchronization notes: | |
128 | * | |
129 | * The global list of ARP entries are stored in llinfo_arp; an entry | |
130 | * gets inserted into the list when the route is created and gets | |
131 | * removed from the list when it is deleted; this is done as part | |
132 | * of RTM_ADD/RTM_RESOLVE/RTM_DELETE in arp_rtrequest(). | |
133 | * | |
134 | * Because rnh_lock and rt_lock for the entry are held during those | |
135 | * operations, the same locks (and thus lock ordering) must be used | |
136 | * elsewhere to access the relevant data structure fields: | |
137 | * | |
138 | * la_le.{le_next,le_prev}, la_rt | |
139 | * | |
140 | * - Routing lock (rnh_lock) | |
141 | * | |
142 | * la_hold, la_asked | |
143 | * | |
144 | * - Routing entry lock (rt_lock) | |
145 | * | |
146 | * Due to the dependency on rt_lock, llinfo_arp has the same lifetime | |
147 | * as the route entry itself. When a route is deleted (RTM_DELETE), | |
148 | * it is simply removed from the global list but the memory is not | |
149 | * freed until the route itself is freed. | |
150 | */ | |
91447636 A |
151 | static LIST_HEAD(, llinfo_arp) llinfo_arp; |
152 | ||
153 | static int arp_inuse, arp_allocated; | |
154 | ||
155 | static int arp_maxtries = 5; | |
156 | static int useloopback = 1; /* use loopback interface for local traffic */ | |
157 | static int arp_proxyall = 0; | |
2d21ac55 | 158 | static int arp_sendllconflict = 0; |
91447636 A |
159 | |
160 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_RW, | |
161 | &arp_maxtries, 0, ""); | |
162 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, useloopback, CTLFLAG_RW, | |
163 | &useloopback, 0, ""); | |
164 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW, | |
165 | &arp_proxyall, 0, ""); | |
2d21ac55 A |
166 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, sendllconflict, CTLFLAG_RW, |
167 | &arp_sendllconflict, 0, ""); | |
91447636 A |
168 | |
169 | static int log_arp_warnings = 0; | |
170 | ||
171 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_warnings, CTLFLAG_RW, | |
172 | &log_arp_warnings, 0, | |
173 | "log arp warning messages"); | |
174 | ||
2d21ac55 A |
175 | static int keep_announcements = 1; |
176 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, keep_announcements, CTLFLAG_RW, | |
177 | &keep_announcements, 0, | |
178 | "keep arp announcements"); | |
179 | ||
180 | static int send_conflicting_probes = 1; | |
181 | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, send_conflicting_probes, CTLFLAG_RW, | |
182 | &send_conflicting_probes, 0, | |
183 | "send conflicting link-local arp probes"); | |
184 | ||
b0d623f7 A |
185 | static errno_t arp_lookup_route(const struct in_addr *, int, |
186 | int, route_t *, unsigned int); | |
187 | static void arptimer(void *); | |
188 | static struct llinfo_arp *arp_llinfo_alloc(void); | |
189 | static void arp_llinfo_free(void *); | |
190 | ||
91447636 A |
191 | extern u_int32_t ipv4_ll_arp_aware; |
192 | ||
b0d623f7 A |
193 | static int arpinit_done; |
194 | ||
195 | static struct zone *llinfo_arp_zone; | |
196 | #define LLINFO_ARP_ZONE_MAX 256 /* maximum elements in zone */ | |
197 | #define LLINFO_ARP_ZONE_NAME "llinfo_arp" /* name for zone */ | |
198 | ||
199 | void | |
200 | arp_init(void) | |
201 | { | |
202 | if (arpinit_done) { | |
203 | log(LOG_NOTICE, "arp_init called more than once (ignored)\n"); | |
204 | return; | |
205 | } | |
206 | ||
207 | LIST_INIT(&llinfo_arp); | |
208 | ||
209 | llinfo_arp_zone = zinit(sizeof (struct llinfo_arp), | |
210 | LLINFO_ARP_ZONE_MAX * sizeof (struct llinfo_arp), 0, | |
211 | LLINFO_ARP_ZONE_NAME); | |
212 | if (llinfo_arp_zone == NULL) | |
213 | panic("%s: failed allocating llinfo_arp_zone", __func__); | |
214 | ||
215 | zone_change(llinfo_arp_zone, Z_EXPAND, TRUE); | |
216 | ||
217 | arpinit_done = 1; | |
218 | ||
219 | /* start timer */ | |
220 | timeout(arptimer, (caddr_t)0, hz); | |
221 | } | |
222 | ||
223 | static struct llinfo_arp * | |
224 | arp_llinfo_alloc(void) | |
225 | { | |
226 | return (zalloc(llinfo_arp_zone)); | |
227 | } | |
228 | ||
229 | static void | |
230 | arp_llinfo_free(void *arg) | |
231 | { | |
232 | struct llinfo_arp *la = arg; | |
233 | ||
234 | if (la->la_le.le_next != NULL || la->la_le.le_prev != NULL) { | |
235 | panic("%s: trying to free %p when it is in use", __func__, la); | |
236 | /* NOTREACHED */ | |
237 | } | |
238 | ||
239 | /* Just in case there's anything there, free it */ | |
240 | if (la->la_hold != NULL) { | |
241 | m_freem(la->la_hold); | |
242 | la->la_hold = NULL; | |
243 | } | |
244 | ||
245 | zfree(llinfo_arp_zone, la); | |
246 | } | |
247 | ||
91447636 A |
248 | /* |
249 | * Free an arp entry. | |
250 | */ | |
251 | static void | |
b0d623f7 | 252 | arptfree(struct llinfo_arp *la) |
91447636 A |
253 | { |
254 | struct rtentry *rt = la->la_rt; | |
255 | struct sockaddr_dl *sdl; | |
b0d623f7 A |
256 | |
257 | lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); | |
258 | RT_LOCK_ASSERT_HELD(rt); | |
259 | ||
91447636 A |
260 | if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) && |
261 | sdl->sdl_family == AF_LINK) { | |
262 | sdl->sdl_alen = 0; | |
263 | la->la_asked = 0; | |
264 | rt->rt_flags &= ~RTF_REJECT; | |
b0d623f7 A |
265 | RT_UNLOCK(rt); |
266 | } else { | |
267 | /* | |
268 | * Safe to drop rt_lock and use rt_key, since holding | |
269 | * rnh_lock here prevents another thread from calling | |
270 | * rt_setgate() on this route. | |
271 | */ | |
272 | RT_UNLOCK(rt); | |
273 | rtrequest_locked(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), | |
274 | 0, NULL); | |
91447636 | 275 | } |
91447636 A |
276 | } |
277 | ||
278 | /* | |
279 | * Timeout routine. Age arp_tab entries periodically. | |
280 | */ | |
281 | /* ARGSUSED */ | |
282 | static void | |
2d21ac55 | 283 | arptimer(void *ignored_arg) |
91447636 | 284 | { |
2d21ac55 | 285 | #pragma unused (ignored_arg) |
0c530ab8 | 286 | struct llinfo_arp *la, *ola; |
91447636 A |
287 | struct timeval timenow; |
288 | ||
b0d623f7 | 289 | lck_mtx_lock(rnh_lock); |
0c530ab8 | 290 | la = llinfo_arp.lh_first; |
91447636 A |
291 | getmicrotime(&timenow); |
292 | while ((ola = la) != 0) { | |
293 | struct rtentry *rt = la->la_rt; | |
294 | la = la->la_le.le_next; | |
b0d623f7 | 295 | RT_LOCK(rt); |
91447636 A |
296 | if (rt->rt_expire && rt->rt_expire <= timenow.tv_sec) |
297 | arptfree(ola); /* timer has expired, clear */ | |
b0d623f7 A |
298 | else |
299 | RT_UNLOCK(rt); | |
91447636 | 300 | } |
b0d623f7 | 301 | lck_mtx_unlock(rnh_lock); |
91447636 A |
302 | timeout(arptimer, (caddr_t)0, arpt_prune * hz); |
303 | } | |
304 | ||
305 | /* | |
306 | * Parallel to llc_rtrequest. | |
307 | */ | |
308 | static void | |
309 | arp_rtrequest( | |
310 | int req, | |
311 | struct rtentry *rt, | |
312 | __unused struct sockaddr *sa) | |
313 | { | |
314 | struct sockaddr *gate = rt->rt_gateway; | |
b0d623f7 | 315 | struct llinfo_arp *la = rt->rt_llinfo; |
91447636 | 316 | static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK, 0, 0, 0, 0, 0, {0}}; |
91447636 A |
317 | struct timeval timenow; |
318 | ||
319 | if (!arpinit_done) { | |
b0d623f7 A |
320 | panic("%s: ARP has not been initialized", __func__); |
321 | /* NOTREACHED */ | |
91447636 | 322 | } |
b0d623f7 A |
323 | lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); |
324 | RT_LOCK_ASSERT_HELD(rt); | |
91447636 A |
325 | |
326 | if (rt->rt_flags & RTF_GATEWAY) | |
327 | return; | |
328 | getmicrotime(&timenow); | |
329 | switch (req) { | |
330 | ||
331 | case RTM_ADD: | |
332 | /* | |
333 | * XXX: If this is a manually added route to interface | |
334 | * such as older version of routed or gated might provide, | |
335 | * restore cloning bit. | |
336 | */ | |
337 | if ((rt->rt_flags & RTF_HOST) == 0 && | |
338 | SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) | |
339 | rt->rt_flags |= RTF_CLONING; | |
340 | if (rt->rt_flags & RTF_CLONING) { | |
341 | /* | |
342 | * Case 1: This route should come from a route to iface. | |
343 | */ | |
b0d623f7 A |
344 | if (rt_setgate(rt, rt_key(rt), |
345 | (struct sockaddr *)&null_sdl) == 0) { | |
346 | gate = rt->rt_gateway; | |
347 | SDL(gate)->sdl_type = rt->rt_ifp->if_type; | |
348 | SDL(gate)->sdl_index = rt->rt_ifp->if_index; | |
349 | /* | |
350 | * In case we're called before 1.0 sec. | |
351 | * has elapsed. | |
352 | */ | |
353 | rt->rt_expire = MAX(timenow.tv_sec, 1); | |
354 | } | |
91447636 A |
355 | break; |
356 | } | |
357 | /* Announce a new entry if requested. */ | |
b0d623f7 A |
358 | if (rt->rt_flags & RTF_ANNOUNCE) { |
359 | RT_UNLOCK(rt); | |
360 | dlil_send_arp(rt->rt_ifp, ARPOP_REQUEST, | |
361 | SDL(gate), rt_key(rt), NULL, rt_key(rt)); | |
362 | RT_LOCK(rt); | |
363 | } | |
91447636 A |
364 | /*FALLTHROUGH*/ |
365 | case RTM_RESOLVE: | |
366 | if (gate->sa_family != AF_LINK || | |
367 | gate->sa_len < sizeof(null_sdl)) { | |
b0d623f7 | 368 | if (log_arp_warnings) |
91447636 A |
369 | log(LOG_DEBUG, "arp_rtrequest: bad gateway value\n"); |
370 | break; | |
371 | } | |
372 | SDL(gate)->sdl_type = rt->rt_ifp->if_type; | |
373 | SDL(gate)->sdl_index = rt->rt_ifp->if_index; | |
374 | if (la != 0) | |
375 | break; /* This happens on a route change */ | |
376 | /* | |
377 | * Case 2: This route may come from cloning, or a manual route | |
378 | * add with a LL address. | |
379 | */ | |
b0d623f7 A |
380 | rt->rt_llinfo = la = arp_llinfo_alloc(); |
381 | if (la == NULL) { | |
382 | if (log_arp_warnings) | |
383 | log(LOG_DEBUG, "%s: malloc failed\n", __func__); | |
91447636 A |
384 | break; |
385 | } | |
b0d623f7 A |
386 | rt->rt_llinfo_free = arp_llinfo_free; |
387 | ||
91447636 A |
388 | arp_inuse++, arp_allocated++; |
389 | Bzero(la, sizeof(*la)); | |
390 | la->la_rt = rt; | |
391 | rt->rt_flags |= RTF_LLINFO; | |
392 | LIST_INSERT_HEAD(&llinfo_arp, la, la_le); | |
393 | ||
91447636 A |
394 | /* |
395 | * This keeps the multicast addresses from showing up | |
396 | * in `arp -a' listings as unresolved. It's not actually | |
397 | * functional. Then the same for broadcast. | |
398 | */ | |
399 | if (IN_MULTICAST(ntohl(SIN(rt_key(rt))->sin_addr.s_addr))) { | |
b0d623f7 A |
400 | RT_UNLOCK(rt); |
401 | dlil_resolve_multi(rt->rt_ifp, rt_key(rt), gate, | |
402 | sizeof(struct sockaddr_dl)); | |
403 | RT_LOCK(rt); | |
91447636 A |
404 | rt->rt_expire = 0; |
405 | } | |
406 | else if (in_broadcast(SIN(rt_key(rt))->sin_addr, rt->rt_ifp)) { | |
407 | struct sockaddr_dl *gate_ll = SDL(gate); | |
408 | size_t broadcast_len; | |
b0d623f7 A |
409 | ifnet_llbroadcast_copy_bytes(rt->rt_ifp, |
410 | LLADDR(gate_ll), sizeof(gate_ll->sdl_data), | |
411 | &broadcast_len); | |
91447636 A |
412 | gate_ll->sdl_alen = broadcast_len; |
413 | gate_ll->sdl_family = AF_LINK; | |
414 | gate_ll->sdl_len = sizeof(struct sockaddr_dl); | |
593a1d5f A |
415 | /* In case we're called before 1.0 sec. has elapsed */ |
416 | rt->rt_expire = MAX(timenow.tv_sec, 1); | |
91447636 | 417 | } |
91447636 A |
418 | |
419 | if (SIN(rt_key(rt))->sin_addr.s_addr == | |
420 | (IA_SIN(rt->rt_ifa))->sin_addr.s_addr) { | |
421 | /* | |
422 | * This test used to be | |
423 | * if (loif.if_flags & IFF_UP) | |
424 | * It allowed local traffic to be forced | |
425 | * through the hardware by configuring the loopback down. | |
426 | * However, it causes problems during network configuration | |
427 | * for boards that can't receive packets they send. | |
428 | * It is now necessary to clear "useloopback" and remove | |
429 | * the route to force traffic out to the hardware. | |
430 | */ | |
431 | rt->rt_expire = 0; | |
432 | ifnet_lladdr_copy_bytes(rt->rt_ifp, LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6); | |
433 | if (useloopback) | |
2d21ac55 | 434 | rt->rt_ifp = lo_ifp; |
91447636 A |
435 | |
436 | } | |
437 | break; | |
438 | ||
439 | case RTM_DELETE: | |
440 | if (la == 0) | |
441 | break; | |
442 | arp_inuse--; | |
b0d623f7 A |
443 | /* |
444 | * Unchain it but defer the actual freeing until the route | |
445 | * itself is to be freed. rt->rt_llinfo still points to | |
446 | * llinfo_arp, and likewise, la->la_rt still points to this | |
447 | * route entry, except that RTF_LLINFO is now cleared. | |
448 | */ | |
91447636 | 449 | LIST_REMOVE(la, la_le); |
b0d623f7 A |
450 | la->la_le.le_next = NULL; |
451 | la->la_le.le_prev = NULL; | |
91447636 | 452 | rt->rt_flags &= ~RTF_LLINFO; |
b0d623f7 | 453 | if (la->la_hold != NULL) |
91447636 | 454 | m_freem(la->la_hold); |
91447636 | 455 | la->la_hold = NULL; |
91447636 A |
456 | } |
457 | } | |
458 | ||
459 | /* | |
460 | * convert hardware address to hex string for logging errors. | |
461 | */ | |
462 | static const char * | |
463 | sdl_addr_to_hex(const struct sockaddr_dl *sdl, char * orig_buf, int buflen) | |
464 | { | |
465 | char * buf = orig_buf; | |
466 | int i; | |
b0d623f7 | 467 | const u_char * lladdr = (u_char *)(size_t)sdl->sdl_data; |
91447636 A |
468 | int maxbytes = buflen / 3; |
469 | ||
470 | if (maxbytes > sdl->sdl_alen) { | |
471 | maxbytes = sdl->sdl_alen; | |
472 | } | |
473 | *buf = '\0'; | |
474 | for (i = 0; i < maxbytes; i++) { | |
475 | snprintf(buf, 3, "%02x", lladdr[i]); | |
476 | buf += 2; | |
477 | *buf = (i == maxbytes - 1) ? '\0' : ':'; | |
478 | buf++; | |
479 | } | |
480 | return (orig_buf); | |
481 | } | |
482 | ||
483 | /* | |
484 | * arp_lookup_route will lookup the route for a given address. | |
485 | * | |
b0d623f7 A |
486 | * The address must be for a host on a local network on this interface. |
487 | * If the returned route is non-NULL, the route is locked and the caller | |
488 | * is responsible for unlocking it and releasing its reference. | |
91447636 A |
489 | */ |
490 | static errno_t | |
b0d623f7 A |
491 | arp_lookup_route(const struct in_addr *addr, int create, int proxy, |
492 | route_t *route, unsigned int ifscope) | |
91447636 A |
493 | { |
494 | struct sockaddr_inarp sin = {sizeof(sin), AF_INET, 0, {0}, {0}, 0, 0}; | |
2d21ac55 | 495 | const char *why = NULL; |
91447636 | 496 | errno_t error = 0; |
b0d623f7 A |
497 | route_t rt; |
498 | ||
499 | *route = NULL; | |
91447636 A |
500 | |
501 | sin.sin_addr.s_addr = addr->s_addr; | |
502 | sin.sin_other = proxy ? SIN_PROXY : 0; | |
c910b4d9 | 503 | |
b0d623f7 A |
504 | rt = rtalloc1_scoped((struct sockaddr*)&sin, create, 0, ifscope); |
505 | if (rt == NULL) | |
506 | return (ENETUNREACH); | |
507 | ||
508 | RT_LOCK(rt); | |
509 | ||
510 | if (rt->rt_flags & RTF_GATEWAY) { | |
91447636 | 511 | why = "host is not on local network"; |
91447636 | 512 | error = ENETUNREACH; |
b0d623f7 | 513 | } else if (!(rt->rt_flags & RTF_LLINFO)) { |
91447636 | 514 | why = "could not allocate llinfo"; |
91447636 | 515 | error = ENOMEM; |
b0d623f7 | 516 | } else if (rt->rt_gateway->sa_family != AF_LINK) { |
91447636 | 517 | why = "gateway route is not ours"; |
91447636 A |
518 | error = EPROTONOSUPPORT; |
519 | } | |
b0d623f7 A |
520 | |
521 | if (error != 0) { | |
522 | if (create && log_arp_warnings) { | |
523 | char tmp[MAX_IPv4_STR_LEN]; | |
524 | log(LOG_DEBUG, "arplookup link#%d %s failed: %s\n", | |
525 | ifscope, inet_ntop(AF_INET, addr, tmp, | |
526 | sizeof (tmp)), why); | |
527 | } | |
528 | ||
529 | /* | |
530 | * If there are no references to this route, and it is | |
531 | * a cloned route, and not static, and ARP had created | |
532 | * the route, then purge it from the routing table as | |
533 | * it is probably bogus. | |
534 | */ | |
535 | if (rt->rt_refcnt == 1 && | |
536 | (rt->rt_flags & (RTF_WASCLONED | RTF_STATIC)) == | |
537 | RTF_WASCLONED) { | |
538 | /* | |
539 | * Prevent another thread from modiying rt_key, | |
540 | * rt_gateway via rt_setgate() after rt_lock is | |
541 | * dropped by marking the route as defunct. | |
542 | */ | |
543 | rt->rt_flags |= RTF_CONDEMNED; | |
544 | RT_UNLOCK(rt); | |
545 | rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, | |
546 | rt_mask(rt), rt->rt_flags, 0); | |
547 | rtfree(rt); | |
548 | } else { | |
549 | RT_REMREF_LOCKED(rt); | |
550 | RT_UNLOCK(rt); | |
551 | } | |
552 | return (error); | |
91447636 | 553 | } |
91447636 | 554 | |
b0d623f7 A |
555 | /* |
556 | * Caller releases reference and does RT_UNLOCK(rt). | |
557 | */ | |
558 | *route = rt; | |
559 | return (0); | |
560 | } | |
91447636 | 561 | |
91447636 A |
562 | /* |
563 | * arp_route_to_gateway_route will find the gateway route for a given route. | |
564 | * | |
565 | * If the route is down, look the route up again. | |
566 | * If the route goes through a gateway, get the route to the gateway. | |
567 | * If the gateway route is down, look it up again. | |
568 | * If the route is set to reject, verify it hasn't expired. | |
b0d623f7 A |
569 | * |
570 | * If the returned route is non-NULL, the caller is responsible for | |
571 | * releasing the reference and unlocking the route. | |
91447636 | 572 | */ |
b0d623f7 | 573 | #define senderr(e) { error = (e); goto bad; } |
91447636 | 574 | __private_extern__ errno_t |
b0d623f7 A |
575 | arp_route_to_gateway_route(const struct sockaddr *net_dest, route_t hint0, |
576 | route_t *out_route) | |
91447636 | 577 | { |
2d21ac55 | 578 | struct timeval timenow; |
b0d623f7 A |
579 | route_t rt = hint0, hint = hint0; |
580 | errno_t error = 0; | |
581 | ||
91447636 | 582 | *out_route = NULL; |
b0d623f7 A |
583 | |
584 | /* | |
585 | * Next hop determination. Because we may involve the gateway route | |
586 | * in addition to the original route, locking is rather complicated. | |
587 | * The general concept is that regardless of whether the route points | |
588 | * to the original route or to the gateway route, this routine takes | |
589 | * an extra reference on such a route. This extra reference will be | |
590 | * released at the end. | |
591 | * | |
592 | * Care must be taken to ensure that the "hint0" route never gets freed | |
593 | * via rtfree(), since the caller may have stored it inside a struct | |
594 | * route with a reference held for that placeholder. | |
595 | */ | |
596 | if (rt != NULL) { | |
597 | unsigned int ifindex; | |
598 | ||
599 | RT_LOCK_SPIN(rt); | |
600 | ifindex = rt->rt_ifp->if_index; | |
601 | RT_ADDREF_LOCKED(rt); | |
602 | if (!(rt->rt_flags & RTF_UP)) { | |
603 | RT_REMREF_LOCKED(rt); | |
604 | RT_UNLOCK(rt); | |
91447636 | 605 | /* route is down, find a new one */ |
b0d623f7 A |
606 | hint = rt = rtalloc1_scoped((struct sockaddr *) |
607 | (size_t)net_dest, 1, 0, ifindex); | |
608 | if (hint != NULL) { | |
609 | RT_LOCK_SPIN(rt); | |
610 | ifindex = rt->rt_ifp->if_index; | |
611 | } else { | |
612 | senderr(EHOSTUNREACH); | |
91447636 A |
613 | } |
614 | } | |
b0d623f7 A |
615 | |
616 | /* | |
617 | * We have a reference to "rt" by now; it will either | |
618 | * be released or freed at the end of this routine. | |
619 | */ | |
620 | RT_LOCK_ASSERT_HELD(rt); | |
621 | if (rt->rt_flags & RTF_GATEWAY) { | |
622 | struct rtentry *gwrt = rt->rt_gwroute; | |
623 | struct sockaddr_in gw; | |
624 | ||
625 | /* If there's no gateway rt, look it up */ | |
626 | if (gwrt == NULL) { | |
627 | gw = *((struct sockaddr_in *)rt->rt_gateway); | |
628 | RT_UNLOCK(rt); | |
629 | goto lookup; | |
630 | } | |
631 | /* Become a regular mutex */ | |
632 | RT_CONVERT_LOCK(rt); | |
633 | ||
91447636 | 634 | /* |
b0d623f7 A |
635 | * Take gwrt's lock while holding route's lock; |
636 | * this is okay since gwrt never points back | |
637 | * to "rt", so no lock ordering issues. | |
91447636 | 638 | */ |
b0d623f7 A |
639 | RT_LOCK_SPIN(gwrt); |
640 | if (!(gwrt->rt_flags & RTF_UP)) { | |
641 | struct rtentry *ogwrt; | |
642 | ||
643 | rt->rt_gwroute = NULL; | |
644 | RT_UNLOCK(gwrt); | |
645 | gw = *((struct sockaddr_in *)rt->rt_gateway); | |
646 | RT_UNLOCK(rt); | |
647 | rtfree(gwrt); | |
648 | lookup: | |
649 | gwrt = rtalloc1_scoped( | |
650 | (struct sockaddr *)&gw, 1, 0, ifindex); | |
651 | ||
652 | RT_LOCK(rt); | |
653 | /* | |
654 | * Bail out if the route is down, no route | |
655 | * to gateway, circular route, or if the | |
656 | * gateway portion of "rt" has changed. | |
657 | */ | |
658 | if (!(rt->rt_flags & RTF_UP) || | |
659 | gwrt == NULL || gwrt == rt || | |
660 | !equal(SA(&gw), rt->rt_gateway)) { | |
661 | if (gwrt == rt) { | |
662 | RT_REMREF_LOCKED(gwrt); | |
663 | gwrt = NULL; | |
664 | } | |
665 | RT_UNLOCK(rt); | |
666 | if (gwrt != NULL) | |
667 | rtfree(gwrt); | |
668 | senderr(EHOSTUNREACH); | |
91447636 | 669 | } |
b0d623f7 A |
670 | |
671 | /* Remove any existing gwrt */ | |
672 | ogwrt = rt->rt_gwroute; | |
673 | if ((rt->rt_gwroute = gwrt) != NULL) | |
674 | RT_ADDREF(gwrt); | |
675 | ||
676 | /* Clean up "rt" now while we can */ | |
677 | if (rt == hint0) { | |
678 | RT_REMREF_LOCKED(rt); | |
679 | RT_UNLOCK(rt); | |
680 | } else { | |
681 | RT_UNLOCK(rt); | |
682 | rtfree(rt); | |
683 | } | |
684 | rt = gwrt; | |
685 | /* Now free the replaced gwrt */ | |
686 | if (ogwrt != NULL) | |
687 | rtfree(ogwrt); | |
688 | /* If still no route to gateway, bail out */ | |
689 | if (rt == NULL) | |
690 | senderr(EHOSTUNREACH); | |
691 | } else { | |
692 | RT_ADDREF_LOCKED(gwrt); | |
693 | RT_UNLOCK(gwrt); | |
694 | /* Clean up "rt" now while we can */ | |
695 | if (rt == hint0) { | |
696 | RT_REMREF_LOCKED(rt); | |
697 | RT_UNLOCK(rt); | |
698 | } else { | |
699 | RT_UNLOCK(rt); | |
700 | rtfree(rt); | |
701 | } | |
702 | rt = gwrt; | |
703 | } | |
704 | ||
705 | /* rt == gwrt; if it is now down, give up */ | |
706 | RT_LOCK_SPIN(rt); | |
707 | if (!(rt->rt_flags & RTF_UP)) { | |
708 | RT_UNLOCK(rt); | |
709 | senderr(EHOSTUNREACH); | |
91447636 | 710 | } |
91447636 | 711 | } |
b0d623f7 A |
712 | |
713 | if (rt->rt_flags & RTF_REJECT) { | |
91447636 | 714 | getmicrotime(&timenow); |
b0d623f7 A |
715 | if (rt->rt_rmx.rmx_expire == 0 || |
716 | timenow.tv_sec < rt->rt_rmx.rmx_expire) { | |
717 | RT_UNLOCK(rt); | |
718 | senderr(rt == hint ? EHOSTDOWN : EHOSTUNREACH); | |
91447636 A |
719 | } |
720 | } | |
b0d623f7 A |
721 | |
722 | /* Become a regular mutex */ | |
723 | RT_CONVERT_LOCK(rt); | |
724 | ||
725 | /* Caller is responsible for cleaning up "rt" */ | |
726 | *out_route = rt; | |
91447636 | 727 | } |
b0d623f7 A |
728 | return (0); |
729 | ||
730 | bad: | |
731 | /* Clean up route (either it is "rt" or "gwrt") */ | |
732 | if (rt != NULL) { | |
733 | RT_LOCK_SPIN(rt); | |
734 | if (rt == hint0) { | |
735 | RT_REMREF_LOCKED(rt); | |
736 | RT_UNLOCK(rt); | |
737 | } else { | |
738 | RT_UNLOCK(rt); | |
739 | rtfree(rt); | |
740 | } | |
741 | } | |
742 | return (error); | |
91447636 | 743 | } |
b0d623f7 | 744 | #undef senderr |
91447636 | 745 | |
b0d623f7 A |
746 | /* |
747 | * This is the ARP pre-output routine; care must be taken to ensure that | |
748 | * the "hint" route never gets freed via rtfree(), since the caller may | |
749 | * have stored it inside a struct route with a reference held for that | |
750 | * placeholder. | |
751 | */ | |
91447636 | 752 | errno_t |
b0d623f7 A |
753 | arp_lookup_ip(ifnet_t ifp, const struct sockaddr_in *net_dest, |
754 | struct sockaddr_dl *ll_dest, size_t ll_dest_len, route_t hint, | |
755 | mbuf_t packet) | |
91447636 | 756 | { |
b0d623f7 | 757 | route_t route = NULL; /* output route */ |
91447636 A |
758 | errno_t result = 0; |
759 | struct sockaddr_dl *gateway; | |
760 | struct llinfo_arp *llinfo; | |
761 | struct timeval timenow; | |
b0d623f7 | 762 | |
91447636 | 763 | if (net_dest->sin_family != AF_INET) |
b0d623f7 A |
764 | return (EAFNOSUPPORT); |
765 | ||
91447636 | 766 | if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) |
b0d623f7 A |
767 | return (ENETDOWN); |
768 | ||
91447636 A |
769 | /* |
770 | * If we were given a route, verify the route and grab the gateway | |
771 | */ | |
b0d623f7 A |
772 | if (hint != NULL) { |
773 | /* | |
774 | * Callee holds a reference on the route and returns | |
775 | * with the route entry locked, upon success. | |
776 | */ | |
777 | result = arp_route_to_gateway_route((const struct sockaddr*) | |
778 | net_dest, hint, &route); | |
91447636 | 779 | if (result != 0) |
b0d623f7 A |
780 | return (result); |
781 | if (route != NULL) | |
782 | RT_LOCK_ASSERT_HELD(route); | |
91447636 | 783 | } |
b0d623f7 | 784 | |
91447636 | 785 | if (packet->m_flags & M_BCAST) { |
b0d623f7 | 786 | size_t broadcast_len; |
91447636 | 787 | bzero(ll_dest, ll_dest_len); |
b0d623f7 A |
788 | result = ifnet_llbroadcast_copy_bytes(ifp, LLADDR(ll_dest), |
789 | ll_dest_len - offsetof(struct sockaddr_dl, sdl_data), | |
790 | &broadcast_len); | |
791 | if (result == 0) { | |
792 | ll_dest->sdl_alen = broadcast_len; | |
793 | ll_dest->sdl_family = AF_LINK; | |
794 | ll_dest->sdl_len = sizeof(struct sockaddr_dl); | |
91447636 | 795 | } |
b0d623f7 | 796 | goto release; |
91447636 A |
797 | } |
798 | if (packet->m_flags & M_MCAST) { | |
b0d623f7 A |
799 | if (route != NULL) |
800 | RT_UNLOCK(route); | |
801 | result = dlil_resolve_multi(ifp, | |
802 | (const struct sockaddr*)net_dest, | |
803 | (struct sockaddr*)ll_dest, ll_dest_len); | |
804 | if (route != NULL) | |
805 | RT_LOCK(route); | |
806 | goto release; | |
91447636 | 807 | } |
b0d623f7 | 808 | |
91447636 A |
809 | /* |
810 | * If we didn't find a route, or the route doesn't have | |
811 | * link layer information, trigger the creation of the | |
812 | * route and link layer information. | |
813 | */ | |
b0d623f7 A |
814 | if (route == NULL || route->rt_llinfo == NULL) { |
815 | /* Clean up now while we can */ | |
816 | if (route != NULL) { | |
817 | if (route == hint) { | |
818 | RT_REMREF_LOCKED(route); | |
819 | RT_UNLOCK(route); | |
820 | } else { | |
821 | RT_UNLOCK(route); | |
822 | rtfree(route); | |
823 | } | |
824 | } | |
825 | /* | |
826 | * Callee holds a reference on the route and returns | |
827 | * with the route entry locked, upon success. | |
828 | */ | |
c910b4d9 A |
829 | result = arp_lookup_route(&net_dest->sin_addr, 1, 0, &route, |
830 | ifp->if_index); | |
b0d623f7 A |
831 | if (result == 0) |
832 | RT_LOCK_ASSERT_HELD(route); | |
833 | } | |
834 | ||
91447636 A |
835 | if (result || route == NULL || route->rt_llinfo == NULL) { |
836 | char tmp[MAX_IPv4_STR_LEN]; | |
b0d623f7 A |
837 | |
838 | /* In case result is 0 but no route, return an error */ | |
839 | if (result == 0) | |
840 | result = EHOSTUNREACH; | |
841 | ||
842 | if (log_arp_warnings && | |
843 | route != NULL && route->rt_llinfo == NULL) | |
844 | log(LOG_DEBUG, "arpresolve: can't allocate llinfo " | |
845 | "for %s\n", inet_ntop(AF_INET, &net_dest->sin_addr, | |
846 | tmp, sizeof(tmp))); | |
847 | goto release; | |
91447636 | 848 | } |
b0d623f7 | 849 | |
91447636 A |
850 | /* |
851 | * Now that we have the right route, is it filled in? | |
852 | */ | |
853 | gateway = SDL(route->rt_gateway); | |
854 | getmicrotime(&timenow); | |
b0d623f7 A |
855 | if ((route->rt_rmx.rmx_expire == 0 || |
856 | route->rt_rmx.rmx_expire > timenow.tv_sec) && gateway != NULL && | |
857 | gateway->sdl_family == AF_LINK && gateway->sdl_alen != 0) { | |
91447636 | 858 | bcopy(gateway, ll_dest, MIN(gateway->sdl_len, ll_dest_len)); |
b0d623f7 A |
859 | result = 0; |
860 | goto release; | |
91447636 | 861 | } |
b0d623f7 A |
862 | |
863 | if (ifp->if_flags & IFF_NOARP) { | |
864 | result = ENOTSUP; | |
865 | goto release; | |
866 | } | |
867 | ||
91447636 A |
868 | /* |
869 | * Route wasn't complete/valid. We need to arp. | |
870 | */ | |
b0d623f7 A |
871 | llinfo = route->rt_llinfo; |
872 | if (packet != NULL) { | |
873 | if (llinfo->la_hold != NULL) | |
91447636 | 874 | m_freem(llinfo->la_hold); |
91447636 A |
875 | llinfo->la_hold = packet; |
876 | } | |
b0d623f7 | 877 | |
91447636 A |
878 | if (route->rt_rmx.rmx_expire) { |
879 | route->rt_flags &= ~RTF_REJECT; | |
b0d623f7 A |
880 | if (llinfo->la_asked == 0 || |
881 | route->rt_rmx.rmx_expire != timenow.tv_sec) { | |
91447636 A |
882 | route->rt_rmx.rmx_expire = timenow.tv_sec; |
883 | if (llinfo->la_asked++ < arp_maxtries) { | |
b0d623f7 A |
884 | struct ifaddr *rt_ifa = route->rt_ifa; |
885 | ifaref(rt_ifa); | |
886 | RT_UNLOCK(route); | |
887 | dlil_send_arp(ifp, ARPOP_REQUEST, NULL, | |
888 | rt_ifa->ifa_addr, NULL, | |
889 | (const struct sockaddr*)net_dest); | |
890 | ifafree(rt_ifa); | |
891 | RT_LOCK(route); | |
892 | result = EJUSTRETURN; | |
893 | goto release; | |
894 | } else { | |
91447636 A |
895 | route->rt_flags |= RTF_REJECT; |
896 | route->rt_rmx.rmx_expire += arpt_down; | |
897 | llinfo->la_asked = 0; | |
2d21ac55 | 898 | llinfo->la_hold = NULL; |
b0d623f7 A |
899 | result = EHOSTUNREACH; |
900 | goto release; | |
91447636 A |
901 | } |
902 | } | |
903 | } | |
b0d623f7 A |
904 | |
905 | /* The packet is now held inside la_hold (can "packet" be NULL?) */ | |
906 | result = EJUSTRETURN; | |
907 | ||
908 | release: | |
909 | if (route != NULL) { | |
910 | if (route == hint) { | |
911 | RT_REMREF_LOCKED(route); | |
912 | RT_UNLOCK(route); | |
913 | } else { | |
914 | RT_UNLOCK(route); | |
915 | rtfree(route); | |
916 | } | |
917 | } | |
918 | return (result); | |
91447636 A |
919 | } |
920 | ||
921 | errno_t | |
922 | arp_ip_handle_input( | |
923 | ifnet_t ifp, | |
924 | u_short arpop, | |
925 | const struct sockaddr_dl *sender_hw, | |
926 | const struct sockaddr_in *sender_ip, | |
927 | const struct sockaddr_in *target_ip) | |
928 | { | |
929 | char ipv4str[MAX_IPv4_STR_LEN]; | |
b0d623f7 A |
930 | struct sockaddr_dl proxied; |
931 | struct sockaddr_dl *gateway, *target_hw = NULL; | |
932 | struct ifaddr *ifa; | |
91447636 A |
933 | struct in_ifaddr *ia; |
934 | struct in_ifaddr *best_ia = NULL; | |
935 | route_t route = NULL; | |
936 | char buf[3 * MAX_HW_LEN]; // enough for MAX_HW_LEN byte hw address | |
937 | struct llinfo_arp *llinfo; | |
91447636 | 938 | errno_t error; |
2d21ac55 | 939 | int created_announcement = 0; |
b0d623f7 | 940 | |
91447636 | 941 | /* Do not respond to requests for 0.0.0.0 */ |
b0d623f7 A |
942 | if (target_ip->sin_addr.s_addr == 0 && arpop == ARPOP_REQUEST) |
943 | goto done; | |
944 | ||
91447636 A |
945 | /* |
946 | * Determine if this ARP is for us | |
947 | */ | |
b0d623f7 A |
948 | lck_rw_lock_shared(in_ifaddr_rwlock); |
949 | TAILQ_FOREACH(ia, INADDR_HASH(target_ip->sin_addr.s_addr), ia_hash) { | |
91447636 | 950 | /* do_bridge should be tested here for bridging */ |
b0d623f7 A |
951 | if (ia->ia_ifp == ifp && |
952 | ia->ia_addr.sin_addr.s_addr == target_ip->sin_addr.s_addr) { | |
91447636 | 953 | best_ia = ia; |
b0d623f7 A |
954 | ifaref(&best_ia->ia_ifa); |
955 | lck_rw_done(in_ifaddr_rwlock); | |
956 | goto match; | |
91447636 A |
957 | } |
958 | } | |
b0d623f7 A |
959 | |
960 | TAILQ_FOREACH(ia, INADDR_HASH(sender_ip->sin_addr.s_addr), ia_hash) { | |
961 | /* do_bridge should be tested here for bridging */ | |
962 | if (ia->ia_ifp == ifp && | |
963 | ia->ia_addr.sin_addr.s_addr == sender_ip->sin_addr.s_addr) { | |
964 | best_ia = ia; | |
965 | ifaref(&best_ia->ia_ifa); | |
966 | lck_rw_done(in_ifaddr_rwlock); | |
967 | goto match; | |
968 | } | |
91447636 | 969 | } |
b0d623f7 A |
970 | lck_rw_done(in_ifaddr_rwlock); |
971 | ||
972 | /* | |
973 | * No match, use the first inet address on the receive interface | |
974 | * as a dummy address for the rest of the function; we may be | |
975 | * proxying for another address. | |
976 | */ | |
977 | ifnet_lock_shared(ifp); | |
978 | TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |
979 | if (ifa->ifa_addr->sa_family != AF_INET) | |
980 | continue; | |
981 | best_ia = (struct in_ifaddr *)ifa; | |
982 | ifaref(&best_ia->ia_ifa); | |
983 | break; | |
984 | } | |
985 | ifnet_lock_done(ifp); | |
986 | ||
987 | /* If we don't have an IP address on this interface, ignore the packet */ | |
988 | if (best_ia == NULL) | |
989 | goto done; | |
990 | ||
991 | match: | |
91447636 A |
992 | /* If the packet is from this interface, ignore the packet */ |
993 | if (!bcmp(CONST_LLADDR(sender_hw), ifnet_lladdr(ifp), sender_hw->sdl_len)) { | |
b0d623f7 | 994 | goto done; |
91447636 | 995 | } |
b0d623f7 | 996 | |
91447636 A |
997 | /* Check for a conflict */ |
998 | if (sender_ip->sin_addr.s_addr == best_ia->ia_addr.sin_addr.s_addr) { | |
999 | struct kev_msg ev_msg; | |
1000 | struct kev_in_collision *in_collision; | |
1001 | u_char storage[sizeof(struct kev_in_collision) + MAX_HW_LEN]; | |
1002 | in_collision = (struct kev_in_collision*)storage; | |
1003 | log(LOG_ERR, "%s%d duplicate IP address %s sent from address %s\n", | |
1004 | ifp->if_name, ifp->if_unit, | |
1005 | inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str, sizeof(ipv4str)), | |
1006 | sdl_addr_to_hex(sender_hw, buf, sizeof(buf))); | |
b0d623f7 | 1007 | |
91447636 A |
1008 | /* Send a kernel event so anyone can learn of the conflict */ |
1009 | in_collision->link_data.if_family = ifp->if_family; | |
1010 | in_collision->link_data.if_unit = ifp->if_unit; | |
1011 | strncpy(&in_collision->link_data.if_name[0], ifp->if_name, IFNAMSIZ); | |
1012 | in_collision->ia_ipaddr = sender_ip->sin_addr; | |
1013 | in_collision->hw_len = sender_hw->sdl_alen < MAX_HW_LEN ? sender_hw->sdl_alen : MAX_HW_LEN; | |
1014 | bcopy(CONST_LLADDR(sender_hw), (caddr_t)in_collision->hw_addr, in_collision->hw_len); | |
1015 | ev_msg.vendor_code = KEV_VENDOR_APPLE; | |
1016 | ev_msg.kev_class = KEV_NETWORK_CLASS; | |
1017 | ev_msg.kev_subclass = KEV_INET_SUBCLASS; | |
1018 | ev_msg.event_code = KEV_INET_ARPCOLLISION; | |
1019 | ev_msg.dv[0].data_ptr = in_collision; | |
1020 | ev_msg.dv[0].data_length = sizeof(struct kev_in_collision) + in_collision->hw_len; | |
1021 | ev_msg.dv[1].data_length = 0; | |
1022 | kev_post_msg(&ev_msg); | |
b0d623f7 | 1023 | |
91447636 A |
1024 | goto respond; |
1025 | } | |
b0d623f7 | 1026 | |
91447636 A |
1027 | /* |
1028 | * Look up the routing entry. If it doesn't exist and we are the | |
c910b4d9 | 1029 | * target, and the sender isn't 0.0.0.0, go ahead and create one. |
b0d623f7 A |
1030 | * Callee holds a reference on the route and returns with the route |
1031 | * entry locked, upon success. | |
91447636 | 1032 | */ |
c910b4d9 A |
1033 | error = arp_lookup_route(&sender_ip->sin_addr, |
1034 | (target_ip->sin_addr.s_addr == best_ia->ia_addr.sin_addr.s_addr && | |
1035 | sender_ip->sin_addr.s_addr != 0), 0, &route, ifp->if_index); | |
b0d623f7 A |
1036 | |
1037 | if (error == 0) | |
1038 | RT_LOCK_ASSERT_HELD(route); | |
1039 | ||
91447636 | 1040 | if (error || route == 0 || route->rt_gateway == 0) { |
2d21ac55 A |
1041 | if (arpop != ARPOP_REQUEST) { |
1042 | goto respond; | |
1043 | } | |
1044 | if (arp_sendllconflict | |
1045 | && send_conflicting_probes != 0 | |
1046 | && (ifp->if_eflags & IFEF_ARPLL) != 0 | |
1047 | && IN_LINKLOCAL(ntohl(target_ip->sin_addr.s_addr)) | |
1048 | && sender_ip->sin_addr.s_addr == 0) { | |
91447636 A |
1049 | /* |
1050 | * Verify this ARP probe doesn't conflict with an IPv4LL we know of | |
1051 | * on another interface. | |
1052 | */ | |
b0d623f7 A |
1053 | if (route != NULL) { |
1054 | RT_REMREF_LOCKED(route); | |
1055 | RT_UNLOCK(route); | |
1056 | route = NULL; | |
1057 | } | |
1058 | /* | |
1059 | * Callee holds a reference on the route and returns | |
1060 | * with the route entry locked, upon success. | |
1061 | */ | |
c910b4d9 A |
1062 | error = arp_lookup_route(&target_ip->sin_addr, 0, 0, |
1063 | &route, ifp->if_index); | |
b0d623f7 A |
1064 | |
1065 | if (error == 0) | |
1066 | RT_LOCK_ASSERT_HELD(route); | |
1067 | ||
91447636 A |
1068 | if (error == 0 && route && route->rt_gateway) { |
1069 | gateway = SDL(route->rt_gateway); | |
2d21ac55 A |
1070 | if (route->rt_ifp != ifp && gateway->sdl_alen != 0 |
1071 | && (gateway->sdl_alen != sender_hw->sdl_alen | |
1072 | || bcmp(CONST_LLADDR(gateway), CONST_LLADDR(sender_hw), | |
1073 | gateway->sdl_alen) != 0)) { | |
91447636 A |
1074 | /* |
1075 | * A node is probing for an IPv4LL we know exists on a | |
1076 | * different interface. We respond with a conflicting probe | |
1077 | * to force the new device to pick a different IPv4LL | |
1078 | * address. | |
1079 | */ | |
2d21ac55 A |
1080 | if (log_arp_warnings) { |
1081 | log(LOG_INFO, | |
91447636 A |
1082 | "arp: %s on %s%d sent probe for %s, already on %s%d\n", |
1083 | sdl_addr_to_hex(sender_hw, buf, sizeof(buf)), | |
1084 | ifp->if_name, ifp->if_unit, | |
1085 | inet_ntop(AF_INET, &target_ip->sin_addr, ipv4str, | |
1086 | sizeof(ipv4str)), | |
1087 | route->rt_ifp->if_name, route->rt_ifp->if_unit); | |
2d21ac55 | 1088 | log(LOG_INFO, |
91447636 A |
1089 | "arp: sending conflicting probe to %s on %s%d\n", |
1090 | sdl_addr_to_hex(sender_hw, buf, sizeof(buf)), | |
1091 | ifp->if_name, ifp->if_unit); | |
2d21ac55 | 1092 | } |
b0d623f7 A |
1093 | /* We're done with the route */ |
1094 | RT_REMREF_LOCKED(route); | |
1095 | RT_UNLOCK(route); | |
1096 | route = NULL; | |
91447636 A |
1097 | /* |
1098 | * Send a conservative unicast "ARP probe". | |
1099 | * This should force the other device to pick a new number. | |
1100 | * This will not force the device to pick a new number if the device | |
1101 | * has already assigned that number. | |
1102 | * This will not imply to the device that we own that address. | |
1103 | */ | |
b0d623f7 A |
1104 | ifnet_lock_shared(ifp); |
1105 | ifa = TAILQ_FIRST(&ifp->if_addrhead); | |
1106 | if (ifa != NULL) | |
1107 | ifaref(ifa); | |
1108 | ifnet_lock_done(ifp); | |
91447636 | 1109 | dlil_send_arp_internal(ifp, ARPOP_REQUEST, |
b0d623f7 | 1110 | ifa != NULL ? SDL(ifa->ifa_addr) : NULL, |
91447636 A |
1111 | (const struct sockaddr*)sender_ip, sender_hw, |
1112 | (const struct sockaddr*)target_ip); | |
b0d623f7 A |
1113 | if (ifa != NULL) { |
1114 | ifafree(ifa); | |
1115 | ifa = NULL; | |
1116 | } | |
91447636 A |
1117 | } |
1118 | } | |
2d21ac55 A |
1119 | goto respond; |
1120 | } else if (keep_announcements != 0 | |
1121 | && target_ip->sin_addr.s_addr == sender_ip->sin_addr.s_addr) { | |
1122 | /* don't create entry if link-local address and link-local is disabled */ | |
1123 | if (!IN_LINKLOCAL(ntohl(sender_ip->sin_addr.s_addr)) | |
1124 | || (ifp->if_eflags & IFEF_ARPLL) != 0) { | |
b0d623f7 A |
1125 | if (route != NULL) { |
1126 | RT_REMREF_LOCKED(route); | |
1127 | RT_UNLOCK(route); | |
1128 | route = NULL; | |
1129 | } | |
1130 | /* | |
1131 | * Callee holds a reference on the route and | |
1132 | * returns with the route entry locked, upon | |
1133 | * success. | |
1134 | */ | |
c910b4d9 A |
1135 | error = arp_lookup_route(&sender_ip->sin_addr, |
1136 | 1, 0, &route, ifp->if_index); | |
b0d623f7 A |
1137 | |
1138 | if (error == 0) | |
1139 | RT_LOCK_ASSERT_HELD(route); | |
1140 | ||
2d21ac55 A |
1141 | if (error == 0 && route != NULL && route->rt_gateway != NULL) { |
1142 | created_announcement = 1; | |
1143 | } | |
1144 | } | |
1145 | if (created_announcement == 0) { | |
1146 | goto respond; | |
1147 | } | |
1148 | } else { | |
1149 | goto respond; | |
91447636 | 1150 | } |
91447636 | 1151 | } |
b0d623f7 A |
1152 | |
1153 | RT_LOCK_ASSERT_HELD(route); | |
91447636 A |
1154 | gateway = SDL(route->rt_gateway); |
1155 | if (route->rt_ifp != ifp) { | |
2d21ac55 | 1156 | if (!IN_LINKLOCAL(ntohl(sender_ip->sin_addr.s_addr)) || (ifp->if_eflags & IFEF_ARPLL) == 0) { |
91447636 A |
1157 | if (log_arp_warnings) |
1158 | log(LOG_ERR, "arp: %s is on %s%d but got reply from %s on %s%d\n", | |
1159 | inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str, | |
1160 | sizeof(ipv4str)), | |
1161 | route->rt_ifp->if_name, | |
1162 | route->rt_ifp->if_unit, | |
1163 | sdl_addr_to_hex(sender_hw, buf, sizeof(buf)), | |
1164 | ifp->if_name, ifp->if_unit); | |
1165 | goto respond; | |
1166 | } | |
1167 | else { | |
1168 | /* Don't change a permanent address */ | |
1169 | if (route->rt_rmx.rmx_expire == 0) { | |
1170 | goto respond; | |
1171 | } | |
b0d623f7 A |
1172 | |
1173 | /* | |
1174 | * We're about to check and/or change the route's ifp | |
1175 | * and ifa, so do the lock dance: drop rt_lock, hold | |
1176 | * rnh_lock and re-hold rt_lock to avoid violating the | |
1177 | * lock ordering. We have an extra reference on the | |
1178 | * route, so it won't go away while we do this. | |
1179 | */ | |
1180 | RT_UNLOCK(route); | |
1181 | lck_mtx_lock(rnh_lock); | |
1182 | RT_LOCK(route); | |
91447636 | 1183 | /* |
b0d623f7 A |
1184 | * Don't change the cloned route away from the |
1185 | * parent's interface if the address did resolve | |
1186 | * or if the route is defunct. rt_ifp on both | |
1187 | * the parent and the clone can now be freely | |
1188 | * accessed now that we have acquired rnh_lock. | |
91447636 | 1189 | */ |
b0d623f7 A |
1190 | gateway = SDL(route->rt_gateway); |
1191 | if ((gateway->sdl_alen != 0 && route->rt_parent && | |
1192 | route->rt_parent->rt_ifp == route->rt_ifp) || | |
1193 | (route->rt_flags & RTF_CONDEMNED)) { | |
1194 | RT_REMREF_LOCKED(route); | |
1195 | RT_UNLOCK(route); | |
1196 | route = NULL; | |
1197 | lck_mtx_unlock(rnh_lock); | |
91447636 A |
1198 | goto respond; |
1199 | } | |
91447636 A |
1200 | /* Change the interface when the existing route is on */ |
1201 | route->rt_ifp = ifp; | |
1202 | rtsetifa(route, &best_ia->ia_ifa); | |
1203 | gateway->sdl_index = ifp->if_index; | |
b0d623f7 A |
1204 | RT_UNLOCK(route); |
1205 | lck_mtx_unlock(rnh_lock); | |
1206 | RT_LOCK(route); | |
1207 | /* Don't bother if the route is down */ | |
1208 | if (!(route->rt_flags & RTF_UP)) | |
1209 | goto respond; | |
1210 | /* Refresh gateway pointer */ | |
1211 | gateway = SDL(route->rt_gateway); | |
91447636 | 1212 | } |
b0d623f7 | 1213 | RT_LOCK_ASSERT_HELD(route); |
91447636 | 1214 | } |
b0d623f7 | 1215 | |
91447636 | 1216 | if (gateway->sdl_alen && bcmp(LLADDR(gateway), CONST_LLADDR(sender_hw), gateway->sdl_alen)) { |
2d21ac55 | 1217 | if (route->rt_rmx.rmx_expire && log_arp_warnings) { |
91447636 A |
1218 | char buf2[3 * MAX_HW_LEN]; |
1219 | log(LOG_INFO, "arp: %s moved from %s to %s on %s%d\n", | |
2d21ac55 A |
1220 | inet_ntop(AF_INET, &sender_ip->sin_addr, ipv4str, |
1221 | sizeof(ipv4str)), | |
1222 | sdl_addr_to_hex(gateway, buf, sizeof(buf)), | |
1223 | sdl_addr_to_hex(sender_hw, buf2, sizeof(buf2)), | |
1224 | ifp->if_name, ifp->if_unit); | |
91447636 | 1225 | } |
2d21ac55 A |
1226 | else if (route->rt_rmx.rmx_expire == 0) { |
1227 | if (log_arp_warnings) { | |
1228 | log(LOG_ERR, "arp: %s attempts to modify " | |
1229 | "permanent entry for %s on %s%d\n", | |
1230 | sdl_addr_to_hex(sender_hw, buf, | |
1231 | sizeof(buf)), | |
1232 | inet_ntop(AF_INET, &sender_ip->sin_addr, | |
1233 | ipv4str, sizeof(ipv4str)), | |
1234 | ifp->if_name, ifp->if_unit); | |
1235 | } | |
91447636 A |
1236 | goto respond; |
1237 | } | |
1238 | } | |
b0d623f7 | 1239 | |
91447636 A |
1240 | /* Copy the sender hardware address in to the route's gateway address */ |
1241 | gateway->sdl_alen = sender_hw->sdl_alen; | |
1242 | bcopy(CONST_LLADDR(sender_hw), LLADDR(gateway), gateway->sdl_alen); | |
b0d623f7 | 1243 | |
91447636 | 1244 | /* Update the expire time for the route and clear the reject flag */ |
2d21ac55 A |
1245 | if (route->rt_rmx.rmx_expire) { |
1246 | struct timeval timenow; | |
1247 | ||
1248 | getmicrotime(&timenow); | |
91447636 | 1249 | route->rt_rmx.rmx_expire = timenow.tv_sec + arpt_keep; |
2d21ac55 | 1250 | } |
91447636 | 1251 | route->rt_flags &= ~RTF_REJECT; |
b0d623f7 | 1252 | |
91447636 | 1253 | /* update the llinfo, send a queued packet if there is one */ |
b0d623f7 | 1254 | llinfo = route->rt_llinfo; |
91447636 A |
1255 | llinfo->la_asked = 0; |
1256 | if (llinfo->la_hold) { | |
1257 | struct mbuf *m0; | |
1258 | m0 = llinfo->la_hold; | |
1259 | llinfo->la_hold = 0; | |
b0d623f7 A |
1260 | |
1261 | RT_UNLOCK(route); | |
91447636 | 1262 | dlil_output(ifp, PF_INET, m0, (caddr_t)route, rt_key(route), 0); |
b0d623f7 A |
1263 | RT_REMREF(route); |
1264 | route = NULL; | |
91447636 | 1265 | } |
b0d623f7 | 1266 | |
91447636 | 1267 | respond: |
b0d623f7 A |
1268 | if (route != NULL) { |
1269 | RT_REMREF_LOCKED(route); | |
1270 | RT_UNLOCK(route); | |
1271 | route = NULL; | |
91447636 | 1272 | } |
b0d623f7 A |
1273 | |
1274 | if (arpop != ARPOP_REQUEST) | |
1275 | goto done; | |
1276 | ||
91447636 A |
1277 | /* If we are not the target, check if we should proxy */ |
1278 | if (target_ip->sin_addr.s_addr != best_ia->ia_addr.sin_addr.s_addr) { | |
b0d623f7 A |
1279 | /* |
1280 | * Find a proxy route; callee holds a reference on the | |
1281 | * route and returns with the route entry locked, upon | |
1282 | * success. | |
1283 | */ | |
c910b4d9 A |
1284 | error = arp_lookup_route(&target_ip->sin_addr, 0, SIN_PROXY, |
1285 | &route, ifp->if_index); | |
b0d623f7 A |
1286 | |
1287 | if (error == 0) { | |
1288 | RT_LOCK_ASSERT_HELD(route); | |
1289 | proxied = *SDL(route->rt_gateway); | |
1290 | target_hw = &proxied; | |
1291 | } else { | |
1292 | /* | |
1293 | * We don't have a route entry indicating we should | |
1294 | * use proxy. If we aren't supposed to proxy all, | |
1295 | * we are done. | |
1296 | */ | |
1297 | if (!arp_proxyall) | |
1298 | goto done; | |
1299 | ||
1300 | /* | |
1301 | * See if we have a route to the target ip before | |
1302 | * we proxy it. | |
1303 | */ | |
1304 | route = rtalloc1_scoped((struct sockaddr *) | |
1305 | (size_t)target_ip, 0, 0, ifp->if_index); | |
1306 | if (!route) | |
1307 | goto done; | |
1308 | ||
91447636 A |
1309 | /* |
1310 | * Don't proxy for hosts already on the same interface. | |
1311 | */ | |
b0d623f7 | 1312 | RT_LOCK(route); |
91447636 | 1313 | if (route->rt_ifp == ifp) { |
b0d623f7 A |
1314 | RT_UNLOCK(route); |
1315 | rtfree(route); | |
1316 | goto done; | |
91447636 A |
1317 | } |
1318 | } | |
b0d623f7 A |
1319 | RT_REMREF_LOCKED(route); |
1320 | RT_UNLOCK(route); | |
91447636 | 1321 | } |
b0d623f7 A |
1322 | |
1323 | dlil_send_arp(ifp, ARPOP_REPLY, | |
1324 | target_hw, (const struct sockaddr*)target_ip, | |
1325 | sender_hw, (const struct sockaddr*)sender_ip); | |
1326 | ||
1327 | done: | |
1328 | if (best_ia != NULL) | |
1329 | ifafree(&best_ia->ia_ifa); | |
91447636 A |
1330 | return 0; |
1331 | } | |
1332 | ||
1333 | void | |
1334 | arp_ifinit( | |
1335 | struct ifnet *ifp, | |
1336 | struct ifaddr *ifa) | |
1337 | { | |
1338 | ifa->ifa_rtrequest = arp_rtrequest; | |
1339 | ifa->ifa_flags |= RTF_CLONING; | |
1340 | dlil_send_arp(ifp, ARPOP_REQUEST, NULL, ifa->ifa_addr, NULL, ifa->ifa_addr); | |
1341 | } |