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