/* run.c 		The main command loop
 *
 * $Id: run.c,v 1.10 1995/03/19 17:21:06 bdale Exp $
 *
 * Copyright 1991, Michael Westerhof, Sun Microsystems, Inc.
 * This software may be freely used, distributed, or modified, providing
 * this header is not removed.
 *
 */

#include "ipip.h"

#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <fcntl.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
extern int errno;

static int handle();
static int find_route();
static void tracer();

int martian_count = 3;
unsigned int martians[] = { 0x7f000001, 0xffffffff, 0x00000000 };

time_t last_send;
/* This is a loop counter for misc stuff.  We don't want to burden the
   I/O system if we don't have to, so this is designed so that we will
   be able to see if we have to print stats or fflush() no more often
   than once per second if we are busy.  If we are idle, the time value
   to the select() call will bring the delay up to 10 seconds.  So, the
   stats output and the flush of stdio() should be within 10 seconds of
   the correct time. */
#define SEND_CHECK_CNT 100;

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Open all io, then start running it! 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
int
run_it()
{
	int i, j, nb, more_io, checker;
	fd_set readfds;
	struct timeval wait;

	for(i=0;i<ifs_top;i++){
		if(ifs[i].type == IF_TYPE_NONE)continue;
		if(debugd)
		    syslog(LOG_DEBUG,"opening interface %s",ifs[i].id);
		j = ifs[i].ifopen(&ifs[i]);
		if(j<0)return j;
	}

	if(debugd)
	    syslog(LOG_DEBUG,"initialization complete");

	last_send = time(NULL);
	checker = 0;

	for(;;){

		FD_ZERO(&readfds);

		more_io = 0;

		for(i=0;i<ifs_top;i++){
			if((ifs[i].type != IF_TYPE_NONE) &&
			   (ifs[i].status & IF_STAT_OPEN)){
				FD_SET(ifs[i].fd, &readfds);
				if(ifs[i].status & IF_STAT_CALL_AGAIN)more_io++;
			}
		}

		if(more_io){
			wait.tv_sec =  0;	/* Don't wait long! */
			wait.tv_usec = 10000;	/* 10 msecs if chars in buffer */
		} else {
			wait.tv_sec =  10;	/* lets keep things going... */
			wait.tv_usec = 0;	/* but slowly is ok */
			checker = 0;		/* make sure we check the time */
		}

		nb = select(FD_SETSIZE,&readfds,(fd_set *)0,(fd_set *)0,&wait);

		if(nb < 0){
			if(errno == EINTR)continue;	/* Ignore this error */
			PERR("select in run_it()");
			return -1;
		}

		for(i=0;i<ifs_top;i++){
			if((ifs[i].type != IF_TYPE_NONE) &&
			   (ifs[i].status & IF_STAT_OPEN)){
				if(FD_ISSET(ifs[i].fd, &readfds) ||
				  (ifs[i].status & IF_STAT_CALL_AGAIN)){
					if(handle(&ifs[i])<0) {
						PERR("handle in run_it()");
						return -1;
					}
				}
			}
		} /* for each interface */

		if(checker <= 0){
			send_stats(0);			/* see if we must */
			checker = SEND_CHECK_CNT;	/* reset counter */
		} else {
			checker--;
		}

	} /* forever */
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Handle the read/route/send stuff
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static int
handle(ifp)
struct interface *ifp;
{
	struct message m;
	int n;
#ifdef AMPRONLY
	unsigned int srcip, dstip;
#endif

	m.length = 0;
	m.from_if = ifp;
	m.to_if = NULL;
	m.fip = 0;
	m.tip = 0;
	m.fport = 0;
	m.tport = 0;

	n = ifp->ifread(ifp, &m);
	if(n <= 0) return n;		/* error or packet not yet complete */

	m.from_if->in++;

	if(find_route(&m) < 0) {
		PERR("find_route() failed in handle()");
		return -1;
	}

#ifdef AMPRONLY
	/* this is a really, really rude hack... it will prevent any frames
	   from being switched that have source or destination outside of 
	   net 44 */

	(void)memcpy( (char *)&srcip, (char *)m.msg + 12, 4);
	(void)memcpy( (char *)&dstip, (char *)m.msg + 16, 4);

#ifdef notdef
	syslog(LOG_DEBUG,"from ip %lx, to ip %lx", srcip, dstip);
#endif

	if ((( srcip & 0xff ) != 44) || 
	    (( dstip & 0xff ) != 44)) {
                m.to_if=NULL;                   /* drop frame on the floor */
		if(debugt)tracer(&m);		/* Trace the message */
		return 0;			/* No route found */
        }
#endif

#ifndef BDALE
	/* if this is Bdale's compilation, we need to be able to go back out
	   the same interface! */
	if(m.from_if==m.to_if){
		m.to_if=NULL;			/* Can't go out same iface! */
		m.from_if->looped_in++;
	}
#endif

	if(debugt)tracer(&m);			/* Trace the message */

	if(m.to_if==NULL) {
		PERR("route not found in handle()");
		return 0;		/* No route found */
	}

	if (m.to_if->ifsend(m.to_if, &m) < 0) {
		PERR("ifsend() failed in handle()");
		return -1;
	}
	m.to_if->out++;

	return 1;
}	

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Print a trace of a message
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static void
tracer(m)
struct message *m;
{
	char *fromid, *toid, fbuf[32], tbuf[32];
	unsigned char *f, *t, *iphdr;
	int ftype, ttype;

	fromid = m->from_if->id;
	ftype = m->from_if->type;
	f = (unsigned char *)&m->fip;
	if(ftype == IF_TYPE_IPUDP)
		(void)sprintf(fbuf,"(%d.%d.%d.%d:%d)",f[0],f[1],f[2],f[3],ntohs(m->fport));
	else if(ftype == IF_TYPE_IPIP)
		(void)sprintf(fbuf, "(%d.%d.%d.%d)", f[0], f[1], f[2], f[3]);
	else fbuf[0] = '\0';

	if (m->to_if) {
		toid = m->to_if->id;
		ttype = m->to_if->type;
		t = (unsigned char *)&m->tip;
	} else {
		toid = "BitBucket";
		ttype = IF_TYPE_NONE;
		t = (unsigned char *)&m->tip;
	}
	if(ttype == IF_TYPE_IPUDP)
		(void)sprintf(tbuf,"(%d.%d.%d.%d:%d)",t[0],t[1],t[2],t[3],ntohs(m->tport));
	else if(ttype == IF_TYPE_IPIP)
		(void)sprintf(tbuf, "(%d.%d.%d.%d)",t[0],t[1],t[2],t[3]);
	else tbuf[0] = '\0';

	iphdr = m->msg;

	syslog(LOG_DEBUG,"%d.%d.%d.%d->%d.%d.%d.%d len %d [%s%s->%s%s]",
		iphdr[12], iphdr[13], iphdr[14], iphdr[15],
		iphdr[16], iphdr[17], iphdr[18], iphdr[19], 
		m->length, fromid, fbuf, toid, tbuf);
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * find a route for a message
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */

static int
find_route(m)
struct message *m;
{
	int i;
	unsigned int d;

	if(m==NULL) return -1;		/* major internal problem :-) */

	if(m->length < 20){
		m->to_if = NULL;
		return 0;
	}

	(void)memcpy( (char *)&d, (char *)m->msg + 16, 4);

	for(i=0;i<martian_count;i++){
		if(d == htonl(martians[i])){		/* Bad address! */
			m->from_if->martians_in++;
			m->to_if = NULL;
			return 0;
		}
	}

	for(i=0;i<rts_top;i++){
		if((rts[i].ipaddr & rts[i].mask) == (d & rts[i].mask)){
			m->to_if = rts[i].destif;
			m->tip = rts[i].destaddr;
			m->tport = rts[i].destport;
			rts[i].hits++;
			return 1;
		}
	}
	m->to_if = NULL;
	return 0;
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Send the statistics if it's time to do so...
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */

void
send_stats(force)
int force;			/* non-zero to force a stat print */
{
	int i;
	time_t now;
	struct tm *t;
	char cnow[32];

	if(stat_interval==0) return;	/* no stat print at all? */

	now = time(NULL);

	if(force==0){			/* if not being forced */
		if(stat_interval>0){		/* and the interval is > 0 */
			if(now < (last_send + stat_interval)) return;
		}
	}

	if(no_timestamp){
		cnow[0] = '\0';
	} else {
		t = localtime(&now);
		(void)sprintf(cnow,"%2d/%2d/%2d %2d:%2d:%2d ",t->tm_mon + 1,
			t->tm_mday,t->tm_year,t->tm_hour,t->tm_min,t->tm_sec);
	}

	for(i=0;i<ifs_top;i++){
		if(ifs[i].type == IF_TYPE_NONE)continue;
		syslog(LOG_DEBUG,"%s%-4s in %4ld out %4ld baddr %ld loop %ld bogus %ld ovrn %ld\n",
			cnow, ifs[i].id, ifs[i].in, ifs[i].out,
			ifs[i].martians_in,ifs[i].looped_in,
			ifs[i].bogus_in, ifs[i].out_overruns);
	}

	last_send = now;
}
