/*
    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/NetAttr.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2936 $
    Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $  by: $Author: baderma $
 
*/
#include <NetAttr.h>
#include <NetDebug.h>
#include <UserSupport.h>

int netattrNew(NetAttrs *a)
{
  NetAttr *at;
  int i = 0;

  if(!netbufAlloc((NetBuf *) a, NETATTRS_SIZE))
    return 0;

  (*a)->netbufhdr.start=NETBUF_HEADERSIZE;
  (*a)->netbufhdr.end  =NETBUF_HEADERSIZE+NETATTRS_SIZE;

  at = (*a)->attr;
  for(i=0; i<NETATTRS_MAXATTRS; i++)
    at[i].key=0;

  return 1;
}

/* This could be better :-)
 */
inline int netattrHash(unsigned short key)
{
  return key % NETATTRS_MAXATTRS;
}

inline int netattrFind(NetAttrs a, unsigned short key)
{
  int begin = netattrHash(key);
  int i=begin;

  while(a->attr[i].key != 0)
  {
    if(a->attr[i].key == key)
      return i;
    i++;
    i%=NETATTRS_MAXATTRS;
    if(i == begin)
      break;
  }
  return -1;
}

int netattrSet(NetAttrs a, unsigned short key, unsigned short data)
{
  int begin = netattrHash(key);
  int i = begin;

  /* find first empty place */
  while(a->attr[i].key != 0 && a->attr[i].key != key)
  {
    i++;
    i%=NETATTRS_MAXATTRS;
    if(i == begin)
      return 0; /* full! */
  }
  a->attr[i].key=key;
  a->attr[i].data=data;
  return 1;
}

void netattrUnset(NetAttrs a, unsigned short key)
{
  int i = netattrFind(a, key);
  if(i>=0)
    a->attr[i].key=0;
}

int netattrGet(NetAttrs a, unsigned short key, unsigned short *data)
{
  int i = netattrFind(a, key);
  if(i>=0)
  {
    *data = a->attr[i].data;
    return 1;
  }
  *data = 0;
  return 0;
}

int netattrCopy(NetAttrs target, NetAttrs source)
{
  int i = 0;

  for(i=0; i<NETATTRS_MAXATTRS;i++)
  {
    if ( source->attr[i].key != 0 )
    {
      if (!netattrSet(target, source->attr[i].key, source->attr[i].data))
        return 0;
    }
  }
  return 1;
}

/* take from header file use vim and:
 * :.! cat NetAttr.h | perl -e 'foreach $_ (<>) { s$__NETATTR_H__|//.*|/\*.*$$g; foreach $p (m$NETATTR_[^ ,\t]*$g) { printf "\%s\n", $p } }' | grep NETATTR
 * :'<,'>s/\(NETATTR[^ \t,]*\)/\t\tcase \1:\t\t\treturn "\1";/g
 * dont forget ident it
 * :) -- cjeker 20010315
 */
char netattrUnknownKey[24];

char* netattrGetName(unsigned short key)
{
  switch(key)
  {
  case NETATTR_UNKNOWN:
    return "NETATTR_UNKNOWN";
  case NETATTR_OSI_3_TYPE:
    return "NETATTR_OSI_3_TYPE";
  case NETATTR_OSI_4_TYPE:
    return "NETATTR_OSI_4_TYPE";
  case NETATTR_IN_TYPE:
    return "NETATTR_IN_TYPE";
  case NETATTR_OSI_2_TYPE:
    return "NETATTR_OSI_2_TYPE";
  case NETATTR_TYPE_ETHERNET:
    return "NETATTR_TYPE_ETHERNET";
  case NETATTR_TYPE_IPv4:
    return "NETATTR_TYPE_IPv4";
  case NETATTR_TYPE_IPv6:
    return "NETATTR_TYPE_IPv6";
  case NETATTR_TYPE_ARP:
    return "NETATTR_TYPE_ARP";
  case NETATTR_TYPE_UDP:
    return "NETATTR_TYPE_UDP";
  case NETATTR_TYPE_ICMP:
    return "NETATTR_TYPE_ICMP";
  case NETATTR_TYPE_ICMP6:
    return "NETATTR_TYPE_ICMP6";
  case NETATTR_TYPE_TCP:
    return "NETATTR_TYPE_TCP";
  case NETATTR_TYPE_RAWIP:
    return "NETATTR_TYPE_RAWIP";
  case NETATTR_TYPE_UNKOWN:
    return "NETATTR_TYPE_UNKOWN";
  case NETATTR_IF_FROM:
    return "NETATTR_IF_FROM";
  case NETATTR_IF_TO:
    return "NETATTR_IF_TO";
  case NETATTR_IF_MTU:
    return "NETATTR_IF_MTU";
  case NETATTR_BROADCAST:
    return "NETATTR_BROADCAST";
  case NETATTR_ETH_LOCAL_0:
    return "NETATTR_ETH_LOCAL_0";
  case NETATTR_ETH_LOCAL_1:
    return "NETATTR_ETH_LOCAL_1";
  case NETATTR_ETH_LOCAL_2:
    return "NETATTR_ETH_LOCAL_2";
  case NETATTR_ETH_REMOTE_0:
    return "NETATTR_ETH_REMOTE_0";
  case NETATTR_ETH_REMOTE_1:
    return "NETATTR_ETH_REMOTE_1";
  case NETATTR_ETH_REMOTE_2:
    return "NETATTR_ETH_REMOTE_2";
  case NETATTR_ETH_TYPE:
    return "NETATTR_ETH_TYPE";
  case NETATTR_IP_LOCAL_0:
    return "NETATTR_IP_LOCAL_0";
  case NETATTR_IP_LOCAL_1:
    return "NETATTR_IP_LOCAL_1";
  case NETATTR_IP_LOCAL_2:
    return "NETATTR_IP_LOCAL_2";
  case NETATTR_IP_LOCAL_3:
    return "NETATTR_IP_LOCAL_3";
  case NETATTR_IP_REMOTE_0:
    return "NETATTR_IP_REMOTE_0";
  case NETATTR_IP_REMOTE_1:
    return "NETATTR_IP_REMOTE_1";
  case NETATTR_IP_REMOTE_2:
    return "NETATTR_IP_REMOTE_2";
  case NETATTR_IP_REMOTE_3:
    return "NETATTR_IP_REMOTE_3";
  case NETATTR_IP_VERSION:
    return "NETATTR_IP_VERSION";
  case NETATTR_IP_TOS:
    return "NETATTR_IP_TOS";
  case NETATTR_IP_TTL:
    return "NETATTR_IP_TTL";
  case NETATTR_IP_FRAG:
    return "NETATTR_IP_FRAG";
  case NETATTR_IP_ID:
    return "NETATTR_IP_ID";
  case NETATTR_IP_DATALEN:
    return "NETATTR_IP_DATALEN";
  case NETATTR_IP_HEADLEN:
    return "NETATTR_IP_HEADLEN";
  case NETATTR_IP_PROTOCOL:
    return "NETATTR_IP_PROTOCOL";
  case NETATTR_IP_CHECKSUM:
    return "NETATTR_IP_CHECKSUM";
  case NETATTR_IP_MTU:
    return "NETATTR_IP_MTU";
  case NETATTR_ARP_HW_TYPE:
    return "NETATTR_ARP_HW_TYPE";
  case NETATTR_ARP_HW_LEN:
    return "NETATTR_ARP_HW_LEN";
  case NETATTR_ARP_PR_TYPE:
    return "NETATTR_ARP_PR_TYPE";
  case NETATTR_ARP_PR_LEN:
    return "NETATTR_ARP_PR_LEN";
  case NETATTR_UDP_LOCAL:
    return "NETATTR_UDP_LOCAL";
  case NETATTR_UDP_REMOTE:
    return "NETATTR_UDP_REMOTE";
  case NETATTR_UDP_LENGTH:
    return "NETATTR_UDP_LENGTH";
  case NETATTR_UDP_CHECKSUM:
    return "NETATTR_UDP_CHECKSUM";
  case NETATTR_ICMP_TYPE:
    return "NETATTR_ICMP_TYPE";
  case NETATTR_ICMP_CODE:
    return "NETATTR_ICMP_CODE";
  case NETATTR_ICMP_CHECKSUM:
    return "NETATTR_ICMP_CHECKSUM";
  case NETATTR_TCP_LOCAL:
    return "NETATTR_TCP_LOCAL";
  case NETATTR_TCP_REMOTE:
    return "NETATTR_TCP_REMOTE";
  case NETATTR_OOBDATA:
    return "NETATTR_OOBDATA";
  case NETATTR_DISTRIBUTOR_KEY:
    return "NETATTR_DISTRIBUTOR_KEY";
  case NETATTR_SENDTO_THREAD:
    return "NETATTR_SENDTO_THREAD";
  case NETATTR_SENDTO_THREADLOW:
    return "NETATTR_SENDTO_THREADLOW";
  case NETATTR_SENDTO_THREADHIGH:
    return "NETATTR_SENDTO_THREADHIGH";
  case NETATTR_SENDTO_LOOP:
    return "NETATTR_SENDTO_LOOP";
  case NETATTR_SOCKET_THREADLOW:
    return "NETATTR_SOCKET_THREADLOW";
  case NETATTR_SOCKET_THREADHIGH:
    return "NETATTR_SOCKET_THREADHIGH";
  case NETATTR_LAST:
    return "NETATTR_LAST";
  default:
    snprintf(netattrUnknownKey, sizeof(netattrUnknownKey),
             "UNKNOWN KEY (%#x)", key);
    return netattrUnknownKey;
  }
}

void netattrDebug(unsigned long family, char *title, NetAttrs a)
{
  int i = 0;

  if((family==0) || (family & NETDEBUG_MASK))
  {
    if ( title )
    {
      netdbgPrintf(family,"%s attributes:\n", title);
    }
    for(i=0; i<NETATTRS_MAXATTRS;i++)
    {
      if(a->attr[i].key)
        netdbgPrintf(family, "  %s: %6i\n",
                     netattrGetName(a->attr[i].key), a->attr[i].data);
    }
  }
}

