From f2ed769880271654297a4be420f26ab94d39666b Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 30 Jan 2014 13:29:46 +0100 Subject: 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]. --- dhcpcd/arp.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 dhcpcd/arp.c (limited to 'dhcpcd/arp.c') diff --git a/dhcpcd/arp.c b/dhcpcd/arp.c new file mode 100644 index 00000000..13779a48 --- /dev/null +++ b/dhcpcd/arp.c @@ -0,0 +1,338 @@ +/* + * dhcpcd - DHCP client daemon + * Copyright (c) 2006-2014 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "arp.h" +#include "ipv4.h" +#include "common.h" +#include "dhcp.h" +#include "dhcpcd.h" +#include "eloop.h" +#include "if-options.h" +#include "ipv4ll.h" +#include "net.h" + +#define ARP_LEN \ + (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN)) + +static int +arp_send(const struct interface *ifp, int op, in_addr_t sip, in_addr_t tip) +{ + uint8_t arp_buffer[ARP_LEN]; + struct arphdr ar; + size_t len; + uint8_t *p; + + ar.ar_hrd = htons(ifp->family); + ar.ar_pro = htons(ETHERTYPE_IP); + ar.ar_hln = ifp->hwlen; + ar.ar_pln = sizeof(sip); + ar.ar_op = htons(op); + + p = arp_buffer; + len = sizeof(arp_buffer); + +#define CHECK(fun, b, l) \ + do { \ + if (len < (l)) \ + goto eexit; \ + fun(p, (b), (l)); \ + p += (l); \ + len -= (l); \ + } while (/* CONSTCOND */ 0) +#define APPEND(b, l) CHECK(memcpy, b, l) +#define ZERO(l) CHECK(memset, 0, l) + + APPEND(&ar, sizeof(ar)); + APPEND(ifp->hwaddr, ifp->hwlen); + APPEND(&sip, sizeof(sip)); + ZERO(ifp->hwlen); + APPEND(&tip, sizeof(tip)); + len = p - arp_buffer; + return ipv4_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len); + +eexit: + errno = ENOBUFS; + return -1; +} + +static void +arp_failure(struct interface *ifp) +{ + const struct dhcp_state *state = D_CSTATE(ifp); + + /* If we failed without a magic cookie then we need to try + * and defend our IPv4LL address. */ + if ((state->offer != NULL && + state->offer->cookie != htonl(MAGIC_COOKIE)) || + (state->new != NULL && + state->new->cookie != htonl(MAGIC_COOKIE))) + { + ipv4ll_handle_failure(ifp); + return; + } + + unlink(state->leasefile); + if (!state->lease.frominfo) + dhcp_decline(ifp); + dhcp_close(ifp); + eloop_timeout_delete(NULL, ifp); + if (state->lease.frominfo) + start_interface(ifp); + else + eloop_timeout_add_sec(DHCP_ARP_FAIL, start_interface, ifp); +} + +static void +arp_packet(void *arg) +{ + struct interface *ifp = arg; + uint8_t arp_buffer[ARP_LEN]; + struct arphdr ar; + uint32_t reply_s; + uint32_t reply_t; + uint8_t *hw_s, *hw_t; + ssize_t bytes; + struct dhcp_state *state; + struct if_options *opts = ifp->options; + const char *hwaddr; + struct in_addr ina; + + state = D_STATE(ifp); + state->fail.s_addr = 0; + for(;;) { + bytes = ipv4_getrawpacket(ifp, ETHERTYPE_ARP, + arp_buffer, sizeof(arp_buffer), NULL); + if (bytes == 0 || bytes == -1) + return; + /* We must have a full ARP header */ + if ((size_t)bytes < sizeof(ar)) + continue; + memcpy(&ar, arp_buffer, sizeof(ar)); + /* Protocol must be IP. */ + if (ar.ar_pro != htons(ETHERTYPE_IP)) + continue; + if (ar.ar_pln != sizeof(reply_s)) + continue; + /* Only these types are recognised */ + if (ar.ar_op != htons(ARPOP_REPLY) && + ar.ar_op != htons(ARPOP_REQUEST)) + continue; + + /* Get pointers to the hardware addreses */ + hw_s = arp_buffer + sizeof(ar); + hw_t = hw_s + ar.ar_hln + ar.ar_pln; + /* Ensure we got all the data */ + if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes) + continue; + /* Ignore messages from ourself */ + if (ar.ar_hln == ifp->hwlen && + memcmp(hw_s, ifp->hwaddr, ifp->hwlen) == 0) + continue; + /* Copy out the IP addresses */ + memcpy(&reply_s, hw_s + ar.ar_hln, ar.ar_pln); + memcpy(&reply_t, hw_t + ar.ar_hln, ar.ar_pln); + + /* Check for arping */ + if (state->arping_index && + state->arping_index <= opts->arping_len && + (reply_s == opts->arping[state->arping_index - 1] || + (reply_s == 0 && + reply_t == opts->arping[state->arping_index - 1]))) + { + ina.s_addr = reply_s; + hwaddr = hwaddr_ntoa((unsigned char *)hw_s, + (size_t)ar.ar_hln); + syslog(LOG_INFO, + "%s: found %s on hardware address %s", + ifp->name, inet_ntoa(ina), hwaddr); + if (select_profile(ifp, hwaddr) == -1 && + errno == ENOENT) + select_profile(ifp, inet_ntoa(ina)); + dhcp_close(ifp); + eloop_timeout_delete(NULL, ifp); + start_interface(ifp); + return; + } + + /* RFC 2131 3.1.5, Client-server interaction + * RFC 3927 2.2.1, Probe Conflict Detection */ + if (state->offer && + (reply_s == state->offer->yiaddr || + (reply_s == 0 && reply_t == state->offer->yiaddr))) + state->fail.s_addr = state->offer->yiaddr; + + /* RFC 3927 2.5, Conflict Defense */ + if (IN_LINKLOCAL(htonl(state->addr.s_addr)) && + reply_s == state->addr.s_addr) + state->fail.s_addr = state->addr.s_addr; + + if (state->fail.s_addr) { + syslog(LOG_ERR, "%s: hardware address %s claims %s", + ifp->name, + hwaddr_ntoa((unsigned char *)hw_s, + (size_t)ar.ar_hln), + inet_ntoa(state->fail)); + errno = EEXIST; + arp_failure(ifp); + return; + } + } +} + +void +arp_announce(void *arg) +{ + struct interface *ifp = arg; + struct dhcp_state *state = D_STATE(ifp); + struct timeval tv; + + if (state->new == NULL) + return; + if (state->arp_fd == -1) { + state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP); + if (state->arp_fd == -1) { + syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name); + return; + } + eloop_event_add(state->arp_fd, arp_packet, ifp); + } + if (++state->claims < ANNOUNCE_NUM) + syslog(LOG_DEBUG, + "%s: sending ARP announce (%d of %d), " + "next in %d.0 seconds", + ifp->name, state->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT); + else + syslog(LOG_DEBUG, + "%s: sending ARP announce (%d of %d)", + ifp->name, state->claims, ANNOUNCE_NUM); + if (arp_send(ifp, ARPOP_REQUEST, + state->new->yiaddr, state->new->yiaddr) == -1) + syslog(LOG_ERR, "send_arp: %m"); + if (state->claims < ANNOUNCE_NUM) { + eloop_timeout_add_sec(ANNOUNCE_WAIT, arp_announce, ifp); + return; + } + if (state->new->cookie != htonl(MAGIC_COOKIE)) { + /* Check if doing DHCP */ + if (!(ifp->options->options & DHCPCD_DHCP)) + return; + /* We should pretend to be at the end + * of the DHCP negotation cycle unless we rebooted */ + if (state->interval != 0) + state->interval = 64; + state->probes = 0; + state->claims = 0; + tv.tv_sec = state->interval - DHCP_RAND_MIN; + tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U); + timernorm(&tv); + eloop_timeout_add_tv(&tv, dhcp_discover, ifp); + } else { + eloop_event_delete(state->arp_fd); + close(state->arp_fd); + state->arp_fd = -1; + } +} + +void +arp_probe(void *arg) +{ + struct interface *ifp = arg; + struct dhcp_state *state = D_STATE(ifp); + struct in_addr addr; + struct timeval tv; + int arping = 0; + + if (state->arp_fd == -1) { + state->arp_fd = ipv4_opensocket(ifp, ETHERTYPE_ARP); + if (state->arp_fd == -1) { + syslog(LOG_ERR, "%s: %s: %m", __func__, ifp->name); + return; + } + eloop_event_add(state->arp_fd, arp_packet, ifp); + } + + if (state->arping_index < ifp->options->arping_len) { + addr.s_addr = ifp->options->arping[state->arping_index]; + arping = 1; + } else if (state->offer) { + if (state->offer->yiaddr) + addr.s_addr = state->offer->yiaddr; + else + addr.s_addr = state->offer->ciaddr; + } else + addr.s_addr = state->addr.s_addr; + + if (state->probes == 0) { + if (arping) + syslog(LOG_DEBUG, "%s: searching for %s", + ifp->name, inet_ntoa(addr)); + else + syslog(LOG_DEBUG, "%s: checking for %s", + ifp->name, inet_ntoa(addr)); + } + if (++state->probes < PROBE_NUM) { + tv.tv_sec = PROBE_MIN; + tv.tv_usec = arc4random() % (PROBE_MAX_U - PROBE_MIN_U); + timernorm(&tv); + eloop_timeout_add_tv(&tv, arp_probe, ifp); + } else { + tv.tv_sec = ANNOUNCE_WAIT; + tv.tv_usec = 0; + if (arping) { + state->probes = 0; + if (++state->arping_index < ifp->options->arping_len) + eloop_timeout_add_tv(&tv, arp_probe, ifp); + else + eloop_timeout_add_tv(&tv, start_interface, ifp); + } else + eloop_timeout_add_tv(&tv, dhcp_bind, ifp); + } + syslog(LOG_DEBUG, + "%s: sending ARP probe (%d of %d), next in %0.1f seconds", + ifp->name, state->probes ? state->probes : PROBE_NUM, PROBE_NUM, + timeval_to_double(&tv)); + if (arp_send(ifp, ARPOP_REQUEST, 0, addr.s_addr) == -1) + syslog(LOG_ERR, "send_arp: %m"); +} + +void +arp_start(struct interface *ifp) +{ + struct dhcp_state *state = D_STATE(ifp); + + state->probes = 0; + state->arping_index = 0; + arp_probe(ifp); +} -- cgit v1.2.3