fix #>&- syntax for closing fds
[oweals/busybox.git] / networking / udhcp / arpping.c
index da01ddd715d728aaf98788b98751b8c14014c7a9..b10bff6514e24599403f4e8fb671b41ee8d37dcc 100644 (file)
+/* 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 tarball for details.
  */
 
-#include <sys/time.h>
-#include <time.h>
-#include <sys/socket.h>
 #include <netinet/if_ether.h>
 #include <net/if_arp.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
 
-#include "dhcpd.h"
-#include "arpping.h"
 #include "common.h"
+#include "dhcpd.h"
+
+
+struct arpMsg {
+       /* Ethernet header */
+       uint8_t  h_dest[6];     /* 00 destination ether addr */
+       uint8_t  h_source[6];   /* 06 source ether addr */
+       uint16_t h_proto;       /* 0c packet type ID field */
+
+       /* ARP packet */
+       uint16_t htype;         /* 0e hardware type (must be ARPHRD_ETHER) */
+       uint16_t ptype;         /* 10 protocol type (must be ETH_P_IP) */
+       uint8_t  hlen;          /* 12 hardware address length (must be 6) */
+       uint8_t  plen;          /* 13 protocol address length (must be 4) */
+       uint16_t operation;     /* 14 ARP opcode */
+       uint8_t  sHaddr[6];     /* 16 sender's hardware address */
+       uint8_t  sInaddr[4];    /* 1c sender's IP address */
+       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) */
+} PACKED;
+
+enum {
+       ARP_MSG_SIZE = 0x2a
+};
 
-/* args:       yiaddr - what IP to ping
- *             ip - our ip
- *             mac - our arp address
- *             interface - interface to use
- * retn:       1 addr free
- *             0 addr used
- *             -1 error
- */
 
-/* FIXME: match response against chaddr */
-int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
+/* Returns 1 if no reply received */
+
+int FAST_FUNC arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface)
 {
+       int timeout_ms;
+       struct pollfd pfd[1];
+#define s (pfd[0].fd)           /* socket */
+       int rv = 1;             /* "no reply received" yet */
+       struct sockaddr addr;   /* for interface name */
+       struct arpMsg arp;
 
-       int     timeout = 2;
-       int     optval = 1;
-       int     s;                      /* socket */
-       int     rv = 1;                 /* return value */
-       struct sockaddr addr;           /* for interface name */
-       struct arpMsg   arp;
-       fd_set          fdset;
-       struct timeval  tm;
-       time_t          prevTime;
-
-
-       if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
-#ifdef IN_BUSYBOX
-               LOG(LOG_ERR, bb_msg_can_not_create_raw_socket);
-#else
-               LOG(LOG_ERR, "Could not open raw socket");
-#endif
+       s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+       if (s == -1) {
+               bb_perror_msg(bb_msg_can_not_create_raw_socket);
                return -1;
        }
 
-       if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
-               LOG(LOG_ERR, "Could not setsocketopt on raw socket");
-               close(s);
-               return -1;
+       if (setsockopt_broadcast(s) == -1) {
+               bb_perror_msg("cannot enable bcast on raw socket");
+               goto ret;
        }
 
        /* send arp request */
        memset(&arp, 0, sizeof(arp));
-       memcpy(arp.h_dest, MAC_BCAST_ADDR, 6);          /* MAC DA */
-       memcpy(arp.h_source, mac, 6);                   /* MAC SA */
-       arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
-       arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
-       arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
-       arp.hlen = 6;                                   /* hardware address length */
-       arp.plen = 4;                                   /* protocol address length */
-       arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
-       memcpy(arp.sInaddr, &ip, sizeof(ip));           /* source IP address */
-       memcpy(arp.sHaddr, mac, 6);                     /* source hardware address */
-       memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr));   /* target IP address */
+       memset(arp.h_dest, 0xff, 6);                    /* MAC DA */
+       memcpy(arp.h_source, from_mac, 6);              /* MAC SA */
+       arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
+       arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
+       arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
+       arp.hlen = 6;                                   /* hardware address length */
+       arp.plen = 4;                                   /* protocol address length */
+       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 is zero-fiiled */                     /* target hardware address */
+       memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */
 
        memset(&addr, 0, sizeof(addr));
-       strcpy(addr.sa_data, interface);
-       if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
-               rv = 0;
-
-       /* wait arp reply, and check it */
-       tm.tv_usec = 0;
-       prevTime = time(NULL);
-       while (timeout > 0) {
-               FD_ZERO(&fdset);
-               FD_SET(s, &fdset);
-               tm.tv_sec = timeout;
-               if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
-                       DEBUG(LOG_ERR, "Error on ARPING request: %m");
-                       if (errno != EINTR) rv = 0;
-               } else if (FD_ISSET(s, &fdset)) {
-                       if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
-                       if (arp.operation == htons(ARPOP_REPLY) &&
-                           bcmp(arp.tHaddr, mac, 6) == 0 &&
-                           *((uint32_t *) arp.sInaddr) == yiaddr) {
-                               DEBUG(LOG_INFO, "Valid arp reply receved for this address");
+       safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
+       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 {
+               int r;
+               unsigned prevTime = monotonic_us();
+
+               pfd[0].events = POLLIN;
+               r = safe_poll(pfd, 1, timeout_ms);
+               if (r < 0)
+                       break;
+               if (r) {
+                       r = read(s, &arp, sizeof(arp));
+                       if (r < 0)
+                               break;
+                       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 */
+                        && *((uint32_t *) arp.sInaddr) == test_ip
+                       ) {
                                rv = 0;
                                break;
                        }
                }
-               timeout -= time(NULL) - prevTime;
-               prevTime = time(NULL);
-       }
+               timeout_ms -= ((unsigned)monotonic_us() - prevTime) / 1000;
+       } while (timeout_ms > 0);
+
+ ret:
        close(s);
-       DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V");      
+       DEBUG("%srp reply received for this address", rv ? "No a" : "A");
        return rv;
 }