Annotation of embedaddon/arping/src/arping.c, revision 1.1.1.3
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.3 ! misho 18: * Copyright (C) 2000-2014 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:
48: #if HAVE_STDINT_H
49: #include <stdint.h>
50: #endif
51:
52: #if HAVE_INTTYPES_H
53: #include <inttypes.h>
54: #endif
55:
1.1.1.2 misho 56: #if HAVE_TIME_H
57: #include <time.h>
58: #endif
59:
1.1 misho 60: #if HAVE_SYS_TIME_H
61: #include <sys/time.h>
62: #endif
63:
64: #if HAVE_SYS_TYPES_H
65: #include <sys/types.h>
66: #endif
67:
68: #if HAVE_SYS_SOCKET_H
69: #include <sys/socket.h>
70: #endif
71:
72: #if HAVE_NETINET_IN_H
73: #include <netinet/in.h>
74: #endif
75:
76: #if HAVE_ARPA_INET_H
77: #include <arpa/inet.h>
78: #endif
79:
80: #if HAVE_LIBNET_H
81: #include <libnet.h>
82: #endif
83:
84: #if HAVE_WIN32_LIBNET_H
85: #include <win32/libnet.h>
86: #endif
87:
1.1.1.3 ! misho 88: #if HAVE_PWD_H
! 89: #include <pwd.h>
! 90: #endif
! 91:
! 92: #if HAVE_SYS_CAPABILITY_H
! 93: #include <sys/capability.h>
! 94: #endif
! 95:
1.1 misho 96: #if HAVE_NET_BPF_H
97: #include <net/bpf.h>
98: #endif
1.1.1.2 misho 99: #include <pcap.h>
100:
101: #include "arping.h"
1.1 misho 102:
103: #ifndef ETH_ALEN
104: #define ETH_ALEN 6
105: #endif
106:
107: #ifndef IP_ALEN
108: #define IP_ALEN 4
109: #endif
110:
111: #ifndef WIN32
112: #define WIN32 0
113: #endif
114:
1.1.1.2 misho 115: #ifndef CLOCK_MONOTONIC
116: #define CLOCK_MONOTONIC CLOCK_REALTIME
117: #endif
118:
1.1 misho 119: /**
120: * OS-specific interface finding using routing table. See findif_*.c
1.1.1.3 ! misho 121: * ebuf must be called with a size of at least
! 122: * max(LIBNET_ERRBUF_SIZE, PCAP_ERRBUF_SIZE).
1.1 misho 123: */
124: const char *
1.1.1.2 misho 125: arping_lookupdev(uint32_t srcip, uint32_t dstip, char *ebuf);
126:
127: const char *
128: arping_lookupdev_default(uint32_t srcip, uint32_t dstip, char *ebuf);
1.1 misho 129:
130: static const char *version = VERSION; /* from autoconf */
131:
132: static libnet_t *libnet = 0;
133:
1.1.1.3 ! misho 134: /* Timestamp of last packet sent.
! 135: * Used for timing, and assumes that reply is due to most recent sent query.
! 136: */
1.1.1.2 misho 137: static struct timespec lastpacketsent;
1.1 misho 138:
1.1.1.2 misho 139: /* target string */
140: static char *target = "huh? bug in arping?";
141:
142: /*
143: * Ping IP mode: cmdline target
144: * Ping MAC mode: 255.255.255.255, override with -T
145: */
146: static uint32_t dstip;
147:
148: /*
149: * Ping IP mode: ethxmas, override with -t
150: * Ping MAC mode: cmdline target
151: */
152: static uint8_t dstmac[ETH_ALEN];
153:
154: static uint32_t srcip; /* autodetected, override with -S/-b/-0 */
155: static uint8_t srcmac[ETH_ALEN]; /* autodetected, override with -s */
156:
157: static int beep = 0; /* beep when reply is received. -a */
158: static int reverse_beep = 0; /* beep when expected reply absent. -e */
159: static int alsototal = 0; /* print sent as well as received. -u */
160: static int addr_must_be_same = 0; /* -A */
161: static int unsolicited = 0; /* -U */
1.1.1.3 ! misho 162: static int send_reply = 0; /* Send reply instead of request. -P */
1.1.1.2 misho 163:
164: static int finddup = 0; /* finddup mode. -d */
165: static int dupfound = 0; /* set to 1 if dup found */
166: static char lastreplymac[ETH_ALEN]; /* if last different from this then dup */
167:
1.1.1.3 ! misho 168: static unsigned int numsent = 0; /* packets sent */
! 169: static unsigned int numrecvd = 0; /* packets received */
! 170: static unsigned int max_replies = UINT_MAX; /* exit after -C replies */
! 171: static unsigned int numdots = 0; /* dots that should be printed */
! 172: static const char* timestamp_type = NULL; /* Incoming packet measurement ts type (-m) */
1.1.1.2 misho 173:
174: static double stats_min_time = -1;
175: static double stats_max_time = -1;
176: static double stats_total_time = 0;
177: static double stats_total_sq_time = 0;
1.1 misho 178:
179: /* RAWRAW is RAW|RRAW */
1.1.1.2 misho 180: static enum { NORMAL, /* normal output */
181: QUIET, /* No output. -q */
182: RAW, /* Print MAC when pinging IP. -r */
183: RRAW, /* Print IP when pinging IP. -R */
184: RAWRAW, /* Print both. -r and -R */
185: DOT /* Print '.' and '!', Cisco-style. -D */
186: } display = NORMAL;
187:
188: static const uint8_t ethnull[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
189: static const uint8_t ethxmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
190:
191: int verbose = 0; /* Increase with -v */
1.1 misho 192:
1.1.1.2 misho 193: /* Doesn't really need to be volatile, but doesn't hurt. */
194: static volatile sig_atomic_t time_to_die = 0;
195:
196: /**
1.1.1.3 ! misho 197: * If possible, chroot.
! 198: *
! 199: * The sshd user is used for privilege separation in OpenSSH.
! 200: * Let's assume it's installed and chroot() to there.
! 201: */
! 202: static void
! 203: drop_fs_root()
! 204: {
! 205: const char* chroot_user = "sshd";
! 206: struct passwd *pw;
! 207: errno = 0;
! 208: if (!(pw = getpwnam(chroot_user))) {
! 209: if (verbose) {
! 210: printf("arping: getpwnam(%s): %s",
! 211: chroot_user, strerror(errno));
! 212: }
! 213: return;
! 214: }
! 215: if (chdir(pw->pw_dir)) {
! 216: if (verbose) {
! 217: printf("arping: chdir(%s): %s",
! 218: pw->pw_dir, strerror(errno));
! 219: }
! 220: return;
! 221: }
! 222: if (chroot(pw->pw_dir)) {
! 223: if (verbose) {
! 224: printf("arping: chroot(%s): %s",
! 225: pw->pw_dir, strerror(errno));
! 226: }
! 227: return;
! 228: }
! 229: if (verbose > 1) {
! 230: printf("arping: Successfully chrooted to %s\n", pw->pw_dir);
! 231: }
! 232: }
! 233:
! 234: /**
! 235: * If possible, drop uid to nobody.
! 236: *
! 237: * This code only successfully sets all [ug]ids if running as
! 238: * root. ARPing is most likely running as root unless using
! 239: * capabilities, and those are dropped elsewhere.
! 240: */
! 241: static void
! 242: drop_uid(uid_t uid, gid_t gid)
! 243: {
! 244: int fail = 0;
! 245: if (setgroups(0, NULL)) {
! 246: if (verbose) {
! 247: printf("arping: setgroups(0, NULL): %s\n", strerror(errno));
! 248: }
! 249: fail++;
! 250: }
! 251: if (gid && setgid(gid)) {
! 252: if (verbose) {
! 253: printf("arping: setgid(): %s\n", strerror(errno));
! 254: }
! 255: fail++;
! 256: }
! 257: if (uid && setuid(uid)) {
! 258: if (verbose) {
! 259: printf("arping: setuid(): %s\n", strerror(errno));
! 260: }
! 261: fail++;
! 262: }
! 263: if (!fail && verbose > 1) {
! 264: printf("arping: Successfully dropped uid/gid to %d/%d.\n",
! 265: uid, gid);
! 266: }
! 267: }
! 268:
! 269: /**
! 270: * Drop any and all capabilities.
! 271: */
! 272: static void
! 273: drop_capabilities()
! 274: {
! 275: #if HAVE_CAP_INIT
! 276: cap_t no_cap;
! 277: if (!(no_cap = cap_init())) {
! 278: if (verbose) {
! 279: printf("arping: cap_init(): %s\n", strerror(errno));
! 280: }
! 281: return;
! 282: }
! 283: if (cap_set_proc(no_cap)) {
! 284: if (verbose) {
! 285: printf("arping: cap_set_proc(): %s\n", strerror(errno));
! 286: }
! 287: }
! 288: if (verbose > 1) {
! 289: printf("arping: Successfully dropped all capabilities.\n");
! 290: }
! 291: cap_free(no_cap);
! 292: #endif
! 293: }
! 294:
! 295: /**
! 296: * drop all privileges.
! 297: */
! 298: static void
! 299: drop_privileges()
! 300: {
! 301: // Need to get uid/gid of 'nobody' before chroot().
! 302: const char* drop_user = "nobody";
! 303: struct passwd *pw;
! 304: errno = 0;
! 305: uid_t uid = 0;
! 306: gid_t gid = 0;
! 307: if (!(pw = getpwnam(drop_user))) {
! 308: if (verbose) {
! 309: printf("arping: getpwnam(%s): %s\n",
! 310: drop_user, strerror(errno));
! 311: }
! 312: return;
! 313: } else {
! 314: uid = pw->pw_uid;
! 315: gid = pw->pw_gid;
! 316: }
! 317: drop_fs_root();
! 318: drop_uid(uid, gid);
! 319: drop_capabilities();
! 320: }
! 321:
! 322:
! 323: /**
! 324: * Do pcap_open_live(), except by using the pcap_create() interface
! 325: * introduced in 2008 (libpcap 0.4) where available.
! 326: *
! 327: * FIXME: Use pcap_set_buffer_size()?
! 328: */
! 329: static pcap_t*
! 330: try_pcap_open_live(const char *device, int snaplen,
! 331: int promisc, int to_ms, char *errbuf)
! 332: {
! 333: #ifdef HAVE_PCAP_CREATE
! 334: pcap_t* pcap;
! 335: int rc;
! 336:
! 337: if (!(pcap = pcap_create(device, errbuf))) {
! 338: goto err;
! 339: }
! 340: if ((rc = pcap_set_snaplen(pcap, snaplen))) {
! 341: snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_snaplen(): %s", pcap_statustostr(rc));
! 342: goto err;
! 343: }
! 344: if ((rc = pcap_set_promisc(pcap, promisc))) {
! 345: snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_promisc(): %s", pcap_statustostr(rc));
! 346: goto err;
! 347: }
! 348: if ((rc = pcap_set_timeout(pcap, to_ms))) {
! 349: snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_timeout(): %s", pcap_statustostr(rc));
! 350: goto err;
! 351: }
! 352:
! 353: #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
! 354: // Without immediate mode some architectures (e.g. Linux with
! 355: // TPACKET_V3) will buffer replies and incorrectly upwards of
! 356: // hundreds of milliseconds of delay.
! 357: if ((rc = pcap_set_immediate_mode(pcap, 1))) {
! 358: if (verbose) {
! 359: fprintf(stderr, "arping: pcap_set_immediate_mode() failed: %s\n", pcap_statustostr(rc));
! 360: }
! 361: }
! 362: #endif
! 363: #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES
! 364: if (timestamp_type) {
! 365: int err;
! 366: int v = pcap_tstamp_type_name_to_val(timestamp_type);
! 367: if (v == PCAP_ERROR) {
! 368: fprintf(stderr, "arping: Unknown timestamp type \"%s\"\n", timestamp_type);
! 369: exit(1);
! 370: }
! 371: if ((err = pcap_set_tstamp_type(pcap, v))) {
! 372: fprintf(stderr,
! 373: "arping: Failed to set timestamp type \"%s\" (%d): %s\n",
! 374: timestamp_type, v, pcap_statustostr(err));
! 375: }
! 376: }
! 377: #endif
! 378: if ((rc = pcap_activate(pcap))) {
! 379: if (timestamp_type) {
! 380: snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(tstype=\"%s\"): %s. Try without setting timestamp type.", timestamp_type, pcap_statustostr(rc));
! 381: } else {
! 382: snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(): %s", pcap_statustostr(rc));
! 383: }
! 384: goto err;
! 385: }
! 386: #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES
! 387: // List timestamp types after activating, since we don't want to list
! 388: // them if activating failed.
! 389: if (verbose > 1) {
! 390: int *ts;
! 391: int count;
! 392: count = pcap_list_tstamp_types(pcap, &ts);
! 393: if (count == PCAP_ERROR) {
! 394: fprintf(stderr, "arping: pcap_list_tstamp_types() failed\n");
! 395: } else {
! 396: int c;
! 397: const char* fmt = " %-18s %s\n";
! 398: fprintf(stderr, "Timestamp types:\n");
! 399: fprintf(stderr, fmt, "Name", "Description");
! 400: for (c = 0; c < count; c++) {
! 401: fprintf(stderr, fmt, pcap_tstamp_type_val_to_name(ts[c]),
! 402: pcap_tstamp_type_val_to_description(ts[c]));
! 403: }
! 404: pcap_free_tstamp_types(ts);
! 405: }
! 406: }
! 407: #endif
! 408: return pcap;
! 409: err:
! 410: if (pcap) {
! 411: pcap_close(pcap);
! 412: }
! 413: return NULL;
! 414: #else
! 415: return pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
! 416: #endif
! 417: }
! 418:
! 419: /**
1.1.1.2 misho 420: * Some stupid OSs (Solaris) think it's a good idea to put network
421: * devices in /dev and then play musical chairs with them.
422: *
423: * Since libpcap doesn't seem to have a workaround for that, here's arpings
424: * workaround.
425: *
426: * E.g. if the network interface is called net0, pcap will fail because it
427: * fails to open /dev/net, because it's a directory.
428: */
429: static pcap_t*
430: do_pcap_open_live(const char *device, int snaplen,
431: int promisc, int to_ms, char *errbuf)
432: {
433: pcap_t* ret;
434: char buf[PATH_MAX];
435:
1.1.1.3 ! misho 436: if ((ret = try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2 misho 437: return ret;
438: }
439:
440: snprintf(buf, sizeof(buf), "/dev/%s", device);
1.1.1.3 ! misho 441: if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2 misho 442: return ret;
443: }
444:
445: snprintf(buf, sizeof(buf), "/dev/net/%s", device);
1.1.1.3 ! misho 446: if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) {
1.1.1.2 misho 447: return ret;
448: }
449:
450: /* Call original again to reset the error message. */
1.1.1.3 ! misho 451: return try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf);
1.1.1.2 misho 452: }
1.1 misho 453:
454: /**
1.1.1.3 ! misho 455: * Some Libnet error messages end with a newline. Strip that in place.
1.1 misho 456: */
1.1.1.3 ! misho 457: void
! 458: strip_newline(char* s) {
! 459: if (!*s) {
! 460: return;
! 461: }
! 462: size_t n;
! 463: for (n = strlen(s); s[n - 1] == '\n'; --n) {
! 464: s[n - 1] = 0;
1.1 misho 465: }
466: }
467:
468: /**
1.1.1.2 misho 469: * Init libnet with specified ifname. Destroy if already inited.
1.1.1.3 ! misho 470: * If this function retries with different parameter it will preserve
! 471: * the original error message and print that.
! 472: * Call with recursive=0.
1.1.1.2 misho 473: */
1.1 misho 474: void
1.1.1.3 ! misho 475: do_libnet_init(const char *ifname, int recursive)
1.1 misho 476: {
477: char ebuf[LIBNET_ERRBUF_SIZE];
1.1.1.3 ! misho 478: ebuf[0] = 0;
1.1 misho 479: if (verbose > 1) {
1.1.1.3 ! misho 480: printf("arping: libnet_init(%s)\n", ifname ? ifname : "<null>");
1.1 misho 481: }
482: if (libnet) {
1.1.1.2 misho 483: /* Probably going to switch interface from temp to real. */
1.1 misho 484: libnet_destroy(libnet);
485: libnet = 0;
486: }
487:
1.1.1.2 misho 488: /* Try libnet_init() even though we aren't root. We may have
489: * a capability or something. */
1.1 misho 490: if (!(libnet = libnet_init(LIBNET_LINK,
491: (char*)ifname,
492: ebuf))) {
1.1.1.3 ! misho 493: strip_newline(ebuf);
! 494: if (!ifname) {
! 495: /* Sometimes libnet guesses an interface that it then
! 496: * can't use. Work around that by attempting to
! 497: * use "lo". */
! 498: return do_libnet_init("lo", 1);
! 499: } else if (recursive) {
! 500: /* Continue original execution. */
! 501: return;
! 502: }
! 503: fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n",
! 504: ifname ? ifname : "<null>", ebuf);
1.1.1.2 misho 505: if (getuid() && geteuid()) {
506: fprintf(stderr,
507: "arping: you may need to run as root\n");
508: }
1.1 misho 509: exit(1);
510: }
511: }
512:
513: /**
514: *
515: */
1.1.1.2 misho 516: void
517: sigint(int i)
1.1 misho 518: {
519: time_to_die = 1;
520: }
521:
522: /**
1.1.1.2 misho 523: * idiot-proof clock_gettime() wrapper
1.1 misho 524: */
1.1.1.2 misho 525: static void
526: getclock(struct timespec *ts)
1.1 misho 527: {
1.1.1.2 misho 528: #if HAVE_CLOCK_MONOTONIC
529: if (-1 == clock_gettime(CLOCK_MONOTONIC, ts)) {
530: fprintf(stderr,
531: "arping: clock_gettime(): %s\n",
532: strerror(errno));
533: sigint(0);
534: }
535: #else
536: struct timeval tv;
537: if (-1 == gettimeofday(&tv, NULL)) {
538: fprintf(stderr, "arping: gettimeofday(): %s\n",
539: strerror(errno));
540: sigint(0);
541: }
542: ts->tv_sec = tv.tv_sec;
543: ts->tv_nsec = tv.tv_usec * 1000;
544: #endif
1.1 misho 545: }
546:
547: /**
548: *
549: */
1.1.1.3 ! misho 550: static char*
! 551: format_mac(unsigned char* mac, char* buf, size_t bufsize) {
! 552: snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
! 553: mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
! 554: return buf;
! 555: }
! 556:
! 557: /**
! 558: *
! 559: */
1.1 misho 560: static void
561: extended_usage()
562: {
563: printf("\nOptions:\n");
564: printf("\n"
565: " -0 Use this option to ping with source IP address 0.0.0.0. Use this\n"
566: " when you haven't configured your interface yet. Note that this\n"
567: " may get the MAC-ping unanswered. This is an alias for -S\n"
568: " 0.0.0.0.\n"
569: " -a Audiable ping.\n"
570: " -A Only count addresses matching requested address (This *WILL*\n"
571: " break most things you do. Only useful if you are arpinging many\n"
572: " hosts at once. See arping-scan-net.sh for an example).\n"
573: " -b Like -0 but source broadcast source address (255.255.255.255).\n"
574: " Note that this may get the arping unanswered since it's not nor-\n"
575: " mal behavior for a host.\n"
576: " -B Use instead of host if you want to address 255.255.255.255.\n"
577: " -c count\n"
578: " Only send count requests.\n"
1.1.1.3 ! misho 579: " -C count\n"
! 580: " Only wait for this many replies, regardless of -c and -w.\n"
1.1 misho 581: " -d Find duplicate replies. Exit with 1 if there are "
582: "answers from\n"
583: " two different MAC addresses.\n"
1.1.1.2 misho 584: " -D Display answers as exclamation points and missing packets as dots.\n"
1.1 misho 585: " -e Like -a but beep when there is no reply.\n"
586: " -F Don't try to be smart about the interface name. (even if this\n"
587: " switch is not given, -i overrides smartness)\n"
588: " -h Displays a help message and exits.\n"
589: " -i interface\n"
590: " Use the specified interface.\n"
1.1.1.3 ! misho 591: " -m type"
! 592: #ifndef HAVE_PCAP_LIST_TSTAMP_TYPES
! 593: " (Disabled on this system. Option ignored)"
! 594: #endif
! 595: "\n Type of timestamp to use for incoming packets. Use -vv when\n"
! 596: " pinging to list available ones.\n"
1.1 misho 597: " -q Does not display messages, except error messages.\n"
598: " -r Raw output: only the MAC/IP address is displayed for each reply.\n"
599: " -R Raw output: Like -r but shows \"the other one\", can be combined\n"
600: " with -r.\n"
601: " -s MAC Set source MAC address. You may need to use -p with this.\n"
602: " -S IP Like -b and -0 but with set source address. Note that this may\n"
1.1.1.3 ! misho 603: " get the arping unanswered if the target does not have routing to\n"
1.1 misho 604: " the IP. If you don't own the IP you are using, you may need to\n"
605: " turn on promiscious mode on the interface (with -p). With this\n"
606: " switch you can find out what IP-address a host has without tak-\n"
607: " ing an IP-address yourself.\n"
608: " -t MAC Set target MAC address to use when pinging IP address.\n"
609: " -T IP Use -T as target address when pinging MACs that won't respond to\n"
610: " a broadcast ping but perhaps to a directed broadcast.\n"
611: " Example:\n"
612: " To check the address of MAC-A, use knowledge of MAC-B and IP-B.\n"
613: " $ arping -S <IP-B> -s <MAC-B> -p <MAC-A>\n"
614: " -p Turn on promiscious mode on interface, use this if you don't\n"
615: " \"own\" the MAC address you are using.\n"
1.1.1.3 ! misho 616: " -P Send ARP replies instead of requests. Useful with -U.\n"
1.1 misho 617: " -u Show index=received/sent instead of just index=received when\n"
618: " pinging MACs.\n"
1.1.1.2 misho 619: " -U Send unsolicited ARP.\n"
1.1 misho 620: " -v Verbose output. Use twice for more messages.\n"
1.1.1.3 ! misho 621: " -w Time to wait between pings, in microseconds.\n"
! 622: " -W Same as -w, but in floating point seconds.\n");
1.1.1.2 misho 623: printf("Report bugs to: thomas@habets.se\n"
1.1 misho 624: "Arping home page: <http://www.habets.pp.se/synscan/>\n"
625: "Development repo: http://github.com/ThomasHabets/arping\n");
626: }
627:
628: /**
629: *
630: */
631: static void
632: standard_usage()
633: {
1.1.1.2 misho 634: printf("ARPing %s, by Thomas Habets <thomas@habets.se>\n",
1.1 misho 635: version);
1.1.1.3 ! misho 636: printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w <us> ] "
! 637: "[ -W <sec> ] "
1.1 misho 638: "[ -S <host/ip> ]\n"
639: " "
640: "[ -T <host/ip ] "
641: "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n"
642: " "
1.1.1.3 ! misho 643: "[ -C <count> ] [ -i <interface> ] [ -m <type> ] "
1.1 misho 644: "<host/ip/MAC | -B>\n");
645: }
646:
647: /**
648: *
649: */
650: static void
651: usage(int ret)
652: {
653: standard_usage();
654: if (WIN32) {
655: extended_usage();
656: } else {
657: printf("For complete usage info, use --help"
658: " or check the manpage.\n");
659: }
660: exit(ret);
661: }
662:
663: /**
664: * It was unclear from msdn.microsoft.com if their scanf() supported
665: * [0-9a-fA-F], so I'll stay away from it.
666: */
667: static int is_mac_addr(const char *p)
668: {
669: /* cisco-style */
670: if (3*5-1 == strlen(p)) {
671: unsigned int c;
672: for (c = 0; c < strlen(p); c++) {
673: if ((c % 5) == 4) {
674: if ('.' != p[c]) {
675: goto checkcolon;
676: }
677: } else {
678: if (!isxdigit(p[c])) {
679: goto checkcolon;
680: }
681: }
682: }
683: return 1;
684: }
685: /* windows-style */
686: if (6*3-1 == strlen(p)) {
687: unsigned int c;
688: for (c = 0; c < strlen(p); c++) {
689: if ((c % 3) == 2) {
690: if ('-' != p[c]) {
691: goto checkcolon;
692: }
693: } else {
694: if (!isxdigit(p[c])) {
695: goto checkcolon;
696: }
697: }
698: }
699: return 1;
700: }
701:
702: checkcolon:
703: /* unix */
704: return strchr(p, ':') ? 1 : 0;
705: }
706:
707: /**
708: * lots of parms since C arrays suck
709: */
710: static int get_mac_addr(const char *in,
711: unsigned int *n0,
712: unsigned int *n1,
713: unsigned int *n2,
714: unsigned int *n3,
715: unsigned int *n4,
716: unsigned int *n5)
717: {
718: if (6 == sscanf(in, "%x:%x:%x:%x:%x:%x",n0,n1,n2,n3,n4,n5)) {
719: return 1;
720: } else if(6 == sscanf(in, "%2x%x.%2x%x.%2x%x",n0,n1,n2,n3,n4,n5)) {
721: return 1;
722: } else if(6 == sscanf(in, "%x-%x-%x-%x-%x-%x",n0,n1,n2,n3,n4,n5)) {
723: return 1;
724: }
725: return 0;
726: }
727:
728: /**
729: *
1.1.1.2 misho 730: */
731: static void
732: update_stats(double sample)
733: {
734: if (stats_min_time < 0 || sample < stats_min_time) {
735: stats_min_time = sample;
736: }
737: if (sample > stats_max_time) {
738: stats_max_time = sample;
739: }
740: stats_total_time += sample;
741: stats_total_sq_time += sample * sample;
742: }
743:
744: /**
745: *
746: */
747: static double
748: timespec2dbl(const struct timespec *tv)
749: {
750: return tv->tv_sec + (double)tv->tv_nsec / 1000000000;
751: }
752:
753: /**
754: * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so
1.1 misho 755: *
756: * Still, I'm using at least 128bytes below
757: */
1.1.1.2 misho 758: static char *ts2str(const struct timespec *tv, const struct timespec *tv2,
1.1.1.3 ! misho 759: char *buf, size_t bufsize)
1.1 misho 760: {
761: double f,f2;
762: int exp = 0;
763:
1.1.1.2 misho 764: f = timespec2dbl(tv);
765: f2 = timespec2dbl(tv2);
766: f = (f2 - f) * 1000000000;
1.1 misho 767: while (f > 1000) {
1.1.1.2 misho 768: exp += 3;
1.1 misho 769: f /= 1000;
770: }
771: switch (exp) {
772: case 0:
1.1.1.3 ! misho 773: snprintf(buf, bufsize, "%.3f nsec", f);
1.1 misho 774: break;
775: case 3:
1.1.1.3 ! misho 776: snprintf(buf, bufsize, "%.3f usec", f);
1.1 misho 777: break;
778: case 6:
1.1.1.3 ! misho 779: snprintf(buf, bufsize, "%.3f msec", f);
1.1 misho 780: break;
781: case 9:
1.1.1.3 ! misho 782: snprintf(buf, bufsize, "%.3f sec", f);
1.1.1.2 misho 783: break;
784: case 12:
1.1.1.3 ! misho 785: snprintf(buf, bufsize, "%.3f sec", f*1000);
1.1 misho 786: break;
787: default:
788: /* huh, uh, huhuh */
1.1.1.3 ! misho 789: snprintf(buf, bufsize, "%.3fe%d sec", f, exp-9);
1.1 misho 790: }
791: return buf;
792: }
793:
794:
795:
796: /** Send directed IPv4 ICMP echo request.
797: *
798: * \param id IP id
799: * \param seq Ping seq
800: */
801: static void
1.1.1.2 misho 802: pingmac_send(uint16_t id, uint16_t seq)
1.1 misho 803: {
804: static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0;
805: int c;
806:
807: if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */
808: 0, /* code */
809: 0, /* checksum */
810: id, /* id */
811: seq, /* seq */
812: NULL, /* payload */
813: 0, /* payload len */
814: libnet,
815: icmp))) {
816: fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n",
817: libnet_geterror(libnet));
818: sigint(0);
819: }
820:
821: if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H
822: + LIBNET_ICMPV4_ECHO_H + 0,
823: 0, /* ToS */
824: id, /* id */
825: 0, /* frag */
826: 64, /* ttl */
827: IPPROTO_ICMP,
828: 0, /* checksum */
829: srcip,
830: dstip,
831: NULL, /* payload */
832: 0,
833: libnet,
834: ipv4))) {
835: fprintf(stderr, "libnet_build_ipv4(): %s\n",
836: libnet_geterror(libnet));
837: sigint(0);
838: }
839: if (-1 == (eth = libnet_build_ethernet(dstmac,
840: srcmac,
841: ETHERTYPE_IP,
842: NULL,
843: 0,
844: libnet,
845: eth))) {
846: fprintf(stderr, "libnet_build_ethernet(): %s\n",
847: libnet_geterror(libnet));
848: sigint(0);
849: }
1.1.1.2 misho 850: if (verbose > 1) {
851: getclock(&lastpacketsent);
852: printf("arping: sending packet at time %ld.%09ld\n",
853: (long)lastpacketsent.tv_sec,
854: (long)lastpacketsent.tv_nsec);
1.1 misho 855: }
856: if (-1 == (c = libnet_write(libnet))) {
857: fprintf(stderr, "arping: libnet_write(): %s\n",
858: libnet_geterror(libnet));
859: sigint(0);
860: }
1.1.1.2 misho 861: getclock(&lastpacketsent);
1.1 misho 862: numsent++;
863: }
864:
865: /** Send ARP who-has.
866: *
867: */
868: static void
1.1.1.2 misho 869: pingip_send()
1.1 misho 870: {
871: static libnet_ptag_t arp=0,eth=0;
872: if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER,
873: ETHERTYPE_IP,
874: ETH_ALEN,
875: IP_ALEN,
1.1.1.3 ! misho 876: send_reply ? ARPOP_REPLY : ARPOP_REQUEST,
1.1 misho 877: srcmac,
878: (uint8_t*)&srcip,
1.1.1.2 misho 879: unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull,
1.1 misho 880: (uint8_t*)&dstip,
881: NULL,
882: 0,
883: libnet,
884: arp))) {
885: fprintf(stderr, "arping: libnet_build_arp(): %s\n",
886: libnet_geterror(libnet));
887: sigint(0);
888: }
889: if (-1 == (eth = libnet_build_ethernet(dstmac,
890: srcmac,
891: ETHERTYPE_ARP,
892: NULL,
893: 0,
894: libnet,
895: eth))) {
896: fprintf(stderr, "arping: libnet_build_ethernet(): %s\n",
897: libnet_geterror(libnet));
898: sigint(0);
899: }
1.1.1.2 misho 900: if (verbose > 1) {
901: getclock(&lastpacketsent);
902: printf("arping: sending packet at time %ld.%09ld\n",
903: (long)lastpacketsent.tv_sec,
904: (long)lastpacketsent.tv_nsec);
1.1 misho 905: }
906: if (-1 == libnet_write(libnet)) {
907: fprintf(stderr, "arping: libnet_write(): %s\n",
908: libnet_geterror(libnet));
909: sigint(0);
910: }
1.1.1.2 misho 911: getclock(&lastpacketsent);
1.1 misho 912: numsent++;
913: }
914:
915: /** handle incoming packet when pinging an IP address.
916: *
917: * \param h packet metadata
918: * \param packet packet data
919: */
920: static void
1.1.1.2 misho 921: pingip_recv(const char *unused, struct pcap_pkthdr *h, uint8_t *packet)
1.1 misho 922: {
923: struct libnet_802_3_hdr *heth;
924: struct libnet_arp_hdr *harp;
1.1.1.2 misho 925: struct timespec arrival;
1.1 misho 926: int c;
927:
1.1.1.2 misho 928: if (verbose > 2) {
1.1.1.3 ! misho 929: printf("arping: received response for IP ping\n");
1.1 misho 930: }
931:
1.1.1.2 misho 932: getclock(&arrival);
933:
1.1 misho 934: heth = (void*)packet;
935: harp = (void*)((char*)heth + LIBNET_ETH_H);
936:
1.1.1.3 ! misho 937: // ARP reply.
! 938: if (htons(harp->ar_op) != ARPOP_REPLY) {
! 939: return;
! 940: }
! 941: if (verbose > 3) {
! 942: printf("arping: ... packet is ARP reply\n");
! 943: }
1.1 misho 944:
1.1.1.3 ! misho 945: // From IPv4 address reply.
! 946: if (htons(harp->ar_pro) != ETHERTYPE_IP) {
! 947: return;
! 948: }
! 949: if (verbose > 3) {
! 950: printf("arping: ... from IPv4 address\n");
! 951: }
1.1 misho 952:
1.1.1.3 ! misho 953: // To Ethernet address.
! 954: if (htons(harp->ar_hrd) != ARPHRD_ETHER) {
! 955: return;
! 956: }
! 957: if (verbose > 3) {
! 958: printf("arping: ... to Ethernet address\n");
! 959: }
! 960:
! 961: // Must be sent from target address.
! 962: // Should very likely only be used if using -T.
! 963: if (addr_must_be_same) {
! 964: if (memcmp((u_char*)harp + sizeof(struct libnet_arp_hdr),
! 965: dstmac, ETH_ALEN)) {
! 966: return;
! 967: }
! 968: }
! 969: if (verbose > 3) {
! 970: printf("arping: ... sent by acceptable host\n");
! 971: }
! 972:
! 973: // Actually the IPv4 address we asked for.
! 974: uint32_t ip;
! 975: memcpy(&ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4);
! 976: if (dstip != ip) {
! 977: return;
! 978: }
! 979: if (verbose > 3) {
! 980: printf("arping: ... for the right IPv4 address!\n");
! 981: }
! 982:
! 983: update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));
! 984: char buf[128];
! 985: if (beep) {
! 986: printf("\a");
! 987: }
! 988: switch(display) {
! 989: case DOT:
! 990: putchar('!');
! 991: break;
! 992: case NORMAL:
! 993: printf("%d bytes from %s (%s): index=%d",
! 994: h->len, format_mac(heth->_802_3_shost,
! 995: buf, sizeof(buf)),
! 996: libnet_addr2name4(ip, 0), numrecvd);
! 997:
! 998: if (alsototal) {
! 999: printf("/%u", numsent-1);
! 1000: }
! 1001: printf(" time=%s", ts2str(&lastpacketsent, &arrival, buf,
! 1002: sizeof(buf)));
! 1003: break;
! 1004: case QUIET:
! 1005: break;
! 1006: case RAWRAW:
! 1007: printf("%s %s", format_mac(heth->_802_3_shost,
! 1008: buf, sizeof(buf)),
! 1009: libnet_addr2name4(ip, 0));
! 1010: break;
! 1011: case RRAW:
! 1012: printf("%s", libnet_addr2name4(ip, 0));
! 1013: break;
! 1014: case RAW:
! 1015: printf("%s", format_mac(heth->_802_3_shost,
! 1016: buf, sizeof(buf)));
! 1017: break;
! 1018: default:
! 1019: fprintf(stderr, "arping: can't happen!\n");
! 1020: }
! 1021: fflush(stdout);
! 1022:
! 1023: switch (display) {
! 1024: case QUIET:
! 1025: case DOT:
! 1026: break;
! 1027: default:
! 1028: printf("\n");
! 1029: }
! 1030: if (numrecvd) {
! 1031: if (memcmp(lastreplymac,
! 1032: heth->_802_3_shost, ETH_ALEN)) {
! 1033: dupfound = 1;
! 1034: }
! 1035: }
! 1036: memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN);
! 1037:
! 1038: numrecvd++;
! 1039: if (numrecvd >= max_replies) {
! 1040: sigint(0);
! 1041: }
1.1 misho 1042: }
1043:
1044: /** handle incoming packet when pinging an MAC address.
1045: *
1046: * \param h packet metadata
1047: * \param packet packet data
1048: */
1049: static void
1.1.1.2 misho 1050: pingmac_recv(const char *unused, struct pcap_pkthdr *h, uint8_t *packet)
1.1 misho 1051: {
1052: struct libnet_802_3_hdr *heth;
1053: struct libnet_ipv4_hdr *hip;
1054: struct libnet_icmpv4_hdr *hicmp;
1.1.1.2 misho 1055: struct timespec arrival;
1.1 misho 1056: int c;
1057:
1058: if(verbose>2) {
1059: printf("arping: received response for mac ping\n");
1060: }
1061:
1.1.1.2 misho 1062: getclock(&arrival);
1.1 misho 1063:
1064: heth = (void*)packet;
1065: hip = (void*)((char*)heth + LIBNET_ETH_H);
1066: hicmp = (void*)((char*)hip + LIBNET_IPV4_H);
1067:
1.1.1.3 ! misho 1068: // Dest MAC must be me.
! 1069: if (memcmp(heth->_802_3_dhost, srcmac, ETH_ALEN)) {
! 1070: return;
! 1071: }
! 1072:
! 1073: // Source MAC must match, if set.
! 1074: if (memcmp(dstmac, ethxmas, ETH_ALEN)) {
! 1075: if (memcmp(heth->_802_3_shost, dstmac, ETH_ALEN)) {
! 1076: return;
! 1077: }
! 1078: }
! 1079:
! 1080: // IPv4 Address must be me (maybe).
! 1081: if (addr_must_be_same) {
! 1082: uint32_t tmp;
! 1083: memcpy(&tmp, &hip->ip_src, 4);
! 1084: if (dstip != tmp) {
! 1085: return;
! 1086: }
! 1087: }
! 1088:
! 1089: // Must be ICMP echo reply.
! 1090: if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) {
! 1091: return;
! 1092: }
! 1093:
! 1094: update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent));
! 1095: if (beep) {
! 1096: printf("\a");
! 1097: }
! 1098: char buf[128];
! 1099: char buf2[128];
! 1100: switch(display) {
! 1101: case QUIET:
! 1102: break;
! 1103: case DOT:
! 1104: putchar('!');
! 1105: break;
! 1106: case NORMAL:
! 1107: printf("%d bytes from %s (%s): icmp_seq=%d time=%s", h->len,
! 1108: libnet_addr2name4(*(int*)&hip->ip_src, 0),
! 1109: format_mac(heth->_802_3_shost, buf, sizeof(buf)),
! 1110: htons(hicmp->icmp_seq),
! 1111: ts2str(&lastpacketsent, &arrival, buf2, sizeof(buf2)));
! 1112: break;
! 1113: case RAW:
! 1114: printf("%s", libnet_addr2name4(hip->ip_src.s_addr, 0));
! 1115: break;
! 1116: case RRAW:
! 1117: printf("%s", format_mac(heth->_802_3_shost, buf, sizeof(buf)));
! 1118: break;
! 1119: case RAWRAW:
! 1120: printf("%s %s",
! 1121: format_mac(heth->_802_3_shost, buf, sizeof(buf)),
! 1122: libnet_addr2name4(hip->ip_src.s_addr, 0));
! 1123: break;
! 1124: default:
! 1125: fprintf(stderr, "arping: can't-happen-bug\n");
! 1126: sigint(0);
! 1127: }
! 1128: fflush(stdout);
! 1129: switch (display) {
! 1130: case QUIET:
! 1131: case DOT:
! 1132: break;
! 1133: default:
! 1134: printf("\n");
! 1135: }
! 1136: numrecvd++;
! 1137: if (numrecvd >= max_replies) {
! 1138: sigint(0);
! 1139: }
1.1 misho 1140: }
1141:
1142: /**
1.1.1.2 misho 1143: * while negative nanoseconds, take from whole seconds.
1.1 misho 1144: * help function for measuring deltas.
1145: */
1146: static void
1.1.1.2 misho 1147: fixup_timespec(struct timespec *tv)
1.1 misho 1148: {
1.1.1.2 misho 1149: while (tv->tv_nsec < 0) {
1.1 misho 1150: tv->tv_sec--;
1.1.1.2 misho 1151: tv->tv_nsec += 1000000000;
1.1 misho 1152: }
1153: }
1154:
1155: /**
1.1.1.2 misho 1156: * try to receive a packet for 'packetwait' microseconds
1.1 misho 1157: */
1158: static void
1.1.1.2 misho 1159: ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_handler func)
1.1 misho 1160: {
1.1.1.2 misho 1161: struct timespec ts;
1162: struct timespec endtime;
1.1 misho 1163: char done = 0;
1.1.1.2 misho 1164: int fd;
1165: int old_received;
1.1 misho 1166:
1.1.1.2 misho 1167: if (verbose > 3) {
1168: printf("arping: receiving packets...\n");
1169: }
1.1 misho 1170:
1.1.1.2 misho 1171: getclock(&ts);
1172: endtime.tv_sec = ts.tv_sec + (packetwait / 1000000);
1173: endtime.tv_nsec = ts.tv_nsec + 1000 * (packetwait % 1000000);
1174: fixup_timespec(&endtime);
1.1 misho 1175:
1176: fd = pcap_get_selectable_fd(pcap);
1.1.1.2 misho 1177: old_received = numrecvd;
1.1 misho 1178:
1179: for (;!done;) {
1180: int trydispatch = 0;
1181:
1.1.1.2 misho 1182: getclock(&ts);
1183: ts.tv_sec = endtime.tv_sec - ts.tv_sec;
1184: ts.tv_nsec = endtime.tv_nsec - ts.tv_nsec;
1185: fixup_timespec(&ts);
1186: if (verbose > 2) {
1.1.1.3 ! misho 1187: printf("arping: listen for replies for %ld.%09ld sec\n",
1.1.1.2 misho 1188: (long)ts.tv_sec, (long)ts.tv_nsec);
1189: }
1190:
1191: /* if time has passed, do one last check and then we're done.
1192: * this also triggers if not using monotonic clock and time
1193: * is set forwards */
1194: if (ts.tv_sec < 0) {
1195: ts.tv_sec = 0;
1196: ts.tv_nsec = 1;
1.1 misho 1197: done = 1;
1198: }
1.1.1.2 misho 1199:
1200: /* if wait-for-packet time is longer than full period,
1201: * we're obviously not using a monotonic clock and the system
1202: * time has been changed.
1203: * we don't know how far we're into the waiting, so just end
1204: * it here */
1205: if ((ts.tv_sec > packetwait / 1000000)
1206: || ((ts.tv_sec == packetwait / 1000000)
1207: && (ts.tv_nsec/1000 > packetwait % 1000000))) {
1208: ts.tv_sec = 0;
1209: ts.tv_nsec = 1;
1210: done = 1;
1211: }
1212:
1213: /* check for sigint */
1.1 misho 1214: if (time_to_die) {
1215: return;
1216: }
1217:
1218: /* try to wait for data */
1219: {
1.1.1.2 misho 1220: fd_set fds;
1.1 misho 1221: int r;
1.1.1.2 misho 1222: struct timeval tv;
1223: tv.tv_sec = ts.tv_sec;
1224: tv.tv_usec = ts.tv_nsec / 1000;
1225:
1226: FD_ZERO(&fds);
1227: FD_SET(fd, &fds);
1.1 misho 1228:
1.1.1.2 misho 1229: r = select(fd + 1, &fds, NULL, NULL, &tv);
1.1 misho 1230: switch (r) {
1231: case 0: /* timeout */
1.1.1.3 ! misho 1232: if (numrecvd == old_received) {
! 1233: if (reverse_beep) {
! 1234: printf("\a");
! 1235: }
! 1236: switch (display) {
! 1237: case NORMAL:
1.1.1.2 misho 1238: printf("Timeout\n");
1.1.1.3 ! misho 1239: break;
! 1240: case DOT:
! 1241: printf(".");
! 1242: break;
1.1.1.2 misho 1243: }
1.1.1.3 ! misho 1244: fflush(stdout);
1.1.1.2 misho 1245: }
1.1 misho 1246: done = 1;
1247: break;
1248: case -1: /* error */
1249: if (errno != EINTR) {
1250: done = 1;
1251: sigint(0);
1252: fprintf(stderr,
1.1.1.2 misho 1253: "arping: select() failed: %s\n",
1.1 misho 1254: strerror(errno));
1255: }
1256: break;
1257: default: /* data returned */
1258: trydispatch = 1;
1259: break;
1260: }
1261: }
1262:
1263: if (trydispatch) {
1264: int ret;
1.1.1.2 misho 1265: if (0 > (ret = pcap_dispatch(pcap, -1,
1266: func,
1267: NULL))) {
1.1 misho 1268: /* rest, so we don't take 100% CPU... mostly
1269: hmm... does usleep() exist everywhere? */
1270: usleep(1);
1271:
1272: /* weird is normal on bsd :) */
1273: if (verbose > 3) {
1274: fprintf(stderr,
1.1.1.2 misho 1275: "arping: select says ok, but "
1.1 misho 1276: "pcap_dispatch=%d!\n",
1277: ret);
1278: }
1279: }
1280: }
1281: }
1282: }
1283:
1284: /**
1285: *
1286: */
1287: int main(int argc, char **argv)
1288: {
1289: char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE];
1290: char *cp;
1291: int promisc = 0;
1292: int srcip_given = 0;
1293: int srcmac_given = 0;
1294: int dstip_given = 0;
1295: const char *ifname = NULL;
1296: char *parm;
1297: int c;
1298: unsigned int maxcount = -1;
1299: int dont_use_arping_lookupdev=0;
1300: struct bpf_program bp;
1301: pcap_t *pcap;
1.1.1.2 misho 1302: enum { NONE, PINGMAC, PINGIP } mode = NONE;
1.1 misho 1303: unsigned int packetwait = 1000000;
1.1.1.3 ! misho 1304: ebuf[0] = 0;
1.1 misho 1305:
1306: for (c = 1; c < argc; c++) {
1307: if (!strcmp(argv[c], "--help")) {
1308: standard_usage();
1309: extended_usage();
1310: exit(0);
1311: }
1312: }
1313:
1314: srcip = 0;
1315: dstip = 0xffffffff;
1.1.1.2 misho 1316: memcpy(dstmac, ethxmas, ETH_ALEN);
1.1 misho 1317:
1.1.1.3 ! misho 1318: while (EOF != (c = getopt(argc, argv,
! 1319: "0aAbBC:c:dDeFhi:I:m:pPqrRs:S:t:T:uUvw:W:"))) {
1.1 misho 1320: switch(c) {
1321: case '0':
1322: srcip = 0;
1323: srcip_given = 1;
1324: break;
1325: case 'a':
1326: beep = 1;
1327: break;
1328: case 'A':
1329: addr_must_be_same = 1;
1330: break;
1331: case 'b':
1332: srcip = 0xffffffff;
1333: srcip_given = 1;
1334: break;
1335: case 'B':
1336: dstip = 0xffffffff;
1337: dstip_given = 1;
1338: break;
1339: case 'c':
1340: maxcount = atoi(optarg);
1341: break;
1.1.1.3 ! misho 1342: case 'C':
! 1343: max_replies = atoi(optarg);
! 1344: break;
1.1 misho 1345: case 'd':
1346: finddup = 1;
1347: break;
1348: case 'D':
1349: display = DOT;
1350: break;
1351: case 'e':
1352: reverse_beep = 1;
1353: break;
1354: case 'F':
1355: dont_use_arping_lookupdev=1;
1356: break;
1357: case 'h':
1358: usage(0);
1359: case 'i':
1360: if (strchr(optarg, ':')) {
1361: fprintf(stderr, "arping: If you're trying to "
1362: "feed me an interface alias then you "
1363: "don't really\nknow what this programs"
1364: " does, do you?\nUse -I if you really"
1365: " mean it (undocumented on "
1366: "purpose)\n");
1367: exit(1);
1368: }
1369: case 'I': /* FALL THROUGH */
1370: ifname = optarg;
1371: break;
1.1.1.3 ! misho 1372: case 'm':
! 1373: timestamp_type = optarg;
! 1374: break;
1.1 misho 1375: case 'p':
1376: promisc = 1;
1377: break;
1.1.1.3 ! misho 1378: case 'P':
! 1379: send_reply = 1;
! 1380: break;
1.1 misho 1381: case 'q':
1382: display = QUIET;
1383: break;
1384: case 'r':
1385: display = (display==RRAW)?RAWRAW:RAW;
1386: break;
1387: case 'R':
1388: display = (display==RAW)?RAWRAW:RRAW;
1389: break;
1390: case 's': { /* spoof source MAC */
1391: unsigned int n[6];
1392: if (!get_mac_addr(optarg,
1393: &n[0],&n[1],&n[2],
1394: &n[3],&n[4],&n[5])){
1395: fprintf(stderr, "arping: Weird MAC addr %s\n",
1396: optarg);
1397: exit(1);
1398: }
1399: for (c = 0; c < 6; c++) {
1400: srcmac[c] = n[c] & 0xff;
1401: }
1402: srcmac_given = 1;
1403: break;
1404: }
1405: case 'S': /* set source IP, may be null for don't-know */
1.1.1.3 ! misho 1406: do_libnet_init(ifname, 0);
1.1 misho 1407: if (-1 == (srcip = libnet_name2addr4(libnet,
1408: optarg,
1409: LIBNET_RESOLVE))){
1410: fprintf(stderr, "arping: Can't resolve %s, or "
1411: "%s is broadcast. If it is, use -b"
1412: " instead of -S\n", optarg,optarg);
1413: exit(1);
1414: }
1415: srcip_given = 1;
1416: break;
1417: case 't': { /* set taget mac */
1418: unsigned int n[6];
1419: if (mode == PINGMAC) {
1420: fprintf(stderr, "arping: -t can only be used "
1421: "in IP ping mode\n");
1422: exit(1);
1423: }
1424: if (!get_mac_addr(optarg,
1425: &n[0],&n[1],&n[2],
1426: &n[3],&n[4],&n[5])){
1427: fprintf(stderr, "Illegal MAC addr %s\n",
1428: optarg);
1429: exit(1);
1430: }
1431: for (c = 0; c < 6; c++) {
1432: dstmac[c] = n[c] & 0xff;
1433: }
1434: mode = PINGIP;
1435: break;
1436: }
1437: case 'T': /* set destination IP */
1438: if (mode == PINGIP) {
1439: fprintf(stderr, "arping: -T can only be used "
1440: "in MAC ping mode\n");
1441: exit(1);
1442: }
1.1.1.3 ! misho 1443: do_libnet_init(ifname, 0);
1.1 misho 1444: if (-1 == (dstip = libnet_name2addr4(libnet,
1445: optarg,
1446: LIBNET_RESOLVE))){
1447: fprintf(stderr,"arping: Can't resolve %s, or "
1448: "%s is broadcast. If it is, use -B "
1449: "instead of -T\n",optarg,optarg);
1450: exit(1);
1451: }
1452: mode = PINGMAC;
1453: break;
1454: case 'u':
1455: alsototal = 1;
1456: break;
1.1.1.2 misho 1457: case 'U':
1458: if (mode == PINGMAC) {
1459: fprintf(stderr, "arping: -U can only be used "
1460: "in IP ping mode\n");
1461: exit(1);
1462: }
1463: unsolicited = 1;
1464: break;
1.1 misho 1465: case 'v':
1466: verbose++;
1467: break;
1468: case 'w':
1469: packetwait = (unsigned)atoi(optarg);
1470: break;
1.1.1.3 ! misho 1471: case 'W':
! 1472: packetwait = (unsigned)(1000000.0 * atof(optarg));
! 1473: break;
1.1 misho 1474: default:
1475: usage(1);
1476: }
1477: }
1478:
1.1.1.2 misho 1479: if (verbose > 1) {
1480: #if HAVE_CLOCK_MONOTONIC
1481: struct timespec ts;
1482: clock_getres(CLOCK_MONOTONIC, &ts);
1.1.1.3 ! misho 1483: printf("arping: clock_getres() = %ld %ld\n",
1.1.1.2 misho 1484: (long)ts.tv_sec, (long)ts.tv_nsec);
1485: #else
1.1.1.3 ! misho 1486: printf("arping: Using gettimeofday() for time measurements\n");
1.1.1.2 misho 1487: #endif
1488: }
1489:
1.1 misho 1490: if (display == DOT) {
1491: setvbuf(stdout, NULL, _IONBF, 0);
1492: }
1493:
1494: if (finddup && maxcount == -1) {
1495: maxcount = 3;
1496: }
1497:
1498: parm = (optind < argc) ? argv[optind] : NULL;
1499:
1500: /* default to own IP address when doing -d */
1501: if (finddup && !parm) {
1502: dstip_given = 1;
1.1.1.3 ! misho 1503: do_libnet_init(ifname, 0);
1.1 misho 1504: dstip = libnet_get_ipaddr4(libnet);
1505: if (verbose) {
1506: printf("defaulting to checking dup for %s\n",
1507: libnet_addr2name4(dstip, 0));
1508: }
1509: }
1510:
1511: /*
1512: * Handle dstip_given instead of ip address after parms (-B really)
1513: */
1514: if (mode == NONE) {
1515: if (optind + 1 == argc) {
1516: mode = is_mac_addr(parm)?PINGMAC:PINGIP;
1517: } else if (dstip_given) {
1518: mode = PINGIP;
1.1.1.3 ! misho 1519: do_libnet_init(ifname, 0);
1.1 misho 1520: parm = strdup(libnet_addr2name4(dstip,0));
1521: if (!parm) {
1522: fprintf(stderr, "arping: out of mem\n");
1523: exit(1);
1524: }
1525: }
1526: }
1527:
1528: if (!parm) {
1529: usage(1);
1530: }
1531:
1532: /*
1533: *
1534: */
1535: if (mode == NONE) {
1536: usage(1);
1537: }
1538:
1539: /*
1540: * libnet init (may be done already for resolving)
1541: */
1.1.1.3 ! misho 1542: do_libnet_init(ifname, 0);
! 1543:
1.1 misho 1544: /*
1545: * Make sure dstip and parm like eachother
1546: */
1547: if (mode == PINGIP && (!dstip_given)) {
1548: if (is_mac_addr(parm)) {
1549: fprintf(stderr, "arping: Options given only apply to "
1550: "IP ping, but MAC address given as argument"
1551: "\n");
1552: exit(1);
1553: }
1554: if (-1 == (dstip = libnet_name2addr4(libnet,
1555: parm,
1556: LIBNET_RESOLVE))) {
1557: fprintf(stderr, "arping: Can't resolve %s\n", parm);
1558: exit(1);
1559: }
1560: parm = strdup(libnet_addr2name4(dstip,0));
1561: }
1562:
1563: /*
1564: * parse parm into dstmac
1565: */
1566: if (mode == PINGMAC) {
1567: unsigned int n[6];
1568: if (optind + 1 != argc) {
1569: usage(1);
1570: }
1571: if (!is_mac_addr(parm)) {
1572: fprintf(stderr, "arping: Options given only apply to "
1573: "MAC ping, but no MAC address given as "
1574: "argument\n");
1575: exit(1);
1576: }
1577: if (!get_mac_addr(argv[optind],
1578: &n[0],&n[1],&n[2],
1579: &n[3],&n[4],&n[5])) {
1580: fprintf(stderr, "arping: Illegal mac addr %s\n",
1581: argv[optind]);
1582: return 1;
1583: }
1584: for (c = 0; c < 6; c++) {
1585: dstmac[c] = n[c] & 0xff;
1586: }
1587: }
1588:
1589: target = parm;
1590: /*
1591: * Argument processing done, parameters considered sane below
1592: */
1593:
1594: /*
1595: * Get some good iface.
1596: */
1597: if (!ifname) {
1.1.1.2 misho 1598: if (!dont_use_arping_lookupdev) {
1599: ifname = arping_lookupdev(srcip, dstip, ebuf);
1.1.1.3 ! misho 1600: strip_newline(ebuf);
! 1601: if (!ifname) {
! 1602: fprintf(stderr, "arping: lookup dev: %s\n",
! 1603: ebuf);
! 1604: }
1.1.1.2 misho 1605: }
1606: if (!ifname) {
1607: ifname = arping_lookupdev_default(srcip, dstip, ebuf);
1.1.1.3 ! misho 1608: strip_newline(ebuf);
! 1609: if (ifname && !dont_use_arping_lookupdev) {
1.1.1.2 misho 1610: fprintf(stderr,
1611: "arping: Unable to automatically find "
1612: "interface to use. Is it on the local "
1613: "LAN?\n"
1614: "arping: Use -i to manually "
1615: "specify interface. "
1616: "Guessing interface %s.\n", ifname);
1617: }
1.1 misho 1618: }
1619: if (!ifname) {
1.1.1.2 misho 1620: fprintf(stderr, "arping: Gave up looking for interface"
1621: " to use: %s\n", ebuf);
1.1 misho 1622: exit(1);
1623: }
1624: /* FIXME: check for other probably-not interfaces */
1625: if (!strcmp(ifname, "ipsec")
1626: || !strcmp(ifname,"lo")) {
1627: fprintf(stderr, "arping: Um.. %s looks like the wrong "
1628: "interface to use. Is it? "
1629: "(-i switch)\n", ifname);
1630: fprintf(stderr, "arping: using it anyway this time\n");
1631: }
1632: }
1633:
1634: /*
1635: * Init libnet again, because we now know the interface name.
1636: * We should know it by know at least
1637: */
1.1.1.3 ! misho 1638: do_libnet_init(ifname, 0);
1.1 misho 1639:
1640: /*
1641: * pcap init
1642: */
1.1.1.2 misho 1643: if (!(pcap = do_pcap_open_live(ifname, 100, promisc, 10, ebuf))) {
1.1.1.3 ! misho 1644: strip_newline(ebuf);
! 1645: fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf);
1.1 misho 1646: exit(1);
1647: }
1.1.1.3 ! misho 1648: drop_privileges();
1.1 misho 1649: if (pcap_setnonblock(pcap, 1, ebuf)) {
1.1.1.3 ! misho 1650: strip_newline(ebuf);
1.1 misho 1651: fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf);
1652: exit(1);
1653: }
1.1.1.2 misho 1654: if (verbose > 1) {
1.1.1.3 ! misho 1655: printf("arping: pcap_get_selectable_fd(): %d\n",
1.1 misho 1656: pcap_get_selectable_fd(pcap));
1657: }
1658:
1.1.1.2 misho 1659: #ifdef BIOCIMMEDIATE
1.1 misho 1660: {
1.1.1.3 ! misho 1661: // This may be redundant if pcap_set_immediate_mode() is present.
1.1 misho 1662: uint32_t on = 1;
1663: if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE,
1664: &on))) {
1665: fprintf(stderr, "arping: ioctl(fd,BIOCIMMEDIATE, 1) "
1666: "failed, continuing anyway, YMMV: %s\n",
1667: strerror(errno));
1668: }
1669: }
1670: #endif
1671:
1672: if (mode == PINGIP) {
1673: /* FIXME: better filter with addresses? */
1674: if (-1 == pcap_compile(pcap, &bp, "arp", 0,-1)) {
1675: fprintf(stderr, "arping: pcap_compile(): error\n");
1676: exit(1);
1677: }
1678: } else { /* ping mac */
1679: /* FIXME: better filter with addresses? */
1680: if (-1 == pcap_compile(pcap, &bp, "icmp", 0,-1)) {
1681: fprintf(stderr, "arping: pcap_compile(): error\n");
1682: exit(1);
1683: }
1684: }
1685: if (-1 == pcap_setfilter(pcap, &bp)) {
1686: fprintf(stderr, "arping: pcap_setfilter(): error\n");
1687: exit(1);
1688: }
1689:
1690: /*
1691: * final init
1692: */
1693: if (!srcmac_given) {
1694: if (!(cp = (char*)libnet_get_hwaddr(libnet))) {
1695: fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n",
1696: libnet_geterror(libnet));
1697: exit(1);
1698: }
1699: memcpy(srcmac, cp, ETH_ALEN);
1700: }
1701: if (!srcip_given) {
1702: if (-1 == (srcip = libnet_get_ipaddr4(libnet))) {
1.1.1.2 misho 1703: fprintf(stderr,
1704: "arping: Unable to get the IPv4 address of "
1705: "interface %s:\narping: %s"
1706: "arping: "
1707: "Use -S to specify address manually.\n",
1708: ifname, libnet_geterror(libnet));
1.1 misho 1709: exit(1);
1710: }
1711: }
1.1.1.2 misho 1712: do_signal_init();
1.1 misho 1713:
1714: if (verbose) {
1.1.1.3 ! misho 1715: char buf[128];
! 1716: printf("This box: Interface: %s IP: %s MAC address: %s\n",
! 1717: ifname,
! 1718: libnet_addr2name4(libnet_get_ipaddr4(libnet), 0),
! 1719: format_mac(srcmac, buf, sizeof(buf)));
1.1 misho 1720: }
1721:
1722:
1723: if (display == NORMAL) {
1724: printf("ARPING %s\n", parm);
1725: }
1726:
1727: /*
1728: * let's roll
1729: */
1730: if (mode == PINGIP) {
1731: unsigned int c;
1732: unsigned int r;
1733: for (c = 0; c < maxcount && !time_to_die; c++) {
1.1.1.2 misho 1734: pingip_send();
1.1 misho 1735: r = numrecvd;
1736: ping_recv(pcap,packetwait,
1737: (pcap_handler)pingip_recv);
1738: }
1739: } else { /* PINGMAC */
1740: unsigned int c;
1741: unsigned int r;
1742: for (c = 0; c < maxcount && !time_to_die; c++) {
1.1.1.2 misho 1743: pingmac_send(rand(), c);
1.1 misho 1744: r = numrecvd;
1745: ping_recv(pcap,packetwait,
1746: (pcap_handler)pingmac_recv);
1747: }
1748: }
1749: if (display == DOT) {
1.1.1.3 ! misho 1750: const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent;
! 1751: printf("\t%3.0f%% packet loss (%d extra)\n",
! 1752: (succ < 0.0) ? 0.0 : succ,
! 1753: (succ < 0.0) ? (numrecvd - numsent) : 0);
1.1 misho 1754: } else if (display == NORMAL) {
1.1.1.3 ! misho 1755: const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent;
1.1 misho 1756: printf("\n--- %s statistics ---\n"
1757: "%d packets transmitted, "
1758: "%d packets received, "
1759: "%3.0f%% "
1760: "unanswered (%d extra)\n",
1761: target,numsent,numrecvd,
1762: (succ < 0.0) ? 0.0 : succ,
1.1.1.2 misho 1763: (succ < 0.0) ? (numrecvd - numsent): 0);
1764: if (numrecvd) {
1765: double avg = stats_total_time / numrecvd;
1766: printf("rtt min/avg/max/std-dev = "
1767: "%.3f/%.3f/%.3f/%.3f ms",
1768: 1000*stats_min_time,
1769: 1000*avg,
1770: 1000*stats_max_time,
1771: 1000*sqrt(stats_total_sq_time/numrecvd
1772: -avg*avg));
1773: }
1774: printf("\n");
1.1 misho 1775: }
1776:
1777: if (finddup) {
1778: return dupfound;
1779: } else {
1780: return !numrecvd;
1781: }
1782: }
1.1.1.2 misho 1783: /* ---- Emacs Variables ----
1784: * Local Variables:
1785: * c-basic-offset: 8
1786: * indent-tabs-mode: nil
1787: * End:
1788: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>