/*
    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/ARP/NetARP.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: NetARP.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
 
 
*/
/* Skeleton for a net module. */
/* 1. Replace 'arp' with abbrevation (eth) */
/* 2. Replace 'arp' with full-name (ethernet) */
/* 3. Fill in netarpUp and netarpDown */

#include <Topsy.h>
#include <Messages.h>
#include <Syscall.h>

#include <NetAttr.h>
#include <NetBuf.h>
#include <NetDebug.h>
#include <NetHAL.h>
#include <NetModules.h>
#include <NetIface.h>
#include <NetConfig.h>
#include <NetConfigARP.h>

#include <ARP/NetARP.h>
#include <Ethernet/NetEthernet.h>

#define NETARP_OP_REQUEST 1
#define NETARP_OP_REPLY   2

void netarpRead(ThreadId id, unsigned long int length);
static void netarpMain(ThreadArg arg);
static void netarpUp(NetBuf buf, NetAttrs attrs);
static void netarpDown(NetBuf buf, NetAttrs attrs);

void netarpInit()
{
  ThreadId netarpId;

  /* Start main thread */
  if (tmStart(&netarpId, netarpMain, (ThreadArg)0, "netarp") != TM_STARTOK)
  {
    netdbgDisplay(NETDEBUG_GENERIC, "Start: couldn't start arp thread\n");
    return;
  }
}

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

  tmGetInfo(SELF, &myThreadId, &parentThreadId);

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

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

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

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

static int netarpUpETHIP(NetBuf buf, NetAttrs attrs);

static void netarpUp(NetBuf buf, NetAttrs attrs)
{
  NetARPHdr   *hdr = (NetARPHdr *) NETBUF_DATA(buf);
  int sendreply=0;
  unsigned short iface;

  netdbgDisplay(NETDEBUG_ARP, "netarp: Got upcoming packet.\n");
  if(NETBUF_LEN(buf) >= sizeof(NetARPHdr) && ntohs(hdr->hw_type)==NETARP_HWTYPE_ETH)
  {
    switch(ntohs(hdr->pr_type))
    {
    case NETETH_TYPE_IP:
      sendreply=netarpUpETHIP(buf,attrs);
      break;
    }
  }

  if(sendreply)
  {
    /* send to module which sent request */
    if(!netattrGet(attrs,NETATTR_IF_FROM,&iface))
      goto discard;
    netattrSet(attrs,NETATTR_IF_TO,iface);

    netdbgDisplay(NETDEBUG_ARP, "netarp: Sending reply...\n");

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

discard:
  netdbgDisplay(NETDEBUG_ARP, "netarp: Discarding upcoming packet.\n");
  netbufFree(buf);
  netbufFree((NetBuf)attrs);
}

int netarpDownFillSourceETHIP(NetBuf buf, NetAttrs attrs);

static void netarpDown(NetBuf buf, NetAttrs attrs)
{
  unsigned short hw_type, hw_len, pr_type, pr_len, tmp, pkt_len;
  NetARPHdr *hdr;
  char *data;

  netdbgDisplay(NETDEBUG_ARP, "netarp: Got request to send ARP.\n");

  /* buf should be 0... */
  if(buf)
  {
    netbufFree(buf);
    buf=0;
  }

  /* get header data */
  if(!netattrGet(attrs,NETATTR_ARP_HW_TYPE,&hw_type))
    goto error;
  if(!netattrGet(attrs,NETATTR_ARP_HW_LEN, &hw_len))
    goto error;
  if(!netattrGet(attrs,NETATTR_ARP_PR_TYPE,&pr_type))
    goto error;
  if(!netattrGet(attrs,NETATTR_ARP_PR_LEN, &pr_len))
    goto error;

  /* realloc a buf of suitable size */
  pkt_len = sizeof(NetARPHdr)+hw_len*2+pr_len*2;
  if(!netbufAlloc(&buf, pkt_len))
  {
    buf=0;
    goto error;
  }

  //buf->end = buf->start+pkt_len;
  /* put arp request at end of buffer to leave space for ethernet header */
  buf->end   = NETBUF_SIZE(buf)+NETBUF_HEADERSIZE;
  buf->start = buf->end-pkt_len;

  /* fill header */
  hdr = (NetARPHdr *) NETBUF_DATA(buf);
  hdr->hw_type = hw_type;
  hdr->hw_len  = hw_len;
  hdr->pr_type = pr_type;
  hdr->pr_len  = pr_len;
  hdr->op      = NETARP_OP_REQUEST;

  /* target can be made protocol independent */

  /* fill target */
  data = NETBUF_DATA(buf) + sizeof(NetARPHdr) + hw_len*2 + pr_len;

  /* at the moment only pr_len=4 is supported */
  if(pr_len!=4)
    goto error;

  if(!netattrGet(attrs,NETATTR_ARP_PR_0,&tmp))
    goto error;
  data[0]=tmp%256;
  data[1]=tmp>>8;
  if(!netattrGet(attrs,NETATTR_ARP_PR_1,&tmp))
    goto error;
  data[2]=tmp%256;
  data[3]=tmp>>8;

  /* fill source (protocol dependent) */
  if(hw_type==NETARP_HWTYPE_ETH && pr_type==NETETH_TYPE_IP)
  {
    if(!netarpDownFillSourceETHIP(buf,attrs))
      goto error;
  }
  else
  {
    goto error;
  }
  /* Ethernet: it's ARP and it's a broadcast... */
  if(hw_type==NETARP_HWTYPE_ETH)
  {
    netattrSet(attrs, NETATTR_ETH_TYPE, NETETH_TYPE_ARP);
    netattrSet(attrs, NETATTR_ETH_TO_0, (unsigned short) 0xFFFF);
    netattrSet(attrs, NETATTR_ETH_TO_1, (unsigned short) 0xFFFF);
    netattrSet(attrs, NETATTR_ETH_TO_2, (unsigned short) 0xFFFF);
  }

  netdbgDisplay(NETDEBUG_ARP, "netarp: Sending ARP request.\n");
  netcfgSendNextDown(NETMODULE_ARP, buf, attrs);
  return;

error:
  netdbgDisplay(NETDEBUG_ARP, "netarp: Discarding downgoing packet.\n");
  if(buf)
    netbufFree(buf);
  NETATTR_FREE(attrs);
}




/***************** IPv4 on ETHERNET (ETHIP) ******************/

struct NetARP_ETHIP
{
  NetARPHdr hdr;
  unsigned char shw[6];
  unsigned char sip[4];
  unsigned char thw[6];
  unsigned char tip[4];
};

static int netarpUpETHIP(NetBuf buf, NetAttrs attrs)
{
  unsigned short iface,tmp,i;
  struct NetARP_ETHIP *arp = (struct NetARP_ETHIP *) NETBUF_DATA(buf);

  if(NETBUF_LEN(buf)<sizeof(struct NetARP_ETHIP))
  {
    netdbgPrintf(NETDEBUG_ARP, "ARP: packet too small.\n");
    return 0; /* discard */
  }

  /* Trim packet */
  netbufTrim(buf,sizeof(struct NetARP_ETHIP));

  netdbgPrintf(NETDEBUG_ARP, "netarp: source=(%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d)\n",
               arp->shw[0], arp->shw[1], arp->shw[2],
               arp->shw[3], arp->shw[4], arp->shw[5],
               arp->sip[0], arp->sip[1], arp->sip[2], arp->sip[3]);

  netcfgarpAddCacheETHIP(arp->shw,arp->sip);
  if(arp->hdr.op != NETARP_OP_REQUEST)
  {
    netcfgarpAddCacheETHIP(arp->thw,arp->tip);
  }

  netdbgPrintf(NETDEBUG_ARP, "netarp: target=(%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d)\n",
               arp->thw[0], arp->thw[1], arp->thw[2],
               arp->thw[3], arp->thw[4], arp->thw[5],
               arp->tip[0], arp->tip[1], arp->tip[2], arp->tip[3]);

  /* Get iface nr. */
  if(!netattrGet(attrs,NETATTR_IF_FROM,&iface))
    return 0; /* discard */

  /* We could cache this somehow... ARP shouldn't come frequently though. */
  /* Get our IP addr. and compare */
  if(!netifGetAttr(iface,NETATTR_IP_FROM_0,&tmp))
    return 0;
  if(tmp!=arp->tip[0])
    return 0;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_1,&tmp))
    return 0;
  if(tmp!=arp->tip[1])
    return 0;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_2,&tmp))
    return 0;
  if(tmp!=arp->tip[2])
    return 0;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_3,&tmp))
    return 0;
  if(tmp!=arp->tip[3])
    return 0;

  /* If it's a REPLY, it is already processed... */
  if(arp->hdr.op != NETARP_OP_REQUEST)
    return 0;

  /* The request is for us!! Send reply... (Stevens-1, 57) */

  /* Swap source/target */
  for(i=0;i<4;i++)
  {
    tmp=arp->tip[i];
    arp->tip[i]=arp->sip[i];
    arp->sip[i]=tmp;
  }
  for(i=0;i<6;i++)
  {
    arp->thw[i]=arp->shw[i];
  }

  /* Fill in HW address */
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_0,&tmp))
    return 0;
  arp->shw[0]=tmp%256;
  arp->shw[1]=tmp>>8;
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_1,&tmp))
    return 0;
  arp->shw[2]=tmp%256;
  arp->shw[3]=tmp>>8;
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_2,&tmp))
    return 0;
  arp->shw[4]=tmp%256;
  arp->shw[5]=tmp>>8;

  /* Set 'op' to REPLY */
  arp->hdr.op = NETARP_OP_REPLY;

  /* Set attributes */
  tmp = arp->thw[1];
  tmp <<= 8;
  tmp += arp->thw[0];
  netattrSet(attrs, NETATTR_ETH_TO_0, tmp);
  tmp = arp->thw[3];
  tmp <<= 8;
  tmp += arp->thw[2];
  netattrSet(attrs, NETATTR_ETH_TO_1, tmp);
  tmp = arp->thw[5];
  tmp <<= 8;
  tmp += arp->thw[4];
  netattrSet(attrs, NETATTR_ETH_TO_2, tmp);

  netdbgDisplay(NETDEBUG_ARP, "ARP: Sending reply.\n");

  return 1;
}

int netarpDownFillSourceETHIP(NetBuf buf, NetAttrs attrs)
{
  unsigned short iface, tmp;
  struct NetARP_ETHIP *arp = (struct NetARP_ETHIP *) NETBUF_DATA(buf);

  if(!netattrGet(attrs,NETATTR_IF_TO,&iface))
    return 0;

  /* set source hw address */
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_0,&tmp))
    return 0;
  arp->shw[0]=tmp%256;
  arp->shw[1]=tmp>>8;
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_1,&tmp))
    return 0;
  arp->shw[2]=tmp%256;
  arp->shw[3]=tmp>>8;
  if(!netifGetAttr(iface,NETATTR_ETH_FROM_2,&tmp))
    return 0;
  arp->shw[4]=tmp%256;
  arp->shw[5]=tmp>>8;

  /* set source ip address */
  if(!netifGetAttr(iface,NETATTR_IP_FROM_0,&tmp))
    return 0;
  arp->sip[0]=tmp;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_1,&tmp))
    return 0;
  arp->sip[1]=tmp;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_2,&tmp))
    return 0;
  arp->sip[2]=tmp;
  if(!netifGetAttr(iface,NETATTR_IP_FROM_3,&tmp))
    return 0;
  arp->sip[3]=tmp;

  return 1;
}
