For my latest project I needed to get the MAC address (ethernet hardware address) of a machine on my LAN given the IP of the machine. I did a lot of googling and found no solution which returned the address quickly. Most people said to use the ‘arp’ command but this was very slow and didn’t always work.
I knew from writing a packet sniffer in the past that every packet destined for a local machine had to have its MAC address in the ethernet header portion of the packet. So I decided to write my own tool to get the job done. Here’s the source:
#include <pcap.h>
#include <stdlib.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
int child_pid = 0;
void print_eth_addr(void *addr) {
printf("%s\n", ether_ntoa((struct ether_addr *)addr));
kill(child_pid, 9);
exit(0);
}
void find_eth_addr(struct in_addr *search_ip, const struct pcap_pkthdr* pkthdr, const u_char *packet) {
struct ether_header *eth_hdr = (struct ether_header *)packet;
if (ntohs(eth_hdr->ether_type) == ETHERTYPE_IP) {
struct ip *ip_hdr = (struct ip *)(packet + sizeof(struct ether_header));
if (ip_hdr->ip_dst.s_addr == search_ip->s_addr)
print_eth_addr(eth_hdr->ether_dhost);
if (ip_hdr->ip_src.s_addr == search_ip->s_addr)
print_eth_addr(eth_hdr->ether_shost);
}
}
int main(int argc, char **argv)
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
pcap_t* descr;
bpf_u_int32 maskp, netp;
if (argc < 2) {
printf("Usage: %s <ip> [interface]\n", argv[0]);
return 1;
}
if (argc == 2 && !(dev = pcap_lookupdev(errbuf))) {
fprintf(stderr, "%s\n", errbuf); return 1;
} else if (argc == 3) {
dev = argv[2];
}
pcap_lookupnet(dev,&netp,&maskp,errbuf);
if (!(descr = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf))) {
printf("pcap_open_live(): %s\n", errbuf); return 1;
}
struct in_addr search_ip;
if (!inet_aton(argv[1], &search_ip)) {
fprintf(stderr, "bad ip\n"); exit(1);
}
int pid = fork();
if (pid == 0) {
while (1) {
struct sockaddr_in sin;
sin.sin_family = PF_INET;
inet_aton(argv[1], &sin.sin_addr);
sin.sin_port = htons(1);
int s = socket(PF_INET, SOCK_STREAM, 0);
connect(s, (struct sockaddr *)&sin, sizeof(sin));
usleep(100000);
}
} else {
child_pid = pid;
pcap_loop(descr, -1, (pcap_handler)find_eth_addr, (void *)&search_ip);
}
return 0;
}
Compile it with gcc -o getmac getmac.c -lpcap. You’ll need libpcap and the pcap’s header files to compile it. apt-get install libpcap libpcap-dev should get you them.
You’ll also need to run it with sudo since you need root privileges to be able to open a network device in promiscuous mode: sudo ./getmac 192.168.0.5 or sudo ./getmac 192.168.1.84 eth1 to specify a specific interface.

I get a “Segment violation” message… :(
I tried to run it on Debian Etch/amd64 as root.
gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
No compilation problems.
Thanks
Comment by Zaphir — May 9, 2008 @ 4:14 am
interesting… you should compile it with debug info and run it with gdb to see where its dieing…
gcc -g -o getmac getmac.c -lpcap
sudo gdb ./getmac
set args IP INTERFACE
run
it should then tell you where it’s crashing
Comment by coderrr — May 9, 2008 @ 12:34 pm
The arp protocol is not slow and unreliable as it is the underlying protocol for basically all netowrking operations to determine the table of MAC – IP
If you wrote it right I’m sure it would work.
Comment by Robin — September 14, 2009 @ 3:46 am