/**************************************************
OpengateM - a MAC address authentication system
 module for checking TTL(TimeToLive) (or hlim in ipv6) to detect router
 defaut TTL has some fixed value (depending OS).
 and it is decrement at passing the router/NAT.

Copyright (C) 2011 Opengate Project Team
Written by Yoshiaki Watanabe

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.

Email: watanaby@is.saga-u.ac.jp
**************************************************/
#include "opengatemd.h"

struct SubnetSet{
  unsigned long subnetBits;
  int maskLength;
  int hopCount;
};

static char validInitialTtl[256];
static struct SubnetSet* pSubnet;
static int nSubnet;

/**********************************
initialize ttl check routine
**********************************/
int initTtlCheck(void){

  char* pStart;
  int ttlValue;
  int i;
  char* subnetStr=NULL;
  struct SubnetSet* pNext;
  unsigned long subnetBits;
  unsigned int byte[4];
  int maskLength=0;
  int hopCount=0;


  /***** ttl table setting ******/
  /* initial values of table is false */
  for(i=0; i<256; i++) validInitialTtl[i] = FALSE;

  /* get string for initial ttls from conf file, eg:[64 128 255] */
  pStart = GetConfValue("ValidInitialTtl");
  if(isNull(pStart)) return FALSE;
  
  /* loop for setting ttl table */
  while(1){

    /* get number and set table entry */
    if(sscanf(pStart, "%d", &ttlValue)==1){
      validInitialTtl[ttlValue]=TRUE;
    }

    /* set next scan point */
    pStart = strchr(pStart+1, ' ');
    if(isNull(pStart)) break;
  }

  /******** subnet setting ***********/
  /* count subnet setting */
  nSubnet=0;
  subnetStr=GetFirstConfValue("SubnetHopCount");
  while(!isNull(subnetStr)){
    nSubnet++;
    subnetStr=GetNextConfValue();
  }

  /* get memory for the setting */
  pSubnet=malloc(sizeof(struct SubnetSet)*(nSubnet));
  pNext=pSubnet;
  
  /*loop all subnet setting */
  subnetStr=GetFirstConfValue("SubnetHopCount");
  while(!isNull(subnetStr)){

    /* get address and hop count defined as [192.168.1.0/24 1] */
    if(sscanf(subnetStr, "%d.%d.%d.%d/%d %d", 
	      &byte[0], &byte[1], &byte[2], &byte[3], 
	      &maskLength, &hopCount)==6){

      /* sum up address bytes to a long int */
      subnetBits=0;
      for(i=0; i<4; i++){
	subnetBits = subnetBits<<8;
	subnetBits += byte[i];
      }
      
      /* save subnet setting to malloc area */
      pNext->subnetBits=subnetBits;
      if(maskLength<=32) pNext->maskLength=maskLength;
      else               pNext->maskLength=0;
      pNext->hopCount=hopCount;

      /* goto next store area */
      pNext++;
    }

    /* get next subnet setting */
    subnetStr=GetNextConfValue();
  }

  return TRUE;
}

/*********************************
check ttl for detecting access via nat/router
*********************************/
int isSentViaNatOrRouter(char* ipAddress, char* macAddress, int ttl){

  unsigned int byte[4]; /* 4 bytes representing given ipv4 address */
  unsigned long addressBits=0;
  unsigned long subnetMask=0;
  int maxMaskLength=0;
  int hopSave=0;
  struct SubnetSet* pNext;
  int i;

  /* if ttl is valid initial ttl then no router/nat */
  if( validInitialTtl[ttl] ) return 0; /* no router/nat */

  /* if ipv6, the nat/router is assumed as a router */
  if( strchr(ipAddress, ':')!=NULL){
    if( validInitialTtl[ttl] ) return 0; /* direct (no router/nat) */
    else return 2; /* router */ 
  }

  /* if ipv4, calculate initial ttl as (present ttl)+(router hops) */
  /* and compare to the router hop setting in conf file */
  /* get 4 bytes of ipv4 address */
  if(sscanf(ipAddress, "%d.%d.%d.%d", 
	    &byte[0], &byte[1], &byte[2], &byte[3])!=4) return -1;/*error */

  /* sum up address bytes to a long int */
  addressBits=0;
  for(i=0; i<4; i++){
    addressBits = addressBits<<8;
    addressBits += byte[i];
  }

  /* check each subnet setting */
  pNext=pSubnet;
  maxMaskLength=0;
  hopSave=0;
  for(i=0; i<nSubnet; i++){

    /* compare subnet and address */
    /*  subnetBits  1011 0011 1100 0011 0000 */
    /*  addressBits 1011 0011 0110 0011 1100 */
    /*    Bit XOR   0000 0000 1010 0000 1100 */ 
    /*  subnet mask 1111 1111 1111 1111 0000 */
    /*    Bit AND   0000 0000 1010 0000 0000 */
    /* If result is all-zero, the address is in the subnet */
    subnetMask=0xFFFFFFFF<<(32 - (pNext->maskLength));
    if(!( ((pNext->subnetBits)^addressBits) & subnetMask )){

      /* if the address is included in subnet having longest mask */
      /* save the hop count */
      if((pNext->maskLength) > maxMaskLength){
	maxMaskLength = pNext->maskLength;
	hopSave = pNext->hopCount;
      }
    }

    /* get next SubnetHopCount */
    pNext++;
  }
  
  /* add hop to ttl, then compare to valid initial ttl */
  if( hopSave<0 || (ttl+hopSave)>=256 ) return ERROR; /* error */
  if( validInitialTtl[ttl+hopSave] ){
    if(hopSave==0) return NONAT; /* no nat/router */
    else  return ROUTER; /* valid router */
  }
  return NAT; /* unknown nat or router inserted */
}

/***********************************************
 putout log at nat/router detection 
 isNatOrRouter:1=UnknownNatOrRouter, 2=FormalRouterOnly, 0=None, -1=err
**********************************************/
void putLogAtNatOrRouter(int isNatOrRouter, char* ipAddress, char* macAddress, int ttl){

  if(isNatOrRouter==1 && atoi(GetConfValue("ShowNat"))){
    err_msg("INFO : packet is sent via unknown nat/router[%s][%s][%d]", 
	    ipAddress, macAddress, ttl);
  }
  if(isNatOrRouter==2 && atoi(GetConfValue("ShowRouter"))){
    err_msg("INFO : packet is sent via formal router[%s][%s][%d]", 
	    ipAddress, macAddress, ttl);
  }
}

/***********************************************
 routines for debugging output 
**********************************************/
int InitTtlCheck(void){
  int ret;
  if(debug>1) err_msg("DEBUG:=>initTtlCheck( )");
  ret = initTtlCheck();
  if(debug>1) err_msg("DEBUG:(%d)<=initTtlCheck( )",ret);
  return ret;
}

int IsSentViaNatOrRouter(char* ipAddress, char* macAddress, int ttl){
  int ret;
  if(debug>2) err_msg("DEBUG:=>isSentViaNatOrRouter(%s,%s,%d)", ipAddress, macAddress, ttl);
  ret = isSentViaNatOrRouter(ipAddress, macAddress, ttl);
  if(debug>2) err_msg("DEBUG:(%d)<=isSentViaNatOrRouter( )", ret);
  return ret;
}

void PutLogAtNatOrRouter(int isNatOrRouter, char* ipAddress, char* macAddress, int ttl){
  if(debug>2) err_msg("DEBUG:=>putLogAtNatOrRouter(%d,%s,%s,%d)", 
		      isNatOrRouter,ipAddress,macAddress,ttl);
  putLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
  if(debug>2) err_msg("DEBUG:(%d)<=putLOgAtNatOrRouter( )");
}



/**************test main********************
int testmain(){

}
******************************************/
