/* * NOTE: This is arping 1.x */ /* * arping * * By Thomas Habets * * ARP 'ping' utility * * Broadcasts a who-has ARP packet on the network and prints answers. * *VERY* useful when you are trying to pick an unused IP for a net that * 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. * * Also finds out IP of specified MAC * */ /* * Copyright (C) 2000-2008 Thomas Habets * * 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 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. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * Note to self: * Test checklist: (cmac = mac in cisco format (0000.0000.0000) * command expected response * arping host pongs * arping -a host audible pongs * arping mac pongs * arping cmac pongs * arping -a mac audible pongs * arping -A host nothing * arping -A mac nothing * arping -u host pongs, index n/m * arping -t cmac -A host pongs * arping -t mac -A host pongs * arping -T ip -A mac pongs * arping -T ip -A cmac pongs * arping -T bcast mac pongs # bcast of current net * arping -r host mac * arping -R host ip * arping -r mac ip * arping -R mac mac * arping -rR mac mac ip * arping -rR ip mac ip * ./arping-scan-net.sh mac ip * * Arch checklist: * Linux/x86 test with debian package libnet0-dev and libnet1-dev * Linux/sparc * Linux/hppa * Linux/x86-64 * IRIX/mips Need IRIX install CD to test this. * Solaris/sparc * NetBSD/alpha (is libnet or arping unaligned? -- libnet I think) * OpenBSD/sparc64 (libnet a bit buggy here) * */ #include #include #include #include #ifndef ETH_ALEN #define ETH_ALEN 6 #endif #ifndef ETH_P_IP #define ETH_P_IP 0x0800 #endif #if FREEBSD #include #include "freebsd.h" #endif #if MACOSX #include #include "freebsd.h" #endif #if USE_NETIF #include #include #endif #include #include #if OPENBSD #include "openbsd.h" #endif #if SOLARIS #include "solaris.h" #include "netinet/arp.h" #endif #if 0 #define DEBUG(a) a #else #define DEBUG(a) #endif const float version = 1.09; struct ether_addr *mymac; static u_char eth_xmas[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static u_char eth_null[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static u_char eth_target[ETH_ALEN]; static u_char eth_source[ETH_ALEN]; // only used in main() but it belongs here static struct timeval lastpacketsent; static const u_int ip_xmas = 0xffffffff; static pcap_t *pcap; //static struct bpf_program bpf_prog; //static struct in_addr net,mask; #if 0 // Use this if you want to hard-code a default interface static char *ifname = "eth0"; #else static char *ifname = NULL; #endif static u_int32_t dip = 0; static u_char *packet; static struct libnet_link_int *linkint; static unsigned int beep = 0; static unsigned int verbose = 0; static unsigned int numsent = 0; static unsigned int numrecvd = 0; static unsigned int searchmac = 0; static unsigned int finddup = 0; static unsigned int maxcount = -1; static unsigned int rawoutput = 0; static unsigned int alsototal = 0; static unsigned int quiet = 0; static unsigned int nullip = 0; static unsigned int is_promisc = 0; static unsigned int addr_must_be_same = 0; /* * It was unclear from msdn.microsoft.com if their scanf() supported * [0-9a-fA-F], so I'll stay away from it. */ static int is_mac_addr(const char *p) { if (4+1+4+1+4 == strlen(p)) { int c; for (c = 0; c < strlen(p); c++) { if ((9 == c) || (4 == c)) { if (!strchr(".-", p[c])) { goto checkcolon; } } else { if (!isxdigit(p[c])) { goto checkcolon; } } } return 1; } checkcolon: return strchr(p, ':') ? 1 : 0; } const char *arping_lookupdev_default(u_int32_t srcip, u_int32_t dstip, char *ebuf) { static const char *ifname; if (!(ifname = pcap_lookupdev(ebuf))) { return 0; } return ifname; } #if defined(FINDIF) && defined(linux) const char *arping_lookupdev(u_int32_t srcip, u_int32_t dstip, char *ebuf) { FILE *f; static char buf[1024]; char buf1[1024]; char buf2[1024]; char *p,*p2; int n; libnet_host_lookup_r(dstip,0,buf2); libnet_host_lookup_r(srcip,0,buf1); /* * Construct and run command */ snprintf(buf, 1023, "/sbin/ip route get %s from %s 2>&1", buf2,buf1); DEBUG(printf("%s\n",buf)); if (!(f = popen(buf, "r"))) { goto failed; } if (0 > (n = fread(buf, 1, sizeof(buf)-1, f))) { pclose(f); goto failed; } buf[n] = 0; if (-1 == pclose(f)) { perror("arping: pclose()"); goto failed; } /* * Parse out device */ p = strstr(buf, "dev "); if (!p) { goto failed; } p+=4; p2 = strchr(p, ' '); if (!p2) { goto failed; } *p2 = 0; return p; failed: return arping_lookupdev_default(srcip,dstip,ebuf); } #else const char *arping_lookupdev(u_int32_t srcip, u_int32_t dstip, char *ebuf) { return arping_lookupdev_default(srcip,dstip,ebuf); } #endif static void sigint(int i) { DEBUG(printf("sigint()\n")); if (!rawoutput) { if (searchmac) { u_char *cp=eth_target; int c; printf("\n--- "); for (c = 0; c < ETH_ALEN-1; c++) { printf("%.2x:", (u_char)*cp++); } printf("%.2x statistics ---\n", *cp); } else { printf("\n--- %s statistics ---\n", libnet_host_lookup(dip,0)); } printf("%d packets transmitted, %d packets received, %3.0f%% " "unanswered\n", numsent, numrecvd, 100.0 - 100.0 * (float)(numrecvd)/(float)numsent); } exit(i); } static void usage(int ret) { printf("ARPing %1.2f, by Thomas Habets \n", version); printf("usage: arping [ -0aAbdFpqrRuv ] [ -S ] " "[ -T ]\n" " [ -t ] [ -c ] [ -i ] " "\n"); exit(ret); } static void alasend(int i) { DEBUG(printf("alasend()\n")); if (numsent >= maxcount) { sigint(numrecvd ? 0 : 1); } numsent++; if (searchmac) { libnet_build_icmp_echo(ICMP_ECHO, /* type */ 0, /* code */ (short)rand(), /* id */ htons(numsent-1), /* seq */ NULL, /* pointer to payload */ 0, /* payload length */ /* header memory */ packet + LIBNET_ETH_H + LIBNET_IP_H); if (libnet_do_checksum(packet + LIBNET_ETH_H, IPPROTO_ICMP, LIBNET_ICMP_ECHO_H) == -1) { libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n"); } if (libnet_do_checksum(packet + LIBNET_ETH_H, IPPROTO_IP, LIBNET_IP_H) == -1) { libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n"); } } if (finddup && numrecvd) { sigint(0); } if (verbose > 1) { printf("Sending packet\n"); } if (-1 == (libnet_write_link_layer(linkint, (u_char*)ifname, (u_char*)packet, LIBNET_ARP_H + LIBNET_ETH_H))) { fprintf(stderr, "libnet_write_link_layer(): error\n"); exit(1); } if (gettimeofday(&lastpacketsent, NULL)) { fprintf(stderr, "arping: %s\n", strerror(errno)); exit(1); } alarm(1); DEBUG(fprintf(stderr, "Resetting timer\n")); #if SOLARIS signal(SIGALRM, alasend); #endif } /* * NOTE: not re-entrant */ static char* tvtoda(const struct timeval *tv, const struct timeval *tv2) { static char buf[128]; double f,f2; f = tv->tv_sec + (double)tv->tv_usec / 1000000; f2 = tv2->tv_sec + (double)tv2->tv_usec / 1000000; f = (f2 - f) * 1000000; if (f < 1000) { sprintf(buf, "%.3f usec", f); } else if (f < 1000000) { sprintf(buf, "%.3f msec", f / 1000); } else { sprintf(buf, "%.3f sec", f / 1000000); } return buf; } static void handlepacket(const char *unused, struct pcap_pkthdr *h, u_char *packet) { struct ethhdr *eth; struct arphdr *harp; struct iphdr *hip; struct icmphdr *hicmp; unsigned int c; unsigned char *cp; struct timeval recvtime; DEBUG(printf("handlepacket()\n")); if (gettimeofday(&recvtime, NULL)) { fprintf(stderr, "arping: %s\n", strerror(errno)); exit(1); } eth = (struct ethhdr*)packet; if (searchmac) { // ping mac hip = (struct iphdr*)((char*)eth + sizeof(struct libnet_ethernet_hdr)); hicmp = (struct icmphdr*)((char*)hip + sizeof(struct iphdr)); if ((htons(hicmp->type) == ICMP_ECHOREPLY) && ((!memcmp(eth->h_source, eth_target, ETH_ALEN) || !memcmp(eth_target, eth_xmas, ETH_ALEN))) && !memcmp(eth->h_dest, eth_source, ETH_ALEN)) { u_char *cp = eth->h_source; if (addr_must_be_same) { u_int32_t tmp; memcpy(&tmp, &hip->saddr, 4); if (dip != tmp) { return; } } numrecvd++; if (!rawoutput) { printf("%d bytes from ", h->len); } if (!quiet) { if (rawoutput & 2) { for (c = 0; c < 5; c++) { printf("%.2x:", *cp++); } printf("%.2x ", *cp++); } if (rawoutput & 1) { u_int32_t tmp; memcpy(&tmp, &hip->saddr, 4); printf("%s",libnet_host_lookup(tmp,0)); } if (!rawoutput) { /* * ugly code due to non-aligned saddr * (bus error on sparc) */ u_int32_t tmp; memcpy(&tmp, &hip->saddr, sizeof(u_int32_t)); printf("%s",libnet_host_lookup(tmp,0)); printf(" ("); for (c = 0; c < ETH_ALEN-1; c++) { printf("%.2x:", *cp++); } printf("%.2x): icmp_seq=%d time=%s", *cp, hicmp->un.echo.sequence, tvtoda(&lastpacketsent, &recvtime)); } if (beep) { printf("\a"); } printf("\n"); } } } else { /* ping ip */ harp = (struct arphdr*)((char*)eth + sizeof(struct libnet_ethernet_hdr)); if ((htons(harp->ar_op) == ARPOP_REPLY) && (htons(harp->ar_pro) == ETH_P_IP) && (htons(harp->ar_hrd) == ARPHRD_ETHER)) { u_int32_t ip; memcpy(&ip, (char*)harp + harp->ar_hln + sizeof(struct arphdr), 4); if (addr_must_be_same && (memcmp((u_char*)harp+sizeof(struct arphdr), eth_target, ETH_ALEN))) { return; } if (dip == ip) { cp = (u_char*)harp + sizeof(struct arphdr); if (!rawoutput && !finddup) { printf("%d bytes from ", h->len); } if (!quiet) { if (rawoutput & 1) { for (c = 0; c < harp->ar_hln-1; c++) { printf("%.2x:", *cp++); } printf("%.2x ", *cp++); } if (rawoutput & 2) { printf("%s", libnet_host_lookup(ip, 0)); } if (!rawoutput) { for (c = 0; c < harp->ar_hln-1; c++) { printf("%.2x:", *cp++); } printf("%.2x", *cp); } if (!rawoutput) { printf(" (%s): index=%d", libnet_host_lookup(ip,0), numrecvd); if (alsototal) { printf("/%u",numsent-1); } printf(" time=%s", tvtoda(&lastpacketsent, &recvtime)); } if (beep) { printf("\a"); } printf("\n"); } numrecvd++; } } } } static void recvpackets(void) { DEBUG(printf("recvpackets()\n")); if (-1 == pcap_loop(pcap, -1, (pcap_handler)handlepacket, NULL)) { fprintf(stderr, "pcap_loop(): error\n"); exit(1); } // does not return } int main(int argc, char **argv) { u_long myip = 0; char ebuf[LIBNET_ERRBUF_SIZE]; int c; struct bpf_program bp; char must_be_pingip = 0; char have_eth_source = 0; unsigned int n[6]; int dont_use_arping_lookupdev=0; DEBUG(printf("main()\n")); strncpy(ebuf, "no error", LIBNET_ERRBUF_SIZE); ebuf[LIBNET_ERRBUF_SIZE -1 ] = 0; memcpy(eth_target, eth_xmas, ETH_ALEN); while ((c = getopt(argc, argv, "0aAbBc:dFhi:I:pqrRs:S:t:T:uv")) != EOF) { switch (c) { case 'A': addr_must_be_same = 1; break; case 'a': beep = 1; break; case 'c': maxcount = atoi(optarg); break; case 'd': finddup = 1; break; case 'F': #if !defined(FINDIF) fprintf(stderr, "arping: find-interface support not " "compiled in\n"); #endif dont_use_arping_lookupdev=1; break; case 'u': alsototal=1; break; case 'v': verbose++; break; case 'h': usage(0); case 'i': if (strchr(optarg, ':')) { fprintf(stderr, "arping: If you're trying to " "feed me an interface alias then you " "don't really\nknow what this programs" " does, do you?\nUse -I if you really" " mean it (undocumented on " "purpose)\n"); exit(1); } case 'I': /* FALL THROUGH */ ifname = optarg; break; case 'r': rawoutput |= 1; break; case 'R': rawoutput |= 2; break; case 'q': quiet = rawoutput = 1; break; case 'S': if (-1==(myip = libnet_name_resolve(optarg, LIBNET_RESOLVE))){ fprintf(stderr,"arping: Can't to resolve %s\n", optarg); exit(1); } if (!myip) { nullip = 1; } break; case 'T': // destination IP in mac ping (default: 0xffffffff) if (-1 == (dip = libnet_name_resolve(optarg, LIBNET_RESOLVE))){ fprintf(stderr, "arping: can't resolve: %s\n", optarg); exit(1); } searchmac = 1; break; case 'b': myip = 0xffffffff; break; case 'B': dip = 0xffffffff; break; case '0': nullip = 1; break; case 's': // spoofed source MAC if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { ; } else if(sscanf(optarg, "%2x%2x.%2x%2x.%2x%2x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { ; } else { fprintf(stderr, "arping: Illegal MAC addr " "%s\n", optarg); exit(1); } for (c = 0; c < 6; c++) { eth_source[c] = n[c] & 0xff; } have_eth_source = 1; break; case 't': must_be_pingip = 1; if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { ; } else if(sscanf(optarg, "%2x%2x.%2x%2x.%2x%2x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { ; } else { fprintf(stderr,"arping: Illegal MAC addr %s\n", optarg); exit(1); } for (c = 0; c < 6; c++) { eth_target[c] = n[c] & 0xff; } break; case 'p': is_promisc = 1; break; default: usage(1); } } if (!searchmac && !dip && (optind + 1 != argc)) { usage(1); } else if (searchmac && (optind + 1 != argc)) { usage(1); } if (getuid() && geteuid()) { fprintf(stderr, "arping: must run as root\n"); return 1; } if (myip && nullip) { fprintf(stderr, "-S, -b and -0 are mutually exclusive\n"); exit(1); } if (is_mac_addr(argv[optind]) && sscanf(argv[optind], "%x:%x:%x:%x:%x:%x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { searchmac = 1; } else if(is_mac_addr(argv[optind]) && sscanf(argv[optind], "%2x%2x.%2x%2x.%2x%2x", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] ) == 6) { searchmac = 1; } else if (!dip) { if (-1 == (dip=libnet_name_resolve((u_char*)argv[optind], LIBNET_RESOLVE))) { fprintf(stderr, "arping: Can't resolve %s\n", argv[optind]); exit(1); } } else if (must_be_pingip) { fprintf(stderr, "arping: Illegal IP %s\n", argv[optind]); exit(1); } else { fprintf(stderr, "arping: Illegal mac %s\n", argv[optind]); exit(1); } if (searchmac && !dip) { dip = ip_xmas; } if (searchmac && must_be_pingip) { fprintf(stderr, "arping: Specified switch can't be used in " "MAC-ping mode\n"); exit(1); } if (searchmac) { for (c = 0; c < 6; c++) { eth_target[c] = n[c] & 0xff; } } if (finddup && maxcount == -1) { maxcount = 3; } /* * libnet init */ if (!ifname) { if (dont_use_arping_lookupdev) { ifname = arping_lookupdev_default(myip,dip,ebuf); } else { ifname = arping_lookupdev(myip,dip,ebuf); } if (!ifname) { fprintf(stderr,"arping_lookupdev(): %s\n", ebuf); exit(1); } // FIXME: check for other probably-not interfaces if (!strcmp(ifname, "ipsec") || !strcmp(ifname, "lo")) { fprintf(stderr, "arping: Um.. %s looks like the wrong " "interface to use. Is it? " "(-i switch)\n", ifname); fprintf(stderr, "arping: using it anyway this time\n"); } } if (!(linkint = libnet_open_link_interface(ifname, ebuf))) { fprintf(stderr, "libnet_open_link_interface(): %s\n", ebuf); exit(1); } if (!have_eth_source) { if (!(mymac = libnet_get_hwaddr(linkint, (u_char*)ifname, ebuf))) { fprintf(stderr, "libnet_get_hwaddr(): %s\n", ebuf); exit(1); } memcpy(eth_source, mymac->ether_addr_octet, ETH_ALEN); have_eth_source = 1; } if (nullip) { myip = 0; } else if (myip) { // myip set, don't touch it } else if (!(myip = htonl(libnet_get_ipaddr(linkint,(u_char*)ifname, ebuf)))) { fprintf(stderr, "libnet_get_ipaddr(): %s\n", ebuf); exit(1); } if (searchmac) { if (-1 == libnet_init_packet(LIBNET_ETH_H + LIBNET_IP_H + LIBNET_ICMP_ECHO_H, &packet)) { fprintf(stderr, "libnet_init_packet(): error\n"); exit(1); } } else { /* * this makes it work on solaris. * not sure if LIBNET_ICMP_H is needed though, but it works */ if (-1 == libnet_init_packet(LIBNET_ETH_H + LIBNET_ARP_H + LIBNET_ICMP_H, &packet)) { fprintf(stderr, "libnet_init_packet(): error\n"); exit(1); } } if (verbose) { printf("This box: Interface: %s IP: %s MAC address: ", ifname, libnet_host_lookup(myip,0)); for (c = 0; c < ETH_ALEN - 1; c++) { printf("%.2x:", (unsigned )eth_source[c]); } printf("%.2x\n", eth_source[ETH_ALEN - 1]); } if (searchmac) { // ping MAC /* * KEYWORD * What the hell was I thinking when I wrote the comment below? * ------- * note: it's eth_xmas below, that's a feature. I don't want * a -t line to affect a MAC ping (even though it can't * since the lone arg is written last). * ------- * Phew! Anyway, change eth_target to eth_xmas three lines * below to change it back. */ if (-1 == libnet_build_ethernet(eth_target, /* <---- here */ eth_source, ETHERTYPE_IP, NULL, 0, packet)) { fprintf(stderr, "libnet_build_ethernet(): error\n"); exit(1); } libnet_build_ip(ICMP_ECHO_H, /* Size of the payload */ 0, /* IP tos */ rand(), /* IP ID */ 0, /* frag stuff */ 48, /* TTL */ IPPROTO_ICMP, /* transport protocol */ myip, /* source IP */ dip, /* destination IP */ NULL, /* pointer to payload */ 0, /* payload length */ packet + LIBNET_ETH_H); /* header memory */ libnet_build_icmp_echo(ICMP_ECHO, /* type */ 0, /* code */ 4321, /* id */ 6, /* seq */ NULL, /* pointer to payload */ 0, /* payload length */ /* header memory */ packet + LIBNET_ETH_H + LIBNET_IP_H); } else { // ping ip if (-1 == libnet_build_ethernet(eth_target, // usually xmas eth_source, // this box ETHERTYPE_ARP, NULL, 0, packet)) { fprintf(stderr, "libnet_build_ethernet(): error\n"); exit(1); } if (-1 == libnet_build_arp(ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, ARPOP_REQUEST, eth_source, (u_char*)&myip, eth_null, (u_char*)&dip, NULL, 0, packet + LIBNET_ETH_H)) { fprintf(stderr, "libnet_build_arp(): error\n"); exit(1); } } /* * pcap init */ if (!(pcap = pcap_open_live(ifname, 100, is_promisc, 10, ebuf))) { fprintf(stderr, "pcap_open_live(): %s\n", ebuf); exit(1); } if (searchmac) { if (-1 == pcap_compile(pcap,&bp,"icmp",0,-1)) { fprintf(stderr, "pcap_compile(): error\n"); exit(1); } } else { if (-1 == pcap_compile(pcap,&bp,"arp",0,-1)) { fprintf(stderr, "pcap_compile(): error\n"); exit(1); } } if (-1 == pcap_setfilter(pcap, &bp)) { fprintf(stderr, "pcap_setfilter(): error\n"); exit(1); } /* * main program */ signal(SIGALRM, alasend); signal(SIGINT, sigint); if (!rawoutput) { if (searchmac) { printf("ARPING %s\n", argv[optind]); } else { printf("ARPING %s\n", libnet_host_lookup(dip,0)); } } alasend(0); for(;;) { recvpackets(); } libnet_destroy_packet(&packet); return 0; }