/*
    NetUDPModuleDown.c, Copyright  (c) by Boris Lutz, Claudio Jeker
    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/UDP/NetUDPModuleDown.c,v $
    Author(s):
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Creation Date:
    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"
#include "NetUDPChecksum.h"
#include "NetUDPModule.h"
#include "NetICMP6Checksum.h"


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


int netUDPModuleDownConfig (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 */
      netdbgPrintf( NETDEBUG_UDP, "netUDPModuleDown: adding key %i\n", a[i].key);
    case NETATTR_UDP_LOCAL:
    case NETATTR_UDP_REMOTE:
      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 netUDPModuleDownMain(LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetUDPHdr		*hdr;
  unsigned short	val,
  tLen;
  int				cSum;
  NetBuf			data;
  NetAttrs		meta;
  u16 o3type;

  while (1)
  {
    netmodRecv (lw, &data, &meta);
    PACKSTART(modDesc);

    netdbgPrintf( NETDEBUG_UDP, "netUDPModuleDown: received UDP down\n");

    if (netbufAddHeadSpace(&data, sizeof(NetUDPHdr)) != 1)
    {
      netdbgDisplay(NETDEBUG_UDP, "netudp: Failed to allocate header!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    hdr = (NetUDPHdr *) NETBUF_DATA(data);

    // Set source port
    if (netattrGet(meta, NETATTR_UDP_LOCAL, &val) )
    {
      hdr->sport = htons(val);
    }
    else if (netattrGet (modDesc->attributes, NETATTR_UDP_LOCAL, &val) )
    {
      hdr->sport = htons(val);
    }
    else
    {
      netdbgDisplay(NETDEBUG_UDP, "netudp: Failed to get attribute (LOCAL)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set destination port
    if (netattrGet(meta, NETATTR_UDP_REMOTE, &val) )
    {
      hdr->dport = htons(val);
    }
    else if (netattrGet (modDesc->attributes, NETATTR_UDP_REMOTE, &val) )
    {
      hdr->dport = htons(val);
    }
    else
    {
      netdbgDisplay(NETDEBUG_UDP, "netudp: Failed to get attribute (REMOTE)!\n");
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "Discarding packet...\n");
      continue;
    }
    // Set length
    tLen = netbufLen(data);
    hdr->len = htons(tLen);
    // Set Checksum
    hdr->checksum = 0;
    if ( netattrGet(meta, NETATTR_OSI_3_TYPE, &o3type) )
    {
      netdbgPrintf(NETDEBUG_UDP, "netUDPPCBDown: NETATTR_OSI_3_TYPE = %d\n", o3type);
    }

    switch ( o3type )
    {
    case NETATTR_TYPE_IPv4:
      cSum = netudpChecksum(data, meta, tLen);
      if (cSum != -1)
      {
        hdr->checksum = cSum;
      }
      netattrSet(meta, NETATTR_IP_PROTOCOL, NETIP_PROTOCOL_UDP);
      break;
    case NETATTR_TYPE_IPv6:
      //hdr->checksum = htons( (u16)netICMP6Checksum(lw, data, netbufLen(data), meta, arg) );
      netattrSet(meta, NETATTR_IP6_NEXTHEADER, NETIP_PROTOCOL_UDP);
      netattrSet(meta, NETATTR_IP6_PAYLOADLENGTH, tLen);
      cSum =  htons( (u16)netICMP6Checksum(lw, data, netbufLen(data), meta, arg));
      if (cSum != -1)
      {
        hdr->checksum = cSum;
      }
      break;
    default:
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: wrong osi_3 type\n");
      continue;
    }


    netdbgPrintf(NETDEBUG_UDP, "netudp: Checksum = hdr->checksum\n");
    // Let the lower layer know where this comes from
    //netattrSet(meta, NETATTR_IP_PROTOCOL, NETIP_PROTOCOL_UDP);
    //netattrSet(meta, NETATTR_IP6_NEXTHEADER, NETIP_PROTOCOL_UDP);
    netdbgPrintf( NETDEBUG_UDP, "netUDPModuleDown: received UDP down\n");
    // finished: send the packet
    PACKDONE(modDesc);
    netmodSend (lw, CHAINNEXT(modDesc), data, meta);
  }
}
