/* 
 * main.cpp 
 * Copyright (c) 2004-2006 Vlad GALU <dudu@dudu.ro> 
 *                    Andrei GAVRILOAIE <gavriloaie_andrei@yahoo.com> 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 */

#include <Class.h>

CFlowTree* pFlowTree=NULL;
CHostTree* pHostTree=NULL;
time_t currentTime;


int main(
	int argc, 
	char* argv[])
{
	pcap_t* pPcap = NULL;
	char  errbuff[PCAP_ERRBUF_SIZE];
	struct bpf_program filter;
	int ipLayerOffset = 0;
	time_t lastHostCleanup;
	time_t lastFlowCleanup;
	int packetsCaptured = 0;
	int i;
	pid_t child;

	pFlowTree = new CFlowTree();
	pHostTree = new CHostTree();
			
	
	if (argc != 3) {
		fprintf(stdout, "\nUsage: %s <interface> <filter>\n",argv[0]);
		return -1;
	};

#ifndef _DEBUG
	if (getppid() == 1)
		return -1;
	if ((child = fork()) < 0)
		exit(1);
	else if (child > 0) 
			exit(0);
	setsid();
	for (i = getdtablesize(); i >= 0; i--)
		close(i);
	signal(SIGCHLD, SIG_IGN); 
	signal(SIGTSTP, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	signal(SIGTTIN, SIG_IGN);
#endif	

	dprintf ("Opening interface %s... ", argv[1]);
	if ((pPcap = pcap_open_live(argv[1], 
								SNAPLEN, 1, 10, errbuff)) == NULL) {
		syslog(LOG_LOCAL6|LOG_ALERT, 
			   "%s %d Cannot open pcap device: %s",
				__FILE__, __LINE__, errbuff);
		dprintf("\n%s %d Cannot open pcap device: %s\n",
				__FILE__, __LINE__, errbuff);
		return -1;
	};
	dprintf("done\n");

	
	dprintf("Compiling filter `%s`... ", argv[2]);
	if (pcap_compile(pPcap, &filter, argv[2], 1, 0x00000000) < 0) {
		syslog(LOG_LOCAL6|LOG_ALERT, 
			   "%s %d Cannot compile filter: %s",
				__FILE__, __LINE__, pcap_geterr(pPcap));
		dprintf("\n%s %d Cannot compile filter: %s\n",
				__FILE__, __LINE__, pcap_geterr(pPcap));
		return -1;
	};
	dprintf("done\n");

	
	dprintf("Setting filter... ");
	if (pcap_setfilter(pPcap, &filter) < 0) {
		syslog(LOG_LOCAL6|LOG_ALERT, 
			   "%s %d Cannot set compiled filter: %s",
				__FILE__, __LINE__, pcap_geterr(pPcap));
		dprintf("\n%s %d  Cannot set compiled filter: %s\n",
				__FILE__, __LINE__, pcap_geterr(pPcap));
		return -1;
	};
	dprintf("done\n");

	
	dprintf("Detecting IP layer offset in bytes... ");
	ipLayerOffset = getIPLayerOffset(pPcap);
	if (ipLayerOffset < 0) {
		syslog(LOG_LOCAL6|LOG_ALERT,
			   "%s %d Unknown link type",
				__FILE__, __LINE__);
		dprintf("\n%s %d Unknown link type\n",
				__FILE__, __LINE__);
		return -1;
	}
	dprintf("%d done\n", ipLayerOffset);


	lastFlowCleanup = time(NULL);
	lastHostCleanup = time(NULL);
	
	while (true) {
		if (pcap_dispatch(pPcap, -1, handlePacket,
			 (u_char *)&ipLayerOffset) < 0) {
			syslog(LOG_LOCAL6|LOG_ALERT, 
				   "%s %d pcap_dispatch failed with error: %s",
					__FILE__, __LINE__, pcap_geterr(pPcap));
			dprintf("%s %d pcap_dispatch failed with error: %s",
					__FILE__, __LINE__, pcap_geterr(pPcap));
		};
		
		if ((currentTime - lastFlowCleanup) >= 
					TIME_BETWEEN_FLOW_CLEANUPS) {
			lastFlowCleanup = currentTime;
			pFlowTree->cleanUp(cleanupFlowTest);
		};	
		
		if ((currentTime - lastHostCleanup) >= 
					TIME_BETWEEN_HOST_CLEANUPS) {
			lastHostCleanup = currentTime;
			pHostTree->cleanUp(cleanupHostTest);
		};
		
	};
	
	dprintf("Ending pcap session... ");
	pcap_close(pPcap);
	dprintf("done\n");
}

int 
getIPLayerOffset(pcap_t* pPcap) {
	int ipLayerOffset;

	switch(pcap_datalink(pPcap)) {
	case DLT_NULL:
	case DLT_LOOP:
		ipLayerOffset = 4;
		break;
	case DLT_EN10MB:
		ipLayerOffset = ETHSIZE;
		break;
	case DLT_IEEE802:
		ipLayerOffset = TRSIZE;
		break;
	case DLT_FDDI:
		ipLayerOffset = FDDISIZE;
		break;
	case DLT_SLIP:
		ipLayerOffset = SLIPSIZE;
		break;
	case DLT_PPP:
		ipLayerOffset = PPPSIZE;
		break;
	case DLT_RAW:
		ipLayerOffset = RAWSIZE;
		break;
	case DLT_LINUX_SLL:
		ipLayerOffset = ISDNSIZE;
		break;
	default:
		ipLayerOffset = -1;
		break;
	}

	return ipLayerOffset;
}

void 
handlePacket(
	u_char *pIPLayerOffsetV, 
	const pcap_pkthdr* pPcapHeader, 
	const u_char* pPacketData)
{
	struct ip& ipLayer = *((struct ip*)(pPacketData + *((int*)pIPLayerOffsetV)));
	in_addr& srcAddr = ipLayer.ip_src;
	in_addr& dstAddr = ipLayer.ip_dst;
	unsigned fragmented = ntohs(ipLayer.ip_off) & (IP_MF | IP_OFFMASK);
#if defined(AIX)
#undef ip_hl
	unsigned ip_hl = ipLayer.ip_ff.ip_fhl * 4;
#else
	unsigned ip_hl = ipLayer.ip_hl * 4;
#endif
	u_int packetLen = ntohs(ipLayer.ip_len);;
	u_char zero = 0;
	time_t t_zero = 0;
	currentTime = pPcapHeader->ts.tv_sec;

	if (!fragmented) {
		switch (ipLayer.ip_p) {
		case 6: {
			struct tcphdr& tcp = *((struct tcphdr*)(((char*)&ipLayer) + ip_hl));
			
			if (pFlowTree->insertNode
				(
				(in_addr&)ipLayer.ip_src,
				(in_addr&)ipLayer.ip_dst,
				tcp.th_sport,
				tcp.th_dport,
				tcp.th_flags,
				(u_char&)ipLayer.ip_p,
				(u_char&)ipLayer.ip_tos,
				packetLen,
				(time_t&)pPcapHeader->ts.tv_sec
				))
			{
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)pPcapHeader->ts.tv_sec, 
									  (time_t&)t_zero);
			} else
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)t_zero, 
									  (time_t&)pPcapHeader->ts.tv_sec);
			break; }
		
		case 17: {
			struct udphdr& udp = *((struct udphdr*)(((char*)&ipLayer) + ip_hl));
			
			if (pFlowTree->insertNode
				(
				(in_addr&)ipLayer.ip_src,
				(in_addr&)ipLayer.ip_dst,
				udp.uh_sport,
				udp.uh_dport,
				zero,
				(u_char&)ipLayer.ip_p,
				(u_char&)ipLayer.ip_tos,
				packetLen,
				(time_t&)pPcapHeader->ts.tv_sec
				))
			{
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)pPcapHeader->ts.tv_sec, 
									  (time_t&)t_zero);
			} else
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)t_zero, 
									  (time_t&)pPcapHeader->ts.tv_sec);
			break; }
		
		case 1: {
			struct icmp& icmp = *((struct icmp*)(((char*)&ipLayer) + ip_hl));
			u_short type = (u_short)icmp.icmp_type;
			u_short code = (u_short)icmp.icmp_code;
			
			if (pFlowTree->insertNode (
				(in_addr&)ipLayer.ip_src,
				(in_addr&)ipLayer.ip_dst,
				type, // sport
				code, // dport
				zero,
				(u_char&)ipLayer.ip_p,
				(u_char&)ipLayer.ip_tos,
				packetLen,
				(time_t&)pPcapHeader->ts.tv_sec
				))
			{
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)pPcapHeader->ts.tv_sec, 
									  (time_t&)t_zero);
			} else
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)t_zero, 
									  (time_t&)pPcapHeader->ts.tv_sec);
			break; } 
		
		default: {
			if (pFlowTree->insertNode (
				(in_addr&)ipLayer.ip_src,
				(in_addr&)ipLayer.ip_dst,
				(u_short&)t_zero,
				(u_short&)t_zero,
				zero,
				(u_char&)zero,
				(u_char&)ipLayer.ip_tos,
				packetLen,
				(time_t&)pPcapHeader->ts.tv_sec
				))
			{
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst,
									  (time_t&)pPcapHeader->ts.tv_sec,
									  (time_t&)t_zero);
			} else
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst,
									  (time_t&)t_zero,
									  (time_t&)pPcapHeader->ts.tv_sec);
			break; }
		}
	} else if (pFlowTree->insertNode(
			  (in_addr&)ipLayer.ip_src,
    		  (in_addr&)ipLayer.ip_dst,
			  (u_short&)t_zero,		// set these to 0,
			  (u_short&)t_zero,		// sport = dport = 0 => flow :)
			  zero,					// no tcp flags
			  (u_char&)zero,			// set protocol to IP
			  (u_char&)ipLayer.ip_tos,
			  packetLen,				// this remains uninitialized
			  (time_t&)pPcapHeader->ts.tv_sec))
			{
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)pPcapHeader->ts.tv_sec, 
									  (time_t&)t_zero);
			} else
				pHostTree->insertNode((in_addr&)ipLayer.ip_dst, 
									  (time_t&)t_zero, 
									  (time_t&)pPcapHeader->ts.tv_sec);
};

bool 
cleanupFlowTest(
	void* current, 
	void* udata)
{
	CFlowNode& existingNode = *((CFlowNode*)current);
	if ((currentTime - existingNode.m_flow.records.last) > FLOW_LIFETIME)
		return true;
	return false;
};

bool 
cleanupHostTest(
	void* current, 
	void* udata)
{
	CHostNode& existingNode = *((CHostNode*)current);
	if ((((currentTime - existingNode.m_lastFlow) > HOST_LIFETIME) &&
	    ((currentTime - existingNode.m_lastPacket) > HOST_LIFETIME)) ||
		(existingNode.m_nrFlows <= 0))
		return true;
	return false;
};

// vi:ts=4



syntax highlighted by Code2HTML, v. 0.9.1