#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>

#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 main(int argc, char **argv)
{
	char buffer[1600];
	char eth_device[MAX_ETH_STR + 1] = ETH_DEVICE;

	int nfd, leidos, opc, foreground = 0;
	fd_set fds;
	int i;

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

	fprintf(stderr, "Attached to %s \n", eth_device);

	FD_ZERO(&fds);
	FD_SET(nfd, &fds);

	for (i = 0; i < 10; i++) {
		if (select(nfd + 1, &fds, NULL, NULL, NULL) < 0) {
			perror("Error in select()");
			return 4;
		}
		if (FD_ISSET(nfd, &fds)) {
			leidos = read(nfd, buffer, sizeof(buffer));
			if (leidos == -1) {
				perror("Error reading from ethernet interface");
				return 2;
			}
#ifdef DEBUG
			show_buffer(buffer, leidos);
#endif
		}
		FD_ZERO(&fds);
		FD_SET(nfd, &fds);
	}

	return 0;
}