////////////////////////////////////////
//                                    //
// RARP Client for Linux              //
// Steve Buer <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.        //
//                                                                 //
/////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <pcap.h>

#define PKT_SIZE 1500

#define ARPOP_RREQUEST  3               /* RARP request.  */
#define ARPOP_RREPLY    4               /* RARP reply.  */

#define ARP_ETHER	1		/* Ethernet HW type */
#define ARP_PROTO_IP 	0x0800

struct arp_header {

	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).        */

	// These are 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            */

};

struct option longopts[] = {

	{"verbose", 0, 0, 'v'},
	{"help", 0, 0, 'h'},
	{0}

};

void usage()
{

	fprintf(stderr, "Usage: rarp_client [-v] <MAC address>\n");
	fprintf(stderr, "	--verbose	-v 	verbose output\n");
	fprintf(stderr, "	--help		-h 	display this\n");

}

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

	int sockfd, c, n;
	int ifindex		= 0;
	int verbose 		= 0;
	int errflg 		= 0;
	int pktlen 		= 0;
	char *theMAC 		= NULL;
	char *theInterface 	= NULL;
	char sendbuf[PKT_SIZE];
	char recvbuf[PKT_SIZE];
	char ebuf[128];
	struct arp_header arphdr;
	struct arp_header *reply;
	struct sockaddr_ll to;
	struct in_addr source;
	struct in_addr target;
	struct ifreq if_data;
	struct ifconf if_list;
	
	u_char s[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 	/* source HW address */

	while ((c = getopt_long(argc, argv, "vh", longopts, 0)) != EOF) {


		switch (c) {

			case 'v':

				verbose++;
				break;

			case 'h':

				errflg++;
				break;

			default:
			
				// errflg++;
				break;

		} //switch

	} // while

	if (errflg) {

		usage();
		exit(1);

	}
	
	if ((sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_RARP))) < 0) {

		perror("socket");
		exit(1);

	}

	// Process MAC from cmd line

	if (optind < argc) {

		theMAC = argv[optind++];

		n = sscanf(theMAC, "%x:%x:%x:%x:%x:%x",
                                     &s[0],
                                     &s[1],
                                     &s[2],
                                     &s[3],
                                     &s[4],
                                     &s[5]);            
		if (n != 6) {

			fprintf(stderr, "invalid MAC address\n");
			exit(1);

		}

	}

	////////////////////////////////////
	// Find an interface to use

	if (!(theInterface = pcap_lookupdev(ebuf))) {

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

	}

	////////////////////////////////////
	// Get interface index

	memset(&if_data, '\0', sizeof(struct ifreq));

	strncpy(if_data.ifr_name, theInterface, sizeof(if_data.ifr_name)); 

	if (ioctl(sockfd, SIOCGIFINDEX, &if_data) < 0) {

		perror("ioctl");
		exit(1);

        }

	ifindex = if_data.ifr_ifindex;

	/////////////////////////////////////////////
	// lookup the MAC address if not specified

	if (!theMAC) {

		memset(&if_data, '\0', sizeof(struct ifreq));

		strcpy(if_data.ifr_name, theInterface);

		if (ioctl(sockfd, SIOCGIFHWADDR, &if_data) < 0) {
                
			perror("ioctl");
                	exit(1);
        
		}

		memcpy(&s, &if_data.ifr_hwaddr.sa_data, 6);

		theMAC = (char *) malloc(sizeof("00:00:00:00:00:00"));

		sprintf(theMAC, "%02x:%02x:%02x:%02x:%02x:%02x", s[0], s[1], s[2], s[3], s[4], s[5]);

	}

	// Build the ARP request

	memset(&arphdr, '\0', sizeof(struct arp_header));

	arphdr.ar_hrd	= htons(1);
	arphdr.ar_pro	= htons(ARP_PROTO_IP);
	arphdr.ar_hln	= 6;
	arphdr.ar_pln	= 4;
	arphdr.ar_op	= htons(ARPOP_RREQUEST);

	memcpy(&arphdr.ar_sha, s, 6);
	memcpy(&arphdr.ar_tha, s, 6);

	// Fill the buffer

	memset(sendbuf, '0', sizeof(sendbuf));

	memcpy(sendbuf, &arphdr, sizeof(arphdr));

	pktlen = sizeof(struct arp_header);

	// Send

	memset(&to, '\0', sizeof(struct sockaddr_ll));
	
	to.sll_family		= AF_PACKET;
	to.sll_protocol 	= htons(ETH_P_RARP);
	to.sll_ifindex		= ifindex;
	to.sll_hatype		= ARP_ETHER;
	to.sll_pkttype 		= PACKET_BROADCAST;
	to.sll_halen		= 6;

	memset(&to.sll_addr, 0xff, 6); 		/* send to the broadcast address */

	if (sendto(sockfd, &sendbuf, pktlen, 0, (struct sockaddr *) &to, sizeof(struct sockaddr_ll)) < 0) {

		perror("sendto");
		exit(1);

	}

	for (;;) {

		memset(&recvbuf, '\0', sizeof(recvbuf));

		n = recvfrom(sockfd, &recvbuf, sizeof(recvbuf), 0, NULL, NULL);

		if (n < 0) {

			perror("recvfrom");
			exit(1);

		}

		reply = (struct arp_header *) &recvbuf;

		if (memcmp(reply->ar_tha, &s, 6) != 0) {

			continue;

		}
		
		bcopy(reply->ar_tip, &target.s_addr, 4);
		bcopy(reply->ar_sip, &source.s_addr, 4);

		if (!verbose) {
		
			printf("%s\n", inet_ntoa(target));
			return 0;

		}

		printf(" Iface: %s\n", theInterface);
		printf("   MAC: %s\n", theMAC);
		printf("Server: %s\n", inet_ntoa(source));
		printf(" My IP: %s\n", inet_ntoa(target));

		break;

	} // for

	return 0;

}

/*
 * Compile with 'gcc -o rarp_client -lpcap rarp_client.c'
 */

