blob: 1db1e893f82bedbc7a9ba17c645cdac8ae69cbe4 [file] [log] [blame]
Samuel Tand7ed8512015-08-13 16:11:35 -07001/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/socket.h>
29#include <sys/types.h>
30
31#include <net/if.h>
32#include <netinet/in.h>
33#include <netinet/if_ether.h>
34
35#include <errno.h>
36#include <signal.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#define ELOOP_QUEUE 5
42#include "config.h"
43#include "arp.h"
44#include "if.h"
45#include "ipv4.h"
46#include "common.h"
47#include "dhcp.h"
48#include "dhcpcd.h"
49#include "eloop.h"
50#include "if.h"
51#include "if-options.h"
52#include "ipv4ll.h"
53
54#define ARP_LEN \
55 (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
56
57static ssize_t
Samuel Tanf20514b2015-08-13 16:24:02 -070058arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip,
59 const uint8_t *dest_hw_addr)
Samuel Tand7ed8512015-08-13 16:11:35 -070060{
61 uint8_t arp_buffer[ARP_LEN];
62 struct arphdr ar;
63 size_t len;
64 uint8_t *p;
65
66 ar.ar_hrd = htons(ifp->family);
67 ar.ar_pro = htons(ETHERTYPE_IP);
68 ar.ar_hln = ifp->hwlen;
69 ar.ar_pln = sizeof(sip);
70 ar.ar_op = htons(ARPOP_REQUEST);
71
72 p = arp_buffer;
73 len = 0;
74
75#define CHECK(fun, b, l) \
76 do { \
77 if (len + (l) > sizeof(arp_buffer)) \
78 goto eexit; \
79 fun(p, (b), (l)); \
80 p += (l); \
81 len += (l); \
82 } while (/* CONSTCOND */ 0)
83#define APPEND(b, l) CHECK(memcpy, b, l)
84#define ZERO(l) CHECK(memset, 0, l)
85
86 APPEND(&ar, sizeof(ar));
87 APPEND(ifp->hwaddr, ifp->hwlen);
88 APPEND(&sip, sizeof(sip));
Samuel Tanf20514b2015-08-13 16:24:02 -070089 if (dest_hw_addr)
90 APPEND(dest_hw_addr, ifp->hwlen);
91 else
92 ZERO(ifp->hwlen);
Samuel Tand7ed8512015-08-13 16:11:35 -070093 APPEND(&tip, sizeof(tip));
Samuel Tanf20514b2015-08-13 16:24:02 -070094 return if_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len,
95 dest_hw_addr);
Samuel Tand7ed8512015-08-13 16:11:35 -070096
97eexit:
98 errno = ENOBUFS;
99 return -1;
100}
101
102void
103arp_report_conflicted(const struct arp_state *astate, const struct arp_msg *amsg)
104{
105
106 if (amsg) {
107 char buf[HWADDR_LEN * 3];
108
109 logger(astate->iface->ctx, LOG_ERR,
110 "%s: hardware address %s claims %s",
111 astate->iface->name,
112 hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
113 buf, sizeof(buf)),
114 inet_ntoa(astate->failed));
115 } else
116 logger(astate->iface->ctx, LOG_ERR,
117 "%s: DAD detected %s",
118 astate->iface->name, inet_ntoa(astate->failed));
119}
120
121static void
122arp_packet(void *arg)
123{
124 struct interface *ifp = arg;
125 const struct interface *ifn;
126 uint8_t arp_buffer[ARP_LEN];
127 struct arphdr ar;
128 struct arp_msg arm;
129 ssize_t bytes;
130 struct dhcp_state *state;
131 struct arp_state *astate, *astaten;
132 unsigned char *hw_s, *hw_t;
133 int flags;
134
135 state = D_STATE(ifp);
Samuel Tan5158c9d2015-08-13 18:09:30 -0700136 state->failed.s_addr = 0;
Samuel Tand7ed8512015-08-13 16:11:35 -0700137 flags = 0;
138 while (!(flags & RAW_EOF)) {
139 bytes = if_readrawpacket(ifp, ETHERTYPE_ARP,
140 arp_buffer, sizeof(arp_buffer), &flags);
141 if (bytes == -1) {
142 logger(ifp->ctx, LOG_ERR,
143 "%s: arp if_readrawpacket: %m", ifp->name);
144 dhcp_close(ifp);
145 return;
146 }
147 /* We must have a full ARP header */
148 if ((size_t)bytes < sizeof(ar))
149 continue;
150 memcpy(&ar, arp_buffer, sizeof(ar));
151 /* Families must match */
152 if (ar.ar_hrd != htons(ifp->family))
153 continue;
154 /* Protocol must be IP. */
155 if (ar.ar_pro != htons(ETHERTYPE_IP))
156 continue;
157 if (ar.ar_pln != sizeof(arm.sip.s_addr))
158 continue;
159 /* Only these types are recognised */
160 if (ar.ar_op != htons(ARPOP_REPLY) &&
161 ar.ar_op != htons(ARPOP_REQUEST))
162 continue;
163
164 /* Get pointers to the hardware addreses */
165 hw_s = arp_buffer + sizeof(ar);
166 hw_t = hw_s + ar.ar_hln + ar.ar_pln;
167 /* Ensure we got all the data */
168 if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes)
169 continue;
170 /* Ignore messages from ourself */
171 TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
172 if (ar.ar_hln == ifn->hwlen &&
173 memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0)
174 break;
175 }
176 if (ifn)
177 continue;
178 /* Copy out the HW and IP addresses */
179 memcpy(&arm.sha, hw_s, ar.ar_hln);
180 memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln);
181 memcpy(&arm.tha, hw_t, ar.ar_hln);
182 memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
183
184 /* Run the conflicts */
185 TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
186 if (astate->conflicted_cb)
187 astate->conflicted_cb(astate, &arm);
188 }
189 }
190}
191
192static void
193arp_open(struct interface *ifp)
194{
195 struct dhcp_state *state;
196
197 state = D_STATE(ifp);
198 if (state->arp_fd == -1) {
199 state->arp_fd = if_openrawsocket(ifp, ETHERTYPE_ARP);
200 if (state->arp_fd == -1) {
201 logger(ifp->ctx, LOG_ERR, "%s: %s: %m",
202 __func__, ifp->name);
203 return;
204 }
205 eloop_event_add(ifp->ctx->eloop, state->arp_fd,
206 arp_packet, ifp, NULL, NULL);
207 }
208}
209
210static void
211arp_announced(void *arg)
212{
213 struct arp_state *astate = arg;
214
215 if (astate->announced_cb) {
216 astate->announced_cb(astate);
217 return;
218 }
219
220 /* Nothing more to do, so free us */
221 arp_free(astate);
222}
223
224static void
225arp_announce1(void *arg)
226{
227 struct arp_state *astate = arg;
228 struct interface *ifp = astate->iface;
229
230 if (++astate->claims < ANNOUNCE_NUM)
231 logger(ifp->ctx, LOG_DEBUG,
232 "%s: ARP announcing %s (%d of %d), "
233 "next in %d.0 seconds",
234 ifp->name, inet_ntoa(astate->addr),
235 astate->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
236 else
237 logger(ifp->ctx, LOG_DEBUG,
238 "%s: ARP announcing %s (%d of %d)",
239 ifp->name, inet_ntoa(astate->addr),
240 astate->claims, ANNOUNCE_NUM);
Samuel Tanf20514b2015-08-13 16:24:02 -0700241 if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr,
242 NULL) == -1)
Samuel Tand7ed8512015-08-13 16:11:35 -0700243 logger(ifp->ctx, LOG_ERR, "send_arp: %m");
244 eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
245 astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
246 astate);
247}
248
249void
250arp_announce(struct arp_state *astate)
251{
252
253 arp_open(astate->iface);
254 astate->claims = 0;
255 arp_announce1(astate);
256}
257
258static void
259arp_probed(void *arg)
260{
261 struct arp_state *astate = arg;
262
263 astate->probed_cb(astate);
264}
265
266static void
267arp_probe1(void *arg)
268{
269 struct arp_state *astate = arg;
270 struct interface *ifp = astate->iface;
271 struct timespec tv;
Samuel Tanf20514b2015-08-13 16:24:02 -0700272 uint8_t *dest_hwaddr = NULL;
Samuel Tand7ed8512015-08-13 16:11:35 -0700273
274 if (++astate->probes < PROBE_NUM) {
275 tv.tv_sec = PROBE_MIN;
276 tv.tv_nsec = (suseconds_t)arc4random_uniform(
277 (PROBE_MAX - PROBE_MIN) * NSEC_PER_SEC);
278 timespecnorm(&tv);
279 eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probe1, astate);
280 } else {
281 tv.tv_sec = ANNOUNCE_WAIT;
282 tv.tv_nsec = 0;
283 eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probed, astate);
284 }
Samuel Tan24aa62e2015-08-13 17:12:30 -0700285 logger(ifp->ctx, LOG_INFO,
Samuel Tand7ed8512015-08-13 16:11:35 -0700286 "%s: ARP probing %s (%d of %d), next in %0.1f seconds",
287 ifp->name, inet_ntoa(astate->addr),
288 astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
289 timespec_to_double(&tv));
Samuel Tanf20514b2015-08-13 16:24:02 -0700290 if (astate->dest_hwlen == ifp->hwlen)
291 dest_hwaddr = astate->dest_hwaddr;
Samuel Tan1c4088e2015-08-13 16:18:58 -0700292 if (arp_request(ifp, astate->src_addr.s_addr,
Samuel Tanf20514b2015-08-13 16:24:02 -0700293 astate->addr.s_addr, dest_hwaddr) == -1)
Samuel Tand7ed8512015-08-13 16:11:35 -0700294 logger(ifp->ctx, LOG_ERR, "send_arp: %m");
295}
296
297void
298arp_probe(struct arp_state *astate)
299{
300
301 arp_open(astate->iface);
302 astate->probes = 0;
303 logger(astate->iface->ctx, LOG_DEBUG, "%s: probing for %s",
304 astate->iface->name, inet_ntoa(astate->addr));
305 arp_probe1(astate);
306}
307
308static struct arp_state *
309arp_find(struct interface *ifp, const struct in_addr *addr)
310{
311 struct arp_state *astate;
312 struct dhcp_state *state;
313
314 state = D_STATE(ifp);
315 TAILQ_FOREACH(astate, &state->arp_states, next) {
316 if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
317 return astate;
318 }
319 errno = ESRCH;
320 return NULL;
321}
322
323struct arp_state *
324arp_new(struct interface *ifp, const struct in_addr *addr)
325{
326 struct arp_state *astate;
327 struct dhcp_state *state;
328
329 if (addr && (astate = arp_find(ifp, addr)))
330 return astate;
331
332 if ((astate = calloc(1, sizeof(*astate))) == NULL) {
333 logger(ifp->ctx, LOG_ERR, "%s: %s: %m", ifp->name, __func__);
334 return NULL;
335 }
336 state = D_STATE(ifp);
337 astate->iface = ifp;
338 if (addr)
339 astate->addr = *addr;
340 TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
341 return astate;
342}
343
344void
345arp_cancel(struct arp_state *astate)
346{
347
348 eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate);
349}
350
351void
352arp_free(struct arp_state *astate)
353{
354 struct dhcp_state *state;
355
356 if (astate) {
357 eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate);
358 state = D_STATE(astate->iface);
359 TAILQ_REMOVE(&state->arp_states, astate, next);
360 if (state->arp_ipv4ll == astate) {
361 ipv4ll_stop(astate->iface);
362 state->arp_ipv4ll = NULL;
363 }
364 free(astate);
365 }
366}
367
368void
369arp_free_but(struct arp_state *astate)
370{
371 struct arp_state *p, *n;
372 struct dhcp_state *state;
373
374 state = D_STATE(astate->iface);
375 TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) {
376 if (p != astate)
377 arp_free(p);
378 }
379}
380
381void
382arp_close(struct interface *ifp)
383{
384 struct dhcp_state *state = D_STATE(ifp);
385 struct arp_state *astate;
386
387 if (state == NULL)
388 return;
389
390 if (state->arp_fd != -1) {
391 eloop_event_delete(ifp->ctx->eloop, state->arp_fd, 0);
392 close(state->arp_fd);
393 state->arp_fd = -1;
394 }
395
396 while ((astate = TAILQ_FIRST(&state->arp_states))) {
397#ifndef __clang_analyzer__
398 /* clang guard needed for a more compex variant on this bug:
399 * http://llvm.org/bugs/show_bug.cgi?id=18222 */
400 arp_free(astate);
401#endif
402 }
403}
404
405void
406arp_handleifa(int cmd, struct interface *ifp, const struct in_addr *addr,
407 int flags)
408{
409#ifdef IN_IFF_DUPLICATED
410 struct dhcp_state *state = D_STATE(ifp);
411 struct arp_state *astate, *asn;
412
413 if (cmd != RTM_NEWADDR || (state = D_STATE(ifp)) == NULL)
414 return;
415
416 TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) {
417 if (astate->addr.s_addr == addr->s_addr) {
418 if (flags & IN_IFF_DUPLICATED) {
419 if (astate->conflicted_cb)
420 astate->conflicted_cb(astate, NULL);
421 } else if (!(flags & IN_IFF_NOTUSEABLE)) {
422 if (astate->probed_cb)
423 astate->probed_cb(astate);
424 }
425 }
426 }
427#else
428 UNUSED(cmd);
429 UNUSED(ifp);
430 UNUSED(addr);
431 UNUSED(flags);
432#endif
433}