/*
 * arpdump.c
 *
 * Dump Ethernet ARP traffic.
 * Steve Buer -- Oct 2001
 * <sabuer@syr.edu>
 *
 * 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.
 * 
 */

#include <pcap.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <string.h>

/* ARP protocol opcodes. */

#define ARPOP_REQUEST   1               /* ARP request.  */
#define ARPOP_REPLY     2               /* ARP reply.  */
#define ARPOP_RREQUEST  3               /* RARP request.  */
#define ARPOP_RREPLY    4               /* RARP reply.  */

struct arphdr {

	unsigned short int ar_hrd;          /* Format of hardware address.  */
	unsigned short int ar_pro;          /* Format of protocol address.  */
	unsigned char ar_hln;               /* Length of hardware address.  */
	unsigned char ar_pln;               /* Length of protocol address.  */
	unsigned short int ar_op;           /* ARP opcode (command).  */

	// Ethernet Specific
    	unsigned char ar_sha[ETH_ALEN];     /* Sender hardware address.  */
    	unsigned char ar_sip[4];            /* Sender IP address.  */
    	unsigned char ar_tha[ETH_ALEN];     /* Target hardware address.  */
    	unsigned char ar_tip[4];            /* Target IP address.  */

};

int debug = 0;
int print_ether = 0;
char buf[sizeof("00:00:00:00:00:00")];

void arp_print(u_char *, const struct pcap_pkthdr *, const u_char *);
char *etheraddr_string(const u_char *);

void usage() 
{
	fprintf(stderr, "Usage: arpdump [-e] [-h] [-c count] -i <interface>\n");
	fprintf(stderr, "	-e print ether addresses\n");
	fprintf(stderr, "	-i <interface>\n");
	fprintf(stderr, "	-h	help\n");
}

int main(int argc, char **argv)
{

	int x, n;
	u_int16_t theSnaplen 	= 128;
	int errflg 		= 0;
	int count 		= -1;
	char ebuf[128];
	pcap_t *pcap;
	u_char *pcap_userdata;
	char *theInterface 	= NULL;

	while (x = getopt(argc, argv, "i:he"),x != EOF) {

		switch (x) {

			case 'e':

				print_ether = 1;
				break;

			case 'i':

				theInterface = optarg;
				break;

			case 'h':

				errflg++;
				break;

			default: 
			
				errflg++;
				break;

		} // switch 
			
	} // while

	if (errflg) {

		usage();
		exit(1);

	}

	if (!theInterface) {

		theInterface = pcap_lookupdev(ebuf);

		if (theInterface == NULL) {

			fprintf(stderr, "pcap_lookupdev(): %s\n:", ebuf);
			exit(1);

		}

	}

	printf("Listening on: %s\n", theInterface);

	pcap = pcap_open_live(theInterface, theSnaplen, 1, 0, ebuf);

	if (pcap == NULL) {

		fprintf(stderr, "pcap_open_live(): %s\n", ebuf);
		exit(1);

	}

	if (pcap_datalink(pcap) != DLT_EN10MB) {

		fprintf(stderr, "Link layer must be Ethernet.\n");
		exit(1);
	
	}

	if (pcap_loop(pcap, count, arp_print, pcap_userdata) < 0) {

		fprintf(stderr, "pcap_loop(): %s\n", pcap_geterr(pcap));
		exit(1);

	}

	return 0;

} 

/////////////////////////////////////////////////
// Callback function for handling packets
//

void arp_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{

	struct ether_header *eth;
	struct arphdr *arp;
	struct in_addr saddr;
	struct in_addr daddr;

	eth = (struct ether_header *)p;
	arp = (struct arphdr *)(p + ETHER_HDR_LEN);

	if (ntohs(eth->ether_type) != ETHERTYPE_ARP)
		return;

	if (print_ether) {

		printf("%s ", etheraddr_string(eth->ether_shost));
		printf("%s ", etheraddr_string(eth->ether_dhost));

	}

	bzero(&saddr, sizeof(struct in_addr));
	bzero(&daddr, sizeof(struct in_addr));

	switch (ntohs(arp->ar_op)) {

		case ARPOP_REQUEST:

			bcopy(arp->ar_tip, &daddr.s_addr, 4);
			bcopy(arp->ar_sip, &saddr.s_addr, 4);

			printf("arp who has %s ", inet_ntoa(daddr));
			printf("tell %s\n", inet_ntoa(saddr));

			break;

		case ARPOP_REPLY:

			bcopy(arp->ar_sip, &saddr.s_addr, 4);
			printf("%s is at %s\n", inet_ntoa(saddr), etheraddr_string(arp->ar_sha));
			break;

		// To be done
 
		case ARPOP_RREQUEST:

 
		case ARPOP_RREPLY:
	

		default:

			printf("unkown ARP type\n");
			
	} //switch

}

/////////////////////////////////////////////////
// Print ethernet addresses
//

char *etheraddr_string(const u_char *ep)
{

	char hex[] = "0123456789abcdef";
	u_int i, j;
	char *cp;

	cp = buf;

        j = *ep >> 4;
        *cp++ = hex[j];
        *cp++ = hex[*ep++ & 0xf];

        for (i = 5; (int)--i >= 0;) {

                *cp++ = ':';

                j = *ep >> 4;
                *cp++ = hex[j];
                *cp++ = hex[*ep++ & 0xf];

        }

        *cp = '\0';

	return buf;

}

/*
 * Compile with 'gcc -o arpdump arpdump.c /usr/local/lib/libpcap.a'
 */
