/*
    NetUDPPCBDown.c, Copyright (c) by 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/NetUDPPCBDown.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 "NetDebug.h"
#include "NetDevice.h"
#include "NetPCB.h"
#include "NetUDPModule.h"


void netUDPPCBDownInit (NetModule this)
{
  return;
}


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


void netUDPPCBDownMain (LWPool lw, ThreadArg arg)
{
  NetModule modDesc = (NetModule) arg;
  NetBuf data;
  NetAttrs meta;
  NetPCBTable table;
  NetPCB pcb;
  ThreadId socket;
  u16 v[2], dummy, devNr, val[8];
  NetDevice device;
  u16 o3type;

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

    /* first get the PCB of this packet */
    /* the good thing of UDP is that it is a DATAGRAM service so no
       state needs to be saved we just try to find a possible PCB or dump
       it */
    /* try to get the corresponding PCB */
    if ( !(	netattrGet(meta, NETATTR_SOCKET_THREADLOW, &v[0]) &&
            netattrGet(meta, NETATTR_SOCKET_THREADHIGH, &v[1]) ) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: failed to get attributes\n");
      continue;
    }

    socket = (ThreadId)(long)v[0];
    socket |= (ThreadId)((long)v[1]<<16) ;
    pcb = netSocket2PCB(socket);
    if (! pcb )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: no PCB found for this socket\n");
      continue;
    }

    /* set the appropriate attributes (LOCAL, REMOTE and ROUTE) */
    /* we just copy the attributes form the PCB to the packet */
    if (! netattrCopy(meta, pcb->attributes) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: can not copy the attributes\n");
      continue;
    }
    if ( netattrGet(meta, NETATTR_OSI_3_TYPE, &o3type) )
    {
      netdbgPrintf(NETDEBUG_UDP, "netUDPPCBDown: NETATTR_OSI_3_TYPE = %d\n", o3type);
    }
    else
    {
      o3type =  NETATTR_TYPE_IPv4;
      /* future use, it will happen soon*/
      //o3type =  NETATTR_TYPE_IPv6;

      netdbgPrintf(NETDEBUG_UDP, "netUDPPCBDown: setting NETATTR_OSI_3_TYPE  to default %d\n", o3type);
    }


    switch ( o3type )
    {
    case NETATTR_TYPE_IPv4:
      if ( netattrGet(meta, NETATTR_IP_LOCAL_0, &dummy) &&
           netattrGet(meta, NETATTR_IP_LOCAL_1, &dummy) &&
           netattrGet(meta, NETATTR_IP_LOCAL_2, &dummy) &&
           netattrGet(meta, NETATTR_IP_LOCAL_3, &dummy) )
      {
        /* LOCAL Address allready set, so pass it on */
        PACKERROR(modDesc);
        netmodSend(lw, CHAINNEXT(modDesc), data, meta);
        continue;
      }
      break;
    case NETATTR_TYPE_IPv6:
      if (  netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_0, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_1, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_2, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_3, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_4, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_5, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_6, &dummy) &&
            netattrGet(meta,  NETATTR_IP6_SOURCEADDRESS_7, &dummy) )
      {
        /* all OK so pass it on */
        PACKDONE(modDesc);
        netmodSend (lw, CHAINNEXT(modDesc), data, meta);
        continue;
      }
      break;
    default:
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: wrong osi_3 type\n");
      continue;
    }

    /* now set the local Address because not specified */
    if (! netattrGet(meta, NETATTR_IF_TO, &devNr ) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBDown: failed to get device\n");
      continue;
    }
    if (! netdevGet(devNr, &device) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netUDPPCBDown: no such device\n");
      continue;
    }
    switch ( o3type )
    {
    case NETATTR_TYPE_IPv4:
      if ( netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_0, &dummy) &&
           netattrSet(meta, NETATTR_IP_LOCAL_0, dummy) &&
           netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_1, &dummy) &&
           netattrSet(meta, NETATTR_IP_LOCAL_1, dummy) &&
           netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_2, &dummy) &&
           netattrSet(meta, NETATTR_IP_LOCAL_2, dummy) &&
           netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_3, &dummy) &&
           netattrSet(meta, NETATTR_IP_LOCAL_3, dummy) )
      {
        /* all OK so pass it on */
        PACKDONE(modDesc);
        netmodSend (lw, CHAINNEXT(modDesc), data, meta);
        continue;
      }
      break;
    case NETATTR_TYPE_IPv6:
      if (  netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_0, &val[0]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_1, &val[1]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_2, &val[2]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_3, &val[3]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_4, &val[4]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_5, &val[5]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_6, &val[6]) &&
            netattrGet(device->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_7, &val[7]) &&

            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_0, val[0]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_1, val[1]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_2, val[2]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_3, val[3]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_4, val[4]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_5, val[5]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_6, val[6]) &&
            netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_7, val[7]))
      {
        /* all OK so pass it on */
        netdbgPrintf(NETDEBUG_UDP,
                     "netUDPPCBDown: netICMP6DownNeighborSolitation: SourceAddress %x %x %x %x %x %x %x %x \n",
                     val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
        PACKDONE(modDesc);
        netmodSend (lw, CHAINNEXT(modDesc), data, meta);
        continue;
      }
      break;
    default:
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netUDPPCBDown: failed to set local Address\n");
      continue;
    }

    /* XXX THIS IS A HACK NOT IPv6 COMPATIBLE */
    /*  		if ( netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_0, &dummy) && */
    /*  			 netattrSet(meta, NETATTR_IP_LOCAL_0, dummy) && */
    /*  			 netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_1, &dummy) && */
    /*  			 netattrSet(meta, NETATTR_IP_LOCAL_1, dummy) && */
    /*  			 netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_2, &dummy) && */
    /*  			 netattrSet(meta, NETATTR_IP_LOCAL_2, dummy) && */
    /*  			 netattrGet(device->addrList->addr, NETATTR_IP_LOCAL_3, &dummy) && */
    /*  			 netattrSet(meta, NETATTR_IP_LOCAL_3, dummy) ) {  */

    /* all OK so pass it on */

    /*  //			netdbgPrintf(NETDEBUG_UDP, "netUDPPCBDown: dumping attrs\n"); */
    /*  //			netdbgDumpAttr(NETDEBUG_UDP, meta); */
    /*  			PACKDONE(modDesc); */
    /*  			netmodSend (lw, CHAINNEXT(modDesc), data, meta); */
    /*  			continue; */
    /*  		} else { */
    /*  			PACKERROR(modDesc); */
    /*  			netmodRIP (lw, data, meta, "netUDPPCBDown: failed to set local Address\n"); */
    /*  			continue; */
    /*  		} */




  }
}

