Diff for /embedaddon/arping/src/arping.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2016/10/18 13:16:10 version 1.1.1.4, 2021/03/16 23:40:57
Line 15 Line 15
  *   *
  */   */
 /*  /*
 *  Copyright (C) 2000-2014 Thomas Habets <thomas@habets.se> *  Copyright (C) 2000-2019 Thomas Habets <thomas@habets.se>
  *   *
  *  This program is free software; you can redistribute it and/or modify   *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by   *  it under the terms of the GNU General Public License as published by
Line 45 Line 45
 #include <unistd.h>  #include <unistd.h>
 #endif  #endif
   
   #if HAVE_GETOPT_H
   #include <getopt.h>
   #endif
   
 #if HAVE_STDINT_H  #if HAVE_STDINT_H
 #include <stdint.h>  #include <stdint.h>
 #endif  #endif
Line 65 Line 69
 #include <sys/types.h>  #include <sys/types.h>
 #endif  #endif
   
   #if HAVE_SYS_PARAM_H
   #include <sys/param.h>
   #endif
   
   #if HAVE_SYS_RANDOM_H
   #include <sys/random.h>
   #endif
   
   #if HAVE_GRP_H
   #include <grp.h>
   #endif
   
 #if HAVE_SYS_SOCKET_H  #if HAVE_SYS_SOCKET_H
 #include <sys/socket.h>  #include <sys/socket.h>
 #endif  #endif
Line 116 Line 132
 #define CLOCK_MONOTONIC CLOCK_REALTIME  #define CLOCK_MONOTONIC CLOCK_REALTIME
 #endif  #endif
   
   #define UNUSED(x) (void)(x)
   
 /**  /**
  * OS-specific interface finding using routing table. See findif_*.c   * OS-specific interface finding using routing table. See findif_*.c
  * ebuf must be called with a size of at least   * ebuf must be called with a size of at least
Line 129  arping_lookupdev_default(uint32_t srcip, uint32_t dsti Line 147  arping_lookupdev_default(uint32_t srcip, uint32_t dsti
   
 static const char *version = VERSION; /* from autoconf */  static const char *version = VERSION; /* from autoconf */
   
static libnet_t *libnet = 0;libnet_t *libnet = 0;
   
 /* Timestamp of last packet sent.  /* Timestamp of last packet sent.
  * Used for timing, and assumes that reply is due to most recent sent query.   * Used for timing, and assumes that reply is due to most recent sent query.
Line 137  static libnet_t *libnet = 0; Line 155  static libnet_t *libnet = 0;
 static struct timespec lastpacketsent;  static struct timespec lastpacketsent;
   
 /* target string */  /* target string */
static char *target = "huh? bug in arping?";static const char *target = "huh? bug in arping?";
   
 /*  /*
  * Ping IP mode:   cmdline target   * Ping IP mode:   cmdline target
  * Ping MAC mode:  255.255.255.255, override with -T   * Ping MAC mode:  255.255.255.255, override with -T
  */   */
static uint32_t dstip;uint32_t dstip;
   
 /*  /*
  * Ping IP mode:   ethxmas, override with -t   * Ping IP mode:   ethxmas, override with -t
Line 151  static uint32_t dstip; Line 169  static uint32_t dstip;
  */   */
 static uint8_t dstmac[ETH_ALEN];  static uint8_t dstmac[ETH_ALEN];
   
static uint32_t srcip;            /* autodetected, override with -S/-b/-0 */static char* payload_suffix = NULL;
static uint8_t srcmac[ETH_ALEN];  /* autodetected, override with -s */static ssize_t payload_suffix_size = -1;
   
   uint32_t srcip;                   /* autodetected, override with -S/-b/-0 */
   uint8_t srcmac[ETH_ALEN];         /* autodetected, override with -s */
   
   static int16_t vlan_tag = -1; /* 802.1Q tag to add to packets. -V */
   static int16_t vlan_prio = -1; /* 802.1p prio to use with 802.1Q. -Q */
   
 static int beep = 0;                 /* beep when reply is received. -a */  static int beep = 0;                 /* beep when reply is received. -a */
 static int reverse_beep = 0;         /* beep when expected reply absent. -e */  static int reverse_beep = 0;         /* beep when expected reply absent. -e */
 static int alsototal = 0;            /* print sent as well as received. -u */  static int alsototal = 0;            /* print sent as well as received. -u */
 static int addr_must_be_same = 0;    /* -A */  static int addr_must_be_same = 0;    /* -A */
 static int unsolicited = 0;          /* -U */  static int unsolicited = 0;          /* -U */
 static int send_reply = 0;           /* Send reply instead of request. -P */  static int send_reply = 0;           /* Send reply instead of request. -P */
   static int promisc = 0;              /* Use promisc mode. -p */
   
 static int finddup = 0;              /* finddup mode. -d */  static int finddup = 0;              /* finddup mode. -d */
 static int dupfound = 0;             /* set to 1 if dup found */  static int dupfound = 0;             /* set to 1 if dup found */
 static char lastreplymac[ETH_ALEN];  /* if last different from this then dup */  static char lastreplymac[ETH_ALEN];  /* if last different from this then dup */
   
static unsigned int numsent = 0;            /* packets sent */unsigned int numsent = 0;                   /* packets sent */
static unsigned int numrecvd = 0;           /* packets received */unsigned int numrecvd = 0;                  /* packets received */
 static unsigned int max_replies = UINT_MAX; /* exit after -C replies */  static unsigned int max_replies = UINT_MAX; /* exit after -C replies */
 static unsigned int numdots = 0;            /* dots that should be printed */  
 static const char* timestamp_type = NULL;   /* Incoming packet measurement ts type (-m) */  static const char* timestamp_type = NULL;   /* Incoming packet measurement ts type (-m) */
   
 static double stats_min_time = -1;  static double stats_min_time = -1;
Line 187  static enum { NORMAL,      /* normal output */ Line 211  static enum { NORMAL,      /* normal output */
   
 static const uint8_t ethnull[ETH_ALEN] = {0, 0, 0, 0, 0, 0};  static const uint8_t ethnull[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
 static const uint8_t ethxmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};  static const uint8_t ethxmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
   static const char* ip_broadcast = "255.255.255.255";
   static const uint32_t ip_broadcast_num = (uint32_t)-1;
   
 int verbose = 0;  /* Increase with -v */  int verbose = 0;  /* Increase with -v */
   
 /* Doesn't really need to be volatile, but doesn't hurt. */  /* Doesn't really need to be volatile, but doesn't hurt. */
 static volatile sig_atomic_t time_to_die = 0;  static volatile sig_atomic_t time_to_die = 0;
   
   static ssize_t
   xgetrandom(void *buf, const size_t buflen, const unsigned int flags)
   {
   #ifdef HAVE_GETRANDOM
           return getrandom(buf, buflen, flags);
   #else
           char* p = buf;
           for (int n = 0; n < buflen; n++) {
                   p[n] = random() & 0xff;
           }
           return buflen;
   #endif
   }
   
   static long int
   xrandom() {
           const int maxtry = 10;
           for (int c = 0; c < maxtry; c++) {
                   long int ret;
                   const ssize_t rc = xgetrandom(&ret, sizeof(ret), 0);
                   if (rc == -1) {
                           fprintf(stderr, "arping: failed to get random bytes: %s\n", strerror(errno));
                           continue;
                   }
                   if (sizeof(ret) != rc) {
                           fprintf(stderr, "arping: got too few random bytes %zd, want %zd\n", rc, sizeof(ret));
                           continue;
                   }
                   return ret;
           }
           fprintf(stderr, "arping: failed to get random bytes after %d tries\n", maxtry);
           exit(1);
   }
   
 /**  /**
  * If possible, chroot.   * If possible, chroot.
  *   *
Line 207  drop_fs_root() Line 267  drop_fs_root()
         errno = 0;          errno = 0;
         if (!(pw = getpwnam(chroot_user))) {          if (!(pw = getpwnam(chroot_user))) {
                 if (verbose) {                  if (verbose) {
                        printf("arping: getpwnam(%s): %s",                        printf("arping: getpwnam(%s): %s\n",
                                chroot_user, strerror(errno));                                 chroot_user, strerror(errno));
                 }                  }
                 return;                  return;
         }          }
         if (chdir(pw->pw_dir)) {          if (chdir(pw->pw_dir)) {
                 if (verbose) {                  if (verbose) {
                        printf("arping: chdir(%s): %s",                        printf("arping: chdir(%s): %s\n",
                                pw->pw_dir, strerror(errno));                                 pw->pw_dir, strerror(errno));
                 }                  }
                 return;                  return;
         }          }
         if (chroot(pw->pw_dir)) {          if (chroot(pw->pw_dir)) {
                 if (verbose) {                  if (verbose) {
                        printf("arping: chroot(%s): %s",                        printf("arping: chroot(%s): %s\n",
                                pw->pw_dir, strerror(errno));                                 pw->pw_dir, strerror(errno));
                 }                  }
                 return;                  return;
         }          }
           if (chdir("/")) {
                   if (verbose) {
                           printf("arping: chdir(/): %s\n", strerror(errno));
                   }
                   return;
           }
         if (verbose > 1) {          if (verbose > 1) {
                 printf("arping: Successfully chrooted to %s\n", pw->pw_dir);                  printf("arping: Successfully chrooted to %s\n", pw->pw_dir);
         }          }
Line 293  drop_capabilities() Line 359  drop_capabilities()
 }  }
   
 /**  /**
    * Get GID of input (handle both name and number) or die.
    */
   static gid_t
   must_get_group(const char* ident)
   {
           // Special case empty string, because strtol.
           int saved_errno = 0;
           if (*ident) {
                   // First try it as a name.
                   {
                           struct group* gr;
                           errno = 0;
                           if ((gr = getgrnam(ident))) {
                                   return gr->gr_gid;
                           }
                           saved_errno = errno;
                   }
   
                   // Not a name. Try it as an integer.
                   {
                           char* endp = NULL;
                           gid_t r = strtol(ident, &endp, 0);
                           if (!*endp) {
                                   return r;
                           }
                   }
           }
   
           if (saved_errno != 0) {
                   fprintf(stderr,
                           "arping: %s not a number and getgrnam(%s): %s\n",
                           ident, ident, strerror(saved_errno));
           } else {
                   // If group was supplied, then not
                   // existing is fatal error too.
                   fprintf(stderr,
                           "arping: %s is not a number or group\n",
                           ident);
           }
           exit(1);
   }
   
   /**
  * drop all privileges.   * drop all privileges.
    *
    * To be called as early as possible. IOW: immediately after opening
    * raw socket.
  */   */
 static void  static void
drop_privileges()drop_privileges(const char* drop_group)
 {  {
         // Need to get uid/gid of 'nobody' before chroot().          // Need to get uid/gid of 'nobody' before chroot().
         const char* drop_user = "nobody";          const char* drop_user = "nobody";
Line 306  drop_privileges() Line 418  drop_privileges()
         gid_t gid = 0;          gid_t gid = 0;
         if (!(pw = getpwnam(drop_user))) {          if (!(pw = getpwnam(drop_user))) {
                 if (verbose) {                  if (verbose) {
                        printf("arping: getpwnam(%s): %s\n",                        if (errno != 0) {
                               drop_user, strerror(errno));                                printf("arping: getpwnam(%s): %s\n",
                                        drop_user, strerror(errno));
                         } else {
                                 printf("arping: getpwnam(%s): unknown user\n",
                                        drop_user);
                         }
                 }                  }
                 return;  
         } else {          } else {
                 uid = pw->pw_uid;                  uid = pw->pw_uid;
                 gid = pw->pw_gid;                  gid = pw->pw_gid;
         }          }
   
           // If group is supplied, use that gid instead.
           if (drop_group != NULL) {
                   gid = must_get_group(drop_group);
           }
         drop_fs_root();          drop_fs_root();
         drop_uid(uid, gid);          drop_uid(uid, gid);
         drop_capabilities();          drop_capabilities();
   #ifdef HAVE_UNVEIL
           if (unveil("/", "")) {
                   fprintf(stderr,
                           "arping: failed to unveil(/, <>): %s\n",
                           strerror(errno));
                   exit(1);
           }
           if (unveil(NULL, NULL)) {
                   fprintf(stderr, "arping: failed to unveil(NULL, NULL): %s\n",
                          strerror(errno));
                   exit(1);
           }
           if (verbose > 1) {
                   printf("arping: Successfully unveiled\n");
           }
   #endif
 }  }
   
   /**
    * drop even more privileges, where possible.
    *
    * After all setup is done and main loop is about to start.
    */
   static void
   drop_more_privileges()
   {
   #ifdef HAVE_PLEDGE
           if (pledge("stdio tty", "")) {
                   fprintf(stderr, "arping: failed to pledge(stdio, <empty>): %s\n",
                          strerror(errno));
                   exit(1);
           } else if (verbose > 1) {
                   printf("arping: Successfully pledged\n");
           }
   #endif
   }
   
   
 /**  /**
  * Do pcap_open_live(), except by using the pcap_create() interface   * Do pcap_open_live(), except by using the pcap_create() interface
  * introduced in 2008 (libpcap 0.4) where available.   * introduced in 2008 (libpcap 0.4) where available.
    * This is so that we can set some options, which can't be set with
    * pcap_open_live:
    * 1) Immediate mode -- this prevents pcap from buffering.
    * 2) Set timestamp type -- specify what type of timer to use.
  *   *
  * FIXME: Use pcap_set_buffer_size()?   * FIXME: Use pcap_set_buffer_size()?
  */   */
Line 352  try_pcap_open_live(const char *device, int snaplen, Line 512  try_pcap_open_live(const char *device, int snaplen,
   
 #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE  #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
         // Without immediate mode some architectures (e.g. Linux with          // Without immediate mode some architectures (e.g. Linux with
        // TPACKET_V3) will buffer replies and incorrectly upwards of        // TPACKET_V3) will buffer replies and incorrectly report upwards of
         // hundreds of milliseconds of delay.          // hundreds of milliseconds of delay.
         if ((rc = pcap_set_immediate_mode(pcap, 1))) {          if ((rc = pcap_set_immediate_mode(pcap, 1))) {
                 if (verbose) {                  if (verbose) {
Line 456  do_pcap_open_live(const char *device, int snaplen, Line 616  do_pcap_open_live(const char *device, int snaplen,
  */   */
 void  void
 strip_newline(char* s) {  strip_newline(char* s) {
         if (!*s) {  
                 return;  
         }  
         size_t n;          size_t n;
        for (n = strlen(s); s[n - 1] == '\n'; --n) {        for (n = strlen(s); n && (s[n - 1] == '\n'); --n) {
                 s[n - 1] = 0;                  s[n - 1] = 0;
         }          }
 }  }
Line 495  do_libnet_init(const char *ifname, int recursive) Line 652  do_libnet_init(const char *ifname, int recursive)
                         /* Sometimes libnet guesses an interface that it then                          /* Sometimes libnet guesses an interface that it then
                          * can't use. Work around that by attempting to                           * can't use. Work around that by attempting to
                          * use "lo". */                           * use "lo". */
                        return do_libnet_init("lo", 1);                        do_libnet_init("lo", 1);
                         if (libnet != NULL) {
                                 return;
                         }
                 } else if (recursive) {                  } else if (recursive) {
                        /* Continue original execution. */                        /* Continue original execution to get that
                          * error message. */
                         return;                          return;
                 }                  }
                 fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n",                  fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n",
                        ifname ? ifname : "<null>", ebuf);                        ifname ? ifname : "<null>",
                         *ebuf ? ebuf : "<no error message>");
                 if (getuid() && geteuid()) {                  if (getuid() && geteuid()) {
                         fprintf(stderr,                          fprintf(stderr,
                                 "arping: you may need to run as root\n");                                  "arping: you may need to run as root\n");
Line 516  do_libnet_init(const char *ifname, int recursive) Line 678  do_libnet_init(const char *ifname, int recursive)
 void  void
 sigint(int i)  sigint(int i)
 {  {
           UNUSED(i);
         time_to_die = 1;          time_to_die = 1;
 }  }
   
Line 526  static void Line 689  static void
 getclock(struct timespec *ts)  getclock(struct timespec *ts)
 {  {
 #if HAVE_CLOCK_MONOTONIC  #if HAVE_CLOCK_MONOTONIC
        if (-1 == clock_gettime(CLOCK_MONOTONIC, ts)) {        static int clock_gettime_failed = 0;
                fprintf(stderr,        if (!clock_gettime_failed) {
                        "arping: clock_gettime(): %s\n",                if (0 == clock_gettime(CLOCK_MONOTONIC, ts)) {
                         return;
                 }
                 fprintf(stderr, "arping: clock_gettime(): %s\n",
                         strerror(errno));                          strerror(errno));
                sigint(0);                clock_gettime_failed = 1; // Prevent duplicate error messages.
         }          }
#else#endif
         struct timeval tv;          struct timeval tv;
         if (-1 == gettimeofday(&tv, NULL)) {          if (-1 == gettimeofday(&tv, NULL)) {
                 fprintf(stderr, "arping: gettimeofday(): %s\n",                  fprintf(stderr, "arping: gettimeofday(): %s\n",
Line 541  getclock(struct timespec *ts) Line 707  getclock(struct timespec *ts)
         }          }
         ts->tv_sec = tv.tv_sec;          ts->tv_sec = tv.tv_sec;
         ts->tv_nsec = tv.tv_usec * 1000;          ts->tv_nsec = tv.tv_usec * 1000;
 #endif  
 }  }
   
 /**  /**
  *   *
  */   */
 static char*  static char*
format_mac(unsigned char* mac, char* buf, size_t bufsize) {format_mac(const unsigned char* mac, char* buf, size_t bufsize) {
         snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",          snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);                  mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
         return buf;          return buf;
Line 585  extended_usage() Line 750  extended_usage()
                "    -e     Like -a but beep when there is no reply.\n"                 "    -e     Like -a but beep when there is no reply.\n"
                "    -F     Don't try to be smart about the interface name.  (even  if  this\n"                 "    -F     Don't try to be smart about the interface name.  (even  if  this\n"
                "           switch is not given, -i overrides smartness)\n"                 "           switch is not given, -i overrides smartness)\n"
                  "    -g group\n"
                  "           setgid() to this group instead of the nobody group.\n"
                "    -h     Displays a help message and exits.\n"                 "    -h     Displays a help message and exits.\n"
                "    -i interface\n"                 "    -i interface\n"
                "           Use the specified interface.\n"                 "           Use the specified interface.\n"
Line 595  extended_usage() Line 762  extended_usage()
                "\n           Type of timestamp to use for incoming packets. Use -vv when\n"                 "\n           Type of timestamp to use for incoming packets. Use -vv when\n"
                "           pinging to list available ones.\n"                 "           pinging to list available ones.\n"
                "    -q     Does not display messages, except error messages.\n"                 "    -q     Does not display messages, except error messages.\n"
                  "    -Q pri 802.1p priority to set. Should be used with 802.1Q (-V).\n"
                  "           Defaults to 0.\n"
                "    -r     Raw output: only the MAC/IP address is displayed for each reply.\n"                 "    -r     Raw output: only the MAC/IP address is displayed for each reply.\n"
                "    -R     Raw output: Like -r but shows \"the other one\", can  be  combined\n"                 "    -R     Raw output: Like -r but shows \"the other one\", can  be  combined\n"
                "           with -r.\n"                 "           with -r.\n"
Line 618  extended_usage() Line 787  extended_usage()
                "           pinging MACs.\n"                 "           pinging MACs.\n"
                "    -U     Send unsolicited ARP.\n"                 "    -U     Send unsolicited ARP.\n"
                "    -v     Verbose output. Use twice for more messages.\n"                 "    -v     Verbose output. Use twice for more messages.\n"
               "    -w     Time to wait between pings, in microseconds.\n"               "    -V num 802.1Q tag to add. Defaults to no VLAN tag.\n"
               "    -W     Same as -w, but in floating point seconds.\n");               "    -w sec Specify a timeout before ping exits regardless of how"
                " many\npackets have been sent or received.\n"
                "    -W sec Time to wait between pings.\n");
         printf("Report bugs to: thomas@habets.se\n"          printf("Report bugs to: thomas@habets.se\n"
                "Arping home page: <http://www.habets.pp.se/synscan/>\n"                 "Arping home page: <http://www.habets.pp.se/synscan/>\n"
                "Development repo: http://github.com/ThomasHabets/arping\n");                 "Development repo: http://github.com/ThomasHabets/arping\n");
Line 633  standard_usage() Line 804  standard_usage()
 {  {
         printf("ARPing %s, by Thomas Habets <thomas@habets.se>\n",          printf("ARPing %s, by Thomas Habets <thomas@habets.se>\n",
                version);                 version);
        printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w <us> ] "        printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w <sec> ] "
                "[ -W <sec> ] "                 "[ -W <sec> ] "
                "[ -S <host/ip> ]\n"                 "[ -S <host/ip> ]\n"
                "              "                 "              "
                "[ -T <host/ip ] "                 "[ -T <host/ip ] "
                "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n"                 "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n"
                "              "                 "              "
               "[ -C <count> ] [ -i <interface> ] [ -m <type> ] "               "[ -C <count> ] [ -i <interface> ] [ -m <type> ]"
                " [ -g <group> ]\n"
                "              "
                "[ -V <vlan> ] [ -Q <priority> ] "
                "<host/ip/MAC | -B>\n");                 "<host/ip/MAC | -B>\n");
 }  }
   
Line 661  usage(int ret) Line 835  usage(int ret)
 }  }
   
 /**  /**
    * Check to see if it looks somewhat like a MAC address.
    *
  * It was unclear from msdn.microsoft.com if their scanf() supported   * It was unclear from msdn.microsoft.com if their scanf() supported
  * [0-9a-fA-F], so I'll stay away from it.   * [0-9a-fA-F], so I'll stay away from it.
    *
  */   */
 static int is_mac_addr(const char *p)  static int is_mac_addr(const char *p)
 {  {
Line 705  static int is_mac_addr(const char *p) Line 882  static int is_mac_addr(const char *p)
 }  }
   
 /**  /**
 * lots of parms since C arrays suck * parse mac address.
  *
  * return 1 on success.
  */   */
static int get_mac_addr(const char *in,int
                        unsigned int *n0,get_mac_addr(const char *in, uint8_t *out)
                        unsigned int *n1, 
                        unsigned int *n2, 
                        unsigned int *n3, 
                        unsigned int *n4, 
                        unsigned int *n5) 
 {  {
        if (6 == sscanf(in, "%x:%x:%x:%x:%x:%x",n0,n1,n2,n3,n4,n5)) {        const char *formats[] = {
                return 1;                "%x:%x:%x:%x:%x:%x",
        } else if(6 == sscanf(in, "%2x%x.%2x%x.%2x%x",n0,n1,n2,n3,n4,n5)) {                "%2x%x.%2x%x.%2x%x",
                return 1;                "%x-%x-%x-%x-%x-%x",
        } else if(6 == sscanf(in, "%x-%x-%x-%x-%x-%x",n0,n1,n2,n3,n4,n5)) {                NULL,
                return 1;        };
        }        int c;
        return 0;        for (c = 0; formats[c]; c++) {
                 unsigned int n[6];
                 if (6 == sscanf(in, formats[c],
                                 &n[0], &n[1], &n[2], &n[3], &n[4], &n[5])) {
                         for (c = 0; c < 6; c++) {
                                 out[c] = n[c] & 0xff;
                         }
                         return 1;
                 }
         }
         return 0;
 }  }
   
 /**  /**
Line 751  timespec2dbl(const struct timespec *tv) Line 935  timespec2dbl(const struct timespec *tv)
 }  }
   
 /**  /**
    * return number of microseconds to wait for packets.
    */
   static uint32_t
   wait_time(double deadline, uint32_t packetwait)
   {
           struct timespec ts;
   
           // If deadline not specified, then don't use it.
           if (deadline < 0) {
                   return packetwait;
           }
   
           getclock(&ts);
           const double max_wait = deadline - timespec2dbl(&ts);
           if (max_wait < 0) {
                   return 0;
           }
           if (max_wait > packetwait / 1000000.0) {
                   return packetwait;
           }
           return max_wait * 1000000;
   }
   
   /**
  * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so   * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so
  *   *
  * Still, I'm using at least 128bytes below   * Still, I'm using at least 128bytes below
Line 791  static char *ts2str(const struct timespec *tv, const s Line 999  static char *ts2str(const struct timespec *tv, const s
         return buf;          return buf;
 }  }
   
   
   
 /** Send directed IPv4 ICMP echo request.  /** Send directed IPv4 ICMP echo request.
  *   *
  * \param id      IP id   * \param id      IP id
Line 802  static void Line 1008  static void
 pingmac_send(uint16_t id, uint16_t seq)  pingmac_send(uint16_t id, uint16_t seq)
 {  {
         static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0;          static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0;
   
           // Padding size chosen fairly arbitrarily.
           // Without this padding some systems (e.g. Raspberry Pi 3
           // wireless interface) failed. dmesg said:
           //   arping: packet size is too short (42 <= 50)
           const size_t padding_size = sizeof(struct timespec) + payload_suffix_size;
           uint8_t padding[padding_size];
           memset(padding, 0, padding_size);
           {
                   struct timespec ts;
                   getclock(&ts);
                   memcpy(padding, &ts, sizeof(struct timespec));
                   memcpy(&padding[sizeof(struct timespec)],
                          payload_suffix, payload_suffix_size);
           }
   
         int c;          int c;
   
         if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */          if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */
Line 809  pingmac_send(uint16_t id, uint16_t seq) Line 1031  pingmac_send(uint16_t id, uint16_t seq)
                                                    0, /* checksum */                                                     0, /* checksum */
                                                    id, /* id */                                                     id, /* id */
                                                    seq, /* seq */                                                     seq, /* seq */
                                                   NULL, /* payload */                                                   (uint8_t*)padding, /* payload */
                                                   0, /* payload len */                                                   sizeof padding, /* payload len */
                                                    libnet,                                                     libnet,
                                                    icmp))) {                                                     icmp))) {
                 fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n",                  fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n",
Line 819  pingmac_send(uint16_t id, uint16_t seq) Line 1041  pingmac_send(uint16_t id, uint16_t seq)
         }          }
   
         if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H          if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H
                                          + LIBNET_ICMPV4_ECHO_H + 0,                                          + LIBNET_ICMPV4_ECHO_H
                                           + sizeof padding,
                                           0, /* ToS */                                            0, /* ToS */
                                           id, /* id */                                            id, /* id */
                                           0, /* frag */                                            0, /* frag */
Line 836  pingmac_send(uint16_t id, uint16_t seq) Line 1059  pingmac_send(uint16_t id, uint16_t seq)
                         libnet_geterror(libnet));                          libnet_geterror(libnet));
                 sigint(0);                  sigint(0);
         }          }
        if (-1 == (eth = libnet_build_ethernet(dstmac,        if (vlan_tag >= 0) {
                                               srcmac,                eth = libnet_build_802_1q(dstmac,
                                               ETHERTYPE_IP,                                          srcmac,
                                               NULL,                                          ETHERTYPE_VLAN,
                                               0,                                          vlan_prio,
                                               libnet,                                          0, // cfi
                                               eth))) {                                          vlan_tag,
                fprintf(stderr, "libnet_build_ethernet(): %s\n",                                          ETHERTYPE_IP,
                        libnet_geterror(libnet));                                          NULL, // payload
                sigint(0);                                          0, // payload length
        }                                          libnet,
                                           eth);
         } else {
                 eth = libnet_build_ethernet(dstmac,
                                             srcmac,
                                             ETHERTYPE_IP,
                                             NULL, // payload
                                             0, // payload length
                                             libnet,
                                             eth);
         }
         if (-1 == eth) {
                 fprintf(stderr, "arping: %s: %s\n",
                         (vlan_tag >= 0) ? "libnet_build_802_1q()" :
                         "libnet_build_ethernet()",
                         libnet_geterror(libnet));
                 sigint(0);
         }
         if (verbose > 1) {          if (verbose > 1) {
                 getclock(&lastpacketsent);                  getclock(&lastpacketsent);
                 printf("arping: sending packet at time %ld.%09ld\n",                  printf("arping: sending packet at time %ld.%09ld\n",
Line 869  static void Line 1109  static void
 pingip_send()  pingip_send()
 {  {
         static libnet_ptag_t arp=0,eth=0;          static libnet_ptag_t arp=0,eth=0;
   
           // Padding size chosen fairly arbitrarily.
           // Without this padding some systems (e.g. Raspberry Pi 3
           // wireless interface) failed. dmesg said:
           //   arping: packet size is too short (42 <= 50)
           const uint8_t padding[16] = {0};
   
         if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER,          if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER,
                                           ETHERTYPE_IP,                                            ETHERTYPE_IP,
                                           ETH_ALEN,                                            ETH_ALEN,
Line 878  pingip_send() Line 1125  pingip_send()
                                           (uint8_t*)&srcip,                                            (uint8_t*)&srcip,
                                           unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull,                                            unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull,
                                           (uint8_t*)&dstip,                                            (uint8_t*)&dstip,
                                          NULL,                                          (uint8_t*)padding,
                                          0,                                          sizeof padding,
                                           libnet,                                            libnet,
                                           arp))) {                                            arp))) {
                 fprintf(stderr, "arping: libnet_build_arp(): %s\n",                  fprintf(stderr, "arping: libnet_build_arp(): %s\n",
                         libnet_geterror(libnet));                          libnet_geterror(libnet));
                 sigint(0);                  sigint(0);
         }          }
        if (-1 == (eth = libnet_build_ethernet(dstmac,
                                               srcmac,        if (vlan_tag >= 0) {
                                               ETHERTYPE_ARP,                eth = libnet_build_802_1q(dstmac,
                                               NULL,                                          srcmac,
                                               0,                                          ETHERTYPE_VLAN,
                                               libnet,                                          vlan_prio,
                                               eth))) {                                          0, // cfi
                fprintf(stderr, "arping: libnet_build_ethernet(): %s\n",                                          vlan_tag,
                                           ETHERTYPE_ARP,
                                           NULL, // payload
                                           0, // payload size
                                           libnet,
                                           eth);
         } else {
                 eth = libnet_build_ethernet(dstmac,
                                             srcmac,
                                             ETHERTYPE_ARP,
                                             NULL, // payload
                                             0, // payload size
                                             libnet,
                                             eth);
         }
         if (-1 == eth) {
                 fprintf(stderr, "arping: %s: %s\n",
                         (vlan_tag >= 0) ? "libnet_build_802_1q()" :
                         "libnet_build_ethernet()",
                         libnet_geterror(libnet));                          libnet_geterror(libnet));
                 sigint(0);                  sigint(0);
         }          }
Line 904  pingip_send() Line 1169  pingip_send()
                        (long)lastpacketsent.tv_nsec);                         (long)lastpacketsent.tv_nsec);
         }          }
         if (-1 == libnet_write(libnet)) {          if (-1 == libnet_write(libnet)) {
                fprintf(stderr, "arping: libnet_write(): %s\n",                 fprintf(stderr, "arping: libnet_write(): %s\n",
                         libnet_geterror(libnet));                          libnet_geterror(libnet));
                 sigint(0);                  sigint(0);
         }          }
Line 917  pingip_send() Line 1182  pingip_send()
  * \param h       packet metadata   * \param h       packet metadata
  * \param packet  packet data   * \param packet  packet data
  */   */
static voidvoid
pingip_recv(const char *unused, struct pcap_pkthdr *h, uint8_t *packet)pingip_recv(const char *unused, struct pcap_pkthdr *h, const char * const packet)
 {  {
           const unsigned char *pkt_srcmac;
           const struct libnet_802_1q_hdr *veth;
         struct libnet_802_3_hdr *heth;          struct libnet_802_3_hdr *heth;
         struct libnet_arp_hdr *harp;          struct libnet_arp_hdr *harp;
         struct timespec arrival;          struct timespec arrival;
        int c;        UNUSED(unused);
   
         if (verbose > 2) {          if (verbose > 2) {
                 printf("arping: received response for IP ping\n");                  printf("arping: received response for IP ping\n");
Line 931  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1198  pingip_recv(const char *unused, struct pcap_pkthdr *h,
   
         getclock(&arrival);          getclock(&arrival);
   
        heth = (void*)packet;        if (vlan_tag >= 0) {
        harp = (void*)((char*)heth + LIBNET_ETH_H);                veth = (void*)packet;
                 harp = (void*)((char*)veth + LIBNET_802_1Q_H);
                 pkt_srcmac = veth->vlan_shost;
         } else {
                 // Short packet.
                 if (h->caplen < LIBNET_ETH_H + LIBNET_ARP_H + 2*(ETH_ALEN + 4)) {
                         return;
                 }
   
                   heth = (void*)packet;
                   harp = (void*)((char*)heth + LIBNET_ETH_H);
                   pkt_srcmac = heth->_802_3_shost;
                   // Wrong length of hardware address.
                   if (harp->ar_hln != ETH_ALEN) {
                           return;
                   }
   
                   // Wrong length of protocol address.
                   if (harp->ar_pln != 4) {
                           return;
                   }
           }
   
         // ARP reply.          // ARP reply.
         if (htons(harp->ar_op) != ARPOP_REPLY) {          if (htons(harp->ar_op) != ARPOP_REPLY) {
                 return;                  return;
Line 970  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1258  pingip_recv(const char *unused, struct pcap_pkthdr *h,
                 printf("arping: ... sent by acceptable host\n");                  printf("arping: ... sent by acceptable host\n");
         }          }
   
           // Special case: If we're not in promisc mode we could still
           // get packets where DST mac is not us, if they're *sent* from
           // the local host. This is an edge case but in general falls under "is promisc?".
           //
           // It may cause confusion because `-p` now means not just
           // enable promisc mode (disable filter on card / in kernel),
           // but also allow packets to any destination (disable filter
           // in `arping`).
           {
                   const uint8_t* p = (u_char*)harp
                           + sizeof(struct libnet_arp_hdr)
                           + ETH_ALEN
                           + IP_ALEN;
                   char buf[128];
                   if (!promisc && memcmp(p, srcmac, ETH_ALEN)) {
                           format_mac(p, buf, sizeof buf);
                           if (verbose > 3) {
                                   printf("arping: ... but sent from %s\n", buf);
                           }
                           return;
                   }
           }
           if (verbose > 3) {
                   printf("arping: ... destination is the source we used\n");
           }
   
   
         // Actually the IPv4 address we asked for.          // Actually the IPv4 address we asked for.
         uint32_t ip;          uint32_t ip;
         memcpy(&ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4);          memcpy(&ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4);
Line 991  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1306  pingip_recv(const char *unused, struct pcap_pkthdr *h,
                 break;                  break;
         case NORMAL:          case NORMAL:
                 printf("%d bytes from %s (%s): index=%d",                  printf("%d bytes from %s (%s): index=%d",
                       h->len, format_mac(heth->_802_3_shost,                       h->len, format_mac(pkt_srcmac, buf, sizeof(buf)),
                                          buf, sizeof(buf)), 
                        libnet_addr2name4(ip, 0), numrecvd);                         libnet_addr2name4(ip, 0), numrecvd);
   
                 if (alsototal) {                  if (alsototal) {
Line 1004  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1318  pingip_recv(const char *unused, struct pcap_pkthdr *h,
         case QUIET:          case QUIET:
                 break;                  break;
         case RAWRAW:          case RAWRAW:
                printf("%s %s", format_mac(heth->_802_3_shost,                printf("%s %s", format_mac(pkt_srcmac, buf, sizeof(buf)),
                                           buf, sizeof(buf)), 
                        libnet_addr2name4(ip, 0));                         libnet_addr2name4(ip, 0));
                 break;                  break;
         case RRAW:          case RRAW:
                 printf("%s", libnet_addr2name4(ip, 0));                  printf("%s", libnet_addr2name4(ip, 0));
                 break;                  break;
         case RAW:          case RAW:
                printf("%s", format_mac(heth->_802_3_shost,                printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf)));
                                        buf, sizeof(buf))); 
                 break;                  break;
         default:          default:
                 fprintf(stderr, "arping: can't happen!\n");                  fprintf(stderr, "arping: can't happen!\n");
Line 1029  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1341  pingip_recv(const char *unused, struct pcap_pkthdr *h,
         }          }
         if (numrecvd) {          if (numrecvd) {
                 if (memcmp(lastreplymac,                  if (memcmp(lastreplymac,
                           heth->_802_3_shost, ETH_ALEN)) {                           pkt_srcmac, ETH_ALEN)) {
                         dupfound = 1;                          dupfound = 1;
                 }                  }
         }          }
        memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN);        memcpy(lastreplymac, pkt_srcmac, ETH_ALEN);
   
         numrecvd++;          numrecvd++;
         if (numrecvd >= max_replies) {          if (numrecvd >= max_replies) {
Line 1046  pingip_recv(const char *unused, struct pcap_pkthdr *h, Line 1358  pingip_recv(const char *unused, struct pcap_pkthdr *h,
  * \param h       packet metadata   * \param h       packet metadata
  * \param packet  packet data   * \param packet  packet data
  */   */
static voidvoid
pingmac_recv(const char *unused, struct pcap_pkthdr *h, uint8_t *packet)pingmac_recv(const char* unused, struct pcap_pkthdr *h, uint8_t *packet)
 {  {
           const unsigned char *pkt_dstmac;
           const unsigned char *pkt_srcmac;
           const struct libnet_802_1q_hdr *veth;
         struct libnet_802_3_hdr *heth;          struct libnet_802_3_hdr *heth;
         struct libnet_ipv4_hdr *hip;          struct libnet_ipv4_hdr *hip;
         struct libnet_icmpv4_hdr *hicmp;          struct libnet_icmpv4_hdr *hicmp;
         struct timespec arrival;          struct timespec arrival;
        int c;        UNUSED(unused);
   
         if(verbose>2) {          if(verbose>2) {
                 printf("arping: received response for mac ping\n");                  printf("arping: received response for mac ping\n");
Line 1061  pingmac_recv(const char *unused, struct pcap_pkthdr *h Line 1376  pingmac_recv(const char *unused, struct pcap_pkthdr *h
   
         getclock(&arrival);          getclock(&arrival);
   
        heth = (void*)packet;        if (vlan_tag >= 0) {
        hip = (void*)((char*)heth + LIBNET_ETH_H);                veth = (void*)packet;
        hicmp = (void*)((char*)hip + LIBNET_IPV4_H);                hip = (void*)((char*)veth + LIBNET_802_1Q_H);
                 hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
                 pkt_srcmac = veth->vlan_shost;
                 pkt_dstmac = veth->vlan_dhost;
         } else {
                 heth = (void*)packet;
                 hip = (void*)((char*)heth + LIBNET_ETH_H);
                 hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
                 pkt_srcmac = heth->_802_3_shost;
                 pkt_dstmac = heth->_802_3_dhost;
         }
   
         // Dest MAC must be me.          // Dest MAC must be me.
        if (memcmp(heth->_802_3_dhost, srcmac, ETH_ALEN)) {        if (memcmp(pkt_dstmac, srcmac, ETH_ALEN)) {
                 return;                  return;
         }          }
   
           if (verbose > 3) {
                   printf("arping: ... right dst mac\n");
           }
   
         // Source MAC must match, if set.          // Source MAC must match, if set.
         if (memcmp(dstmac, ethxmas, ETH_ALEN)) {          if (memcmp(dstmac, ethxmas, ETH_ALEN)) {
                if (memcmp(heth->_802_3_shost, dstmac, ETH_ALEN)) {                if (memcmp(pkt_srcmac, dstmac, ETH_ALEN)) {
                         return;                          return;
                 }                  }
         }          }
   
           if (verbose > 3) {
                   printf("arping: ... right src mac\n");
           }
   
         // IPv4 Address must be me (maybe).          // IPv4 Address must be me (maybe).
         if (addr_must_be_same) {          if (addr_must_be_same) {
                 uint32_t tmp;                  uint32_t tmp;
Line 1086  pingmac_recv(const char *unused, struct pcap_pkthdr *h Line 1419  pingmac_recv(const char *unused, struct pcap_pkthdr *h
                 }                  }
         }          }
   
        // Must be ICMP echo reply.        if (verbose > 3) {
                 printf("arping: ... src IP acceptable\n");
         }
 
         // Must be ICMP echo reply type.
         if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) {          if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) {
                 return;                  return;
         }          }
   
           if (verbose > 3) {
                   printf("arping: ... is echo reply type\n");
           }
   
           // Must be ICMP echo reply code 0.
           if (htons(hicmp->icmp_code) != 0) {
                   return;
           }
   
           if (verbose > 3) {
                   printf("arping: ... is echo reply code\n");
           }
   
           const char* payload = (char*)hicmp + LIBNET_ICMPV4_ECHO_H;
           const ssize_t payload_size = h->len - (payload - (char*)packet);
           if (payload_size < 0) {
                   return;
           }
           if (payload_size < sizeof(struct timespec) + payload_suffix_size) {
                   return;
           }
           if (verbose > 3) {
                   printf("arping: ... correct payload size (%zd)\n",
                          payload_size);
           }
           if (memcmp(&payload[sizeof(struct timespec)],
                       payload_suffix, payload_suffix_size)) {
                       return;
           }
           if (verbose > 3) {
                   printf("arping: ... correct payload suffix\n");
           }
   
         update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));          update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));
         if (beep) {          if (beep) {
                 printf("\a");                  printf("\a");
Line 1106  pingmac_recv(const char *unused, struct pcap_pkthdr *h Line 1476  pingmac_recv(const char *unused, struct pcap_pkthdr *h
         case NORMAL:          case NORMAL:
                 printf("%d bytes from %s (%s): icmp_seq=%d time=%s", h->len,                  printf("%d bytes from %s (%s): icmp_seq=%d time=%s", h->len,
                        libnet_addr2name4(*(int*)&hip->ip_src, 0),                         libnet_addr2name4(*(int*)&hip->ip_src, 0),
                       format_mac(heth->_802_3_shost, buf, sizeof(buf)),                       format_mac(pkt_srcmac, buf, sizeof(buf)),
                        htons(hicmp->icmp_seq),                         htons(hicmp->icmp_seq),
                        ts2str(&lastpacketsent, &arrival, buf2, sizeof(buf2)));                         ts2str(&lastpacketsent, &arrival, buf2, sizeof(buf2)));
                 break;                  break;
Line 1114  pingmac_recv(const char *unused, struct pcap_pkthdr *h Line 1484  pingmac_recv(const char *unused, struct pcap_pkthdr *h
                 printf("%s", libnet_addr2name4(hip->ip_src.s_addr, 0));                  printf("%s", libnet_addr2name4(hip->ip_src.s_addr, 0));
                 break;                  break;
         case RRAW:          case RRAW:
                printf("%s", format_mac(heth->_802_3_shost, buf, sizeof(buf)));                printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf)));
                 break;                  break;
         case RAWRAW:          case RAWRAW:
                 printf("%s %s",                  printf("%s %s",
                       format_mac(heth->_802_3_shost, buf, sizeof(buf)),                       format_mac(pkt_srcmac, buf, sizeof(buf)),
                        libnet_addr2name4(hip->ip_src.s_addr, 0));                         libnet_addr2name4(hip->ip_src.s_addr, 0));
                 break;                  break;
         default:          default:
Line 1162  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand Line 1532  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand
        struct timespec endtime;         struct timespec endtime;
        char done = 0;         char done = 0;
        int fd;         int fd;
       int old_received;       unsigned int old_received;
   
        if (verbose > 3) {         if (verbose > 3) {
                printf("arping: receiving packets...\n");                 printf("arping: receiving packets...\n");
Line 1174  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand Line 1544  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand
        fixup_timespec(&endtime);         fixup_timespec(&endtime);
   
        fd = pcap_get_selectable_fd(pcap);         fd = pcap_get_selectable_fd(pcap);
          if (fd == -1) {
                  fprintf(stderr, "arping: pcap_get_selectable_fd()=-1: %s\n",
                          pcap_geterr(pcap));
                  exit(1);
          }
        old_received = numrecvd;         old_received = numrecvd;
   
        for (;!done;) {         for (;!done;) {
Line 1240  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand Line 1615  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand
                                        case DOT:                                         case DOT:
                                                printf(".");                                                 printf(".");
                                                break;                                                 break;
                                          case RAW:
                                          case RAWRAW:
                                          case RRAW:
                                          case QUIET:
                                                  break;
                                        }                                         }
                                        fflush(stdout);                                         fflush(stdout);
                                }                                 }
Line 1281  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand Line 1661  ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand
        }         }
 }  }
   
   // return 1 on success.
   static int
   xresolve(libnet_t* l, const char *name, int r, uint32_t *addr)
   {
           if (!strcmp(ip_broadcast, name)) {
                   *addr = 0xffffffff;
                   return 1;
           }
           *addr = libnet_name2addr4(l, (char*)name, r);
           return *addr != 0xffffffff;
   }
   
 /**  /**
  *   *
  */   */
int main(int argc, char **argv)int
 arping_main(int argc, char **argv)
 {  {
         char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];          char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];
         char *cp;          char *cp;
        int promisc = 0;        const char *srcip_opt = NULL;
        int srcip_given = 0;        const char *dstip_opt = NULL;
        int srcmac_given = 0;        // `dstip_given` can be set even when there's no arg past flags on the
         // cmdline and -B not set. E.g. -d defaults to self, so requires no
         // extra arg.
         int dstip_given = 0;          int dstip_given = 0;
        const char *ifname = NULL;        const char *srcmac_opt = NULL;
        char *parm;        const char *dstmac_opt = NULL;
         const char *ifname = NULL;      // -i/-I
         int opt_B = 0;
         int opt_T = 0;
         int opt_U = 0;
         const char* drop_group = NULL;  // -g
         const char *parm; // First argument, meaning the target IP.
         int c;          int c;
        unsigned int maxcount = -1;        int maxcount = -1;
         int dont_use_arping_lookupdev=0;          int dont_use_arping_lookupdev=0;
         struct bpf_program bp;          struct bpf_program bp;
         pcap_t *pcap;          pcap_t *pcap;
         enum { NONE, PINGMAC, PINGIP } mode = NONE;          enum { NONE, PINGMAC, PINGIP } mode = NONE;
        unsigned int packetwait = 1000000;        unsigned int packetwait = 1000000; // Default one second.
         double deadline = -1;
         char bpf_filter[64];
         ebuf[0] = 0;          ebuf[0] = 0;
           srandom(time(NULL));
   
         for (c = 1; c < argc; c++) {          for (c = 1; c < argc; c++) {
                 if (!strcmp(argv[c], "--help")) {                  if (!strcmp(argv[c], "--help")) {
Line 1316  int main(int argc, char **argv) Line 1720  int main(int argc, char **argv)
         memcpy(dstmac, ethxmas, ETH_ALEN);          memcpy(dstmac, ethxmas, ETH_ALEN);
   
         while (EOF != (c = getopt(argc, argv,          while (EOF != (c = getopt(argc, argv,
                                  "0aAbBC:c:dDeFhi:I:m:pPqrRs:S:t:T:uUvw:W:"))) {                                  "0aAbBC:c:dDeFg:hi:I:m:pPqQ:rRs:S:t:T:uUvV:w:W:"))) {
                 switch(c) {                  switch(c) {
                 case '0':                  case '0':
                        srcip = 0;                        srcip_opt = "0.0.0.0";
                        srcip_given = 1; 
                         break;                          break;
                 case 'a':                  case 'a':
                         beep = 1;                          beep = 1;
Line 1329  int main(int argc, char **argv) Line 1732  int main(int argc, char **argv)
                         addr_must_be_same = 1;                          addr_must_be_same = 1;
                         break;                          break;
                 case 'b':                  case 'b':
                        srcip = 0xffffffff;                        srcip_opt = ip_broadcast;
                        srcip_given = 1; 
                         break;                          break;
                 case 'B':                  case 'B':
                        dstip = 0xffffffff;                        dstip_opt = ip_broadcast;
                        dstip_given = 1;                        dstip_given = 1;
                         opt_B = 1;
                         break;                          break;
                 case 'c':                  case 'c':
                         maxcount = atoi(optarg);                          maxcount = atoi(optarg);
Line 1356  int main(int argc, char **argv) Line 1759  int main(int argc, char **argv)
                         break;                          break;
                 case 'h':                  case 'h':
                         usage(0);                          usage(0);
                           exit(0); // Needless but shuts up compiler warnings.
                   case 'g':
                           drop_group = optarg;
                           break;
                 case 'i':                  case 'i':
                         if (strchr(optarg, ':')) {                          if (strchr(optarg, ':')) {
                                 fprintf(stderr, "arping: If you're trying to "                                  fprintf(stderr, "arping: If you're trying to "
Line 1366  int main(int argc, char **argv) Line 1773  int main(int argc, char **argv)
                                         "purpose)\n");                                          "purpose)\n");
                                 exit(1);                                  exit(1);
                         }                          }
                 case 'I': /* FALL THROUGH */  
                         ifname = optarg;                          ifname = optarg;
                           break;
                   case 'I':
                           ifname = optarg;
                         break;                          break;
                 case 'm':                  case 'm':
                         timestamp_type = optarg;                          timestamp_type = optarg;
Line 1381  int main(int argc, char **argv) Line 1790  int main(int argc, char **argv)
                 case 'q':                  case 'q':
                         display = QUIET;                          display = QUIET;
                         break;                          break;
                   case 'Q':
                           vlan_prio = atoi(optarg);
                           if (vlan_prio < 0 || vlan_prio > 7) {
                                   fprintf(stderr,
                                           "arping: 802.1p priority must be 0-7. It's %d\n",
                                           vlan_prio);
                                   exit(1);
                           }
                           break;
                 case 'r':                  case 'r':
                         display = (display==RRAW)?RAWRAW:RAW;                          display = (display==RRAW)?RAWRAW:RAW;
                         break;                          break;
Line 1388  int main(int argc, char **argv) Line 1806  int main(int argc, char **argv)
                         display = (display==RAW)?RAWRAW:RRAW;                          display = (display==RAW)?RAWRAW:RRAW;
                         break;                          break;
                 case 's': { /* spoof source MAC */                  case 's': { /* spoof source MAC */
                        unsigned int n[6];                        srcmac_opt = optarg;
                        if (!get_mac_addr(optarg, 
                                          &n[0],&n[1],&n[2], 
                                          &n[3],&n[4],&n[5])){ 
                                fprintf(stderr, "arping: Weird MAC addr %s\n", 
                                        optarg); 
                                exit(1); 
                        } 
                        for (c = 0; c < 6; c++) { 
                                srcmac[c] = n[c] & 0xff; 
                        } 
                        srcmac_given = 1; 
                         break;                          break;
                 }                  }
                 case 'S': /* set source IP, may be null for don't-know */                  case 'S': /* set source IP, may be null for don't-know */
                        do_libnet_init(ifname, 0);                        srcip_opt = optarg;
                        if (-1 == (srcip = libnet_name2addr4(libnet, 
                                                             optarg, 
                                                             LIBNET_RESOLVE))){ 
                                fprintf(stderr, "arping: Can't resolve %s, or " 
                                        "%s is broadcast. If it is, use -b" 
                                        " instead of -S\n", optarg,optarg); 
                                exit(1); 
                        } 
                        srcip_given = 1; 
                         break;                          break;
                 case 't': { /* set taget mac */                  case 't': { /* set taget mac */
                        unsigned int n[6];                        dstmac_opt = optarg;
                        if (mode == PINGMAC) { 
                                fprintf(stderr, "arping: -t can only be used " 
                                        "in IP ping mode\n"); 
                                exit(1); 
                        } 
                        if (!get_mac_addr(optarg, 
                                          &n[0],&n[1],&n[2], 
                                          &n[3],&n[4],&n[5])){ 
                                fprintf(stderr, "Illegal MAC addr %s\n", 
                                        optarg); 
                                exit(1); 
                        } 
                        for (c = 0; c < 6; c++) { 
                                dstmac[c] = n[c] & 0xff; 
                        } 
                         mode = PINGIP;                          mode = PINGIP;
                         break;                          break;
                 }                  }
                 case 'T': /* set destination IP */                  case 'T': /* set destination IP */
                        if (mode == PINGIP) {                        opt_T = 1;
                                fprintf(stderr, "arping: -T can only be used "                        dstip_opt = optarg;
                                        "in MAC ping mode\n"); 
                                exit(1); 
                        } 
                        do_libnet_init(ifname, 0); 
                        if (-1 == (dstip = libnet_name2addr4(libnet, 
                                                             optarg, 
                                                             LIBNET_RESOLVE))){ 
                                fprintf(stderr,"arping: Can't resolve %s, or " 
                                        "%s is broadcast. If it is, use -B " 
                                        "instead of -T\n",optarg,optarg); 
                                exit(1); 
                        } 
                         mode = PINGMAC;                          mode = PINGMAC;
                         break;                          break;
                 case 'u':                  case 'u':
                         alsototal = 1;                          alsototal = 1;
                         break;                          break;
                 case 'U':                  case 'U':
                        if (mode == PINGMAC) {                        opt_U = 1;
                                fprintf(stderr, "arping: -U can only be used " 
                                        "in IP ping mode\n"); 
                                exit(1); 
                        } 
                         unsolicited = 1;                          unsolicited = 1;
                           mode = PINGIP;
                         break;                          break;
                 case 'v':                  case 'v':
                         verbose++;                          verbose++;
                         break;                          break;
                   case 'V':
                           vlan_tag = atoi(optarg);
                           if (vlan_tag < 0 || vlan_tag > 4095) {
                                   fprintf(stderr,
                                           "arping: vlan tag must 0-4095. Is %d\n",
                                           vlan_tag);
                                   exit(1);
                           }
                           break;
                 case 'w':                  case 'w':
                        packetwait = (unsigned)atoi(optarg);                        deadline = atof(optarg);
                         break;                          break;
                 case 'W':                  case 'W':
                         packetwait = (unsigned)(1000000.0 * atof(optarg));                          packetwait = (unsigned)(1000000.0 * atof(optarg));
Line 1476  int main(int argc, char **argv) Line 1853  int main(int argc, char **argv)
                 }                  }
         }          }
   
           if (argc - optind > 1) {
                   // Can be zero if using -d or -B.
                   fprintf(stderr, "arping: Too many args on command line."
                           " Expected at most one.\n");
                   exit(1);
           }
   
           // Generate random payload suffix for MAC pings, to be able to
           // differentiate from unrelated ping replies.
           if (payload_suffix_size < 0) {
                   payload_suffix_size = 4;
                   payload_suffix = malloc(payload_suffix_size);
                   if (payload_suffix) {
                           const ssize_t rc = xgetrandom(payload_suffix, payload_suffix_size, 0);
                           if (rc == -1) {
                                   fprintf(stderr,
                                           "arping: failed to get %zd random bytes: %s\n",
                                           payload_suffix_size,
                                           strerror(errno));
                                   free(payload_suffix);
                                   payload_suffix = NULL;
                           } else if (payload_suffix_size != rc) {
                                   fprintf(stderr,
                                           "arping: only got %zd out of %zd bytes for random suffix\n",
                                           rc, payload_suffix_size);
                           }
                   } else {
                           fprintf(stderr, "arping: failed to allocate %zd bytes for payload suffix.\n",
                                   payload_suffix_size);
                   }
   
                   if (!payload_suffix) {
                           fprintf(stderr, "arping:  Using constant suffix.\n");
                           payload_suffix = "arping";
                           payload_suffix_size = strlen(payload_suffix);
                   }
           }
   
           if (((mode == PINGIP) && opt_T)
               || ((mode == PINGMAC) && (opt_B || dstmac_opt || opt_U))) {
                   fprintf(stderr, "arping: -T can only be used to ping MAC"
                           " and -BtU only to ping IPs");
                   exit(1);
           }
           if (opt_T && opt_B) {
                   fprintf(stderr,
                           "arping: -B can't be used with -T,"
                           " since they set the same thing\n");
                   exit(1);
           }
   
           if (srcmac_opt != NULL) {
                   if (!get_mac_addr(srcmac_opt, srcmac)) {
                           fprintf(stderr, "arping: Weird MAC addr %s\n",
                                   srcmac_opt);
                           exit(1);
                   }
           }
   
           if (dstmac_opt != NULL) {
                   if (!get_mac_addr(dstmac_opt, dstmac)) {
                           fprintf(stderr, "Illegal MAC addr %s\n", dstmac_opt);
                           exit(1);
                   }
           }
   
           if (srcip_opt != NULL) {
                   do_libnet_init(ifname, 0);
                   if (!xresolve(libnet, srcip_opt, LIBNET_RESOLVE, &srcip)) {
                           fprintf(stderr, "arping: Can't resolve %s, or "
                                   "%s is broadcast. If it is, use -b"
                                   " instead of -S\n", srcip_opt,srcip_opt);
                           exit(1);
                   }
           }
   
           if (dstip_opt) {
                   do_libnet_init(ifname, 0);
                   if (!xresolve(libnet, dstip_opt, LIBNET_RESOLVE, &dstip)) {
                           fprintf(stderr,"arping: Can't resolve %s, or "
                                   "%s is broadcast. If it is, use -B "
                                   "instead of -T\n",dstip_opt,dstip_opt);
                           exit(1);
                   }
           }
   
           if (vlan_prio >= 0 && vlan_tag == -1) {
                   fprintf(stderr, "arping: -Q requires the use of 802.1Q (-V)\n");
                   exit(1);
           }
           if (vlan_prio == -1) {
                   vlan_prio = 0;
           }
   
         if (verbose > 1) {          if (verbose > 1) {
 #if HAVE_CLOCK_MONOTONIC  #if HAVE_CLOCK_MONOTONIC
                 struct timespec ts;                  struct timespec ts;
                clock_getres(CLOCK_MONOTONIC, &ts);                if (clock_getres(CLOCK_MONOTONIC, &ts)) {
                printf("arping: clock_getres() = %ld %ld\n",                        fprintf(stderr,
                       (long)ts.tv_sec, (long)ts.tv_nsec);                                "arping: clock_getres(CLOCK_MONOTONIC, ...): %s\n",
                                 strerror(errno));
                 } else {
                         printf("arping: clock_getres() = %lds %ldns\n",
                                (long)ts.tv_sec, (long)ts.tv_nsec);
                 }
 #else  #else
                 printf("arping: Using gettimeofday() for time measurements\n");                  printf("arping: Using gettimeofday() for time measurements\n");
 #endif  #endif
         }          }
   
         if (display == DOT) {          if (display == DOT) {
                setvbuf(stdout, NULL, _IONBF, 0);                if (0 != setvbuf(stdout, NULL, _IONBF, 0)) {
                         fprintf(stderr,
                                 "arping: setvbuf(stdout, NULL, IONBF, 0): %s\n",
                                 strerror(errno));
                 }
         }          }
   
         if (finddup && maxcount == -1) {          if (finddup && maxcount == -1) {
Line 1512  int main(int argc, char **argv) Line 1992  int main(int argc, char **argv)
          * Handle dstip_given instead of ip address after parms (-B really)           * Handle dstip_given instead of ip address after parms (-B really)
          */           */
         if (mode == NONE) {          if (mode == NONE) {
                if (optind + 1 == argc) {                if (parm) {
                         mode = is_mac_addr(parm)?PINGMAC:PINGIP;                          mode = is_mac_addr(parm)?PINGMAC:PINGIP;
                 } else if (dstip_given) {                  } else if (dstip_given) {
                         mode = PINGIP;                          mode = PINGIP;
                         do_libnet_init(ifname, 0);                          do_libnet_init(ifname, 0);
                         parm = strdup(libnet_addr2name4(dstip,0));                          parm = strdup(libnet_addr2name4(dstip,0));
                         if (!parm) {                          if (!parm) {
                                fprintf(stderr, "arping: out of mem\n");                                fprintf(stderr, "arping: out of memory\n");
                                 exit(1);                                  exit(1);
                         }                          }
                 }                  }
Line 1551  int main(int argc, char **argv) Line 2031  int main(int argc, char **argv)
                                 "\n");                                  "\n");
                         exit(1);                          exit(1);
                 }                  }
                if (-1 == (dstip = libnet_name2addr4(libnet,                if (!xresolve(libnet, parm, LIBNET_RESOLVE, &dstip)) {
                                                     parm, 
                                                     LIBNET_RESOLVE))) { 
                         fprintf(stderr, "arping: Can't resolve %s\n", parm);                          fprintf(stderr, "arping: Can't resolve %s\n", parm);
                         exit(1);                          exit(1);
                 }                  }
Line 1564  int main(int argc, char **argv) Line 2042  int main(int argc, char **argv)
          * parse parm into dstmac           * parse parm into dstmac
          */           */
         if (mode == PINGMAC) {          if (mode == PINGMAC) {
                 unsigned int n[6];  
                 if (optind + 1 != argc) {                  if (optind + 1 != argc) {
                         usage(1);                          usage(1);
                 }                  }
Line 1574  int main(int argc, char **argv) Line 2051  int main(int argc, char **argv)
                                 "argument\n");                                  "argument\n");
                         exit(1);                          exit(1);
                 }                  }
                if (!get_mac_addr(argv[optind],                if (!get_mac_addr(argv[optind], dstmac)) {
                                  &n[0],&n[1],&n[2], 
                                  &n[3],&n[4],&n[5])) { 
                         fprintf(stderr, "arping: Illegal mac addr %s\n",                          fprintf(stderr, "arping: Illegal mac addr %s\n",
                                 argv[optind]);                                  argv[optind]);
                         return 1;                          return 1;
                 }                  }
                for (c = 0; c < 6; c++) {        }
                        dstmac[c] = n[c] & 0xff; 
                } 
        }        
   
         target = parm;          target = parm;
         /*          /*
Line 1645  int main(int argc, char **argv) Line 2117  int main(int argc, char **argv)
                 fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf);                  fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf);
                 exit(1);                  exit(1);
         }          }
        drop_privileges();        drop_privileges(drop_group);
         if (pcap_setnonblock(pcap, 1, ebuf)) {          if (pcap_setnonblock(pcap, 1, ebuf)) {
                 strip_newline(ebuf);                  strip_newline(ebuf);
                 fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf);                  fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf);
Line 1671  int main(int argc, char **argv) Line 2143  int main(int argc, char **argv)
   
         if (mode == PINGIP) {          if (mode == PINGIP) {
                 /* FIXME: better filter with addresses? */                  /* FIXME: better filter with addresses? */
                if (-1 == pcap_compile(pcap, &bp, "arp", 0,-1)) {                if (vlan_tag >= 0) {
                        fprintf(stderr, "arping: pcap_compile(): error\n");                        snprintf(bpf_filter, sizeof(bpf_filter),
                                  "vlan %u and arp", vlan_tag);
                 } else {
                         snprintf(bpf_filter, sizeof(bpf_filter), "arp");
                 }
                 if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0, -1)) {
                         fprintf(stderr, "arping: pcap_compile(%s): %s\n",
                                 bpf_filter, pcap_geterr(pcap));
                         exit(1);                          exit(1);
                 }                  }
         } else { /* ping mac */          } else { /* ping mac */
                 /* FIXME: better filter with addresses? */                  /* FIXME: better filter with addresses? */
                if (-1 == pcap_compile(pcap, &bp, "icmp", 0,-1)) {                if (vlan_tag >= 0) {
                        fprintf(stderr, "arping: pcap_compile(): error\n");                        snprintf(bpf_filter, sizeof(bpf_filter),
                                  "vlan %u and icmp", vlan_tag);
                 } else {
                         snprintf(bpf_filter, sizeof(bpf_filter), "icmp");
                 }
                 if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0,-1)) {
                         fprintf(stderr, "arping: pcap_compile(%s): %s\n",
                                 bpf_filter, pcap_geterr(pcap));
                         exit(1);                          exit(1);
                 }                  }
         }          }
         if (-1 == pcap_setfilter(pcap, &bp)) {          if (-1 == pcap_setfilter(pcap, &bp)) {
                fprintf(stderr, "arping: pcap_setfilter(): error\n");                fprintf(stderr, "arping: pcap_setfilter(): %s\n",
                         pcap_geterr(pcap));
                 exit(1);                  exit(1);
         }          }
   
         /*          /*
          * final init           * final init
          */           */
        if (!srcmac_given) {        if (srcmac_opt == NULL) {
                 if (!(cp = (char*)libnet_get_hwaddr(libnet))) {                  if (!(cp = (char*)libnet_get_hwaddr(libnet))) {
                         fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n",                          fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n",
                                 libnet_geterror(libnet));                                  libnet_geterror(libnet));
Line 1698  int main(int argc, char **argv) Line 2185  int main(int argc, char **argv)
                 }                  }
                 memcpy(srcmac, cp, ETH_ALEN);                  memcpy(srcmac, cp, ETH_ALEN);
         }          }
        if (!srcip_given) {        if (srcip_opt == NULL) {
                if (-1 == (srcip = libnet_get_ipaddr4(libnet))) {                if (ip_broadcast_num == (srcip = libnet_get_ipaddr4(libnet))) {
                         fprintf(stderr,                          fprintf(stderr,
                                 "arping: Unable to get the IPv4 address of "                                  "arping: Unable to get the IPv4 address of "
                                 "interface %s:\narping: %s"                                  "interface %s:\narping: %s"
Line 1719  int main(int argc, char **argv) Line 2206  int main(int argc, char **argv)
                        format_mac(srcmac, buf, sizeof(buf)));                         format_mac(srcmac, buf, sizeof(buf)));
         }          }
   
           drop_more_privileges();
   
         if (display == NORMAL) {          if (display == NORMAL) {
                 printf("ARPING %s\n", parm);                  printf("ARPING %s\n", parm);
Line 1727  int main(int argc, char **argv) Line 2215  int main(int argc, char **argv)
         /*          /*
          * let's roll           * let's roll
          */           */
           if (deadline > 0) {
                   struct timespec ts;
                   getclock(&ts);
                   deadline += timespec2dbl(&ts);
           }
         if (mode == PINGIP) {          if (mode == PINGIP) {
                unsigned int c;                int c;
                unsigned int r;                for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) {
                for (c = 0; c < maxcount && !time_to_die; c++) { 
                         pingip_send();                          pingip_send();
                        r = numrecvd;                        const uint32_t w = wait_time(deadline, packetwait);
                        ping_recv(pcap,packetwait,                        if (w == 0) {
                                  (pcap_handler)pingip_recv);                                break;
                         }
                         ping_recv(pcap, w, (pcap_handler)pingip_recv);
                 }                  }
         } else { /* PINGMAC */          } else { /* PINGMAC */
                unsigned int c;                int c;
                unsigned int r;                for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) {
                for (c = 0; c < maxcount && !time_to_die; c++) {                        pingmac_send(xrandom(), c);
                        pingmac_send(rand(), c);                        const uint32_t w = wait_time(deadline, packetwait);
                        r = numrecvd;                        if (w == 0) {
                        ping_recv(pcap,packetwait,                                break;
                                  (pcap_handler)pingmac_recv);                        }
                         ping_recv(pcap, w,  (pcap_handler)pingmac_recv);
                 }                  }
         }          }
         if (display == DOT) {          if (display == DOT) {
Line 1785  int main(int argc, char **argv) Line 2280  int main(int argc, char **argv)
  * c-basic-offset: 8   * c-basic-offset: 8
  * indent-tabs-mode: nil   * indent-tabs-mode: nil
  * End:   * End:
    *
    * vim: ts=8 sw=8
  */   */

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>