2006-11-28 04:23:22 +08:00
|
|
|
/*
|
2007-11-08 00:26:41 +08:00
|
|
|
* dhcpcd - DHCP client daemon
|
2007-11-06 23:03:38 +08:00
|
|
|
* Copyright 2006-2007 Roy Marples <roy@marples.name>
|
2006-11-28 04:23:22 +08:00
|
|
|
*
|
2007-11-08 00:26:41 +08:00
|
|
|
* Distributed under the terms of the GNU General Public License v2
|
2006-11-28 04:23:22 +08:00
|
|
|
*/
|
|
|
|
|
2007-11-08 04:59:52 +08:00
|
|
|
#include <sys/time.h>
|
2006-12-15 07:17:27 +08:00
|
|
|
#include <sys/types.h>
|
2006-11-28 04:23:22 +08:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in_systm.h>
|
2007-08-10 00:25:20 +08:00
|
|
|
#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>
|
2007-07-18 19:26:59 +08:00
|
|
|
#include <errno.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>
|
|
|
|
|
2007-05-10 17:39:26 +08:00
|
|
|
#include "config.h"
|
2006-11-28 04:23:22 +08:00
|
|
|
#include "common.h"
|
2006-12-15 07:17:27 +08:00
|
|
|
#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"
|
|
|
|
|
2007-07-18 19:26:59 +08:00
|
|
|
/* These are really for IPV4LL */
|
|
|
|
#define NPROBES 3
|
|
|
|
#define PROBE_INTERVAL 200000
|
|
|
|
#define NCLAIMS 2
|
|
|
|
#define CLAIM_INTERVAL 200000
|
2006-11-28 04:23:22 +08:00
|
|
|
|
|
|
|
/* Linux does not seem to define these handy macros */
|
|
|
|
#ifndef ar_sha
|
|
|
|
#define ar_sha(ap) (((unsigned char *) ((ap) + 1)) + 0)
|
|
|
|
#define ar_spa(ap) (((unsigned char *) ((ap) + 1)) + (ap)->ar_hln)
|
2006-12-22 05:00:14 +08:00
|
|
|
#define ar_tha(ap) (((unsigned char *) ((ap) + 1)) + \
|
2007-04-11 21:18:33 +08:00
|
|
|
(ap)->ar_hln + (ap)->ar_pln)
|
2006-12-22 05:00:14 +08:00
|
|
|
#define ar_tpa(ap) (((unsigned char *) ((ap) + 1)) + \
|
2007-04-11 21:18:33 +08:00
|
|
|
2 * (ap)->ar_hln + (ap)->ar_pln)
|
2007-04-27 00:16:56 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef arphdr_len
|
2006-12-22 05:00:14 +08:00
|
|
|
#define arphdr_len2(ar_hln, ar_pln) (sizeof (struct arphdr) + \
|
2007-04-11 21:18:33 +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
|
|
|
|
|
2007-05-10 17:39:26 +08:00
|
|
|
#ifdef ENABLE_ARP
|
2007-07-18 19:26:59 +08:00
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
static int send_arp (const interface_t *iface, int op, struct in_addr sip,
|
|
|
|
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;
|
|
|
|
int arpsize = arphdr_len2 (iface->hwlen, sizeof (struct in_addr));
|
2007-07-18 19:26:59 +08:00
|
|
|
int retval;
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-05-22 01:10:10 +08:00
|
|
|
arp = xmalloc (arpsize);
|
|
|
|
memset (arp, 0, arpsize);
|
2007-04-11 21:18:33 +08:00
|
|
|
|
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 (struct in_addr);
|
2007-07-18 19:26:59 +08:00
|
|
|
arp->ar_op = htons (op);
|
2007-08-10 00:25:20 +08:00
|
|
|
memcpy (ar_sha (arp), iface->hwaddr, arp->ar_hln);
|
2007-07-18 19:26:59 +08:00
|
|
|
memcpy (ar_spa (arp), &sip, arp->ar_pln);
|
|
|
|
if (taddr)
|
|
|
|
memcpy (ar_tha (arp), taddr, arp->ar_hln);
|
|
|
|
memcpy (ar_tpa (arp), &tip, arp->ar_pln);
|
|
|
|
|
|
|
|
retval = send_packet (iface, ETHERTYPE_ARP,
|
|
|
|
(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;
|
2007-08-21 00:33:09 +08:00
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
if (! iface)
|
|
|
|
return (-1);
|
2007-07-18 19:26:59 +08:00
|
|
|
|
|
|
|
if (! iface->arpable) {
|
|
|
|
logger (LOG_DEBUG, "interface `%s' is not ARPable", iface->name);
|
|
|
|
return (0);
|
|
|
|
}
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-11-10 00:55:00 +08:00
|
|
|
if (! IN_LINKLOCAL (ntohl (iface->previous_address.s_addr)) &&
|
|
|
|
! 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
|
|
|
|
2007-07-18 19:26:59 +08:00
|
|
|
if (! open_socket (iface, true))
|
2007-08-10 00:25:20 +08:00
|
|
|
return (-1);
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
memset (&null_address, 0, sizeof (struct in_addr));
|
2007-05-22 01:10:10 +08:00
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
buffer = xmalloc (iface->buffer_length);
|
|
|
|
reply = xmalloc (iface->buffer_length);
|
2007-05-22 01:10:10 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
while (1) {
|
2007-05-22 01:10:10 +08:00
|
|
|
struct timeval tv;
|
2007-04-11 21:18:33 +08:00
|
|
|
int bufpos = -1;
|
2007-08-10 00:25:20 +08:00
|
|
|
int buflen = iface->buffer_length;
|
2007-05-22 01:10:10 +08:00
|
|
|
fd_set rset;
|
|
|
|
int bytes;
|
2007-08-10 00:25:20 +08:00
|
|
|
int s = 0;
|
2007-10-11 21:20:37 +08:00
|
|
|
int maxfd;
|
2007-08-21 17:50:01 +08:00
|
|
|
struct timeval stopat;
|
|
|
|
struct timeval now;
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
/* Only select if we have a timeout */
|
|
|
|
if (timeout > 0) {
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = timeout;
|
2007-04-11 21:18:33 +08:00
|
|
|
|
2007-10-11 21:20:37 +08:00
|
|
|
maxfd = signal_fd_set (&rset, iface->fd);
|
|
|
|
if ((s = select (maxfd + 1, &rset, NULL, NULL, &tv)) == -1) {
|
|
|
|
if (errno == EINTR) {
|
2007-11-07 18:15:14 +08:00
|
|
|
if (signal_exists (NULL) == -1) {
|
2007-10-11 21:20:37 +08:00
|
|
|
errno = 0;
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
2007-10-11 16:53:29 +08:00
|
|
|
|
|
|
|
logger (LOG_ERR, "select: `%s'", strerror (errno));
|
2007-08-10 00:25:20 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Timed out */
|
|
|
|
if (s == 0) {
|
2007-07-18 19:26:59 +08:00
|
|
|
if (nprobes < NPROBES) {
|
|
|
|
nprobes ++;
|
|
|
|
timeout = PROBE_INTERVAL;
|
|
|
|
logger (LOG_DEBUG, "sending ARP probe #%d", nprobes);
|
2007-10-11 21:20:37 +08:00
|
|
|
if (send_arp (iface, ARPOP_REQUEST,
|
|
|
|
null_address, NULL, address) == -1)
|
|
|
|
break;
|
2007-07-18 19:26:59 +08:00
|
|
|
} else if (nclaims < NCLAIMS) {
|
|
|
|
nclaims ++;
|
|
|
|
timeout = CLAIM_INTERVAL;
|
|
|
|
logger (LOG_DEBUG, "sending ARP claim #%d", nclaims);
|
2007-10-11 21:20:37 +08:00
|
|
|
if (send_arp (iface, ARPOP_REQUEST,
|
|
|
|
address, iface->hwaddr, address) == -1)
|
|
|
|
break;
|
2007-07-18 19:26:59 +08:00
|
|
|
} else {
|
|
|
|
/* No replies, so done */
|
|
|
|
retval = 0;
|
|
|
|
break;
|
|
|
|
}
|
2007-08-10 00:25:20 +08:00
|
|
|
|
|
|
|
/* Setup our stop time */
|
|
|
|
if (get_time (&stopat) != 0)
|
|
|
|
break;
|
|
|
|
stopat.tv_usec += timeout;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-08-21 17:50:01 +08:00
|
|
|
/* We maybe ARP flooded, so check our time */
|
2007-08-10 00:25:20 +08:00
|
|
|
if (get_time (&now) != 0)
|
|
|
|
break;
|
|
|
|
if (timercmp (&now, &stopat, >)) {
|
|
|
|
timeout = 0;
|
|
|
|
continue;
|
2007-07-18 19:26:59 +08:00
|
|
|
}
|
2007-08-10 00:25:20 +08:00
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
if (! FD_ISSET (iface->fd, &rset))
|
|
|
|
continue;
|
2007-07-18 19:26:59 +08:00
|
|
|
|
2007-05-22 01:10:10 +08:00
|
|
|
memset (buffer, 0, buflen);
|
2007-04-11 21:18:33 +08:00
|
|
|
while (bufpos != 0) {
|
|
|
|
union {
|
|
|
|
unsigned char *c;
|
|
|
|
struct in_addr *a;
|
|
|
|
} rp;
|
|
|
|
union {
|
|
|
|
unsigned char *c;
|
|
|
|
struct ether_addr *a;
|
|
|
|
} rh;
|
|
|
|
|
2007-08-10 00:25:20 +08:00
|
|
|
memset (reply, 0, iface->buffer_length);
|
2007-05-22 01:10:10 +08:00
|
|
|
if ((bytes = get_packet (iface, (unsigned char *) reply,
|
|
|
|
buffer,
|
2007-07-18 19:39:30 +08:00
|
|
|
&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;
|
2007-05-22 01:10:10 +08:00
|
|
|
if (reply->ar_pln != sizeof (struct in_addr))
|
2007-04-11 21:18:33 +08:00
|
|
|
continue;
|
2007-05-22 01:10:10 +08:00
|
|
|
if ((unsigned) bytes < sizeof (reply) +
|
|
|
|
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);
|
2007-08-10 00:25:20 +08:00
|
|
|
|
2007-07-26 17:35:45 +08:00
|
|
|
/* Ensure the ARP reply is for the address we asked for */
|
|
|
|
if (rp.a->s_addr != address.s_addr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Some systems send a reply back from our hwaddress - weird */
|
|
|
|
if (reply->ar_hln == iface->hwlen &&
|
|
|
|
memcmp (rh.c, iface->hwaddr, iface->hwlen) == 0)
|
|
|
|
continue;
|
|
|
|
|
2007-04-11 21:18:33 +08:00
|
|
|
logger (LOG_ERR, "ARPOP_REPLY received from %s (%s)",
|
2007-07-26 05:01:10 +08:00
|
|
|
inet_ntoa (*rp.a),
|
2007-07-26 17:35:45 +08:00
|
|
|
hwaddr_ntoa (rh.c, reply->ar_hln));
|
2007-05-22 01:10:10 +08:00
|
|
|
retval = -1;
|
|
|
|
goto eexit;
|
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
|
|
|
}
|
2007-05-10 17:39:26 +08:00
|
|
|
#endif
|