dhcpcd/arp.c

285 lines
7.3 KiB
C
Raw Normal View History

/*
* dhcpcd - DHCP client daemon
2008-01-08 17:51:23 +08:00
* Copyright 2006-2008 Roy Marples <roy@marples.name>
* 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.
2006-11-28 04:23:22 +08:00
*/
#include <sys/time.h>
#include <sys/types.h>
2006-11-28 04:23:22 +08:00
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#ifdef __linux__
2006-11-28 04:23:22 +08:00
#include <netinet/ether.h>
#include <netpacket/packet.h>
#endif
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
2007-05-22 01:10:10 +08:00
#include <stdlib.h>
2006-11-28 04:23:22 +08:00
#include <string.h>
#include <unistd.h>
#include "config.h"
2006-11-28 04:23:22 +08:00
#include "common.h"
#include "arp.h"
2006-11-28 04:23:22 +08:00
#include "interface.h"
#include "logger.h"
2007-11-08 21:16:00 +08:00
#include "signal.h"
2006-11-28 04:23:22 +08:00
#include "socket.h"
/* These are really for IPV4LL */
#define NPROBES 3
#define PROBE_INTERVAL 200
#define NCLAIMS 2
#define CLAIM_INTERVAL 200
2006-11-28 04:23:22 +08:00
/* Linux does not seem to define these handy macros */
#ifndef ar_sha
#define ar_sha(ap) (((caddr_t) ((ap) + 1)) + 0)
#define ar_spa(ap) (((caddr_t) ((ap) + 1)) + (ap)->ar_hln)
2008-01-18 01:54:03 +08:00
#define ar_tha(ap) (((caddr_t) ((ap) + 1)) + (ap)->ar_hln + (ap)->ar_pln)
#define ar_tpa(ap) (((caddr_t) ((ap) + 1)) + 2 * (ap)->ar_hln + (ap)->ar_pln)
#endif
#ifndef arphdr_len
2006-12-22 05:00:14 +08:00
#define arphdr_len2(ar_hln, ar_pln) (sizeof (struct arphdr) + \
2008-01-17 00:38:47 +08:00
2 * (ar_hln) + 2 * (ar_pln))
2006-11-28 04:23:22 +08:00
#define arphdr_len(ap) (arphdr_len2 ((ap)->ar_hln, (ap)->ar_pln))
#endif
#ifdef ENABLE_ARP
static int send_arp (const interface_t *iface, int op, struct in_addr sip,
2008-01-17 00:38:47 +08:00
const unsigned char *taddr, struct in_addr tip)
2006-11-28 04:23:22 +08:00
{
2007-05-22 01:10:10 +08:00
struct arphdr *arp;
size_t arpsize = arphdr_len2 (iface->hwlen, sizeof (sip));
caddr_t tha;
int retval;
2007-04-11 21:18:33 +08:00
arp = xzalloc (arpsize);
2007-05-22 01:10:10 +08:00
arp->ar_hrd = htons (iface->family);
arp->ar_pro = htons (ETHERTYPE_IP);
arp->ar_hln = iface->hwlen;
arp->ar_pln = sizeof (sip);
arp->ar_op = htons (op);
2008-01-22 05:19:53 +08:00
memcpy (ar_sha (arp), iface->hwaddr, (size_t) arp->ar_hln);
memcpy (ar_spa (arp), &sip, (size_t) arp->ar_pln);
if (taddr) {
/* NetBSD can return NULL from ar_tha, which is probably wrong
* but we still need to deal with it */
if (! (tha = ar_tha (arp))) {
free (arp);
errno = EINVAL;
return (-1);
}
2008-01-22 05:19:53 +08:00
memcpy (tha, taddr, (size_t) arp->ar_hln);
}
2008-01-22 05:19:53 +08:00
memcpy (ar_tpa (arp), &tip, (size_t) arp->ar_pln);
retval = send_packet (iface, ETHERTYPE_ARP,
2008-01-17 00:38:47 +08:00
(unsigned char *) arp, arphdr_len (arp));
free (arp);
return (retval);
}
int arp_claim (interface_t *iface, struct in_addr address)
{
struct arphdr *reply = NULL;
long timeout = 0;
unsigned char *buffer;
int retval = -1;
int nprobes = 0;
int nclaims = 0;
struct in_addr null_address;
struct pollfd fds[] = {
{ -1, POLLIN, 0 },
{ -1, POLLIN, 0 }
};
if (! iface)
return (-1);
if (! iface->arpable) {
logger (LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
return (0);
}
2007-04-11 21:18:33 +08:00
if (! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)) &&
2008-01-17 00:38:47 +08:00
! IN_LINKLOCAL (ntohl (address.s_addr)))
logger (LOG_INFO,
"checking %s is available on attached networks",
inet_ntoa (address));
2007-04-11 21:18:33 +08:00
if (! open_socket (iface, ETHERTYPE_ARP))
return (-1);
2007-04-11 21:18:33 +08:00
fds[0].fd = signal_fd ();
fds[1].fd = iface->fd;
memset (&null_address, 0, sizeof (null_address));
2007-05-22 01:10:10 +08:00
buffer = xmalloc (iface->buffer_length);
reply = xmalloc (iface->buffer_length);
2007-05-22 01:10:10 +08:00
2008-01-22 00:08:42 +08:00
for (;;) {
2008-01-22 05:19:53 +08:00
size_t bufpos = 0;
size_t buflen = iface->buffer_length;
2007-05-22 01:10:10 +08:00
int bytes;
int s = 0;
struct timeval stopat;
struct timeval now;
2007-04-11 21:18:33 +08:00
/* Only poll if we have a timeout */
if (timeout > 0) {
s = poll (fds, 2, timeout);
2008-01-17 00:38:47 +08:00
if (s == -1) {
2007-10-11 21:20:37 +08:00
if (errno == EINTR) {
if (signal_exists (NULL) == -1) {
2007-10-11 21:20:37 +08:00
errno = 0;
continue;
} else
break;
}
2008-01-17 00:38:47 +08:00
logger (LOG_ERR, "poll: `%s'",
2008-01-17 00:38:47 +08:00
strerror (errno));
break;
}
}
/* Timed out */
if (s == 0) {
if (nprobes < NPROBES) {
nprobes ++;
timeout = PROBE_INTERVAL;
2008-01-17 00:38:47 +08:00
logger (LOG_DEBUG, "sending ARP probe #%d",
nprobes);
2007-10-11 21:20:37 +08:00
if (send_arp (iface, ARPOP_REQUEST,
2008-01-17 00:38:47 +08:00
null_address, NULL,
address) == -1)
2007-10-11 21:20:37 +08:00
break;
/* IEEE1394 cannot set ARP target address
* according to RFC2734 */
if (nprobes >= NPROBES &&
iface->family == ARPHRD_IEEE1394)
nclaims = NCLAIMS;
} else if (nclaims < NCLAIMS) {
nclaims ++;
timeout = CLAIM_INTERVAL;
2008-01-17 00:38:47 +08:00
logger (LOG_DEBUG, "sending ARP claim #%d",
nclaims);
2007-10-11 21:20:37 +08:00
if (send_arp (iface, ARPOP_REQUEST,
2008-01-17 00:38:47 +08:00
address, iface->hwaddr,
address) == -1)
2007-10-11 21:20:37 +08:00
break;
} else {
/* No replies, so done */
retval = 0;
break;
}
/* Setup our stop time */
if (get_time (&stopat) != 0)
break;
stopat.tv_usec += timeout;
continue;
}
/* We maybe ARP flooded, so check our time */
if (get_time (&now) != 0)
break;
if (timercmp (&now, &stopat, >)) {
timeout = 0;
continue;
}
if (! fds[1].revents & POLLIN)
2007-04-11 21:18:33 +08:00
continue;
2007-05-22 01:10:10 +08:00
memset (buffer, 0, buflen);
2008-01-22 05:19:53 +08:00
do {
2007-04-11 21:18:33 +08:00
union {
unsigned char *c;
struct in_addr *a;
} rp;
union {
unsigned char *c;
struct ether_addr *a;
} rh;
memset (reply, 0, iface->buffer_length);
2007-05-22 01:10:10 +08:00
if ((bytes = get_packet (iface, (unsigned char *) reply,
2008-01-17 00:38:47 +08:00
buffer,
&buflen, &bufpos)) == -1)
2007-04-11 21:18:33 +08:00
break;
/* Only these types are recognised */
2007-05-22 01:10:10 +08:00
if (reply->ar_op != htons (ARPOP_REPLY))
2007-04-11 21:18:33 +08:00
continue;
/* Protocol must be IP. */
2007-05-22 01:10:10 +08:00
if (reply->ar_pro != htons (ETHERTYPE_IP))
2007-04-11 21:18:33 +08:00
continue;
if (reply->ar_pln != sizeof (address))
2007-04-11 21:18:33 +08:00
continue;
2007-05-22 01:10:10 +08:00
if ((unsigned) bytes < sizeof (reply) +
2008-01-17 00:38:47 +08:00
2 * (4 + reply->ar_hln))
2007-04-11 21:18:33 +08:00
continue;
2007-05-22 01:10:10 +08:00
rp.c = (unsigned char *) ar_spa (reply);
rh.c = (unsigned char *) ar_sha (reply);
2008-01-17 00:38:47 +08:00
/* Ensure the ARP reply is for the our address */
2007-07-26 17:35:45 +08:00
if (rp.a->s_addr != address.s_addr)
continue;
2008-01-17 00:38:47 +08:00
/* Some systems send a reply back from our hwaddress,
2008-01-29 19:20:55 +08:00
* which is wierd */
2007-07-26 17:35:45 +08:00
if (reply->ar_hln == iface->hwlen &&
2008-01-17 00:38:47 +08:00
memcmp (rh.c, iface->hwaddr, iface->hwlen) == 0)
2007-07-26 17:35:45 +08:00
continue;
2007-04-11 21:18:33 +08:00
logger (LOG_ERR, "ARPOP_REPLY received from %s (%s)",
2008-01-17 00:38:47 +08:00
inet_ntoa (*rp.a),
2008-01-22 05:19:53 +08:00
hwaddr_ntoa (rh.c, (size_t) reply->ar_hln));
2007-05-22 01:10:10 +08:00
retval = -1;
goto eexit;
2008-01-22 05:19:53 +08:00
} while (bufpos != 0);
2007-04-11 21:18:33 +08:00
}
2007-05-22 01:10:10 +08:00
eexit:
2007-04-11 21:18:33 +08:00
close (iface->fd);
iface->fd = -1;
2007-05-22 01:10:10 +08:00
free (buffer);
free (reply);
return (retval);
2006-11-28 04:23:22 +08:00
}
#endif