source: rtems-libbsd/dhcpcd/arp.c @ f949b43

55-freebsd-126-freebsd-12
Last change on this file since f949b43 was f2ed769, checked in by Sebastian Huber <sebastian.huber@…>, on 01/30/14 at 12:29:46

DHCPCD(8): Import

Import DHCPCD(8) from:

http://roy.marples.name/projects/dhcpcd/

The upstream sources can be obtained via:

fossil clone http://roy.marples.name/projects/dhcpcd

The imported version is 2014-01-29 19:46:44 [6b209507bb].

  • Property mode set to 100644
File size: 9.3 KB
Line 
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2014 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 <errno.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <string.h>
32#include <syslog.h>
33#include <unistd.h>
34
35#include "config.h"
36#include "arp.h"
37#include "ipv4.h"
38#include "common.h"
39#include "dhcp.h"
40#include "dhcpcd.h"
41#include "eloop.h"
42#include "if-options.h"
43#include "ipv4ll.h"
44#include "net.h"
45
46#define ARP_LEN                                                               \
47        (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
48
49static int
50arp_send(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip)
51{
52        uint8_t arp_buffer[ARP_LEN];
53        struct arphdr ar;
54        size_t len;
55        uint8_t *p;
56
57        ar.ar_hrd = htons(ifp->family);
58        ar.ar_pro = htons(ETHERTYPE_IP);
59        ar.ar_hln = ifp->hwlen;
60        ar.ar_pln = sizeof(sip);
61        ar.ar_op = htons(op);
62
63        p = arp_buffer;
64        len = sizeof(arp_buffer);
65
66#define CHECK(fun, b, l)                                                \
67        do {                                                            \
68                if (len < (l))                                          \
69                        goto eexit;                                     \
70                fun(p, (b), (l));                                       \
71                p += (l);                                               \
72                len -= (l);                                             \
73        } while (/* CONSTCOND */ 0)
74#define APPEND(b, l)    CHECK(memcpy, b, l)
75#define ZERO(l)         CHECK(memset, 0, l)
76
77        APPEND(&ar, sizeof(ar));
78        APPEND(ifp->hwaddr, ifp->hwlen);
79        APPEND(&sip, sizeof(sip));
80        ZERO(ifp->hwlen);
81        APPEND(&tip, sizeof(tip));
82        len = p - arp_buffer;
83        return ipv4_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len);
84
85eexit:
86        errno = ENOBUFS;
87        return -1;
88}
89
90static void
91arp_failure(struct interface *ifp)
92{
93        const struct dhcp_state *state = D_CSTATE(ifp);
94
95        /* If we failed without a magic cookie then we need to try
96         * and defend our IPv4LL address. */
97        if ((state->offer != NULL &&
98            state->offer->cookie != htonl(MAGIC_COOKIE)) ||
99            (state->new != NULL &&
100            state->new->cookie != htonl(MAGIC_COOKIE)))
101        {
102                ipv4ll_handle_failure(ifp);
103                return;
104        }
105
106        unlink(state->leasefile);
107        if (!state->lease.frominfo)
108                dhcp_decline(ifp);
109        dhcp_close(ifp);
110        eloop_timeout_delete(NULL, ifp);
111        if (state->lease.frominfo)
112                start_interface(ifp);
113        else
114                eloop_timeout_add_sec(DHCP_ARP_FAIL, start_interface, ifp);
115}
116
117static void
118arp_packet(void *arg)
119{
120        struct interface *ifp = arg;
121        uint8_t arp_buffer[ARP_LEN];
122        struct arphdr ar;
123        uint32_t reply_s;
124        uint32_t reply_t;
125        uint8_t *hw_s, *hw_t;
126        ssize_t bytes;
127        struct dhcp_state *state;
128        struct if_options *opts = ifp->options;
129        const char *hwaddr;
130        struct in_addr ina;
131
132        state = D_STATE(ifp);
133        state->fail.s_addr = 0;
134        for(;;) {
135                bytes = ipv4_getrawpacket(ifp, ETHERTYPE_ARP,
136                    arp_buffer, sizeof(arp_buffer), NULL);
137                if (bytes == 0 || bytes == -1)
138                        return;
139                /* We must have a full ARP header */
140                if ((size_t)bytes < sizeof(ar))
141                        continue;
142                memcpy(&ar, arp_buffer, sizeof(ar));
143                /* Protocol must be IP. */
144                if (ar.ar_pro != htons(ETHERTYPE_IP))
145                        continue;
146                if (ar.ar_pln != sizeof(reply_s))
147                        continue;
148                /* Only these types are recognised */
149                if (ar.ar_op != htons(ARPOP_REPLY) &&
150                    ar.ar_op != htons(ARPOP_REQUEST))
151                        continue;
152
153                /* Get pointers to the hardware addreses */
154                hw_s = arp_buffer + sizeof(ar);
155                hw_t = hw_s + ar.ar_hln + ar.ar_pln;
156                /* Ensure we got all the data */
157                if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes)
158                        continue;
159                /* Ignore messages from ourself */
160                if (ar.ar_hln == ifp->hwlen &&
161                    memcmp(hw_s, ifp->hwaddr, ifp->hwlen) == 0)
162                        continue;
163                /* Copy out the IP addresses */
164                memcpy(&reply_s, hw_s + ar.ar_hln, ar.ar_pln);
165                memcpy(&reply_t, hw_t + ar.ar_hln, ar.ar_pln);
166
167                /* Check for arping */
168                if (state->arping_index &&
169                    state->arping_index <= opts->arping_len &&
170                    (reply_s == opts->arping[state->arping_index - 1] ||
171                    (reply_s == 0 &&
172                    reply_t == opts->arping[state->arping_index - 1])))
173                {
174                        ina.s_addr = reply_s;
175                        hwaddr = hwaddr_ntoa((unsigned char *)hw_s,
176                            (size_t)ar.ar_hln);
177                        syslog(LOG_INFO,
178                            "%s: found %s on hardware address %s",
179                            ifp->name, inet_ntoa(ina), hwaddr);
180                        if (select_profile(ifp, hwaddr) == -1 &&
181                            errno == ENOENT)
182                                select_profile(ifp, inet_ntoa(ina));
183                        dhcp_close(ifp);
184                        eloop_timeout_delete(NULL, ifp);
185                        start_interface(ifp);
186                        return;
187                }
188
189                /* RFC 2131 3.1.5, Client-server interaction
190                 * RFC 3927 2.2.1, Probe Conflict Detection */
191                if (state->offer &&
192                    (reply_s == state->offer->yiaddr ||
193                    (reply_s == 0 && reply_t == state->offer->yiaddr)))
194                        state->fail.s_addr = state->offer->yiaddr;
195
196                /* RFC 3927 2.5, Conflict Defense */
197                if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
198                    reply_s == state->addr.s_addr)
199                        state->fail.s_addr = state->addr.s_addr;
200
201                if (state->fail.s_addr) {
202                        syslog(LOG_ERR, "%s: hardware address %s claims %s",
203                            ifp->name,
204                            hwaddr_ntoa((unsigned char *)hw_s,
205                                (size_t)ar.ar_hln),
206                            inet_ntoa(state->fail));
207                        errno = EEXIST;
208                        arp_failure(ifp);
209                        return;
210                }
211        }
212}
213
214void
215arp_announce(void *arg)
216{
217        struct interface *ifp = arg;
218        struct dhcp_state *state = D_STATE(ifp);
219        struct timeval tv;
220
221        if (state->new == NULL)
222                return;
223        if (state->arp_fd == -1) {
224                state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP);
225                if (state->arp_fd == -1) {
226                        syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
227                        return;
228                }
229                eloop_event_add(state->arp_fd, arp_packet, ifp);
230        }
231        if (++state->claims < ANNOUNCE_NUM)
232                syslog(LOG_DEBUG,
233                    "%s: sending ARP announce (%d of %d), "
234                    "next in %d.0 seconds",
235                    ifp->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
236        else
237                syslog(LOG_DEBUG,
238                    "%s: sending ARP announce (%d of %d)",
239                    ifp->name, state->claims, ANNOUNCE_NUM);
240        if (arp_send(ifp, ARPOP_REQUEST,
241                state->new->yiaddr, state->new->yiaddr) == -1)
242                syslog(LOG_ERR, "send_arp: %m");
243        if (state->claims < ANNOUNCE_NUM) {
244                eloop_timeout_add_sec(ANNOUNCE_WAIT, arp_announce, ifp);
245                return;
246        }
247        if (state->new->cookie != htonl(MAGIC_COOKIE)) {
248                /* Check if doing DHCP */
249                if (!(ifp->options->options & DHCPCD_DHCP))
250                        return;
251                /* We should pretend to be at the end
252                 * of the DHCP negotation cycle unless we rebooted */
253                if (state->interval != 0)
254                        state->interval = 64;
255                state->probes = 0;
256                state->claims = 0;
257                tv.tv_sec = state->interval - DHCP_RAND_MIN;
258                tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
259                timernorm(&tv);
260                eloop_timeout_add_tv(&tv, dhcp_discover, ifp);
261        } else {
262                eloop_event_delete(state->arp_fd);
263                close(state->arp_fd);
264                state->arp_fd = -1;
265        }
266}
267
268void
269arp_probe(void *arg)
270{
271        struct interface *ifp = arg;
272        struct dhcp_state *state = D_STATE(ifp);
273        struct in_addr addr;
274        struct timeval tv;
275        int arping = 0;
276
277        if (state->arp_fd == -1) {
278                state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP);
279                if (state->arp_fd == -1) {
280                        syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name);
281                        return;
282                }
283                eloop_event_add(state->arp_fd, arp_packet, ifp);
284        }
285
286        if (state->arping_index < ifp->options->arping_len) {
287                addr.s_addr = ifp->options->arping[state->arping_index];
288                arping = 1;
289        } else if (state->offer) {
290                if (state->offer->yiaddr)
291                        addr.s_addr = state->offer->yiaddr;
292                else
293                        addr.s_addr = state->offer->ciaddr;
294        } else
295                addr.s_addr = state->addr.s_addr;
296
297        if (state->probes == 0) {
298                if (arping)
299                        syslog(LOG_DEBUG, "%s: searching for %s",
300                            ifp->name, inet_ntoa(addr));
301                else
302                        syslog(LOG_DEBUG, "%s: checking for %s",
303                            ifp->name, inet_ntoa(addr));
304        }
305        if (++state->probes < PROBE_NUM) {
306                tv.tv_sec = PROBE_MIN;
307                tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U);
308                timernorm(&tv);
309                eloop_timeout_add_tv(&tv, arp_probe, ifp);
310        } else {
311                tv.tv_sec = ANNOUNCE_WAIT;
312                tv.tv_usec = 0;
313                if (arping) {
314                        state->probes = 0;
315                        if (++state->arping_index < ifp->options->arping_len)
316                                eloop_timeout_add_tv(&tv, arp_probe, ifp);
317                        else
318                                eloop_timeout_add_tv(&tv, start_interface, ifp);
319                } else
320                        eloop_timeout_add_tv(&tv, dhcp_bind, ifp);
321        }
322        syslog(LOG_DEBUG,
323            "%s: sending ARP probe (%d of %d), next in %0.1f seconds",
324            ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM,
325            timeval_to_double(&tv));
326        if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1)
327                syslog(LOG_ERR, "send_arp: %m");
328}
329
330void
331arp_start(struct interface *ifp)
332{
333        struct dhcp_state *state = D_STATE(ifp);
334
335        state->probes = 0;
336        state->arping_index = 0;
337        arp_probe(ifp);
338}
Note: See TracBrowser for help on using the repository browser.