/* * arping.c * * 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. * * Authors: Alexey Kuznetsov, * 2006.08.18 erik quanstrom * use select instead of signals so timeouts will work. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SNAPSHOT.h" static void usage(void) __attribute__((noreturn)); char *device="eth0"; int ifindex; char *source; struct in_addr src, dst; char *target; int dad, unsolicited, advert; int quiet; int count=-1; int ntosend=-1; int ntorecv=-1; int timeout; struct timeval timeouttv; int unicasting; int s; int broadcast_only; struct sockaddr_ll me; struct sockaddr_ll he; struct timeval start, last; int sent, brd_sent; int received, brd_recv, req_recv; #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ ((tv1).tv_usec-(tv2).tv_usec)/1000 ) #define pkttrace(...) /* fprintf(stderr, __VA_ARGS__) */ void errexit(int err, const char* fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(err); } void usage(void) { fprintf(stderr, "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n" " -f : quit on first reply\n" " -q : be quiet\n" " -b : keep broadcasting, don't go unicast\n" " -D : duplicate address detection mode\n" " -U : Unsolicited ARP mode, update your neighbours\n" " -A : ARP answer mode, update your neighbours\n" " -V : print version and exit\n" " -c count : how many packets to send\n" " -w timeout : how long to wait for a reply\n" " -I device : which ethernet device to use (eth0)\n" " -s source : source ip address\n" " destination : ask for what ip address\n" ); exit(2); } int send_pack(int s, struct in_addr src, struct in_addr dst, struct sockaddr_ll *ME, struct sockaddr_ll *HE) { int err; struct timeval now; unsigned char buf[256]; struct arphdr *ah = (struct arphdr*)buf; unsigned char *p = (unsigned char *)(ah+1); ah->ar_hrd = htons(ME->sll_hatype); if (ah->ar_hrd == htons(ARPHRD_FDDI)) ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ME->sll_halen; ah->ar_pln = 4; ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); memcpy(p, &ME->sll_addr, ah->ar_hln); p+=ME->sll_halen; memcpy(p, &src, 4); p+=4; if (advert) memcpy(p, &ME->sll_addr, ah->ar_hln); else memcpy(p, &HE->sll_addr, ah->ar_hln); p+=ah->ar_hln; memcpy(p, &dst, 4); p+=4; gettimeofday(&now, NULL); pkttrace("sendto->\n"); err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, sizeof(*HE)); pkttrace("sendto<-\n"); if (err == p-buf) { last = now; sent++; if (!unicasting) brd_sent++; } return err; } void finish(void) { if (!quiet) { fflush(stdout); fprintf(stderr, "Sent %d probes (%d broadcast(s))\n", sent, brd_sent); fprintf(stderr, "Received %d response(s)", received); if (brd_recv || req_recv) { fprintf(stderr, " ("); if (req_recv) fprintf(stderr, "%d request(s)", req_recv); if (brd_recv) fprintf(stderr, "%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); fprintf(stderr, ")"); } fprintf(stderr, "\n"); } if (dad) exit(!!received); if (unsolicited) exit(0); exit(!received); } void print_hex(unsigned char *p, int len) { int i; for (i=0; isll_pkttype != PACKET_HOST && FROM->sll_pkttype != PACKET_BROADCAST && FROM->sll_pkttype != PACKET_MULTICAST) return 0; /* Only these types are recognised */ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) return 0; /* ARPHRD check and this darned FDDI hack here :-( */ if (ah->ar_hrd != htons(FROM->sll_hatype) && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) return 0; /* Protocol must be IP. */ if (ah->ar_pro != htons(ETH_P_IP)) return 0; if (ah->ar_pln != 4) return 0; if (ah->ar_hln != me.sll_halen) return 0; if (len < sizeof(*ah) + 2*(4 + ah->ar_hln)) return 0; memcpy(&src_ip, p+ah->ar_hln, 4); memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); if (!dad) { if (src_ip.s_addr != dst.s_addr) return 0; if (src.s_addr != dst_ip.s_addr) return 0; if (memcmp(p+ah->ar_hln+4, &me.sll_addr, ah->ar_hln)) return 0; } else { /* DAD packet was: src_ip = 0 (or some src) src_hw = ME dst_ip = tested address dst_hw = We fail, if receive request/reply with: src_ip = tested_address src_hw != ME if src_ip in request was not zero, check also that it matches to dst_ip, otherwise dst_ip/dst_hw do not matter. */ if (src_ip.s_addr != dst.s_addr) return 0; if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) return 0; if (src.s_addr && src.s_addr != dst_ip.s_addr) return 0; } if (!quiet) { int s_printed = 0; printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast"); printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); printf("%s [", inet_ntoa(src_ip)); print_hex(p, ah->ar_hln); printf("] "); if (dst_ip.s_addr != src.s_addr) { printf("for %s ", inet_ntoa(dst_ip)); s_printed = 1; } if (memcmp(p+ah->ar_hln+4, me.sll_addr, ah->ar_hln)) { if (!s_printed) printf("for "); printf("["); print_hex(p+ah->ar_hln+4, ah->ar_hln); printf("]"); } if (last.tv_sec) { long usecs = (tv.tv_sec-last.tv_sec) * 1000000 + tv.tv_usec-last.tv_usec; long msecs = (usecs+500)/1000; usecs -= msecs*1000 - 500; printf(" %ld.%03ldms\n", msecs, usecs); } else { printf(" UNSOLICITED?\n"); } fflush(stdout); } received++; if (FROM->sll_pkttype != PACKET_HOST) brd_recv++; if (ah->ar_op == htons(ARPOP_REQUEST)) req_recv++; if(!broadcast_only) { memcpy(he.sll_addr, p, me.sll_halen); unicasting=1; } return 1; } unsigned char packet[4096]; // too big for stack on some arch. void pktloop(void) { struct sockaddr_ll from; socklen_t alen = sizeof(from); fd_set rset; struct timeval tmo; int cc, i; for(;;){ if(ntosend-- == 0) break; send_pack(s, src, dst, &me, &he); gettimeofday(&tmo, 0); if(timeout) i = MS_TDIFF(timeouttv, tmo) ; else i = 1000; if(i < 0) break; if(i > 1000) i = 1000; // maximum select 1s. tmo.tv_sec = i/1000; tmo.tv_usec = (i%1000)*1000; FD_ZERO(&rset); FD_SET(s, &rset); pkttrace("select %d ; %d\n", (int)tmo.tv_sec, (int)tmo.tv_usec); i = select(s+1, &rset, 0, 0, &tmo); pkttrace("<- select\n"); if(i == -1){ perror("arping: select"); exit(1); } if(i == 0){ gettimeofday(&tmo, 0); i = MS_TDIFF(timeouttv, tmo); if(timeout && i <= 0) break; continue; } if ((cc = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&from, &alen)) < 0) { perror("arping: recvfrom"); continue; } if(recv_pack(packet, cc, &from)) if(--ntorecv == 0) break; } } int main(int argc, char **argv) { int ch, onereply; while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:V")) != EOF) { switch(ch) { case 'b': broadcast_only = 1; break; case 'D': dad++; onereply = 1; break; case 'U': unsolicited++; break; case 'A': advert++; unsolicited++; break; case 'q': quiet++; break; case 'c': count = atoi(optarg); break; case 'w': gettimeofday(&timeouttv, 0); timeouttv.tv_sec += atoi(optarg); timeout = 1; break; case 'I': device = optarg; break; case 'f': onereply = 1; break; case 's': source = optarg; break; case 'V': printf("arping utility, iputils-ss%s\n", SNAPSHOT); exit(0); case 'h': case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); target = *argv; if(onereply) ntorecv = 1; if(timeout) // strange ping compatability. ntorecv = count; else ntosend = count; if (device == 0) { fprintf(stderr, "arping: device (option -I) is required\n"); usage(); } s = socket(PF_PACKET, SOCK_DGRAM, 0); if (s < 0) { perror("arping: socket"); exit(2); } setuid(getuid()); if (1) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, IFNAMSIZ-1); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { fprintf(stderr, "arping: unknown iface %s\n", device); exit(2); } ifindex = ifr.ifr_ifindex; if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) { perror("arping: ioctl(SIOCGIFFLAGS)"); exit(2); } if (!(ifr.ifr_flags&IFF_UP)) errexit(2, "Interface \"%s\" is down\n", device); if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) errexit(dad ? 0 : 2, "Interface \"%s\" is not ARPable\n", device); } if (inet_aton(target, &dst) != 1) { struct hostent *hp; hp = gethostbyname2(target, AF_INET); if (!hp) { fprintf(stderr, "arping: unknown host %s\n", target); exit(2); } memcpy(&dst, hp->h_addr, 4); } if (source && inet_aton(source, &src) != 1) { fprintf(stderr, "arping: invalid source %s\n", source); exit(2); } if (!dad && unsolicited && src.s_addr == 0) src = dst; if (!dad || src.s_addr) { struct sockaddr_in saddr; int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); if (probe_fd < 0) { perror("arping: socket"); exit(2); } if (device) { if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) perror("WARNING: interface is ignored"); } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (src.s_addr) { saddr.sin_addr = src; if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("arping: bind"); exit(2); } } else if (!dad) { int on = 1; socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) perror("WARNING: setsockopt(SO_DONTROUTE)"); if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("arping: connect"); exit(2); } if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { perror("arping: getsockname"); exit(2); } src = saddr.sin_addr; } close(probe_fd); }; me.sll_family = AF_PACKET; me.sll_ifindex = ifindex; me.sll_protocol = htons(ETH_P_ARP); if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { perror("arping: bind"); exit(2); } if (1) { socklen_t alen = sizeof(me); if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { perror("arping: getsockname"); exit(2); } } if (me.sll_halen == 0) errexit(dad ? 0 : 2, "Interface \"%s\" is not ARPable (no ll address)\n", device); he = me; memset(he.sll_addr, -1, he.sll_halen); if (!quiet) { fprintf(stderr, "ARPING %s ", inet_ntoa(dst)); fprintf(stderr, "from %s %s\n", inet_ntoa(src), device ? : ""); } if (!src.s_addr && !dad) errexit(2, "arping: no source address in not-DAD mode\n"); pktloop(); finish(); return 1; // shut up gcc. }