udhcp: add PXELINUX config file option (code 209) definition
[oweals/busybox.git] / networking / udhcp / arpping.c
index 33518077b178917794fd832c020f19df9236d986..b43e52e964e0b48c3031b41e9c820b9da2d21969 100644 (file)
@@ -1,18 +1,16 @@
 /* vi: set sw=4 ts=4: */
 /*
- * arpping.c
- *
  * Mostly stolen from: dhcpcd - DHCP client daemon
  * by Yoichi Hariguchi <yoichi@fore.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-
 #include <netinet/if_ether.h>
 #include <net/if_arp.h>
 
 #include "common.h"
 #include "dhcpd.h"
 
-
 struct arpMsg {
        /* Ethernet header */
        uint8_t  h_dest[6];     /* 00 destination ether addr */
@@ -30,14 +28,20 @@ struct arpMsg {
        uint8_t  tHaddr[6];     /* 20 target's hardware address */
        uint8_t  tInaddr[4];    /* 26 target's IP address */
        uint8_t  pad[18];       /* 2a pad for min. ethernet payload (60 bytes) */
-} ATTRIBUTE_PACKED;
+} PACKED;
 
+enum {
+       ARP_MSG_SIZE = 0x2a
+};
 
 /* Returns 1 if no reply received */
-
-int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface)
+int FAST_FUNC arpping(uint32_t test_nip,
+               const uint8_t *safe_mac,
+               uint32_t from_ip,
+               uint8_t *from_mac,
+               const char *interface)
 {
-       int timeout_ms = 2000;
+       int timeout_ms;
        struct pollfd pfd[1];
 #define s (pfd[0].fd)           /* socket */
        int rv = 1;             /* "no reply received" yet */
@@ -51,7 +55,7 @@ int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *i
        }
 
        if (setsockopt_broadcast(s) == -1) {
-               bb_perror_msg("cannot setsocketopt on raw socket");
+               bb_perror_msg("can't enable bcast on raw socket");
                goto ret;
        }
 
@@ -67,42 +71,63 @@ int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *i
        arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
        memcpy(arp.sHaddr, from_mac, 6);                /* source hardware address */
        memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
-       /* tHaddr */                                    /* target hardware address */
-       memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */
+       /* tHaddr is zero-filled */                     /* target hardware address */
+       memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */
 
        memset(&addr, 0, sizeof(addr));
        safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
-       if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
+       if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
+               // TODO: error message? caller didn't expect us to fail,
+               // just returning 1 "no reply received" misleads it.
                goto ret;
+       }
 
        /* wait for arp reply, and check it */
+       timeout_ms = 2000;
        do {
+               typedef uint32_t aliased_uint32_t FIX_ALIASING;
                int r;
-               unsigned prevTime = monotonic_us();
+               unsigned prevTime = monotonic_ms();
 
                pfd[0].events = POLLIN;
-               r = poll(pfd, 1, timeout_ms);
-               if (r < 0) {
-                       if (errno != EINTR) {
-                               bb_perror_msg("poll");
+               r = safe_poll(pfd, 1, timeout_ms);
+               if (r < 0)
+                       break;
+               if (r) {
+                       r = safe_read(s, &arp, sizeof(arp));
+                       if (r < 0)
                                break;
-                       }
-               } else if (r) {
-                       if (read(s, &arp, sizeof(arp)) < 0)
-                               break;
-                       if (arp.operation == htons(ARPOP_REPLY)
-                        && memcmp(arp.tHaddr, from_mac, 6) == 0
-                        && *((uint32_t *) arp.sInaddr) == test_ip
+
+                       //log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
+                       //      arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
+                       //      arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
+
+                       if (r >= ARP_MSG_SIZE
+                        && arp.operation == htons(ARPOP_REPLY)
+                        /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
+                        /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
+                        && *(aliased_uint32_t*)arp.sInaddr == test_nip
                        ) {
-                               rv = 0;
+                               /* if ARP source MAC matches safe_mac
+                                * (which is client's MAC), then it's not a conflict
+                                * (client simply already has this IP and replies to ARPs!)
+                                */
+                               if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
+                                       rv = 0;
+                               //else log2("sHaddr == safe_mac");
                                break;
                        }
                }
-               timeout_ms -= (monotonic_us() - prevTime) / 1000;
-       } while (timeout_ms > 0);
+               timeout_ms -= (unsigned)monotonic_ms() - prevTime + 1;
+
+               /* We used to check "timeout_ms > 0", but
+                * this is more under/overflow-resistant
+                * (people did see overflows here when system time jumps):
+                */
+       } while ((unsigned)timeout_ms <= 2000);
 
  ret:
        close(s);
-       DEBUG("%srp reply received for this address", rv ? "No a" : "A");
+       log1("%srp reply received for this address", rv ? "No a" : "A");
        return rv;
 }