--- embedaddon/arping/src/arping.c 2014/06/15 16:26:43 1.1.1.2 +++ embedaddon/arping/src/arping.c 2016/10/18 13:16:10 1.1.1.3 @@ -15,17 +15,17 @@ * */ /* - * Copyright (C) 2000-2011 Thomas Habets + * Copyright (C) 2000-2014 Thomas Habets * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (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 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * * 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., @@ -85,6 +85,14 @@ #include #endif +#if HAVE_PWD_H +#include +#endif + +#if HAVE_SYS_CAPABILITY_H +#include +#endif + #if HAVE_NET_BPF_H #include #endif @@ -110,6 +118,8 @@ /** * 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 * arping_lookupdev(uint32_t srcip, uint32_t dstip, char *ebuf); @@ -121,6 +131,9 @@ static const char *version = VERSION; /* from autoconf static libnet_t *libnet = 0; +/* Timestamp of last packet sent. + * Used for timing, and assumes that reply is due to most recent sent query. + */ static struct timespec lastpacketsent; /* target string */ @@ -146,14 +159,17 @@ static int reverse_beep = 0; /* beep when expe 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 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 */ -static unsigned int numsent = 0; /* packets sent */ -static unsigned int numrecvd = 0; /* packets received */ -static unsigned int numdots = 0; /* dots that should be printed */ +static unsigned int numsent = 0; /* packets sent */ +static unsigned int numrecvd = 0; /* packets received */ +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 double stats_min_time = -1; static double stats_max_time = -1; @@ -178,6 +194,229 @@ int verbose = 0; /* Increase with -v */ static volatile sig_atomic_t time_to_die = 0; /** + * 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 +drop_fs_root() +{ + const char* chroot_user = "sshd"; + struct passwd *pw; + errno = 0; + if (!(pw = getpwnam(chroot_user))) { + if (verbose) { + printf("arping: getpwnam(%s): %s", + chroot_user, strerror(errno)); + } + return; + } + if (chdir(pw->pw_dir)) { + if (verbose) { + printf("arping: chdir(%s): %s", + pw->pw_dir, strerror(errno)); + } + return; + } + if (chroot(pw->pw_dir)) { + if (verbose) { + printf("arping: chroot(%s): %s", + pw->pw_dir, 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 +} + +/** + * drop all privileges. + */ +static void +drop_privileges() +{ + // 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) { + printf("arping: getpwnam(%s): %s\n", + drop_user, strerror(errno)); + } + return; + } else { + uid = pw->pw_uid; + gid = pw->pw_gid; + } + drop_fs_root(); + drop_uid(uid, gid); + drop_capabilities(); +} + + +/** + * Do pcap_open_live(), except by using the pcap_create() interface + * introduced in 2008 (libpcap 0.4) where available. + * + * 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 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. * @@ -194,45 +433,51 @@ do_pcap_open_live(const char *device, int snaplen, pcap_t* ret; char buf[PATH_MAX]; - if ((ret = pcap_open_live(device, snaplen, promisc, to_ms, errbuf))) { + if ((ret = try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf))) { return ret; } snprintf(buf, sizeof(buf), "/dev/%s", device); - if ((ret = pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { + if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { return ret; } snprintf(buf, sizeof(buf), "/dev/net/%s", device); - if ((ret = pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { + if ((ret = try_pcap_open_live(buf, snaplen, promisc, to_ms, errbuf))) { return ret; } /* Call original again to reset the error message. */ - return pcap_open_live(device, snaplen, promisc, to_ms, errbuf); + return try_pcap_open_live(device, snaplen, promisc, to_ms, errbuf); } /** - * + * Some Libnet error messages end with a newline. Strip that in place. */ -static void -count_missing_dots() -{ - while (numsent > numdots) { - putchar('.'); - numdots++; +void +strip_newline(char* s) { + if (!*s) { + return; } + size_t n; + for (n = strlen(s); 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) +do_libnet_init(const char *ifname, int recursive) { char ebuf[LIBNET_ERRBUF_SIZE]; + ebuf[0] = 0; if (verbose > 1) { - printf("libnet_init(%s)\n", ifname ? ifname : ""); + printf("arping: libnet_init(%s)\n", ifname ? ifname : ""); } if (libnet) { /* Probably going to switch interface from temp to real. */ @@ -245,7 +490,18 @@ do_libnet_init(const char *ifname) if (!(libnet = libnet_init(LIBNET_LINK, (char*)ifname, ebuf))) { - fprintf(stderr, "arping: %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". */ + return do_libnet_init("lo", 1); + } else if (recursive) { + /* Continue original execution. */ + return; + } + fprintf(stderr, "arping: libnet_init(LIBNET_LINK, %s): %s\n", + ifname ? ifname : "", ebuf); if (getuid() && geteuid()) { fprintf(stderr, "arping: you may need to run as root\n"); @@ -291,6 +547,16 @@ getclock(struct timespec *ts) /** * */ +static char* +format_mac(unsigned char* mac, char* buf, size_t bufsize) { + snprintf(buf, bufsize, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return buf; +} + +/** + * + */ static void extended_usage() { @@ -310,6 +576,8 @@ extended_usage() " -B Use instead of host if you want to address 255.255.255.255.\n" " -c count\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 " "answers from\n" " two different MAC addresses.\n" @@ -320,13 +588,19 @@ extended_usage() " -h Displays a help message and exits.\n" " -i 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" " -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" " with -r.\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" - " 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" " 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" @@ -339,11 +613,13 @@ extended_usage() " $ arping -S -s -p \n" " -p Turn on promiscious mode on interface, use this if you don't\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" " pinging MACs.\n" " -U Send unsolicited ARP.\n" " -v Verbose output. Use twice for more messages.\n" - " -w Time to wait between pings, in microseconds.\n"); + " -w Time to wait between pings, in microseconds.\n" + " -W Same as -w, but in floating point seconds.\n"); printf("Report bugs to: thomas@habets.se\n" "Arping home page: \n" "Development repo: http://github.com/ThomasHabets/arping\n"); @@ -357,13 +633,14 @@ standard_usage() { printf("ARPing %s, by Thomas Habets \n", version); - printf("usage: arping [ -0aAbdDeFpqrRuUv ] [ -w ] " + printf("usage: arping [ -0aAbdDeFpPqrRuUv ] [ -w ] " + "[ -W ] " "[ -S ]\n" " " "[ -T ] [ -t ] [ -c ]\n" " " - "[ -i ] " + "[ -C ] [ -i ] [ -m ] " "\n"); } @@ -477,11 +754,9 @@ timespec2dbl(const struct timespec *tv) * max size of buffer is intsize + 1 + intsize + 4 = 70 bytes or so * * Still, I'm using at least 128bytes below - * - * (because snprintf() sadly isn't as portable, that's why) */ static char *ts2str(const struct timespec *tv, const struct timespec *tv2, - char *buf) + char *buf, size_t bufsize) { double f,f2; int exp = 0; @@ -495,23 +770,23 @@ static char *ts2str(const struct timespec *tv, const s } switch (exp) { case 0: - sprintf(buf, "%.3f nsec", f); + snprintf(buf, bufsize, "%.3f nsec", f); break; case 3: - sprintf(buf, "%.3f usec", f); + snprintf(buf, bufsize, "%.3f usec", f); break; case 6: - sprintf(buf, "%.3f msec", f); + snprintf(buf, bufsize, "%.3f msec", f); break; case 9: - sprintf(buf, "%.3f sec", f); + snprintf(buf, bufsize, "%.3f sec", f); break; case 12: - sprintf(buf, "%.3f sec", f*1000); + snprintf(buf, bufsize, "%.3f sec", f*1000); break; default: /* huh, uh, huhuh */ - sprintf(buf, "%.3fe%d sec", f, exp-9); + snprintf(buf, bufsize, "%.3fe%d sec", f, exp-9); } return buf; } @@ -598,7 +873,7 @@ pingip_send() ETHERTYPE_IP, ETH_ALEN, IP_ALEN, - ARPOP_REQUEST, + send_reply ? ARPOP_REPLY : ARPOP_REQUEST, srcmac, (uint8_t*)&srcip, unsolicited ? (uint8_t*)ethxmas : (uint8_t*)ethnull, @@ -651,7 +926,7 @@ pingip_recv(const char *unused, struct pcap_pkthdr *h, int c; if (verbose > 2) { - printf("arping: received response for ip ping\n"); + printf("arping: received response for IP ping\n"); } getclock(&arrival); @@ -659,88 +934,111 @@ pingip_recv(const char *unused, struct pcap_pkthdr *h, heth = (void*)packet; harp = (void*)((char*)heth + LIBNET_ETH_H); - if ((htons(harp->ar_op) == ARPOP_REPLY) - && (htons(harp->ar_pro) == ETHERTYPE_IP) - && (htons(harp->ar_hrd) == ARPHRD_ETHER)) { - uint32_t ip; - memcpy(&ip, (char*)harp + harp->ar_hln - + LIBNET_ARP_H,4); - if (addr_must_be_same - && (memcmp((u_char*)harp+sizeof(struct libnet_arp_hdr), - dstmac, ETH_ALEN))) { - return; - } - if (dstip == ip) { - update_stats(timespec2dbl(&arrival) - - timespec2dbl(&lastpacketsent)); - 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", - ts2str(&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"); - } + // ARP reply. + if (htons(harp->ar_op) != ARPOP_REPLY) { + return; + } + if (verbose > 3) { + printf("arping: ... packet is ARP reply\n"); + } - switch (display) { - case QUIET: - case DOT: - break; - default: - if (beep) { - printf("\a"); - } - printf("\n"); - } - if (numrecvd) { - if (memcmp(lastreplymac, - heth->_802_3_shost, ETH_ALEN)) { - dupfound = 1; - } - } - memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN); + // From IPv4 address reply. + if (htons(harp->ar_pro) != ETHERTYPE_IP) { + return; + } + if (verbose > 3) { + printf("arping: ... from IPv4 address\n"); + } - numrecvd++; - } - } + // 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"); + } + + // 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(heth->_802_3_shost, + 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(heth->_802_3_shost, + buf, sizeof(buf)), + libnet_addr2name4(ip, 0)); + break; + case RRAW: + printf("%s", libnet_addr2name4(ip, 0)); + break; + case RAW: + printf("%s", format_mac(heth->_802_3_shost, + 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, + heth->_802_3_shost, ETH_ALEN)) { + dupfound = 1; + } + } + memcpy(lastreplymac, heth->_802_3_shost, ETH_ALEN); + + numrecvd++; + if (numrecvd >= max_replies) { + sigint(0); + } } /** handle incoming packet when pinging an MAC address. @@ -767,61 +1065,78 @@ pingmac_recv(const char *unused, struct pcap_pkthdr *h hip = (void*)((char*)heth + LIBNET_ETH_H); hicmp = (void*)((char*)hip + LIBNET_IPV4_H); - if ((htons(hicmp->icmp_type) == ICMP_ECHOREPLY) - && ((!memcmp(heth->_802_3_shost, dstmac,ETH_ALEN) - || !memcmp(dstmac, ethxmas, ETH_ALEN))) - && !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; - } - } - update_stats(timespec2dbl(&arrival) - - timespec2dbl(&lastpacketsent)); - 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),ts2str(&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++; - } + // Dest MAC must be me. + if (memcmp(heth->_802_3_dhost, srcmac, ETH_ALEN)) { + return; + } + + // Source MAC must match, if set. + if (memcmp(dstmac, ethxmas, ETH_ALEN)) { + if (memcmp(heth->_802_3_shost, dstmac, ETH_ALEN)) { + return; + } + } + + // IPv4 Address must be me (maybe). + if (addr_must_be_same) { + uint32_t tmp; + memcpy(&tmp, &hip->ip_src, 4); + if (dstip != tmp) { + return; + } + } + + // Must be ICMP echo reply. + if (htons(hicmp->icmp_type) != ICMP_ECHOREPLY) { + return; + } + + 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(heth->_802_3_shost, 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(heth->_802_3_shost, buf, sizeof(buf))); + break; + case RAWRAW: + printf("%s %s", + format_mac(heth->_802_3_shost, 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); + } } /** @@ -869,7 +1184,7 @@ ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand ts.tv_nsec = endtime.tv_nsec - ts.tv_nsec; fixup_timespec(&ts); if (verbose > 2) { - printf("listen for replies for %ld.%09ld sec\n", + printf("arping: listen for replies for %ld.%09ld sec\n", (long)ts.tv_sec, (long)ts.tv_nsec); } @@ -914,10 +1229,19 @@ ping_recv(pcap_t *pcap, uint32_t packetwait, pcap_hand r = select(fd + 1, &fds, NULL, NULL, &tv); switch (r) { case 0: /* timeout */ - if (display == NORMAL) { - if (numrecvd == old_received) { + if (numrecvd == old_received) { + if (reverse_beep) { + printf("\a"); + } + switch (display) { + case NORMAL: printf("Timeout\n"); + break; + case DOT: + printf("."); + break; } + fflush(stdout); } done = 1; break; @@ -977,6 +1301,7 @@ int main(int argc, char **argv) pcap_t *pcap; enum { NONE, PINGMAC, PINGIP } mode = NONE; unsigned int packetwait = 1000000; + ebuf[0] = 0; for (c = 1; c < argc; c++) { if (!strcmp(argv[c], "--help")) { @@ -990,7 +1315,8 @@ int main(int argc, char **argv) dstip = 0xffffffff; memcpy(dstmac, ethxmas, ETH_ALEN); - while (EOF!=(c=getopt(argc,argv,"0aAbBc:dDeFhi:I:pqrRs:S:t:T:uUvw:"))) { + while (EOF != (c = getopt(argc, argv, + "0aAbBC:c:dDeFhi:I:m:pPqrRs:S:t:T:uUvw:W:"))) { switch(c) { case '0': srcip = 0; @@ -1013,6 +1339,9 @@ int main(int argc, char **argv) case 'c': maxcount = atoi(optarg); break; + case 'C': + max_replies = atoi(optarg); + break; case 'd': finddup = 1; break; @@ -1040,9 +1369,15 @@ int main(int argc, char **argv) case 'I': /* FALL THROUGH */ ifname = optarg; break; + case 'm': + timestamp_type = optarg; + break; case 'p': promisc = 1; break; + case 'P': + send_reply = 1; + break; case 'q': display = QUIET; break; @@ -1068,7 +1403,7 @@ int main(int argc, char **argv) break; } case 'S': /* set source IP, may be null for don't-know */ - do_libnet_init(ifname); + do_libnet_init(ifname, 0); if (-1 == (srcip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE))){ @@ -1105,7 +1440,7 @@ int main(int argc, char **argv) "in MAC ping mode\n"); exit(1); } - do_libnet_init(ifname); + do_libnet_init(ifname, 0); if (-1 == (dstip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE))){ @@ -1133,6 +1468,9 @@ int main(int argc, char **argv) case 'w': packetwait = (unsigned)atoi(optarg); break; + case 'W': + packetwait = (unsigned)(1000000.0 * atof(optarg)); + break; default: usage(1); } @@ -1142,10 +1480,10 @@ int main(int argc, char **argv) #if HAVE_CLOCK_MONOTONIC struct timespec ts; clock_getres(CLOCK_MONOTONIC, &ts); - printf("clock_getres() = %ld %ld\n", + printf("arping: clock_getres() = %ld %ld\n", (long)ts.tv_sec, (long)ts.tv_nsec); #else - printf("Using gettimeofday() for time measurements\n"); + printf("arping: Using gettimeofday() for time measurements\n"); #endif } @@ -1162,7 +1500,7 @@ int main(int argc, char **argv) /* default to own IP address when doing -d */ if (finddup && !parm) { dstip_given = 1; - do_libnet_init(ifname); + do_libnet_init(ifname, 0); dstip = libnet_get_ipaddr4(libnet); if (verbose) { printf("defaulting to checking dup for %s\n", @@ -1178,7 +1516,7 @@ int main(int argc, char **argv) mode = is_mac_addr(parm)?PINGMAC:PINGIP; } else if (dstip_given) { mode = PINGIP; - do_libnet_init(ifname); + do_libnet_init(ifname, 0); parm = strdup(libnet_addr2name4(dstip,0)); if (!parm) { fprintf(stderr, "arping: out of mem\n"); @@ -1201,8 +1539,8 @@ int main(int argc, char **argv) /* * libnet init (may be done already for resolving) */ - do_libnet_init(ifname); - + do_libnet_init(ifname, 0); + /* * Make sure dstip and parm like eachother */ @@ -1259,10 +1597,16 @@ int main(int argc, char **argv) if (!ifname) { if (!dont_use_arping_lookupdev) { ifname = arping_lookupdev(srcip, dstip, ebuf); + strip_newline(ebuf); + if (!ifname) { + fprintf(stderr, "arping: lookup dev: %s\n", + ebuf); + } } if (!ifname) { ifname = arping_lookupdev_default(srcip, dstip, ebuf); - if (!dont_use_arping_lookupdev) { + strip_newline(ebuf); + if (ifname && !dont_use_arping_lookupdev) { fprintf(stderr, "arping: Unable to automatically find " "interface to use. Is it on the local " @@ -1291,26 +1635,30 @@ int main(int argc, char **argv) * Init libnet again, because we now know the interface name. * We should know it by know at least */ - do_libnet_init(ifname); + do_libnet_init(ifname, 0); /* * pcap init */ 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); } + drop_privileges(); if (pcap_setnonblock(pcap, 1, ebuf)) { + strip_newline(ebuf); fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf); exit(1); } if (verbose > 1) { - printf("pcap_get_selectable_fd(): %d\n", + printf("arping: pcap_get_selectable_fd(): %d\n", pcap_get_selectable_fd(pcap)); } #ifdef BIOCIMMEDIATE { + // This may be redundant if pcap_set_immediate_mode() is present. uint32_t on = 1; if (0 < (ioctl(pcap_fileno(pcap), BIOCIMMEDIATE, &on))) { @@ -1364,13 +1712,11 @@ int main(int argc, char **argv) do_signal_init(); if (verbose) { - printf("This box: Interface: %s IP: %s MAC address: ", - ifname, libnet_addr2name4(libnet_get_ipaddr4(libnet), - 0)); - for (c = 0; c < ETH_ALEN - 1; c++) { - printf("%.2x:", (uint8_t)srcmac[c]); - } - printf("%.2x\n", (uint8_t)srcmac[ETH_ALEN - 1]); + char buf[128]; + printf("This box: Interface: %s IP: %s MAC address: %s\n", + ifname, + libnet_addr2name4(libnet_get_ipaddr4(libnet), 0), + format_mac(srcmac, buf, sizeof(buf))); } @@ -1389,10 +1735,6 @@ int main(int argc, char **argv) r = numrecvd; ping_recv(pcap,packetwait, (pcap_handler)pingip_recv); - if (reverse_beep && !time_to_die && (r == numrecvd)) { - printf("\a"); - fflush(stdout); - } } } else { /* PINGMAC */ unsigned int c; @@ -1402,19 +1744,15 @@ int main(int argc, char **argv) r = numrecvd; ping_recv(pcap,packetwait, (pcap_handler)pingmac_recv); - if (reverse_beep && !time_to_die && (r == numrecvd)) { - printf("\a"); - fflush(stdout); - } } } if (display == DOT) { - count_missing_dots(); - printf("\t%3.0f%% packet loss\n", - 100.0 - 100.0 * (float)(numrecvd)/(float)numsent); + const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; + printf("\t%3.0f%% packet loss (%d extra)\n", + (succ < 0.0) ? 0.0 : succ, + (succ < 0.0) ? (numrecvd - numsent) : 0); } else if (display == NORMAL) { - float succ; - succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; + const float succ = 100.0 - 100.0 * (float)(numrecvd)/(float)numsent; printf("\n--- %s statistics ---\n" "%d packets transmitted, " "%d packets received, "