/* 
 * Arp toolkit
 *
 * Simple frontend for quickly crafting arp packets.
 *
 * Jon Hart <warchild@spoofed.org>
 * Sat Aug 31 13:06:48 EDT 2002
 * 
 * Requires:
 *      libnet >= 1.1.x (http://www.packetfactory.net/projects/libnet/)
 *      root
 *
 * Compile with something like:
 *  gcc -Wall `libnet-config --defines --cflags` -o arp-tk arp-tk.c `libnet-config --libs`
 *
 * Sample uses where 10.0.0.1 is your gateway, eth0 is your default interface
 *
 * (arp spoof the gateway to intercept traffic)
 * arp-tk -i eth0 -o rep -p 10.0.0.1
 *
 * (arp ping some host)
 * arp-tk -i eth0 -o req -P 10.0.0.2 
 *
 * (act as a rarp client ("I have this hardware address. What is my IP?"))
 * arp-tk -i eth0 -v -o rreq -S 1:2:3:4:5:6 -D 1:2:3:4:5:6 
 * 
 * (act as a rarpd server for a:b:c:d:e:f/1.2.3.4)
 * arp-tk -i eth0 -o rrep -D 1:2:3:4:5:6 -P 1.2.3.4
 *
 * 
 * Copyright (c) 2002, 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

#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_SRCARP      7
#define OPT_DSTARP      8
#define OPT_ARPOP       9
#define OPT_ARPLOAD      10

/* I hate the 'options' thing that people use for getopt.  How useless */
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", 1, 0},
    {"destination ethernet address", 1, 0},
    {"source arp address", 1, 0},
    {"destination arp address", 1, 0},
    {"arp operation", 1, 0},
    {"arp payload", 0, 0}
};

void usage() {
   fprintf(stderr, "\tarp-tk\n\tA simple ARP Toolkit\n\tby Jon Hart <warchild@spoofed.org>\n");
   fprintf(stderr, "\thttp://spoofed.org/files/arp-tk.c\n");
   fprintf(stderr, "\nUsage:\n");
   fprintf(stderr, "\n\tRequired:\n");
   fprintf(stderr, "\t-i <interface>     Specify interface\n");
   fprintf(stderr, "\t-o <arp operation> One of req, rep, rreq, rrep, ireq, irep\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 adddress (default: FF:FF:FF:FF:FF:FF)\n");
   fprintf(stderr, "\tARP Layer:\n");
   fprintf(stderr, "\t-p <src IP>        Source IP address (default: auto)\n");
   fprintf(stderr, "\t-P <dst IP>        Destination IP address (default: 0.0.0.0)\n");
   fprintf(stderr, "\t-d <src arp>       Source arp address (default: auto)\n");
   fprintf(stderr, "\t-D <dst arp>       Destination arp address (default: FF:FF:FF:FF:FF:FF)\n");
   fprintf(stderr, "\t-c <payload>       Put a payload in the arp packet.  Serves no purpose.  Or does it?\n");
   fprintf(stderr, "\t-h                 Help\n");
   fprintf(stderr, "\t-v                 Be verbose\n");
}

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

    libnet_t *libnet;
    libnet_ptag_t arp, ether;

   u_long src_ip, dst_ip;
   u_short arpop, ethertype;

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

    /* The source and destination MAC addresses
     * for the arp level
     */
    struct libnet_ether_addr *src_arp; 
    struct libnet_ether_addr *dst_arp; 

    char *interface;
    struct ether_addr *ether_t;
    char errbuf[LIBNET_ERRBUF_SIZE];
    
    char *arp_payload;
    int arp_payload_l;

    u_char broadcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

    while ((c = getopt(argc, argv, "i:c:o:e:d:E:D:p:P: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 'c':
                arp_payload = optarg;
                arp_payload_l = strlen(arp_payload);
                opts[OPT_ARPLOAD].set = 1;
                break;
            case 'p':
                src_ip = libnet_name2addr4(libnet, optarg, LIBNET_DONT_RESOLVE);
                opts[OPT_SRCIP].set = 1;
                break;
            case 'P':
                dst_ip = libnet_name2addr4(libnet, optarg, LIBNET_DONT_RESOLVE);
                opts[OPT_DSTIP].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 'd':
               if ((ether_t = ether_aton(optarg)) == NULL) {
                  printf("Invalid source arp address: %s\n", optarg);
                  return(EXIT_FAILURE);
               }
               if ((src_arp = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
                  printf("Couldn't malloc() for src_arp\n");
                  return(EXIT_FAILURE);
               }
               memcpy(src_arp->ether_addr_octet, ether_t, sizeof(struct ether_addr));
               opts[OPT_SRCARP].set = 1;
               break;
            case 'E':
               if ((ether_t = ether_aton(optarg)) == NULL) {
                    printf("Invalid destination 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 'D':
               if ((ether_t = ether_aton(optarg)) == NULL) {
                  printf("Invalid destination arp address: %s\n", optarg);
                  return(EXIT_FAILURE);
               }
               if ((dst_arp = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
                  printf("Couldn't malloc() for dst_arp\n");
                  return(EXIT_FAILURE);
               }  
               memcpy(dst_arp->ether_addr_octet, ether_t, sizeof(struct ether_addr));
               opts[OPT_DSTARP].set = 1;
               break;
            case 'o':
                if (strncmp(optarg, "req", 3) == 0) {
                    arpop = ARPOP_REQUEST;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else if (strncmp(optarg, "rep", 3) == 0) {
                    arpop = ARPOP_REPLY;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else if (strncmp(optarg, "rreq", 4) == 0) {
                    arpop = ARPOP_REVREQUEST;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else if (strncmp(optarg, "rrep", 4) == 0) {
                    arpop = ARPOP_REVREPLY;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else if (strncmp(optarg, "ireq", 4) == 0) {
                    arpop = ARPOP_INVREQUEST;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else if (strncmp(optarg, "irep", 4) == 0) {
                    arpop = ARPOP_INVREPLY;
                    opts[OPT_ARPOP].set = 1;
                    break;
                } else {
                    fprintf(stderr, "Invalid arp operation %s.  Must be one of req, rep, rreq, rrep, ireq, or irep\n", optarg);
                    opts[OPT_ARPOP].set = 0;
                   goto fail; 
                }
            default:
                usage();
                goto fail;
        }
    }

    if (!opts[OPT_INTERFACE].set) {
        usage();
        goto fail;
    }

    if ((libnet = libnet_init(LIBNET_LINK, interface, errbuf)) == NULL) {
        fprintf(stderr, "libnet_init() failed: %s", errbuf);
        goto fail;
    }

    if (!opts[OPT_SRCETHER].set) {
        src_ether = libnet_get_hwaddr(libnet);
        if (src_ether == NULL) {
            fprintf(stderr, "No source ethernet address specified, and I couldn't guess it: %s\n", libnet_geterror(libnet));
            libnet_destroy(libnet);
            goto fail;
        }
    }

   if (!opts[OPT_DSTETHER].set) {
      if ((dst_ether = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
         printf("Couldn't malloc() for dst_ether\n");
         libnet_destroy(libnet);
         goto fail;
      }
      memcpy(dst_ether->ether_addr_octet, broadcast, sizeof(broadcast));
      opts[OPT_DSTETHER].set = 1; 
    }

   if (!opts[OPT_SRCARP].set) {
      if ((src_arp = libnet_get_hwaddr(libnet)) == NULL) {
         if ((src_arp = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
            printf("Couldn't malloc() for src_arp\n");
            libnet_destroy(libnet);
            goto fail;
         }
         memcpy(src_arp->ether_addr_octet, broadcast, sizeof(broadcast));
      }
    }

    if (!opts[OPT_DSTARP].set) {
      if ((dst_arp = malloc(sizeof(struct libnet_ether_addr))) == NULL) {
         printf("Couldn't malloc() for dst_arp\n");
         libnet_destroy(libnet);
         goto fail;
      }
      memcpy(dst_arp->ether_addr_octet, broadcast, sizeof(broadcast));
      opts[OPT_DSTARP].set = 1;
    }

    if (!opts[OPT_SRCIP].set) {
        src_ip = libnet_get_ipaddr4(libnet);
        if (src_ip == -1) {
            fprintf(stderr, "No source IP specified and I couldn't guess it: %s\n", libnet_geterror(libnet));
            libnet_destroy(libnet);
            goto fail;
        }
        opts[OPT_SRCIP].set = 1;
    }

    if (!opts[OPT_DSTIP].set) {
      dst_ip = libnet_name2addr4(libnet, "0.0.0.0", LIBNET_RESOLVE);
      opts[OPT_DSTIP].set = 1;
    } 

    if (!opts[OPT_ARPLOAD].set) {
        arp_payload = NULL;
        arp_payload_l = 0;
        opts[OPT_ARPLOAD].set = 1;
    }

    if (opts[OPT_ARPOP].set) {
        /* inverse arp is simply an extension of regular arp.
         * reverse arp is all sorts of special, so it has its
         * own ethertype.
         * just make sure we use the right one...
         */
        switch (arpop) {
            case ARPOP_REVREQUEST:
                ethertype = ETHERTYPE_REVARP;
                break;
            case ARPOP_REVREPLY:
                ethertype = ETHERTYPE_REVARP;
                break;
            default:
                ethertype = ETHERTYPE_ARP;
                break;
        }
    }

    /* build the arp part of the packet */
    arp = libnet_build_arp(
            ARPHRD_ETHER,
            ETHERTYPE_IP,
            ETHER_ADDR_LEN, 
            4, 
            arpop,
            src_arp->ether_addr_octet,
            (u_char *)&src_ip,
            dst_arp->ether_addr_octet,
            (u_char *)&dst_ip,
            arp_payload,
            arp_payload_l,
            libnet,
            0);

    if (arp == -1) {
        fprintf(stderr, "Can't build arp: %s\n", libnet_geterror(libnet));
        libnet_destroy(libnet);
        goto fail;
    }
        
    /* sprinkle some ethernet */
    ether = libnet_build_ethernet(
                dst_ether->ether_addr_octet,
                src_ether->ether_addr_octet,
                ethertype,
                NULL,
                0,
                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) { 
            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("ARP Layer: ");
            switch (arpop) { 
               case ARPOP_REQUEST:
                  printf("arp who-has %s tell %s\n", 
                        libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE),
                        libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE));
                  break;
               case ARPOP_REPLY:
                  printf("arp reply %s is at %s ",
                     libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE),
                     ether_ntoa((struct ether_addr *) src_arp->ether_addr_octet));
                  printf("tell %s (%s)\n\n",
                        libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE),
                        ether_ntoa((struct ether_addr *) dst_arp->ether_addr_octet));
                  break;
               case ARPOP_INVREQUEST:
                  printf("inverse arp who is %s (%s) ",
                     ether_ntoa((struct ether_addr *) dst_arp->ether_addr_octet),
                     libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE));
                  printf("tell %s (%s)\b\n",
                     ether_ntoa((struct ether_addr *) src_arp->ether_addr_octet),
                     libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE));
                  break;
               case ARPOP_INVREPLY:
                  printf("inverse arp %s is at %s ",
                     ether_ntoa((struct ether_addr *) src_arp->ether_addr_octet),
                     libnet_addr2name4(src_ip, LIBNET_DONT_RESOLVE));
                  printf("tell %s (%s)\n\n",
                        libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE),
                        ether_ntoa((struct ether_addr *) dst_arp->ether_addr_octet));
                  break;
               case ARPOP_REVREQUEST:
                  printf("rarp who is %s ",
                     ether_ntoa((struct ether_addr *) dst_arp->ether_addr_octet));
                  printf("tell %s\n\n",
                     ether_ntoa((struct ether_addr *) src_arp->ether_addr_octet));
                  break;
               case ARPOP_REVREPLY:
                  printf("rarp %s is at %s\n\n",
                     ether_ntoa((struct ether_addr *) dst_arp->ether_addr_octet),
                     libnet_addr2name4(dst_ip, LIBNET_DONT_RESOLVE));
                  break;
            }
        }
    }

    goto pass;

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

   if (opts[OPT_SRCARP].set) {
      free(src_arp);
   }
   if (opts[OPT_DSTARP].set) {
      free(dst_arp);
   }
   return(EXIT_SUCCESS);


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

   if (opts[OPT_SRCARP].set) {
      free(src_arp);
   }
   if (opts[OPT_DSTARP].set) {
      free(dst_arp);
   }
   return(EXIT_FAILURE);

}
