/*
    NetIPModuleUp.c, Copyright  (c) by Boris Lutz,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public
    Licence. A full copy of the GNU GPL is provided in the file COPYING
    found in the development root of Topsy.
*/
/*
 
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Net/Modules/IP/NetIPModuleUp.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/
#include "NetModulesNew.h"
#include "NetAttr.h"
#include "LWThreads.h"
#include "NetBitFields.h"
#include "NetByteorder.h"
#include "NetDebug.h"
#include "NetIPChecksum.h"
#include "NetIPModule.h"


void netIPModuleUpInit (NetModule this)
{
  // XXX what should I do?
  return;
}


int netIPModuleUpConfig (NetModule this, Message *msg)
{
  return 0;
}


void netIPModuleUpMain (LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetIPHdr		*hdr;
  unsigned short	version,
  hLen,
  bLen,
  tLen;
  NetBuf			data;
  NetAttrs		meta;


  //	printf ("IPMod: modDesc = %p, modDesc->chain = %p, modDesc->chain->id = %p\n", modDesc, modDesc->chain, modDesc->chain->id);	/* XXX this is a gcc fix */
  /* DON'T ASK US WHY BUT OUR COMPILER HAS SOME STRANGE BUGS */

  while (1)
  {

    netdbgPrintf(NETDEBUG_MODULES, "netip: netIPModuleUpMain received \n");

    netmodRecv (lw, &data, &meta);
    PACKSTART(modDesc);
    hdr = (NetIPHdr*) NETBUF_DATA(data);

    bLen = netbufLen(data);
    /* Verify minimum lenght */
    if ( bLen < sizeof(NetIPHdr) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta,
                 "netIPv4: Packet size smaller than minimal length!\n");
      continue;
    }

    /* Verify version number */
    version = BNIBGET(hdr->bitfield1,0);
    if (version != 4)
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netIPv4: Wrong version number!");
      continue;
    }

    /* Verify header length */
    hLen = BNIBGET(hdr->bitfield1,1) << 2;
    if (bLen < hLen)
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta,
                 "netIPv4: Packet size smaller than header length!\n");
      continue;
    }

    /* Verify checksum */
    if (netipChecksum(data, hLen) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netIPv4: Wrong checksum!\n");
      continue;
    }

    /* Verify total length */
    tLen = ntohs (hdr->tot_len);
    if (netbufLen(data) < tLen )
    {
      netdbgPrintf( NETDEBUG_IP,
                    "NetIPv4: hdr->tot_len = %i bufLen = %i\n",
                    hdr->tot_len, netbufLen(data) );
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netIPv4: Packet has wrong size\n");
      continue;
    }
    if (netbufLen(data) > tLen)
      netbufTrim(data, tLen);

    /* Set all the netattributes according to the header
     * the destination address will be used by the forward module */
    if ( !(	netattrSet (meta, NETATTR_IP_LOCAL_0, hdr->daddr0) &&
            netattrSet (meta, NETATTR_IP_LOCAL_1, hdr->daddr1) &&
            netattrSet (meta, NETATTR_IP_LOCAL_2, hdr->daddr2) &&
            netattrSet (meta, NETATTR_IP_LOCAL_3, hdr->daddr3) &&
            netattrSet (meta, NETATTR_IP_REMOTE_0, hdr->saddr0) &&
            netattrSet (meta, NETATTR_IP_REMOTE_1, hdr->saddr1) &&
            netattrSet (meta, NETATTR_IP_REMOTE_2, hdr->saddr2) &&
            netattrSet (meta, NETATTR_IP_REMOTE_3, hdr->saddr3) &&
            netattrSet (meta, NETATTR_IP_VERSION, version) &&
            netattrSet (meta, NETATTR_IP_TOS, hdr->tos) &&
            netattrSet (meta, NETATTR_IP_DATALEN, tLen - hLen) &&
            netattrSet (meta, NETATTR_IP_HEADLEN, hLen) &&
            netattrSet (meta, NETATTR_IP_ID, ntohs (hdr->id)) &&
            // FRAGMENTING NOT IMPLEMENTED XXX
            // farg_off is just stored in the netattrs, could use the modDesc->extra
            // pointer to access a linked list where these fragmented packets are
            // stored untill the packet is complete.
            netattrSet (meta, NETATTR_IP_FRAG, ntohs (hdr->frag_off)) &&
            netattrSet (meta, NETATTR_IP_CHECKSUM, ntohs(hdr->checksum)) &&
            netattrSet (meta, NETATTR_IP_PROTOCOL, hdr->protocol) &&
            netattrSet (meta, NETATTR_IP_TTL, hdr->ttl) ))
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta,
                 "netIPv4: Failed to set new attributes!\n");
      continue;
    }

    switch ( hdr->protocol )
    {
    case NETIP_PROTOCOL_ICMP:
      if (!netattrSet(meta, NETATTR_OSI_4_TYPE, NETATTR_TYPE_ICMP))
      {
        goto fail;
      }
      break;
    case NETIP_PROTOCOL_UDP:
      if (!netattrSet(meta, NETATTR_OSI_4_TYPE, NETATTR_TYPE_UDP))
      {
        goto fail;
      }
      break;
    case NETIP_PROTOCOL_TCP:
      if (!netattrSet(meta, NETATTR_OSI_4_TYPE, NETATTR_TYPE_TCP))
      {
fail:
        PACKERROR(modDesc);
        netmodRIP (lw, data, meta,
                   "netIPv4: Failed to set protocol attribute!\n");
        continue;
      }
      break;
    default:
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "unknown IP protocol\n");
      continue;
    }

    //-CJ	netdbgPrintf(NETDEBUG_IP, "netIPv4: attributes set, dumping\n");
    //-CJ	netdbgDumpAttr(NETDEBUG_IP, meta);
    //-CJ	netdbgPrintf(NETDEBUG_IP, "********************\n");

    // XXX options will be ignored right now XXX
    if (hLen > 20)
    {
      netdbgDisplay(NETDEBUG_IP, "netip: IP-Packet contains options. Ingnoring...\n");
    }
    // Strip header (including options XXX)
    data->start += hLen;
    PACKDONE(modDesc);
    netmodSend (lw, CHAINNEXT(modDesc), data, meta);
  }
}
