From: Roy Marples Date: Thu, 9 Aug 2007 16:25:20 +0000 (+0000) Subject: Ensure ARP checking times out when there is a flood. X-Git-Tag: v3.2.3~216 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0930e024c30c4974de82417ddd6ff633cca82b6c;p=thirdparty%2Fdhcpcd.git Ensure ARP checking times out when there is a flood. --- diff --git a/ChangeLog b/ChangeLog index a318e496..60d21b07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +Ensure ARP checking times out when there is a flood. Add -x option to quit without releasing the lease. dhcpcd-3.1.3 diff --git a/Makefile b/Makefile index 8f64812c..cd0690f2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION = 3.1.3 +VERSION = 3.1.4_pre3 CFLAGS ?= -O2 -pipe # Should work for both GNU make and BSD make @@ -49,12 +49,16 @@ dhcpcd_H = version.h dhcpcd_OBJS = arp.o client.o common.o configure.o dhcp.o dhcpcd.o duid.o \ info.o interface.o ipv4ll.o logger.o signals.o socket.o -# By default we don't need to link to anything -# Except on Darwin where we need -lresolv, so they need to uncomment this -#dhcpcd_LIBS = -lresolv +# These are nasty hacks to work out what libs we need to link to +# We require librt for glibc bases systems +LIBRT != ldd /bin/date 2>/dev/null | grep -q /librt.so && echo "-lrt" || exit 0 +LIBRT ?= $(shell ldd /bin/date 2>/dev/null | grep -q /librt.so && echo "-lrt") + +# Darwin needs this, but we have no way of detecting this atm +#LIBRESOLV = -lresolv dhcpcd: $(dhcpcd_H) $(dhcpcd_OBJS) - $(CC) $(LDFLAGS) $(dhcpcd_OBJS) $(dhcpcd_LIBS) -o dhcpcd + $(CC) $(LDFLAGS) $(dhcpcd_OBJS) $(LIBRT) $(LIBRESOLV) -o dhcpcd version.h: echo '#define VERSION "$(VERSION)"' > version.h diff --git a/arp.c b/arp.c index ee2df40e..ce2ba417 100644 --- a/arp.c +++ b/arp.c @@ -24,7 +24,7 @@ #include #include #include -#ifdef __linux +#ifdef __linux__ #include #include #endif @@ -65,12 +65,10 @@ #define arphdr_len(ap) (arphdr_len2 ((ap)->ar_hln, (ap)->ar_pln)) #endif -#define IP_MIN_FRAME_LENGTH 46 - #ifdef ENABLE_ARP -static int send_arp (interface_t *iface, int op, struct in_addr sip, - unsigned char *taddr, struct in_addr tip) +static int send_arp (const interface_t *iface, int op, struct in_addr sip, + const unsigned char *taddr, struct in_addr tip) { struct arphdr *arp; int arpsize = arphdr_len2 (iface->hwlen, sizeof (struct in_addr)); @@ -84,7 +82,7 @@ static int send_arp (interface_t *iface, int op, struct in_addr sip, arp->ar_hln = iface->hwlen; arp->ar_pln = sizeof (struct in_addr); arp->ar_op = htons (op); - memcpy (ar_sha (arp), &iface->hwaddr, arp->ar_hln); + memcpy (ar_sha (arp), iface->hwaddr, arp->ar_hln); memcpy (ar_spa (arp), &sip, arp->ar_pln); if (taddr) memcpy (ar_tha (arp), taddr, arp->ar_hln); @@ -105,6 +103,11 @@ int arp_claim (interface_t *iface, struct in_addr address) int nprobes = 0; int nclaims = 0; struct in_addr null_address; + struct timeval stopat; + struct timeval now; + + if (! iface) + return (-1); if (! iface->arpable) { logger (LOG_DEBUG, "interface `%s' is not ARPable", iface->name); @@ -115,35 +118,39 @@ int arp_claim (interface_t *iface, struct in_addr address) inet_ntoa (address)); if (! open_socket (iface, true)) - return (0); + return (-1); - memset (&null_address, 0, sizeof (null_address)); + memset (&null_address, 0, sizeof (struct in_addr)); - buffer = xmalloc (sizeof (char *) * iface->buffer_length); - - /* Our ARP packets are always smaller - hopefully */ - reply = xmalloc (IP_MIN_FRAME_LENGTH); + buffer = xmalloc (iface->buffer_length); + reply = xmalloc (iface->buffer_length); while (1) { struct timeval tv; int bufpos = -1; - int buflen = sizeof (char *) * iface->buffer_length; + int buflen = iface->buffer_length; fd_set rset; int bytes; - int s; + int s = 0; - tv.tv_sec = 0; - tv.tv_usec = timeout; + /* Only select if we have a timeout */ + if (timeout > 0) { + tv.tv_sec = 0; + tv.tv_usec = timeout; - FD_ZERO (&rset); - FD_SET (iface->fd, &rset); - errno = 0; - if ((s = select (FD_SETSIZE, &rset, NULL, NULL, &tv)) == -1) { - if (errno != EINTR) - logger (LOG_ERR, "select: `%s'", strerror (errno)); - break; - } else if (s == 0) { - /* Timed out */ + FD_ZERO (&rset); + FD_SET (iface->fd, &rset); + + errno = 0; + if ((s = select (FD_SETSIZE, &rset, NULL, NULL, &tv)) == -1) { + if (errno != EINTR) + logger (LOG_ERR, "select: `%s'", strerror (errno)); + break; + } + } + + /* Timed out */ + if (s == 0) { if (nprobes < NPROBES) { nprobes ++; timeout = PROBE_INTERVAL; @@ -159,8 +166,23 @@ int arp_claim (interface_t *iface, struct in_addr address) 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 (! FD_ISSET (iface->fd, &rset)) continue; @@ -175,7 +197,7 @@ int arp_claim (interface_t *iface, struct in_addr address) struct ether_addr *a; } rh; - memset (reply, 0, IP_MIN_FRAME_LENGTH); + memset (reply, 0, iface->buffer_length); if ((bytes = get_packet (iface, (unsigned char *) reply, buffer, &buflen, &bufpos)) == -1) @@ -196,7 +218,7 @@ int arp_claim (interface_t *iface, struct in_addr address) rp.c = (unsigned char *) ar_spa (reply); rh.c = (unsigned char *) ar_sha (reply); - + /* Ensure the ARP reply is for the address we asked for */ if (rp.a->s_addr != address.s_addr) continue; diff --git a/common.c b/common.c index b699dd61..2eb4e937 100644 --- a/common.c +++ b/common.c @@ -76,18 +76,47 @@ size_t strlcpy (char *dst, const char *src, size_t size) # endif #endif -/* This requires us to link to rt on glibc, so we use sysinfo instead */ -#ifdef __linux__ -#include +/* Handy function to get the time. + * We only care about time advancements, not the actual time itself + * Which is why we use CLOCK_MONOTONIC, but it is not available on all + * platforms + */ +#ifdef CLOCK_MONOTONIC +int get_time (struct timeval *tp) +{ + struct timespec ts; + + if (clock_gettime (CLOCK_MONOTONIC, &ts) == -1) { + logger (LOG_ERR, "clock_gettime: %s", strerror (errno)); + return (-1); + } + + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; + return (0); +} + long uptime (void) { - struct sysinfo info; + struct timespec tp; + + if (clock_gettime (CLOCK_MONOTONIC, &tp) == -1) { + logger (LOG_ERR, "clock_gettime: %s", strerror (errno)); + return (-1); + } - sysinfo (&info); - return info.uptime; + return (tp.tv_sec); } -#elif __APPLE__ -/* Darwin doesn't appear to have an uptime, so try and make one ourselves */ +#else +int get_time (struct timeval *tp) +{ + if (gettimeofday (&tp, NULL) == -1) { + logger (LOG_ERR, "gettimeofday: %s", strerror (errno)); + return (-1); + } + return (0); +} + long uptime (void) { struct timeval tv; @@ -95,34 +124,22 @@ long uptime (void) if (gettimeofday (&tv, NULL) == -1) { logger (LOG_ERR, "gettimeofday: %s", strerror (errno)); - return -1; + return (-1); } if (start == 0) start = tv.tv_sec; - return tv.tv_sec - start; -} -#else -long uptime (void) -{ - struct timespec tp; - - if (clock_gettime (CLOCK_MONOTONIC, &tp) == -1) { - logger (LOG_ERR, "clock_gettime: %s", strerror (errno)); - return -1; - } - - return tp.tv_sec; + return (tv.tv_sec - start); } #endif -void *xmalloc (size_t size) +void *xmalloc (size_t s) { - void *value = malloc (size); + void *value = malloc (s); if (value) - return value; + return (value); logger (LOG_ERR, "memory exhausted"); exit (EXIT_FAILURE); diff --git a/common.h b/common.h index 2f7310f9..79940997 100644 --- a/common.h +++ b/common.h @@ -23,6 +23,7 @@ #define COMMON_H /* string.h pulls in features.h so the below define checks work */ +#include #include /* Only GLIBC doesn't support strlcpy */ @@ -36,6 +37,7 @@ size_t strlcpy (char *dst, const char *src, size_t size); void srandomdev (void); #endif +int get_time (struct timeval *tp); long uptime (void); void *xmalloc (size_t size); char *xstrdup (const char *str); diff --git a/dhcpcd.c b/dhcpcd.c index 5f510269..63f922da 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -61,10 +61,10 @@ _int = (int) _number; \ } -static pid_t read_pid(const char *pidfile) +static pid_t read_pid (const char *pidfile) { FILE *fp; - pid_t pid; + pid_t pid = 0; if ((fp = fopen (pidfile, "r")) == NULL) { errno = ENOENT;