/* Arp relaying... Mostly Based on hunt , sendarp */
/* Mac discovery ripped from aurora by lamagra */
/* Version Beta 1.2B - CON MAC discovery */

/* By luna@aditel.org '99 */ 




#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <linux/if.h>
#include <linux/sockios.h>
#include <signal.h>

#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <net/if_arp.h>

#define ETH_HW_ADDR_LEN 6
#define IP_ADDR_LEN 4
#define ARP_FRAME_TYPE 0x0806
#define ETHER_HW_TYPE 1
#define IP_PROTO_TYPE 0x0800
#define OP_ARP_REQUEST 1
#define OP_ARP_REPLY 2

#define DEFAULT_DEVICE "eth0"
#define CONFFILE "mac2ip"

char usage[]={"Arp Daemon Relayer(1.2B) by dreyer\narp_relay: host1 host2 [seconds]\n"};

struct arp_packet {
        u_char targ_hw_addr[ETH_HW_ADDR_LEN];
        u_char src_hw_addr[ETH_HW_ADDR_LEN];
        u_short frame_type;
        u_short hw_type;
        u_short prot_type;
        u_char hw_addr_size;
        u_char prot_addr_size;
        u_short op;
        u_char sndr_hw_addr[ETH_HW_ADDR_LEN];
        u_char sndr_ip_addr[IP_ADDR_LEN];
        u_char rcpt_hw_addr[ETH_HW_ADDR_LEN];
        u_char rcpt_ip_addr[IP_ADDR_LEN];
        u_char padding[18];
};

struct sockaddr sa;
int sock;
unsigned char my_eth[ETH_ALEN];
u_long my_ip;
struct arp_packet pkt, pkt2;
u_long ip1,ip2;

void die(char* str){
fprintf(stderr,"%s\n",str);
exit(1);
}


int arp_cache_lookup(u_long ip, char *ether)
{
  int sock;
  struct arpreq ar;
  struct sockaddr_in *sin;

  memset((char *)&ar, 0, sizeof(ar));
  strncpy(ar.arp_dev, DEFAULT_DEVICE, sizeof(ar.arp_dev));
  sin = (struct sockaddr_in *)&ar.arp_pa;
  sin->sin_family = AF_INET;
  sin->sin_addr.s_addr = ip;

  if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    return (-1);
  }
  if (ioctl(sock, SIOCGARP, (caddr_t)&ar) == -1) {
    close(sock);
    return (-1);
  }
  close(sock);
  memcpy(ether, ar.arp_ha.sa_data, ETH_HW_ADDR_LEN);

  return (0);
}

int lookup_mac(unsigned long ip,char  *mac)
{
    int i;
    struct arp_packet apkt; 
    apkt.frame_type = htons(ARP_FRAME_TYPE);
    apkt.hw_type = htons(ETHER_HW_TYPE);
    apkt.prot_type = htons(IP_PROTO_TYPE);
    apkt.hw_addr_size = ETH_HW_ADDR_LEN;
    apkt.prot_addr_size = IP_ADDR_LEN;
    apkt.op=htons(OP_ARP_REQUEST);
    memcpy(apkt.src_hw_addr,my_eth,ETH_HW_ADDR_LEN);
    memcpy(apkt.sndr_hw_addr,my_eth,ETH_HW_ADDR_LEN);
    memcpy(apkt.targ_hw_addr,"\xff\xff\xff\xff\xff\xff",ETH_HW_ADDR_LEN); 
    memcpy(apkt.rcpt_hw_addr,"\xff\xff\xff\xff\xff\xff",ETH_HW_ADDR_LEN); 
    memcpy(apkt.sndr_ip_addr,&my_ip,IP_ADDR_LEN);
    memcpy(apkt.rcpt_ip_addr,&ip,IP_ADDR_LEN);
    bzero(apkt.padding,18);
 
    for(i = 0;(i < 5) && arp_cache_lookup(ip,mac) == -1;i++)
    {
        if(sendto(sock,&apkt,sizeof(apkt),0,&sa,sizeof(sa)) < 0){
        perror("sendto");
        exit(1);
       }
        sleep(1);
    }
    if (i==5) {printf("Arp resolution Timed Out\n");exit(-1);}    

}


int my_hw (char *eth) 
/* Obtiene la mac de nuestra tarjeta */
{
   int fd;
   struct ifreq ifr;
   	if ((fd = socket(AF_INET, SOCK_PACKET, 
			/*htons(ETH_P_IP)*/ htons(ETH_P_ALL))) < 0) {
			perror(" SOCK_PACKET allocation problems [fatal]");
	        exit(1);					           
	}
    strncpy(ifr.ifr_name, eth, 4);
    ifr.ifr_name[4]=0;
    if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
		   perror("Cannot get interface HW address");
		/*tap(device, 0);*/
		exit(1);
	}
    memcpy(my_eth, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
    close(fd);
}

int  my_addr (char *eth)
{
    int sock;
    struct ifreq ifr;
    struct sockaddr_in *sin;

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        return -1;
    }

    memset((char *)&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, eth, 4);
    sin = (struct sockaddr_in *)&ifr.ifr_addr;
    sin->sin_family = AF_INET;

    if (ioctl(sock, SIOCGIFADDR, (char *)&ifr) == -1)
    {
        close(sock);
        return -1;
    }
    close(sock);
    my_ip=sin->sin_addr.s_addr;
}

int bye()
{
  memcpy(pkt.sndr_hw_addr,pkt2.rcpt_hw_addr,ETH_HW_ADDR_LEN);
  memcpy(pkt2.sndr_hw_addr,pkt.rcpt_hw_addr,ETH_HW_ADDR_LEN);
  if(sendto(sock,&pkt,sizeof(pkt),0,&sa,sizeof(sa)) < 0){
        perror("sendto");
        exit(1);
        }
  if(sendto(sock,&pkt2,sizeof(pkt2),0,&sa,sizeof(sa)) < 0){
        perror("sendto");
        exit(1);
       }
   printf("Re-sync MAC addresses\n");
   exit(0);
}

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

struct hostent *h;
int slept=1;

if(argc < 3)die(usage);
if (argc == 4) slept=atoi(argv[3]);
my_hw("eth0");
my_addr("eth0");
sock=socket(AF_INET,SOCK_PACKET,htons(ETH_P_RARP));
if(sock<0){
        perror("socket");
        exit(1);
        }
signal(SIGINT,bye);
pkt.frame_type = htons(ARP_FRAME_TYPE);
pkt.hw_type = htons(ETHER_HW_TYPE);
pkt.prot_type = htons(IP_PROTO_TYPE);
pkt.hw_addr_size = ETH_HW_ADDR_LEN;
pkt.prot_addr_size = IP_ADDR_LEN;
pkt.op=htons(OP_ARP_REPLY);
memcpy(&pkt2,&pkt,sizeof (pkt));

memcpy(pkt.src_hw_addr,my_eth,ETH_HW_ADDR_LEN);
memcpy(pkt.sndr_hw_addr,my_eth,ETH_HW_ADDR_LEN);
memcpy(pkt2.src_hw_addr,my_eth,ETH_HW_ADDR_LEN);
memcpy(pkt2.sndr_hw_addr,my_eth,ETH_HW_ADDR_LEN);

  
  if ((h=gethostbyname(argv[1])) == NULL) {
        perror("gethostbyname");
        exit(1);
   }
  ip1=((struct in_addr *)h->h_addr)->s_addr;

  if ((h=gethostbyname(argv[2])) == NULL) {
        perror("gethostbyname");
        exit(1);
   }
  ip2=((struct in_addr *)h->h_addr)->s_addr;

strcpy(sa.sa_data,DEFAULT_DEVICE);
lookup_mac(ip2,pkt.targ_hw_addr);
lookup_mac(ip2,pkt.rcpt_hw_addr);
lookup_mac(ip1,pkt2.targ_hw_addr);
lookup_mac(ip1,pkt2.rcpt_hw_addr);

memcpy(pkt.sndr_ip_addr,&ip1,IP_ADDR_LEN);
memcpy(pkt.rcpt_ip_addr,&ip2,IP_ADDR_LEN);
memcpy(pkt2.sndr_ip_addr,&ip2,IP_ADDR_LEN);
memcpy(pkt2.rcpt_ip_addr,&ip1,IP_ADDR_LEN);

bzero(pkt.padding,18);

while(1)
{
if(sendto(sock,&pkt,sizeof(pkt),0,&sa,sizeof(sa)) < 0){
        perror("sendto");
        exit(1);
        }
if(sendto(sock,&pkt2,sizeof(pkt2),0,&sa,sizeof(sa)) < 0){
        perror("sendto");
        exit(1);
       }
 sleep(slept);
}
}

