udhcp: s/sipservers/sipsrv/ to match other similar option names
[oweals/busybox.git] / networking / udhcp / arpping.c
index 08669208236abd17a0c72cf4006b7094ab02c57f..7c8c24446c50a7ae47048fc1a1a82256cb8240f6 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 <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"
 
-/* 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
- */
+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 */
 
-/* FIXME: match response against chaddr */
-int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
-{
+       /* 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;
 
-       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;
+enum {
+       ARP_MSG_SIZE = 0x2a
+};
 
+/* Returns 1 if no reply received */
+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;
+       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;
 
-       if ((s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
+       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) {
-               bb_perror_msg("cannot setsocketopt on raw socket");
-               close(s);
-               return -1;
+       if (setsockopt_broadcast(s) == -1) {
+               bb_perror_msg("can't 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-filled */                     /* target hardware address */
+       memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* 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;
+       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 {
+               typedef uint32_t aliased_uint32_t FIX_ALIASING;
+               int r;
+               unsigned prevTime = monotonic_ms();
+
+               pfd[0].events = POLLIN;
+               r = safe_poll(pfd, 1, timeout_ms);
+               if (r < 0)
+                       break;
+               if (r) {
+                       r = safe_read(s, &arp, sizeof(arp));
+                       if (r < 0)
+                               break;
+
+                       //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]);
 
-       /* wait arp reply, and check it */
-       tm.tv_usec = 0;
-       prevTime = uptime();
-       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) {
-                       bb_perror_msg("Error on ARPING request");
-                       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) &&
-                           memcmp(arp.tHaddr, mac, 6) == 0 &&
-                           *((uint32_t *) arp.sInaddr) == yiaddr) {
-                               DEBUG("Valid arp reply received for this address");
-                               rv = 0;
+                       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
+                       ) {
+                               /* 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 -= uptime() - prevTime;
-               prevTime = uptime();
-       }
+               timeout_ms -= (unsigned)monotonic_ms() - prevTime;
+       } while (timeout_ms > 0);
+
+ ret:
        close(s);
-       DEBUG("%salid arp replies for this address", rv ? "No v" : "V");
+       log1("%srp reply received for this address", rv ? "No a" : "A");
        return rv;
 }