/*
    Copyright 2000 (c) by David Schweikert,
    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/old/ICMP/NetICMP.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 $
 
 
    $Log: NetICMP.c,v $
    Revision 1.2  2000/11/15 10:12:14  ruf
    Repository Reorganisation
 
    Revision 1.1  2000/03/31 17:50:36  gfa
    Merged with /Net from several term projects
 
 
*/
#include "NetICMP.h"

#include <Topsy.h>
#include <Messages.h>
#include <Syscall.h>
#include <NetAttr.h>
#include <NetBuf.h>
#include <NetConfig.h>
#include <NetDebug.h>
#include <NetIface.h>
#include <NetModules.h>
#include <IP/NetIP.h>
#include <IP/NetIPchecksum.h>

void neticmpRead(ThreadId id, unsigned long int length);
static void neticmpMain(ThreadArg arg);
static void neticmpUp(NetBuf buf, NetAttrs attrs);
static void neticmpDown(NetBuf buf, NetAttrs attrs);

void neticmpInit()
{
  ThreadId neticmpId;

  /* Start main thread */
  if (tmStart(&neticmpId, neticmpMain, (ThreadArg)0, "neticmp") != TM_STARTOK)
  {
    netdbgDisplay(NETDEBUG_GENERIC, "Start: couldn't start ICMP thread\n");
    return;
  }
}

static void neticmpMain(ThreadArg arg)
{
  ThreadId myThreadId, parentThreadId;
  NetModMsg msg;

  tmGetInfo(SELF, &myThreadId, &parentThreadId);

  // Add to list of modules
  netmodAdd(NETMODULE_ICMP, myThreadId);

  netdbgPrintf(NETDEBUG_MODULES,
               "ICMP module started (moduleId=%d, threadId=%d).\n",
               NETMODULE_ICMP, myThreadId);

  while(1)
  {
    if(!netmodMsgRecv(&msg))
      return;

    switch (msg.id)
    {
    case NETMOD_SENDUP:
      neticmpUp(msg.buf, msg.attrs);
      break;
    case NETMOD_SENDDOWN:
      neticmpDown(msg.buf, msg.attrs);
      break;
    default:
      netdbgPrintf(NETDEBUG_GENERIC,
                   "*** ERROR: ICMP: Unknown message (%d)!\n",
                   msg.id);
      break;
    }
  }
}

static void neticmpEchoUp(NetBuf buf, NetAttrs attrs)
{
  NetICMPHdr hdr=(NetICMPHdr) NETBUF_DATA(buf);
  NetAttrs newattrs=0;
  unsigned short tmp;

  netdbgDisplay(NETDEBUG_ICMP, "neticmp: Got ECHO REQUEST.\n");

  /* We resend exactly the same data to the sender. */
  /* Only 'type' is changed from ECHO to ECHOREPLY */
  hdr->type = NETICMP_TYPE_ECHOREPLY;

  /* We rebuild the attributes for the down-going packet */
  if(!netattrNew(&newattrs))
    goto error;

  /* Determine sender and set destination to the same. */
  if(!netattrGet(attrs, NETATTR_IP_FROM_0, &tmp))
    goto error;
  netattrSet(newattrs, NETATTR_IP_TO_0, tmp);
  if(!netattrGet(attrs, NETATTR_IP_FROM_1, &tmp))
    goto error;
  netattrSet(newattrs, NETATTR_IP_TO_1, tmp);
  if(!netattrGet(attrs, NETATTR_IP_FROM_2, &tmp))
    goto error;
  netattrSet(newattrs, NETATTR_IP_TO_2, tmp);
  if(!netattrGet(attrs, NETATTR_IP_FROM_3, &tmp))
    goto error;
  netattrSet(newattrs, NETATTR_IP_TO_3, tmp);

  /* 'Protocol' for IP is ICMP */
  netattrSet(newattrs, NETATTR_IP_PROTOCOL, NETIP_PROTOCOL_ICMP);

  /* Re-calculate checksum (see RFC 1141) */
  /* ~C' = ~(C + (-m) + m') = ~C + (m - m') = ~C + m + ~m' */
  hdr->checksum+=NETICMP_TYPE_ECHO + ~(NETICMP_TYPE_ECHOREPLY);
  //hdr->checksum=htons(netipChecksum(buf,netbufLen(buf)));

  /* Send down the reply packet */
  netdbgDisplay(NETDEBUG_ICMP, "neticmp: Sending ECHO REPLY.\n");
  netcfgSendNextDown(NETMODULE_ICMP, buf, newattrs);

  /* Free the old attributes */
  NETATTR_FREE(attrs);

  return;

error:
  netdbgDisplay(NETDEBUG_ICMP, "neticmp: Discarding packet.\n");
  if(newattrs)
    NETATTR_FREE(newattrs);
  NETATTR_FREE(attrs);
  netbufFree(buf);
}

static void neticmpUp(NetBuf buf, NetAttrs attrs)
{
  NetICMPHdr hdr=(NetICMPHdr) NETBUF_DATA(buf);
  int len = netbufLen(buf);

  /* Process packet and fill attrs */

  netdbgPrintf(NETDEBUG_ICMP, "neticmp: Got packet (len=%d).\n",NETBUF_LEN(buf));

  /* Check length */
  if(len<4)
    goto discard;

  /* Check checksum */
  if(netipChecksum(buf,netbufLen(buf)))
  {
    netdbgPrintf(NETDEBUG_GENERIC, "neticmp: Checkum error!\n");
    goto discard;
  }

  switch(hdr->type)
  {
  case NETICMP_TYPE_ECHO:
    neticmpEchoUp(buf,attrs);
    break;
  default:
    goto discard;
  }

  return;

discard:
  netdbgDisplay(NETDEBUG_ICMP, "neticmp: Discarding packet.\n");
  netbufFree(buf);
  netbufFree((NetBuf)attrs);
}

static void neticmpDown(NetBuf buf, NetAttrs attrs)
{
  /* Build header and fill attrs */

  /* Send down packet */
  netcfgSendNextDown(NETMODULE_ICMP, buf, attrs);
}
