/*
 * icmpid.c - host OS identification via icmp packets.
 *
 * Written by Simple Nomad, based upon Ofir Arkin's ICMP research.
 * 
 * Requirements: Libnet 1.0 or higher (http://www.packetfactory.net/libnet/)
 *               libpcap 0.4 (ftp://ftp.ee.lbl.gov/libpcap.tar.Z)
 *
 * Compilation instructions:
 *    gcc `libnet-config --defines` -o icmpid icmpid.c -lnet -lpcap
 *
 * Tested on RedHat 6.1, but should work most places that libnet and libpcap
 * compile and install properly.
 *
 * Initial revision 15May2001
 * 
 * 21May2001 - More bugs removed, only working on figure_results() logic.
 * 22May2001 - Made sure we are using IP_RF in the ECHO packet. Added 5th
 *             packet as combining IP_DF and IP_RF in the same packet
 *             caused problems. Fixed a couple of greater-than signs that
 *             were pointing the wrong way.
 * 23May2001 - The second echo packet reply could have the IP_DF bit set,
 *             so we now make sure we take that into consideration when
 *             when checking for IP_RF. Added code to differentiate ME from
 *             98/98SE. Added code in case one of the echo replies is dropped
 *             to prevent a false positive OS ID.
 * 24May2001 - Fixed a few minor bugs and typos. Added check to ensure
 *             user is root when running.
 *
 * Notes:
 * This is intended as a proof of concept piece of code. It is not very
 * accurate going across the open Internet, mainly because of two things --
 * first, there is a distinct possibility that one of the five ICMP packets
 * gets dropped, and second, there is a distinct possibility that all or part
 * of the ICMP packets will be blocked (via firewalling, etc) before reaching
 * the target. Considering that at least part of the OS identification 
 * involves determining whether the OS actually replies or ignores certain
 * types of ICMP packets, a dropped or firewalled packet could lead to a false
 * OS identification.
 *
 * Nonetheless, it is extremely accurate on a local network, and if dropped
 * packets may be a problem then of course multiple runs can ID the OS.
 * I hope you enjoy the tool, as always let me know if you find it useful.
 * If you have questions regarding the logic behind the tool, I recommend you
 * review Ofir Arkin's paper on ICMP available at 
 * http://www.sys-security.com/html/projects/icmp.html.
 *
 * Simple Nomad
 * Sun Jul 15 17:05:06 CDT 2001
 */

#include <libnet.h>
#include <pcap.h> 
#include <net/bpf.h>

#define VER "1.0"

/*
 * More globals
 */
struct bpf_program pcapfilter;
struct in_addr net, mask;
pcap_t *pd;
char pcap_err[PCAP_ERRBUF_SIZE];
u_char *ebuf;
char *dev;
struct libnet_link_int *l;
int link_offset, verbose, packets_answered, unexpected, icmp_type;
u_short icmp_header, id0, id1, id2, id3, id4;
u_int8_t ttl_reply, code_field = 0, precedence_bits = 0;
int tstamp_reply = 0, addrmask_reply = 0, info_reply = 0, first_echo = 0, second_echo = 0;
int df_bit = 0, unused_bit = 0;
u_short precedence_info_reply = 0, ip_id = 0;
u_long src_ip, dst_ip;
char *targetname;

/*
 * build and send the icmp packets to remotely ID the OS of the target
 */
void send_os_packets(void)
{
  int packet_size, network, i, os, checksum_header;
  u_char *packet;
  char echodata[48];
  u_short id = 0;
  u_short df = 0;
  u_char tos = 0;
  u_char ttl;

  /* main sending loop */
  /* do some random stuff to avoid IDS */
  ttl = libnet_get_prand(LIBNET_PR8); /* Time To Live */
  if(ttl < 32) ttl = ttl + 32;        /* make sure TTL isn't too small */

  /* loop to build the individual packets for OS identification */
  for (os=0;os<5;os++)
  {
    switch(os)
    {
      case 0:
        checksum_header = LIBNET_ICMP_ECHO_H;
        break;
      case 1:
        checksum_header = LIBNET_ICMP_TS_H;
        break;
      case 2:
        checksum_header = LIBNET_ICMP_MASK_H;
        break;
      case 3: /* libnet doesn't support info request, so we lie about the header */
        checksum_header = LIBNET_ICMP_ECHO_H;
        break;
      case 4:
        checksum_header = LIBNET_ICMP_ECHO_H;
        break;
    }

    /* compute packet size */
    packet_size = LIBNET_IP_H + checksum_header;

    /* get mem for packet */
    libnet_init_packet(packet_size, &packet);
    if (packet == NULL)
    {
      libnet_error(LIBNET_ERR_FATAL, "unable to init packet mem\n");
    }

    /* get network ready */
    network = libnet_open_raw_sock(IPPROTO_RAW);
    if (network == -1)
    {
      libnet_error(LIBNET_ERR_FATAL, "unable to open network for sending\n");
    }
    /* build ICMP section */
    switch (os)
    {
      case 0: /* ICMP_ECHO with code != 0 and precedence != 0 */
        if (verbose) fprintf(stdout,"Sending first echo,");
        libnet_build_icmp_echo(ICMP_ECHO,0x20,id0,0,NULL,0,packet + LIBNET_IP_H);
        tos = 0x6;
        df = IP_DF;
        break;        
      case 1: /* ICMP_TSTAMP */
        if (verbose) fprintf(stdout," timestamp,");
        libnet_build_icmp_timestamp(ICMP_TSTAMP,0,id1,0,0x00000000,0x00000000,0x00000000,NULL,0,packet + LIBNET_IP_H);
        tos = 0x00;
        df = 0;
        break;
      case 2: /* ICMP_MASKREQ */
        if (verbose) fprintf(stdout," mask request,");
        libnet_build_icmp_mask(ICMP_MASKREQ,0,id2,0,0,NULL,0,packet + LIBNET_IP_H);
        break;
      case 3: /* ICMP_INFO_REQUEST with precedence != 0 */
        /* we use the ECHO build but use the INFOREQ type to get around libnet */
        if (verbose) fprintf(stdout," info request,");
        libnet_build_icmp_echo(ICMP_INFO_REQUEST,0,id3,0,NULL,0,packet + LIBNET_IP_H);
        tos = 0x6;
        break;       
      case 4: /* ICMP_ECHO with code != 0 and precedence != 0 */
        if (verbose) fprintf(stdout," second echo packets\n");
        libnet_build_icmp_echo(ICMP_ECHO,0,id4,0,NULL,0,packet + LIBNET_IP_H);
        tos = 0;
        df = IP_RF;
        break;        
    }
    
    /* randomize the IP id */
    id = libnet_get_prand(LIBNET_PR16);
    /* build IP section */
    libnet_build_ip(checksum_header,tos,id,df,ttl,IPPROTO_ICMP,src_ip,dst_ip,NULL,0,packet);
    /* do the checksum */
    if (libnet_do_checksum(packet, IPPROTO_ICMP, checksum_header) == -1) 
    { 
      libnet_error(LIBNET_ERR_FATAL, "checksum failed on packet\n"); 
    }

    /* send the packet */
    i = libnet_write_ip(network, packet, packet_size);
    if (i == -1)
    {
      libnet_error(LIBNET_ERR_FATAL, "failed to write to network\n");
    }

    /* 
     * this isn't *too* bad, but if we're trying to be stealth there
     * is no reason to send part of a packet in case it does get there
     * as it might set off an alarm. therefore if we start trying to
     * send just parts, abandon ship 
     */
    if (i < packet_size) 
    { 
      libnet_error(LIBNET_ERR_FATAL, "only wrote %d bytes\n", i); 
    }

    /* 
     * miniscule delay between packets to prevent overflowing the local
     * interface
     */ 
    usleep(1);
    /* clean things up */
    if (libnet_close_raw_sock(network) == -1) 
    { 
      libnet_error(LIBNET_ERR_WARNING, "couldn't close the interface after sending"); 
    } 
    libnet_destroy_packet(&packet); 
  }
}

/*
 * Receive the packets normally, i.e. no sniffing involved.
 */
void receive_packets_normal(void)
{
  char buf[1500];
  struct ip *ip = (struct ip *)buf;
  struct icmp *icmp = (struct icmp *)(ip + 1);
  int s, err = 0, on = 1;
  long int fromlen = 0;
  ulong src_ip;
  
  packets_answered = 0;
  unexpected = 0;

  /* for non-sniffing we listen the old-fashioned way */
  if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) 
  {
    fprintf(stderr, "Problem opening socket for listening");
    exit(-1);
  }

  if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) 
  {
    fprintf(stderr, "Problem setting option on socket");
    exit(-1);
  }

  while (packets_answered < 6)
  {
    if ((err = recvfrom(s, buf, sizeof buf, 0, NULL, (int *)&fromlen)) < 0)
    {
      fprintf(stderr,"error receiving on network");
    }

    if (dst_ip == ip->ip_src.s_addr) 
    {
      if (IPPROTO_ICMP == ip->ip_p) 
      {
        switch (icmp->icmp_type)
        {
          case ICMP_ECHOREPLY: /* ECHO with code != 0, precedence != 0, DF bit set */
            if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id0)
            {
              code_field = icmp->icmp_code;
              precedence_bits = ip->ip_tos;
              ttl_reply = ip->ip_ttl;
              if(ip->ip_off == 0x0000) df_bit = 0;
              if(ip->ip_off == 0x0040) df_bit = 1;
              ip_id = ip->ip_id;
              first_echo++;
              packets_answered++;
            }
            else if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id4)
            {
              if(ip->ip_off == 0x0000) unused_bit = 0;
              if((ip->ip_off == 0x0080) || (ip->ip_off == 0x00c0)) unused_bit = 1;
              second_echo++;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_ECHOREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_TSTAMPREPLY: /* ICMP_TSTAMP */
            if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id1)
            {
              tstamp_reply = 1;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_TSTAMPREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_MASKREPLY: /* ICMP_MASKREQ */
            if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id2)
            {
              addrmask_reply = 1;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_MASKREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_INFO_REPLY: /* ICMP_INFO_REQUEST */
            if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id3)
            {
              info_reply = 1;
              precedence_info_reply = ip->ip_tos;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_INFO_REPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          default:
            if (verbose) printf("Received unexpected ICMP packet from %s\n",inet_ntoa(ip->ip_src));
            unexpected++;
            break;
        }
      }
      else
      {
        /* we do not wish to see any odd packets from our own address */
        src_ip = libnet_get_ipaddr(l, dev, ebuf);
        src_ip = htonl(src_ip);
        if (!(src_ip == ip->ip_src.s_addr))
        {  
          if (verbose) printf("Received unexpected packet from %s\n",inet_ntoa(ip->ip_src));
          unexpected++;
        }
      }
    }
    if (packets_answered == 5) break;
  }
}

/*
 * this handles packets as they are sniffed and processes them, similar to
 * the receive_packets_normal routine
 */
void grab_icmp(u_char *data1, struct pcap_pkthdr* h, u_char *p)
{
  struct ip* ip = (struct ip *)(p + link_offset);
  struct icmp *icmp = (struct icmp *)(ip + 1);
  int i,current;

  while (packets_answered < 5)
  {
    if (dst_ip == ip->ip_src.s_addr) 
    {
      if (IPPROTO_ICMP == ip->ip_p)
      {
        switch (icmp->icmp_type)
        {
          case ICMP_ECHOREPLY: /* ECHO with code != 0, precedence != 0, DF bit set */
            if (icmp->icmp_hun.ih_idseq.icd_id == id0)
            {
              code_field = icmp->icmp_code;
              precedence_bits = ip->ip_tos;
              ttl_reply = ip->ip_ttl;
              if(ip->ip_off == 0x0000) df_bit = 0;
              if(ip->ip_off == 0x0040) df_bit = 1;
              ip_id = ip->ip_id;
              first_echo++;
              packets_answered++;
            }
            else if (icmp->icmp_hun.ih_idseq.icd_id == id4)
            {
              if(ip->ip_off == 0x0000) unused_bit = 0;
              if((ip->ip_off == 0x0080) || (ip->ip_off == 0x00c0)) unused_bit = 1;
              second_echo++;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_ECHOREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_TSTAMPREPLY: /* ICMP_TSTAMP */
            if (icmp->icmp_hun.ih_idseq.icd_id == id1)
            {
              tstamp_reply = 1;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_TSTAMPREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_MASKREPLY: /* ICMP_MASKREQ */
            if (icmp->icmp_hun.ih_idseq.icd_id == id2)
            {
              addrmask_reply = 1;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_MASKREPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          case ICMP_INFO_REPLY: /* ICMP_INFO_REQUEST */
            if (htons(icmp->icmp_hun.ih_idseq.icd_id) == id3)
            {
              info_reply = 1;
              precedence_info_reply = ip->ip_tos;
              packets_answered++;
            }
            else
            {
              if (verbose) fprintf(stdout,"Received unexpected ICMP_INFO_REPLY packet from %s\n",inet_ntoa(ip->ip_src));
              unexpected++;
            }
            break;
          default:
            if (verbose) printf("Received unexpected ICMP packet from %s\n",inet_ntoa(ip->ip_src));
            unexpected++;
            break;
        }
      }
      else
      {
        /* we do not wish to see any odd packets from our own address */
        src_ip = libnet_get_ipaddr(l, dev, ebuf);
        src_ip = htonl(src_ip);
        if (!(src_ip == ip->ip_src.s_addr))
        {  
          if (verbose) printf("Received unexpected packet from %s\n",inet_ntoa(ip->ip_src));
          unexpected++;
        }
      }
    }
    if (packets_answered == 5) break;
  }
}

/*
 * So why do a "normal" and a "sniffing" routine separately? Sniffing requires
 * putting the receiving interface in promiscuous mode, something you may not
 * want to do.
 */
void receive_packets_sniffing(void)
{
  int snaplen = 65535, promisc = 1, to_ms = 1000;

  if (pcap_lookupnet(dev,&net.s_addr,&mask.s_addr, pcap_err) == -1) 
  {
    perror(pcap_err);
    exit(-1);
  } 

  if (verbose) printf("listening for replies on %s: %s\n", dev, inet_ntoa(net));

  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:
      link_offset = 14;
      break;
    case DLT_SLIP: 
      link_offset = 16;
      break;
    case DLT_PPP:
    case DLT_NULL:
      link_offset = 4;
      break;
    case DLT_RAW: 
      link_offset = 0;
      break;
    default:
      fprintf(stderr,"unsupported interface type\n");
      exit(-1);
  }

  while (pcap_loop(pd,0,(pcap_handler)grab_icmp,0));
}

/*
 * Called by caughtsig, if we're sniffing to turn off the promisc bit
 */
void stop_sniffing(void)
{
  int fd, i;
  struct ifreq buf[16];
  struct ifconf ifc;

  if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0)
  {
    ifc.ifc_len = sizeof buf;
    ifc.ifc_buf = (caddr_t) buf;
    if (!(ioctl (fd, SIOCGIFCONF, (char *) &ifc))) 
    {
      i = ifc.ifc_len / sizeof (struct ifreq);
      if (!i)
      {
        fprintf(stderr, "Can't find an interface.\n");
        exit(-1);
      }
      while (i-- > 0)
      {
        /* cmp only length of dev since ifr_name may have extra chars */
        if (!(strncmp(buf[i].ifr_name,dev,strlen(dev))))
        {
          if(!(ioctl(fd, SIOCGIFFLAGS, (char *) &buf[i])))
          {
            if (buf[i].ifr_flags & IFF_PROMISC) 
            {
              buf[i].ifr_flags = buf[i].ifr_flags ^ IFF_PROMISC;
              if(ioctl(fd, SIOCSIFFLAGS, &buf[i])) perror("ioctl");
            }
          }
          else perror("ioctl");
        } 
      } /* end while loop */
    } 
    else perror ("ioctl");
  } 
  else perror ("socket");
  close (fd);
}

void totals(void)
{
  fprintf(stdout,"Total expected packets received   : %d out of 5\n",packets_answered);
  fprintf(stdout,"Total unexpected packets received : %d\n",unexpected);
}

void figure_results(void)
{
  if (packets_answered == 0) fprintf(stdout,"No answer back from target, possibly ICMP blocked or target down\n");
  else if (!first_echo) fprintf(stdout,"WARNING: No reply to the first echo packet, unable to fingerprint the OS.\n");
  else
  {
    if ((second_echo) && (!tstamp_reply) && (!addrmask_reply) && (!info_reply))
    {
      fprintf(stdout,"\nWARNING: Only Echo packets were received back, results could be in error if a\nfirewall allowed Echo only to the target.\n\n");
    }
    if (!second_echo) fprintf(stdout,"\nWARNING: The second echo packet was not received (possibly dropped), the results\ncould be in error.\n\n");
    if (verbose)
    {
      fprintf(stdout,"Echo Packets results:\n");
      fprintf(stdout,"  IP ID        - 0x%x\n",ip_id);
      fprintf(stdout,"  Code         - 0x%x\n",code_field);
      fprintf(stdout,"  TOS          - 0x%x\n",precedence_bits);
      fprintf(stdout,"  DF Bit       - %d\n",df_bit);
      fprintf(stdout,"  Unused bit   - %d\n",unused_bit);
      fprintf(stdout,"  TTL          - %d\n",ttl_reply);
      fprintf(stdout,"Timestamp results:\n");
      if (tstamp_reply) fprintf(stdout,"  Replied\n");
      else fprintf(stdout,"  No Reply\n");
      fprintf(stdout,"Address Mask results:\n");
      if (addrmask_reply) fprintf(stdout,"  Replied\n");
      else fprintf(stdout,"  No Reply\n");
      fprintf(stdout,"Info Request results:\n");
      if (info_reply) 
      {
        fprintf(stdout,"  Replied\n");
        fprintf(stdout,"  TOS          - 0x%x\n",precedence_info_reply);
      }
      else fprintf(stdout,"  No Reply\n");
    }
    fprintf(stdout,"%s is ",targetname);
    if (code_field == 0) /* Windows OS */ 
    {
      if (ttl_reply < 32) fprintf(stdout,"Windows 95\n");
      else if (precedence_bits == 0) fprintf(stdout,"Windows 2000\n");
      else
      {
        if (tstamp_reply != 0)
        {
          if (addrmask_reply == 0) fprintf(stdout,"Windows ME\n");
          else fprintf(stdout,"Windows 98/98SE\n");
        }
        else 
        {
          if (addrmask_reply == 0) fprintf(stdout,"Windows NT SP4 or higher\n");
          else fprintf(stdout,"Windows NT SP3 or lower\n");
        }
      }
    }
    else /* other OSes */
    {
      if (ip_id == 0) fprintf(stdout,"Linux 2.4.x\n"); 
      else
      if (precedence_bits == 0) /* Ultrix or Novell */
      {
        if (ttl_reply > 128) fprintf(stdout,"Ultrix\n");
        else fprintf(stdout,"Novell\n");
      }
      else
      {
        if (unused_bit == 0)
        {
          if(df_bit == 0)
          {
            if(ttl_reply < 64) fprintf(stdout,"Linux 2.0.x\n");
            else fprintf(stdout,"Linux 2.2.x\n");
          }
          else
          {
            if(info_reply == 0) fprintf(stdout,"Unknown\n");
            else
            {
              if(precedence_info_reply == 0) fprintf(stdout,"OpenVMS\n");
              else fprintf(stdout,"AIX\n");
            }
          }
        }
        else 
        {
          if(tstamp_reply == 0) fprintf(stdout,"HPUX 11.x\n");
          else
          {
            if(info_reply == 0) fprintf(stdout,"Sun Solaris\n");
            else fprintf(stdout,"HPUX 10.x or older\n");
          }
        }
      }
    }
  }
}

void caughtsig(int sig)
{
  if (pd) stop_sniffing();
  if (verbose) totals();
  figure_results();
  exit(sig);
}

/*
 * usage
 */
void usage(char *prog)
{
  fprintf(stderr,"USAGE: ");
  fprintf(stderr,"%s [opts] [-d dev] [-s src] [-t sec] target_system\n\n",prog);
  fprintf(stderr,"  opts are h n p r v V\n");
  fprintf(stderr,"    -h this help screen\n");
  fprintf(stderr,"    -n no sending of packets\n");
  fprintf(stderr,"    -p promiscuous receive mode\n");
  fprintf(stderr,"    -r receiving packets only (no sending)\n");
  fprintf(stderr,"    -v verbose\n");
  fprintf(stderr,"    -V version info and exit\n");
  fprintf(stderr,"  -d device to grab local IP or sniff from, default is eth0\n");
  fprintf(stderr,"  -s spoofed source address\n");
  fprintf(stderr,"  -t time in seconds to wait for all replies (default 5)\n");
  fprintf(stderr,"  target_system is hostname or IP address\n");
  fprintf(stderr,"\n");
}

int main(int argc, char **argv)
{
  char *prog;
  extern char *optarg;
  extern int optind;
  extern int optopt;
  extern int opterr;
  char ch;
  int spoof = 0, receive = 0, promiscuous = 0, no_send = 0, setdev = 0;
  int i, sent, timeout = 5, icmpenum = 1, class_c = 0, results_counter = 0;
  pid_t pid;
  
  dev = "eth0"; /* default */
  verbose = 0;
  prog = argv[0];

  while ((ch = getopt(argc, argv, "hrnpvVd:s:t:")) != EOF) 
    switch(ch)
    {
      case 'h':
        usage(prog);
        exit(0);
      case 'r':
        receive = 1;
        break;
      case 'p':
        promiscuous = 1;
        break;
      case 'n':
        no_send = 1;
        break;
      case 'd':
        dev = optarg;
        setdev = 1;
        break;
      case 's':
        if (!(src_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
        {
          fprintf(stderr,"=== Unable to resolve source host,\n");
          fprintf(stderr,"=== try spoofing with an IP address\n");
          usage(prog);
          exit(-1);
        }
        spoof = 1;
        break;
      case 't':
        timeout = (int) strtol(optarg, NULL, 10);
        break;
      case 'v':
        verbose = 1;
        break;
      case 'V':
        fprintf(stderr,"ICMPID v%s -- Written by Simple Nomad\n",VER);
        fprintf(stderr,"http://www.nmrc.org/ -- thegnome@nmrc.org\n\n");
        fprintf(stderr,"See http://www.sys-security.com/html/projects/icmp.html\nfor more information.\n\n");
        exit(0);
      default:
        usage(prog);
        exit(-1);
    }
  argc -= optind;
  argv += optind;

  if(getuid()!=0) 
  {
    fprintf(stderr, "=== You must be root to run %s!\n\n", prog);
    exit(-1);
  } 

  /* post arg processing */
  if ((setdev) && (spoof))
  {
    fprintf(stderr, "=== You cannot specify a device for a source IP address\n");
    fprintf(stderr, "=== and spoof your source IP address at the same time.\n");
    usage(prog);
    exit(-1);
  }

  if ((no_send) && (!promiscuous) && (!receive))
  {
    fprintf(stderr,"=== You are not sending and receiving any packets,\n");
    fprintf(stderr,"=== so not much is going to happen.\n");
    usage(prog);
    exit(-1);
  }

  /* if the receive flag is set, set the no_send flag so we don't send
     any packets */
  if ((receive) && (!no_send)) no_send++;

  if ((spoof) && (!receive) && (!promiscuous))
  {
    printf("NOTE: This machine will not see responses while spoofing.\n");
    timeout = 0; /* no need to wait around for replies */
  }

  if(!spoof)
  {
    src_ip = libnet_get_ipaddr(l, dev, ebuf);
    if ((src_ip == -1) || (src_ip == 0))
    {
      fprintf(stderr, "=== Grabbing address from %s failed,\n",dev);
      fprintf(stderr, "=== try a different device.\n");
      usage(prog);
      exit(-1);
    }
    src_ip = htonl(src_ip);
  }  

  if ((receive) && (promiscuous))
  {
    fprintf(stderr,"=== Receive mode is for normal receiving of spoofed packets\n");
    fprintf(stderr,"=== while Promiscuous mode is for sniffing of spoofed packets\n");
    fprintf(stderr,"=== passing nearby. They cannot be used together.\n");
    usage(prog);
    exit(-1);
  }

  if (!argv[0] || !strlen(argv[0]))
  {
    fprintf(stderr,"=== You must specify a target\n");
    usage(prog);
    exit(-1);
  }

  targetname = *argv;
  dst_ip = libnet_name_resolve(targetname,LIBNET_RESOLVE);

  /* end post arg processing */

  /* seed the randomness */
  i = libnet_seed_prand();
  if (i == -1)
  {
    libnet_error(LIBNET_ERR_WARNING, "unable to properly seed the prng\n");
  }
  /* build our id's to track our 5 packets */
  id0 = libnet_get_prand(LIBNET_PR16);
  id1 = libnet_get_prand(LIBNET_PR16);
  id2 = libnet_get_prand(LIBNET_PR16);
  id3 = libnet_get_prand(LIBNET_PR16);
  id4 = libnet_get_prand(LIBNET_PR16);

  /* set up timeout*/
  signal(SIGALRM, caughtsig);
  alarm(timeout);

  /* if sending packets, fork off the process so we can start listening
     to replies immediately */
  pid = fork();
  switch(pid)
  {
    case -1:
      /* if not sending we don't care that much about fork failures */
      if (!no_send)
      {
        fprintf(stderr, "Unable to fork off for sending packets\n");
        exit(-1);
      }
      break;
    case 0:
      if (!no_send) send_os_packets();
      break;
    default:
      if (!spoof)
      {
        receive_packets_normal();
        if (verbose) totals();
        break;
      }
      if (promiscuous)
      {
        receive_packets_sniffing();
        break;
      }
  }
  exit(0);
}
