/* (C) kanda@nn.iij4u.or.jp */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>

#include <net/if.h>
#include <sys/ioctl.h>

#include <linux/filter.h>

#define ENABLE_PROMISC 	1
#define ENABLE_BPF	1

int main(int argc, char **argv)
{
	int sock, n;
	char buffer[2048];
	unsigned char *iphead, *ethhead;
	struct ifreq	ethreq;

#ifdef ENABLE_BPF
	/* tcpdump -d host 210.237.134.121 and port 22
	(000) ldh      [12]
	(001) jeq      #0x800           jt 2    jf 17
	(002) ld       [26]
	(003) jeq      #0xd2ed8679      jt 6    jf 4
	(004) ld       [30]
	(005) jeq      #0xd2ed8679      jt 6    jf 17
	(006) ldb      [23]
	(007) jeq      #0x6             jt 9    jf 8
	(008) jeq      #0x11            jt 9    jf 17
	(009) ldh      [20]
	(010) jset     #0x1fff          jt 17   jf 11
	(011) ldxb     4*([14]&0xf)
	(012) ldh      [x + 14]
	(013) jeq      #0x16            jt 16   jf 14
	(014) ldh      [x + 16]
	(015) jeq      #0x16            jt 16   jf 17
	(016) ret      #96
	(017) ret      #0
	*/
	struct sock_filter BPF_code[] = {
		{ 0x28, 0, 0, 0x0000000c },
		{ 0x15, 0, 15, 0x00000800 },
		{ 0x20, 0, 0, 0x0000001a },
		{ 0x15, 2, 0, 0xd2ed8679 },
		{ 0x20, 0, 0, 0x0000001e },
		{ 0x15, 0, 11, 0xd2ed8679 },
		{ 0x30, 0, 0, 0x00000017 },
		{ 0x15, 1, 0, 0x00000006 },
		{ 0x15, 0, 8, 0x00000011 },
		{ 0x28, 0, 0, 0x00000014 },
		{ 0x45, 6, 0, 0x00001fff },
		{ 0xb1, 0, 0, 0x0000000e },
		{ 0x48, 0, 0, 0x0000000e },
		{ 0x15, 2, 0, 0x00000016 },
		{ 0x48, 0, 0, 0x00000010 },
		{ 0x15, 0, 1, 0x00000016 },
		{ 0x6, 0, 0, 0x00000060 },
		{ 0x6, 0, 0, 0x00000000 }
	};
	struct sock_fprog Filter;

	Filter.len = 18; /* number of lines BPF_code */
	Filter.filter = BPF_code;
#endif /* ENABLE_BPF */

	if ( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) {
		perror("socket");
		exit(EXIT_FAILURE);
	}
#ifdef ENABLE_PROMISC
	 /* promiscuos mode */
	strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
	if (ioctl(sock,SIOCGIFFLAGS,&ethreq)==-1) {
		perror("ioctl");
		close(sock);
		exit(EXIT_FAILURE);
	}
	ethreq.ifr_flags|=IFF_PROMISC;
	if (ioctl(sock,SIOCSIFFLAGS,&ethreq)==-1) {
		perror("ioctl");
		close(sock);
		exit(EXIT_FAILURE);
	}
#endif /* ENABLE_PROMISC */

#ifdef ENABLE_BPF
	/* Attach the filter to the socket */
	if( setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0 ) {
		perror("setsockopt");
		close(sock);
		exit(EXIT_FAILURE);
	}
#endif /* ENABLE_BPF */

	/*
	 * ether   (14)
	 * ip      (20)
	 * tcp/udp (8)
	 */
	while (1) {
		printf("----------\n");
		n = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
		printf("%d bytes read\n", n);

		if (n<42) {
			perror("recvfrom():");
			printf("Incomplete packet (errno is %d)\n", errno);
			close(sock);
			exit(EXIT_FAILURE);
		}

		ethhead = buffer;
		printf("Source MAC address: "
				"%02x:%02x:%02x:%02x:%02x:%02x\n",
				ethhead[0], ethhead[1], ethhead[2],
				ethhead[3], ethhead[4], ethhead[5]);
		printf("Destination address: "
				"%02x:%02x:%02x:%02x:%02x:%02x\n",
				ethhead[6], ethhead[7], ethhead[8],
				ethhead[9], ethhead[10], ethhead[11]);
		iphead = buffer + 14;
		if (*iphead == 0x45) {
			printf("Source host %d.%d.%d.%d\n",
					iphead[12],iphead[13],
					iphead[14],iphead[15]);
			printf("Dest host %d.%d.%d.%d\n",
					iphead[16], iphead[17],
					iphead[18], iphead[19]);
			printf("Source, Dest ports %d,%d\n",
					(iphead[20]<<8)+iphead[21],
					(iphead[22]<<8)+iphead[23]);
			printf("Layer-4 protocol %d\n", iphead[9]);
		}
	}

	return 0;
}
