/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2007-2008 Louis Pasteur University
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
 */

#include <iomanip>
#include <memory.h>

#include "ns3/log.h"
#include "ns3/assert.h"

#include "mac48-address.h"
#include "ipv6-address.h"

NS_LOG_COMPONENT_DEFINE ("Ipv6Address");

namespace ns3 {

#ifdef __cplusplus
extern "C"
{ /* } */
#endif

/**
 * \brief Get a hash key.
 * \param k the key
 * \param length the length of the key
 * \param level the previous hash, or an arbitrary value
 * \return hash
 * \note Adapted from Jens Jakobsen implementation (chillispot).
 */
static uint32_t lookuphash (unsigned char* k, uint32_t length, uint32_t level)
{
#define mix(a, b, c) \
  ({ \
     (a) -= (b); (a) -= (c); (a) ^= ((c) >> 13); \
     (b) -= (c); (b) -= (a); (b) ^= ((a) << 8);  \
     (c) -= (a); (c) -= (b); (c) ^= ((b) >> 13); \
     (a) -= (b); (a) -= (c); (a) ^= ((c) >> 12); \
     (b) -= (c); (b) -= (a); (b) ^= ((a) << 16); \
     (c) -= (a); (c) -= (b); (c) ^= ((b) >> 5);  \
     (a) -= (b); (a) -= (c); (a) ^= ((c) >> 3);  \
     (b) -= (c); (b) -= (a); (b) ^= ((a) << 10); \
     (c) -= (a); (c) -= (b); (c) ^= ((b) >> 15); \
   })

  typedef uint32_t  ub4;   /* unsigned 4-byte quantities */
  typedef unsigned  char ub1;   /* unsigned 1-byte quantities */
  uint32_t a = 0;
  uint32_t b = 0;
  uint32_t c = 0;
  uint32_t len = 0;

  /* Set up the internal state */
  len = length;
  a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
  c = level;           /* the previous hash value */

  /* handle most of the key */
  while (len >= 12)
    {
      a += (k[0] + ((ub4)k[1] << 8) + ((ub4)k[2] << 16) + ((ub4)k[3] << 24));
      b += (k[4] + ((ub4)k[5] << 8) + ((ub4)k[6] << 16) + ((ub4)k[7] << 24));
      c += (k[8] + ((ub4)k[9] << 8) + ((ub4)k[10] << 16) + ((ub4)k[11] << 24));
      mix (a, b, c);
      k += 12; 
      len -= 12;
    }

  /* handle the last 11 bytes */
  c += length;
  switch (len) /* all the case statements fall through */
    {
    case 11: c += ((ub4)k[10] << 24);
    case 10: c += ((ub4)k[9] << 16);
    case 9: c += ((ub4)k[8] << 8);  /* the first byte of c is reserved for the length */
    case 8: b += ((ub4)k[7] << 24);
    case 7: b += ((ub4)k[6] << 16);
    case 6: b += ((ub4)k[5] << 8);
    case 5: b += k[4];
    case 4: a += ((ub4)k[3] << 24);
    case 3: a += ((ub4)k[2] << 16);
    case 2: a += ((ub4)k[1] << 8);
    case 1: a += k[0];
      /* case 0: nothing left to add */
    }
  mix (a, b, c);

#undef mix

  /* report the result */
  return c;
}

#ifdef __cplusplus
}
#endif

/**
 * \brief Convert an IPv6 C-string into a 128-bit representation.
 * \return true if success, false otherwise (bad format, ...)
 * \note This function is strongly inspired by inet_pton6() from Paul Vixie.
 * \todo Handle IPv6 address with decimal value for last four bytes.
 */
static bool AsciiToIpv6Host (const char *address, uint8_t addr[16])
{
  static const char xdigits_l[] = "0123456789abcdef";
  static const char xdigits_u[] = "0123456789ABCDEF";
  unsigned char tmp[16];
  unsigned char* tp = tmp;
  unsigned char* endp = 0;
  unsigned char* colonp = 0;
  const char* xdigits = 0;
#if 0
  const char* curtok = 0;
#endif
  int ch = 0;
  int seen_xdigits = 0;
  unsigned int val = 0;

  memset (tp, 0x00, 16);
  endp = tp + 16;

  /* Leading :: requires some special handling. */
  if (*address == ':')
    {
      if (*++address != ':')
        {
          return (0);
        }
    }
#if 0
  curtok = address;
#endif
  while ((ch = *address++) != '\0')
    {
      const char *pch = 0;

      if ((pch = strchr ((xdigits = xdigits_l), ch)) == 0)
        {
          pch = strchr ((xdigits = xdigits_u), ch);
        }

      if (pch != 0)
        {
          val <<= 4;
          val |= (pch - xdigits);

          if (++seen_xdigits > 4)
            {
              return (0);
            }
          continue;
        }
      if (ch == ':')
        {
#if 0
          curtok = address;
#endif

          if (!seen_xdigits)
            {
              if (colonp)
                return (0);
              colonp = tp;
              continue;
            }

          if (tp + 2 > endp)
            {
              return (0);
            }

          *tp++ = (unsigned char)(val >> 8) & 0xff;
          *tp++ = (unsigned char) val & 0xff;
          seen_xdigits = 0;
          val = 0;
          continue;
        }

      /* TODO Handle IPv4 mapped address (2001::192.168.0.1) */
#if 0
      if (ch == '.' && ((tp + 4 /*NS_INADDRSZ*/) <= endp) &&
          inet_pton4 (curtok, tp) > 0)
        {
          tp += 4 /*NS_INADDRSZ*/;
          seen_xdigits = 0;
          break; /* '\0' was seen by inet_pton4(). */
        }
#endif
      return (0);
    }

  if (seen_xdigits)
    {
      if (tp + 2 > endp)
        {
          return (0);
        }
      *tp++ = (unsigned char)(val >> 8) & 0xff;
      *tp++ = (unsigned char) val & 0xff;
    }

  if (colonp != 0)
    {
      /*
       * Since some memmove ()'s erroneously fail to handle
       * overlapping regions, we'll do the shift by hand.
       */
      const int n = tp - colonp;
      int i = 0;

      if (tp == endp)
        {
          return (0);
        }

      for (i = 1; i <= n; i++)
        {
          endp[-i] = colonp[n - i];
          colonp[n - i] = 0;
        }

      tp = endp;
    }

  if (tp != endp)
    {
      return (0);
    }

  memcpy (addr, tmp, 16);
  return (1);
}

Ipv6Address::Ipv6Address ()
{
  memset (m_address, 0x00, 16);
}

Ipv6Address::Ipv6Address (Ipv6Address const& addr)
{
  memcpy (m_address, addr.m_address, 16);
}

Ipv6Address::Ipv6Address (Ipv6Address const* addr)
{
  memcpy (m_address, addr->m_address, 16);
}

Ipv6Address::Ipv6Address (char const* address)
{
  AsciiToIpv6Host (address, m_address);
}

Ipv6Address::Ipv6Address (uint8_t address[16])
{
  /* 128 bit => 16 bytes */
  memcpy (m_address, address, 16);
}

Ipv6Address::~Ipv6Address ()
{
  /* do nothing */
}

void Ipv6Address::Set (char const* address)
{
  AsciiToIpv6Host (address, m_address);
}

void Ipv6Address::Set (uint8_t address[16])
{
  /* 128 bit => 16 bytes */
  memcpy (m_address, address, 16);
}

void Ipv6Address::Serialize (uint8_t buf[16]) const
{
  memcpy (buf, m_address, 16);
}

Ipv6Address Ipv6Address::Deserialize (const uint8_t buf[16])
{
  Ipv6Address ipv6 ((uint8_t*)buf);
  return ipv6;
}

Ipv6Address Ipv6Address::MakeIpv4MappedAddress(Ipv4Address addr)
{
  uint8_t buf[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
  addr.Serialize (&buf[12]);
  return (Ipv6Address (buf));
}

Ipv4Address Ipv6Address::GetIpv4MappedAddress() const
{
    uint8_t buf[16];
    Ipv4Address v4Addr;

    Serialize (buf);
    v4Addr = Ipv4Address::Deserialize (&buf[12]);
    return (v4Addr);
}

Ipv6Address Ipv6Address::MakeAutoconfiguredAddress (Mac48Address addr, Ipv6Address prefix)
{
  Ipv6Address ret;
  uint8_t buf[16];
  uint8_t buf2[16];

  addr.CopyTo (buf);
  prefix.GetBytes (buf2);

  memcpy (buf2 + 8, buf, 3);
  buf2[11] = 0xff;
  buf2[12] = 0xfe;
  memcpy (buf2 + 13, buf + 3, 3);
  buf2[8] |= 0x02;

  ret.Set (buf2);
  return ret;
}

Ipv6Address Ipv6Address::MakeAutoconfiguredLinkLocalAddress (Mac48Address addr)
{
  Ipv6Address ret;
  uint8_t buf[16];
  uint8_t buf2[16];

  addr.CopyTo (buf);

  memset (buf2, 0x00, sizeof (buf2));
  buf2[0] = 0xfe;
  buf2[1] = 0x80;
  memcpy (buf2 + 8, buf, 3);
  buf2[11] = 0xff;
  buf2[12] = 0xfe;
  memcpy (buf2 + 13, buf + 3, 3);
  buf2[8] |= 0x02;

  ret.Set (buf2);
  return ret;
}

Ipv6Address Ipv6Address::MakeSolicitedAddress (Ipv6Address addr)
{
  uint8_t buf[16];
  uint8_t buf2[16];
  Ipv6Address ret;

  addr.Serialize (buf2);

  memset (buf, 0x00, sizeof (buf));
  buf[0] = 0xff;
  buf[1] = 0x02;
  buf[11] = 0x01;
  buf[12] = 0xff;
  buf[13] = buf2[13];
  buf[14] = buf2[14];
  buf[15] = buf2[15];

  ret.Set (buf);
  return ret;
}

void Ipv6Address::Print (std::ostream& os) const
{
  os << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[0]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[1] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[2]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[3] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[4]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[5] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[6]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[7] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[8]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[9] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[10]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[11] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[12]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[13] << ":"
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[14]
     << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_address[15]
     << std::dec << std::setfill (' ');
}

bool Ipv6Address::IsLocalhost () const
{
  static Ipv6Address localhost ("::1");
  return (*this == localhost);
}

bool Ipv6Address::IsMulticast () const
{
  if (m_address[0] == 0xff)
    {
      return true;
    }
  return false;
}

bool Ipv6Address::IsLinkLocalMulticast () const
{
  if (m_address[0] == 0xff && m_address[1] == 0x02)
    {
      return true;
    }
  return false;
}

bool Ipv6Address::IsIpv4MappedAddress ()
{
  uint8_t v4MappedPrefix[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                 0x00, 0x00, 0xff, 0xff };
  if (memcmp(m_address, v4MappedPrefix, sizeof(v4MappedPrefix)) == 0)
    {
      return (true);
    }
  return (false);
}

Ipv6Address Ipv6Address::CombinePrefix (Ipv6Prefix const& prefix)
{
  Ipv6Address ipv6;
  uint8_t addr[16];
  uint8_t pref[16];
  unsigned int i = 0;

  memcpy (addr, m_address, 16);
  ((Ipv6Prefix)prefix).GetBytes (pref);

  /* a little bit ugly... */
  for (i = 0; i < 16; i++)
    {
      addr[i] = addr[i] & pref[i];
    }
  ipv6.Set (addr);
  return ipv6;
}

bool Ipv6Address::IsSolicitedMulticast () const
{
  uint8_t buf[16];

  Serialize (buf);

  if (buf[0] == 0xff && 
      buf[1] == 0x02 &&
      buf[11] == 0x01 &&
      buf[12] == 0xff)
    {
      return true;
    }
  return false;
}

bool Ipv6Address::IsAllNodesMulticast () const
{
  static Ipv6Address allnodes ("ff02::1");
  return (*this == allnodes);
}

bool Ipv6Address::IsAllRoutersMulticast () const
{
  static Ipv6Address allrouters ("ff02::2");
  return (*this == allrouters);
}

bool Ipv6Address::IsAllHostsMulticast () const
{
  static Ipv6Address allhosts ("ff02::3");
  return (*this == allhosts);
}

bool Ipv6Address::IsAny () const
{
  static Ipv6Address any ("::");
  return (*this == any);
}

bool Ipv6Address::IsMatchingType (const Address& address)
{
  return address.CheckCompatible (GetType (), 16);
}

Ipv6Address::operator Address () const
{
  return ConvertTo ();
}

Address Ipv6Address::ConvertTo (void) const
{
  uint8_t buf[16];
  Serialize (buf);
  return Address (GetType (), buf, 16);
}

Ipv6Address Ipv6Address::ConvertFrom (const Address &address)
{
  NS_ASSERT (address.CheckCompatible (GetType (), 16));
  uint8_t buf[16];
  address.CopyTo (buf);
  return Deserialize (buf);
}

uint8_t Ipv6Address::GetType (void)
{
  static uint8_t type = Address::Register ();
  return type;
}

Ipv6Address Ipv6Address::GetAllNodesMulticast ()
{
  static Ipv6Address nmc ("ff02::1");
  return nmc;
}

Ipv6Address Ipv6Address::GetAllRoutersMulticast ()
{
  static Ipv6Address rmc ("ff02::2");
  return rmc;
}

Ipv6Address Ipv6Address::GetAllHostsMulticast ()
{
  static Ipv6Address hmc ("ff02::3");
  return hmc;
}

Ipv6Address Ipv6Address::GetLoopback ()
{
  static Ipv6Address loopback ("::1");
  return loopback;
}

Ipv6Address Ipv6Address::GetZero ()
{
  static Ipv6Address zero ("::");
  return zero;
}

Ipv6Address Ipv6Address::GetAny ()
{
  static Ipv6Address any ("::");
  return any;
}

Ipv6Address Ipv6Address::GetOnes ()
{
  static Ipv6Address ones ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
  return ones; 
}

void Ipv6Address::GetBytes (uint8_t buf[16]) const
{
  memcpy (buf, m_address, 16);
}

bool Ipv6Address::IsLinkLocal () const
{
  Ipv6Address linkLocal ("fe80::0");
  if (!IsMulticast () && ((Ipv6Address*)this)->CombinePrefix (Ipv6Prefix (64)) == linkLocal)
    {
      return true;
    }
  return false;
}

bool Ipv6Address::IsEqual (const Ipv6Address& other) const
{
  if (!memcmp (m_address, other.m_address, 16))
    {
      return true;
    }
  return false;
}

std::ostream& operator << (std::ostream& os, Ipv6Address const& address)
{
  address.Print (os);
  return os;
}

std::istream& operator >> (std::istream& is, Ipv6Address& address)
{
  std::string str;
  is >> str;
  address = Ipv6Address (str.c_str ());
  return is;
}

Ipv6Prefix::Ipv6Prefix ()
{
  memset (m_prefix, 0x00, 16);
}

Ipv6Prefix::Ipv6Prefix (char const* prefix)
{
  AsciiToIpv6Host (prefix, m_prefix);
}

Ipv6Prefix::Ipv6Prefix (uint8_t prefix[16])
{
  memcpy (m_prefix, prefix, 16);
}

Ipv6Prefix::Ipv6Prefix (uint8_t prefix)
{
  unsigned int nb=0;
  unsigned int mod=0;
  unsigned int i=0;

  memset (m_prefix, 0x00, 16);

  NS_ASSERT (prefix <= 128);

  nb = prefix / 8;
  mod = prefix % 8;

  // protect memset with 'nb > 0' check to suppress
  // __warn_memset_zero_len compiler errors in some gcc>4.5.x 
  if (nb > 0)
    {
      memset (m_prefix, 0xff, nb);
    }
  if (mod)
    {
      m_prefix[nb] = 0xff << (8-mod);
    }

  if (nb < 16)
    {
      nb++;
      for (i = nb; i < 16; i++)
        {
          m_prefix[i] = 0x00;
        }
    }
}

Ipv6Prefix::Ipv6Prefix (Ipv6Prefix const& prefix)
{
  memcpy (m_prefix, prefix.m_prefix, 16);
}

Ipv6Prefix::Ipv6Prefix (Ipv6Prefix const* prefix)
{
  memcpy (m_prefix, prefix->m_prefix, 16);
}

Ipv6Prefix::~Ipv6Prefix ()
{
  /* do nothing */
}

bool Ipv6Prefix::IsMatch (Ipv6Address a, Ipv6Address b) const
{
  uint8_t addrA[16];
  uint8_t addrB[16];
  unsigned int i = 0;

  a.GetBytes (addrA);
  b.GetBytes (addrB);

  /* a little bit ugly... */
  for (i = 0; i < 16; i++)
    {
      if ((addrA[i] & m_prefix[i]) != (addrB[i] & m_prefix[i]))
        {
          return false;
        }
    }
  return true;
}

void Ipv6Prefix::Print (std::ostream &os) const
{
  os << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[0]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[1] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[2]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[3] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[4]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[5] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[6]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[7] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[8]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[9] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[10]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[11] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[12]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[13] << ":"
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[14]
  << std::hex << std::setw (2) << std::setfill ('0') << (unsigned int) m_prefix[15];
}

Ipv6Prefix Ipv6Prefix::GetLoopback ()
{
  static Ipv6Prefix prefix ((uint8_t)128);
  return prefix;
}

Ipv6Prefix Ipv6Prefix::GetOnes ()
{
  static Ipv6Prefix ones ((uint8_t)128);
  return ones; 
}

Ipv6Prefix Ipv6Prefix::GetZero ()
{
  static Ipv6Prefix prefix ((uint8_t)0);
  return prefix;
}

void Ipv6Prefix::GetBytes (uint8_t buf[16]) const
{
  memcpy (buf, m_prefix, 16);
}

uint8_t Ipv6Prefix::GetPrefixLength () const
{
  uint8_t i = 0;
  uint8_t prefixLength = 0;

  for(i = 0; i < 16; i++)
    {
      uint8_t mask = m_prefix[i];

      while(mask != 0)
        {
          mask = mask << 1;
          prefixLength++;
        }
    }

  return prefixLength;
}

bool Ipv6Prefix::IsEqual (const Ipv6Prefix& other) const
{
  if (!memcmp (m_prefix, other.m_prefix, 16))
    {
      return true;
    }
  return false;
}

std::ostream& operator << (std::ostream& os, Ipv6Prefix const& prefix)
{
  prefix.Print (os);
  return os;
}

std::istream& operator >> (std::istream& is, Ipv6Prefix& prefix)
{
  std::string str;
  is >> str;
  prefix = Ipv6Prefix (str.c_str ());
  return is;
}

bool operator == (Ipv6Prefix const &a, Ipv6Prefix const &b)
{
  return a.IsEqual (b);
}

bool operator != (Ipv6Prefix const &a, Ipv6Prefix const &b)
{
  return !a.IsEqual (b);
}

size_t Ipv6AddressHash::operator () (Ipv6Address const &x) const
{
  uint8_t buf[16];

  x.GetBytes (buf);

  return lookuphash (buf, sizeof (buf), 0);
}

ATTRIBUTE_HELPER_CPP (Ipv6Address);
ATTRIBUTE_HELPER_CPP (Ipv6Prefix);

} /* namespace ns3 */

