#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/netfilter.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>

/* packet data (65535) + packet length (5) + safety(1) */
#define BUFSIZE 65541

#define ETH_DEVICE "eth0"

#define MAX_ETH_STR 30

#define ERR_IOCTL -2
#define ERR_BIND -3

/* #define DEBUG */

void show_buffer(unsigned char *buf, int len)
{
	int i, j;
	char str[17];

	str[16] = 0;
	printf("\n");
	for (i = 0; i < len; i += 16) {
		fprintf(stdout, "   ");
		for (j = 0; j < 16 && i + j < len; j++) {
			fprintf(stdout, "%02x ", buf[i + j]);

			if (buf[i + j] > 32 && buf[i + j] < 127)
				str[j] = buf[i + j];
			else
				str[j] = '.';
		}
		for (; j < 16; j++) {
			fprintf(stdout, "   ", buf[i + j]);
			str[j] = ' ';
		}
		printf("   |   %s   |\n", str);
	}
	fflush(stdout);
}

int open_eth_device(const char *device)
{
	struct ifreq ifr;
	int sock_fd;
	int if_index;
	struct sockaddr_ll sll;

	/* linux/if_ether.h: ETH_P_ALL: Every packet */
	sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

	strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));

	/* netdevice(7): Retrieve the interface index */
	if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) < 0) {
		fprintf(stderr, "Error! Can not retrieve interface index.\n");
		perror("ioctl SIOCGIFINDEX");
		return ERR_IOCTL;
	}
	if_index = ifr.ifr_ifindex;

	/* netdevice(7): Get the active flag word of the device. */
	if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) < 0) {
		perror("Error Getting the active flag word of the device "
		       "(SIOCGIFFLAGS ioctl)");
		return ERR_IOCTL;
	}

	/* Promiscuous mode */
	ifr.ifr_flags |= IFF_PROMISC;

	/* netdevice(7): Set the active flag word of the device. */
	if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) < 0) {
		perror("Error setting promiscuous mode (SIOCSIFFLAGS ioctl)");
		return ERR_IOCTL;
	}

	/* packet(7): netpacket/packet.h */
	memset(&sll, 0, sizeof(sll));
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = if_index;
	sll.sll_protocol = htons(ETH_P_ALL);
	if (bind(sock_fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
		fprintf(stderr, "Error while binding to %s ", device);
		perror("[bind()]");
		return ERR_BIND;
	}
	return sock_fd;
}

int raw_send(unsigned char *buf, int len)
{
	char eth_device[MAX_ETH_STR + 1] = ETH_DEVICE;

	int nfd, leidos;
	fd_set fds;
	int i;
	unsigned char header[] = { 0x00, 0x24, 0x01, 0x0f, 0x18, 0xd8, 0x00,
		0x26, 0x5a, 0x70, 0xb6, 0x6e, 0x08, 0x00
	};
	unsigned char *packet = (unsigned char *)malloc(len + 14);

	memcpy(packet, header, 14);
	memcpy(packet + 14, buf, len);

	if ((nfd = open_eth_device(eth_device)) < 0) {
		fprintf(stderr, "Error opening ETH device\n");
		return -1;
	}

	if (write(nfd, packet, len + 14) == -1)
		perror("write() error");
	else
		printf("%d ", len);
	fflush(stdout);
#ifdef DEBUG
	show_buffer(buf, len);
#endif
	close(nfd);
	return 0;
}

int get_char()
{
	int buf[1];
	ssize_t ret;

	ret = read(0, (void *)buf, 1);
	if (ret == -1) {
		perror("Error! read()");
		exit(EXIT_FAILURE);
	}
	if (ret == 0) {
		return EOF;
	}

	return *buf;
}

int get_len()
{
	int i, ch, factor, len;

	factor = 10000;
	len = 0;
	for (i = 0; i < 5; i++) {
		ch = get_char();
		ch = (unsigned char)ch - 48;
		len += (ch * factor);
		factor /= 10;
	}
	if (len < 0 || len > 65535) {
		printf("Packet length Error: %d", len);
		exit(EXIT_FAILURE);
	}

	return len;
}

void get_data()
{
	int i, len;
	unsigned char buf[BUFSIZE] = { 0 };

	len = get_len();

	for (i = 0; i < len; i++)
		buf[i] = get_char();

	raw_send(buf, len);
}

int main(void)
{
	while (1)
		get_data();
	return 0;
}