/*
    NetIPModuleDown.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/NetIPModuleDown.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 "NetEthernetModule.h"
#include "NetIPChecksum.h"
#include "NetIPModule.h"


void netIPModuleDownInit (NetModule this)
{
  netattrSet (this->attributes, NETATTR_IP_ID, 0);
  // XXX what should I do?
  return;
}


int netIPModuleDownConfig (NetModule this, Message *msg)
{
  unsigned long	len = msg->msg.netConf.ModuleConf.len;
  unsigned long	i;
  NetAttr			*a = msg->msg.netConf.ModuleConf.attrVec;
  int				exitCode = 0;


  for ( i = 0; i < len ; i++)
  {
    switch ( a[i].key )
    {
      /* add all known keys here */
    case NETATTR_IP_MTU:
    case NETATTR_IP_TOS:
    case NETATTR_IP_ID:
    case NETATTR_IP_TTL:
    case NETATTR_IP_PROTOCOL:
    case NETATTR_IP_LOCAL_0:
    case NETATTR_IP_LOCAL_1:
    case NETATTR_IP_LOCAL_2:
    case NETATTR_IP_LOCAL_3:
    case NETATTR_IP_REMOTE_0:
    case NETATTR_IP_REMOTE_1:
    case NETATTR_IP_REMOTE_2:
    case NETATTR_IP_REMOTE_3:
      if ( msg->id == NET_CONF_MODULESET )
      {
        /* set attributes */
        if (! netattrSet(this->attributes, a[i].key, a[i].data) )
        {
          netdbgPrintf(0, "netmodConfigure: netattrSet failed (%i, %i)\n",
                       a[i].key, a[i].data);
          CONF_FAILEDPLUS(exitCode);
          return exitCode;
        }
        CONF_SUCCEDPLUS(exitCode);
      }
      else if ( msg->id == NET_CONF_MODULEGET )
      {
        if (!netattrGet(this->attributes, a[i].key, &(a[i].data)))
        {
          netdbgPrintf(0, "netmodConfigure: netattrGet failed (%i)\n",
                       a[i].key);
          CONF_FAILEDPLUS(exitCode);
          return exitCode;
        }
        CONF_SUCCEDPLUS(exitCode);
      }
      else
      {
        netdbgPrintf(0, "netmodConfigure: AIIEEE, got hit by a bus\n");
        CONF_FAILEDPLUS(exitCode);
        return exitCode;
      }
      break;
    default:
      netdbgPrintf(0, "netmodConfigure: unknown key %i\n", a[i].key);
      CONF_IGNOREPLUS(exitCode);
    }
  }
  return exitCode;
}


void netIPModuleDownMain (LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetIPHdr		*hdr;
  unsigned short	val,
  ipMTU,
  hLen;
  NetBuf			data;
  NetAttrs		meta;


  while (1)
  {

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


    netmodRecv (lw, &data, &meta);
    PACKSTART(modDesc);
    if (netbufAddHeadSpace(&data, sizeof(NetIPHdr)) != 1)
    {	// XXX no options right now!
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to allocate header!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    hdr = (NetIPHdr*) NETBUF_DATA(data);

    // Get MTU
    if (netattrGet (meta, NETATTR_IP_MTU, &val) )
    {
      ipMTU = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_MTU, &val) )
    {
      ipMTU = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (MTU)! Setting to default!\n");
      ipMTU = NETIP_DEFAULT_MTU;
    }
    // Set IP-Version (4)
    BNIBSET(hdr->bitfield1, 0, 4);
    // Set header length
    hLen = sizeof(NetIPHdr);		// XXX no options right now!
    BNIBSET(hdr->bitfield1, 1, hLen >> 2);
    // Set TOS
    if (netattrGet (meta, NETATTR_IP_TOS, &val) )
    {
      hdr->tos = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_TOS, &val) )
    {
      hdr->tos = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (TOS)! Setting to default!\n");
      hdr->tos = NETIP_DEFAULT_TOS;
    }
    // Set total length
    hdr->tot_len = htons(netbufLen(data) );		// since I already added the header I don't have to add hLen
    // Set ID
    if (netattrGet (meta, NETATTR_IP_ID, &val) )
    {
      hdr->id = htons(val);
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_ID, &val) )
    {
      hdr->id = htons(val++);
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (ID)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    netattrSet (modDesc->attributes, NETATTR_IP_ID, val);
    // Set FRAG
    if (netbufLen(data) <= ipMTU)
    {
      hdr->frag_off = 0;
    }
    else
    {
      // FRAGMENTING NOT IMPLEMENTED XXX
      // well... here we should fragment a packet if it's lager than
      // MTU this is difficult since this a per packet lw-thread...
      netdbgDisplay(NETDEBUG_IP, "netip: Data too long and fragmenting not implemented!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set TTL
    if (netattrGet (meta, NETATTR_IP_TTL, &val) )
    {
      hdr->ttl = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_TTL, &val) )
    {
      hdr->ttl = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (TTL)! Setting to default!\n");
      hdr->ttl = NETIP_DEFAULT_TTL;
    }
    // Set Protocol
    if (netattrGet (meta, NETATTR_IP_PROTOCOL, &val) )
    {
      hdr->protocol = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_PROTOCOL, &val) )
    {
      hdr->protocol = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (PROTOCOL)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set from address
    if (netattrGet (meta, NETATTR_IP_LOCAL_0, &val) )
    {
      hdr->saddr0 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_LOCAL_0, &val) )
    {
      hdr->saddr0 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_LOCAL_0)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_LOCAL_1, &val) )
    {
      hdr->saddr1 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_LOCAL_1, &val) )
    {
      hdr->saddr1 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_LOCAL_1)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_LOCAL_2, &val) )
    {
      hdr->saddr2 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_LOCAL_2, &val) )
    {
      hdr->saddr2 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_LOCAL_2)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_LOCAL_3, &val) )
    {
      hdr->saddr3 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_LOCAL_3, &val) )
    {
      hdr->saddr3 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_LOCAL_3)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set destination address
    if (netattrGet (meta, NETATTR_IP_REMOTE_0, &val) )
    {
      hdr->daddr0 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_REMOTE_0, &val) )
    {
      hdr->daddr0 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_REMOTE_0)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_REMOTE_1, &val) )
    {
      hdr->daddr1 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_REMOTE_1, &val) )
    {
      hdr->daddr1 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_REMOTE_1)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_REMOTE_2, &val) )
    {
      hdr->daddr2 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_REMOTE_2, &val) )
    {
      hdr->daddr2 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_REMOTE_2)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    if (netattrGet (meta, NETATTR_IP_REMOTE_3, &val) )
    {
      hdr->daddr3 = val;
    }
    else if (netattrGet (modDesc->attributes, NETATTR_IP_REMOTE_3, &val) )
    {
      hdr->daddr3 = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_IP, "netip: Failed to get attribute (IP_REMOTE_3)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set Checksum
    hdr->checksum = 0;
    hdr->checksum = netipChecksum(data, hLen);

    // Have to let the lower level know that this is an IP-packet
    netattrSet (meta, NETATTR_ETH_TYPE, NETETH_TYPE_IP);

    // Finished: send the packet

    netdbgPrintf(NETDEBUG_MODULES, "netip: netIPModuleDownMain sending \n");
    netdbgPrintf(NETDEBUG_MODULES, "netip: netIPModuleDownMain sending%x \n", NETETH_TYPE_IP);


    //printf("J: IP sending to next Nodule.\n");


    PACKDONE(modDesc);
    netmodSend (lw, CHAINNEXT(modDesc), data, meta);
  }
}
