/*
 * shoveler- used to proxy and redirect packets
 *
 * Requires libpcap and libdnet, www.tcpdump.org and libdnet.sourceforge.net
 * respectively. Compile using:
 *
 *   gcc -o shoveler shoveler.c -lpcap -ldnet
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <dnet.h>
#include <pcap.h>
#include "mn.h"

struct shoveler_settings shoveler_ctx;
struct bpf_program pcapfilter;
struct in_addr net, mask;
pcap_t *pd;
char pcap_err[PCAP_ERRBUF_SIZE];
char *dev;

/*
 * usage routine
 */
void usage(char *prog, int exit_code)
{
  printf("USAGE:\n");
  printf("%s [options] -r <real IP> -s <source IP> -t <target IP>\n\n",prog);
  printf("Options include:\n");
  printf("  -d <device>  use something other than the default eth0\n");
  printf("  -p           proxy mode\n");
  printf("  -v           verbose mode\n");
  printf("  -V           version info\n");
  printf("  -h           this help screen\n");
  printf("\n");
  exit(exit_code);
}

u_long grab_my_addr( void )
{
  struct ifreq ifr;
  register struct sockaddr_in *sin;
  int fd;

  fd = socket(PF_INET, SOCK_DGRAM, 0);
  if (fd < 0)
    return 0;

  memset(&ifr,0,sizeof(ifr));
  sin = (struct sockaddr_in *)&ifr.ifr_addr;
  strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
  ifr.ifr_addr.sa_family = AF_INET;

  if (ioctl(fd, SIOCGIFADDR, (char*) &ifr) < 0)
  {
    close(fd);
    return 0;
  }
  close(fd);
  return(ntohl(sin->sin_addr.s_addr));
}

/* initialize the shoveler */
int shoveler_init( void )
{
  shoveler_ctx.link_offset = 0;
  shoveler_ctx.verbose = 0;
  shoveler_ctx.proxy = 0;
  shoveler_ctx.spleen = 0;
  if(((ip_addr_t)shoveler_ctx.my_ip.addr_ip = grab_my_addr()) == 0)
    return(FAILURE);
  if((shoveler_ctx.handle = ip_open()) == NULL)
    return(FAILURE);
  else return (SUCCESS);
}

/*
 * packet handler, rewrites packets from on of four scenarios as needed
 */
void grab_packets(u_char *data1, struct pcap_pkthdr* h, u_char *p)
{
  struct ip_hdr *ip_header;
  int len = 0, sent = 0, i;

  ip_header = (struct ip_hdr *)(p + shoveler_ctx.link_offset);

  /* we see a packet coming from the target back toward the real address,
     so we need to rewrite the destination */
  if((ip_header->ip_src == shoveler_ctx.target_ip.addr_ip) &&
     (ip_header->ip_dst == shoveler_ctx.real_ip.addr_ip))
  {
    memcpy(&(ip_header->ip_dst),&(shoveler_ctx.source_ip.addr_ip),IP_ADDR_LEN);
    len = ntohs(ip_header->ip_len);
    ip_checksum(p, len);
    for(i=0; i<len; i++) fprintf(stdout,"%02x ",p[i]);
    sent = ip_send(shoveler_ctx.handle, p, len); 
  }

  /* we are seeing a packet from the real IP heading back toward the target
     which could be bad */
  if((ip_header->ip_src == shoveler_ctx.real_ip.addr_ip) &&
     (ip_header->ip_dst == shoveler_ctx.target_ip.addr_ip) &&
     (shoveler_ctx.spleen))
  {
    
    ip_checksum(p,len);
  }

  /* we get a packet from the attack source for us, rewrite the source and
     destination addresses, assuming we are in proxy mode */
  if((ip_header->ip_src == shoveler_ctx.source_ip.addr_ip) &&
     (ip_header->ip_dst == shoveler_ctx.my_ip.addr_ip) &&
     (shoveler_ctx.proxy))
  {
    memcpy(&(ip_header->ip_src),&(shoveler_ctx.real_ip.addr_ip),IP_ADDR_LEN);
    memcpy(&(ip_header->ip_dst),&(shoveler_ctx.target_ip.addr_ip),IP_ADDR_LEN);
    len = ntohs(ip_header->ip_len);
    ip_checksum(p, len);
    for(i=0; i<len; i++) fprintf(stdout,"%02x ",p[i]);
    sent = ip_send(shoveler_ctx.handle, p, len);
  }

  if(shoveler_ctx.verbose)
  {
    if(len) fprintf(stdout,"%d(%d)",sent,len);
    else fprintf(stdout,".");
    fflush(stdout);
  }
}

void do_sniffing(void)
{
  int snaplen = 65535, promisc = 1, to_ms = 1000;
  pcap_t *pd;

  if (pcap_lookupnet(dev,&net.s_addr,&mask.s_addr, pcap_err) == -1)
  {
    perror(pcap_err);
    exit(-1);
  }
  
  if ((pd = pcap_open_live(dev, snaplen, promisc, to_ms, pcap_err)) == NULL)
  {
    perror(pcap_err);
    exit(-1);
  }
                                                                                
  switch(pcap_datalink(pd))
  {
    case DLT_EN10MB:
    case DLT_IEEE802:
      shoveler_ctx.link_offset = 14;
      break;
    case DLT_SLIP:
      shoveler_ctx.link_offset = 16;
      break;
    case DLT_PPP:
    case DLT_NULL:
      shoveler_ctx.link_offset = 4;
      break;
    case DLT_RAW:
      shoveler_ctx.link_offset = 0;
      break;
    default:
      fprintf(stderr,"unsupported interface type\n");
      exit(-1);
  }
                                                                                
  while (pcap_loop(pd,0,(pcap_handler)grab_packets,0));
}

int main(int argc, char **argv)
{
  int real = 0, source = 0, target = 0;
  int c;
  char *prog;

  dev = "eth0";
  prog = argv[0];

  /* must be root since we are playing with packets at a low level */
  if(!(getuid() == 0))
  {
    fprintf(stderr,"*** %s requires root privileges\n\n",prog);
    return(FAILURE);
  }

  /* init packet purgatory context structure */
  if(shoveler_init() == FAILURE)
  {
    fprintf(stderr,"*** shoveler_init failed\n\n");
    return(FAILURE);
  }

  /* process args */
  while((c=getopt(argc,argv,"d:hpr:s:St:vV")) != -1)
  {
    switch(c)
    {
      case 'd':
        dev = (char *)optarg;
        break;
      case 'h':
        usage(prog,SUCCESS);
        break;
      case 'p':
        shoveler_ctx.proxy = 1;
        break;
      case 'r':
        if((addr_pton(optarg, &(shoveler_ctx.real_ip))) < 0)
        {
          fprintf(stderr,"*** Unable to resolve real IP: %s\n", optarg);
          return(FAILURE);
        }
        real = 1;
        break;
      case 's':
        if((addr_pton(optarg, &(shoveler_ctx.source_ip))) < 0)
        {
          fprintf(stderr,"*** Unable to resolve source IP: %s\n", optarg);
          return(FAILURE);
        }
        source = 1;
        break;
      case 'S':
        shoveler_ctx.spleen = 1;
        break;
      case 't':
        if((addr_pton(optarg, &(shoveler_ctx.target_ip))) < 0)
        {
          fprintf(stderr,"*** Unable to resolve target IP: %s\n", optarg);
          return(FAILURE);
        }
        target = 1;
        break;
      case 'v':
        shoveler_ctx.verbose = 1;
        break;
      case 'V':
        fprintf(stdout,"Mystery Net Utility - The Shoveler, v%s\n\n",VERSION);
        return(SUCCESS);
        break;
    }
  }

  /* begin post arg processing */
  if(!(source))
  {
    fprintf(stderr,"*** You must supply a source address\n\n");
    usage(prog,FAILURE);
  }
  if(!(target))
  {
    fprintf(stderr,"*** You must supply a target address\n\n");
    usage(prog,FAILURE);
  }
  if(!(real))
  {
    fprintf(stderr,"*** You must supply a real address\n\n");
    usage(prog,FAILURE);
  }
  /* end post arg processing */

  if(shoveler_ctx.verbose)
  {
    printf("My IP address is: %s (%s)\n",addr_ntoa(&shoveler_ctx.my_ip),dev);
    printf("Target IP is:     %s\n",addr_ntoa(&shoveler_ctx.target_ip));
    printf("Spoofing as IP:   %s\n",addr_ntoa(&shoveler_ctx.real_ip));
    printf("Attacker IP is:   %s",addr_ntoa(&shoveler_ctx.source_ip));
    if(shoveler_ctx.proxy) printf(" (proxy mode)");
    printf("\n");
    if(shoveler_ctx.spleen) printf("Using Spleen mode\n");
  }

  /* start shoveling.... */
  do_sniffing();
  return(SUCCESS);
}

