/*
    NetIP6ModuleUp.c, Copyright  (c) by Jonas Bandi,
    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/IP6/NetIP6ModuleUp.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 "NetIP6Module.h"
#include "NetDevice.h"


void
netIP6ModuleUpInit (NetModule this)
{
  //netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUpInit \n");
  // XXX what should I do?
  return;
}


int
netIP6ModuleUpConfig (NetModule this, Message * msg)
{
  //netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUpConfig \n");
  return 0;
}


void
netIP6ModuleUpMain (LWPool lw, ThreadArg arg)
{
  NetModule modDesc = (NetModule) arg;
  NetIP6Hdr *hdr;
  unsigned short bitfield0, version, trafficclass, flowlabel0, flowlabel1,
  bLen, val;
  NetBuf data;
  NetAttrs meta;
  long test, test2;
  NetDevice device;
  NetAttr myAddrAttr[8];	/* need to be enough big to hold all needed attrs */
  int forward = 1;
  NetAddress address;
  u16 devNr, value, value1;



  //netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp \n");


  //      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)
  {
    netmodRecv (lw, &data, &meta);
    PACKSTART (modDesc);
    hdr = (NetIP6Hdr *) NETBUF_DATA (data);

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

    // Verify version number
    version = WNIBGET (ntohs (hdr->bitfield0), 0);
    if (version != 6)
    {
      PACKERROR (modDesc);
      netmodRIP (lw, data, meta, "netIPv6: Wrong version number!\n");
      continue;
    }
    // Get traffic class
    trafficclass = (ntohs (hdr->bitfield0) & 0xff << 4);

    // Get flowlabel0 (the first 4 bits of the flowlabel)
    flowlabel0 = WNIBGET (ntohs (hdr->bitfield0), 12);

    // Set all the netattributes according to the header
    //netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: going to parse\n");
    if (!
        (netattrSet
         (meta, NETATTR_IP6_DESTINATIONADDRESS_0, ntohs (hdr->saddr0))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_1,
                        ntohs (hdr->saddr1))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_2,
                        ntohs (hdr->saddr2))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_3,
                        ntohs (hdr->saddr3))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_4,
                        ntohs (hdr->saddr4))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_5,
                        ntohs (hdr->saddr5))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_6,
                        ntohs (hdr->saddr6))
         && netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_7,
                        ntohs (hdr->saddr7))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_0,
                        ntohs (hdr->daddr0))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_1,
                        ntohs (hdr->daddr1))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_2,
                        ntohs (hdr->daddr2))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_3,
                        ntohs (hdr->daddr3))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_4,
                        ntohs (hdr->daddr4))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_5,
                        ntohs (hdr->daddr5))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_6,
                        ntohs (hdr->daddr6))
         && netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_7,
                        ntohs (hdr->daddr7))
         && netattrSet (meta, NETATTR_IP_VERSION, version)
         && netattrSet (meta, NETATTR_IP6_TRAFFICCLASS, trafficclass)
         && netattrSet (meta, NETATTR_IP6_FLOWLABEL_0, flowlabel0)
         && netattrSet (meta, NETATTR_IP6_FLOWLABEL_1,
                        ntohs (hdr->bitfield1))
         && netattrSet (meta, NETATTR_IP6_PAYLOADLENGTH,
                        ntohs (hdr->payloadlength))
         && netattrSet (meta, NETATTR_IP6_NEXTHEADER, hdr->nextheader)
         && netattrSet (meta, NETATTR_IP6_HOPLIMIT, hdr->hoplimit)))
    {

      PACKERROR (modDesc);
      netmodRIP (lw, data, meta,
                 "netIPv6: Failed to set new attributes!\n");
      continue;
    }
    /*                 netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: destaddr: %x %x %x %x %x %x %x %x \n", */
    /*                               hdr->daddr0,hdr->daddr1,hdr->daddr2,hdr->daddr3, */
    /*                               hdr->daddr4,hdr->daddr5,hdr->daddr6,hdr->daddr7); */

    switch (hdr->nextheader)
    {
    case NETIP_PROTOCOL_ICMP6:
      netdbgPrintf (NETDEBUG_IP6,
                    "J: netIP6ModuleUp: received ICMPv6 message\n");
      if (!netattrSet (meta, NETATTR_OSI_4_TYPE, NETATTR_TYPE_ICMP6))
      {
        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,
                   "netIPv6: Extension-Headers not supported! Killing the Packet!\n");
        continue;
      }
      break;
    default:
      PACKERROR (modDesc);
      netmodRIP (lw, data, meta,
                 "netIPv6: unknown upper layer protocol\n");
      continue;
    }

    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_0, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr0: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_1, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr1: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_2, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr2: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_3, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr3: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_4, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr4: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_5, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr5: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_6, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr6: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_DESTINATIONADDRESS_7, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: destaddr7: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_0, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr0: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_1, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr1: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_2, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr2: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_3, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr3: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_4, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr4: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_5, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr5: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_6, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr6: %x\n", val); */
    /*                  netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_7, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: srcaddr7: %x\n", val); */

    /*                 netattrGet (meta, NETATTR_IP6_VERSION, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: version: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_TRAFFICCLASS, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: trafficlass: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_FLOWLABEL_0, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: flowlabel0: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_FLOWLABEL_1, &val); */
    /*                   netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: flowlabel1: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_PAYLOADLENGTH, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: payloadlength: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_NEXTHEADER, &val); */
    /*                   netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: nextheader: %x\n", val); */

    /*                  netattrGet (meta, NETATTR_IP6_HOPLIMIT, &val); */
    /*                  netdbgPrintf(NETDEBUG_IP6, "J: netIP6ModuleUp: hoplimit: %x\n", val); */


    //(jb) handle forwrding decision
    forward = 1;


    //netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp:going to make routing decision\n");
    if (!netattrGet (meta, NETATTR_IF_FROM, &devNr))
    {
      PACKERROR (modDesc);
      netmodRIP (lw, data, meta, "IPForward: failed to get devNr\n");
      continue;
    }

    if (!netdevGet (devNr, &device))
    {
      PACKERROR (modDesc);
      netmodRIP (lw, data, meta,
                 "netIP6ModuleUp: failed to get device\n");
      continue;
    }
    myAddrAttr[0].key = NETATTR_IP6_SOURCEADDRESS_0;
    myAddrAttr[1].key = NETATTR_IP6_SOURCEADDRESS_1;
    myAddrAttr[2].key = NETATTR_IP6_SOURCEADDRESS_2;
    myAddrAttr[3].key = NETATTR_IP6_SOURCEADDRESS_3;
    myAddrAttr[4].key = NETATTR_IP6_SOURCEADDRESS_4;
    myAddrAttr[5].key = NETATTR_IP6_SOURCEADDRESS_5;
    myAddrAttr[6].key = NETATTR_IP6_SOURCEADDRESS_6;
    myAddrAttr[7].key = NETATTR_IP6_SOURCEADDRESS_7;
    if (!
        (netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_0, &myAddrAttr[0].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_1,
                        &myAddrAttr[1].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_2,
                        &myAddrAttr[2].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_3,
                        &myAddrAttr[3].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_4,
                        &myAddrAttr[4].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_5,
                        &myAddrAttr[5].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_6,
                        &myAddrAttr[6].data)
         && netattrGet (meta, NETATTR_IP6_SOURCEADDRESS_7,
                        &myAddrAttr[7].data)))
    {
      PACKERROR (modDesc);
      netmodRIP (lw, data, meta,
                 "netIP6ModuleUp: failed to get my own IP address\n");
      continue;
    }

    //netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: comparing with solicited-node multicast address\n");
    /*                  netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: myaddr: %x %x %x %x %x %x %x %x \n", */
    /*                               myAddrAttr[0].data, myAddrAttr[1].data, myAddrAttr[2].data, myAddrAttr[3].data, */
    /*                               myAddrAttr[4].data, myAddrAttr[5].data, myAddrAttr[6].data, myAddrAttr[7].data); */
    if (myAddrAttr[0].data == 0xff02 &&
        myAddrAttr[1].data == 0x0 &&
        myAddrAttr[2].data == 0x0 &&
        myAddrAttr[3].data == 0x0 &&
        myAddrAttr[4].data == 0x0 && myAddrAttr[5].data == 0x1)
    {
      /*                      netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: actually it is a solicited-node address\n"); */
      //(jb) marking the packet as a multicast packet, so that we can set
      // the correct ethernet-multicast address dirctly in ARPLookup.c, in case
      // the packet is forwarded
      if (!netattrSet (meta, NETATTR_ETH_MULTICAST, 1))
      {
        PACKERROR (modDesc);
        netmodRIP (lw, data, meta,
                   "netIP6ModuleUp: failed to set multicast attribute\n");
        continue;
      }
      address = device->addrList;
      while (address)
      {
        netattrGet (address->addr, NETATTR_IP6_SOURCEADDRESS_7, &value);
        netattrGet (address->addr, NETATTR_IP6_SOURCEADDRESS_6,
                    &value1);
        netdbgPrintf (NETDEBUG_IP6,
                      "netIP6ModuleUp: my postfix: %x%x, received: %x%x\n",
                      value1, value, myAddrAttr[6].data,
                      myAddrAttr[7].data);
        if (value == myAddrAttr[7].data
            && (value1 & 0xff) == (myAddrAttr[6].data & 0xff))
        {
          forward = 0;
          netdbgPrintf (NETDEBUG_IP6,
                        "netIP6ModuleUp: received solicited-node multicast packet for me\n");
          break;
        }
        address = address->next;
      }
    }
    else
    {
      devNr = 0;

      if (netdevIsLocal (device, &myAddrAttr[0], 8))
      {
        forward = 0;
        /*                         netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: received packet for me\n");  */
      }
      //devNr = devNr+1;

    }


    if (forward == 1)
    {
      //netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp: forwarding an IPv6 packet.\n");
      // (jb) we are directing the packet on the IP_FORWARDING_CHAIN
      //the 3/4-distributor will send the packet there when
      //NETATTR_OSI_4_TYPE is set to NETATTR_TYPE_IPv4
      //even when the packet is a IPv6 packet... (its ugly, but who cares?)
      if (!netattrSet (meta, NETATTR_OSI_4_TYPE, NETATTR_TYPE_IPv4))
      {
        PACKERROR (modDesc);
        netmodRIP (lw, data, meta,
                   "netIP6ModuleUp: failed to mark packet to be forwarded\n");
      }
    }

    data->start += sizeof (NetIP6Hdr);
    //netdbgPrintf(NETDEBUG_IP6, "netIP6ModuleUp:sending to next module\n");
    PACKDONE (modDesc);
    netmodSend (lw, CHAINNEXT (modDesc), data, meta);
    //PACKERROR(modDesc);
    //netmodRIP (lw, data, meta,   "netIPv6:Temporarily deleting packet\n");
  }

}
