/*
 * IP Toolkit 
 * 
 * Simple utility for crafting IP traffic of your choosing. 
 *
 * Requires:
 *      libnet >= 1.1.x (http://www.packetfactory.net/projects/libnet/)
 *      root
 *      a basic understanding what this tool does
 *
 * Compile with something like:
 *  gcc -Wall `libnet-config --defines --cflags` -o ip-tk ip-tk.c `libnet-config --libs`
 *
 *  This code can be found in its original form here:
 *
 *    http://spoofed.org/files/ip-tk.c
 *
 *
 * Jon Hart <warchild@spoofed.org>
 *
 * Copyright (c) 2003, Jon Hart 
 * All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification, 
 *  are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the organization nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 *
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <libnet.h>
#if (SOLARIS || BSD)
#include <netinet/if_ether.h>
#else
#include <netinet/ether.h>
#endif

#include <netdb.h> /* for getprotoby*()*/


#define OPT_HELP        0
#define OPT_VERBOSE     1
#define OPT_INTERFACE   2
#define OPT_SRCIP       3
#define OPT_DSTIP       4
#define OPT_SRCETHER    5
#define OPT_DSTETHER    6
#define OPT_PAYLOAD     7 
#define OPT_IPID        8 
#define OPT_IPTOS       9 
#define OPT_IPTTL       10 
#define OPT_IPPROTO     11


struct opt {
   char *name; /* name of this option */
   int req;    /* required? */
   int set;    /* Does it have a value? */
}; 

struct opt opts[] = {
   {"help", 0, 0},
   {"verbose", 0, 0},
   {"interface", 1, 0},
   {"source IP", 1, 0},
   {"destination IP", 1, 0},
   {"source ethernet address", 0, 0},
   {"destination ethernet address", 0, 0},
   {"payload", 1, 0},
   {"IP ID number", 1, 0},
   {"TOS", 1, 0},
   {"TTL", 1, 0},
   {"IP Protocol number", 1, 0}
   
};

void usage() {
   fprintf(stderr, "\tip-tk\n\n\tA simple IP Toolkit\n\tby Jon Hart <warchild@spoofed.org>\n");
   fprintf(stderr, "\thttp://spoofed.org/files/ip-tk.c\n\n");
   fprintf(stderr, "Usage:\n");
   fprintf(stderr, "\tRequired:\n");
   fprintf(stderr, "\t-i <interface>      Specify interface\n");
   fprintf(stderr, "\t-d <dst ip>         Destination IP\n");
   fprintf(stderr, "\n\tOptional:\n\n");
   fprintf(stderr, "\tLink Layer:\n");
   fprintf(stderr, "\t-e <src ethernet>    Source ethernet address (default: auto)\n");
   fprintf(stderr, "\t-E <dst ethernet>    Destination ethernet address (default: auto)\n");
   fprintf(stderr, "\tIP Layer:\n");
   fprintf(stderr, "\t-I <IP id #>         IP ID number (default: sane)\n");
   fprintf(stderr, "\t-p <proto num>       IP Protocol number (default: random)\n");
   fprintf(stderr, "\t-s <src IP>          Source IP (default: auto)\n");
   fprintf(stderr, "\t-t <TTL>             IP TTL (default: 255)\n");
   fprintf(stderr, "\tProtocol Layer:\n");
   fprintf(stderr, "\t-c <payload>         Packet payload (default: NULL)\n");
   fprintf(stderr, "\tOther:\n");
   fprintf(stderr, "\t-h                   Help (this message)\n");
   fprintf(stderr, "\t-v                   Be verbose\n");
}

void mem_free(void **ptr) {
   if (*ptr) free(*ptr);
   *ptr = NULL;
}

int main(int argc, char *argv[]) {
    
   int c, i; 

   libnet_t *libnet;
   libnet_ptag_t ipv4, ether;
   struct protoent *protoent;

   u_long src_ip, dst_ip;

   u_char ip_ttl, ip_tos, ip_proto;
   u_short ip_id;

   /* The source and destination MAC addresses
    * for the ethernet level
    */ 
   struct libnet_ether_addr *src_ether; 
   struct libnet_ether_addr *dst_ether; 

   char *interface;
   struct ether_addr *ether_t;
   u_char *payload;
   u_long payload_l;
   char errbuf[LIBNET_ERRBUF_SIZE];
    
   while ((c = getopt(argc, argv, "i:c:e:s:E:d:p:i:u:I:t:vh")) != EOF) {
      switch (c) {
         case 'h':
            usage();
            exit(EXIT_SUCCESS);
         case 'v':
            opts[OPT_VERBOSE].set = 1;
            break;
         case 'i':
            interface = optarg;
            opts[OPT_INTERFACE].set = 1;
            break;
         case 'e':
            if ((ether_t = ether_aton(optarg)) == NULL) {
               printf("Invalid source ethernet address: %s\n", optarg);
               return(EXIT_FAILURE);
            }
            if ((src_ether = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
               printf("Couldn't malloc() for src_ether\n");
               return(EXIT_FAILURE);
            }
            memcpy(src_ether->ether_addr_octet, ether_t, sizeof(struct ether_addr));
            opts[OPT_SRCETHER].set = 1;
            break;
         case 'E':
            if ((ether_t = ether_aton(optarg)) == NULL) {
               printf("Invalid ethernet address: %s\n", optarg);
               return(EXIT_FAILURE);
            }
            if ((dst_ether = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
               printf("Couldn't malloc() for dst_ether\n");
               return(EXIT_FAILURE);
            }
            memcpy(dst_ether->ether_addr_octet, ether_t, sizeof(struct ether_addr));
            opts[OPT_DSTETHER].set = 1;
            break;
         case 's':
            src_ip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE);
            opts[OPT_SRCIP].set = 1;
            break;
         case 'd':
            dst_ip = libnet_name2addr4(libnet, optarg, LIBNET_RESOLVE);
            opts[OPT_DSTIP].set = 1;
            break;
         case 't':
            ip_ttl = atoi(optarg);
            opts[OPT_IPTTL].set = 1;
            break;
         case 'c':
            payload = optarg;
            payload_l = strlen(payload);
            opts[OPT_PAYLOAD].set = 1;
            break;
         case 'I':
            ip_id = atoi(optarg);
            opts[OPT_IPID].set = 1;
            break;
         case 'p':
            ip_proto = atoi(optarg);
            opts[OPT_IPPROTO].set = 1;
            break;
         default:
            usage();
            goto pass;
            break;
      }
   }

   /* Ensure that an interface is specified */
   if (!opts[OPT_INTERFACE].set) {
      usage();
      goto fail;
   }

   /* Depending on whether or not they choose to pick their own MACs,
    * libnet needs to be initialized differently for some reason */
   if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
      if ((libnet = libnet_init(LIBNET_LINK, interface, errbuf)) == NULL) {
         fprintf(stderr, "libnet_init() failed: %s", errbuf);
         goto fail;
      }
   } else {
      if ((libnet = libnet_init(LIBNET_RAW4, interface, errbuf)) == NULL) {
         fprintf(stderr, "libnet_init() failed: %s", errbuf);
         goto fail;
      }
   }

   libnet_seed_prand(libnet);

   /* If they didn't set a source ethernet address, figure out */
   if (!opts[OPT_SRCETHER].set) {
      if ((src_ether = libnet_get_hwaddr(libnet)) == NULL) {
         fprintf(stderr, "libnet_get_hwaddr() failed: %s", errbuf);
         libnet_destroy(libnet);
         goto fail;
      }
   }

   /* If they didn't set a destination ethernet address, figure out */
   if (!opts[OPT_DSTETHER].set) {
      if ((dst_ether = libnet_get_hwaddr(libnet)) == NULL) {
         fprintf(stderr, "libnet_get_hwaddr() failed: %s", errbuf);
         libnet_destroy(libnet);
         goto fail;
      }
   }

   /* If they didn't set a source IP, have libnet figure it out */
   if (!opts[OPT_SRCIP].set) {
      src_ip = libnet_get_ipaddr4(libnet);
      opts[OPT_SRCIP].set = 1;
   } 

   /* If no IP id is set, pick a random one */
   if (!opts[OPT_IPID].set) {
      ip_id = libnet_get_prand(LIBNET_PRu16);
      opts[OPT_IPID].set = 1;
   }
   
   /* If no IP TOS is set, pick 0 */
   if (!opts[OPT_IPTOS].set) {
      ip_tos = 0;
      opts[OPT_IPTOS].set = 1;
   }
   
   /* No payload? Then NULL */
   if (!opts[OPT_PAYLOAD].set) {
      payload = NULL;
      payload_l = 0;
      opts[OPT_PAYLOAD].set = 1;
   }

   if (!opts[OPT_IPPROTO].set) {
      ip_proto = libnet_get_prand(LIBNET_PRu16);
      opts[OPT_IPPROTO].set = 1;
   }
       
   /* If no TTL is set, default to something sly. */
   if (!opts[OPT_IPTTL].set) {
      /* 255 is too high, others are too low.  I'll take the midground! */
      ip_ttl = 200 + (libnet_get_prand(LIBNET_PR8) % 55);
      opts[OPT_IPTTL].set = 1;
   }
       
   /* make sure that everything that needs to be set is set... */
   for (i = 0; i < 10; i++) {
      if (opts[i].req && !opts[i].set) {
         fprintf(stderr, "No %s specified\n\n", opts[i].name);
         usage();
         libnet_destroy(libnet);
         goto fail;
      }
   }       

   ipv4 = libnet_build_ipv4(
            LIBNET_IPV4_H + payload_l,  /* length */
            ip_tos,        /* TOS */
            ip_id,         /* ID */
            IP_DF,         /* Frag */
            ip_ttl,        /* TTL */
            ip_proto,      /* Protocol */
            0,             /* Sum */
            src_ip,        /* source IP */
            dst_ip,        /* destination IP */
            payload,       /* Payload */
            payload_l,     /* Payload size */
            libnet,
            0);
    
    if (ipv4 == -1) {
        fprintf(stderr, "Can't build ipv4: %s\n", libnet_geterror(libnet));
        libnet_destroy(libnet);
        goto fail;
    }
   
   /* If they wish to set their own src/dst ethernet addresses
    * we need to actually build the ethernet.  If they don't,
    * libnet does it automagically for us. */ 
   if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
      ether = libnet_build_ethernet(
               dst_ether->ether_addr_octet,      /* Destination ethernet address */
               src_ether->ether_addr_octet,      /* Source ethernet address */
               ETHERTYPE_IP,   /* Protocol type */
               NULL,           /* Payload */
               0,              /* Payload size */
               libnet,
               0);

      if (ether == -1) {
         fprintf(stderr, "Can't build ethernet: %s\n", libnet_geterror(libnet));
         libnet_destroy(libnet);
         goto fail;
      }
   }

   if ((c = libnet_write(libnet)) == -1) {
      fprintf(stderr, "Couldn't write packet to the wire: %s\n", libnet_geterror(libnet));
   } else {
      if (opts[OPT_VERBOSE].set) {
         if (opts[OPT_SRCETHER].set || opts[OPT_DSTETHER].set) {
            printf("Link Layer: ");
            printf("%s -> ", opts[OPT_SRCETHER].set ? ether_ntoa((struct ether_addr *) src_ether->ether_addr_octet) : interface);
            printf("%s\n",  opts[OPT_DSTETHER].set ? ether_ntoa((struct ether_addr *) dst_ether->ether_addr_octet) : "(auto)");
         }
         printf("  IP Layer: %s -> %s\n", libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE), libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE));
         printf("  IP Layer: TTL: %d TOS: 0x%X ID: %d ", ip_ttl, ip_tos, ip_id);
         printf("Protocol: %d (%s)\n", ip_proto, (protoent = getprotobynumber(ip_proto)) != NULL ? protoent->p_name : "unknown");
         printf(" App Layer: %ld byte payload\n", payload_l);
      }
   }

   libnet_destroy(libnet);
   goto pass;

fail:
   if (opts[OPT_SRCETHER].set) {
      free(src_ether);
   }
   if (opts[OPT_DSTETHER].set) {
      free(dst_ether);
   }
   return(EXIT_FAILURE);

pass:
   if (opts[OPT_SRCETHER].set) {
      free(src_ether);
   }
   if (opts[OPT_DSTETHER].set) {
      free(dst_ether);
   }
   return(EXIT_SUCCESS);

}
