/*
    Copyright 2000 (c) by Claudio Jeker,
    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/ARP/NetARPSend.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 "NetARP.h"
#include "NetARPModules.h"
#include "NetDevice.h"
#include "NetBuf.h"
#include "NetAttr.h"
#include "NetByteorder.h"
#include "NetDebug.h"
#include "Support.h"

void netARPSendInit(NetModule this)
{
  return;
}

int netARPSendConfig(NetModule this, Message *msg)
{
  return 0;
}

void netARPSendMain(LWPool lw, ThreadArg arg)
{
  NetModule		modDesc = (NetModule) arg;
  NetBuf			data;
  NetAttrs		meta;
  NetDevice device;
  u16 hw_type, ha_len, pa_len, hrd, pro, v;
  short dev_nr;
  struct arphdr *head;
  u8 *d;

  while (1)
  {
    netmodRecv(lw, &data, &meta);
    PACKSTART(modDesc);
    /* data is set to tpa */
    /* we need to set sha, spa, tha (0) */

    /* first get attributes */
    if ( !(	netattrGet(meta, NETATTR_ARP_HW_TYPE, &hw_type ) &&
            netattrGet(meta, NETATTR_IF_TO, &dev_nr ) &&
            netattrGet(meta, NETATTR_ARP_PR_TYPE, &pro) &&
            netattrGet(meta, NETATTR_ARP_PR_LEN, &pa_len ) ) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netARPSend: failed to get attributes\n");
      continue;
    }
    /* now get the device descriptor (braindead ARP needs to know the local
       hardware and protocol address */
    if (! netdevGet(dev_nr, &device) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netARPSend: unknown device\n");
      continue;
    }

    /* set the ha_len and hardware type for arp */
    switch ( hw_type )
    {
    case NETATTR_TYPE_ETHERNET:
      ha_len = 6;
      hrd = ARP_TYPE_ETHERNET;
      if (!netattrSet(meta, NETATTR_OSI_2_TYPE, NETATTR_TYPE_ETHERNET))
      {
        PACKERROR(modDesc);
        netmodRIP(lw, data, meta,
                  "netARPSend: can't set hardware type\n");
        continue;
      }
      break;
    default:
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta,
                "netARPSend: unknown hardware type\n");
      continue;
    }

    /* now we can calculate the needed space */
    if ( ! netbufAddHeadSpace(&data, sizeof(struct arphdr) +
                              2 * ha_len + pa_len) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netARPSend: not enough memory\n");
      continue;
    }
    /* set the header */
    head = (struct arphdr *) NETBUF_DATA(data);
    head->ar_hrd = htons(hrd);
    head->ar_pro = htons(pro);
    head->ar_hln = (u8) ha_len;
    head->ar_pln = (u8) pa_len;
    head->ar_op = htons(ARP_REQUEST_OP);

    d = (u8*)head + sizeof(struct arphdr);
    switch ( hw_type )
    {
    case NETATTR_TYPE_ETHERNET:
      /* get source ethernet address and set it */
      /* set also the target hardware address to 0 */
      if (! netattrGet(device->addrList->addr,
                       NETATTR_ETH_LOCAL_0, &v ) )
        goto fail;
      d[1] = v & 0xFF;
      d[0] = v >> 8;
      if (! netattrGet(device->addrList->addr,
                       NETATTR_ETH_LOCAL_1, &v ) )
        goto fail;
      d[3] = v & 0xFF;
      d[2] = v >> 8;
      if (! netattrGet(device->addrList->addr,
                       NETATTR_ETH_LOCAL_2, &v ) )
        goto fail;
      d[5] = v & 0xFF;
      d[4] = v >> 8;
      /* wipe target hardware address */
      zeroOut( d + ha_len + pa_len, ha_len);
      break;
    default: /* make gcc happy */
      break;
    }
    /* now set the correct IP address */
    switch ( pro )
    {
    case ARP_TYPE_IPv4:
      /* get source IPv4 address and set it */
      if (! netattrGet(device->addrList->addr,
                       NETATTR_IP_LOCAL_0, &v ) )
        goto fail;
      d[ha_len + 0] = v & 0xFF;
      if (! netattrGet(device->addrList->addr,
                       NETATTR_IP_LOCAL_1, &v ) )
        goto fail;
      d[ha_len + 1] = v & 0xFF;
      if (! netattrGet(device->addrList->addr,
                       NETATTR_IP_LOCAL_2, &v ) )
        goto fail;
      d[ha_len + 2] = v & 0xFF;
      if (! netattrGet(device->addrList->addr,
                       NETATTR_IP_LOCAL_3, &v ) )
        goto fail;
      d[ha_len + 3] = v & 0xFF;
      break;
    case ARP_TYPE_IPv6:
    default: /* make gcc happy */
      break;
    }

    /* let the lover protocol now what we are arp broadcast */
    if ( !(	netattrSet(meta, NETATTR_OSI_3_TYPE, NETATTR_TYPE_ARP) &&
            netattrSet(meta, NETATTR_BROADCAST, 1) ) )
    {
      PACKERROR(modDesc);
      netmodRIP(lw, data, meta, "netARPSend: failed to set attribute\n");
      continue;
    }

    PACKDONE(modDesc);
    netmodSend(lw, CHAINNEXT(modDesc), data, meta);
    continue;
fail:
    PACKERROR(modDesc);
    netmodRIP(lw, data, meta, "netARPSend: failed to get attributes\n");
    continue;
  }
}

