/*
    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/NetConfigARP.c,v $
    Author(s):             
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
 
 
    $Log: NetConfigARP.c,v $
    Revision 1.3  2001/01/23 09:43:31  cjeker
    moved to Net/old
 
    Revision 1.2  2000/11/15 10:12:14  ruf
    Repository Reorganisation
 
    Revision 1.1  2000/03/31 17:50:34  gfa
    Merged with /Net from several term projects
 
*/
#include <NetConfigARP.h>

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

/* This file is the ARP cache */

void netcfgarpAddCacheETHIP(unsigned char *hw, unsigned char *ip);
static void netcfgarpInitETHIP();

void netcfgarpInit()
{
  netcfgarpInitETHIP();
}

/* ETH <-> IP (ETHIP) is the most common Address Resolution Protocol (ARP) */

typedef struct NetCfgARP_ETHIP_Entry_t
{
  unsigned char ip[4];
  unsigned char hw[6];
}
NetCfgARP_ETHIP_Entry;

static NetCfgARP_ETHIP_Entry netcfgarp_ethip_cache[NETCFGARP_CACHESIZE];
static int netcfgarp_ethip_pos;

NetCfgARP_ETHIP_Entry *netcfgarpLookupETHIP(unsigned char *ip);

static void netcfgarpInitETHIP()
{
  int i = 0;

  for(i=0; i<NETCFGARP_CACHESIZE; i++)
  {
    netcfgarp_ethip_cache[i].ip[0]=0;
    netcfgarp_ethip_cache[i].ip[1]=0;
    netcfgarp_ethip_cache[i].ip[2]=0;
    netcfgarp_ethip_cache[i].ip[3]=0;
  }

  netcfgarp_ethip_pos=0;
}

void netcfgarpAddCacheETHIP(unsigned char *hw, unsigned char *ip)
{
  NetCfgARP_ETHIP_Entry *entry;

  entry = netcfgarpLookupETHIP(ip);

  if(!entry)
  {
    netcfgarp_ethip_pos = (netcfgarp_ethip_pos+1)%NETCFGARP_CACHESIZE;
    entry = netcfgarp_ethip_cache+netcfgarp_ethip_pos;
    entry->ip[0]=ip[0];
    entry->ip[1]=ip[1];
    entry->ip[2]=ip[2];
    entry->ip[3]=ip[3];
  }

  entry->hw[0]=hw[0];
  entry->hw[1]=hw[1];
  entry->hw[2]=hw[2];
  entry->hw[3]=hw[3];
  entry->hw[4]=hw[4];
  entry->hw[5]=hw[5];

#ifdef NETDEBUG

  netdbgPrintf(NETDEBUG_ARP,
               "netcfg: Adding ARP cache entry (%02x:%02x:%02x:%02x:%02x:%02x,%d.%d.%d.%d)\n",
               hw[0], hw[1], hw[2], hw[3], hw[4], hw[5],
               ip[0], ip[1], ip[2], ip[3]);
#endif
}

NetCfgARP_ETHIP_Entry *netcfgarpLookupETHIP(unsigned char *ip)
{
  int i = 0,j = 0;
  NetCfgARP_ETHIP_Entry *entry;

  /* brute search ... */

  for(i=0; i<NETCFGARP_CACHESIZE; i++)
  {
    entry = netcfgarp_ethip_cache+i;
    for(j=3; j>=0; j--)
    {
      if(entry->ip[j] != ip[j])
      {
        break;
      }
    }
    if(j<0)
      return entry;
  }

  /* not found...*/
  netdbgPrintf(NETDEBUG_ARP, "netcfg: ARP lookup failed.\n");
  return 0;
}

void netcfgarpSendRequestETHIP(NetBuf buf, NetAttrs attrs, unsigned char *ip);

int netcfgarpResolveETHIP(NetBuf buf, NetAttrs attrs)
{
  NetCfgARP_ETHIP_Entry *e;
  unsigned short tmp;
  unsigned char ip[4];

  if(!netattrGet(attrs,NETATTR_IP_TO_0, &tmp))
    return 0;
  ip[0] = tmp;
  if(!netattrGet(attrs,NETATTR_IP_TO_1, &tmp))
    return 0;
  ip[1] = tmp;
  if(!netattrGet(attrs,NETATTR_IP_TO_2, &tmp))
    return 0;
  ip[2] = tmp;
  if(!netattrGet(attrs,NETATTR_IP_TO_3, &tmp))
    return 0;
  ip[3] = tmp;

  e = netcfgarpLookupETHIP(ip);
  if(e)
  {
    tmp = (unsigned short) e->hw[0]<<8 | e->hw[1];
    netattrSet(attrs,NETATTR_ETH_TO_0,tmp);
    tmp = (unsigned short) e->hw[2]<<8 | e->hw[3];
    netattrSet(attrs,NETATTR_ETH_TO_1,tmp);
    tmp = (unsigned short) e->hw[4]<<8 | e->hw[5];
    netattrSet(attrs,NETATTR_ETH_TO_2,tmp);
    return 1;
  }
  else
  {
    /* send arp */
    /* since IP doesn't make any delivery guarantee, it is ok
     * (although ugly) to throw away the not resolved packet
     * instead of putting it in a wait queue
     */
    netdbgDisplay(NETDEBUG_CONFIG, "netcfg: Sending ARP request.\n");
    netcfgarpSendRequestETHIP(buf, attrs, ip);

    return 0;
  }

}

void netcfgarpSendRequestETHIP(NetBuf buf, NetAttrs pkt_attrs, unsigned char *ip)
{
  unsigned short tmp;
  NetAttrs arp_attrs;

  if(!netattrNew(&arp_attrs))
    return;

  /* ARP should be sent on the same interface */
  if(!netattrGet(pkt_attrs, NETATTR_IF_TO, &tmp))
  {
    NETATTR_FREE(arp_attrs);
    return;
  }
  netattrSet(arp_attrs, NETATTR_IF_TO, tmp);

  /* set hw_type, hw_len, pr_type, pr_len */
  netattrSet(arp_attrs, NETATTR_ARP_HW_TYPE, (unsigned short) NETARP_HWTYPE_ETH);
  netattrSet(arp_attrs, NETATTR_ARP_HW_LEN, (unsigned short) 6);
  netattrSet(arp_attrs, NETATTR_ARP_PR_TYPE, (unsigned short) NETETH_TYPE_IP);
  netattrSet(arp_attrs, NETATTR_ARP_PR_LEN, (unsigned short) 4);

  /* set target pr_address */
  netattrSet(arp_attrs, NETATTR_ARP_PR_0, ip[0]+(ip[1]<<8));
  netattrSet(arp_attrs, NETATTR_ARP_PR_1, ip[2]+(ip[3]<<8));

  /* send... */
  netdbgDisplay(NETDEBUG_CONFIG, "netcfg: Message sent to module ARP.\n");
  netmodSendDown(NETMODULE_ARP, 0, arp_attrs);
}
