|
version 1.1, 2012/02/21 22:16:27
|
version 1.1.1.4, 2021/03/16 23:40:57
|
|
Line 2
|
Line 2
|
| * |
* |
| * arping |
* arping |
| * |
* |
| * By Thomas Habets <thomas@habets.pp.se> | * By Thomas Habets <thomas@habets.se> |
| * |
* |
| * ARP 'ping' utility |
* ARP 'ping' utility |
| * |
* |
|
Line 11
|
Line 11
|
| * you don't yet have routing to. Then again, if you have no idea what I'm |
* you don't yet have routing to. Then again, if you have no idea what I'm |
| * talking about then you prolly don't need it. |
* talking about then you prolly don't need it. |
| * |
* |
| * Also finds out IP of specified MAC | * Also finds out IP of specified MAC. |
| * |
* |
| */ |
*/ |
| /* |
/* |
| * Copyright (C) 2000-2010 Thomas Habets <thomas@habets.pp.se> | * Copyright (C) 2000-2019 Thomas Habets <thomas@habets.se> |
| * |
* |
| * This library is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or modify |
| * modify it under the terms of the GNU General Public | * it under the terms of the GNU General Public License as published by |
| * License as published by the Free Software Foundation; either | * the Free Software Foundation; either version 2 of the License, or |
| * version 2 of the License, or (at your option) any later version. | * (at your option) any later version. |
| * |
* |
| * This library is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * General Public License for more details. | * GNU General Public License for more details. |
| * |
* |
| * You should have received a copy of the GNU General Public License along |
* You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
* with this program; if not, write to the Free Software Foundation, Inc., |
|
Line 37
|
Line 37
|
| |
|
| #include <stdio.h> |
#include <stdio.h> |
| #include <stdlib.h> |
#include <stdlib.h> |
| |
#include <limits.h> |
| |
#include <math.h> |
| #include <poll.h> |
#include <poll.h> |
| |
|
| #if HAVE_UNISTD_H |
#if HAVE_UNISTD_H |
| #include <unistd.h> |
#include <unistd.h> |
| #endif |
#endif |
| |
|
| |
#if HAVE_GETOPT_H |
| |
#include <getopt.h> |
| |
#endif |
| |
|
| #if HAVE_STDINT_H |
#if HAVE_STDINT_H |
| #include <stdint.h> |
#include <stdint.h> |
| #endif |
#endif |
|
Line 51
|
Line 57
|
| #include <inttypes.h> |
#include <inttypes.h> |
| #endif |
#endif |
| |
|
| |
#if HAVE_TIME_H |
| |
#include <time.h> |
| |
#endif |
| |
|
| #if HAVE_SYS_TIME_H |
#if HAVE_SYS_TIME_H |
| #include <sys/time.h> |
#include <sys/time.h> |
| #endif |
#endif |
|
Line 59
|
Line 69
|
| #include <sys/types.h> |
#include <sys/types.h> |
| #endif |
#endif |
| |
|
| |
#if HAVE_SYS_PARAM_H |
| |
#include <sys/param.h> |
| |
#endif |
| |
|
| |
#if HAVE_SYS_RANDOM_H |
| |
#include <sys/random.h> |
| |
#endif |
| |
|
| |
#if HAVE_GRP_H |
| |
#include <grp.h> |
| |
#endif |
| |
|
| #if HAVE_SYS_SOCKET_H |
#if HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
#include <sys/socket.h> |
| #endif |
#endif |
|
Line 78
|
Line 100
|
| #if HAVE_WIN32_LIBNET_H |
#if HAVE_WIN32_LIBNET_H |
| #include <win32/libnet.h> |
#include <win32/libnet.h> |
| #endif |
#endif |
| #include <pcap.h> |
|
| |
|
| |
#if HAVE_PWD_H |
| |
#include <pwd.h> |
| |
#endif |
| |
|
| |
#if HAVE_SYS_CAPABILITY_H |
| |
#include <sys/capability.h> |
| |
#endif |
| |
|
| #if HAVE_NET_BPF_H |
#if HAVE_NET_BPF_H |
| #include <net/bpf.h> |
#include <net/bpf.h> |
| #endif |
#endif |
| |
#include <pcap.h> |
| |
|
| |
#include "arping.h" |
| |
|
| #ifndef ETH_ALEN |
#ifndef ETH_ALEN |
| #define ETH_ALEN 6 |
#define ETH_ALEN 6 |
| #endif |
#endif |
|
Line 96
|
Line 128
|
| #define WIN32 0 |
#define WIN32 0 |
| #endif |
#endif |
| |
|
| |
#ifndef CLOCK_MONOTONIC |
| |
#define CLOCK_MONOTONIC CLOCK_REALTIME |
| |
#endif |
| |
|
| |
#define UNUSED(x) (void)(x) |
| |
|
| /** |
/** |
| * OS-specific interface finding using routing table. See findif_*.c |
* OS-specific interface finding using routing table. See findif_*.c |
| |
* ebuf must be called with a size of at least |
| |
* max(LIBNET_ERRBUF_SIZE, PCAP_ERRBUF_SIZE). |
| */ |
*/ |
| const char * |
const char * |
| arping_lookupdev(const char *ifname, | arping_lookupdev(uint32_t srcip, uint32_t dstip, char *ebuf); |
| uint32_t srcip, | |
| uint32_t dstip, | |
| char *ebuf); | |
| |
|
| |
const char * |
| |
arping_lookupdev_default(uint32_t srcip, uint32_t dstip, char *ebuf); |
| |
|
| static const char *version = VERSION; /* from autoconf */ |
static const char *version = VERSION; /* from autoconf */ |
| |
|
| static libnet_t *libnet = 0; | libnet_t *libnet = 0; |
| |
|
| static struct timeval lastpacketsent; | /* Timestamp of last packet sent. |
| | * Used for timing, and assumes that reply is due to most recent sent query. |
| | */ |
| | static struct timespec lastpacketsent; |
| |
|
| uint32_t srcip, dstip; | /* target string */ |
| | static const char *target = "huh? bug in arping?"; |
| |
|
| static int beep = 0; | /* |
| static int reverse_beep = 0; | * Ping IP mode: cmdline target |
| static int verbose = 0; | * Ping MAC mode: 255.255.255.255, override with -T |
| static int alsototal = 0; | */ |
| /*static int pingmac = 0; */ | uint32_t dstip; |
| static int finddup = 0; | |
| static int dupfound = 0; | /* |
| static unsigned int numsent = 0; | * Ping IP mode: ethxmas, override with -t |
| static unsigned int numrecvd = 0; | * Ping MAC mode: cmdline target |
| static unsigned int numdots = 0; | */ |
| static int addr_must_be_same = 0; | static uint8_t dstmac[ETH_ALEN]; |
| | |
| | static char* payload_suffix = NULL; |
| | static ssize_t payload_suffix_size = -1; |
| | |
| | uint32_t srcip; /* autodetected, override with -S/-b/-0 */ |
| | uint8_t srcmac[ETH_ALEN]; /* autodetected, override with -s */ |
| | |
| | static int16_t vlan_tag = -1; /* 802.1Q tag to add to packets. -V */ |
| | static int16_t vlan_prio = -1; /* 802.1p prio to use with 802.1Q. -Q */ |
| | |
| | static int beep = 0; /* beep when reply is received. -a */ |
| | static int reverse_beep = 0; /* beep when expected reply absent. -e */ |
| | static int alsototal = 0; /* print sent as well as received. -u */ |
| | static int addr_must_be_same = 0; /* -A */ |
| | static int unsolicited = 0; /* -U */ |
| | static int send_reply = 0; /* Send reply instead of request. -P */ |
| | static int promisc = 0; /* Use promisc mode. -p */ |
| | |
| | static int finddup = 0; /* finddup mode. -d */ |
| | static int dupfound = 0; /* set to 1 if dup found */ |
| | static char lastreplymac[ETH_ALEN]; /* if last different from this then dup */ |
| | |
| | unsigned int numsent = 0; /* packets sent */ |
| | unsigned int numrecvd = 0; /* packets received */ |
| | static unsigned int max_replies = UINT_MAX; /* exit after -C replies */ |
| | static const char* timestamp_type = NULL; /* Incoming packet measurement ts type (-m) */ |
| | |
| | static double stats_min_time = -1; |
| | static double stats_max_time = -1; |
| | static double stats_total_time = 0; |
| | static double stats_total_sq_time = 0; |
| | |
| /* RAWRAW is RAW|RRAW */ |
/* RAWRAW is RAW|RRAW */ |
| static enum { NORMAL, QUIET, RAW, RRAW, RAWRAW, DOT } display = NORMAL; | static enum { NORMAL, /* normal output */ |
| static char *target = "huh? bug in arping?"; | QUIET, /* No output. -q */ |
| static uint8_t ethnull[ETH_ALEN]; | RAW, /* Print MAC when pinging IP. -r */ |
| static uint8_t ethxmas[ETH_ALEN]; | RRAW, /* Print IP when pinging IP. -R */ |
| static char srcmac[ETH_ALEN]; | RAWRAW, /* Print both. -r and -R */ |
| static char dstmac[ETH_ALEN]; | DOT /* Print '.' and '!', Cisco-style. -D */ |
| static char lastreplymac[ETH_ALEN]; | } display = NORMAL; |
| |
|
| /* doesn't need to be volatile */ | static const uint8_t ethnull[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; |
| volatile int time_to_die = 0; | static const uint8_t ethxmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| | static const char* ip_broadcast = "255.255.255.255"; |
| | static const uint32_t ip_broadcast_num = (uint32_t)-1; |
| |
|
| |
int verbose = 0; /* Increase with -v */ |
| |
|
| |
/* Doesn't really need to be volatile, but doesn't hurt. */ |
| |
static volatile sig_atomic_t time_to_die = 0; |
| |
|
| |
static ssize_t |
| |
xgetrandom(void *buf, const size_t buflen, const unsigned int flags) |
| |
{ |
| |
#ifdef HAVE_GETRANDOM |
| |
return getrandom(buf, buflen, flags); |
| |
#else |
| |
char* p = buf; |
| |
for (int n = 0; n < buflen; n++) { |
| |
p[n] = random() & 0xff; |
| |
} |
| |
return buflen; |
| |
#endif |
| |
} |
| |
|
| |
static long int |
| |
xrandom() { |
| |
const int maxtry = 10; |
| |
for (int c = 0; c < maxtry; c++) { |
| |
long int ret; |
| |
const ssize_t rc = xgetrandom(&ret, sizeof(ret), 0); |
| |
if (rc == -1) { |
| |
fprintf(stderr, "arping: failed to get random bytes: %s\n", strerror(errno)); |
| |
continue; |
| |
} |
| |
if (sizeof(ret) != rc) { |
| |
fprintf(stderr, "arping: got too few random bytes %zd, want %zd\n", rc, sizeof(ret)); |
| |
continue; |
| |
} |
| |
return ret; |
| |
} |
| |
fprintf(stderr, "arping: failed to get random bytes after %d tries\n", maxtry); |
| |
exit(1); |
| |
} |
| |
|
| /** |
/** |
| |
* If possible, chroot. |
| * |
* |
| |
* The sshd user is used for privilege separation in OpenSSH. |
| |
* Let's assume it's installed and chroot() to there. |
| */ |
*/ |
| static void |
static void |
| count_missing_dots() | drop_fs_root() |
| { |
{ |
| while (numsent > numdots) { | const char* chroot_user = "sshd"; |
| putchar('.'); | struct passwd *pw; |
| numdots++; | errno = 0; |
| | if (!(pw = getpwnam(chroot_user))) { |
| | if (verbose) { |
| | printf("arping: getpwnam(%s): %s\n", |
| | chroot_user, strerror(errno)); |
| | } |
| | return; |
| } |
} |
| |
if (chdir(pw->pw_dir)) { |
| |
if (verbose) { |
| |
printf("arping: chdir(%s): %s\n", |
| |
pw->pw_dir, strerror(errno)); |
| |
} |
| |
return; |
| |
} |
| |
if (chroot(pw->pw_dir)) { |
| |
if (verbose) { |
| |
printf("arping: chroot(%s): %s\n", |
| |
pw->pw_dir, strerror(errno)); |
| |
} |
| |
return; |
| |
} |
| |
if (chdir("/")) { |
| |
if (verbose) { |
| |
printf("arping: chdir(/): %s\n", strerror(errno)); |
| |
} |
| |
return; |
| |
} |
| |
if (verbose > 1) { |
| |
printf("arping: Successfully chrooted to %s\n", pw->pw_dir); |
| |
} |
| } |
} |
| |
|
| /** |
/** |
| |
* If possible, drop uid to nobody. |
| * |
* |
| */ | * This code only successfully sets all [ug]ids if running as |
| | * root. ARPing is most likely running as root unless using |
| | * capabilities, and those are dropped elsewhere. |
| | */ |
| | static void |
| | drop_uid(uid_t uid, gid_t gid) |
| | { |
| | int fail = 0; |
| | if (setgroups(0, NULL)) { |
| | if (verbose) { |
| | printf("arping: setgroups(0, NULL): %s\n", strerror(errno)); |
| | } |
| | fail++; |
| | } |
| | if (gid && setgid(gid)) { |
| | if (verbose) { |
| | printf("arping: setgid(): %s\n", strerror(errno)); |
| | } |
| | fail++; |
| | } |
| | if (uid && setuid(uid)) { |
| | if (verbose) { |
| | printf("arping: setuid(): %s\n", strerror(errno)); |
| | } |
| | fail++; |
| | } |
| | if (!fail && verbose > 1) { |
| | printf("arping: Successfully dropped uid/gid to %d/%d.\n", |
| | uid, gid); |
| | } |
| | } |
| | |
| | /** |
| | * Drop any and all capabilities. |
| | */ |
| | static void |
| | drop_capabilities() |
| | { |
| | #if HAVE_CAP_INIT |
| | cap_t no_cap; |
| | if (!(no_cap = cap_init())) { |
| | if (verbose) { |
| | printf("arping: cap_init(): %s\n", strerror(errno)); |
| | } |
| | return; |
| | } |
| | if (cap_set_proc(no_cap)) { |
| | if (verbose) { |
| | printf("arping: cap_set_proc(): %s\n", strerror(errno)); |
| | } |
| | } |
| | if (verbose > 1) { |
| | printf("arping: Successfully dropped all capabilities.\n"); |
| | } |
| | cap_free(no_cap); |
| | #endif |
| | } |
| | |
| | /** |
| | * Get GID of input (handle both name and number) or die. |
| | */ |
| | static gid_t |
| | must_get_group(const char* ident) |
| | { |
| | // Special case empty string, because strtol. |
| | int saved_errno = 0; |
| | if (*ident) { |
| | // First try it as a name. |
| | { |
| | struct group* gr; |
| | errno = 0; |
| | if ((gr = getgrnam(ident))) { |
| | return gr->gr_gid; |
| | } |
| | saved_errno = errno; |
| | } |
| | |
| | // Not a name. Try it as an integer. |
| | { |
| | char* endp = NULL; |
| | gid_t r = strtol(ident, &endp, 0); |
| | if (!*endp) { |
| | return r; |
| | } |
| | } |
| | } |
| | |
| | if (saved_errno != 0) { |
| | fprintf(stderr, |
| | "arping: %s not a number and getgrnam(%s): %s\n", |
| | ident, ident, strerror(saved_errno)); |
| | } else { |
| | // If group was supplied, then not |
| | // existing is fatal error too. |
| | fprintf(stderr, |
| | "arping: %s is not a number or group\n", |
| | ident); |
| | } |
| | exit(1); |
| | } |
| | |
| | /** |
| | * drop all privileges. |
| | * |
| | * To be called as early as possible. IOW: immediately after opening |
| | * raw socket. |
| | */ |
| | static void |
| | drop_privileges(const char* drop_group) |
| | { |
| | // Need to get uid/gid of 'nobody' before chroot(). |
| | const char* drop_user = "nobody"; |
| | struct passwd *pw; |
| | errno = 0; |
| | uid_t uid = 0; |
| | gid_t gid = 0; |
| | if (!(pw = getpwnam(drop_user))) { |
| | if (verbose) { |
| | if (errno != 0) { |
| | printf("arping: getpwnam(%s): %s\n", |
| | drop_user, strerror(errno)); |
| | } else { |
| | printf("arping: getpwnam(%s): unknown user\n", |
| | drop_user); |
| | } |
| | } |
| | } else { |
| | uid = pw->pw_uid; |
| | gid = pw->pw_gid; |
| | } |
| | |
| | // If group is supplied, use that gid instead. |
| | if (drop_group != NULL) { |
| | gid = must_get_group(drop_group); |
| | } |
| | drop_fs_root(); |
| | drop_uid(uid, gid); |
| | drop_capabilities(); |
| | #ifdef HAVE_UNVEIL |
| | if (unveil("/", "")) { |
| | fprintf(stderr, |
| | "arping: failed to unveil(/, <>): %s\n", |
| | strerror(errno)); |
| | exit(1); |
| | } |
| | if (unveil(NULL, NULL)) { |
| | fprintf(stderr, "arping: failed to unveil(NULL, NULL): %s\n", |
| | strerror(errno)); |
| | exit(1); |
| | } |
| | if (verbose > 1) { |
| | printf("arping: Successfully unveiled\n"); |
| | } |
| | #endif |
| | } |
| | |
| | /** |
| | * drop even more privileges, where possible. |
| | * |
| | * After all setup is done and main loop is about to start. |
| | */ |
| | static void |
| | drop_more_privileges() |
| | { |
| | #ifdef HAVE_PLEDGE |
| | if (pledge("stdio tty", "")) { |
| | fprintf(stderr, "arping: failed to pledge(stdio, <empty>): %s\n", |
| | strerror(errno)); |
| | exit(1); |
| | } else if (verbose > 1) { |
| | printf("arping: Successfully pledged\n"); |
| | } |
| | #endif |
| | } |
| | |
| | |
| | /** |
| | * Do pcap_open_live(), except by using the pcap_create() interface |
| | * introduced in 2008 (libpcap 0.4) where available. |
| | * This is so that we can set some options, which can't be set with |
| | * pcap_open_live: |
| | * 1) Immediate mode -- this prevents pcap from buffering. |
| | * 2) Set timestamp type -- specify what type of timer to use. |
| | * |
| | * FIXME: Use pcap_set_buffer_size()? |
| | */ |
| | static pcap_t* |
| | try_pcap_open_live(const char *device, int snaplen, |
| | int promisc, int to_ms, char *errbuf) |
| | { |
| | #ifdef HAVE_PCAP_CREATE |
| | pcap_t* pcap; |
| | int rc; |
| | |
| | if (!(pcap = pcap_create(device, errbuf))) { |
| | goto err; |
| | } |
| | if ((rc = pcap_set_snaplen(pcap, snaplen))) { |
| | snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_snaplen(): %s", pcap_statustostr(rc)); |
| | goto err; |
| | } |
| | if ((rc = pcap_set_promisc(pcap, promisc))) { |
| | snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_promisc(): %s", pcap_statustostr(rc)); |
| | goto err; |
| | } |
| | if ((rc = pcap_set_timeout(pcap, to_ms))) { |
| | snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_set_timeout(): %s", pcap_statustostr(rc)); |
| | goto err; |
| | } |
| | |
| | #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE |
| | // Without immediate mode some architectures (e.g. Linux with |
| | // TPACKET_V3) will buffer replies and incorrectly report upwards of |
| | // hundreds of milliseconds of delay. |
| | if ((rc = pcap_set_immediate_mode(pcap, 1))) { |
| | if (verbose) { |
| | fprintf(stderr, "arping: pcap_set_immediate_mode() failed: %s\n", pcap_statustostr(rc)); |
| | } |
| | } |
| | #endif |
| | #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES |
| | if (timestamp_type) { |
| | int err; |
| | int v = pcap_tstamp_type_name_to_val(timestamp_type); |
| | if (v == PCAP_ERROR) { |
| | fprintf(stderr, "arping: Unknown timestamp type \"%s\"\n", timestamp_type); |
| | exit(1); |
| | } |
| | if ((err = pcap_set_tstamp_type(pcap, v))) { |
| | fprintf(stderr, |
| | "arping: Failed to set timestamp type \"%s\" (%d): %s\n", |
| | timestamp_type, v, pcap_statustostr(err)); |
| | } |
| | } |
| | #endif |
| | if ((rc = pcap_activate(pcap))) { |
| | if (timestamp_type) { |
| | snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(tstype=\"%s\"): %s. Try without setting timestamp type.", timestamp_type, pcap_statustostr(rc)); |
| | } else { |
| | snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap_activate(): %s", pcap_statustostr(rc)); |
| | } |
| | goto err; |
| | } |
| | #ifdef HAVE_PCAP_LIST_TSTAMP_TYPES |
| | // List timestamp types after activating, since we don't want to list |
| | // them if activating failed. |
| | if (verbose > 1) { |
| | int *ts; |
| | int count; |
| | count = pcap_list_tstamp_types(pcap, &ts); |
| | if (count == PCAP_ERROR) { |
| | fprintf(stderr, "arping: pcap_list_tstamp_types() failed\n"); |
| | } else { |
| | int c; |
| | const char* fmt = " %-18s %s\n"; |
| | fprintf(stderr, "Timestamp types:\n"); |
| | fprintf(stderr, fmt, "Name", "Description"); |
| | for (c = 0; c < count; c++) { |
| | fprintf(stderr, fmt, pcap_tstamp_type_val_to_name(ts[c]), |
| | pcap_tstamp_type_val_to_description(ts[c])); |
| | } |
| | pcap_free_tstamp_types(ts); |
| | } |
| | } |
| | #endif |
| | return pcap; |
| | err: |
| | if (pcap) { |
| | pcap_close(pcap); |
| | } |
| | return NULL; |
| | #else |
| | return pcap_open_live(device, snaplen, promisc, to_ms, errbuf); |
| | #endif |
| | } |
| | |
| | /** |
| | * Some stupid OSs (Solaris) think it's a good idea to put network |
| | * devices in /dev and then play musical chairs with them. |
| | * |
| | * Since libpcap doesn't seem to have a workaround for that, here's arpings |
| | * workaround. |
| | * |
| | * E.g. if the network interface is called net0, pcap will fail because it |
| | * fails to open /dev/net, because it's a directory. |
| | */ |
| | static pcap_t* |
| | do_pcap_open_live(const char *device, int snaplen, |
| | int promisc, int to_ms, char *errbuf) |
| | { |
| | pcap_t* ret; |
| | char buf[PATH_MAX]; |
| | |
| | if ((ret = try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf))) { |
| | return ret; |
| | } |
| | |
| | snprintf(buf, sizeof(buf), "/dev/%s", device); |
| | if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { |
| | return ret; |
| | } |
| | |
| | snprintf(buf, sizeof(buf), "/dev/net/%s", device); |
| | if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { |
| | return ret; |
| | } |
| | |
| | /* Call original again to reset the error message. */ |
| | return try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf); |
| | } |
| | |
| | /** |
| | * Some Libnet error messages end with a newline. Strip that in place. |
| | */ |
| void |
void |
| do_libnet_init(const char *ifname) | strip_newline(char* s) { |
| | size_t n; |
| | for (n = strlen(s); n && (s[n - 1] == '\n'); --n) { |
| | s[n - 1] = 0; |
| | } |
| | } |
| | |
| | /** |
| | * Init libnet with specified ifname. Destroy if already inited. |
| | * If this function retries with different parameter it will preserve |
| | * the original error message and print that. |
| | * Call with recursive=0. |
| | */ |
| | void |
| | do_libnet_init(const char *ifname, int recursive) |
| { |
{ |
| char ebuf[LIBNET_ERRBUF_SIZE]; |
char ebuf[LIBNET_ERRBUF_SIZE]; |
| |
ebuf[0] = 0; |
| if (verbose > 1) { |
if (verbose > 1) { |
| printf("libnet_init(%s)\n", ifname?ifname:"<null>"); | printf("arping: libnet_init(%s)\n", ifname ? ifname : "<null>"); |
| } |
} |
| if (libnet) { |
if (libnet) { |
| /* prolly going to switch interface from temp to real */ | /* Probably going to switch interface from temp to real. */ |
| libnet_destroy(libnet); |
libnet_destroy(libnet); |
| libnet = 0; |
libnet = 0; |
| } |
} |
| if (getuid() && geteuid()) { |
|
| fprintf(stderr, "arping: must run as root\n"); |
|
| exit(1); |
|
| } |
|
| |
|
| |
/* Try libnet_init() even though we aren't root. We may have |
| |
* a capability or something. */ |
| if (!(libnet = libnet_init(LIBNET_LINK, |
if (!(libnet = libnet_init(LIBNET_LINK, |
| (char*)ifname, |
(char*)ifname, |
| ebuf))) { |
ebuf))) { |
| fprintf(stderr, "arping: libnet_init(): %s\n", ebuf); | strip_newline(ebuf); |
| | if (!ifname) { |
| | /* Sometimes libnet guesses an interface that it then |
| | * can't use. Work around that by attempting to |
| | * use "lo". */ |
| | do_libnet_init("lo", 1); |
| | if (libnet != NULL) { |
| | return; |
| | } |
| | } else if (recursive) { |
| | /* Continue original execution to get that |
| | * error message. */ |
| | return; |
| | } |
| | fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n", |
| | ifname ? ifname : "<null>", |
| | *ebuf ? ebuf : "<no error message>"); |
| | if (getuid() && geteuid()) { |
| | fprintf(stderr, |
| | "arping: you may need to run as root\n"); |
| | } |
| exit(1); |
exit(1); |
| } |
} |
| } |
} |
|
Line 179 do_libnet_init(const char *ifname)
|
Line 675 do_libnet_init(const char *ifname)
|
| /** |
/** |
| * |
* |
| */ |
*/ |
| const char * | void |
| arping_lookupdev_default(const char *ifname, | sigint(int i) |
| uint32_t srcip, uint32_t dstip, | |
| char *ebuf) | |
| { |
{ |
| #if WIN32 | UNUSED(i); |
| WCHAR buf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE]; | time_to_die = 1; |
| WCHAR* ret = (WCHAR*)pcap_lookupdev((char*)buf); | |
| if (ret != NULL) { | |
| wcstombs(ebuf, ret, LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE); | |
| return ebuf; | |
| } | |
| return NULL; | |
| #else | |
| return pcap_lookupdev(ebuf); | |
| #endif | |
| } |
} |
| |
|
| #if WIN32 | /** |
| static BOOL WINAPI arping_console_ctrl_handler(DWORD dwCtrlType) | * idiot-proof clock_gettime() wrapper |
| | */ |
| | static void |
| | getclock(struct timespec *ts) |
| { |
{ |
| if(verbose) { | #if HAVE_CLOCK_MONOTONIC |
| printf("arping_console_ctrl_handler( %d )\n", (int)dwCtrlType); | static int clock_gettime_failed = 0; |
| } | if (!clock_gettime_failed) { |
| time_to_die = 1; | if (0 == clock_gettime(CLOCK_MONOTONIC, ts)) { |
| return; |
| #if 0 | } |
| /* if SetConsoleCtrlHandler() does what I think, this isn't needed */ | fprintf(stderr, "arping: clock_gettime(): %s\n", |
| if (display == NORMAL) { | strerror(errno)); |
| printf("\n--- %s statistics ---\n" | clock_gettime_failed = 1; // Prevent duplicate error messages. |
| "%d packets transmitted, %d packets received, %3.0f%% " | |
| "unanswered\n",target,numsent,numrecvd, | |
| 100.0 - 100.0 * (float)(numrecvd)/(float)numsent); | |
| } |
} |
| #endif |
#endif |
| return TRUE; | struct timeval tv; |
| | if (-1 == gettimeofday(&tv, NULL)) { |
| | fprintf(stderr, "arping: gettimeofday(): %s\n", |
| | strerror(errno)); |
| | sigint(0); |
| | } |
| | ts->tv_sec = tv.tv_sec; |
| | ts->tv_nsec = tv.tv_usec * 1000; |
| } |
} |
| #endif |
|
| |
|
| |
|
| /** |
/** |
| * |
* |
| */ |
*/ |
| static void sigint(int i) | static char* |
| { | format_mac(const unsigned char* mac, char* buf, size_t bufsize) { |
| time_to_die = 1; | snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", |
| | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| | return buf; |
| } |
} |
| |
|
| /** |
/** |
|
Line 249 extended_usage()
|
Line 741 extended_usage()
|
| " -B Use instead of host if you want to address 255.255.255.255.\n" |
" -B Use instead of host if you want to address 255.255.255.255.\n" |
| " -c count\n" |
" -c count\n" |
| " Only send count requests.\n" |
" Only send count requests.\n" |
| |
" -C count\n" |
| |
" Only wait for this many replies, regardless of -c and -w.\n" |
| " -d Find duplicate replies. Exit with 1 if there are " |
" -d Find duplicate replies. Exit with 1 if there are " |
| "answers from\n" |
"answers from\n" |
| " two different MAC addresses.\n" |
" two different MAC addresses.\n" |
| " -D Display answers as dots and missing packets as exclamation points.\n" | " -D Display answers as exclamation points and missing packets as dots.\n" |
| " -e Like -a but beep when there is no reply.\n" |
" -e Like -a but beep when there is no reply.\n" |
| " -F Don't try to be smart about the interface name. (even if this\n" |
" -F Don't try to be smart about the interface name. (even if this\n" |
| " switch is not given, -i overrides smartness)\n" |
" switch is not given, -i overrides smartness)\n" |
| |
" -g group\n" |
| |
" setgid() to this group instead of the nobody group.\n" |
| " -h Displays a help message and exits.\n" |
" -h Displays a help message and exits.\n" |
| " -i interface\n" |
" -i interface\n" |
| " Use the specified interface.\n" |
" Use the specified interface.\n" |
| |
" -m type" |
| |
#ifndef HAVE_PCAP_LIST_TSTAMP_TYPES |
| |
" (Disabled on this system. Option ignored)" |
| |
#endif |
| |
"\n Type of timestamp to use for incoming packets. Use -vv when\n" |
| |
" pinging to list available ones.\n" |
| " -q Does not display messages, except error messages.\n" |
" -q Does not display messages, except error messages.\n" |
| |
" -Q pri 802.1p priority to set. Should be used with 802.1Q (-V).\n" |
| |
" Defaults to 0.\n" |
| " -r Raw output: only the MAC/IP address is displayed for each reply.\n" |
" -r Raw output: only the MAC/IP address is displayed for each reply.\n" |
| " -R Raw output: Like -r but shows \"the other one\", can be combined\n" |
" -R Raw output: Like -r but shows \"the other one\", can be combined\n" |
| " with -r.\n" |
" with -r.\n" |
| " -s MAC Set source MAC address. You may need to use -p with this.\n" |
" -s MAC Set source MAC address. You may need to use -p with this.\n" |
| " -S IP Like -b and -0 but with set source address. Note that this may\n" |
" -S IP Like -b and -0 but with set source address. Note that this may\n" |
| " get the arping unanswered if the target does not have routing to\n" | " get the arping unanswered if the target does not have routing to\n" |
| " the IP. If you don't own the IP you are using, you may need to\n" |
" the IP. If you don't own the IP you are using, you may need to\n" |
| " turn on promiscious mode on the interface (with -p). With this\n" |
" turn on promiscious mode on the interface (with -p). With this\n" |
| " switch you can find out what IP-address a host has without tak-\n" |
" switch you can find out what IP-address a host has without tak-\n" |
|
Line 278 extended_usage()
|
Line 782 extended_usage()
|
| " $ arping -S <IP-B> -s <MAC-B> -p <MAC-A>\n" |
" $ arping -S <IP-B> -s <MAC-B> -p <MAC-A>\n" |
| " -p Turn on promiscious mode on interface, use this if you don't\n" |
" -p Turn on promiscious mode on interface, use this if you don't\n" |
| " \"own\" the MAC address you are using.\n" |
" \"own\" the MAC address you are using.\n" |
| |
" -P Send ARP replies instead of requests. Useful with -U.\n" |
| " -u Show index=received/sent instead of just index=received when\n" |
" -u Show index=received/sent instead of just index=received when\n" |
| " pinging MACs.\n" |
" pinging MACs.\n" |
| |
" -U Send unsolicited ARP.\n" |
| " -v Verbose output. Use twice for more messages.\n" |
" -v Verbose output. Use twice for more messages.\n" |
| " -w Time to wait between pings, in microseconds.\n"); | " -V num 802.1Q tag to add. Defaults to no VLAN tag.\n" |
| printf("Report bugs to: thomas@habets.pp.se\n" | " -w sec Specify a timeout before ping exits regardless of how" |
| | " many\npackets have been sent or received.\n" |
| | " -W sec Time to wait between pings.\n"); |
| | printf("Report bugs to: thomas@habets.se\n" |
| "Arping home page: <http://www.habets.pp.se/synscan/>\n" |
"Arping home page: <http://www.habets.pp.se/synscan/>\n" |
| "Development repo: http://github.com/ThomasHabets/arping\n"); |
"Development repo: http://github.com/ThomasHabets/arping\n"); |
| } |
} |
|
Line 293 extended_usage()
|
Line 802 extended_usage()
|
| static void |
static void |
| standard_usage() |
standard_usage() |
| { |
{ |
| printf("ARPing %s, by Thomas Habets <thomas@habets.pp.se>\n", | printf("ARPing %s, by Thomas Habets <thomas@habets.se>\n", |
| version); |
version); |
| printf("usage: arping [ -0aAbdDeFpqrRuv ] [ -w <us> ] " | printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w <sec> ] " |
| | "[ -W <sec> ] " |
| "[ -S <host/ip> ]\n" |
"[ -S <host/ip> ]\n" |
| " " |
" " |
| "[ -T <host/ip ] " |
"[ -T <host/ip ] " |
| "[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n" |
"[ -s <MAC> ] [ -t <MAC> ] [ -c <count> ]\n" |
| " " |
" " |
| "[ -i <interface> ] " | "[ -C <count> ] [ -i <interface> ] [ -m <type> ]" |
| | " [ -g <group> ]\n" |
| | " " |
| | "[ -V <vlan> ] [ -Q <priority> ] " |
| "<host/ip/MAC | -B>\n"); |
"<host/ip/MAC | -B>\n"); |
| } |
} |
| |
|
|
Line 322 usage(int ret)
|
Line 835 usage(int ret)
|
| } |
} |
| |
|
| /** |
/** |
| |
* Check to see if it looks somewhat like a MAC address. |
| |
* |
| * It was unclear from msdn.microsoft.com if their scanf() supported |
* It was unclear from msdn.microsoft.com if their scanf() supported |
| * [0-9a-fA-F], so I'll stay away from it. |
* [0-9a-fA-F], so I'll stay away from it. |
| |
* |
| */ |
*/ |
| static int is_mac_addr(const char *p) |
static int is_mac_addr(const char *p) |
| { |
{ |
|
Line 366 static int is_mac_addr(const char *p)
|
Line 882 static int is_mac_addr(const char *p)
|
| } |
} |
| |
|
| /** |
/** |
| * lots of parms since C arrays suck | * parse mac address. |
| | * |
| | * return 1 on success. |
| */ |
*/ |
| static int get_mac_addr(const char *in, | int |
| unsigned int *n0, | get_mac_addr(const char *in, uint8_t *out) |
| unsigned int *n1, | |
| unsigned int *n2, | |
| unsigned int *n3, | |
| unsigned int *n4, | |
| unsigned int *n5) | |
| { |
{ |
| if (6 == sscanf(in, "%x:%x:%x:%x:%x:%x",n0,n1,n2,n3,n4,n5)) { | const char *formats[] = { |
| return 1; | "%x:%x:%x:%x:%x:%x", |
| } else if(6 == sscanf(in, "%2x%x.%2x%x.%2x%x",n0,n1,n2,n3,n4,n5)) { | "%2x%x.%2x%x.%2x%x", |
| return 1; | "%x-%x-%x-%x-%x-%x", |
| } else if(6 == sscanf(in, "%x-%x-%x-%x-%x-%x",n0,n1,n2,n3,n4,n5)) { | NULL, |
| return 1; | }; |
| } | int c; |
| return 0; | for (c = 0; formats[c]; c++) { |
| | unsigned int n[6]; |
| | if (6 == sscanf(in, formats[c], |
| | &n[0], &n[1], &n[2], &n[3], &n[4], &n[5])) { |
| | for (c = 0; c < 6; c++) { |
| | out[c] = n[c] & 0xff; |
| | } |
| | return 1; |
| | } |
| | } |
| | return 0; |
| } |
} |
| |
|
| /** |
/** |
| * as always, the answer is 42 |
|
| * |
* |
| * in this case the question is how many bytes buf needs to be. | */ |
| * Assuming a 33 byte max %d | static void |
| | update_stats(double sample) |
| | { |
| | if (stats_min_time < 0 || sample < stats_min_time) { |
| | stats_min_time = sample; |
| | } |
| | if (sample > stats_max_time) { |
| | stats_max_time = sample; |
| | } |
| | stats_total_time += sample; |
| | stats_total_sq_time += sample * sample; |
| | } |
| | |
| | /** |
| * |
* |
| * Still, I'm using at least 128bytes below | */ |
| | static double |
| | timespec2dbl(const struct timespec *tv) |
| | { |
| | return tv->tv_sec + (double)tv->tv_nsec / 1000000000; |
| | } |
| | |
| | /** |
| | * return number of microseconds to wait for packets. |
| | */ |
| | static uint32_t |
| | wait_time(double deadline, uint32_t packetwait) |
| | { |
| | struct timespec ts; |
| | |
| | // If deadline not specified, then don't use it. |
| | if (deadline < 0) { |
| | return packetwait; |
| | } |
| | |
| | getclock(&ts); |
| | const double max_wait = deadline - timespec2dbl(&ts); |
| | if (max_wait < 0) { |
| | return 0; |
| | } |
| | if (max_wait > packetwait / 1000000.0) { |
| | return packetwait; |
| | } |
| | return max_wait * 1000000; |
| | } |
| | |
| | /** |
| | * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so |
| * |
* |
| * (because snprintf() sadly isn't as portable, that's why) | * Still, I'm using at least 128bytes below |
| */ |
*/ |
| static char *tv2str(const struct timeval *tv, const struct timeval *tv2, | static char *ts2str(const struct timespec *tv, const struct timespec *tv2, |
| char *buf) | char *buf, size_t bufsize) |
| { |
{ |
| double f,f2; |
double f,f2; |
| int exp = 0; |
int exp = 0; |
| |
|
| f = tv->tv_sec + (double)tv->tv_usec / 1000000; | f = timespec2dbl(tv); |
| f2 = tv2->tv_sec + (double)tv2->tv_usec / 1000000; | f2 = timespec2dbl(tv2); |
| f = (f2 - f) * 1000000; | f = (f2 - f) * 1000000000; |
| while (f > 1000) { |
while (f > 1000) { |
| exp+= 3; | exp += 3; |
| f /= 1000; |
f /= 1000; |
| } |
} |
| switch (exp) { |
switch (exp) { |
| case 0: |
case 0: |
| sprintf(buf, "%.3f usec", f); | snprintf(buf, bufsize, "%.3f nsec", f); |
| break; |
break; |
| case 3: |
case 3: |
| sprintf(buf, "%.3f msec", f); | snprintf(buf, bufsize, "%.3f usec", f); |
| break; |
break; |
| case 6: |
case 6: |
| sprintf(buf, "%.3f sec", f); | snprintf(buf, bufsize, "%.3f msec", f); |
| break; |
break; |
| case 9: |
case 9: |
| sprintf(buf, "%.3f sec", f*1000); | snprintf(buf, bufsize, "%.3f sec", f); |
| break; |
break; |
| |
case 12: |
| |
snprintf(buf, bufsize, "%.3f sec", f*1000); |
| |
break; |
| default: |
default: |
| /* huh, uh, huhuh */ |
/* huh, uh, huhuh */ |
| sprintf(buf, "%.3fe%d sec", f, exp-6); | snprintf(buf, bufsize, "%.3fe%d sec", f, exp-9); |
| } |
} |
| return buf; |
return buf; |
| } |
} |
| |
|
| |
|
| |
|
| /** Send directed IPv4 ICMP echo request. |
/** Send directed IPv4 ICMP echo request. |
| * |
* |
| * \param srcmac Source MAC. From -s switch or autodetected |
|
| * \param dstmac Destination/target MAC. Target command line. |
|
| * \param srcip From -S switch or autodetected |
|
| * \param dstip From -D switch, or 255.255.255.255 |
|
| * \param id IP id |
* \param id IP id |
| * \param seq Ping seq |
* \param seq Ping seq |
| */ |
*/ |
| static void |
static void |
| pingmac_send(uint8_t *srcmac, uint8_t *dstmac, | pingmac_send(uint16_t id, uint16_t seq) |
| uint32_t srcip, uint32_t dstip, | |
| uint16_t id, uint16_t seq) | |
| { |
{ |
| static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0; |
static libnet_ptag_t icmp = 0, ipv4 = 0,eth=0; |
| |
|
| |
// Padding size chosen fairly arbitrarily. |
| |
// Without this padding some systems (e.g. Raspberry Pi 3 |
| |
// wireless interface) failed. dmesg said: |
| |
// arping: packet size is too short (42 <= 50) |
| |
const size_t padding_size = sizeof(struct timespec) + payload_suffix_size; |
| |
uint8_t padding[padding_size]; |
| |
memset(padding, 0, padding_size); |
| |
{ |
| |
struct timespec ts; |
| |
getclock(&ts); |
| |
memcpy(padding, &ts, sizeof(struct timespec)); |
| |
memcpy(&padding[sizeof(struct timespec)], |
| |
payload_suffix, payload_suffix_size); |
| |
} |
| |
|
| int c; |
int c; |
| |
|
| if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */ |
if (-1 == (icmp = libnet_build_icmpv4_echo(ICMP_ECHO, /* type */ |
|
Line 453 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
Line 1031 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
| 0, /* checksum */ |
0, /* checksum */ |
| id, /* id */ |
id, /* id */ |
| seq, /* seq */ |
seq, /* seq */ |
| NULL, /* payload */ | (uint8_t*)padding, /* payload */ |
| 0, /* payload len */ | sizeof padding, /* payload len */ |
| libnet, |
libnet, |
| icmp))) { |
icmp))) { |
| fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n", |
fprintf(stderr, "libnet_build_icmpv4_echo(): %s\n", |
|
Line 463 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
Line 1041 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
| } |
} |
| |
|
| if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H |
if (-1==(ipv4 = libnet_build_ipv4(LIBNET_IPV4_H |
| + LIBNET_ICMPV4_ECHO_H + 0, | + LIBNET_ICMPV4_ECHO_H |
| | + sizeof padding, |
| 0, /* ToS */ |
0, /* ToS */ |
| id, /* id */ |
id, /* id */ |
| 0, /* frag */ |
0, /* frag */ |
|
Line 480 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
Line 1059 pingmac_send(uint8_t *srcmac, uint8_t *dstmac,
|
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
| sigint(0); |
sigint(0); |
| } |
} |
| if (-1 == (eth = libnet_build_ethernet(dstmac, | if (vlan_tag >= 0) { |
| srcmac, | eth = libnet_build_802_1q(dstmac, |
| ETHERTYPE_IP, | srcmac, |
| NULL, | ETHERTYPE_VLAN, |
| 0, | vlan_prio, |
| libnet, | 0, // cfi |
| eth))) { | vlan_tag, |
| fprintf(stderr, "libnet_build_ethernet(): %s\n", | ETHERTYPE_IP, |
| libnet_geterror(libnet)); | NULL, // payload |
| sigint(0); | 0, // payload length |
| | libnet, |
| | eth); |
| | } else { |
| | eth = libnet_build_ethernet(dstmac, |
| | srcmac, |
| | ETHERTYPE_IP, |
| | NULL, // payload |
| | 0, // payload length |
| | libnet, |
| | eth); |
| | } |
| | if (-1 == eth) { |
| | fprintf(stderr, "arping: %s: %s\n", |
| | (vlan_tag >= 0) ? "libnet_build_802_1q()" : |
| | "libnet_build_ethernet()", |
| | libnet_geterror(libnet)); |
| | sigint(0); |
| | } |
| | if (verbose > 1) { |
| | getclock(&lastpacketsent); |
| | printf("arping: sending packet at time %ld.%09ld\n", |
| | (long)lastpacketsent.tv_sec, |
| | (long)lastpacketsent.tv_nsec); |
| } |
} |
| if(verbose>1) { |
|
| if (-1 == gettimeofday(&lastpacketsent, NULL)) { |
|
| fprintf(stderr, "arping: gettimeofday(): %s\n", |
|
| strerror(errno)); |
|
| sigint(0); |
|
| } |
|
| printf("arping: sending packet at time %d %d\n", |
|
| lastpacketsent.tv_sec, |
|
| lastpacketsent.tv_usec); |
|
| } |
|
| if (-1 == (c = libnet_write(libnet))) { |
if (-1 == (c = libnet_write(libnet))) { |
| fprintf(stderr, "arping: libnet_write(): %s\n", |
fprintf(stderr, "arping: libnet_write(): %s\n", |
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
| sigint(0); |
sigint(0); |
| } |
} |
| if (-1 == gettimeofday(&lastpacketsent, NULL)) { | getclock(&lastpacketsent); |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | |
| strerror(errno)); | |
| sigint(0); | |
| } | |
| numsent++; |
numsent++; |
| } |
} |
| |
|
| /** Send ARP who-has. |
/** Send ARP who-has. |
| * |
* |
| * \param srcmac -s or autodetected |
|
| * \param dstmac -t or ff:ff:ff:ff:ff:ff |
|
| * \param srcip -S or autodetected |
|
| * \param dstip -T or or cmdline |
|
| * |
|
| */ |
*/ |
| static void |
static void |
| pingip_send(uint8_t *srcmac, uint8_t *dstmac, | pingip_send() |
| uint32_t srcip, uint32_t dstip) | |
| { |
{ |
| static libnet_ptag_t arp=0,eth=0; |
static libnet_ptag_t arp=0,eth=0; |
| |
|
| |
// Padding size chosen fairly arbitrarily. |
| |
// Without this padding some systems (e.g. Raspberry Pi 3 |
| |
// wireless interface) failed. dmesg said: |
| |
// arping: packet size is too short (42 <= 50) |
| |
const uint8_t padding[16] = {0}; |
| |
|
| if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER, |
if (-1 == (arp = libnet_build_arp(ARPHRD_ETHER, |
| ETHERTYPE_IP, |
ETHERTYPE_IP, |
| ETH_ALEN, |
ETH_ALEN, |
| IP_ALEN, |
IP_ALEN, |
| ARPOP_REQUEST, | send_reply ? ARPOP_REPLY : ARPOP_REQUEST, |
| srcmac, |
srcmac, |
| (uint8_t*)&srcip, |
(uint8_t*)&srcip, |
| ethnull, | unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull, |
| (uint8_t*)&dstip, |
(uint8_t*)&dstip, |
| NULL, | (uint8_t*)padding, |
| 0, | sizeof padding, |
| libnet, |
libnet, |
| arp))) { |
arp))) { |
| fprintf(stderr, "arping: libnet_build_arp(): %s\n", |
fprintf(stderr, "arping: libnet_build_arp(): %s\n", |
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
| sigint(0); |
sigint(0); |
| } |
} |
| if (-1 == (eth = libnet_build_ethernet(dstmac, | |
| srcmac, | if (vlan_tag >= 0) { |
| ETHERTYPE_ARP, | eth = libnet_build_802_1q(dstmac, |
| NULL, | srcmac, |
| 0, | ETHERTYPE_VLAN, |
| libnet, | vlan_prio, |
| eth))) { | 0, // cfi |
| fprintf(stderr, "arping: libnet_build_ethernet(): %s\n", | vlan_tag, |
| | ETHERTYPE_ARP, |
| | NULL, // payload |
| | 0, // payload size |
| | libnet, |
| | eth); |
| | } else { |
| | eth = libnet_build_ethernet(dstmac, |
| | srcmac, |
| | ETHERTYPE_ARP, |
| | NULL, // payload |
| | 0, // payload size |
| | libnet, |
| | eth); |
| | } |
| | if (-1 == eth) { |
| | fprintf(stderr, "arping: %s: %s\n", |
| | (vlan_tag >= 0) ? "libnet_build_802_1q()" : |
| | "libnet_build_ethernet()", |
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
| sigint(0); |
sigint(0); |
| } |
} |
| if(verbose>1) { | if (verbose > 1) { |
| if (-1 == gettimeofday(&lastpacketsent, NULL)) { | getclock(&lastpacketsent); |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | printf("arping: sending packet at time %ld.%09ld\n", |
| strerror(errno)); | (long)lastpacketsent.tv_sec, |
| sigint(0); | (long)lastpacketsent.tv_nsec); |
| } | |
| printf("arping: sending packet at time %d %d\n", | |
| lastpacketsent.tv_sec, | |
| lastpacketsent.tv_usec); | |
| } |
} |
| if (-1 == libnet_write(libnet)) { |
if (-1 == libnet_write(libnet)) { |
| fprintf(stderr, "arping: libnet_write(): %s\n", | fprintf(stderr, "arping: libnet_write(): %s\n", |
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
| sigint(0); |
sigint(0); |
| } |
} |
| if (-1 == gettimeofday(&lastpacketsent, NULL)) { | getclock(&lastpacketsent); |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | |
| strerror(errno)); | |
| sigint(0); | |
| } | |
| numsent++; |
numsent++; |
| } |
} |
| |
|
|
Line 583 pingip_send(uint8_t *srcmac, uint8_t *dstmac,
|
Line 1182 pingip_send(uint8_t *srcmac, uint8_t *dstmac,
|
| * \param h packet metadata |
* \param h packet metadata |
| * \param packet packet data |
* \param packet packet data |
| */ |
*/ |
| static void | void |
| pingip_recv(const char *unused, struct pcap_pkthdr *h, | pingip_recv(const char *unused, struct pcap_pkthdr *h, const char * const packet) |
| uint8_t *packet) | |
| { |
{ |
| |
const unsigned char *pkt_srcmac; |
| |
const struct libnet_802_1q_hdr *veth; |
| struct libnet_802_3_hdr *heth; |
struct libnet_802_3_hdr *heth; |
| struct libnet_arp_hdr *harp; |
struct libnet_arp_hdr *harp; |
| struct timeval arrival; | struct timespec arrival; |
| int c; | UNUSED(unused); |
| |
|
| if(verbose>2) { | if (verbose > 2) { |
| printf("arping: received response for ip ping\n"); | printf("arping: received response for IP ping\n"); |
| } |
} |
| |
|
| if (-1 == gettimeofday(&arrival, NULL)) { | getclock(&arrival); |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | |
| strerror(errno)); | if (vlan_tag >= 0) { |
| sigint(0); | veth = (void*)packet; |
| | harp = (void*)((char*)veth + LIBNET_802_1Q_H); |
| | pkt_srcmac = veth->vlan_shost; |
| | } else { |
| | // Short packet. |
| | if (h->caplen < LIBNET_ETH_H + LIBNET_ARP_H + 2*(ETH_ALEN + 4)) { |
| | return; |
| | } |
| | |
| | heth = (void*)packet; |
| | harp = (void*)((char*)heth + LIBNET_ETH_H); |
| | pkt_srcmac = heth->_802_3_shost; |
| | // Wrong length of hardware address. |
| | if (harp->ar_hln != ETH_ALEN) { |
| | return; |
| | } |
| | |
| | // Wrong length of protocol address. |
| | if (harp->ar_pln != 4) { |
| | return; |
| | } |
| } |
} |
| heth = (void*)packet; |
|
| harp = (void*)((char*)heth + LIBNET_ETH_H); |
|
| |
|
| if ((htons(harp->ar_op) == ARPOP_REPLY) | // ARP reply. |
| && (htons(harp->ar_pro) == ETHERTYPE_IP) | if (htons(harp->ar_op) != ARPOP_REPLY) { |
| && (htons(harp->ar_hrd) == ARPHRD_ETHER)) { | return; |
| uint32_t ip; | } |
| memcpy(&ip, (char*)harp + harp->ar_hln | if (verbose > 3) { |
| + LIBNET_ARP_H,4); | printf("arping: ... packet is ARP reply\n"); |
| if (addr_must_be_same | } |
| && (memcmp((u_char*)harp+sizeof(struct libnet_arp_hdr), | |
| dstmac, ETH_ALEN))) { | |
| return; | |
| } | |
| if (dstip == ip) { | |
| switch(display) { | |
| case DOT: | |
| numdots++; | |
| count_missing_dots(); | |
| putchar('!'); | |
| break; | |
| case NORMAL: { | |
| char buf[128]; | |
| printf("%d bytes from ", h->len); | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%c", heth->_802_3_shost[c], | |
| (c<5)?':':' '); | |
| } | |
| | |
| printf("(%s): index=%d", | |
| libnet_addr2name4(ip,0), | |
| numrecvd); | |
| if (alsototal) { | |
| printf("/%u", numsent-1); | |
| } | |
| printf(" time=%s", | |
| tv2str(&lastpacketsent, | |
| &arrival,buf)); | |
| break; } | |
| case QUIET: | |
| break; | |
| case RAWRAW: | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%c", heth->_802_3_shost[c], | |
| (c<5)?':':' '); | |
| } | |
| printf("%s", libnet_addr2name4(ip,0)); | |
| break; | |
| case RRAW: | |
| printf("%s", libnet_addr2name4(ip,0)); | |
| break; | |
| case RAW: | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%s", heth->_802_3_shost[c], | |
| (c<5)?":":""); | |
| } | |
| break; | |
| default: | |
| fprintf(stderr, "arping: can't happen!\n"); | |
| } | |
| |
|
| switch (display) { | // From IPv4 address reply. |
| case QUIET: | if (htons(harp->ar_pro) != ETHERTYPE_IP) { |
| case DOT: | return; |
| break; | } |
| default: | if (verbose > 3) { |
| if (beep) { | printf("arping: ... from IPv4 address\n"); |
| printf("\a"); | } |
| } | |
| printf("\n"); | // To Ethernet address. |
| | if (htons(harp->ar_hrd) != ARPHRD_ETHER) { |
| | return; |
| | } |
| | if (verbose > 3) { |
| | printf("arping: ... to Ethernet address\n"); |
| | } |
| | |
| | // Must be sent from target address. |
| | // Should very likely only be used if using -T. |
| | if (addr_must_be_same) { |
| | if (memcmp((u_char*)harp + sizeof(struct libnet_arp_hdr), |
| | dstmac, ETH_ALEN)) { |
| | return; |
| | } |
| | } |
| | if (verbose > 3) { |
| | printf("arping: ... sent by acceptable host\n"); |
| | } |
| | |
| | // Special case: If we're not in promisc mode we could still |
| | // get packets where DST mac is not us, if they're *sent* from |
| | // the local host. This is an edge case but in general falls under "is promisc?". |
| | // |
| | // It may cause confusion because `-p` now means not just |
| | // enable promisc mode (disable filter on card / in kernel), |
| | // but also allow packets to any destination (disable filter |
| | // in `arping`). |
| | { |
| | const uint8_t* p = (u_char*)harp |
| | + sizeof(struct libnet_arp_hdr) |
| | + ETH_ALEN |
| | + IP_ALEN; |
| | char buf[128]; |
| | if (!promisc && memcmp(p, srcmac, ETH_ALEN)) { |
| | format_mac(p, buf, sizeof buf); |
| | if (verbose > 3) { |
| | printf("arping: ... but sent from %s\n", buf); |
| } |
} |
| if (numrecvd) { | return; |
| if (memcmp(lastreplymac, | } |
| heth->_802_3_shost, ETH_ALEN)) { | } |
| dupfound = 1; | if (verbose > 3) { |
| } | printf("arping: ... destination is the source we used\n"); |
| } | } |
| memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN); | |
| |
|
| numrecvd++; | |
| } | // Actually the IPv4 address we asked for. |
| } | uint32_t ip; |
| | memcpy(&ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4); |
| | if (dstip != ip) { |
| | return; |
| | } |
| | if (verbose > 3) { |
| | printf("arping: ... for the right IPv4 address!\n"); |
| | } |
| | |
| | update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent)); |
| | char buf[128]; |
| | if (beep) { |
| | printf("\a"); |
| | } |
| | switch(display) { |
| | case DOT: |
| | putchar('!'); |
| | break; |
| | case NORMAL: |
| | printf("%d bytes from %s (%s): index=%d", |
| | h->len, format_mac(pkt_srcmac, buf, sizeof(buf)), |
| | libnet_addr2name4(ip, 0), numrecvd); |
| | |
| | if (alsototal) { |
| | printf("/%u", numsent-1); |
| | } |
| | printf(" time=%s", ts2str(&lastpacketsent, &arrival, buf, |
| | sizeof(buf))); |
| | break; |
| | case QUIET: |
| | break; |
| | case RAWRAW: |
| | printf("%s %s", format_mac(pkt_srcmac, buf, sizeof(buf)), |
| | libnet_addr2name4(ip, 0)); |
| | break; |
| | case RRAW: |
| | printf("%s", libnet_addr2name4(ip, 0)); |
| | break; |
| | case RAW: |
| | printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf))); |
| | break; |
| | default: |
| | fprintf(stderr, "arping: can't happen!\n"); |
| | } |
| | fflush(stdout); |
| | |
| | switch (display) { |
| | case QUIET: |
| | case DOT: |
| | break; |
| | default: |
| | printf("\n"); |
| | } |
| | if (numrecvd) { |
| | if (memcmp(lastreplymac, |
| | pkt_srcmac, ETH_ALEN)) { |
| | dupfound = 1; |
| | } |
| | } |
| | memcpy(lastreplymac, pkt_srcmac, ETH_ALEN); |
| | |
| | numrecvd++; |
| | if (numrecvd >= max_replies) { |
| | sigint(0); |
| | } |
| } |
} |
| |
|
| /** handle incoming packet when pinging an MAC address. |
/** handle incoming packet when pinging an MAC address. |
|
Line 690 pingip_recv(const char *unused, struct pcap_pkthdr *h,
|
Line 1358 pingip_recv(const char *unused, struct pcap_pkthdr *h,
|
| * \param h packet metadata |
* \param h packet metadata |
| * \param packet packet data |
* \param packet packet data |
| */ |
*/ |
| static void | void |
| pingmac_recv(const char *unused, struct pcap_pkthdr *h, | pingmac_recv(const char* unused, struct pcap_pkthdr *h, uint8_t *packet) |
| uint8_t *packet) | |
| { |
{ |
| |
const unsigned char *pkt_dstmac; |
| |
const unsigned char *pkt_srcmac; |
| |
const struct libnet_802_1q_hdr *veth; |
| struct libnet_802_3_hdr *heth; |
struct libnet_802_3_hdr *heth; |
| struct libnet_ipv4_hdr *hip; |
struct libnet_ipv4_hdr *hip; |
| struct libnet_icmpv4_hdr *hicmp; |
struct libnet_icmpv4_hdr *hicmp; |
| struct timeval arrival; | struct timespec arrival; |
| int c; | UNUSED(unused); |
| |
|
| if(verbose>2) { |
if(verbose>2) { |
| printf("arping: received response for mac ping\n"); |
printf("arping: received response for mac ping\n"); |
| } |
} |
| |
|
| if (-1 == gettimeofday(&arrival, NULL)) { | getclock(&arrival); |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | |
| strerror(errno)); | |
| sigint(0); | |
| } | |
| |
|
| heth = (void*)packet; | if (vlan_tag >= 0) { |
| hip = (void*)((char*)heth + LIBNET_ETH_H); | veth = (void*)packet; |
| hicmp = (void*)((char*)hip + LIBNET_IPV4_H); | hip = (void*)((char*)veth + LIBNET_802_1Q_H); |
| | hicmp = (void*)((char*)hip + LIBNET_IPV4_H); |
| | pkt_srcmac = veth->vlan_shost; |
| | pkt_dstmac = veth->vlan_dhost; |
| | } else { |
| | heth = (void*)packet; |
| | hip = (void*)((char*)heth + LIBNET_ETH_H); |
| | hicmp = (void*)((char*)hip + LIBNET_IPV4_H); |
| | pkt_srcmac = heth->_802_3_shost; |
| | pkt_dstmac = heth->_802_3_dhost; |
| | } |
| |
|
| if ((htons(hicmp->icmp_type) == ICMP_ECHOREPLY) | // Dest MAC must be me. |
| && ((!memcmp(heth->_802_3_shost, dstmac,ETH_ALEN) | if (memcmp(pkt_dstmac, srcmac, ETH_ALEN)) { |
| || !memcmp(dstmac, ethxmas, ETH_ALEN))) | return; |
| && !memcmp(heth->_802_3_dhost, srcmac, ETH_ALEN)) { | } |
| if (addr_must_be_same) { | |
| uint32_t tmp; | |
| memcpy(&tmp, &hip->ip_src, 4); | |
| if (dstip != tmp) { | |
| return; | |
| } | |
| } | |
| switch(display) { | |
| case QUIET: | |
| break; | |
| case NORMAL: { | |
| char buf[128]; | |
| printf("%d bytes from %s (",h->len, | |
| libnet_addr2name4(*(int*)&hip->ip_src, 0)); | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%c", heth->_802_3_shost[c], | |
| (c<5)?':':')'); | |
| } | |
| printf(": icmp_seq=%d time=%s", | |
| htons(hicmp->icmp_seq),tv2str(&lastpacketsent, | |
| &arrival,buf)); | |
| break; } | |
| case RAW: | |
| printf("%s", | |
| libnet_addr2name4(hip->ip_src.s_addr, 0)); | |
| break; | |
| case RRAW: | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%s", heth->_802_3_shost[c], | |
| (c<5)?":":""); | |
| } | |
| break; | |
| case RAWRAW: | |
| for (c = 0; c < 6; c++) { | |
| printf("%.2x%c", heth->_802_3_shost[c], | |
| (c<5)?':':' '); | |
| } | |
| printf("%s", | |
| libnet_addr2name4(hip->ip_src.s_addr, 0)); | |
| break; | |
| default: | |
| fprintf(stderr, "arping: can't-happen-bug\n"); | |
| sigint(0); | |
| } | |
| if (display != QUIET) { | |
| printf(beep?"\a\n":"\n"); | |
| } | |
| numrecvd++; | |
| } | |
| } | |
| |
|
| |
if (verbose > 3) { |
| |
printf("arping: ... right dst mac\n"); |
| |
} |
| |
|
| #if WIN32 | // Source MAC must match, if set. |
| /** | if (memcmp(dstmac, ethxmas, ETH_ALEN)) { |
| * untested for a long time. Maybe since arping 2.05 or so. | if (memcmp(pkt_srcmac, dstmac, ETH_ALEN)) { |
| */ | return; |
| static void | } |
| ping_recv_win32(pcap_t *pcap,uint32_t packetwait, pcap_handler func) | } |
| { | |
| struct timeval tv,tv2; | if (verbose > 3) { |
| char done = 0; | printf("arping: ... right src mac\n"); |
| /* windows won't let us do select() */ | } |
| if (-1 == gettimeofday(&tv2,NULL)) { | |
| fprintf(stderr, "arping: gettimeofday(): %s\n", | // IPv4 Address must be me (maybe). |
| strerror(errno)); | if (addr_must_be_same) { |
| sigint(0); | uint32_t tmp; |
| } | memcpy(&tmp, &hip->ip_src, 4); |
| while (!done && !time_to_die) { | if (dstip != tmp) { |
| struct pcap_pkthdr *pkt_header; | return; |
| u_char *pkt_data; | } |
| if (pcap_next_ex(pcap, &pkt_header, &pkt_data) == 1) { | } |
| func(pcap, pkt_header, pkt_data); | |
| } | if (verbose > 3) { |
| if (-1 == gettimeofday(&tv,NULL)) { | printf("arping: ... src IP acceptable\n"); |
| fprintf(stderr, "arping: " | } |
| "gettimeofday(): %s\n", | |
| strerror(errno)); | // Must be ICMP echo reply type. |
| sigint(0); | if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) { |
| } | return; |
| /* | } |
| * setup next timeval, not very exact | |
| */ | if (verbose > 3) { |
| tv.tv_sec = (packetwait / 1000000) | printf("arping: ... is echo reply type\n"); |
| - (tv.tv_sec - tv2.tv_sec); | } |
| tv.tv_usec = (packetwait % 1000000) | |
| - (tv.tv_usec - tv2.tv_usec); | // Must be ICMP echo reply code 0. |
| while (tv.tv_usec < 0) { | if (htons(hicmp->icmp_code) != 0) { |
| tv.tv_sec--; | return; |
| tv.tv_usec += 1000000; | } |
| } | |
| usleep(10); | if (verbose > 3) { |
| if (tv.tv_sec < 0) { | printf("arping: ... is echo reply code\n"); |
| done=1; | } |
| } | |
| } | const char* payload = (char*)hicmp + LIBNET_ICMPV4_ECHO_H; |
| | const ssize_t payload_size = h->len - (payload - (char*)packet); |
| | if (payload_size < 0) { |
| | return; |
| | } |
| | if (payload_size < sizeof(struct timespec) + payload_suffix_size) { |
| | return; |
| | } |
| | if (verbose > 3) { |
| | printf("arping: ... correct payload size (%zd)\n", |
| | payload_size); |
| | } |
| | if (memcmp(&payload[sizeof(struct timespec)], |
| | payload_suffix, payload_suffix_size)) { |
| | return; |
| | } |
| | if (verbose > 3) { |
| | printf("arping: ... correct payload suffix\n"); |
| | } |
| | |
| | update_stats(timespec2dbl(&arrival) - timespec2dbl(&lastpacketsent)); |
| | if (beep) { |
| | printf("\a"); |
| | } |
| | char buf[128]; |
| | char buf2[128]; |
| | switch(display) { |
| | case QUIET: |
| | break; |
| | case DOT: |
| | putchar('!'); |
| | break; |
| | case NORMAL: |
| | printf("%d bytes from %s (%s): icmp_seq=%d time=%s", h->len, |
| | libnet_addr2name4(*(int*)&hip->ip_src, 0), |
| | format_mac(pkt_srcmac, buf, sizeof(buf)), |
| | htons(hicmp->icmp_seq), |
| | ts2str(&lastpacketsent, &arrival, buf2, sizeof(buf2))); |
| | break; |
| | case RAW: |
| | printf("%s", libnet_addr2name4(hip->ip_src.s_addr, 0)); |
| | break; |
| | case RRAW: |
| | printf("%s", format_mac(pkt_srcmac, buf, sizeof(buf))); |
| | break; |
| | case RAWRAW: |
| | printf("%s %s", |
| | format_mac(pkt_srcmac, buf, sizeof(buf)), |
| | libnet_addr2name4(hip->ip_src.s_addr, 0)); |
| | break; |
| | default: |
| | fprintf(stderr, "arping: can't-happen-bug\n"); |
| | sigint(0); |
| | } |
| | fflush(stdout); |
| | switch (display) { |
| | case QUIET: |
| | case DOT: |
| | break; |
| | default: |
| | printf("\n"); |
| | } |
| | numrecvd++; |
| | if (numrecvd >= max_replies) { |
| | sigint(0); |
| | } |
| } |
} |
| #endif |
|
| |
|
| /** |
/** |
| * while negative microseconds, take from whole seconds. | * while negative nanoseconds, take from whole seconds. |
| * help function for measuring deltas. |
* help function for measuring deltas. |
| */ |
*/ |
| static void |
static void |
| fixup_timeval(struct timeval *tv) | fixup_timespec(struct timespec *tv) |
| { |
{ |
| while (tv->tv_usec < 0) { | while (tv->tv_nsec < 0) { |
| tv->tv_sec--; |
tv->tv_sec--; |
| tv->tv_usec += 1000000; | tv->tv_nsec += 1000000000; |
| } |
} |
| } |
} |
| |
|
| /** |
/** |
| * idiot-proof gettimeofday() wrapper | * try to receive a packet for 'packetwait' microseconds |
| */ |
*/ |
| static void |
static void |
| gettv(struct timeval *tv) | ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_handler func) |
| { |
{ |
| if (-1 == gettimeofday(tv,NULL)) { | struct timespec ts; |
| fprintf(stderr, "arping: " | struct timespec endtime; |
| "gettimeofday(): %s\n", | |
| strerror(errno)); | |
| sigint(0); | |
| } | |
| } | |
| |
| |
| /** | |
| * | |
| */ | |
| static void | |
| ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_handler func) | |
| { | |
| struct timeval tv; | |
| struct timeval endtime; | |
| char done = 0; |
char done = 0; |
| |
int fd; |
| |
unsigned int old_received; |
| |
|
| gettv(&tv); | if (verbose > 3) { |
| endtime.tv_sec = tv.tv_sec + (packetwait / 1000000); | printf("arping: receiving packets...\n"); |
| endtime.tv_usec = tv.tv_usec + (packetwait % 1000000); | } |
| fixup_timeval(&endtime); | |
| |
|
| int fd; | getclock(&ts); |
| | endtime.tv_sec = ts.tv_sec + (packetwait / 1000000); |
| | endtime.tv_nsec = ts.tv_nsec + 1000 * (packetwait % 1000000); |
| | fixup_timespec(&endtime); |
| |
|
| fd = pcap_get_selectable_fd(pcap); |
fd = pcap_get_selectable_fd(pcap); |
| |
if (fd == -1) { |
| |
fprintf(stderr, "arping: pcap_get_selectable_fd()=-1: %s\n", |
| |
pcap_geterr(pcap)); |
| |
exit(1); |
| |
} |
| |
old_received = numrecvd; |
| |
|
| for (;!done;) { |
for (;!done;) { |
| int trydispatch = 0; |
int trydispatch = 0; |
| |
|
| gettv(&tv); | getclock(&ts); |
| tv.tv_sec = endtime.tv_sec - tv.tv_sec; | ts.tv_sec = endtime.tv_sec - ts.tv_sec; |
| tv.tv_usec = endtime.tv_usec - tv.tv_usec; | ts.tv_nsec = endtime.tv_nsec - ts.tv_nsec; |
| fixup_timeval(&tv); | fixup_timespec(&ts); |
| if (tv.tv_sec < 0) { | if (verbose > 2) { |
| tv.tv_sec = 0; | printf("arping: listen for replies for %ld.%09ld sec\n", |
| tv.tv_usec = 1; | (long)ts.tv_sec, (long)ts.tv_nsec); |
| | } |
| | |
| | /* if time has passed, do one last check and then we're done. |
| | * this also triggers if not using monotonic clock and time |
| | * is set forwards */ |
| | if (ts.tv_sec < 0) { |
| | ts.tv_sec = 0; |
| | ts.tv_nsec = 1; |
| done = 1; |
done = 1; |
| } |
} |
| |
|
| |
/* if wait-for-packet time is longer than full period, |
| |
* we're obviously not using a monotonic clock and the system |
| |
* time has been changed. |
| |
* we don't know how far we're into the waiting, so just end |
| |
* it here */ |
| |
if ((ts.tv_sec > packetwait / 1000000) |
| |
|| ((ts.tv_sec == packetwait / 1000000) |
| |
&& (ts.tv_nsec/1000 > packetwait % 1000000))) { |
| |
ts.tv_sec = 0; |
| |
ts.tv_nsec = 1; |
| |
done = 1; |
| |
} |
| |
|
| |
/* check for sigint */ |
| if (time_to_die) { |
if (time_to_die) { |
| return; |
return; |
| } |
} |
| |
|
| /* try to wait for data */ |
/* try to wait for data */ |
| { |
{ |
| struct pollfd p; | fd_set fds; |
| int r; |
int r; |
| p.fd = fd; | struct timeval tv; |
| p.events = POLLIN | POLLPRI; | tv.tv_sec = ts.tv_sec; |
| | tv.tv_usec = ts.tv_nsec / 1000; |
| |
|
| r = poll(&p, 1, tv.tv_sec * 1000 + tv.tv_usec / 1000); | FD_ZERO(&fds); |
| | FD_SET(fd, &fds); |
| | |
| | r = select(fd + 1, &fds, NULL, NULL, &tv); |
| switch (r) { |
switch (r) { |
| case 0: /* timeout */ |
case 0: /* timeout */ |
| |
if (numrecvd == old_received) { |
| |
if (reverse_beep) { |
| |
printf("\a"); |
| |
} |
| |
switch (display) { |
| |
case NORMAL: |
| |
printf("Timeout\n"); |
| |
break; |
| |
case DOT: |
| |
printf("."); |
| |
break; |
| |
case RAW: |
| |
case RAWRAW: |
| |
case RRAW: |
| |
case QUIET: |
| |
break; |
| |
} |
| |
fflush(stdout); |
| |
} |
| done = 1; |
done = 1; |
| break; |
break; |
| case -1: /* error */ |
case -1: /* error */ |
|
Line 896 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
Line 1630 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
| done = 1; |
done = 1; |
| sigint(0); |
sigint(0); |
| fprintf(stderr, |
fprintf(stderr, |
| "arping: poll() failed: %s\n", | "arping: select() failed: %s\n", |
| strerror(errno)); |
strerror(errno)); |
| } |
} |
| break; |
break; |
|
Line 908 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
Line 1642 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
| |
|
| if (trydispatch) { |
if (trydispatch) { |
| int ret; |
int ret; |
| if (1 != (ret = pcap_dispatch(pcap, 1, | if (0 > (ret = pcap_dispatch(pcap, -1, |
| func, | func, |
| NULL))) { | NULL))) { |
| /* rest, so we don't take 100% CPU... mostly |
/* rest, so we don't take 100% CPU... mostly |
| hmm... does usleep() exist everywhere? */ |
hmm... does usleep() exist everywhere? */ |
| usleep(1); |
usleep(1); |
|
Line 918 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
Line 1652 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
| /* weird is normal on bsd :) */ |
/* weird is normal on bsd :) */ |
| if (verbose > 3) { |
if (verbose > 3) { |
| fprintf(stderr, |
fprintf(stderr, |
| "arping: poll says ok, " | "arping: select says ok, but " |
| "pcap_dispatch=%d!\n", |
"pcap_dispatch=%d!\n", |
| ret); |
ret); |
| } |
} |
|
Line 927 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
Line 1661 ping_recv_unix(pcap_t *pcap,uint32_t packetwait, pcap_
|
| } |
} |
| } |
} |
| |
|
| /** | // return 1 on success. |
| * | static int |
| */ | xresolve(libnet_t* l, const char *name, int r, uint32_t *addr) |
| static void | |
| ping_recv(pcap_t *pcap,uint32_t packetwait, pcap_handler func) | |
| { |
{ |
| if(verbose>3) { | if (!strcmp(ip_broadcast, name)) { |
| printf("arping: receiving packets...\n"); | *addr = 0xffffffff; |
| } | return 1; |
| } |
| #if WIN32 | *addr = libnet_name2addr4(l, (char*)name, r); |
| ping_recv_win32(pcap,packetwait,func); | return *addr != 0xffffffff; |
| #else | |
| ping_recv_unix(pcap,packetwait,func); | |
| #endif | |
| } |
} |
| |
|
| /** |
/** |
| * |
* |
| */ |
*/ |
| int main(int argc, char **argv) | int |
| | arping_main(int argc, char **argv) |
| { |
{ |
| char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE]; |
char ebuf[LIBNET_ERRBUF_SIZE + PCAP_ERRBUF_SIZE]; |
| char *cp; |
char *cp; |
| int promisc = 0; | const char *srcip_opt = NULL; |
| int srcip_given = 0; | const char *dstip_opt = NULL; |
| int srcmac_given = 0; | // `dstip_given` can be set even when there's no arg past flags on the |
| | // cmdline and -B not set. E.g. -d defaults to self, so requires no |
| | // extra arg. |
| int dstip_given = 0; |
int dstip_given = 0; |
| const char *ifname = NULL; | const char *srcmac_opt = NULL; |
| char *parm; | const char *dstmac_opt = NULL; |
| | const char *ifname = NULL; // -i/-I |
| | int opt_B = 0; |
| | int opt_T = 0; |
| | int opt_U = 0; |
| | const char* drop_group = NULL; // -g |
| | const char *parm; // First argument, meaning the target IP. |
| int c; |
int c; |
| unsigned int maxcount = -1; | int maxcount = -1; |
| int dont_use_arping_lookupdev=0; |
int dont_use_arping_lookupdev=0; |
| struct bpf_program bp; |
struct bpf_program bp; |
| pcap_t *pcap; |
pcap_t *pcap; |
| static enum { NONE, PINGMAC, PINGIP } mode = NONE; | enum { NONE, PINGMAC, PINGIP } mode = NONE; |
| unsigned int packetwait = 1000000; | unsigned int packetwait = 1000000; // Default one second. |
| | double deadline = -1; |
| | char bpf_filter[64]; |
| | ebuf[0] = 0; |
| | srandom(time(NULL)); |
| |
|
| for (c = 1; c < argc; c++) { |
for (c = 1; c < argc; c++) { |
| if (!strcmp(argv[c], "--help")) { |
if (!strcmp(argv[c], "--help")) { |
|
Line 973 int main(int argc, char **argv)
|
Line 1715 int main(int argc, char **argv)
|
| } |
} |
| } |
} |
| |
|
| memset(ethnull, 0, ETH_ALEN); |
|
| |
|
| srcip = 0; |
srcip = 0; |
| dstip = 0xffffffff; |
dstip = 0xffffffff; |
| memset(dstmac, 0xff, ETH_ALEN); | memcpy(dstmac, ethxmas, ETH_ALEN); |
| memset(ethxmas, 0xff, ETH_ALEN); | |
| |
|
| while (EOF!=(c=getopt(argc,argv,"0aAbBc:dDeFhi:I:pqrRs:S:t:T:uvw:"))) { | while (EOF != (c = getopt(argc, argv, |
| | "0aAbBC:c:dDeFg:hi:I:m:pPqQ:rRs:S:t:T:uUvV:w:W:"))) { |
| switch(c) { |
switch(c) { |
| case '0': |
case '0': |
| srcip = 0; | srcip_opt = "0.0.0.0"; |
| srcip_given = 1; | |
| break; |
break; |
| case 'a': |
case 'a': |
| beep = 1; |
beep = 1; |
|
Line 993 int main(int argc, char **argv)
|
Line 1732 int main(int argc, char **argv)
|
| addr_must_be_same = 1; |
addr_must_be_same = 1; |
| break; |
break; |
| case 'b': |
case 'b': |
| srcip = 0xffffffff; | srcip_opt = ip_broadcast; |
| srcip_given = 1; | |
| break; |
break; |
| case 'B': |
case 'B': |
| dstip = 0xffffffff; | dstip_opt = ip_broadcast; |
| dstip_given = 1; | dstip_given = 1; |
| | opt_B = 1; |
| break; |
break; |
| case 'c': |
case 'c': |
| maxcount = atoi(optarg); |
maxcount = atoi(optarg); |
| break; |
break; |
| |
case 'C': |
| |
max_replies = atoi(optarg); |
| |
break; |
| case 'd': |
case 'd': |
| finddup = 1; |
finddup = 1; |
| break; |
break; |
|
Line 1017 int main(int argc, char **argv)
|
Line 1759 int main(int argc, char **argv)
|
| break; |
break; |
| case 'h': |
case 'h': |
| usage(0); |
usage(0); |
| |
exit(0); // Needless but shuts up compiler warnings. |
| |
case 'g': |
| |
drop_group = optarg; |
| |
break; |
| case 'i': |
case 'i': |
| if (strchr(optarg, ':')) { |
if (strchr(optarg, ':')) { |
| fprintf(stderr, "arping: If you're trying to " |
fprintf(stderr, "arping: If you're trying to " |
|
Line 1027 int main(int argc, char **argv)
|
Line 1773 int main(int argc, char **argv)
|
| "purpose)\n"); |
"purpose)\n"); |
| exit(1); |
exit(1); |
| } |
} |
| case 'I': /* FALL THROUGH */ |
|
| ifname = optarg; |
ifname = optarg; |
| |
break; |
| |
case 'I': |
| |
ifname = optarg; |
| break; |
break; |
| |
case 'm': |
| |
timestamp_type = optarg; |
| |
break; |
| case 'p': |
case 'p': |
| promisc = 1; |
promisc = 1; |
| break; |
break; |
| |
case 'P': |
| |
send_reply = 1; |
| |
break; |
| case 'q': |
case 'q': |
| display = QUIET; |
display = QUIET; |
| break; |
break; |
| |
case 'Q': |
| |
vlan_prio = atoi(optarg); |
| |
if (vlan_prio < 0 || vlan_prio > 7) { |
| |
fprintf(stderr, |
| |
"arping: 802.1p priority must be 0-7. It's %d\n", |
| |
vlan_prio); |
| |
exit(1); |
| |
} |
| |
break; |
| case 'r': |
case 'r': |
| display = (display==RRAW)?RAWRAW:RAW; |
display = (display==RRAW)?RAWRAW:RAW; |
| break; |
break; |
|
Line 1043 int main(int argc, char **argv)
|
Line 1806 int main(int argc, char **argv)
|
| display = (display==RAW)?RAWRAW:RRAW; |
display = (display==RAW)?RAWRAW:RRAW; |
| break; |
break; |
| case 's': { /* spoof source MAC */ |
case 's': { /* spoof source MAC */ |
| unsigned int n[6]; | srcmac_opt = optarg; |
| if (!get_mac_addr(optarg, | |
| &n[0],&n[1],&n[2], | |
| &n[3],&n[4],&n[5])){ | |
| fprintf(stderr, "arping: Weird MAC addr %s\n", | |
| optarg); | |
| exit(1); | |
| } | |
| for (c = 0; c < 6; c++) { | |
| srcmac[c] = n[c] & 0xff; | |
| } | |
| srcmac_given = 1; | |
| break; |
break; |
| } |
} |
| case 'S': /* set source IP, may be null for don't-know */ |
case 'S': /* set source IP, may be null for don't-know */ |
| do_libnet_init(ifname); | srcip_opt = optarg; |
| if (-1 == (srcip = libnet_name2addr4(libnet, | |
| optarg, | |
| LIBNET_RESOLVE))){ | |
| fprintf(stderr, "arping: Can't resolve %s, or " | |
| "%s is broadcast. If it is, use -b" | |
| " instead of -S\n", optarg,optarg); | |
| exit(1); | |
| } | |
| srcip_given = 1; | |
| break; |
break; |
| case 't': { /* set taget mac */ |
case 't': { /* set taget mac */ |
| unsigned int n[6]; | dstmac_opt = optarg; |
| if (mode == PINGMAC) { | |
| fprintf(stderr, "arping: -t can only be used " | |
| "in IP ping mode\n"); | |
| exit(1); | |
| } | |
| if (!get_mac_addr(optarg, | |
| &n[0],&n[1],&n[2], | |
| &n[3],&n[4],&n[5])){ | |
| fprintf(stderr, "Illegal MAC addr %s\n", | |
| optarg); | |
| exit(1); | |
| } | |
| for (c = 0; c < 6; c++) { | |
| dstmac[c] = n[c] & 0xff; | |
| } | |
| mode = PINGIP; |
mode = PINGIP; |
| break; |
break; |
| } |
} |
| case 'T': /* set destination IP */ |
case 'T': /* set destination IP */ |
| if (mode == PINGIP) { | opt_T = 1; |
| fprintf(stderr, "arping: -T can only be used " | dstip_opt = optarg; |
| "in MAC ping mode\n"); | |
| exit(1); | |
| } | |
| do_libnet_init(ifname); | |
| if (-1 == (dstip = libnet_name2addr4(libnet, | |
| optarg, | |
| LIBNET_RESOLVE))){ | |
| fprintf(stderr,"arping: Can't resolve %s, or " | |
| "%s is broadcast. If it is, use -B " | |
| "instead of -T\n",optarg,optarg); | |
| exit(1); | |
| } | |
| mode = PINGMAC; |
mode = PINGMAC; |
| break; |
break; |
| case 'u': |
case 'u': |
| alsototal = 1; |
alsototal = 1; |
| break; |
break; |
| |
case 'U': |
| |
opt_U = 1; |
| |
unsolicited = 1; |
| |
mode = PINGIP; |
| |
break; |
| case 'v': |
case 'v': |
| verbose++; |
verbose++; |
| break; |
break; |
| |
case 'V': |
| |
vlan_tag = atoi(optarg); |
| |
if (vlan_tag < 0 || vlan_tag > 4095) { |
| |
fprintf(stderr, |
| |
"arping: vlan tag must 0-4095. Is %d\n", |
| |
vlan_tag); |
| |
exit(1); |
| |
} |
| |
break; |
| case 'w': |
case 'w': |
| packetwait = (unsigned)atoi(optarg); | deadline = atof(optarg); |
| break; |
break; |
| |
case 'W': |
| |
packetwait = (unsigned)(1000000.0 * atof(optarg)); |
| |
break; |
| default: |
default: |
| usage(1); |
usage(1); |
| } |
} |
| } |
} |
| |
|
| |
if (argc - optind > 1) { |
| |
// Can be zero if using -d or -B. |
| |
fprintf(stderr, "arping: Too many args on command line." |
| |
" Expected at most one.\n"); |
| |
exit(1); |
| |
} |
| |
|
| |
// Generate random payload suffix for MAC pings, to be able to |
| |
// differentiate from unrelated ping replies. |
| |
if (payload_suffix_size < 0) { |
| |
payload_suffix_size = 4; |
| |
payload_suffix = malloc(payload_suffix_size); |
| |
if (payload_suffix) { |
| |
const ssize_t rc = xgetrandom(payload_suffix, payload_suffix_size, 0); |
| |
if (rc == -1) { |
| |
fprintf(stderr, |
| |
"arping: failed to get %zd random bytes: %s\n", |
| |
payload_suffix_size, |
| |
strerror(errno)); |
| |
free(payload_suffix); |
| |
payload_suffix = NULL; |
| |
} else if (payload_suffix_size != rc) { |
| |
fprintf(stderr, |
| |
"arping: only got %zd out of %zd bytes for random suffix\n", |
| |
rc, payload_suffix_size); |
| |
} |
| |
} else { |
| |
fprintf(stderr, "arping: failed to allocate %zd bytes for payload suffix.\n", |
| |
payload_suffix_size); |
| |
} |
| |
|
| |
if (!payload_suffix) { |
| |
fprintf(stderr, "arping: Using constant suffix.\n"); |
| |
payload_suffix = "arping"; |
| |
payload_suffix_size = strlen(payload_suffix); |
| |
} |
| |
} |
| |
|
| |
if (((mode == PINGIP) && opt_T) |
| |
|| ((mode == PINGMAC) && (opt_B || dstmac_opt || opt_U))) { |
| |
fprintf(stderr, "arping: -T can only be used to ping MAC" |
| |
" and -BtU only to ping IPs"); |
| |
exit(1); |
| |
} |
| |
if (opt_T && opt_B) { |
| |
fprintf(stderr, |
| |
"arping: -B can't be used with -T," |
| |
" since they set the same thing\n"); |
| |
exit(1); |
| |
} |
| |
|
| |
if (srcmac_opt != NULL) { |
| |
if (!get_mac_addr(srcmac_opt, srcmac)) { |
| |
fprintf(stderr, "arping: Weird MAC addr %s\n", |
| |
srcmac_opt); |
| |
exit(1); |
| |
} |
| |
} |
| |
|
| |
if (dstmac_opt != NULL) { |
| |
if (!get_mac_addr(dstmac_opt, dstmac)) { |
| |
fprintf(stderr, "Illegal MAC addr %s\n", dstmac_opt); |
| |
exit(1); |
| |
} |
| |
} |
| |
|
| |
if (srcip_opt != NULL) { |
| |
do_libnet_init(ifname, 0); |
| |
if (!xresolve(libnet, srcip_opt, LIBNET_RESOLVE, &srcip)) { |
| |
fprintf(stderr, "arping: Can't resolve %s, or " |
| |
"%s is broadcast. If it is, use -b" |
| |
" instead of -S\n", srcip_opt,srcip_opt); |
| |
exit(1); |
| |
} |
| |
} |
| |
|
| |
if (dstip_opt) { |
| |
do_libnet_init(ifname, 0); |
| |
if (!xresolve(libnet, dstip_opt, LIBNET_RESOLVE, &dstip)) { |
| |
fprintf(stderr,"arping: Can't resolve %s, or " |
| |
"%s is broadcast. If it is, use -B " |
| |
"instead of -T\n",dstip_opt,dstip_opt); |
| |
exit(1); |
| |
} |
| |
} |
| |
|
| |
if (vlan_prio >= 0 && vlan_tag == -1) { |
| |
fprintf(stderr, "arping: -Q requires the use of 802.1Q (-V)\n"); |
| |
exit(1); |
| |
} |
| |
if (vlan_prio == -1) { |
| |
vlan_prio = 0; |
| |
} |
| |
|
| |
if (verbose > 1) { |
| |
#if HAVE_CLOCK_MONOTONIC |
| |
struct timespec ts; |
| |
if (clock_getres(CLOCK_MONOTONIC, &ts)) { |
| |
fprintf(stderr, |
| |
"arping: clock_getres(CLOCK_MONOTONIC, ...): %s\n", |
| |
strerror(errno)); |
| |
} else { |
| |
printf("arping: clock_getres() = %lds %ldns\n", |
| |
(long)ts.tv_sec, (long)ts.tv_nsec); |
| |
} |
| |
#else |
| |
printf("arping: Using gettimeofday() for time measurements\n"); |
| |
#endif |
| |
} |
| |
|
| if (display == DOT) { |
if (display == DOT) { |
| setvbuf(stdout, NULL, _IONBF, 0); | if (0 != setvbuf(stdout, NULL, _IONBF, 0)) { |
| | fprintf(stderr, |
| | "arping: setvbuf(stdout, NULL, IONBF, 0): %s\n", |
| | strerror(errno)); |
| | } |
| } |
} |
| |
|
| if (finddup && maxcount == -1) { |
if (finddup && maxcount == -1) { |
|
Line 1133 int main(int argc, char **argv)
|
Line 1980 int main(int argc, char **argv)
|
| /* default to own IP address when doing -d */ |
/* default to own IP address when doing -d */ |
| if (finddup && !parm) { |
if (finddup && !parm) { |
| dstip_given = 1; |
dstip_given = 1; |
| do_libnet_init(ifname); | do_libnet_init(ifname, 0); |
| dstip = libnet_get_ipaddr4(libnet); |
dstip = libnet_get_ipaddr4(libnet); |
| if (verbose) { |
if (verbose) { |
| printf("defaulting to checking dup for %s\n", |
printf("defaulting to checking dup for %s\n", |
|
Line 1145 int main(int argc, char **argv)
|
Line 1992 int main(int argc, char **argv)
|
| * Handle dstip_given instead of ip address after parms (-B really) |
* Handle dstip_given instead of ip address after parms (-B really) |
| */ |
*/ |
| if (mode == NONE) { |
if (mode == NONE) { |
| if (optind + 1 == argc) { | if (parm) { |
| mode = is_mac_addr(parm)?PINGMAC:PINGIP; |
mode = is_mac_addr(parm)?PINGMAC:PINGIP; |
| } else if (dstip_given) { |
} else if (dstip_given) { |
| mode = PINGIP; |
mode = PINGIP; |
| do_libnet_init(ifname); | do_libnet_init(ifname, 0); |
| parm = strdup(libnet_addr2name4(dstip,0)); |
parm = strdup(libnet_addr2name4(dstip,0)); |
| if (!parm) { |
if (!parm) { |
| fprintf(stderr, "arping: out of mem\n"); | fprintf(stderr, "arping: out of memory\n"); |
| exit(1); |
exit(1); |
| } |
} |
| } |
} |
|
Line 1172 int main(int argc, char **argv)
|
Line 2019 int main(int argc, char **argv)
|
| /* |
/* |
| * libnet init (may be done already for resolving) |
* libnet init (may be done already for resolving) |
| */ |
*/ |
| do_libnet_init(ifname); | do_libnet_init(ifname, 0); |
| | |
| /* |
/* |
| * Make sure dstip and parm like eachother |
* Make sure dstip and parm like eachother |
| */ |
*/ |
|
Line 1184 int main(int argc, char **argv)
|
Line 2031 int main(int argc, char **argv)
|
| "\n"); |
"\n"); |
| exit(1); |
exit(1); |
| } |
} |
| if (-1 == (dstip = libnet_name2addr4(libnet, | if (!xresolve(libnet, parm, LIBNET_RESOLVE, &dstip)) { |
| parm, | |
| LIBNET_RESOLVE))) { | |
| fprintf(stderr, "arping: Can't resolve %s\n", parm); |
fprintf(stderr, "arping: Can't resolve %s\n", parm); |
| exit(1); |
exit(1); |
| } |
} |
|
Line 1197 int main(int argc, char **argv)
|
Line 2042 int main(int argc, char **argv)
|
| * parse parm into dstmac |
* parse parm into dstmac |
| */ |
*/ |
| if (mode == PINGMAC) { |
if (mode == PINGMAC) { |
| unsigned int n[6]; |
|
| if (optind + 1 != argc) { |
if (optind + 1 != argc) { |
| usage(1); |
usage(1); |
| } |
} |
|
Line 1207 int main(int argc, char **argv)
|
Line 2051 int main(int argc, char **argv)
|
| "argument\n"); |
"argument\n"); |
| exit(1); |
exit(1); |
| } |
} |
| if (!get_mac_addr(argv[optind], | if (!get_mac_addr(argv[optind], dstmac)) { |
| &n[0],&n[1],&n[2], | |
| &n[3],&n[4],&n[5])) { | |
| fprintf(stderr, "arping: Illegal mac addr %s\n", |
fprintf(stderr, "arping: Illegal mac addr %s\n", |
| argv[optind]); |
argv[optind]); |
| return 1; |
return 1; |
| } |
} |
| for (c = 0; c < 6; c++) { | } |
| dstmac[c] = n[c] & 0xff; | |
| } | |
| } | |
| |
|
| target = parm; |
target = parm; |
| /* |
/* |
|
Line 1228 int main(int argc, char **argv)
|
Line 2067 int main(int argc, char **argv)
|
| * Get some good iface. |
* Get some good iface. |
| */ |
*/ |
| if (!ifname) { |
if (!ifname) { |
| if (dont_use_arping_lookupdev) { | if (!dont_use_arping_lookupdev) { |
| ifname = arping_lookupdev_default(ifname, | ifname = arping_lookupdev(srcip, dstip, ebuf); |
| srcip,dstip,ebuf); | strip_newline(ebuf); |
| } else { | if (!ifname) { |
| ifname = arping_lookupdev(ifname,srcip,dstip,ebuf); | fprintf(stderr, "arping: lookup dev: %s\n", |
| | ebuf); |
| | } |
| | } |
| | if (!ifname) { |
| | ifname = arping_lookupdev_default(srcip, dstip, ebuf); |
| | strip_newline(ebuf); |
| | if (ifname && !dont_use_arping_lookupdev) { |
| | fprintf(stderr, |
| | "arping: Unable to automatically find " |
| | "interface to use. Is it on the local " |
| | "LAN?\n" |
| | "arping: Use -i to manually " |
| | "specify interface. " |
| | "Guessing interface %s.\n", ifname); |
| | } |
| } |
} |
| if (!ifname) { |
if (!ifname) { |
| fprintf(stderr, "arping: arping_lookupdev(): %s\n", | fprintf(stderr, "arping: Gave up looking for interface" |
| ebuf); | " to use: %s\n", ebuf); |
| exit(1); |
exit(1); |
| } |
} |
| /* FIXME: check for other probably-not interfaces */ |
/* FIXME: check for other probably-not interfaces */ |
|
Line 1253 int main(int argc, char **argv)
|
Line 2107 int main(int argc, char **argv)
|
| * Init libnet again, because we now know the interface name. |
* Init libnet again, because we now know the interface name. |
| * We should know it by know at least |
* We should know it by know at least |
| */ |
*/ |
| do_libnet_init(ifname); | do_libnet_init(ifname, 0); |
| |
|
| /* |
/* |
| * pcap init |
* pcap init |
| */ |
*/ |
| if (!(pcap = pcap_open_live((char*)ifname, 100, promisc, 10, ebuf))) { | if (!(pcap = do_pcap_open_live(ifname, 100, promisc, 10, ebuf))) { |
| fprintf(stderr, "arping: pcap_open_live(): %s\n",ebuf); | strip_newline(ebuf); |
| | fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf); |
| exit(1); |
exit(1); |
| } |
} |
| |
drop_privileges(drop_group); |
| if (pcap_setnonblock(pcap, 1, ebuf)) { |
if (pcap_setnonblock(pcap, 1, ebuf)) { |
| |
strip_newline(ebuf); |
| fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf); |
fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf); |
| exit(1); |
exit(1); |
| } |
} |
| if (verbose) { | if (verbose > 1) { |
| printf("pcap_get_selectable(): %d\n", | printf("arping: pcap_get_selectable_fd(): %d\n", |
| pcap_get_selectable_fd(pcap)); |
pcap_get_selectable_fd(pcap)); |
| } |
} |
| |
|
| #if HAVE_NET_BPF_H | #ifdef BIOCIMMEDIATE |
| { |
{ |
| |
// This may be redundant if pcap_set_immediate_mode() is present. |
| uint32_t on = 1; |
uint32_t on = 1; |
| if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE, |
if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE, |
| &on))) { |
&on))) { |
|
Line 1285 int main(int argc, char **argv)
|
Line 2143 int main(int argc, char **argv)
|
| |
|
| if (mode == PINGIP) { |
if (mode == PINGIP) { |
| /* FIXME: better filter with addresses? */ |
/* FIXME: better filter with addresses? */ |
| if (-1 == pcap_compile(pcap, &bp, "arp", 0,-1)) { | if (vlan_tag >= 0) { |
| fprintf(stderr, "arping: pcap_compile(): error\n"); | snprintf(bpf_filter, sizeof(bpf_filter), |
| | "vlan %u and arp", vlan_tag); |
| | } else { |
| | snprintf(bpf_filter, sizeof(bpf_filter), "arp"); |
| | } |
| | if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0, -1)) { |
| | fprintf(stderr, "arping: pcap_compile(%s): %s\n", |
| | bpf_filter, pcap_geterr(pcap)); |
| exit(1); |
exit(1); |
| } |
} |
| } else { /* ping mac */ |
} else { /* ping mac */ |
| /* FIXME: better filter with addresses? */ |
/* FIXME: better filter with addresses? */ |
| if (-1 == pcap_compile(pcap, &bp, "icmp", 0,-1)) { | if (vlan_tag >= 0) { |
| fprintf(stderr, "arping: pcap_compile(): error\n"); | snprintf(bpf_filter, sizeof(bpf_filter), |
| | "vlan %u and icmp", vlan_tag); |
| | } else { |
| | snprintf(bpf_filter, sizeof(bpf_filter), "icmp"); |
| | } |
| | if (-1 == pcap_compile(pcap, &bp, bpf_filter, 0,-1)) { |
| | fprintf(stderr, "arping: pcap_compile(%s): %s\n", |
| | bpf_filter, pcap_geterr(pcap)); |
| exit(1); |
exit(1); |
| } |
} |
| } |
} |
| if (-1 == pcap_setfilter(pcap, &bp)) { |
if (-1 == pcap_setfilter(pcap, &bp)) { |
| fprintf(stderr, "arping: pcap_setfilter(): error\n"); | fprintf(stderr, "arping: pcap_setfilter(): %s\n", |
| | pcap_geterr(pcap)); |
| exit(1); |
exit(1); |
| } |
} |
| |
|
| /* |
/* |
| * final init |
* final init |
| */ |
*/ |
| if (!srcmac_given) { | if (srcmac_opt == NULL) { |
| if (!(cp = (char*)libnet_get_hwaddr(libnet))) { |
if (!(cp = (char*)libnet_get_hwaddr(libnet))) { |
| fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n", |
fprintf(stderr, "arping: libnet_get_hwaddr(): %s\n", |
| libnet_geterror(libnet)); |
libnet_geterror(libnet)); |
|
Line 1312 int main(int argc, char **argv)
|
Line 2185 int main(int argc, char **argv)
|
| } |
} |
| memcpy(srcmac, cp, ETH_ALEN); |
memcpy(srcmac, cp, ETH_ALEN); |
| } |
} |
| if (!srcip_given) { | if (srcip_opt == NULL) { |
| if (-1 == (srcip = libnet_get_ipaddr4(libnet))) { | if (ip_broadcast_num == (srcip = libnet_get_ipaddr4(libnet))) { |
| fprintf(stderr, "arping: libnet_get_ipaddr4(libnet): " | fprintf(stderr, |
| "%s\n", libnet_geterror(libnet)); | "arping: Unable to get the IPv4 address of " |
| | "interface %s:\narping: %s" |
| | "arping: " |
| | "Use -S to specify address manually.\n", |
| | ifname, libnet_geterror(libnet)); |
| exit(1); |
exit(1); |
| } |
} |
| } |
} |
| #if WIN32 | do_signal_init(); |
| /* SetConsoleCtrlHandler(NULL, TRUE); */ | |
| SetConsoleCtrlHandler(arping_console_ctrl_handler, TRUE); | |
| #else | |
| signal(SIGINT, sigint); | |
| #endif | |
| |
|
| if (verbose) { |
if (verbose) { |
| printf("This box: Interface: %s IP: %s MAC address: ", | char buf[128]; |
| ifname, libnet_addr2name4(libnet_get_ipaddr4(libnet), | printf("This box: Interface: %s IP: %s MAC address: %s\n", |
| 0)); | ifname, |
| for (c = 0; c < ETH_ALEN - 1; c++) { | libnet_addr2name4(libnet_get_ipaddr4(libnet), 0), |
| printf("%.2x:", (uint8_t)srcmac[c]); | format_mac(srcmac, buf, sizeof(buf))); |
| } | |
| printf("%.2x\n", (uint8_t)srcmac[ETH_ALEN - 1]); | |
| } |
} |
| |
|
| |
drop_more_privileges(); |
| |
|
| if (display == NORMAL) { |
if (display == NORMAL) { |
| printf("ARPING %s\n", parm); |
printf("ARPING %s\n", parm); |
|
Line 1344 int main(int argc, char **argv)
|
Line 2215 int main(int argc, char **argv)
|
| /* |
/* |
| * let's roll |
* let's roll |
| */ |
*/ |
| |
if (deadline > 0) { |
| |
struct timespec ts; |
| |
getclock(&ts); |
| |
deadline += timespec2dbl(&ts); |
| |
} |
| if (mode == PINGIP) { |
if (mode == PINGIP) { |
| unsigned int c; | int c; |
| unsigned int r; | for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) { |
| for (c = 0; c < maxcount && !time_to_die; c++) { | pingip_send(); |
| pingip_send(srcmac, dstmac, srcip, dstip); | const uint32_t w = wait_time(deadline, packetwait); |
| r = numrecvd; | if (w == 0) { |
| ping_recv(pcap,packetwait, | break; |
| (pcap_handler)pingip_recv); | |
| if (reverse_beep && !time_to_die && (r == numrecvd)) { | |
| printf("\a"); | |
| fflush(stdout); | |
| } |
} |
| |
ping_recv(pcap, w, (pcap_handler)pingip_recv); |
| } |
} |
| } else { /* PINGMAC */ |
} else { /* PINGMAC */ |
| unsigned int c; | int c; |
| unsigned int r; | for (c = 0; (maxcount < 0 || c < maxcount) && !time_to_die; c++) { |
| for (c = 0; c < maxcount && !time_to_die; c++) { | pingmac_send(xrandom(), c); |
| pingmac_send(srcmac, dstmac, srcip, dstip, rand(), c); | const uint32_t w = wait_time(deadline, packetwait); |
| r = numrecvd; | if (w == 0) { |
| ping_recv(pcap,packetwait, | break; |
| (pcap_handler)pingmac_recv); | |
| if (reverse_beep && !time_to_die && (r == numrecvd)) { | |
| printf("\a"); | |
| fflush(stdout); | |
| } |
} |
| |
ping_recv(pcap, w, (pcap_handler)pingmac_recv); |
| } |
} |
| } |
} |
| if (display == DOT) { |
if (display == DOT) { |
| count_missing_dots(); | const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; |
| printf("\t%3.0f%% packet loss\n", | printf("\t%3.0f%% packet loss (%d extra)\n", |
| 100.0 - 100.0 * (float)(numrecvd)/(float)numsent); | (succ < 0.0) ? 0.0 : succ, |
| | (succ < 0.0) ? (numrecvd - numsent) : 0); |
| } else if (display == NORMAL) { |
} else if (display == NORMAL) { |
| float succ; | const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; |
| succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; | |
| printf("\n--- %s statistics ---\n" |
printf("\n--- %s statistics ---\n" |
| "%d packets transmitted, " |
"%d packets transmitted, " |
| "%d packets received, " |
"%d packets received, " |
|
Line 1385 int main(int argc, char **argv)
|
Line 2255 int main(int argc, char **argv)
|
| "unanswered (%d extra)\n", |
"unanswered (%d extra)\n", |
| target,numsent,numrecvd, |
target,numsent,numrecvd, |
| (succ < 0.0) ? 0.0 : succ, |
(succ < 0.0) ? 0.0 : succ, |
| (succ < 0.0) ? (numrecvd - numsent): 0); | (succ < 0.0) ? (numrecvd - numsent): 0); |
| | if (numrecvd) { |
| | double avg = stats_total_time / numrecvd; |
| | printf("rtt min/avg/max/std-dev = " |
| | "%.3f/%.3f/%.3f/%.3f ms", |
| | 1000*stats_min_time, |
| | 1000*avg, |
| | 1000*stats_max_time, |
| | 1000*sqrt(stats_total_sq_time/numrecvd |
| | -avg*avg)); |
| | } |
| | printf("\n"); |
| } |
} |
| |
|
| if (finddup) { |
if (finddup) { |
|
Line 1394 int main(int argc, char **argv)
|
Line 2275 int main(int argc, char **argv)
|
| return !numrecvd; |
return !numrecvd; |
| } |
} |
| } |
} |
| |
/* ---- Emacs Variables ---- |
| |
* Local Variables: |
| |
* c-basic-offset: 8 |
| |
* indent-tabs-mode: nil |
| |
* End: |
| |
* |
| |
* vim: ts=8 sw=8 |
| |
*/ |