/*
    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/IP/NetIPup.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: NetIPup.c,v $
    Revision 1.2  2000/11/15 10:12:14  ruf
    Repository Reorganisation
 
    Revision 1.1  2000/03/31 17:50:37  gfa
    Merged with /Net from several term projects
 
 
*/
#include "NetIP.h"
#include "NetIPchecksum.h"

#include <Topsy.h>
#include <Messages.h>
#include <NetDebug.h>
#include <NetBuf.h>
#include <NetAttr.h>
#include <NetConfig.h>
#include <NetIface.h>
#include <NetModules.h>
#include <NetBitFields.h>

static int netipVerify(NetBuf buf);
static int netipForUs(NetBuf buf, NetAttrs attrs);
static void netipSetAttrs(NetBuf buf, NetAttrs attrs);

void netipUp(NetBuf buf, NetAttrs attrs)
{
  NetIPHdr *hdr=(NetIPHdr *) NETBUF_DATA(buf);

  netdbgDisplay(NETDEBUG_IP, "netip : Verifying.\n");

  /* Is the packet ok ? */

  if(!netipVerify(buf))
    goto discard;

  /* Is the packet for us ? */
  if(!netipForUs(buf,attrs))
  {
    netdbgDisplay(NETDEBUG_IP,"netip : Packet not for us.\n");
    goto discard; /* no forwarding at the moment. */
  }

  /* Set remaining attributes not already done */
  netipSetAttrs(buf,attrs);

  netdbgDisplay(NETDEBUG_IP, "netip : Verification passed.\n");

  netdbgPrintf(NETDEBUG_GENERIC, "netip: before=%p\n", NETBUF_DATA(buf));
  /* Remove header */
  buf->start += BNIBGET(hdr->bitfield1,1)<<2;
  if(buf->start>buf->end)
    goto discard;

  netdbgPrintf(NETDEBUG_GENERIC, "netip: after=%p\n", NETBUF_DATA(buf));

  /* Send up packet */
  netcfgSendNextUp(NETMODULE_IP, buf, attrs);
  return;

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

static int netipVerify(NetBuf buf)
{
  NetIPHdr *hdr=(NetIPHdr *) NETBUF_DATA(buf);
  int len = 0;

  /* check minimal length */
  if(NETBUF_LEN(buf) < sizeof(NetIPHdr))
  {
    netdbgDisplay(NETDEBUG_IP, "netip : Packet size smaller than minimal length.\n");
    return 0;
  }

  /* check version */
  /* If version==6, the packet should be sent to the IPv6 module. */
  if(BNIBGET(hdr->bitfield1,0) != 4)
  {
    netdbgDisplay(NETDEBUG_IP, "netip : IP version not 4.\n");
    return 0;
  }

  /* check header length */
  len = BNIBGET(hdr->bitfield1,1) << 2;
  if(NETBUF_LEN(buf) < len)
    return 0;

  /* check checksum */
  if((hdr->checksum = netipChecksum(buf, len)))
  {
    netdbgDisplay(NETDEBUG_IP, "netip : Wrong checksum. Discarding packet.\n");
    return 0;
  }

  /* check total length */
  len = netbufLen(buf);
  if(len < hdr->tot_len)
  {
    netdbgPrintf(NETDEBUG_IP,
                 "netip : Packet too small (is %d, should be at least %d).\n",
                 len, hdr->tot_len);
    return 0;
  }
  if(len > hdr->tot_len)
    netbufTrim(buf, hdr->tot_len);

  return 1;
}

static int netipForUs(NetBuf buf, NetAttrs attrs)
{
  NetIPHdr *hdr=(NetIPHdr *) NETBUF_DATA(buf);
  unsigned short iface,a;

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

  /* Broadcast is not handled yet... */

  if(!netifGetAttr(iface,NETATTR_IP_FROM_0,&a))
    return 0;
  if(a!=hdr->daddr0)
    return 0;
  netattrSet(attrs,NETATTR_IP_TO_0,a);

  if(!netifGetAttr(iface,NETATTR_IP_FROM_1,&a))
    return 0;
  if(a!=hdr->daddr1)
    return 0;
  netattrSet(attrs,NETATTR_IP_TO_1,a);

  if(!netifGetAttr(iface,NETATTR_IP_FROM_2,&a))
    return 0;
  if(a!=hdr->daddr2)
    return 0;
  netattrSet(attrs,NETATTR_IP_TO_2,a);

  if(!netifGetAttr(iface,NETATTR_IP_FROM_3,&a))
    return 0;
  if(a!=hdr->daddr3)
    return 0;
  netattrSet(attrs,NETATTR_IP_TO_3,a);

  return 1;
}

static void netipSetAttrs(NetBuf buf, NetAttrs attrs)
{
  NetIPHdr *hdr=(NetIPHdr *) NETBUF_DATA(buf);
  unsigned short tmp;

  /* Source address */
  netattrSet(attrs,NETATTR_IP_FROM_0,(unsigned short) hdr->saddr0);
  netattrSet(attrs,NETATTR_IP_FROM_1,(unsigned short) hdr->saddr1);
  netattrSet(attrs,NETATTR_IP_FROM_2,(unsigned short) hdr->saddr2);
  netattrSet(attrs,NETATTR_IP_FROM_3,(unsigned short) hdr->saddr3);

  /* Protocol */
  tmp = hdr->protocol;
  netattrSet(attrs, NETATTR_IP_PROTOCOL, tmp);
}

