/*
    NetUDPPCBUp.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/NetUDPPCBUp.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 "NetUDPModule.h"
#include "NetPCB.h"


void netUDPPCBUpInit (NetModule this)
{
  return;
}


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

int udp_compare(NetAttrs a1, NetAttrs a2)
/* a2 is meta and a1 are the attributes of the PCB */
{
  u16 v1, v2;

  if ( !netattrGet(a1, NETATTR_UDP_LOCAL, &v1) &&
       !netattrGet(a2, NETATTR_UDP_REMOTE, &v2) &&
       v1 != v2 )
    return 0;
  /* could be that we send to a socket binded to 0.0.0.0:port */
  if ( !netattrGet(a1, NETATTR_UDP_REMOTE, &v1) &&
       !netattrGet(a2, NETATTR_UDP_LOCAL, &v2) )
    if (v1 != v2 )
      return 0;
  if ( netattrGet(a1, NETATTR_TYPE_IPv4, &v1) )
  {
    return 1; /* XXX for the moment we ignore the from and to
    						 addresses, to compare them I need to know how
    						 the socket was binded (connect or point to point)
    						 and what options are valid (broadcast) */
  }
  else if ( netattrGet(a1, NETATTR_TYPE_IPv6, &v1) )
  {
    return 1;
  }
  else
  {
    return 0;
  }
  return 0;
}

void netUDPPCBUpMain (LWPool lw, ThreadArg arg)
{
  NetModule modDesc = (NetModule) arg;
  NetBuf data;
  NetAttrs meta;
  NetPCBTable table;
  NetPCB pcb;
  u16 o3type;
  u32 l1[4], l2, l3;
  unsigned short	v;

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

    netdbgPrintf( NETDEBUG_UDP, "netUDPPCBUp: received UDPPCB up\n");

    if (! netattrGet(meta, NETATTR_OSI_3_TYPE, &o3type) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBUp: can not get OSI 3 type\n");
      continue;
    }

    /* first get the PCB of this packet */
    /* then send the data via out distributor to the socket device */
    /* 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 */
    /* 1. get the udpPCBTable */
    table = netGetPCBTable(o3type, PROTO_UDP); /* XXX AF_INET */
    if (! table )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netUDPPCBUp: can not get PCBTable\n");
      continue;
    }
    /* 2. try to get the corresponding PCB */
    switch ( o3type )
    {
    case NETATTR_TYPE_IPv4:
      netattrGet(meta, NETATTR_IP_REMOTE_0, &v);
      l1[0] = v;
      netattrGet(meta, NETATTR_IP_REMOTE_1, &v);
      l1[0] = v << 8;
      netattrGet(meta, NETATTR_IP_REMOTE_2, &v);
      l1[0] = v << 16;
      netattrGet(meta, NETATTR_IP_REMOTE_3, &v);
      l1[0] = v << 24;
      l1[0] = ntohl(l1[0]);
      break;
    case NETATTR_TYPE_IPv6:
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_0, &v);
      l1[0] = v;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_1, &v);
      l1[0] = v << 16;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_2, &v);
      l1[1] = v;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_3, &v);
      l1[1] = v << 16;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_4, &v);
      l1[2] = v;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_5, &v);
      l1[2] = v << 16;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_6, &v);
      l1[3] = v;
      netattrGet(meta,  NETATTR_IP6_DESTINATIONADDRESS_7, &v);
      l1[3] = v << 16;
      l1[0] = ntohl(l1[0]);
      l1[1] = ntohl(l1[1]);
      l1[2] = ntohl(l1[2]);
      l1[3] = ntohl(l1[3]);
      break;
    default:
      break;
    }
    netattrGet(meta, NETATTR_UDP_REMOTE, &v);
    l2 = v;
    netattrGet(meta, NETATTR_UDP_LOCAL, &v);
    l3 = v;
    l1[0] = l2 = l3 = 0; /* XXX THIS IS A GIANT HACK */
    switch ( o3type )
    {
    case NETATTR_TYPE_IPv4:
      netdbgPrintf( NETDEBUG_UDP, "netUDPPCBUp: will do netAddress2PCB()\n");
      pcb = netAddress2PCB(table, l1[0], l2, l3, udp_compare, meta);

      break;
    case NETATTR_TYPE_IPv6:
      netdbgPrintf( NETDEBUG_UDP, "netUDPPCBUp: should do netV6Address2PCB()\n");

      pcb = netV6Address2PCB(table, l1[0], l2, l3, udp_compare, meta);
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netUDPPCBUp: can not set attributes\n");

      break;
    default:
      break;
    }
    //pcb = netAddress2PCB(table, l1[0], l2, l3, udp_compare, meta);
    if (! pcb )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta,
                "netUDPPCBUp: no PCB found for this packet\n");
      continue;
    }

    /* 3. send to socket device (or just set the attrs appropriate) */
    if ( !(	netattrSet(meta, NETATTR_SENDTO_THREADLOW,
                       (pcb->socket & 0xFFFF) ) &&
            netattrSet(meta, NETATTR_SENDTO_THREADHIGH,
                       ((pcb->socket >> 16) & 0xFFFF)) ) )
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netUDPPCBUp: can not set attributes\n");
      continue;
    }

    PACKDONE(modDesc);
    netmodSend (lw, CHAINNEXT(modDesc), data, meta);
  }
  netdbgPrintf( NETDEBUG_UDP, "netUDPPCBUp: finished UDPPCB up\n");

}

