#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define ETH "eth0"

struct msgARP
	{
	unsigned char destinoEthernet[6];
	unsigned char origenEthernet[6];
	unsigned short tipoEthernet;
	unsigned short tipoHardware;
	unsigned short protocolo;
	unsigned char longitudMAC;
	unsigned char longitudRED;
	unsigned short tipoARP;
	unsigned char origenMAC[6];
	unsigned char origenIP[4];
	unsigned char destinoMAC[6];
	unsigned char destinoIP[4];
	};

int main (int argc,char *argv[])
{
struct msgARP msg;
struct ifreq ifr;
struct sockaddr sa;
int s,optral,n;
unsigned char ip[4];

/*
Se define un socket SOCK_PACKET para que TCP/IP no
incluya información adicional en las cabeceras. Con lo
que se tiene el control de los formatos del mensaje
*/

//Abriendo el socket

if((s=socket(PF_INET,SOCK_PACKET,htons(ETH_P_ARP)))<0)
	{
	perror("Socket");
	exit(1);
	}

//permitir el envío broadcast
optral=1;

if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,&optral,sizeof(optral))<0)
	{
	perror("setsockopt");
	exit(1);
	}

/*obtención de la dirección IP y MAC de nuestra tarjeta*/

strcpy(ifr.ifr_name,ETH);
if(ioctl(s,SIOCGIFHWADDR,&ifr)<0)
	{
	perror("ioctl");
	exit(1);
	}

/*Se copia la dirección MAC al campo correspondiente de la cabecera ARP
y de la trama ethernet
*/

bcopy(&ifr.ifr_hwaddr.sa_data,&msg.origenMAC,6);
bcopy(&ifr.ifr_hwaddr.sa_data,&msg.origenEthernet,6);

/*Se obtiene la dirección IP asociada a la tarjeta de red*/
strcpy(ifr.ifr_name,ETH);

if(ioctl(s,SIOCGIFADDR,&ifr)<0)
	{
	perror("ioctl");
	exit(1);
	}

/*Copiar la dirección IP al campo de la cabecera ARP*/
bcopy(&ifr.ifr_addr.sa_data[2],&msg.origenIP,4);

/*Como dirección MAC de destino para la trama Ethernet se establece
   FF:FF:FF:FF:FF:FF para realizar un broadcast*/

memset(&msg.destinoEthernet,0xff,6);
   
/*En la trama Ethernet se indicará que se transporta un mensaje
ARP, para que al recibir la trama cualquier estación destino sepa a 
quién enviar los datos en el nivel superior*/
msg.tipoEthernet=htons(ETH_P_ARP);
   
/*Tipo de red Ethernet*/
msg.tipoHardware=htons(ARPHRD_ETHER);
/*El protocolo a nivel de red es IP*/
msg.protocolo=htons(ETH_P_IP);
/*longitud de las direcciones físicas en este caso son 6,
ya que se representan de la forma XX:XX:XX:XX:XX:XX:*/
msg.longitudMAC=6;

/*longitud de las direcciones de red*/
msg.longitudRED=4;

/*tipo de mensaje ARP: solicitud o respuesta*/
msg.tipoARP=htons(ARPOP_REQUEST);

/*la dirección MAC de la máquina destino*/
bzero(&msg.destinoMAC,6);

/*la dirección IP de la máquina destino*/
inet_aton(argv[1],msg.destinoIP);
strncpy(ip,msg.destinoIP,4);
bzero(&sa,sizeof(sa));
strcpy(sa.sa_data,ETH);

if(sendto(s,&msg,sizeof(msg),0,(struct sockaddr *)&sa,sizeof(sa))<0)
    {
    perror("sendto");
    exit(1);
    }
    
do
    {
    bzero(&sa,sizeof(sa));
    bzero(&msg,sizeof(msg));
    n=sizeof(sa);
    if(recvfrom(s,&msg,sizeof(msg),0,(struct sockaddr *)&sa,&n)<0)
        {
        perror("recvform");
        exit(1);
        }
    if((ntohs(msg.tipoARP)==ARPOP_REPLY)&&!strncmp(ip,msg.origenIP,4))
        {
        fprintf(stdout,"Respuesta del equipo al que se llamó, la IP es %s y se corresponde con al MAC %02x:%02x:%02x:%02x:%02x:%02x\n",argv[1],msg.origenMAC[0],msg.origenMAC[1],msg.origenMAC[2],msg.origenMAC[3],msg.origenMAC[4],msg.origenMAC[5]);
        }
    }while(1);
    
    /*se cierra el socket*/
    close(s);
}
    

