/*
    Copyright 2000 (c) by Marc Wegmueller,
    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/ICMP6/NetICMP6ModuleDown.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 "NetDevice.h"
#include "NetIP6Module.h"
#include "NetIPChecksum.h"
#include "NetICMP6Module.h"
#include "NetICMP6Checksum.h"

//static void neticmpEchoDown(LWPool lw, NetBuf data, NetAttrs meta, ThreadArg arg);
static void neticmpNeighborSolicitation(LWPool lw, NetBuf data, NetAttrs meta, ThreadArg arg);
static int neticmpNeighborAdvertisement(LWPool lw, NetBuf data, NetAttrs meta, ThreadArg arg);

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


int netICMP6ModuleDownConfig (NetModule this, Message *msg)
{
  /* XXX protocol modules normaly shouldn't need to be configured */
  return 0;
}


void netICMP6ModuleDownMain (LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetICMP6Hdr		*hdr;

  NetBuf			data;
  NetAttrs		meta;
  unsigned short	val, val1, val2;
  unsigned short	rbit, sbit, obit;

  while (1)
  {


    netmodRecv (lw, &data, &meta);
    PACKSTART(modDesc);
    netdbgDisplay(NETDEBUG_ICMP6, "netICMP6down: netICMP6ModuleDown: received\n");

    /*                  PACKERROR(modDesc); */
    /*                  netmodRIP (lw, data, meta, "netICMP6down: TESTING\n"); */

    if (netattrGet (meta, NETATTR_ICMP6_TYPE, &val) )
    {
      switch (val)
      {
      case NETICMP6_TYPE_ECHO_REQUEST:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_ECHO_REQUEST \n");
        break;
      case NETICMP6_TYPE_ECHO_REPLY:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_ECHO_REPLY \n");
        break;
      case NETICMP6_TYPE_NeighborSolicitation:

        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_NeighborSolicitation \n");

        neticmpNeighborSolicitation(lw, data, meta, arg);
        //if ( neticmpNeighborSolicitation(lw, &data, &meta, arg) )
        //     continue;
        continue;

        //break;
      case NETICMP6_TYPE_NeighborAdvertisement:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_NeighborAdvertisement \n");
        if ( !neticmpNeighborAdvertisement(lw, data, meta, arg) )
        {
          PACKERROR(modDesc);
          netmodRIP (lw, data, meta, "netICMP6down: Could not handle NeifhborAdvertisement!\n");
          continue;
        }
        break;
      default:
        PACKERROR(modDesc);
        netmodRIP (lw, data, meta, "netICMP6down: Unknown type\n");
        break;
      }
    }
    else
    {
      netdbgDisplay(NETDEBUG_ICMP6, "netICMP6down: Failed to get attribute (TYPE)!\n ");
    }



    if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr) ) != 1)
    {
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netICMP6down: Failed to allocate header!\n");
      continue;
    }

    hdr = (NetICMP6Hdr*) NETBUF_DATA(data);

    /* Get TYPE */
    if (netattrGet (meta, NETATTR_ICMP6_TYPE, &val) )
    {
      netdbgPrintf(NETDEBUG_ICMP6, "MW: NetICMP6ModuleDown: setting TYPE:%x \n", val );
      hdr->type = val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_ICMP6, "netICMP6: Failed to get attribute (TYPE)! Setting to default!\n");
      hdr->type = NETICMP6_DEFAULT_TYPE;
    }

    /* Get CODE */
    if (netattrGet(meta, NETATTR_ICMP6_CODE, &val) )
    {
      hdr->code =  val;
    }
    else
    {
      netdbgDisplay(NETDEBUG_ICMP6, "netICMP6: Failed to get attribute (CODE)! Setting to default!\n");
      hdr->code = NETICMP6_DEFAULT_CODE;
    }

    /*Get attributes*/
    if ( !( netattrGet(meta, NETATTR_ICMP6_IDENTIFIER, &val1 ) &&
            netattrGet(meta, NETATTR_ICMP6_SEQUENCENUMBER, &val2 ) ))
    {
      val1 = 0;
      val2 = 0;
    }
    hdr->bitfield0 =   htons(val1) ;
    hdr->bitfield1 =   htons(val2) ;

    if (netattrGet (meta, NETATTR_ICMP6_TYPE, &val) )
    {
      switch (val)
      {
      case NETICMP6_TYPE_ECHO_REQUEST:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_ECHO_REQUEST RRRRRRRRRRR------------ \n");
        hdr->bitfield0 = 0;
        netattrSet(meta, NETATTR_IP6_PAYLOADLENGTH, netbufLen(data));
        break;
      case NETICMP6_TYPE_ECHO_REPLY:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_ECHO_REPLY \n");
        break;
      case NETICMP6_TYPE_NeighborSolicitation:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_NeighborSolicitation SSSSSSSSSS--------\n");
        hdr->bitfield0 = 0;
        netattrSet(meta, NETATTR_IP6_PAYLOADLENGTH, netbufLen(data));
        //netattrSet(meta, NETATTR_OSI_3_TYPE, NET);
        break;
      case NETICMP6_TYPE_NeighborAdvertisement:
        netdbgDisplay(NETDEBUG_ICMP6, "netICMP6ModuleDown: NETICMP6_TYPE_NeighborSolicitation AAAAAAAAAA-----------\n");
        rbit = 0;
        sbit = 1;
        obit = 1;
        hdr->bitfield0 = 0;
        hdr->bitfield0 =  htons( (rbit << 15 | sbit << 14 | obit << 13) );
        break;
      default:
        PACKERROR(modDesc);
        netmodRIP (lw, data, meta, "netICMP6down: Unknown type\n");
        break;
      }
    }
    else
    {
      netdbgDisplay(NETDEBUG_ICMP6, "netICMP6down: Failed to get attribute (TYPE)!\n ");
    }

    netattrUnset(meta, NETATTR_ICMP6_CHECKSUM);

    /* Set Checksum */
    if (! netattrGet(meta, NETATTR_ICMP6_CHECKSUM, &val))
    {
      hdr->checksum = 0;
      hdr->checksum = htons( (u16)netICMP6Checksum(lw, data, netbufLen(data), meta, arg) );
      netattrSet(meta, NETATTR_ICMP6_CHECKSUM, ntohs(hdr->checksum));
      netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6: new Checksum set \n");
    }
    else
    {
      hdr->checksum = htons( val );
    }


    netdbgPrintf(NETDEBUG_ICMP6, "MW: NetICMP6ModuleDown: NETATTR_ICMP6_TYPE %x \n", hdr->type );
    netdbgPrintf(NETDEBUG_ICMP6, "MW: NetICMP6ModuleDown: NETATTR_ICMP6_CODE %x \n",  hdr->code );
    netdbgPrintf(NETDEBUG_ICMP6, "MW: NetICMP6ModuleDown: NETATTR_ICMP6_CHECKSUM %x \n",  hdr->checksum );

    netattrSet(meta, NETATTR_OSI_3_TYPE, NETATTR_TYPE_IPv6 );
    netattrSet(meta, NETATTR_OSI_2_TYPE, NETATTR_TYPE_ETHERNET );

    /*
    		netdbgDumpBuf(NETDEBUG_ICMP6, data);
    		netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
    		netdbgDumpAttr(NETDEBUG_ICMP6, meta);
    		netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
    */

    /* Finished: send the packet */
    PACKDONE(modDesc);
    netmodSend (lw, CHAINNEXT(modDesc), data, meta);
  }
}

static void neticmpNeighborSolicitation(LWPool lw, NetBuf data, NetAttrs meta, ThreadArg arg)
{
  unsigned short	        val1, val2;

  NetModule		modDesc = (NetModule) arg;

  NetICMP6Hdr_ta		*hdr_ta;
  NetICMP6Hdr_opt 	*hdr_opt;
  NetICMP6Hdr		*hdr;
  unsigned short	        tLen;
  NetDevice		d;
  unsigned short	        val[8], target[8], deviceNr;


  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborSolicitation: begin\n");

  netdbgDumpBuf(NETDEBUG_ICMP6, data);
  netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
  netdbgDumpAttr(NETDEBUG_ICMP6, meta);
  netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");


  /* get the device descriptor */
  if (! netattrGet(meta, NETATTR_IF_TO, &deviceNr) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: no device specified\n");
  }
  if (! netdevGet(deviceNr, &d) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: no Ethernet device\n");
  }

  if (!(	netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_0, &val[0]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_1, &val[1]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_2, &val[2]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_3, &val[3]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_4, &val[4]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_5, &val[5]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_6, &val[6]) &&
         netattrGet(d->addrList->addr,  NETATTR_IP6_SOURCEADDRESS_7, &val[7]) ))
  {
    /* failed what now: Normaly we would dump the packet
     * How can it be that we do not know our IF_ADDR this will be
     * set when the device is allocated */
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: no ethernet device found \n");
  }
  netdbgPrintf(NETDEBUG_ICMP6,"netICMP6down: 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]);

  netattrSet(meta, NETATTR_IP_VERSION, 6);
  netattrSet(meta, NETATTR_IP6_NEXTHEADER, NETIP_PROTOCOL_ICMP6);
  netattrSet(meta, NETATTR_IP6_HOPLIMIT, 255);


  // (jb) This is quite a hack: For duplicate address detection we have to set
  // the uspecified address (::), but how do we know that we are performing
  // a duplicate address detection? The arp thread doesnt tell us! This interface should be improved!
  // So we are comparing the ICMPv6-Targetaddress with the (tentative) IPv6-Sourceaddress,
  // if they are equal we assume it is a duplicate address detection
  // See RFC 2462 ...

  if ( !( netattrGet(meta, NETATTR_ICMP6_TARGETADDR0, &target[0] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR1, &target[1] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR2, &target[2] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR3, &target[3] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR4, &target[4] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR5, &target[5] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR6, &target[6] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR7, &target[7] ) ))
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: failed to get attributes Test \n");
  }

  if(target[0] == val[0] && target[1] == val[1] && target[2] == val[2] && target[3] == val[3]
      && target[4] == val[4] && target[5] == val[5] && target[6] == val[6] && target[7] == val[7])
  {
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_0, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_1, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_2, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_3, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_4, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_5, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_6, 0);
    netattrSet (meta, NETATTR_IP6_SOURCEADDRESS_7, 0);
  }
  else
  {
    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]);
  }

  if ( !( netattrGet(meta, NETATTR_ICMP6_TARGETADDR6, &val[0] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR7, &val[1] )))
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: failed to build IPv6-multicast address \n");
  }

  val[0] = (val[0] | 0xff00);

  if(!(netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_0, NETICMP6_MulticastAddr0) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_1, NETICMP6_MulticastAddr1) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_2, NETICMP6_MulticastAddr2) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_3, NETICMP6_MulticastAddr3) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_4, NETICMP6_MulticastAddr4) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_5, NETICMP6_MulticastAddr5) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_6, val[0]) &&
       netattrSet (meta, NETATTR_IP6_DESTINATIONADDRESS_7, val[1]) &&
       netattrSet(meta, NETATTR_ETH_MULTICAST, 1) &&
       netattrSet(meta, NETATTR_ETH_LOCAL_0, NETETH_MulticastPrefix) &&
       netattrSet(meta, NETATTR_ETH_LOCAL_1, val[0]) &&
       netattrSet(meta, NETATTR_ETH_LOCAL_2, val[1])))
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: failed to build IPv6-multicast address \n");
  }

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6DownNeighborSolitation: set multicast: %x %x\n ", val[0], val[1]);

  //netdbgDumpBuf(NETDEBUG_ICMP6, data);
  //netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
  //netdbgDumpAttr(NETDEBUG_ICMP6, meta);
  //netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");

  netdbgPrintf(NETDEBUG_ICMP6,"netICMP6down: netICMP6DownNeighborSolitation: DATA %x\n", &data);

  if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr_opt)) != 1)
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: Failed to allocate header!\n");
  }

  /* verify minimum lenght */
  tLen = netbufLen(data) / 8;                              // Hack: hwo knows the length of one Byte???
  netdbgPrintf(NETDEBUG_ICMP6,"netICMP6down: netICMP6DownNeighborSolitation: DATA %x\n", &data);

  hdr_opt = (NetICMP6Hdr_opt*) NETBUF_DATA(data);

  /*Set attributes*/
  hdr_opt->optiontype =  NETICMP6_SOURCE_LINK_LAYER_ADDRESS;
  hdr_opt->optionlength = tLen;

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6DownNeighborSolitation : Options %x %x \n",
               hdr_opt->optiontype,  hdr_opt->optionlength );

  /* get the device descriptor */
  if (! netattrGet(meta, NETATTR_IF_TO, &deviceNr) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: no device specified\n");
  }
  if (! netdevGet(deviceNr, &d) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: no Ethernet device\n");
  }

  /* set source address */
  /* here we try first the packet attributes and then we use
   * the address from the device descriptor */
  if (!(	netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_0, &val[0]) &&
         netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_1, &val[1]) &&
         netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_2, &val[2]) ))
  {
    /* failed what now: Normaly we would dump the packet
     * How can it be that we do not know our IF_ADDR this will be
     * set when the device is allocated */
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: no ethernet device found \n");
  }
  hdr_opt->linklayer[1] = val[0] & 0xFF ;
  hdr_opt->linklayer[0] = val[0] >> 8 ;
  hdr_opt->linklayer[3] = val[1] & 0xFF ;
  hdr_opt->linklayer[2] = val[1] >> 8 ;
  hdr_opt->linklayer[5] = val[2] & 0xFF ;
  hdr_opt->linklayer[4] = val[2] >> 8 ;

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: neticmpNeighborSolicitation: LinkLayer %x %x %x %x %x %x \n",
               hdr_opt->linklayer[0], hdr_opt->linklayer[1], hdr_opt->linklayer[2],
               hdr_opt->linklayer[3], hdr_opt->linklayer[4], hdr_opt->linklayer[5] );


  if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr_ta) ) != 1)
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: Failed to allocate header!\n");
  }

  hdr_ta = (NetICMP6Hdr_ta*) NETBUF_DATA(data);

  hdr_ta->targetaddr[0] = htons( target[0] );
  hdr_ta->targetaddr[1] = htons( target[1] );
  hdr_ta->targetaddr[2] = htons( target[2] );
  hdr_ta->targetaddr[3] = htons( target[3] );
  hdr_ta->targetaddr[4] = htons( target[4] );
  hdr_ta->targetaddr[5] = htons( target[5] );
  hdr_ta->targetaddr[6] = htons( target[6] );
  hdr_ta->targetaddr[7] = htons( target[7] );

  netdbgPrintf(NETDEBUG_ICMP6,"netICMP6down: netICMP6DownNeighborSolitation: hdr-start %x %x %x %x %x %x %x %x %x %x %x \n", &hdr_ta,
               hdr_ta[0], hdr_ta[1], hdr_ta[2], hdr_ta[3], hdr_ta[4], hdr_ta[5], hdr_ta[6], hdr_ta[7], hdr_ta[8],  hdr_ta[9]);

  if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr) ) != 1)
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: Failed to allocate header!\n");
  }

  hdr = (NetICMP6Hdr*) NETBUF_DATA(data);

  /* Get TYPE */
  if (netattrGet (meta, NETATTR_ICMP6_TYPE, &val1) )
  {
    hdr->type =   val1;
  }
  else
  {
    netdbgDisplay(NETDEBUG_ICMP6, "netICMP6: Failed to get attribute (TYPE)! Setting to default!\n");
    hdr->type =  NETICMP6_DEFAULT_TYPE;
  }

  /* Get CODE */

  if (netattrGet(meta, NETATTR_ICMP6_CODE, &val1) )
  {
    hdr->code = val1;
  }
  else
  {
    netdbgDisplay(NETDEBUG_ICMP6, "netICMP6: Failed to get attribute (CODE)! Setting to default!\n");
    hdr->code =  NETICMP6_DEFAULT_CODE;
  }

  /*Get attributes*/
  if ( !( netattrGet(meta, NETATTR_ICMP6_IDENTIFIER, &val1 ) &&
          netattrGet(meta, NETATTR_ICMP6_SEQUENCENUMBER, &val2 ) ))
  {
    val1 = 0;
    val2 = 0;
  }
  hdr->bitfield0 =  htons( val1 );
  hdr->bitfield1 =  htons( val2 );

  if (netattrGet (meta, NETATTR_ICMP6_TYPE, &val1) )
  {
    switch (val1)
    {
    case NETICMP6_TYPE_NeighborSolicitation:
      netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: NETICMP6_TYPE_NeighborSolicitation %x \n", val1);
      hdr->bitfield0 = 0;
      netattrSet(meta, NETATTR_IP6_PAYLOADLENGTH, netbufLen(data) );
      //netattrSet(meta, NETATTR_OSI_3_TYPE, NET);
      break;

    default:
      PACKERROR(modDesc);
      netmodRIP (lw, data, meta, "netICMP6: Unknown type\n");
      break;
    }
  }
  else
  {
    netdbgDisplay(NETDEBUG_ICMP6, "netICMP6: Failed to get attribute (TYPE)!\n ");
  }

  netattrUnset(meta, NETATTR_ICMP6_CHECKSUM);


  /* Set Checksum */
  if (! netattrGet(meta, NETATTR_ICMP6_CHECKSUM, &val1))
  {
    hdr->checksum = 0;
    hdr->checksum = htons( (u16)netICMP6Checksum(lw, data, netbufLen(data), meta, arg) );
    netattrSet(meta, NETATTR_ICMP6_CHECKSUM, ntohs(hdr->checksum));
    netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6: new Checksum set %x \n", ntohs(hdr->checksum));
  }
  else
  {
    hdr->checksum = htons( val1 );
  }

  netattrSet(meta, NETATTR_OSI_3_TYPE, NETATTR_TYPE_IPv6 );
  netattrSet(meta, NETATTR_OSI_2_TYPE, NETATTR_TYPE_ETHERNET );

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6DownNeighborSolitation: NETATTR_ICMP6_TYPE %d \n", ntohs(hdr->type) );
  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6DownNeighborSolitation: NETATTR_ICMP6_CODE %d \n",  ntohs(hdr->code) );
  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6DownNeighborSolitation: NETATTR_ICMP6_CHECKSUM %x \n",  ntohs(hdr->checksum) );

  /*
          netdbgDumpBuf(NETDEBUG_ICMP6, data);
  	netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
          netdbgDumpAttr(NETDEBUG_ICMP6, meta);
  	netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
  */



  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborSolicitation: finished! \n");


  netdbgPrintf(NETDEBUG_ICMP6,"netICMP6down: netICMP6DownNeighborSolitation: hdr-start with hdr %x %x %x %x %x %x %x %x %x %x %x \n",
               &hdr, hdr[0], hdr[1], hdr[2], hdr[3], hdr[4], hdr[5], hdr[6], hdr[7], hdr[8],  hdr[9],  hdr[10]);


  /* Finished: send the packet */
  PACKDONE(modDesc);
  netmodSend (lw, CHAINNEXT(modDesc), data, meta);
}


static int neticmpNeighborAdvertisement(LWPool lw, NetBuf data, NetAttrs meta, ThreadArg arg)
{
  unsigned short	val1, val2;


  NetModule		modDesc = (NetModule) arg;
  NetICMP6Hdr_ta		*hdr_ta;
  NetICMP6Hdr_opt 	*hdr_opt;
  unsigned short	        tLen;
  NetDevice		d;
  unsigned short	val[3], deviceNr;

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborAdvertisement: begin\n");

  /*
  	netdbgDumpBuf(NETDEBUG_ICMP6, data);
  netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
  netdbgDumpAttr(NETDEBUG_ICMP6, meta);
  netdbgDisplay(NETDEBUG_ICMP6, "*************************\n");
  */

  if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr_opt)) != 1)
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: Failed to allocate header!\n");
    return 0;
  }

  /* verify minimum lenght */
  tLen = netbufLen(data);

  hdr_opt = (NetICMP6Hdr_opt*) NETBUF_DATA(data);

  /*Get attributes*/
  if ( !( netattrGet(meta, NETATTR_ICMP6_OPTIONTYPE, &val1 ) &&
          netattrGet(meta, NETATTR_ICMP6_OPTIONLENGTH, &val2 ) ))
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: failed to get attributes\n");
    return 0;
  }


  hdr_opt->optiontype =  val1;
  hdr_opt->optionlength =  val2;
  netdbgPrintf(NETDEBUG_ICMP6, "MW: netICMP6NeighborAdvertisement : Options %x %x \n",
               ntohs(hdr_opt->optiontype),  ntohs(hdr_opt->optionlength) );


  /* get the device descriptor */
  if (! netattrGet(meta, NETATTR_IF_TO, &deviceNr) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netEthernet: no device specified\n");
    return 0;
  }
  if (! netdevGet(deviceNr, &d) )
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: no Ethernet device\n");
    return 0;
  }

  /* set source address */
  /* here we try first the packet attributes and then we use
   * the address from the device descriptor */
  if (!(	netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_0, &val[0]) &&
         netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_1, &val[1]) &&
         netattrGet(d->addrList->addr, NETATTR_ETH_LOCAL_2, &val[2]) ))
  {
    /* failed what now: Normaly we would dump the packet
     * How can it be that we do not know our IF_ADDR this will be
     * set when the device is allocated */
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6down: failed to get ethernet address\n");
    return 0;
  }
  hdr_opt->linklayer[1] = val[0] & 0xFF;
  hdr_opt->linklayer[0] = val[0] >> 8;
  hdr_opt->linklayer[3] = val[1] & 0xFF;
  hdr_opt->linklayer[2] = val[1] >> 8;
  hdr_opt->linklayer[5] = val[2] & 0xFF;
  hdr_opt->linklayer[4] = val[2] >> 8;

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborAdvertisement: LinkLayer %x %x %x %x %x %x \n",
               hdr_opt->linklayer[0], hdr_opt->linklayer[1], hdr_opt->linklayer[2],
               hdr_opt->linklayer[3], hdr_opt->linklayer[4], hdr_opt->linklayer[5] );


  if (netbufAddHeadSpace(&data, sizeof(NetICMP6Hdr_ta) ) != 1)
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: Failed to allocate header!\n");
    return 0;
  }

  hdr_ta = (NetICMP6Hdr_ta*) NETBUF_DATA(data);

  if ( !( netattrGet(meta, NETATTR_ICMP6_TARGETADDR0, &val[0] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR1, &val[1] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR2, &val[2] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR3, &val[3] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR4, &val[4] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR5, &val[5] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR6, &val[6] ) &&
          netattrGet(meta, NETATTR_ICMP6_TARGETADDR7, &val[7] ) ))
  {
    PACKERROR(modDesc);
    netmodRIP (lw, data, meta, "netICMP6: failed to get attributes Test \n");
    return 0;
  }

  hdr_ta->targetaddr[0] = htons(val[0]);
  hdr_ta->targetaddr[1] = htons(val[1]);
  hdr_ta->targetaddr[2] = htons(val[2]);
  hdr_ta->targetaddr[3] = htons(val[3]);
  hdr_ta->targetaddr[4] = htons(val[4]);
  hdr_ta->targetaddr[5] = htons(val[5]);
  hdr_ta->targetaddr[6] = htons(val[6]);
  hdr_ta->targetaddr[7] = htons(val[7]);

  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborAdvertisement: TargetAddr %x %x %x %x %x %x %x %x \n",
               hdr_ta->targetaddr[0], hdr_ta->targetaddr[1], hdr_ta->targetaddr[2], hdr_ta->targetaddr[3],
               hdr_ta->targetaddr[4], hdr_ta->targetaddr[5], hdr_ta->targetaddr[6], hdr_ta->targetaddr[7] );


  netdbgPrintf(NETDEBUG_ICMP6, "netICMP6down: netICMP6NeighborAdvertisement: finished! \n");

  return 1;
}
