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>