Annotation of embedaddon/arping/src/arping.c, revision 1.1.1.4

1.1       misho       1: /** arping/src/arping.c
                      2:  *
                      3:  * arping
                      4:  *
1.1.1.2   misho       5:  * By Thomas Habets <thomas@habets.se>
1.1       misho       6:  *
                      7:  * ARP 'ping' utility
                      8:  *
                      9:  * Broadcasts a who-has ARP packet on the network and prints answers.
                     10:  * *VERY* useful when you are trying to pick an unused IP for a net that
                     11:  * you don't yet have routing to. Then again, if you have no idea what I'm
                     12:  * talking about then you prolly don't need it.
                     13:  *
1.1.1.2   misho      14:  * Also finds out IP of specified MAC.
1.1       misho      15:  *
                     16:  */
                     17: /*
1.1.1.4 ! misho      18:  *  Copyright (C) 2000-2019 Thomas Habets <thomas@habets.se>
1.1       misho      19:  *
1.1.1.3   misho      20:  *  This program is free software; you can redistribute it and/or modify
                     21:  *  it under the terms of the GNU General Public License as published by
                     22:  *  the Free Software Foundation; either version 2 of the License, or
                     23:  *  (at your option) any later version.
1.1       misho      24:  *
1.1.1.3   misho      25:  *  This program is distributed in the hope that it will be useful,
1.1       misho      26:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1.1.1.3   misho      27:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     28:  *  GNU General Public License for more details.
1.1       misho      29:  *
                     30:  *  You should have received a copy of the GNU General Public License along
                     31:  *  with this program; if not, write to the Free Software Foundation, Inc.,
                     32:  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
                     33:  */
                     34: #if HAVE_CONFIG_H
                     35: #include "config.h"
                     36: #endif
                     37: 
                     38: #include <stdio.h>
                     39: #include <stdlib.h>
1.1.1.2   misho      40: #include <limits.h>
                     41: #include <math.h>
1.1       misho      42: #include <poll.h>
                     43: 
                     44: #if HAVE_UNISTD_H
                     45: #include <unistd.h>
                     46: #endif
                     47: 
1.1.1.4 ! misho      48: #if HAVE_GETOPT_H
        !            49: #include <getopt.h>
        !            50: #endif
        !            51: 
1.1       misho      52: #if HAVE_STDINT_H
                     53: #include <stdint.h>
                     54: #endif
                     55: 
                     56: #if HAVE_INTTYPES_H
                     57: #include <inttypes.h>
                     58: #endif
                     59: 
1.1.1.2   misho      60: #if HAVE_TIME_H
                     61: #include <time.h>
                     62: #endif
                     63: 
1.1       misho      64: #if HAVE_SYS_TIME_H
                     65: #include <sys/time.h>
                     66: #endif
                     67: 
                     68: #if HAVE_SYS_TYPES_H
                     69: #include <sys/types.h>
                     70: #endif
                     71: 
1.1.1.4 ! misho      72: #if HAVE_SYS_PARAM_H
        !            73: #include <sys/param.h>
        !            74: #endif
        !            75: 
        !            76: #if HAVE_SYS_RANDOM_H
        !            77: #include <sys/random.h>
        !            78: #endif
        !            79: 
        !            80: #if HAVE_GRP_H
        !            81: #include <grp.h>
        !            82: #endif
        !            83: 
1.1       misho      84: #if HAVE_SYS_SOCKET_H
                     85: #include <sys/socket.h>
                     86: #endif
                     87: 
                     88: #if HAVE_NETINET_IN_H
                     89: #include <netinet/in.h>
                     90: #endif
                     91: 
                     92: #if HAVE_ARPA_INET_H
                     93: #include <arpa/inet.h>
                     94: #endif
                     95: 
                     96: #if HAVE_LIBNET_H
                     97: #include <libnet.h>
                     98: #endif
                     99: 
                    100: #if HAVE_WIN32_LIBNET_H
                    101: #include <win32/libnet.h>
                    102: #endif
                    103: 
1.1.1.3   misho     104: #if HAVE_PWD_H
                    105: #include <pwd.h>
                    106: #endif
                    107: 
                    108: #if HAVE_SYS_CAPABILITY_H
                    109: #include <sys/capability.h>
                    110: #endif
                    111: 
1.1       misho     112: #if HAVE_NET_BPF_H
                    113: #include <net/bpf.h>
                    114: #endif
1.1.1.2   misho     115: #include <pcap.h>
                    116: 
                    117: #include "arping.h"
1.1       misho     118: 
                    119: #ifndef ETH_ALEN
                    120: #define ETH_ALEN 6
                    121: #endif
                    122: 
                    123: #ifndef IP_ALEN
                    124: #define IP_ALEN 4
                    125: #endif
                    126: 
                    127: #ifndef WIN32
                    128: #define WIN32 0
                    129: #endif
                    130: 
1.1.1.2   misho     131: #ifndef CLOCK_MONOTONIC
                    132: #define CLOCK_MONOTONIC CLOCK_REALTIME
                    133: #endif
                    134: 
1.1.1.4 ! misho     135: #define UNUSED(x) (void)(x)
        !           136: 
1.1       misho     137: /**
                    138:  * OS-specific interface finding using routing table. See findif_*.c
1.1.1.3   misho     139:  * ebuf must be called with a size of at least
                    140:  * max(LIBNET_ERRBUF_SIZE, PCAP_ERRBUF_SIZE).
1.1       misho     141:  */
                    142: const char *
1.1.1.2   misho     143: arping_lookupdev(uint32_t srcip, uint32_t dstip, char *ebuf);
                    144: 
                    145: const char *
                    146: arping_lookupdev_default(uint32_t srcip, uint32_t dstip, char *ebuf);
1.1       misho     147: 
                    148: static const char *version = VERSION; /* from autoconf */
                    149: 
1.1.1.4 ! misho     150: libnet_t *libnet = 0;
1.1       misho     151: 
1.1.1.3   misho     152: /* Timestamp of last packet sent.
                    153:  * Used for timing, and assumes that reply is due to most recent sent query.
                    154:  */
1.1.1.2   misho     155: static struct timespec lastpacketsent;
1.1       misho     156: 
1.1.1.2   misho     157: /* target string */
1.1.1.4 ! misho     158: static const char *target = "huh? bug in arping?";
1.1.1.2   misho     159: 
                    160: /*
                    161:  * Ping IP mode:   cmdline target
                    162:  * Ping MAC mode:  255.255.255.255, override with -T
                    163:  */
1.1.1.4 ! misho     164: uint32_t dstip;
1.1.1.2   misho     165: 
                    166: /*
                    167:  * Ping IP mode:   ethxmas, override with -t
                    168:  * Ping MAC mode:  cmdline target
                    169:  */
                    170: static uint8_t dstmac[ETH_ALEN];
                    171: 
1.1.1.4 ! misho     172: static char* payload_suffix = NULL;
        !           173: static ssize_t payload_suffix_size = -1;
        !           174: 
        !           175: uint32_t srcip;                   /* autodetected, override with -S/-b/-0 */
        !           176: uint8_t srcmac[ETH_ALEN];         /* autodetected, override with -s */
        !           177: 
        !           178: static int16_t vlan_tag = -1; /* 802.1Q tag to add to packets. -V */
        !           179: static int16_t vlan_prio = -1; /* 802.1p prio to use with 802.1Q. -Q */
1.1.1.2   misho     180: 
                    181: static int beep = 0;                 /* beep when reply is received. -a */
                    182: static int reverse_beep = 0;         /* beep when expected reply absent. -e */
                    183: static int alsototal = 0;            /* print sent as well as received. -u */
                    184: static int addr_must_be_same = 0;    /* -A */
                    185: static int unsolicited = 0;          /* -U */
1.1.1.3   misho     186: static int send_reply = 0;           /* Send reply instead of request. -P */
1.1.1.4 ! misho     187: static int promisc = 0;              /* Use promisc mode. -p */
1.1.1.2   misho     188: 
                    189: static int finddup = 0;              /* finddup mode. -d */
                    190: static int dupfound = 0;             /* set to 1 if dup found */
                    191: static char lastreplymac[ETH_ALEN];  /* if last different from this then dup */
                    192: 
1.1.1.4 ! misho     193: unsigned int numsent = 0;                   /* packets sent */
        !           194: unsigned int numrecvd = 0;                  /* packets received */
1.1.1.3   misho     195: static unsigned int max_replies = UINT_MAX; /* exit after -C replies */
                    196: static const char* timestamp_type = NULL;   /* Incoming packet measurement ts type (-m) */
1.1.1.2   misho     197: 
                    198: static double stats_min_time = -1;
                    199: static double stats_max_time = -1;
                    200: static double stats_total_time = 0;
                    201: static double stats_total_sq_time = 0;
1.1       misho     202: 
                    203: /* RAWRAW is RAW|RRAW */
1.1.1.2   misho     204: static enum { NORMAL,      /* normal output */
                    205:               QUIET,       /* No output. -q */
                    206:               RAW,         /* Print MAC when pinging IP. -r */
                    207:               RRAW,        /* Print IP when pinging IP. -R */
                    208:               RAWRAW,      /* Print both. -r and -R */
                    209:               DOT          /* Print '.' and '!', Cisco-style. -D */
                    210: } display = NORMAL;
                    211: 
                    212: static const uint8_t ethnull[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
                    213: static const uint8_t ethxmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
1.1.1.4 ! misho     214: static const char* ip_broadcast = "255.255.255.255";
        !           215: static const uint32_t ip_broadcast_num = (uint32_t)-1;
1.1.1.2   misho     216: 
                    217: int verbose = 0;  /* Increase with -v */
1.1       misho     218: 
1.1.1.2   misho     219: /* Doesn't really need to be volatile, but doesn't hurt. */
                    220: static volatile sig_atomic_t time_to_die = 0;
                    221: 
1.1.1.4 ! misho     222: static ssize_t
        !           223: xgetrandom(void *buf, const size_t buflen, const unsigned int flags)
        !           224: {
        !           225: #ifdef HAVE_GETRANDOM
        !           226:         return getrandom(buf, buflen, flags);
        !           227: #else
        !           228:         char* p = buf;
        !           229:         for (int n = 0; n < buflen; n++) {
        !           230:                 p[n] = random() & 0xff;
        !           231:         }
        !           232:         return buflen;
        !           233: #endif
        !           234: }
        !           235: 
        !           236: static long int
        !           237: xrandom() {
        !           238:         const int maxtry = 10;
        !           239:         for (int c = 0; c < maxtry; c++) {
        !           240:                 long int ret;
        !           241:                 const ssize_t rc = xgetrandom(&ret, sizeof(ret), 0);
        !           242:                 if (rc == -1) {
        !           243:                         fprintf(stderr, "arping: failed to get random bytes: %s\n", strerror(errno));
        !           244:                         continue;
        !           245:                 }
        !           246:                 if (sizeof(ret) != rc) {
        !           247:                         fprintf(stderr, "arping: got too few random bytes %zd, want %zd\n", rc, sizeof(ret));
        !           248:                         continue;
        !           249:                 }
        !           250:                 return ret;
        !           251:         }
        !           252:         fprintf(stderr, "arping: failed to get random bytes after %d tries\n", maxtry);
        !           253:         exit(1);
        !           254: }
        !           255: 
1.1.1.2   misho     256: /**
1.1.1.3   misho     257:  * If possible, chroot.
                    258:  *
                    259:  * The sshd user is used for privilege separation in OpenSSH.
                    260:  * Let's assume it's installed and chroot() to there.
                    261:  */
                    262: static void
                    263: drop_fs_root()
                    264: {
                    265:         const char* chroot_user = "sshd";
                    266:         struct passwd *pw;
                    267:         errno = 0;
                    268:         if (!(pw = getpwnam(chroot_user))) {
                    269:                 if (verbose) {
1.1.1.4 ! misho     270:                         printf("arping: getpwnam(%s): %s\n",
1.1.1.3   misho     271:                                chroot_user, strerror(errno));
                    272:                 }
                    273:                 return;
                    274:         }
                    275:         if (chdir(pw->pw_dir)) {
                    276:                 if (verbose) {
1.1.1.4 ! misho     277:                         printf("arping: chdir(%s): %s\n",
1.1.1.3   misho     278:                                pw->pw_dir, strerror(errno));
                    279:                 }
                    280:                 return;
                    281:         }
                    282:         if (chroot(pw->pw_dir)) {
                    283:                 if (verbose) {
1.1.1.4 ! misho     284:                         printf("arping: chroot(%s): %s\n",
1.1.1.3   misho     285:                                pw->pw_dir, strerror(errno));
                    286:                 }
                    287:                 return;
                    288:         }
1.1.1.4 ! misho     289:         if (chdir("/")) {
        !           290:                 if (verbose) {
        !           291:                         printf("arping: chdir(/): %s\n", strerror(errno));
        !           292:                 }
        !           293:                 return;
        !           294:         }
1.1.1.3   misho     295:         if (verbose > 1) {
                    296:                 printf("arping: Successfully chrooted to %s\n", pw->pw_dir);
                    297:         }
                    298: }
                    299: 
                    300: /**
                    301:  * If possible, drop uid to nobody.
                    302:  *
                    303:  * This code only successfully sets all [ug]ids if running as
                    304:  * root. ARPing is most likely running as root unless using
                    305:  * capabilities, and those are dropped elsewhere.
                    306:  */
                    307: static void
                    308: drop_uid(uid_t uid, gid_t gid)
                    309: {
                    310:         int fail = 0;
                    311:         if (setgroups(0, NULL)) {
                    312:                 if (verbose) {
                    313:                         printf("arping: setgroups(0, NULL): %s\n", strerror(errno));
                    314:                 }
                    315:                 fail++;
                    316:         }
                    317:         if (gid && setgid(gid)) {
                    318:                 if (verbose) {
                    319:                         printf("arping: setgid(): %s\n", strerror(errno));
                    320:                 }
                    321:                 fail++;
                    322:         }
                    323:         if (uid && setuid(uid)) {
                    324:                 if (verbose) {
                    325:                         printf("arping: setuid(): %s\n", strerror(errno));
                    326:                 }
                    327:                 fail++;
                    328:         }
                    329:         if (!fail && verbose > 1) {
                    330:                 printf("arping: Successfully dropped uid/gid to %d/%d.\n",
                    331:                        uid, gid);
                    332:         }
                    333: }
                    334: 
                    335: /**
                    336:  * Drop any and all capabilities.
                    337:  */
                    338: static void
                    339: drop_capabilities()
                    340: {
                    341: #if HAVE_CAP_INIT
                    342:         cap_t no_cap;
                    343:         if (!(no_cap = cap_init())) {
                    344:                 if (verbose) {
                    345:                         printf("arping: cap_init(): %s\n", strerror(errno));
                    346:                 }
                    347:                 return;
                    348:         }
                    349:         if (cap_set_proc(no_cap)) {
                    350:                 if (verbose) {
                    351:                         printf("arping: cap_set_proc(): %s\n", strerror(errno));
                    352:                 }
                    353:         }
                    354:         if (verbose > 1) {
                    355:                 printf("arping: Successfully dropped all capabilities.\n");
                    356:         }
                    357:         cap_free(no_cap);
                    358: #endif
                    359: }
                    360: 
                    361: /**
1.1.1.4 ! misho     362:  * Get GID of input (handle both name and number) or die.
        !           363:  */
        !           364: static gid_t
        !           365: must_get_group(const char* ident)
        !           366: {
        !           367:         // Special case empty string, because strtol.
        !           368:         int saved_errno = 0;
        !           369:         if (*ident) {
        !           370:                 // First try it as a name.
        !           371:                 {
        !           372:                         struct group* gr;
        !           373:                         errno = 0;
        !           374:                         if ((gr = getgrnam(ident))) {
        !           375:                                 return gr->gr_gid;
        !           376:                         }
        !           377:                         saved_errno = errno;
        !           378:                 }
        !           379: 
        !           380:                 // Not a name. Try it as an integer.
        !           381:                 {
        !           382:                         char* endp = NULL;
        !           383:                         gid_t r = strtol(ident, &endp, 0);
        !           384:                         if (!*endp) {
        !           385:                                 return r;
        !           386:                         }
        !           387:                 }
        !           388:         }
        !           389: 
        !           390:         if (saved_errno != 0) {
        !           391:                 fprintf(stderr,
        !           392:                         "arping: %s not a number and getgrnam(%s): %s\n",
        !           393:                         ident, ident, strerror(saved_errno));
        !           394:         } else {
        !           395:                 // If group was supplied, then not
        !           396:                 // existing is fatal error too.
        !           397:                 fprintf(stderr,
        !           398:                         "arping: %s is not a number or group\n",
        !           399:                         ident);
        !           400:         }
        !           401:         exit(1);
        !           402: }
        !           403: 
        !           404: /**
1.1.1.3   misho     405:  * drop all privileges.
1.1.1.4 ! misho     406:  *
        !           407:  * To be called as early as possible. IOW: immediately after opening
        !           408:  * raw socket.
1.1.1.3   misho     409:  */
                    410: static void
1.1.1.4 ! misho     411: drop_privileges(const char* drop_group)
1.1.1.3   misho     412: {
                    413:         // Need to get uid/gid of 'nobody' before chroot().
                    414:         const char* drop_user = "nobody";
                    415:         struct passwd *pw;
                    416:         errno = 0;
                    417:         uid_t uid = 0;
                    418:         gid_t gid = 0;
                    419:         if (!(pw = getpwnam(drop_user))) {
                    420:                 if (verbose) {
1.1.1.4 ! misho     421:                         if (errno != 0) {
        !           422:                                 printf("arping: getpwnam(%s): %s\n",
        !           423:                                        drop_user, strerror(errno));
        !           424:                         } else {
        !           425:                                 printf("arping: getpwnam(%s): unknown user\n",
        !           426:                                        drop_user);
        !           427:                         }
1.1.1.3   misho     428:                 }
                    429:         } else {
                    430:                 uid = pw->pw_uid;
                    431:                 gid = pw->pw_gid;
                    432:         }
1.1.1.4 ! misho     433: 
        !           434:         // If group is supplied, use that gid instead.
        !           435:         if (drop_group != NULL) {
        !           436:                 gid = must_get_group(drop_group);
        !           437:         }
1.1.1.3   misho     438:         drop_fs_root();
                    439:         drop_uid(uid, gid);
                    440:         drop_capabilities();
1.1.1.4 ! misho     441: #ifdef HAVE_UNVEIL
        !           442:         if (unveil("/", "")) {
        !           443:                 fprintf(stderr,
        !           444:                         "arping: failed to unveil(/, <>): %s\n",
        !           445:                         strerror(errno));
        !           446:                 exit(1);
        !           447:         }
        !           448:         if (unveil(NULL, NULL)) {
        !           449:                 fprintf(stderr, "arping: failed to unveil(NULL, NULL): %s\n",
        !           450:                        strerror(errno));
        !           451:                 exit(1);
        !           452:         }
        !           453:         if (verbose > 1) {
        !           454:                 printf("arping: Successfully unveiled\n");
        !           455:         }
        !           456: #endif
        !           457: }
        !           458: 
        !           459: /**
        !           460:  * drop even more privileges, where possible.
        !           461:  *
        !           462:  * After all setup is done and main loop is about to start.
        !           463:  */
        !           464: static void
        !           465: drop_more_privileges()
        !           466: {
        !           467: #ifdef HAVE_PLEDGE
        !           468:         if (pledge("stdio tty", "")) {
        !           469:                 fprintf(stderr, "arping: failed to pledge(stdio, <empty>): %s\n",
        !           470:                        strerror(errno));
        !           471:                 exit(1);
        !           472:         } else if (verbose > 1) {
        !           473:                 printf("arping: Successfully pledged\n");
        !           474:         }
        !           475: #endif
1.1.1.3   misho     476: }
                    477: 
                    478: 
                    479: /**
                    480:  * Do pcap_open_live(), except by using the pcap_create() interface
                    481:  * introduced in 2008 (libpcap 0.4) where available.
1.1.1.4 ! misho     482:  * This is so that we can set some options, which can't be set with
        !           483:  * pcap_open_live:
        !           484:  * 1) Immediate mode -- this prevents pcap from buffering.
        !           485:  * 2) Set timestamp type -- specify what type of timer to use.
1.1.1.3   misho     486:  *
                    487:  * FIXME: Use pcap_set_buffer_size()?
                    488:  */
                    489: static pcap_t*
                    490: try_pcap_open_live(const char *device, int snaplen,
                    491:                    int promisc, int to_ms, char *errbuf)
                    492: {
                    493: #ifdef HAVE_PCAP_CREATE
                    494:         pcap_t* pcap;
                    495:         int rc;
                    496: 
                    497:         if (!(pcap = pcap_create(device, errbuf))) {
                    498:                 goto err;
                    499:         }
                    500:         if ((rc = pcap_set_snaplen(pcap, snaplen))) {
                    501:                 snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_snaplen(): %s", pcap_statustostr(rc));
                    502:                 goto err;
                    503:         }
                    504:         if ((rc = pcap_set_promisc(pcap, promisc))) {
                    505:                 snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_promisc(): %s", pcap_statustostr(rc));
                    506:                 goto err;
                    507:         }
                    508:         if ((rc = pcap_set_timeout(pcap, to_ms))) {
                    509:                 snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_timeout(): %s", pcap_statustostr(rc));
                    510:                 goto err;
                    511:         }
                    512: 
                    513: #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
                    514:         // Without immediate mode some architectures (e.g. Linux with
1.1.1.4 ! misho     515:         // TPACKET_V3) will buffer replies and incorrectly report upwards of
1.1.1.3   misho     516:         // hundreds of milliseconds of delay.
                    517:         if ((rc = pcap_set_immediate_mode(pcap, 1))) {
                    518:                 if (verbose) {
                    519:                         fprintf(stderr, "arping: pcap_set_immediate_mode() failed: %s\n", pcap_statustostr(rc));
                    520:                 }
                    521:         }
                    522: #endif
                    523: #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES
                    524:         if (timestamp_type) {
                    525:                 int err;
                    526:                 int v = pcap_tstamp_type_name_to_val(timestamp_type);
                    527:                 if (v == PCAP_ERROR) {
                    528:                         fprintf(stderr, "arping: Unknown timestamp type \"%s\"\n", timestamp_type);
                    529:                         exit(1);
                    530:                 }
                    531:                 if ((err = pcap_set_tstamp_type(pcap, v))) {
                    532:                         fprintf(stderr,
                    533:                                 "arping: Failed to set timestamp type \"%s\" (%d): %s\n",
                    534:                                 timestamp_type, v, pcap_statustostr(err));
                    535:                 }
                    536:         }
                    537: #endif
                    538:         if ((rc = pcap_activate(pcap))) {
                    539:                 if (timestamp_type) {
                    540:                         snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(tstype=\"%s\"): %s. Try without setting timestamp type.", timestamp_type, pcap_statustostr(rc));
                    541:                 } else {
                    542:                         snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(): %s", pcap_statustostr(rc));
                    543:                 }
                    544:                 goto err;
                    545:         }
                    546: #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES
                    547:         // List timestamp types after activating, since we don't want to list
                    548:         // them if activating failed.
                    549:         if (verbose > 1) {
                    550:                 int *ts;
                    551:                 int count;
                    552:                 count = pcap_list_tstamp_types(pcap, &ts);
                    553:                 if (count == PCAP_ERROR) {
                    554:                         fprintf(stderr, "arping: pcap_list_tstamp_types() failed\n");
                    555:                 } else {
                    556:                         int c;
                    557:                         const char* fmt = "  %-18s %s\n";
                    558:                         fprintf(stderr, "Timestamp types:\n");
                    559:                         fprintf(stderr, fmt, "Name", "Description");
                    560:                         for (c = 0; c < count; c++) {
                    561:                                 fprintf(stderr, fmt, pcap_tstamp_type_val_to_name(ts[c]),
                    562:                                         pcap_tstamp_type_val_to_description(ts[c]));
                    563:                         }
                    564:                         pcap_free_tstamp_types(ts);
                    565:                 }
                    566:         }
                    567: #endif
                    568:         return pcap;
                    569: err:
                    570:         if (pcap) {
                    571:                 pcap_close(pcap);
                    572:         }
                    573:         return NULL;
                    574: #else
                    575:         return pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
                    576: #endif
                    577: }
                    578: 
                    579: /**
1.1.1.2   misho     580:  * Some stupid OSs (Solaris) think it's a good idea to put network
                    581:  * devices in /dev and then play musical chairs with them.
                    582:  *
                    583:  * Since libpcap doesn't seem to have a workaround for that, here's arpings
                    584:  * workaround.
                    585:  *
                    586:  * E.g. if the network interface is called net0, pcap will fail because it
                    587:  * fails to open /dev/net, because it's a directory.
                    588:  */
                    589: static pcap_t*
                    590: do_pcap_open_live(const char *device, int snaplen,
                    591:                   int promisc, int to_ms, char *errbuf)
                    592: {
                    593:         pcap_t* ret;
                    594:         char buf[PATH_MAX];
                    595: 
1.1.1.3   misho     596:         if ((ret = try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2   misho     597:                 return ret;
                    598:         }
                    599: 
                    600:         snprintf(buf, sizeof(buf), "/dev/%s", device);
1.1.1.3   misho     601:         if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2   misho     602:                 return ret;
                    603:         }
                    604: 
                    605:         snprintf(buf, sizeof(buf), "/dev/net/%s", device);
1.1.1.3   misho     606:         if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2   misho     607:                 return ret;
                    608:         }
                    609: 
                    610:         /* Call original again to reset the error message. */
1.1.1.3   misho     611:         return try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
1.1.1.2   misho     612: }
1.1       misho     613: 
                    614: /**
1.1.1.3   misho     615:  * Some Libnet error messages end with a newline. Strip that in place.
1.1       misho     616:  */
1.1.1.3   misho     617: void
                    618: strip_newline(char* s) {
                    619:         size_t n;
1.1.1.4 ! misho     620:         for (n = strlen(s); n && (s[n - 1] == '\n'); --n) {
1.1.1.3   misho     621:                 s[n - 1] = 0;
1.1       misho     622:         }
                    623: }
                    624: 
                    625: /**
1.1.1.2   misho     626:  * Init libnet with specified ifname. Destroy if already inited.
1.1.1.3   misho     627:  * If this function retries with different parameter it will preserve
                    628:  * the original error message and print that.
                    629:  * Call with recursive=0.
1.1.1.2   misho     630:  */
1.1       misho     631: void
1.1.1.3   misho     632: do_libnet_init(const char *ifname, int recursive)
1.1       misho     633: {
                    634:        char ebuf[LIBNET_ERRBUF_SIZE];
1.1.1.3   misho     635:         ebuf[0] = 0;
1.1       misho     636:        if (verbose > 1) {
1.1.1.3   misho     637:                 printf("arping: libnet_init(%s)\n", ifname ? ifname : "<null>");
1.1       misho     638:        }
                    639:        if (libnet) {
1.1.1.2   misho     640:                /* Probably going to switch interface from temp to real. */
1.1       misho     641:                libnet_destroy(libnet);
                    642:                libnet = 0;
                    643:        }
                    644: 
1.1.1.2   misho     645:         /* Try libnet_init() even though we aren't root. We may have
                    646:          * a capability or something. */
1.1       misho     647:        if (!(libnet = libnet_init(LIBNET_LINK,
                    648:                                   (char*)ifname,
                    649:                                   ebuf))) {
1.1.1.3   misho     650:                 strip_newline(ebuf);
                    651:                 if (!ifname) {
                    652:                         /* Sometimes libnet guesses an interface that it then
                    653:                          * can't use. Work around that by attempting to
                    654:                          * use "lo". */
1.1.1.4 ! misho     655:                         do_libnet_init("lo", 1);
        !           656:                         if (libnet != NULL) {
        !           657:                                 return;
        !           658:                         }
1.1.1.3   misho     659:                 } else if (recursive) {
1.1.1.4 ! misho     660:                         /* Continue original execution to get that
        !           661:                          * error message. */
1.1.1.3   misho     662:                         return;
                    663:                 }
                    664:                 fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n",
1.1.1.4 ! misho     665:                         ifname ? ifname : "<null>",
        !           666:                         *ebuf ? ebuf : "<no error message>");
1.1.1.2   misho     667:                 if (getuid() && geteuid()) {
                    668:                         fprintf(stderr,
                    669:                                 "arping: you may need to run as root\n");
                    670:                 }
1.1       misho     671:                exit(1);
                    672:        }
                    673: }
                    674: 
                    675: /**
                    676:  *
                    677:  */
1.1.1.2   misho     678: void
                    679: sigint(int i)
1.1       misho     680: {
1.1.1.4 ! misho     681:         UNUSED(i);
1.1       misho     682:        time_to_die = 1;
                    683: }
                    684: 
                    685: /**
1.1.1.2   misho     686:  * idiot-proof clock_gettime() wrapper
1.1       misho     687:  */
1.1.1.2   misho     688: static void
                    689: getclock(struct timespec *ts)
1.1       misho     690: {
1.1.1.2   misho     691: #if HAVE_CLOCK_MONOTONIC
1.1.1.4 ! misho     692:         static int clock_gettime_failed = 0;
        !           693:         if (!clock_gettime_failed) {
        !           694:                 if (0 == clock_gettime(CLOCK_MONOTONIC, ts)) {
        !           695:                         return;
        !           696:                 }
        !           697:                 fprintf(stderr, "arping: clock_gettime(): %s\n",
1.1.1.2   misho     698:                         strerror(errno));
1.1.1.4 ! misho     699:                 clock_gettime_failed = 1; // Prevent duplicate error messages.
1.1.1.2   misho     700:         }
1.1.1.4 ! misho     701: #endif
1.1.1.2   misho     702:         struct timeval tv;
                    703:         if (-1 == gettimeofday(&tv, NULL)) {
                    704:                 fprintf(stderr, "arping: gettimeofday(): %s\n",
                    705:                         strerror(errno));
                    706:                 sigint(0);
                    707:         }
                    708:         ts->tv_sec = tv.tv_sec;
                    709:         ts->tv_nsec = tv.tv_usec * 1000;
1.1       misho     710: }
                    711: 
                    712: /**
                    713:  *
                    714:  */
1.1.1.3   misho     715: static char*
1.1.1.4 ! misho     716: format_mac(const unsigned char* mac, char* buf, size_t bufsize) {
1.1.1.3   misho     717:         snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
                    718:                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
                    719:         return buf;
                    720: }
                    721: 
                    722: /**
                    723:  *
                    724:  */
1.1       misho     725: static void
                    726: extended_usage()
                    727: {
                    728:        printf("\nOptions:\n");
                    729:        printf("\n"
                    730:               "    -0     Use this option to ping with source IP address 0.0.0.0. Use this\n"
                    731:               "           when you haven't configured your interface yet.  Note that  this\n"
                    732:               "           may  get  the  MAC-ping  unanswered.   This  is  an alias for -S\n"
                    733:               "           0.0.0.0.\n"
                    734:               "    -a     Audiable ping.\n"
                    735:               "    -A     Only count addresses matching  requested  address  (This  *WILL*\n"
                    736:               "           break  most things you do. Only useful if you are arpinging many\n"
                    737:               "           hosts at once. See arping-scan-net.sh for an example).\n"
                    738:               "    -b     Like -0 but source broadcast source  address  (255.255.255.255).\n"
                    739:               "           Note that this may get the arping unanswered since it's not nor-\n"
                    740:               "           mal behavior for a host.\n"
                    741:               "    -B     Use instead of host if you want to address 255.255.255.255.\n"
                    742:               "    -c count\n"
                    743:               "           Only send count requests.\n"
1.1.1.3   misho     744:                "    -C count\n"
                    745:                "           Only wait for this many replies, regardless of -c and -w.\n"
1.1       misho     746:               "    -d     Find duplicate replies. Exit with 1 if there are "
                    747:                "answers from\n"
                    748:                "           two different MAC addresses.\n"
1.1.1.2   misho     749:               "    -D     Display answers as exclamation points and missing packets as dots.\n"
1.1       misho     750:                "    -e     Like -a but beep when there is no reply.\n"
                    751:               "    -F     Don't try to be smart about the interface name.  (even  if  this\n"
                    752:               "           switch is not given, -i overrides smartness)\n"
1.1.1.4 ! misho     753:                "    -g group\n"
        !           754:                "           setgid() to this group instead of the nobody group.\n"
1.1       misho     755:               "    -h     Displays a help message and exits.\n"
                    756:               "    -i interface\n"
                    757:               "           Use the specified interface.\n"
1.1.1.3   misho     758:                "    -m type"
                    759: #ifndef HAVE_PCAP_LIST_TSTAMP_TYPES
                    760:                " (Disabled on this system. Option ignored)"
                    761: #endif
                    762:                "\n           Type of timestamp to use for incoming packets. Use -vv when\n"
                    763:                "           pinging to list available ones.\n"
1.1       misho     764:               "    -q     Does not display messages, except error messages.\n"
1.1.1.4 ! misho     765:                "    -Q pri 802.1p priority to set. Should be used with 802.1Q (-V).\n"
        !           766:                "           Defaults to 0.\n"
1.1       misho     767:               "    -r     Raw output: only the MAC/IP address is displayed for each reply.\n"
                    768:               "    -R     Raw output: Like -r but shows \"the other one\", can  be  combined\n"
                    769:               "           with -r.\n"
                    770:               "    -s MAC Set source MAC address. You may need to use -p with this.\n"
                    771:               "    -S IP  Like  -b and -0 but with set source address.  Note that this may\n"
1.1.1.3   misho     772:               "           get the arping unanswered if the target does not have routing to\n"
1.1       misho     773:               "           the  IP.  If you don't own the IP you are using, you may need to\n"
                    774:               "           turn on promiscious mode on the interface (with -p).  With  this\n"
                    775:               "           switch  you can find out what IP-address a host has without tak-\n"
                    776:               "           ing an IP-address yourself.\n"
                    777:               "    -t MAC Set target MAC address to use when pinging IP address.\n"
                    778:               "    -T IP  Use -T as target address when pinging MACs that won't respond to\n"
                    779:               "           a broadcast ping but perhaps to a directed broadcast.\n"
                    780:               "           Example:\n"
                    781:               "           To check the address of MAC-A, use knowledge of MAC-B and  IP-B.\n"
                    782:               "           $ arping -S <IP-B> -s <MAC-B> -p <MAC-A>\n"
                    783:               "    -p     Turn  on  promiscious  mode  on interface, use this if you don't\n"
                    784:               "           \"own\" the MAC address you are using.\n"
1.1.1.3   misho     785:                "    -P     Send ARP replies instead of requests. Useful with -U.\n"
1.1       misho     786:               "    -u     Show index=received/sent instead  of  just  index=received  when\n"
                    787:               "           pinging MACs.\n"
1.1.1.2   misho     788:               "    -U     Send unsolicited ARP.\n"
1.1       misho     789:               "    -v     Verbose output. Use twice for more messages.\n"
1.1.1.4 ! misho     790:                "    -V num 802.1Q tag to add. Defaults to no VLAN tag.\n"
        !           791:                "    -w sec Specify a timeout before ping exits regardless of how"
        !           792:                " many\npackets have been sent or received.\n"
        !           793:                "    -W sec Time to wait between pings.\n");
1.1.1.2   misho     794:         printf("Report bugs to: thomas@habets.se\n"
1.1       misho     795:                "Arping home page: <http://www.habets.pp.se/synscan/>\n"
                    796:                "Development repo: http://github.com/ThomasHabets/arping\n");
                    797: }
                    798: 
                    799: /**
                    800:  *
                    801:  */
                    802: static void
                    803: standard_usage()
                    804: {
1.1.1.2   misho     805:        printf("ARPing %s, by Thomas Habets <thomas@habets.se>\n",
1.1       misho     806:               version);
1.1.1.4 ! misho     807:         printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w <sec> ] "
1.1.1.3   misho     808:                "[ -W <sec> ] "
1.1       misho     809:                "[ -S <host/ip> ]\n"
                    810:                "              "
                    811:                "[ -T <host/ip ] "
                    812:                "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n"
                    813:                "              "
1.1.1.4 ! misho     814:                "[ -C <count> ] [ -i <interface> ] [ -m <type> ]"
        !           815:                " [ -g <group> ]\n"
        !           816:                "              "
        !           817:                "[ -V <vlan> ] [ -Q <priority> ] "
1.1       misho     818:                "<host/ip/MAC | -B>\n");
                    819: }
                    820: 
                    821: /**
                    822:  *
                    823:  */
                    824: static void
                    825: usage(int ret)
                    826: {
                    827:         standard_usage();
                    828:         if (WIN32) {
                    829:                 extended_usage();
                    830:         } else {
                    831:                 printf("For complete usage info, use --help"
                    832:                        " or check the manpage.\n");
                    833:         }
                    834:        exit(ret);
                    835: }
                    836: 
                    837: /**
1.1.1.4 ! misho     838:  * Check to see if it looks somewhat like a MAC address.
        !           839:  *
1.1       misho     840:  * It was unclear from msdn.microsoft.com if their scanf() supported
                    841:  * [0-9a-fA-F], so I'll stay away from it.
1.1.1.4 ! misho     842:  *
1.1       misho     843:  */
                    844: static int is_mac_addr(const char *p)
                    845: {
                    846:        /* cisco-style */
                    847:        if (3*5-1 == strlen(p)) {
                    848:                unsigned int c;
                    849:                for (c = 0; c < strlen(p); c++) {
                    850:                        if ((c % 5) == 4) {
                    851:                                if ('.' != p[c]) {
                    852:                                        goto checkcolon;
                    853:                                }
                    854:                        } else {
                    855:                                if (!isxdigit(p[c])) {
                    856:                                        goto checkcolon;
                    857:                                }
                    858:                        }
                    859:                }
                    860:                return 1;
                    861:        }
                    862:        /* windows-style */
                    863:        if (6*3-1 == strlen(p)) {
                    864:                unsigned int c;
                    865:                for (c = 0; c < strlen(p); c++) {
                    866:                        if ((c % 3) == 2) {
                    867:                                if ('-' != p[c]) {
                    868:                                        goto checkcolon;
                    869:                                }
                    870:                        } else {
                    871:                                if (!isxdigit(p[c])) {
                    872:                                        goto checkcolon;
                    873:                                }
                    874:                        }
                    875:                }
                    876:                return 1;
                    877:        }
                    878: 
                    879:  checkcolon:
                    880:        /* unix */
                    881:        return strchr(p, ':') ? 1 : 0;
                    882: }
                    883: 
                    884: /**
1.1.1.4 ! misho     885:  * parse mac address.
        !           886:  *
        !           887:  * return 1 on success.
1.1       misho     888:  */
1.1.1.4 ! misho     889: int
        !           890: get_mac_addr(const char *in, uint8_t *out)
1.1       misho     891: {
1.1.1.4 ! misho     892:         const char *formats[] = {
        !           893:                 "%x:%x:%x:%x:%x:%x",
        !           894:                 "%2x%x.%2x%x.%2x%x",
        !           895:                 "%x-%x-%x-%x-%x-%x",
        !           896:                 NULL,
        !           897:         };
        !           898:         int c;
        !           899:         for (c = 0; formats[c]; c++) {
        !           900:                 unsigned int n[6];
        !           901:                 if (6 == sscanf(in, formats[c],
        !           902:                                 &n[0], &n[1], &n[2], &n[3], &n[4], &n[5])) {
        !           903:                         for (c = 0; c < 6; c++) {
        !           904:                                 out[c] = n[c] & 0xff;
        !           905:                         }
        !           906:                         return 1;
        !           907:                 }
        !           908:         }
        !           909:         return 0;
1.1       misho     910: }
                    911: 
                    912: /**
                    913:  *
1.1.1.2   misho     914:  */
                    915: static void
                    916: update_stats(double sample)
                    917: {
                    918:         if (stats_min_time < 0 || sample < stats_min_time) {
                    919:                 stats_min_time = sample;
                    920:         }
                    921:         if (sample > stats_max_time) {
                    922:                 stats_max_time = sample;
                    923:         }
                    924:         stats_total_time += sample;
                    925:         stats_total_sq_time += sample * sample;
                    926: }
                    927: 
                    928: /**
                    929:  *
                    930:  */
                    931: static double
                    932: timespec2dbl(const struct timespec *tv)
                    933: {
                    934:         return tv->tv_sec + (double)tv->tv_nsec / 1000000000;
                    935: }
                    936: 
                    937: /**
1.1.1.4 ! misho     938:  * return number of microseconds to wait for packets.
        !           939:  */
        !           940: static uint32_t
        !           941: wait_time(double deadline, uint32_t packetwait)
        !           942: {
        !           943:         struct timespec ts;
        !           944: 
        !           945:         // If deadline not specified, then don't use it.
        !           946:         if (deadline < 0) {
        !           947:                 return packetwait;
        !           948:         }
        !           949: 
        !           950:         getclock(&ts);
        !           951:         const double max_wait = deadline - timespec2dbl(&ts);
        !           952:         if (max_wait < 0) {
        !           953:                 return 0;
        !           954:         }
        !           955:         if (max_wait > packetwait / 1000000.0) {
        !           956:                 return packetwait;
        !           957:         }
        !           958:         return max_wait * 1000000;
        !           959: }
        !           960: 
        !           961: /**
1.1.1.2   misho     962:  * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so
1.1       misho     963:  *
                    964:  * Still, I'm using at least 128bytes below
                    965:  */
1.1.1.2   misho     966: static char *ts2str(const struct timespec *tv, const struct timespec *tv2,
1.1.1.3   misho     967:                     char *buf, size_t bufsize)
1.1       misho     968: {
                    969:        double f,f2;
                    970:        int exp = 0;
                    971: 
1.1.1.2   misho     972:         f = timespec2dbl(tv);
                    973:         f2 = timespec2dbl(tv2);
                    974:        f = (f2 - f) * 1000000000;
1.1       misho     975:        while (f > 1000) {
1.1.1.2   misho     976:                exp += 3;
1.1       misho     977:                f /= 1000;
                    978:        }
                    979:        switch (exp) {
                    980:        case 0:
1.1.1.3   misho     981:                 snprintf(buf, bufsize, "%.3f nsec", f);
1.1       misho     982:                break;
                    983:        case 3:
1.1.1.3   misho     984:                 snprintf(buf, bufsize, "%.3f usec", f);
1.1       misho     985:                break;
                    986:        case 6:
1.1.1.3   misho     987:                 snprintf(buf, bufsize, "%.3f msec", f);
1.1       misho     988:                break;
                    989:        case 9:
1.1.1.3   misho     990:                 snprintf(buf, bufsize, "%.3f sec", f);
1.1.1.2   misho     991:                break;
                    992:        case 12:
1.1.1.3   misho     993:                 snprintf(buf, bufsize, "%.3f sec", f*1000);
1.1       misho     994:                break;
                    995:         default:
                    996:                /* huh, uh, huhuh */
1.1.1.3   misho     997:                 snprintf(buf, bufsize, "%.3fe%d sec", f, exp-9);
1.1       misho     998:        }
                    999:        return buf;
                   1000: }
                   1001: 
                   1002: /** Send directed IPv4 ICMP echo request.
                   1003:  *
                   1004:  * \param id      IP id
                   1005:  * \param seq     Ping seq
                   1006:  */
                   1007: static void
1.1.1.2   misho    1008: pingmac_send(uint16_t id, uint16_t seq)
1.1       misho    1009: {
                   1010:        static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0;
1.1.1.4 ! misho    1011: 
        !          1012:         // Padding size chosen fairly arbitrarily.
        !          1013:         // Without this padding some systems (e.g. Raspberry Pi 3
        !          1014:         // wireless interface) failed. dmesg said:
        !          1015:         //   arping: packet size is too short (42 <= 50)
        !          1016:         const size_t padding_size = sizeof(struct timespec) + payload_suffix_size;
        !          1017:         uint8_t padding[padding_size];
        !          1018:         memset(padding, 0, padding_size);
        !          1019:         {
        !          1020:                 struct timespec ts;
        !          1021:                 getclock(&ts);
        !          1022:                 memcpy(padding, &ts, sizeof(struct timespec));
        !          1023:                 memcpy(&padding[sizeof(struct timespec)],
        !          1024:                        payload_suffix, payload_suffix_size);
        !          1025:         }
        !          1026: 
1.1       misho    1027:        int c;
                   1028: 
                   1029:        if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */
                   1030:                                                   0, /* code */
                   1031:                                                   0, /* checksum */
                   1032:                                                   id, /* id */
                   1033:                                                   seq, /* seq */
1.1.1.4 ! misho    1034:                                                   (uint8_t*)padding, /* payload */
        !          1035:                                                   sizeof padding, /* payload len */
1.1       misho    1036:                                                   libnet,
                   1037:                                                   icmp))) {
                   1038:                fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n",
                   1039:                        libnet_geterror(libnet));
                   1040:                sigint(0);
                   1041:        }
                   1042: 
                   1043:        if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H
1.1.1.4 ! misho    1044:                                           + LIBNET_ICMPV4_ECHO_H
        !          1045:                                           + sizeof padding,
1.1       misho    1046:                                          0, /* ToS */
                   1047:                                          id, /* id */
                   1048:                                          0, /* frag */
                   1049:                                          64, /* ttl */
                   1050:                                          IPPROTO_ICMP,
                   1051:                                          0, /* checksum */
                   1052:                                          srcip,
                   1053:                                          dstip,
                   1054:                                          NULL, /* payload */
                   1055:                                          0,
                   1056:                                          libnet,
                   1057:                                          ipv4))) {
                   1058:                fprintf(stderr, "libnet_build_ipv4(): %s\n",
                   1059:                        libnet_geterror(libnet));
                   1060:                sigint(0);
                   1061:        }
1.1.1.4 ! misho    1062:        if (vlan_tag >= 0) {
        !          1063:                 eth = libnet_build_802_1q(dstmac,
        !          1064:                                           srcmac,
        !          1065:                                           ETHERTYPE_VLAN,
        !          1066:                                           vlan_prio,
        !          1067:                                           0, // cfi
        !          1068:                                           vlan_tag,
        !          1069:                                           ETHERTYPE_IP,
        !          1070:                                           NULL, // payload
        !          1071:                                           0, // payload length
        !          1072:                                           libnet,
        !          1073:                                           eth);
        !          1074:         } else {
        !          1075:                 eth = libnet_build_ethernet(dstmac,
        !          1076:                                             srcmac,
        !          1077:                                             ETHERTYPE_IP,
        !          1078:                                             NULL, // payload
        !          1079:                                             0, // payload length
        !          1080:                                             libnet,
        !          1081:                                             eth);
        !          1082:         }
        !          1083:         if (-1 == eth) {
        !          1084:                 fprintf(stderr, "arping: %s: %s\n",
        !          1085:                         (vlan_tag >= 0) ? "libnet_build_802_1q()" :
        !          1086:                         "libnet_build_ethernet()",
        !          1087:                         libnet_geterror(libnet));
        !          1088:                 sigint(0);
        !          1089:         }
1.1.1.2   misho    1090:        if (verbose > 1) {
                   1091:                 getclock(&lastpacketsent);
                   1092:                 printf("arping: sending packet at time %ld.%09ld\n",
                   1093:                        (long)lastpacketsent.tv_sec,
                   1094:                        (long)lastpacketsent.tv_nsec);
1.1       misho    1095:        }
                   1096:        if (-1 == (c = libnet_write(libnet))) {
                   1097:                fprintf(stderr, "arping: libnet_write(): %s\n",
                   1098:                        libnet_geterror(libnet));
                   1099:                sigint(0);
                   1100:        }
1.1.1.2   misho    1101:         getclock(&lastpacketsent);
1.1       misho    1102:        numsent++;
                   1103: }
                   1104: 
                   1105: /** Send ARP who-has.
                   1106:  *
                   1107:  */
                   1108: static void
1.1.1.2   misho    1109: pingip_send()
1.1       misho    1110: {
                   1111:        static libnet_ptag_t arp=0,eth=0;
1.1.1.4 ! misho    1112: 
        !          1113:         // Padding size chosen fairly arbitrarily.
        !          1114:         // Without this padding some systems (e.g. Raspberry Pi 3
        !          1115:         // wireless interface) failed. dmesg said:
        !          1116:         //   arping: packet size is too short (42 <= 50)
        !          1117:         const uint8_t padding[16] = {0};
        !          1118: 
1.1       misho    1119:        if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER,
                   1120:                                          ETHERTYPE_IP,
                   1121:                                          ETH_ALEN,
                   1122:                                          IP_ALEN,
1.1.1.3   misho    1123:                                           send_reply ? ARPOP_REPLY : ARPOP_REQUEST,
1.1       misho    1124:                                          srcmac,
                   1125:                                          (uint8_t*)&srcip,
1.1.1.2   misho    1126:                                          unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull,
1.1       misho    1127:                                          (uint8_t*)&dstip,
1.1.1.4 ! misho    1128:                                          (uint8_t*)padding,
        !          1129:                                          sizeof padding,
1.1       misho    1130:                                          libnet,
                   1131:                                          arp))) {
                   1132:                fprintf(stderr, "arping: libnet_build_arp(): %s\n",
                   1133:                        libnet_geterror(libnet));
                   1134:                sigint(0);
                   1135:        }
1.1.1.4 ! misho    1136: 
        !          1137:         if (vlan_tag >= 0) {
        !          1138:                 eth = libnet_build_802_1q(dstmac,
        !          1139:                                           srcmac,
        !          1140:                                           ETHERTYPE_VLAN,
        !          1141:                                           vlan_prio,
        !          1142:                                           0, // cfi
        !          1143:                                           vlan_tag,
        !          1144:                                           ETHERTYPE_ARP,
        !          1145:                                           NULL, // payload
        !          1146:                                           0, // payload size
        !          1147:                                           libnet,
        !          1148:                                           eth);
        !          1149:         } else {
        !          1150:                 eth = libnet_build_ethernet(dstmac,
        !          1151:                                             srcmac,
        !          1152:                                             ETHERTYPE_ARP,
        !          1153:                                             NULL, // payload
        !          1154:                                             0, // payload size
        !          1155:                                             libnet,
        !          1156:                                             eth);
        !          1157:         }
        !          1158:        if (-1 == eth) {
        !          1159:                fprintf(stderr, "arping: %s: %s\n",
        !          1160:                        (vlan_tag >= 0) ? "libnet_build_802_1q()" :
        !          1161:                         "libnet_build_ethernet()",
1.1       misho    1162:                        libnet_geterror(libnet));
                   1163:                sigint(0);
                   1164:        }
1.1.1.2   misho    1165:        if (verbose > 1) {
                   1166:                 getclock(&lastpacketsent);
                   1167:                 printf("arping: sending packet at time %ld.%09ld\n",
                   1168:                        (long)lastpacketsent.tv_sec,
                   1169:                        (long)lastpacketsent.tv_nsec);
1.1       misho    1170:        }
                   1171:        if (-1 == libnet_write(libnet)) {
1.1.1.4 ! misho    1172:                fprintf(stderr, "arping: libnet_write(): %s\n",
1.1       misho    1173:                        libnet_geterror(libnet));
                   1174:                sigint(0);
                   1175:        }
1.1.1.2   misho    1176:         getclock(&lastpacketsent);
1.1       misho    1177:        numsent++;
                   1178: }
                   1179: 
                   1180: /** handle incoming packet when pinging an IP address.
                   1181:  *
                   1182:  * \param h       packet metadata
                   1183:  * \param packet  packet data
                   1184:  */
1.1.1.4 ! misho    1185: void
        !          1186: pingip_recv(const char *unused, struct pcap_pkthdr *h, const char * const packet)
1.1       misho    1187: {
1.1.1.4 ! misho    1188:         const unsigned char *pkt_srcmac;
        !          1189:         const struct libnet_802_1q_hdr *veth;
1.1       misho    1190:        struct libnet_802_3_hdr *heth;
                   1191:        struct libnet_arp_hdr *harp;
1.1.1.2   misho    1192:         struct timespec arrival;
1.1.1.4 ! misho    1193:         UNUSED(unused);
1.1       misho    1194: 
1.1.1.2   misho    1195:         if (verbose > 2) {
1.1.1.3   misho    1196:                printf("arping: received response for IP ping\n");
1.1       misho    1197:        }
                   1198: 
1.1.1.2   misho    1199:         getclock(&arrival);
                   1200: 
1.1.1.4 ! misho    1201:        if (vlan_tag >= 0) {
        !          1202:                veth = (void*)packet;
        !          1203:                harp = (void*)((char*)veth + LIBNET_802_1Q_H);
        !          1204:                pkt_srcmac = veth->vlan_shost;
        !          1205:        } else {
        !          1206:                 // Short packet.
        !          1207:                 if (h->caplen < LIBNET_ETH_H + LIBNET_ARP_H + 2*(ETH_ALEN + 4)) {
        !          1208:                         return;
        !          1209:                 }
        !          1210: 
        !          1211:                heth = (void*)packet;
        !          1212:                harp = (void*)((char*)heth + LIBNET_ETH_H);
        !          1213:                pkt_srcmac = heth->_802_3_shost;
        !          1214:                 // Wrong length of hardware address.
        !          1215:                 if (harp->ar_hln != ETH_ALEN) {
        !          1216:                         return;
        !          1217:                 }
        !          1218: 
        !          1219:                 // Wrong length of protocol address.
        !          1220:                 if (harp->ar_pln != 4) {
        !          1221:                         return;
        !          1222:                 }
        !          1223:        }
1.1       misho    1224: 
1.1.1.3   misho    1225:         // ARP reply.
                   1226:         if (htons(harp->ar_op) != ARPOP_REPLY) {
                   1227:                 return;
                   1228:         }
                   1229:         if (verbose > 3) {
                   1230:                 printf("arping: ... packet is ARP reply\n");
                   1231:         }
1.1       misho    1232: 
1.1.1.3   misho    1233:         // From IPv4 address reply.
                   1234:         if (htons(harp->ar_pro) != ETHERTYPE_IP) {
                   1235:                 return;
                   1236:         }
                   1237:         if (verbose > 3) {
                   1238:                 printf("arping: ... from IPv4 address\n");
                   1239:         }
1.1       misho    1240: 
1.1.1.3   misho    1241:         // To Ethernet address.
                   1242:         if (htons(harp->ar_hrd) != ARPHRD_ETHER) {
                   1243:                 return;
                   1244:         }
                   1245:         if (verbose > 3) {
                   1246:                 printf("arping: ... to Ethernet address\n");
                   1247:         }
                   1248: 
                   1249:         // Must be sent from target address.
                   1250:         // Should very likely only be used if using -T.
                   1251:         if (addr_must_be_same) {
                   1252:                 if (memcmp((u_char*)harp + sizeof(struct libnet_arp_hdr),
                   1253:                            dstmac, ETH_ALEN)) {
                   1254:                         return;
                   1255:                 }
                   1256:         }
                   1257:         if (verbose > 3) {
                   1258:                 printf("arping: ... sent by acceptable host\n");
                   1259:         }
                   1260: 
1.1.1.4 ! misho    1261:         // Special case: If we're not in promisc mode we could still
        !          1262:         // get packets where DST mac is not us, if they're *sent* from
        !          1263:         // the local host. This is an edge case but in general falls under "is promisc?".
        !          1264:         //
        !          1265:         // It may cause confusion because `-p` now means not just
        !          1266:         // enable promisc mode (disable filter on card / in kernel),
        !          1267:         // but also allow packets to any destination (disable filter
        !          1268:         // in `arping`).
        !          1269:         {
        !          1270:                 const uint8_t* p = (u_char*)harp
        !          1271:                         + sizeof(struct libnet_arp_hdr)
        !          1272:                         + ETH_ALEN
        !          1273:                         + IP_ALEN;
        !          1274:                 char buf[128];
        !          1275:                 if (!promisc && memcmp(p, srcmac, ETH_ALEN)) {
        !          1276:                         format_mac(p, buf, sizeof buf);
        !          1277:                         if (verbose > 3) {
        !          1278:                                 printf("arping: ... but sent from %s\n", buf);
        !          1279:                         }
        !          1280:                         return;
        !          1281:                 }
        !          1282:         }
        !          1283:         if (verbose > 3) {
        !          1284:                 printf("arping: ... destination is the source we used\n");
        !          1285:         }
        !          1286: 
        !          1287: 
1.1.1.3   misho    1288:         // Actually the IPv4 address we asked for.
                   1289:         uint32_t ip;
                   1290:         memcpy(&ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4);
                   1291:         if (dstip != ip) {
                   1292:                 return;
                   1293:         }
                   1294:         if (verbose > 3) {
                   1295:                 printf("arping: ... for the right IPv4 address!\n");
                   1296:         }
                   1297: 
                   1298:         update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));
                   1299:         char buf[128];
                   1300:         if (beep) {
                   1301:                 printf("\a");
                   1302:         }
                   1303:         switch(display) {
                   1304:         case DOT:
                   1305:                 putchar('!');
                   1306:                 break;
                   1307:         case NORMAL:
                   1308:                 printf("%d bytes from %s (%s): index=%d",
1.1.1.4 ! misho    1309:                        h->len, format_mac(pkt_srcmac, buf, sizeof(buf)),
1.1.1.3   misho    1310:                        libnet_addr2name4(ip, 0), numrecvd);
                   1311: 
                   1312:                 if (alsototal) {
                   1313:                         printf("/%u", numsent-1);
                   1314:                 }
                   1315:                 printf(" time=%s", ts2str(&lastpacketsent, &arrival, buf,
                   1316:                                           sizeof(buf)));
                   1317:                 break;
                   1318:         case QUIET:
                   1319:                 break;
                   1320:         case RAWRAW:
1.1.1.4 ! misho    1321:                 printf("%s %s", format_mac(pkt_srcmac, buf, sizeof(buf)),
1.1.1.3   misho    1322:                        libnet_addr2name4(ip, 0));
                   1323:                 break;
                   1324:         case RRAW:
                   1325:                 printf("%s", libnet_addr2name4(ip, 0));
                   1326:                 break;
                   1327:         case RAW:
1.1.1.4 ! misho    1328:                 printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf)));
1.1.1.3   misho    1329:                 break;
                   1330:         default:
                   1331:                 fprintf(stderr, "arping: can't happen!\n");
                   1332:         }
                   1333:         fflush(stdout);
                   1334: 
                   1335:         switch (display) {
                   1336:         case QUIET:
                   1337:         case DOT:
                   1338:                 break;
                   1339:         default:
                   1340:                 printf("\n");
                   1341:         }
                   1342:         if (numrecvd) {
                   1343:                 if (memcmp(lastreplymac,
1.1.1.4 ! misho    1344:                            pkt_srcmac, ETH_ALEN)) {
1.1.1.3   misho    1345:                         dupfound = 1;
                   1346:                 }
                   1347:         }
1.1.1.4 ! misho    1348:         memcpy(lastreplymac, pkt_srcmac, ETH_ALEN);
1.1.1.3   misho    1349: 
                   1350:         numrecvd++;
                   1351:         if (numrecvd >= max_replies) {
                   1352:                 sigint(0);
                   1353:         }
1.1       misho    1354: }
                   1355: 
                   1356: /** handle incoming packet when pinging an MAC address.
                   1357:  *
                   1358:  * \param h       packet metadata
                   1359:  * \param packet  packet data
                   1360:  */
1.1.1.4 ! misho    1361: void
        !          1362: pingmac_recv(const char* unused, struct pcap_pkthdr *h, uint8_t *packet)
1.1       misho    1363: {
1.1.1.4 ! misho    1364:         const unsigned char *pkt_dstmac;
        !          1365:         const unsigned char *pkt_srcmac;
        !          1366:         const struct libnet_802_1q_hdr *veth;
1.1       misho    1367:        struct libnet_802_3_hdr *heth;
                   1368:        struct libnet_ipv4_hdr *hip;
                   1369:        struct libnet_icmpv4_hdr *hicmp;
1.1.1.2   misho    1370:         struct timespec arrival;
1.1.1.4 ! misho    1371:         UNUSED(unused);
1.1       misho    1372: 
                   1373:        if(verbose>2) {
                   1374:                printf("arping: received response for mac ping\n");
                   1375:        }
                   1376: 
1.1.1.2   misho    1377:         getclock(&arrival);
1.1       misho    1378: 
1.1.1.4 ! misho    1379:         if (vlan_tag >= 0) {
        !          1380:                 veth = (void*)packet;
        !          1381:                 hip = (void*)((char*)veth + LIBNET_802_1Q_H);
        !          1382:                 hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
        !          1383:                 pkt_srcmac = veth->vlan_shost;
        !          1384:                 pkt_dstmac = veth->vlan_dhost;
        !          1385:         } else {
        !          1386:                 heth = (void*)packet;
        !          1387:                 hip = (void*)((char*)heth + LIBNET_ETH_H);
        !          1388:                 hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
        !          1389:                 pkt_srcmac = heth->_802_3_shost;
        !          1390:                 pkt_dstmac = heth->_802_3_dhost;
        !          1391:         }
1.1       misho    1392: 
1.1.1.3   misho    1393:         // Dest MAC must be me.
1.1.1.4 ! misho    1394:         if (memcmp(pkt_dstmac, srcmac, ETH_ALEN)) {
1.1.1.3   misho    1395:                 return;
                   1396:         }
                   1397: 
1.1.1.4 ! misho    1398:         if (verbose > 3) {
        !          1399:                 printf("arping: ... right dst mac\n");
        !          1400:         }
        !          1401: 
1.1.1.3   misho    1402:         // Source MAC must match, if set.
                   1403:         if (memcmp(dstmac, ethxmas, ETH_ALEN)) {
1.1.1.4 ! misho    1404:                 if (memcmp(pkt_srcmac, dstmac, ETH_ALEN)) {
1.1.1.3   misho    1405:                         return;
                   1406:                 }
                   1407:         }
                   1408: 
1.1.1.4 ! misho    1409:         if (verbose > 3) {
        !          1410:                 printf("arping: ... right src mac\n");
        !          1411:         }
        !          1412: 
1.1.1.3   misho    1413:         // IPv4 Address must be me (maybe).
                   1414:         if (addr_must_be_same) {
                   1415:                 uint32_t tmp;
                   1416:                 memcpy(&tmp, &hip->ip_src, 4);
                   1417:                 if (dstip != tmp) {
                   1418:                         return;
                   1419:                 }
                   1420:         }
                   1421: 
1.1.1.4 ! misho    1422:         if (verbose > 3) {
        !          1423:                 printf("arping: ... src IP acceptable\n");
        !          1424:         }
        !          1425: 
        !          1426:         // Must be ICMP echo reply type.
1.1.1.3   misho    1427:         if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) {
                   1428:                 return;
                   1429:         }
                   1430: 
1.1.1.4 ! misho    1431:         if (verbose > 3) {
        !          1432:                 printf("arping: ... is echo reply type\n");
        !          1433:         }
        !          1434: 
        !          1435:         // Must be ICMP echo reply code 0.
        !          1436:         if (htons(hicmp->icmp_code) != 0) {
        !          1437:                 return;
        !          1438:         }
        !          1439: 
        !          1440:         if (verbose > 3) {
        !          1441:                 printf("arping: ... is echo reply code\n");
        !          1442:         }
        !          1443: 
        !          1444:         const char* payload = (char*)hicmp + LIBNET_ICMPV4_ECHO_H;
        !          1445:         const ssize_t payload_size = h->len - (payload - (char*)packet);
        !          1446:         if (payload_size < 0) {
        !          1447:                 return;
        !          1448:         }
        !          1449:         if (payload_size < sizeof(struct timespec) + payload_suffix_size) {
        !          1450:                 return;
        !          1451:         }
        !          1452:         if (verbose > 3) {
        !          1453:                 printf("arping: ... correct payload size (%zd)\n",
        !          1454:                        payload_size);
        !          1455:         }
        !          1456:         if (memcmp(&payload[sizeof(struct timespec)],
        !          1457:                     payload_suffix, payload_suffix_size)) {
        !          1458:                     return;
        !          1459:         }
        !          1460:         if (verbose > 3) {
        !          1461:                 printf("arping: ... correct payload suffix\n");
        !          1462:         }
        !          1463: 
1.1.1.3   misho    1464:         update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));
                   1465:         if (beep) {
                   1466:                 printf("\a");
                   1467:         }
                   1468:         char buf[128];
                   1469:         char buf2[128];
                   1470:         switch(display) {
                   1471:         case QUIET:
                   1472:                 break;
                   1473:         case DOT:
                   1474:                 putchar('!');
                   1475:                 break;
                   1476:         case NORMAL:
                   1477:                 printf("%d bytes from %s (%s): icmp_seq=%d time=%s", h->len,
                   1478:                        libnet_addr2name4(*(int*)&hip->ip_src, 0),
1.1.1.4 ! misho    1479:                        format_mac(pkt_srcmac, buf, sizeof(buf)),
1.1.1.3   misho    1480:                        htons(hicmp->icmp_seq),
                   1481:                        ts2str(&lastpacketsent, &arrival, buf2, sizeof(buf2)));
                   1482:                 break;
                   1483:         case RAW:
                   1484:                 printf("%s", libnet_addr2name4(hip->ip_src.s_addr, 0));
                   1485:                 break;
                   1486:         case RRAW:
1.1.1.4 ! misho    1487:                 printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf)));
1.1.1.3   misho    1488:                 break;
                   1489:         case RAWRAW:
                   1490:                 printf("%s %s",
1.1.1.4 ! misho    1491:                        format_mac(pkt_srcmac, buf, sizeof(buf)),
1.1.1.3   misho    1492:                        libnet_addr2name4(hip->ip_src.s_addr, 0));
                   1493:                 break;
                   1494:         default:
                   1495:                 fprintf(stderr, "arping: can't-happen-bug\n");
                   1496:                 sigint(0);
                   1497:         }
                   1498:         fflush(stdout);
                   1499:         switch (display) {
                   1500:         case QUIET:
                   1501:         case DOT:
                   1502:                 break;
                   1503:         default:
                   1504:                 printf("\n");
                   1505:         }
                   1506:         numrecvd++;
                   1507:         if (numrecvd >= max_replies) {
                   1508:                 sigint(0);
                   1509:         }
1.1       misho    1510: }
                   1511: 
                   1512: /**
1.1.1.2   misho    1513:  * while negative nanoseconds, take from whole seconds.
1.1       misho    1514:  * help function for measuring deltas.
                   1515:  */
                   1516: static void
1.1.1.2   misho    1517: fixup_timespec(struct timespec *tv)
1.1       misho    1518: {
1.1.1.2   misho    1519:        while (tv->tv_nsec < 0) {
1.1       misho    1520:                tv->tv_sec--;
1.1.1.2   misho    1521:                tv->tv_nsec += 1000000000;
1.1       misho    1522:        }
                   1523: }
                   1524: 
                   1525: /**
1.1.1.2   misho    1526:  * try to receive a packet for 'packetwait' microseconds
1.1       misho    1527:  */
                   1528: static void
1.1.1.2   misho    1529: ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_handler func)
1.1       misho    1530: {
1.1.1.2   misho    1531:        struct timespec ts;
                   1532:        struct timespec endtime;
1.1       misho    1533:        char done = 0;
1.1.1.2   misho    1534:        int fd;
1.1.1.4 ! misho    1535:        unsigned int old_received;
1.1       misho    1536: 
1.1.1.2   misho    1537:        if (verbose > 3) {
                   1538:                printf("arping: receiving packets...\n");
                   1539:        }
1.1       misho    1540: 
1.1.1.2   misho    1541:        getclock(&ts);
                   1542:        endtime.tv_sec = ts.tv_sec + (packetwait / 1000000);
                   1543:        endtime.tv_nsec = ts.tv_nsec + 1000 * (packetwait % 1000000);
                   1544:        fixup_timespec(&endtime);
1.1       misho    1545: 
                   1546:        fd = pcap_get_selectable_fd(pcap);
1.1.1.4 ! misho    1547:        if (fd == -1) {
        !          1548:                fprintf(stderr, "arping: pcap_get_selectable_fd()=-1: %s\n",
        !          1549:                        pcap_geterr(pcap));
        !          1550:                exit(1);
        !          1551:        }
1.1.1.2   misho    1552:        old_received = numrecvd;
1.1       misho    1553: 
                   1554:        for (;!done;) {
                   1555:               int trydispatch = 0;
                   1556: 
1.1.1.2   misho    1557:               getclock(&ts);
                   1558:               ts.tv_sec = endtime.tv_sec - ts.tv_sec;
                   1559:               ts.tv_nsec = endtime.tv_nsec - ts.tv_nsec;
                   1560:               fixup_timespec(&ts);
                   1561:                if (verbose > 2) {
1.1.1.3   misho    1562:                        printf("arping: listen for replies for %ld.%09ld sec\n",
1.1.1.2   misho    1563:                               (long)ts.tv_sec, (long)ts.tv_nsec);
                   1564:                }
                   1565: 
                   1566:                /* if time has passed, do one last check and then we're done.
                   1567:                 * this also triggers if not using monotonic clock and time
                   1568:                 * is set forwards */
                   1569:               if (ts.tv_sec < 0) {
                   1570:                       ts.tv_sec = 0;
                   1571:                       ts.tv_nsec = 1;
1.1       misho    1572:                       done = 1;
                   1573:               }
1.1.1.2   misho    1574: 
                   1575:                /* if wait-for-packet time is longer than full period,
                   1576:                 * we're obviously not using a monotonic clock and the system
                   1577:                 * time has been changed.
                   1578:                 * we don't know how far we're into the waiting, so just end
                   1579:                 * it here */
                   1580:                if ((ts.tv_sec > packetwait / 1000000)
                   1581:                    || ((ts.tv_sec == packetwait / 1000000)
                   1582:                        && (ts.tv_nsec/1000 > packetwait % 1000000))) {
                   1583:                       ts.tv_sec = 0;
                   1584:                       ts.tv_nsec = 1;
                   1585:                        done = 1;
                   1586:                }
                   1587: 
                   1588:                /* check for sigint */
1.1       misho    1589:               if (time_to_die) {
                   1590:                       return;
                   1591:               }
                   1592: 
                   1593:               /* try to wait for data */
                   1594:               {
1.1.1.2   misho    1595:                        fd_set fds;
1.1       misho    1596:                       int r;
1.1.1.2   misho    1597:                        struct timeval tv;
                   1598:                        tv.tv_sec = ts.tv_sec;
                   1599:                        tv.tv_usec = ts.tv_nsec / 1000;
                   1600: 
                   1601:                        FD_ZERO(&fds);
                   1602:                        FD_SET(fd, &fds);
1.1       misho    1603: 
1.1.1.2   misho    1604:                        r = select(fd + 1, &fds, NULL, NULL, &tv);
1.1       misho    1605:                       switch (r) {
                   1606:                       case 0: /* timeout */
1.1.1.3   misho    1607:                                if (numrecvd == old_received) {
                   1608:                                        if (reverse_beep) {
                   1609:                                                printf("\a");
                   1610:                                        }
                   1611:                                        switch (display) {
                   1612:                                        case NORMAL:
1.1.1.2   misho    1613:                                                printf("Timeout\n");
1.1.1.3   misho    1614:                                                break;
                   1615:                                        case DOT:
                   1616:                                                printf(".");
                   1617:                                                break;
1.1.1.4 ! misho    1618:                                        case RAW:
        !          1619:                                        case RAWRAW:
        !          1620:                                        case RRAW:
        !          1621:                                        case QUIET:
        !          1622:                                                break;
1.1.1.2   misho    1623:                                        }
1.1.1.3   misho    1624:                                        fflush(stdout);
1.1.1.2   misho    1625:                                }
1.1       misho    1626:                               done = 1;
                   1627:                               break;
                   1628:                       case -1: /* error */
                   1629:                               if (errno != EINTR) {
                   1630:                                       done = 1;
                   1631:                                       sigint(0);
                   1632:                                       fprintf(stderr,
1.1.1.2   misho    1633:                                               "arping: select() failed: %s\n",
1.1       misho    1634:                                               strerror(errno));
                   1635:                               }
                   1636:                               break;
                   1637:                       default: /* data returned */
                   1638:                               trydispatch = 1;
                   1639:                               break;
                   1640:                       }
                   1641:               }
                   1642: 
                   1643:               if (trydispatch) {
                   1644:                       int ret;
1.1.1.2   misho    1645:                        if (0 > (ret = pcap_dispatch(pcap, -1,
                   1646:                                                     func,
                   1647:                                                     NULL))) {
1.1       misho    1648:                               /* rest, so we don't take 100% CPU... mostly
                   1649:                                   hmm... does usleep() exist everywhere? */
                   1650:                               usleep(1);
                   1651: 
                   1652:                               /* weird is normal on bsd :) */
                   1653:                               if (verbose > 3) {
                   1654:                                       fprintf(stderr,
1.1.1.2   misho    1655:                                               "arping: select says ok, but "
1.1       misho    1656:                                               "pcap_dispatch=%d!\n",
                   1657:                                               ret);
                   1658:                               }
                   1659:                       }
                   1660:               }
                   1661:        }
                   1662: }
                   1663: 
1.1.1.4 ! misho    1664: // return 1 on success.
        !          1665: static int
        !          1666: xresolve(libnet_t* l, const char *name, int r, uint32_t *addr)
        !          1667: {
        !          1668:         if (!strcmp(ip_broadcast, name)) {
        !          1669:                 *addr = 0xffffffff;
        !          1670:                 return 1;
        !          1671:         }
        !          1672:         *addr = libnet_name2addr4(l, (char*)name, r);
        !          1673:         return *addr != 0xffffffff;
        !          1674: }
        !          1675: 
1.1       misho    1676: /**
                   1677:  *
                   1678:  */
1.1.1.4 ! misho    1679: int
        !          1680: arping_main(int argc, char **argv)
1.1       misho    1681: {
                   1682:        char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];
                   1683:        char *cp;
1.1.1.4 ! misho    1684:         const char *srcip_opt = NULL;
        !          1685:         const char *dstip_opt = NULL;
        !          1686:         // `dstip_given` can be set even when there's no arg past flags on the
        !          1687:         // cmdline and -B not set. E.g. -d defaults to self, so requires no
        !          1688:         // extra arg.
1.1       misho    1689:        int dstip_given = 0;
1.1.1.4 ! misho    1690:         const char *srcmac_opt = NULL;
        !          1691:         const char *dstmac_opt = NULL;
        !          1692:        const char *ifname = NULL;      // -i/-I
        !          1693:         int opt_B = 0;
        !          1694:         int opt_T = 0;
        !          1695:         int opt_U = 0;
        !          1696:         const char* drop_group = NULL;  // -g
        !          1697:         const char *parm; // First argument, meaning the target IP.
1.1       misho    1698:        int c;
1.1.1.4 ! misho    1699:        int maxcount = -1;
1.1       misho    1700:        int dont_use_arping_lookupdev=0;
                   1701:        struct bpf_program bp;
                   1702:        pcap_t *pcap;
1.1.1.2   misho    1703:        enum { NONE, PINGMAC, PINGIP } mode = NONE;
1.1.1.4 ! misho    1704:        unsigned int packetwait = 1000000; // Default one second.
        !          1705:         double deadline = -1;
        !          1706:         char bpf_filter[64];
1.1.1.3   misho    1707:         ebuf[0] = 0;
1.1.1.4 ! misho    1708:         srandom(time(NULL));
1.1       misho    1709: 
                   1710:         for (c = 1; c < argc; c++) {
                   1711:                 if (!strcmp(argv[c], "--help")) {
                   1712:                         standard_usage();
                   1713:                         extended_usage();
                   1714:                         exit(0);
                   1715:                 }
                   1716:         }
                   1717: 
                   1718:        srcip = 0;
                   1719:        dstip = 0xffffffff;
1.1.1.2   misho    1720:        memcpy(dstmac, ethxmas, ETH_ALEN);
1.1       misho    1721: 
1.1.1.3   misho    1722:         while (EOF != (c = getopt(argc, argv,
1.1.1.4 ! misho    1723:                                   "0aAbBC:c:dDeFg:hi:I:m:pPqQ:rRs:S:t:T:uUvV:w:W:"))) {
1.1       misho    1724:                switch(c) {
                   1725:                case '0':
1.1.1.4 ! misho    1726:                        srcip_opt = "0.0.0.0";
1.1       misho    1727:                        break;
                   1728:                case 'a':
                   1729:                        beep = 1;
                   1730:                        break;
                   1731:                case 'A':
                   1732:                        addr_must_be_same = 1;
                   1733:                        break;
                   1734:                case 'b':
1.1.1.4 ! misho    1735:                        srcip_opt = ip_broadcast;
1.1       misho    1736:                        break;
                   1737:                case 'B':
1.1.1.4 ! misho    1738:                         dstip_opt = ip_broadcast;
        !          1739:                         dstip_given = 1;
        !          1740:                         opt_B = 1;
1.1       misho    1741:                        break;
                   1742:                case 'c':
                   1743:                        maxcount = atoi(optarg);
                   1744:                        break;
1.1.1.3   misho    1745:                 case 'C':
                   1746:                         max_replies = atoi(optarg);
                   1747:                         break;
1.1       misho    1748:                case 'd':
                   1749:                        finddup = 1;
                   1750:                        break;
                   1751:                case 'D':
                   1752:                        display = DOT;
                   1753:                        break;
                   1754:                 case 'e':
                   1755:                         reverse_beep = 1;
                   1756:                         break;
                   1757:                case 'F':
                   1758:                        dont_use_arping_lookupdev=1;
                   1759:                        break;
                   1760:                case 'h':
                   1761:                        usage(0);
1.1.1.4 ! misho    1762:                         exit(0); // Needless but shuts up compiler warnings.
        !          1763:                 case 'g':
        !          1764:                         drop_group = optarg;
        !          1765:                         break;
1.1       misho    1766:                case 'i':
                   1767:                        if (strchr(optarg, ':')) {
                   1768:                                fprintf(stderr, "arping: If you're trying to "
                   1769:                                        "feed me an interface alias then you "
                   1770:                                        "don't really\nknow what this programs"
                   1771:                                        " does, do you?\nUse -I if you really"
                   1772:                                        " mean it (undocumented on "
                   1773:                                        "purpose)\n");
                   1774:                                exit(1);
                   1775:                        }
1.1.1.4 ! misho    1776:                        ifname = optarg;
        !          1777:                         break;
        !          1778:                case 'I':
1.1       misho    1779:                        ifname = optarg;
                   1780:                        break;
1.1.1.3   misho    1781:                 case 'm':
                   1782:                         timestamp_type = optarg;
                   1783:                         break;
1.1       misho    1784:                case 'p':
                   1785:                        promisc = 1;
                   1786:                        break;
1.1.1.3   misho    1787:                 case 'P':
                   1788:                         send_reply = 1;
                   1789:                         break;
1.1       misho    1790:                case 'q':
                   1791:                        display = QUIET;
                   1792:                        break;
1.1.1.4 ! misho    1793:                 case 'Q':
        !          1794:                         vlan_prio = atoi(optarg);
        !          1795:                         if (vlan_prio < 0 || vlan_prio > 7) {
        !          1796:                                 fprintf(stderr,
        !          1797:                                         "arping: 802.1p priority must be 0-7. It's %d\n",
        !          1798:                                         vlan_prio);
        !          1799:                                 exit(1);
        !          1800:                         }
        !          1801:                         break;
1.1       misho    1802:                case 'r':
                   1803:                        display = (display==RRAW)?RAWRAW:RAW;
                   1804:                        break;
                   1805:                case 'R':
                   1806:                        display = (display==RAW)?RAWRAW:RRAW;
                   1807:                        break;
                   1808:                case 's': { /* spoof source MAC */
1.1.1.4 ! misho    1809:                         srcmac_opt = optarg;
1.1       misho    1810:                        break;
                   1811:                }
                   1812:                case 'S': /* set source IP, may be null for don't-know */
1.1.1.4 ! misho    1813:                         srcip_opt = optarg;
1.1       misho    1814:                        break;
                   1815:                case 't': { /* set taget mac */
1.1.1.4 ! misho    1816:                         dstmac_opt = optarg;
1.1       misho    1817:                        mode = PINGIP;
                   1818:                        break;
                   1819:                }
                   1820:                case 'T': /* set destination IP */
1.1.1.4 ! misho    1821:                         opt_T = 1;
        !          1822:                         dstip_opt = optarg;
1.1       misho    1823:                        mode = PINGMAC;
                   1824:                        break;
                   1825:                case 'u':
                   1826:                        alsototal = 1;
                   1827:                        break;
1.1.1.2   misho    1828:                case 'U':
1.1.1.4 ! misho    1829:                         opt_U = 1;
1.1.1.2   misho    1830:                        unsolicited = 1;
1.1.1.4 ! misho    1831:                         mode = PINGIP;
1.1.1.2   misho    1832:                        break;
1.1       misho    1833:                case 'v':
                   1834:                        verbose++;
                   1835:                        break;
1.1.1.4 ! misho    1836:                case 'V':
        !          1837:                        vlan_tag = atoi(optarg);
        !          1838:                         if (vlan_tag < 0 || vlan_tag > 4095) {
        !          1839:                                 fprintf(stderr,
        !          1840:                                         "arping: vlan tag must 0-4095. Is %d\n",
        !          1841:                                         vlan_tag);
        !          1842:                                 exit(1);
        !          1843:                         }
        !          1844:                         break;
1.1       misho    1845:                case 'w':
1.1.1.4 ! misho    1846:                         deadline = atof(optarg);
1.1       misho    1847:                        break;
1.1.1.3   misho    1848:                 case 'W':
                   1849:                         packetwait = (unsigned)(1000000.0 * atof(optarg));
                   1850:                         break;
1.1       misho    1851:                default:
                   1852:                        usage(1);
                   1853:                }
                   1854:        }
                   1855: 
1.1.1.4 ! misho    1856:         if (argc - optind > 1) {
        !          1857:                 // Can be zero if using -d or -B.
        !          1858:                 fprintf(stderr, "arping: Too many args on command line."
        !          1859:                         " Expected at most one.\n");
        !          1860:                 exit(1);
        !          1861:         }
        !          1862: 
        !          1863:         // Generate random payload suffix for MAC pings, to be able to
        !          1864:         // differentiate from unrelated ping replies.
        !          1865:         if (payload_suffix_size < 0) {
        !          1866:                 payload_suffix_size = 4;
        !          1867:                 payload_suffix = malloc(payload_suffix_size);
        !          1868:                 if (payload_suffix) {
        !          1869:                         const ssize_t rc = xgetrandom(payload_suffix, payload_suffix_size, 0);
        !          1870:                         if (rc == -1) {
        !          1871:                                 fprintf(stderr,
        !          1872:                                         "arping: failed to get %zd random bytes: %s\n",
        !          1873:                                         payload_suffix_size,
        !          1874:                                         strerror(errno));
        !          1875:                                 free(payload_suffix);
        !          1876:                                 payload_suffix = NULL;
        !          1877:                         } else if (payload_suffix_size != rc) {
        !          1878:                                 fprintf(stderr,
        !          1879:                                         "arping: only got %zd out of %zd bytes for random suffix\n",
        !          1880:                                         rc, payload_suffix_size);
        !          1881:                         }
        !          1882:                 } else {
        !          1883:                         fprintf(stderr, "arping: failed to allocate %zd bytes for payload suffix.\n",
        !          1884:                                 payload_suffix_size);
        !          1885:                 }
        !          1886: 
        !          1887:                 if (!payload_suffix) {
        !          1888:                         fprintf(stderr, "arping:  Using constant suffix.\n");
        !          1889:                         payload_suffix = "arping";
        !          1890:                         payload_suffix_size = strlen(payload_suffix);
        !          1891:                 }
        !          1892:         }
        !          1893: 
        !          1894:         if (((mode == PINGIP) && opt_T)
        !          1895:             || ((mode == PINGMAC) && (opt_B || dstmac_opt || opt_U))) {
        !          1896:                 fprintf(stderr, "arping: -T can only be used to ping MAC"
        !          1897:                         " and -BtU only to ping IPs");
        !          1898:                 exit(1);
        !          1899:         }
        !          1900:         if (opt_T && opt_B) {
        !          1901:                 fprintf(stderr,
        !          1902:                         "arping: -B can't be used with -T,"
        !          1903:                         " since they set the same thing\n");
        !          1904:                 exit(1);
        !          1905:         }
        !          1906: 
        !          1907:         if (srcmac_opt != NULL) {
        !          1908:                 if (!get_mac_addr(srcmac_opt, srcmac)) {
        !          1909:                         fprintf(stderr, "arping: Weird MAC addr %s\n",
        !          1910:                                 srcmac_opt);
        !          1911:                         exit(1);
        !          1912:                 }
        !          1913:         }
        !          1914: 
        !          1915:         if (dstmac_opt != NULL) {
        !          1916:                 if (!get_mac_addr(dstmac_opt, dstmac)) {
        !          1917:                         fprintf(stderr, "Illegal MAC addr %s\n", dstmac_opt);
        !          1918:                         exit(1);
        !          1919:                 }
        !          1920:         }
        !          1921: 
        !          1922:         if (srcip_opt != NULL) {
        !          1923:                 do_libnet_init(ifname, 0);
        !          1924:                 if (!xresolve(libnet, srcip_opt, LIBNET_RESOLVE, &srcip)) {
        !          1925:                         fprintf(stderr, "arping: Can't resolve %s, or "
        !          1926:                                 "%s is broadcast. If it is, use -b"
        !          1927:                                 " instead of -S\n", srcip_opt,srcip_opt);
        !          1928:                         exit(1);
        !          1929:                 }
        !          1930:         }
        !          1931: 
        !          1932:         if (dstip_opt) {
        !          1933:                 do_libnet_init(ifname, 0);
        !          1934:                 if (!xresolve(libnet, dstip_opt, LIBNET_RESOLVE, &dstip)) {
        !          1935:                         fprintf(stderr,"arping: Can't resolve %s, or "
        !          1936:                                 "%s is broadcast. If it is, use -B "
        !          1937:                                 "instead of -T\n",dstip_opt,dstip_opt);
        !          1938:                         exit(1);
        !          1939:                 }
        !          1940:         }
        !          1941: 
        !          1942:         if (vlan_prio >= 0 && vlan_tag == -1) {
        !          1943:                 fprintf(stderr, "arping: -Q requires the use of 802.1Q (-V)\n");
        !          1944:                 exit(1);
        !          1945:         }
        !          1946:         if (vlan_prio == -1) {
        !          1947:                 vlan_prio = 0;
        !          1948:         }
        !          1949: 
1.1.1.2   misho    1950:         if (verbose > 1) {
                   1951: #if HAVE_CLOCK_MONOTONIC
                   1952:                 struct timespec ts;
1.1.1.4 ! misho    1953:                 if (clock_getres(CLOCK_MONOTONIC, &ts)) {
        !          1954:                         fprintf(stderr,
        !          1955:                                 "arping: clock_getres(CLOCK_MONOTONIC, ...): %s\n",
        !          1956:                                 strerror(errno));
        !          1957:                 } else {
        !          1958:                         printf("arping: clock_getres() = %lds %ldns\n",
        !          1959:                                (long)ts.tv_sec, (long)ts.tv_nsec);
        !          1960:                 }
1.1.1.2   misho    1961: #else
1.1.1.3   misho    1962:                 printf("arping: Using gettimeofday() for time measurements\n");
1.1.1.2   misho    1963: #endif
                   1964:         }
                   1965: 
1.1       misho    1966:         if (display == DOT) {
1.1.1.4 ! misho    1967:                 if (0 != setvbuf(stdout, NULL, _IONBF, 0)) {
        !          1968:                         fprintf(stderr,
        !          1969:                                 "arping: setvbuf(stdout, NULL, IONBF, 0): %s\n",
        !          1970:                                 strerror(errno));
        !          1971:                 }
1.1       misho    1972:         }
                   1973: 
                   1974:         if (finddup && maxcount == -1) {
                   1975:                 maxcount = 3;
                   1976:         }
                   1977: 
                   1978:        parm = (optind < argc) ? argv[optind] : NULL;
                   1979: 
                   1980:         /* default to own IP address when doing -d */
                   1981:         if (finddup && !parm) {
                   1982:                 dstip_given = 1;
1.1.1.3   misho    1983:                 do_libnet_init(ifname, 0);
1.1       misho    1984:                 dstip = libnet_get_ipaddr4(libnet);
                   1985:                 if (verbose) {
                   1986:                         printf("defaulting to checking dup for %s\n",
                   1987:                                libnet_addr2name4(dstip, 0));
                   1988:                 }
                   1989:         }
                   1990: 
                   1991:        /*
                   1992:         * Handle dstip_given instead of ip address after parms (-B really)
                   1993:         */
                   1994:        if (mode == NONE) {
1.1.1.4 ! misho    1995:                if (parm) {
1.1       misho    1996:                        mode = is_mac_addr(parm)?PINGMAC:PINGIP;
                   1997:                } else if (dstip_given) {
                   1998:                        mode = PINGIP;
1.1.1.3   misho    1999:                         do_libnet_init(ifname, 0);
1.1       misho    2000:                        parm = strdup(libnet_addr2name4(dstip,0));
                   2001:                        if (!parm) {
1.1.1.4 ! misho    2002:                                fprintf(stderr, "arping: out of memory\n");
1.1       misho    2003:                                exit(1);
                   2004:                        }
                   2005:                }
                   2006:        }
                   2007: 
                   2008:        if (!parm) {
                   2009:                usage(1);
                   2010:        }
                   2011: 
                   2012:        /*
                   2013:         *
                   2014:         */
                   2015:        if (mode == NONE) {
                   2016:                usage(1);
                   2017:        }
                   2018: 
                   2019:        /*
                   2020:         * libnet init (may be done already for resolving)
                   2021:         */
1.1.1.3   misho    2022:         do_libnet_init(ifname, 0);
                   2023: 
1.1       misho    2024:        /*
                   2025:         * Make sure dstip and parm like eachother
                   2026:         */
                   2027:        if (mode == PINGIP && (!dstip_given)) {
                   2028:                if (is_mac_addr(parm)) {
                   2029:                        fprintf(stderr, "arping: Options given only apply to "
                   2030:                                "IP ping, but MAC address given as argument"
                   2031:                                "\n");
                   2032:                        exit(1);
                   2033:                }
1.1.1.4 ! misho    2034:                 if (!xresolve(libnet, parm, LIBNET_RESOLVE, &dstip)) {
1.1       misho    2035:                        fprintf(stderr, "arping: Can't resolve %s\n", parm);
                   2036:                        exit(1);
                   2037:                }
                   2038:                parm = strdup(libnet_addr2name4(dstip,0));
                   2039:        }
                   2040: 
                   2041:        /*
                   2042:         * parse parm into dstmac
                   2043:         */
                   2044:        if (mode == PINGMAC) {
                   2045:                if (optind + 1 != argc) {
                   2046:                        usage(1);
                   2047:                }
                   2048:                if (!is_mac_addr(parm)) {
                   2049:                        fprintf(stderr, "arping: Options given only apply to "
                   2050:                                "MAC ping, but no MAC address given as "
                   2051:                                "argument\n");
                   2052:                        exit(1);
                   2053:                }
1.1.1.4 ! misho    2054:                 if (!get_mac_addr(argv[optind], dstmac)) {
1.1       misho    2055:                        fprintf(stderr, "arping: Illegal mac addr %s\n",
                   2056:                                argv[optind]);
                   2057:                        return 1;
                   2058:                }
1.1.1.4 ! misho    2059:        }
1.1       misho    2060: 
                   2061:        target = parm;
                   2062:        /*
                   2063:         * Argument processing done, parameters considered sane below
                   2064:         */
                   2065: 
                   2066:        /*
                   2067:         * Get some good iface.
                   2068:         */
                   2069:        if (!ifname) {
1.1.1.2   misho    2070:                 if (!dont_use_arping_lookupdev) {
                   2071:                         ifname = arping_lookupdev(srcip, dstip, ebuf);
1.1.1.3   misho    2072:                         strip_newline(ebuf);
                   2073:                         if (!ifname) {
                   2074:                                 fprintf(stderr, "arping: lookup dev: %s\n",
                   2075:                                         ebuf);
                   2076:                         }
1.1.1.2   misho    2077:                 }
                   2078:                 if (!ifname) {
                   2079:                         ifname = arping_lookupdev_default(srcip, dstip, ebuf);
1.1.1.3   misho    2080:                         strip_newline(ebuf);
                   2081:                         if (ifname && !dont_use_arping_lookupdev) {
1.1.1.2   misho    2082:                                 fprintf(stderr,
                   2083:                                         "arping: Unable to automatically find "
                   2084:                                         "interface to use. Is it on the local "
                   2085:                                         "LAN?\n"
                   2086:                                         "arping: Use -i to manually "
                   2087:                                         "specify interface. "
                   2088:                                         "Guessing interface %s.\n", ifname);
                   2089:                         }
1.1       misho    2090:                }
                   2091:                if (!ifname) {
1.1.1.2   misho    2092:                         fprintf(stderr, "arping: Gave up looking for interface"
                   2093:                                 " to use: %s\n", ebuf);
1.1       misho    2094:                        exit(1);
                   2095:                }
                   2096:                /* FIXME: check for other probably-not interfaces */
                   2097:                if (!strcmp(ifname, "ipsec")
                   2098:                    || !strcmp(ifname,"lo")) {
                   2099:                        fprintf(stderr, "arping: Um.. %s looks like the wrong "
                   2100:                                "interface to use. Is it? "
                   2101:                                "(-i switch)\n", ifname);
                   2102:                        fprintf(stderr, "arping: using it anyway this time\n");
                   2103:                }
                   2104:        }
                   2105: 
                   2106:        /*
                   2107:         * Init libnet again, because we now know the interface name.
                   2108:         * We should know it by know at least
                   2109:         */
1.1.1.3   misho    2110:         do_libnet_init(ifname, 0);
1.1       misho    2111: 
                   2112:        /*
                   2113:         * pcap init
                   2114:         */
1.1.1.2   misho    2115:         if (!(pcap = do_pcap_open_live(ifname, 100, promisc, 10, ebuf))) {
1.1.1.3   misho    2116:                 strip_newline(ebuf);
                   2117:                 fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf);
1.1       misho    2118:                exit(1);
                   2119:        }
1.1.1.4 ! misho    2120:         drop_privileges(drop_group);
1.1       misho    2121:        if (pcap_setnonblock(pcap, 1, ebuf)) {
1.1.1.3   misho    2122:                 strip_newline(ebuf);
1.1       misho    2123:                fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf);
                   2124:                exit(1);
                   2125:        }
1.1.1.2   misho    2126:        if (verbose > 1) {
1.1.1.3   misho    2127:                printf("arping: pcap_get_selectable_fd(): %d\n",
1.1       misho    2128:                       pcap_get_selectable_fd(pcap));
                   2129:        }
                   2130: 
1.1.1.2   misho    2131: #ifdef BIOCIMMEDIATE
1.1       misho    2132:        {
1.1.1.3   misho    2133:                 // This may be redundant if pcap_set_immediate_mode() is present.
1.1       misho    2134:                uint32_t on = 1;
                   2135:                if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE,
                   2136:                               &on))) {
                   2137:                        fprintf(stderr, "arping: ioctl(fd,BIOCIMMEDIATE, 1) "
                   2138:                                "failed, continuing anyway, YMMV: %s\n",
                   2139:                                strerror(errno));
                   2140:                }
                   2141:        }
                   2142: #endif
                   2143: 
                   2144:        if (mode == PINGIP) {
                   2145:                /* FIXME: better filter with addresses? */
1.1.1.4 ! misho    2146:                 if (vlan_tag >= 0) {
        !          2147:                         snprintf(bpf_filter, sizeof(bpf_filter),
        !          2148:                                  "vlan %u and arp", vlan_tag);
        !          2149:                 } else {
        !          2150:                         snprintf(bpf_filter, sizeof(bpf_filter), "arp");
        !          2151:                 }
        !          2152:                 if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0, -1)) {
        !          2153:                         fprintf(stderr, "arping: pcap_compile(%s): %s\n",
        !          2154:                                 bpf_filter, pcap_geterr(pcap));
1.1       misho    2155:                        exit(1);
                   2156:                }
                   2157:        } else { /* ping mac */
                   2158:                /* FIXME: better filter with addresses? */
1.1.1.4 ! misho    2159:                 if (vlan_tag >= 0) {
        !          2160:                         snprintf(bpf_filter, sizeof(bpf_filter),
        !          2161:                                  "vlan %u and icmp", vlan_tag);
        !          2162:                 } else {
        !          2163:                         snprintf(bpf_filter, sizeof(bpf_filter), "icmp");
        !          2164:                 }
        !          2165:                 if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0,-1)) {
        !          2166:                         fprintf(stderr, "arping: pcap_compile(%s): %s\n",
        !          2167:                                 bpf_filter, pcap_geterr(pcap));
1.1       misho    2168:                        exit(1);
                   2169:                }
                   2170:        }
                   2171:        if (-1 == pcap_setfilter(pcap, &bp)) {
1.1.1.4 ! misho    2172:                 fprintf(stderr, "arping: pcap_setfilter(): %s\n",
        !          2173:                         pcap_geterr(pcap));
1.1       misho    2174:                exit(1);
                   2175:        }
                   2176: 
                   2177:        /*
                   2178:         * final init
                   2179:         */
1.1.1.4 ! misho    2180:         if (srcmac_opt == NULL) {
1.1       misho    2181:                if (!(cp = (char*)libnet_get_hwaddr(libnet))) {
                   2182:                        fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n",
                   2183:                                libnet_geterror(libnet));
                   2184:                        exit(1);
                   2185:                }
                   2186:                memcpy(srcmac, cp, ETH_ALEN);
                   2187:        }
1.1.1.4 ! misho    2188:         if (srcip_opt == NULL) {
        !          2189:                if (ip_broadcast_num == (srcip = libnet_get_ipaddr4(libnet))) {
1.1.1.2   misho    2190:                        fprintf(stderr,
                   2191:                                 "arping: Unable to get the IPv4 address of "
                   2192:                                 "interface %s:\narping: %s"
                   2193:                                 "arping: "
                   2194:                                 "Use -S to specify address manually.\n",
                   2195:                                 ifname, libnet_geterror(libnet));
1.1       misho    2196:                        exit(1);
                   2197:                }
                   2198:        }
1.1.1.2   misho    2199:         do_signal_init();
1.1       misho    2200: 
                   2201:        if (verbose) {
1.1.1.3   misho    2202:                 char buf[128];
                   2203:                printf("This box:   Interface: %s  IP: %s   MAC address: %s\n",
                   2204:                       ifname,
                   2205:                        libnet_addr2name4(libnet_get_ipaddr4(libnet), 0),
                   2206:                        format_mac(srcmac, buf, sizeof(buf)));
1.1       misho    2207:        }
                   2208: 
1.1.1.4 ! misho    2209:         drop_more_privileges();
1.1       misho    2210: 
                   2211:        if (display == NORMAL) {
                   2212:                printf("ARPING %s\n", parm);
                   2213:        }
                   2214: 
                   2215:        /*
                   2216:         * let's roll
                   2217:         */
1.1.1.4 ! misho    2218:         if (deadline > 0) {
        !          2219:                 struct timespec ts;
        !          2220:                 getclock(&ts);
        !          2221:                 deadline += timespec2dbl(&ts);
        !          2222:         }
1.1       misho    2223:        if (mode == PINGIP) {
1.1.1.4 ! misho    2224:                int c;
        !          2225:                for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) {
1.1.1.2   misho    2226:                        pingip_send();
1.1.1.4 ! misho    2227:                         const uint32_t w = wait_time(deadline, packetwait);
        !          2228:                         if (w == 0) {
        !          2229:                                 break;
        !          2230:                         }
        !          2231:                         ping_recv(pcap, w, (pcap_handler)pingip_recv);
1.1       misho    2232:                }
                   2233:        } else { /* PINGMAC */
1.1.1.4 ! misho    2234:                int c;
        !          2235:                for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) {
        !          2236:                        pingmac_send(xrandom(), c);
        !          2237:                         const uint32_t w = wait_time(deadline, packetwait);
        !          2238:                         if (w == 0) {
        !          2239:                                 break;
        !          2240:                         }
        !          2241:                         ping_recv(pcap, w,  (pcap_handler)pingmac_recv);
1.1       misho    2242:                }
                   2243:        }
                   2244:         if (display == DOT) {
1.1.1.3   misho    2245:                 const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent;
                   2246:                 printf("\t%3.0f%% packet loss (%d extra)\n",
                   2247:                        (succ < 0.0) ? 0.0 : succ,
                   2248:                        (succ < 0.0) ? (numrecvd - numsent) : 0);
1.1       misho    2249:         } else if (display == NORMAL) {
1.1.1.3   misho    2250:                 const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent;
1.1       misho    2251:                 printf("\n--- %s statistics ---\n"
                   2252:                        "%d packets transmitted, "
                   2253:                        "%d packets received, "
                   2254:                        "%3.0f%% "
                   2255:                        "unanswered (%d extra)\n",
                   2256:                        target,numsent,numrecvd,
                   2257:                        (succ < 0.0) ? 0.0 : succ,
1.1.1.2   misho    2258:                        (succ < 0.0) ? (numrecvd - numsent): 0);
                   2259:                 if (numrecvd) {
                   2260:                         double avg = stats_total_time / numrecvd;
                   2261:                         printf("rtt min/avg/max/std-dev = "
                   2262:                                "%.3f/%.3f/%.3f/%.3f ms",
                   2263:                                1000*stats_min_time,
                   2264:                                1000*avg,
                   2265:                                1000*stats_max_time,
                   2266:                                1000*sqrt(stats_total_sq_time/numrecvd
                   2267:                                          -avg*avg));
                   2268:                 }
                   2269:                 printf("\n");
1.1       misho    2270:        }
                   2271: 
                   2272:         if (finddup) {
                   2273:                 return dupfound;
                   2274:         } else {
                   2275:                 return !numrecvd;
                   2276:         }
                   2277: }
1.1.1.2   misho    2278: /* ---- Emacs Variables ----
                   2279:  * Local Variables:
                   2280:  * c-basic-offset: 8
                   2281:  * indent-tabs-mode: nil
                   2282:  * End:
1.1.1.4 ! misho    2283:  *
        !          2284:  * vim: ts=8 sw=8
1.1.1.2   misho    2285:  */

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