text: //
  // Copyright (C) 2005 Andras Varga
  //
  // This program is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Lesser General Public License
  // as published by the Free Software Foundation; either version 2
  // of the License, or (at your option) any later version.
  //
  // 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 Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public License
  // along with this program; if not, see <http://www.gnu.org/licenses/>.
  //
  
  #include "inet/networklayer/ipv6/IPv6Datagram.h"
  #include "inet/networklayer/ipv6/IPv6ExtensionHeaders.h"
  
  namespace inet {
  
  Register_Class(IPv6Datagram);
  
  std::ostream& operator<<(std::ostream& os, IPv6ExtensionHeaderPtr eh)
  {
      return os << "(" << eh->getClassName() << ") " << eh->str();
  }
  
  IPv6Datagram& IPv6Datagram::operator=(const IPv6Datagram& other)
  {
      if (this == &other)
          return *this;
      clean();
      IPv6Datagram_Base::operator=(other);
      copy(other);
      return *this;
  }
  
  void IPv6Datagram::copy(const IPv6Datagram& other)
  {
      for (const auto & elem : other.extensionHeaders)
          addExtensionHeader((elem)->dup());
  }
  
  void IPv6Datagram::setExtensionHeaderArraySize(unsigned int size)
  {
      throw cRuntimeError(this, "setExtensionHeaderArraySize() not supported, use addExtensionHeader()");
  }
  
  unsigned int IPv6Datagram::getExtensionHeaderArraySize() const
  {
      return extensionHeaders.size();
  }
  
  IPv6ExtensionHeaderPtr& IPv6Datagram::getExtensionHeader(unsigned int k)
  {
      static IPv6ExtensionHeaderPtr null;
      if (k >= extensionHeaders.size())
          return null = nullptr;
      return extensionHeaders[k];
  }
  
  IPv6ExtensionHeader *IPv6Datagram::findExtensionHeaderByType(IPProtocolId extensionType, int index) const
  {
      for (const auto & elem : extensionHeaders)
          if ((elem)->getExtensionType() == extensionType) {
              if (index == 0)
                  return elem;
              else
                  index--;
          }
      return nullptr;
  }
  
  void IPv6Datagram::setExtensionHeader(unsigned int k, const IPv6ExtensionHeaderPtr& extensionHeader_var)
  {
      throw cRuntimeError(this, "setExtensionHeader() not supported, use addExtensionHeader()");
  }
  
  void IPv6Datagram::addExtensionHeader(IPv6ExtensionHeader *eh, int atPos)
  {
      if (atPos != -1)
          throw cRuntimeError(this, "addExtensionHeader() does not support atPos parameter.");
      ASSERT((eh->getByteLength() >= 1) && (eh->getByteLength() % 8 == 0));
      int thisOrder = getExtensionHeaderOrder(eh);
      unsigned int i;
      for (i = 0; i < extensionHeaders.size(); i++) {
          int thatOrder = getExtensionHeaderOrder(extensionHeaders[i]);
          if (thisOrder != -1 && thatOrder > thisOrder)
              break;
          else if (thisOrder == thatOrder) {
              if (thisOrder == 1)
                  thisOrder = 6;
              else if (thisOrder != -1)
                  throw cRuntimeError(this, "addExtensionHeader() duplicate extension header: %d",
                          eh->getExtensionType());
          }
      }
  
      // insert at position atPos, shift up the rest of the array
      extensionHeaders.insert(extensionHeaders.begin() + i, eh);
  }
  
  /*
   *  Defines the order of extension headers according to RFC 2460 4.1.
   *  Note that Destination Options header may come both after the Hop-by-Hop
   *  extension and before the payload (if Routing presents).
   */
  int IPv6Datagram::getExtensionHeaderOrder(IPv6ExtensionHeader *eh)
  {
      switch (eh->getExtensionType()) {
          case IP_PROT_IPv6EXT_HOP:
              return 0;
  
          case IP_PROT_IPv6EXT_DEST:
              return 1;
  
          case IP_PROT_IPv6EXT_ROUTING:
              return 2;
  
          case IP_PROT_IPv6EXT_FRAGMENT:
              return 3;
  
          case IP_PROT_IPv6EXT_AUTH:
              return 4;
  
          case IP_PROT_IPv6EXT_ESP:
              return 5;
  
          // second IP_PROT_IPv6EXT_DEST has order 6
          case IP_PROT_IPv6EXT_MOB:
              return 7;
  
          default:
              return -1;
      }
  }
  
  int IPv6Datagram::calculateHeaderByteLength() const
  {
      int len = 40;
      for (auto & elem : extensionHeaders)
          len += elem->getByteLength();
      return len;
  }
  
  /**
   * Note: it is assumed that headers are ordered as described in RFC 2460 4.1
   */
  int IPv6Datagram::calculateUnfragmentableHeaderByteLength() const
  {
      int lastUnfragmentableExtensionIndex = -1;
      for (int i = ((int)extensionHeaders.size()) - 1; i >= 0; i--) {
          int type = extensionHeaders[i]->getExtensionType();
          if (type == IP_PROT_IPv6EXT_ROUTING || type == IP_PROT_IPv6EXT_HOP) {
              lastUnfragmentableExtensionIndex = i;
              break;
          }
      }
  
      int len = 40;
      for (int i = 0; i <= lastUnfragmentableExtensionIndex; i++)
          len += extensionHeaders[i]->getByteLength();
      return len;
  }
  
  /**
   * Note: it is assumed that headers are ordered as described in RFC 2460 4.1
   */
  int IPv6Datagram::calculateFragmentLength() const
  {
      int len = getByteLength() - IPv6_HEADER_BYTES;
      unsigned int i;
      for (i = 0; i < extensionHeaders.size(); i++) {
          len -= extensionHeaders[i]->getByteLength();
          if (extensionHeaders[i]->getExtensionType() == IP_PROT_IPv6EXT_FRAGMENT)
              break;
      }
      ASSERT2(i < extensionHeaders.size(), "IPv6Datagram::calculateFragmentLength() called on non-fragment datagram");
      return len;
  }
  
  IPv6ExtensionHeader *IPv6Datagram::removeFirstExtensionHeader()
  {
      if (extensionHeaders.empty())
          return nullptr;
      IPv6ExtensionHeader *eh = extensionHeaders.front();
      extensionHeaders.erase(extensionHeaders.begin());
      return eh;
  }
  
  IPv6ExtensionHeader *IPv6Datagram::removeExtensionHeader(IPProtocolId extensionType)
  {
      for (unsigned int i = 0; i < extensionHeaders.size(); i++) {
          if (extensionHeaders[i]->getExtensionType() == extensionType) {
              IPv6ExtensionHeader *eh = extensionHeaders[i];
              extensionHeaders.erase(extensionHeaders.begin() + i);
              return eh;
          }
      }
      return nullptr;
  }
  
  IPv6Datagram::~IPv6Datagram()
  {
      clean();
  }
  
  void IPv6Datagram::clean()
  {
      IPv6ExtensionHeaderPtr eh;
  
      while (!extensionHeaders.empty()) {
          eh = extensionHeaders.back();
          extensionHeaders.pop_back();    // remove pointer element from container
          delete eh;    // delete the header
      }
  }
  
  std::ostream& operator<<(std::ostream& out, const IPv6ExtensionHeader& h)
  {
      out << "{type:" << h.getExtensionType() << ",length:" << h.getByteLength() << "}";
      return out;
  }
  
  } // namespace inet
  
  