/* route.c		Route file reader
 *
 * $Id: route.c,v 1.3 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 <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <memory.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <stdlib.h>

#include "ipip.h"

static void rerr();		/* General error printer */
static void init_route();
static void r_route();
static void print_routes();
static void sort_routes();

static int rlineno;
static int rerrflag;
int rts_top;
struct ipip_route rts[MAX_ROUTES];

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * open and read the route file
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
int
read_routes(f)
char *f;
{
	FILE *cf;
	char buf[256], *p;

	init_route();

/* Open the route file */
	if((cf = fopen(f,"r"))==NULL){
		(void)fprintf(stderr,"%sRoute file %s not found or could not be opened\n",progname,f);
		return -1;
	}

	while(fgets(buf, 255, cf)!=NULL){
		rlineno++;
		if((p = strtok(buf, " \t\n\r"))==NULL)continue;
		if(*p=='#' || *p==';')continue;

		if(strcmp(p,"route")==0)r_route();
/*		else if(strcmp(p,"whatever")==0)r_dowhatever(); */
		else rerr("Unrecognized command: %s",p);
	}

	if(rts_top==0)rerr("No routes defined","");

	if(debugd)print_routes();

	sort_routes();

	if(debugd)print_routes();

	return rerrflag;
}
/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Initialize defaults and null route entries
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static void
init_route()
{
	int i;

	for(i=0;i<MAX_ROUTES;i++){
		rts[i].ipaddr = 0;
		rts[i].mask = 0xffffffff;
		rts[i].destif = NULL;
		rts[i].destaddr = 0;
		rts[i].destport = 0;
		rts[i].hits = 0;
	}
	rts_top = 0;

	rlineno = 0;
	rerrflag = 0;
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Print out the route table (DEBUG)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static void
print_routes()
{
	int i, port, type;
	unsigned char *p, *q;
	char *id;
	unsigned int m;

	if(rerrflag==0)(void)syslog(LOG_DEBUG,"%d routes:", rts_top);
	else (void)syslog(LOG_DEBUG,"%d routes (there are errors):", rts_top);
	for(i=0;i<rts_top;i++){
		p = (unsigned char *)&rts[i].ipaddr;		
		q = (unsigned char *)&rts[i].destaddr;		
		if(rts[i].destif != NULL){
			id = rts[i].destif->id;
			type = rts[i].destif->type;
		} else {
			id = "unknown";
			type = IF_TYPE_NONE;
		}
		port = ntohs(rts[i].destport);
		m = ntohl(rts[i].mask);

		if((type == IF_TYPE_SLIP)||
		   (type == IF_TYPE_TUN)){
			(void)syslog(LOG_DEBUG,"ip %d.%d.%d.%d mask 0x%08x interface %s\n",
				p[0],p[1],p[2],p[3],m,id);
		} else if(type == IF_TYPE_IPIP){
			(void)syslog(LOG_DEBUG,"ip %d.%d.%d.%d mask 0x%08x interface %s ip %d.%d.%d.%d\n",
				p[0],p[1],p[2],p[3],m,id,
				q[0],q[1],q[2],q[3]);
		} else {
			(void)syslog(LOG_DEBUG,"ip %d.%d.%d.%d mask 0x%08x interface %s ip %d.%d.%d.%d port %d\n",
				p[0],p[1],p[2],p[3],m,id,
				q[0],q[1],q[2],q[3],port);
		}
	}
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Print a general route file error
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static void
rerr(s,a)
char *s;
char *a;
{
	(void)syslog(LOG_ERR,"Route file error at line %d:\n",rlineno);
	(void)syslog(LOG_ERR,s,a);
	rerrflag--;
}

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Handle the "route" command
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 */
static void
r_route()
{
	int i, syntax;
	char *q;
	struct hostent *he;

	if(rts_top>=MAX_ROUTES){
		rerr("Too many routes defined","");
		return;
	}

	syntax = 0;

	if((q = strtok((char *)NULL, " \t\n\r"))){
		if((he = gethostbyname(q))){
			(void)memcpy((char *)&rts[rts_top].ipaddr,
					he->h_addr_list[0], 4);
		} else {
			if((rts[rts_top].ipaddr = inet_addr(q)) == 0xffffffff)
				rerr("Bad IP address: %s",q);
		}
	} else syntax++;

	if((q = strtok((char *)NULL, " \t\n\r"))){
		rts[rts_top].mask = htonl(strtoul(q, NULL, 0));
	} else syntax++;

	if((q = strtok((char *)NULL, " \t\n\r"))){
		for(i=0;i<ifs_top;i++){
			if(strcmp(q,ifs[i].id)==0)rts[rts_top].destif = &ifs[i];
		}
		if(rts[rts_top].destif == NULL)
			rerr("Interface %s not defined",q);
	} else syntax++;

	if((q = strtok((char *)NULL, " \t\n\r"))){
		if((he = gethostbyname(q))){
			(void)memcpy((char *)&rts[rts_top].destaddr,
					he->h_addr_list[0], 4);
		} else {
			if((rts[rts_top].destaddr = inet_addr(q)) == (unsigned long)-1)
				rerr("Bad destination IP address: %s",q);
		}
	}

	if((q = strtok((char *)NULL, " \t\n\r"))){
		rts[rts_top].destport = htons((unsigned short)atoi(q));
	}

	if(syntax)rerr("Syntax error (route <ipaddr> <mask> <iface> [<destipaddr> [<destport>]]","");

	rts_top++;
}

static void
sort_routes()
{
	struct ipip_route *tmptable ;
	static unsigned long mask_entries[] = { \
	0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8,  \
	0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80,  \
	0xffffff00, 0xfffffe00,	0xfffffc00, 0xfffff800,  \
	0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000,  \
	0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000,  \
	0xfff00000, 0xffe00000, 0xffc00000, 0xff800000,  \
	0xff000000, 0xfe000000, 0xfc000000, 0xf8000000,  \
	0xf0000000, 0xe0000000, 0xc0000000, 0x80000000,  \
	0x00000000 } ;
	int i, j, newrows ;

	tmptable = malloc ( (rts_top+1) * sizeof(struct ipip_route) ) ;
	
	if (tmptable == NULL) {
		rerr("Unable to allocate memory to resort table","");
		return ;
	}

	/* Search the original table 32 times for each of the 32 possible */
	/* masks.  Copy the most specific routes to the tmptable first    */
	newrows = 0;
	for ( i = 0 ; i <= 32 ; i++ )
		for ( j = 0 ; j < rts_top ; j++ )
			if (rts[j].mask == htonl(mask_entries[i]))
				tmptable[newrows++] = rts[j];

	/* Copy the tmptable back to original */
	for ( i = 0 ; i < rts_top ; i++ )
		rts[i] = tmptable[i] ;
	free (tmptable);
}
