/* * $Id: src/arpdig/doarp.c,v 1.25 2006/05/06 08:43:01 marck Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arpdig.h" static int verbose = 0; static struct _iparp *parp; static int narp; static int bpfopen(void) { char devnm[BPFLEN]; int i, fd; for (i=0; iether_type = htons(ETHERTYPE_ARP); memcpy(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)); ah = (struct arphdr *)(buf + sizeof(struct ether_header)); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ETHER_ADDR_LEN; /* we're always at ethernet */ ah->ar_pln = sizeof(struct in_addr); ah->ar_op = htons(ARPOP_REQUEST); memcpy(ar_sha(ah), enaddr, ETHER_ADDR_LEN); memset(ar_tha(ah), 0, ETHER_ADDR_LEN); memcpy(ar_spa(ah), sip, sizeof(struct in_addr)); memcpy(ar_tpa(ah), tip, sizeof(struct in_addr)); res = write(fd, buf, sizeof(buf)); return (res == sizeof(buf)) ? 0 : -1; } static pcap_t *pcap_setup(char *ifname, char *errb) { pcap_t *p; struct bpf_program bp; static char bpf_filt[] = "arp"; if ((p = pcap_open_live(ifname, 100, 0, 10, errb)) == NULL) return NULL; if (pcap_compile(p, &bp, bpf_filt, 0, -1) == -1) return NULL; if (pcap_setfilter(p, &bp) == -1) return NULL; return p; } /* buf should be at least 3*6 bytes long */ static u_char *printhw(u_char *buf, u_char *hwaddr) { char *s = (char *)buf; int i; for (i=0; i MAXARPOP) { sprintf(buf, "op %d", op); return buf; } else return arpops[op]; } static void rechwaddr(struct in_addr ip, u_char *hw) { int i; for (i = 0; i < narp; i++) if (parp[i].addr.s_addr == ip.s_addr) { memcpy(parp[i].hwaddr, hw, ETHER_ADDR_LEN); parp[i].recd++; return; } /* shouldn't be reached */ } /* ARGSUSED */ static void recvp(u_char *userp, const struct pcap_pkthdr *phdr, const u_char *packet) { struct ether_header *eh; struct arphdr *ah; struct timeval tv, *et; u_char buf[18]; int op; int pline; eh = (struct ether_header *)packet; ah = (struct arphdr *)(&packet[sizeof(struct ether_header)]); gettimeofday(&tv, NULL); et = (struct timeval *)userp; pline = 0; op = ntohs(ah->ar_op); if (verbose) { printf("%02d:%02d.%03d ARP ", (int)((tv.tv_sec%3600)/60), (int)(tv.tv_sec%60), (int)(tv.tv_usec/1000) ); printf("%s ", printarop(op)); pline = 1; } switch (op) { case ARPOP_REQUEST: if (!verbose) break; printf("%s whois ", inet_ntoa(*(struct in_addr*)ar_spa(ah))); printf("%s ?", inet_ntoa(*(struct in_addr*)ar_tpa(ah))); pline = 1; break; case ARPOP_REPLY: rechwaddr(*(struct in_addr*)ar_spa(ah), (u_char *)ar_sha(ah)); if (!verbose) break; printf("%s -> ", inet_ntoa(*(struct in_addr*)ar_spa(ah))); printf("%s is ", inet_ntoa(*(struct in_addr*)ar_tpa(ah))); printf("%s", printhw(buf, (u_char *)ar_sha(ah))); pline = 1; break; default: if (!verbose) break; printf("%s -> ", printhw(buf, (u_char *)ar_sha(ah))); printf("%s", printhw(buf, (u_char *)ar_tha(ah))); pline = 1; } if (pline) printf("\n"); } static char *filladdr(char *ifname, unsigned char hwaddr[ETHER_ADDR_LEN]) { struct ifaddrs *ifap, *p; struct sockaddr *sa; if (getifaddrs(&ifap) == -1) return NULL; for (p = ifap; p; p = p->ifa_next) { if (ifname == NULL) ifname = strdup(p->ifa_name); if (strncmp(p->ifa_name,ifname,IFNAMSIZ) != 0) continue; if ((sa = p->ifa_addr) != NULL && sa->sa_family == AF_LINK) { const struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *)sa; if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == ETHER_ADDR_LEN) { memcpy(hwaddr, LLADDR(sdl), ETHER_ADDR_LEN); break; } } } freeifaddrs(ifap); return (p) ? ifname : NULL; } static void parpres(void) { int i; u_char buf[3*ETHER_ADDR_LEN]; struct hostent *he; for (i = 0; i < narp; i++) { if (parp[i].recd) { printf("%s %s", inet_ntoa(parp[i].addr), printhw(buf, parp[i].hwaddr)); if (!noresolve) { he = gethostbyaddr((char *)&parp[i].addr, sizeof(struct in_addr), AF_INET); printf(" (%s)", he ? he->h_name : "?"); } printf("\n"); } else if (verbose) printf("%s -\n", inet_ntoa(parp[i].addr)); } } static int wd_expired = 0; /* ARGSUSED */ static void watchdog(int sig) { wd_expired = 1; } int arpdig(char *ifname, struct in_addr if_ia, struct in_addr ipstart, int iplen) { int fd, nbuf, level; u_long addr, startaddr, endaddr; u_char hwaddr[ETHER_ADDR_LEN]; char errb[PCAP_ERRBUF_SIZE]; pcap_t *p; struct timeval ct, et, st; /* current, emitted and start time */ struct _iparp *pa; if ((ifname = filladdr(ifname, hwaddr)) == NULL) err(1, "Can't obtain own lladdr for %s", ifname); /* become suser again */ if (getuid() == 0) seteuid(0); p = pcap_setup(ifname, errb); if (p == NULL) errx(1, "Can't setup capture: %s", errb); fd = ifattach(ifname); if (fd == -1) err(1, "Can't attach to %s", ifname); /* paranoia */ if (getuid() == 0) seteuid(-1); /* make a list of addresses to check */ startaddr = ntohl(ipstart.s_addr); if (iplen == 32) { startaddr = ntohl(ipstart.s_addr); endaddr = startaddr + 1; } else { startaddr++; endaddr = ntohl(ipstart.s_addr) | ~((u_long)-1 << (32-iplen)); } narp = 0; nbuf = DEFNARP; parp = malloc(sizeof(struct _iparp)*nbuf); if (parp == NULL) err(1, "Out of memory"); for (addr = startaddr; addr < endaddr; addr++) { struct in_addr sa; sa.s_addr = htonl(addr); if (narp == nbuf - 1) { nbuf *= 2; parp = realloc(parp, sizeof(struct _iparp)*nbuf); if (parp == NULL) err(1, "Out of memory"); } pa = &parp[narp++]; memset(pa, 0, sizeof(struct _iparp)); pa->addr = sa; } signal(SIGALRM, watchdog); alarm(WDSECS); /* loop for it */ gettimeofday(&st, NULL); et = st; pa = parp; level = 0; for(;;) { if (wd_expired) break; gettimeofday(&ct, NULL); /* XXX need to restrict emitter!!! */ /* find address to arping */ while (pa < &parp[narp] && pa->emitted > level) pa++; if (pa == &parp[narp]) { level++; if (level >= MAXTRIES) break; pa = parp; usleep(100); } /* emit a packet */ if (arpreq(fd, &if_ia, &pa->addr, hwaddr) == -1) err(1, "arpreq"); pa->emitted++; /* and listen for the answers... */ pcap_dispatch(p, 1, recvp, (void *)&et); /* sleep a bit as a guard */ usleep(100); } alarm(0); close(fd); pcap_close(p); parpres(); return 0; } /* End of $RCSfile: doarp.c,v $ */