/*
* $Id: src/arpdig/doarp.c,v 1.25 2006/05/06 08:43:01 marck Exp $
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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; i<MAXBPFN; i++) {
snprintf(devnm, BPFLEN, "/dev/bpf%d", i);
if (verbose)
printf("Opening %s...", devnm);
if ((fd = open(devnm, O_RDWR)) != -1) {
if (verbose)
printf(" ok\n");
return fd;
}
if (verbose)
printf(" got %d (errno=%d)\n", fd, errno);
if (errno != EBUSY)
return -1;
}
return -1;
}
static int ifattach(char *ifname)
{
int fd;
struct ifreq ifr;
if ((fd = bpfopen()) == -1)
return -1;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, &ifr) == -1)
return -1;
return fd;
}
/* mostly stolen from if_ether.c:arprequest() */
static int arpreq(int fd, struct in_addr *sip, struct in_addr *tip,
u_char *enaddr)
{
u_char buf[sizeof(struct ether_header) + sizeof(struct arphdr) +
2*(sizeof(struct in_addr) + ETHER_ADDR_LEN)];
static u_char etherbroadcastaddr[ETHER_ADDR_LEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct arphdr *ah;
struct ether_header *eh;
int res;
eh = (struct ether_header *)buf;
eh->ether_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<ETHER_ADDR_LEN; i++) {
s += sprintf(s, "%02x", (int)*hwaddr++);
if (i < ETHER_ADDR_LEN-1)
*s++ = ':';
}
return buf;
}
static char const *printarop(u_short op)
{
static char const *arpops[] = { "OP 0", "REQUEST", "REPLY", "REVREQUEST",
"REVREPLY", "OP 5", "OP 6", "OP 7",
"INVREQUEST", "INVREPLY" };
static char buf[15];
if (op > 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 $ */
syntax highlighted by Code2HTML, v. 0.9.1