src/internet-stack/icmpv6-l4-protocol.cc
changeset 4731 510db8599bfb
child 4761 8c0b3a413f4b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/internet-stack/icmpv6-l4-protocol.cc	Sat Aug 22 14:36:55 2009 -0700
@@ -0,0 +1,1205 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2007-2009 Strasbourg 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>
+ *         David Gross <gdavid.devel@gmail.com>
+ *         Mehdi Benamor <benamor.mehdi@ensi.rnu.tn>
+ */
+
+#include "ns3/log.h"
+#include "ns3/assert.h"
+#include "ns3/packet.h"
+#include "ns3/node.h"
+#include "ns3/ipv6-routing-protocol.h"
+#include "ns3/ipv6-route.h"
+
+#include "ipv6-raw-socket-factory-impl.h"
+#include "icmpv6-l4-protocol.h"
+#include "icmpv6-header.h"
+#include "ipv6-l3-protocol.h"
+#include "ipv6-end-point.h"
+
+#include "ns3/ipv6-static-routing-helper.h"
+
+namespace ns3
+{
+
+NS_OBJECT_ENSURE_REGISTERED (Icmpv6L4Protocol);
+
+NS_LOG_COMPONENT_DEFINE ("Icmpv6L4Protocol");
+
+const uint8_t Icmpv6L4Protocol::PROT_NUMBER = 58;
+
+const uint8_t Icmpv6L4Protocol::MAX_INITIAL_RTR_ADVERT_INTERVAL = 16;
+const uint8_t Icmpv6L4Protocol::MAX_INITIAL_RTR_ADVERTISEMENTS = 3;
+const uint8_t Icmpv6L4Protocol::MAX_FINAL_RTR_ADVERTISEMENTS = 3;
+const uint8_t Icmpv6L4Protocol::MIN_DELAY_BETWEEN_RAS = 3;
+const uint32_t Icmpv6L4Protocol::MAX_RA_DELAY_TIME = 500; /* millisecond */
+
+const uint8_t Icmpv6L4Protocol::MAX_RTR_SOLICITATION_DELAY = 1;
+const uint8_t Icmpv6L4Protocol::RTR_SOLICITATION_INTERVAL = 4;
+const uint8_t Icmpv6L4Protocol::MAX_RTR_SOLICITATIONS = 3;
+
+const uint8_t Icmpv6L4Protocol::MAX_MULTICAST_SOLICIT = 3;
+const uint8_t Icmpv6L4Protocol::MAX_UNICAST_SOLICIT = 3;
+const uint8_t Icmpv6L4Protocol::MAX_ANYCAST_DELAY_TIME = 1;
+const uint8_t Icmpv6L4Protocol::MAX_NEIGHBOR_ADVERTISEMENT = 3;
+const uint32_t Icmpv6L4Protocol::REACHABLE_TIME = 30000;
+const uint32_t Icmpv6L4Protocol::RETRANS_TIMER = 1000;
+const uint8_t Icmpv6L4Protocol::DELAY_FIRST_PROBE_TIME = 5;
+const double Icmpv6L4Protocol::MIN_RANDOM_FACTOR = 0.5;
+const double Icmpv6L4Protocol::MAX_RANDOM_FACTOR = 1.5;
+
+TypeId Icmpv6L4Protocol::GetTypeId ()
+{
+  static TypeId tid = TypeId ("ns3::Icmpv6L4Protocol")
+    .SetParent<Ipv6L4Protocol> ()
+    .AddConstructor<Icmpv6L4Protocol> ()
+    ;
+  return tid;
+}
+
+Icmpv6L4Protocol::Icmpv6L4Protocol ()
+  : m_node (0)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+Icmpv6L4Protocol::~Icmpv6L4Protocol ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+void Icmpv6L4Protocol::DoDispose ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  for (CacheList::const_iterator it = m_cacheList.begin () ; it != m_cacheList.end () ; it++)
+  {
+    Ptr<NdiscCache> cache = *it;
+    cache->Dispose ();
+    cache = 0;
+  }
+  m_cacheList.clear ();
+
+  m_node = 0;
+  Ipv6L4Protocol::DoDispose ();
+}
+
+void Icmpv6L4Protocol::NotifyNewAggregate ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  if (m_node == 0)
+  {
+    Ptr<Node> node = this->GetObject<Node> ();
+    if (node != 0)
+    {
+      Ptr<Ipv6L3Protocol> ipv6 = this->GetObject<Ipv6L3Protocol> ();
+      if (ipv6 != 0)
+      {
+        this->SetNode (node);
+        ipv6->Insert (this);
+        Ptr<Ipv6RawSocketFactoryImpl> rawFactory = CreateObject<Ipv6RawSocketFactoryImpl> ();
+        ipv6->AggregateObject (rawFactory);
+      }
+    }
+  }
+  Object::NotifyNewAggregate ();
+}
+
+void Icmpv6L4Protocol::SetNode (Ptr<Node> node)
+{
+  NS_LOG_FUNCTION (this << node);
+  m_node = node;
+}  
+
+uint16_t Icmpv6L4Protocol::GetStaticProtocolNumber ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return PROT_NUMBER;
+}
+
+int Icmpv6L4Protocol::GetProtocolNumber () const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return PROT_NUMBER;
+}
+
+int Icmpv6L4Protocol::GetVersion () const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return 1;
+}
+
+void Icmpv6L4Protocol::DoDAD (Ipv6Address target, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << target << interface);
+  Ipv6Address addr;
+
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  NS_ASSERT (ipv6);
+
+  /* TODO : disable multicast loopback to prevent NS probing to be received by the sender */
+
+  Ptr<Packet> p = ForgeNS ("::" ,Ipv6Address::MakeSolicitedAddress (target), target, interface->GetDevice ()->GetAddress ());
+
+  /* update last packet UID */
+  interface->SetNsDadUid (target, p->GetUid ());
+  interface->Send (p, Ipv6Address::MakeSolicitedAddress (target));
+}
+
+enum Ipv6L4Protocol::RxStatus_e Icmpv6L4Protocol::Receive (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Ptr<Packet> p = packet->Copy ();
+  Ptr<Ipv6> ipv6 = m_node->GetObject<Ipv6> ();
+
+  switch (*p->PeekData ()) /* very ugly! try to find something better in the future */
+  {
+    case Icmpv6Header::ICMPV6_ND_ROUTER_SOLICITATION:
+      if (ipv6->IsForwarding (ipv6->GetInterfaceForDevice (interface->GetDevice ())))
+      {
+        HandleRS (p, src, dst, interface);
+      }
+      break;
+    case Icmpv6Header::ICMPV6_ND_ROUTER_ADVERTISEMENT:
+      if (!ipv6->IsForwarding (ipv6->GetInterfaceForDevice (interface->GetDevice ())))
+      {
+        HandleRA (p, src, dst, interface);
+      }
+      break;
+    case Icmpv6Header::ICMPV6_ND_NEIGHBOR_SOLICITATION:
+      HandleNS (p, src, dst, interface);
+      break;
+    case Icmpv6Header::ICMPV6_ND_NEIGHBOR_ADVERTISEMENT:
+      HandleNA (p, src, dst, interface);
+      break;
+    case Icmpv6Header::ICMPV6_ND_REDIRECTION:
+      HandleRedirection (p, src, dst, interface);
+      break;
+    case Icmpv6Header::ICMPV6_ECHO_REQUEST:
+      HandleEchoRequest (p, src, dst, interface);
+      break;
+    case Icmpv6Header::ICMPV6_ECHO_REPLY:
+      break;
+    case Icmpv6Header::ICMPV6_ERROR_DESTINATION_UNREACHABLE:
+      break;
+    case Icmpv6Header::ICMPV6_ERROR_PACKET_TOO_BIG:
+      break;
+    case Icmpv6Header::ICMPV6_ERROR_TIME_EXCEEDED:
+      break;
+    case Icmpv6Header::ICMPV6_ERROR_PARAMETER_ERROR:
+      break;
+    default:
+      NS_LOG_LOGIC ("Unknown ICMPv6 message type=" << (uint8_t)*p->PeekData ());
+      break;
+  }
+
+  return Ipv6L4Protocol::RX_OK;
+}
+
+void Icmpv6L4Protocol::HandleEchoRequest (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Icmpv6Echo request;
+  packet->RemoveHeader (request);
+
+  /* if we send message from ff02::* (link-local multicast), we use our link-local address */
+  SendEchoReply (dst.IsMulticast () ? interface->GetLinkLocalAddress ().GetAddress () : dst, src, request.GetId (), request.GetSeq (), packet);
+}
+
+void Icmpv6L4Protocol::HandleRA (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{ 
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Ptr<Packet> p = packet->Copy ();
+  Icmpv6RA raHeader;
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  Icmpv6OptionPrefixInformation prefixHdr;
+  Icmpv6OptionMtu mtuHdr;
+  Icmpv6OptionLinkLayerAddress llaHdr;
+  uint8_t type = 0;
+  bool next = true;
+  bool hasLla = false;
+  bool hasMtu = false;
+
+  p->RemoveHeader (raHeader);
+
+  while (next == true)
+  {
+    type = *p->PeekData ();
+
+    switch (type)
+    {
+      case Icmpv6Header::ICMPV6_OPT_PREFIX:
+        p->RemoveHeader (prefixHdr);
+        ipv6->AddAutoconfiguredAddress (ipv6->GetInterfaceForDevice (interface->GetDevice ()), prefixHdr.GetPrefix (), prefixHdr.GetPrefixLength (), 
+                                        prefixHdr.GetFlags (), prefixHdr.GetValidTime (), prefixHdr.GetPreferredTime (), src);
+        break;
+      case Icmpv6Header::ICMPV6_OPT_MTU:
+        /* take in account the first MTU option */
+        if (!hasMtu)
+        {
+          p->RemoveHeader (mtuHdr);
+          hasMtu = true;
+          /* XXX case of multiple prefix on single interface */
+          /* interface->GetDevice ()->SetMtu (m.GetMtu ()); */
+        }
+        break;
+      case Icmpv6Header::ICMPV6_OPT_LINK_LAYER_SOURCE:
+        /* take in account the first LLA option */
+        if (!hasLla)
+        {
+          p->RemoveHeader (llaHdr);                   
+          ReceiveLLA (llaHdr, src, dst, interface);
+          hasLla = true;
+        }
+        break;
+      default:
+        /* unknow option, quit */
+        next = false;
+    }
+  }
+}
+
+void Icmpv6L4Protocol::ReceiveLLA (Icmpv6OptionLinkLayerAddress lla, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << lla << src << dst << interface);
+  Address hardwareAddress;
+  NdiscCache::Entry* entry = NULL;
+  Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
+  
+  /* check if we have this address in our cache */ 
+  entry = cache->Lookup (src);
+
+  if (!entry)
+  {
+    entry = cache->Add (src);
+    entry->SetRouter (true);
+    entry->SetMacAddress (lla.GetAddress ());
+    entry->MarkReachable ();  
+    entry->StartReachableTimer ();        
+  }                    
+  else
+  {
+    std::list<Ptr<Packet> > waiting;
+    if (entry->IsIncomplete ())
+    {
+      entry->StopRetransmitTimer ();
+      // mark it to reachable 
+      waiting = entry->MarkReachable (lla.GetAddress ());
+      entry->StopReachableTimer ();
+      entry->StartReachableTimer ();
+      // send out waiting packet 
+      for (std::list<Ptr<Packet> >::const_iterator it = waiting.begin (); it != waiting.end (); it++)
+      {
+        cache->GetInterface ()->Send (*it, src);
+      }
+      entry->ClearWaitingPacket ();
+    }
+    else
+    {
+      if (entry->GetMacAddress ()!=lla.GetAddress ())
+      {
+        entry->SetMacAddress (lla.GetAddress ());
+        entry->MarkStale ();
+        entry->SetRouter (true);
+      }  
+      else
+      {
+        if (!entry->IsReachable ())
+        {
+          entry->StopProbeTimer ();
+          entry->StopDelayTimer ();
+          waiting = entry->MarkReachable (lla.GetAddress ());
+          if (entry->IsProbe ())
+          {
+            for (std::list<Ptr<Packet> >::const_iterator it = waiting.begin (); it != waiting.end (); it++)
+            {
+              cache->GetInterface ()->Send (*it, src);
+            }
+          }
+          entry->StopReachableTimer ();
+          entry->StartReachableTimer ();
+        }    
+      }
+    }
+  }
+}
+
+void Icmpv6L4Protocol::HandleRS (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  Icmpv6RS rsHeader;
+  packet->RemoveHeader (rsHeader);
+  Address hardwareAddress;
+  Icmpv6OptionLinkLayerAddress lla (1);
+  NdiscCache::Entry* entry = NULL;
+  Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
+
+  if (src != Ipv6Address::GetAny ())
+  {
+    /* XXX search all options following the RS header */
+    /* test if the next option is SourceLinkLayerAddress */
+    if (*packet->PeekData () != Icmpv6Header::ICMPV6_OPT_LINK_LAYER_SOURCE)
+    {
+      return;
+    }
+    packet->RemoveHeader (lla);
+    NS_LOG_LOGIC ("Cache updated by RS");
+
+    entry = cache->Lookup (src);
+    if (!entry)
+    {
+      entry = cache->Add (src);
+      entry->SetRouter (false);
+      entry->MarkStale (lla.GetAddress ());
+    }
+    else if (entry->GetMacAddress () != lla.GetAddress ())
+    {
+      entry->MarkStale (lla.GetAddress ());
+    }
+  }
+}
+
+void Icmpv6L4Protocol::HandleNS (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Icmpv6NS nsHeader ("::");
+  Ipv6InterfaceAddress ifaddr;
+  uint32_t nb = interface->GetNAddresses ();
+  uint32_t i = 0;
+  bool found = false;
+
+  packet->RemoveHeader (nsHeader);
+
+  Ipv6Address target = nsHeader.GetIpv6Target ();
+
+  for (i = 0 ; i < nb ; i++)
+  {
+    ifaddr = interface->GetAddress (i);
+
+    if (ifaddr.GetAddress () == target)
+    {
+      found = true;
+      break;
+    }
+  }
+
+  if (!found)
+  {
+    NS_LOG_LOGIC ("Not a NS for us");
+    return;
+  }
+
+  if (packet->GetUid () == ifaddr.GetNsDadUid ())
+  {
+    /* don't process our own DAD probe */
+    NS_LOG_LOGIC ("Hey we receive our DAD probe!");
+    return;
+  }
+
+  Icmpv6OptionLinkLayerAddress lla (1);
+  Address hardwareAddress;
+  NdiscCache::Entry* entry = 0;
+  Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
+  uint8_t flags = 0;
+
+  /* XXX search all options following the NS header */
+
+  if (src != Ipv6Address::GetAny ())
+  {
+    if (*packet->PeekData () != Icmpv6Header::ICMPV6_OPT_LINK_LAYER_SOURCE)
+    {
+      return;
+    }
+
+    /* Get LLA */
+    packet->RemoveHeader (lla);
+
+    entry = cache->Lookup (src);
+    if (!entry)
+    {
+      entry = cache->Add (src);
+      entry->SetRouter (false);
+      entry->MarkStale (lla.GetAddress ());
+    }
+    else if (entry->GetMacAddress () != lla.GetAddress ())
+    {
+      entry->MarkStale (lla.GetAddress ());
+    }
+
+    flags = 3; /* S + O flags */
+  }
+  else
+  {
+    /* it means someone do a DAD */
+    flags = 1; /* O flag */
+  }
+
+  /* send a NA to src */
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+
+  if (ipv6->IsForwarding (ipv6->GetInterfaceForDevice (interface->GetDevice ())))
+  {
+    flags += 4; /* R flag */
+  }
+
+  hardwareAddress = interface->GetDevice ()->GetAddress ();
+  Ptr<Packet> p = ForgeNA (target.IsLinkLocal () ? interface->GetLinkLocalAddress ().GetAddress () : ifaddr.GetAddress (), src.IsAny () ? Ipv6Address::GetAllNodesMulticast () : src, &hardwareAddress, flags );
+  interface->Send (p,  src.IsAny () ? Ipv6Address::GetAllNodesMulticast () : src); 
+
+  /* not a NS for us discard it */
+}
+
+Ptr<Packet> Icmpv6L4Protocol::ForgeRS (Ipv6Address src, Ipv6Address dst, Address hardwareAddress)
+{
+  NS_LOG_FUNCTION (this << src << dst << hardwareAddress);
+  Ptr<Packet> p = Create<Packet> ();
+  Ipv6Header ipHeader;
+  Icmpv6RS rs;
+  Icmpv6OptionLinkLayerAddress llOption (1, hardwareAddress);  /* we give our mac address in response */
+
+  NS_LOG_LOGIC ("Send RS ( from " << src << " to " << dst << ")");
+  p->AddHeader (llOption);
+
+  rs.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + rs.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (rs);
+
+  ipHeader.SetSourceAddress (src);
+  ipHeader.SetDestinationAddress (dst);
+  ipHeader.SetNextHeader (PROT_NUMBER);
+  ipHeader.SetPayloadLength (p->GetSize ());
+  ipHeader.SetHopLimit (255);
+
+  p->AddHeader (ipHeader);
+
+  return p;
+}
+
+Ptr<Packet> Icmpv6L4Protocol::ForgeEchoRequest (Ipv6Address src, Ipv6Address dst, uint16_t id, uint16_t seq, Ptr<Packet> data)
+{
+  NS_LOG_FUNCTION (this << src << dst << id << seq << data);
+  Ptr<Packet> p = data->Copy ();
+  Ipv6Header ipHeader;
+  Icmpv6Echo req (1);
+
+  req.SetId (id);
+  req.SetSeq (seq);
+
+  p->AddHeader (req);
+
+  return p;
+}
+
+void Icmpv6L4Protocol::HandleNA (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  Icmpv6NA naHeader;
+  Icmpv6OptionLinkLayerAddress lla (1);
+
+  packet->RemoveHeader (naHeader);
+  Ipv6Address target = naHeader.GetIpv6Target ();
+
+  Address hardwareAddress;
+  NdiscCache::Entry* entry = 0;
+  Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
+  std::list<Ptr<Packet> > waiting;
+
+  /* check if we have something in our cache */
+  entry = cache->Lookup (target);
+
+  if (!entry)
+  {
+    /* ouch!! we are victim of a DAD */
+    Ipv6InterfaceAddress ifaddr;
+    bool found = false;
+    uint32_t i = 0;
+    uint32_t nb = 0;
+
+    for (i = 0 ; i < nb ; i++)
+    {
+      if (ifaddr.GetAddress () == target)
+      {
+        found = true;
+        break;
+      }
+    }
+
+    if (found)
+    {
+      if (ifaddr.GetState () == Ipv6InterfaceAddress::TENTATIVE || ifaddr.GetState () == Ipv6InterfaceAddress::TENTATIVE_OPTIMISTIC)
+      {
+        interface->SetState (ifaddr.GetAddress (), Ipv6InterfaceAddress::INVALID);
+      }
+    }
+    /* we have not initiated any communication with the target so... discard the NA */
+    return;
+  }
+
+  /* XXX search all options following the NA header */
+  /* Get LLA */
+  if (*packet->PeekData () != Icmpv6Header::ICMPV6_OPT_LINK_LAYER_TARGET)
+  {
+    return;
+  }
+  packet->RemoveHeader (lla);
+
+  if (entry->IsIncomplete ())
+  {
+    /* we receive a NA so stop the retransmission timer */
+    entry->StopRetransmitTimer ();
+
+    if (naHeader.GetFlagS ())
+    {
+      /* mark it to reachable */
+      waiting = entry->MarkReachable (lla.GetAddress ());
+      entry->StopReachableTimer ();
+      entry->StartReachableTimer ();
+      /* send out waiting packet */
+      for (std::list<Ptr<Packet> >::const_iterator it = waiting.begin (); it != waiting.end (); it++)
+      {
+        cache->GetInterface ()->Send (*it, src);
+      }
+      entry->ClearWaitingPacket ();
+    }
+    else
+    {
+      entry->MarkStale (lla.GetAddress ());
+    }
+
+    if (naHeader.GetFlagR ())
+    {
+      entry->SetRouter (true);
+    }
+  }
+  else
+  {
+    /* we receive a NA so stop the probe timer or delay timer if any */
+    entry->StopProbeTimer ();
+    entry->StopDelayTimer ();
+
+    /* if the Flag O is clear and mac address differs from the cache */
+    if (!naHeader.GetFlagO () && lla.GetAddress ()!=entry->GetMacAddress ())
+    {
+      if (entry->IsReachable ())
+      {
+        entry->MarkStale ();
+      }
+      return;
+    }
+    else
+    {
+      if ((!naHeader.GetFlagO () && lla.GetAddress () == entry->GetMacAddress ()) || naHeader.GetFlagO ()) /* XXX lake "no target link-layer address option supplied" */
+      {
+        entry->SetMacAddress (lla.GetAddress ());
+
+        if (naHeader.GetFlagS ())
+        {
+          if (!entry->IsReachable ())
+          {
+            if (entry->IsProbe ())
+            {
+              waiting = entry->MarkReachable (lla.GetAddress ());
+              for (std::list<Ptr<Packet> >::const_iterator it = waiting.begin (); it != waiting.end (); it++)
+              {
+                cache->GetInterface ()->Send (*it, src); 
+              }
+              entry->ClearWaitingPacket ();
+            }
+            else
+            {
+              entry->MarkReachable (lla.GetAddress ());
+            }
+          }
+          entry->StopReachableTimer ();
+          entry->StartReachableTimer ();
+        }
+        else if (lla.GetAddress ()!=entry->GetMacAddress ())
+        {
+          entry->MarkStale ();
+        }
+        entry->SetRouter (naHeader.GetFlagR ());
+      }
+    }
+  }
+}
+
+void Icmpv6L4Protocol::HandleRedirection (Ptr<Packet> packet, Ipv6Address const &src, Ipv6Address const &dst, Ptr<Ipv6Interface> interface)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << interface);
+  bool hasLla = false;
+  Ptr<Packet> p = packet->Copy ();
+  Icmpv6OptionLinkLayerAddress llOptionHeader (0);
+
+  Icmpv6Redirection redirectionHeader;
+  p->RemoveHeader (redirectionHeader);
+
+  /* little ugly try to find a better way */
+  if (*p->PeekData () == Icmpv6Header::ICMPV6_OPT_LINK_LAYER_TARGET)
+  {
+    hasLla = true;
+    p->RemoveHeader (llOptionHeader);
+  }
+
+  Icmpv6OptionRedirected redirectedOptionHeader;
+  p->RemoveHeader (redirectedOptionHeader);
+
+  Ipv6Address redirTarget = redirectionHeader.GetTarget ();
+  Ipv6Address redirDestination = redirectionHeader.GetDestination ();
+
+  if (hasLla)
+  {
+    /* update the cache if needed */
+    NdiscCache::Entry* entry = NULL;
+    Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
+
+    entry = cache->Lookup (redirTarget);
+    if (!entry)
+    {
+      entry = cache->Add (redirTarget);
+      /* destination and target different => necessarily a router */
+      entry->SetRouter (!redirTarget.IsEqual (redirDestination) ? true : false);
+      entry->SetMacAddress (llOptionHeader.GetAddress ());
+      entry->MarkStale ();
+    }
+    else
+    {
+      if (entry->IsIncomplete () || entry->GetMacAddress () != llOptionHeader.GetAddress ())
+      {
+        /* update entry to STALE */
+        if (entry->GetMacAddress ()!=llOptionHeader.GetAddress ())
+        {
+          entry->SetMacAddress (llOptionHeader.GetAddress ());
+          entry->MarkStale ();
+        }
+      }
+      else
+      {
+        /* stay unchanged */
+      }
+    }
+  }
+
+  /* add redirection in routing table */
+  Ptr<Ipv6> ipv6 = m_node->GetObject<Ipv6> ();
+
+  if (redirTarget.IsEqual (redirDestination))
+  {
+    ipv6->GetRoutingProtocol ()->NotifyAddRoute (redirDestination, Ipv6Prefix (128), Ipv6Address ("::"), ipv6->GetInterfaceForAddress (dst));
+  }
+  else
+  {
+    uint32_t ifIndex = ipv6->GetInterfaceForAddress (dst);
+    ipv6->GetRoutingProtocol ()->NotifyAddRoute (redirDestination, Ipv6Prefix (128), redirTarget, ifIndex);
+  }
+}
+
+void Icmpv6L4Protocol::SendMessage (Ptr<Packet> packet, Ipv6Address src, Ipv6Address dst, uint8_t ttl)
+{
+  NS_LOG_FUNCTION (this << packet << src << dst << (uint32_t)ttl);
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  SocketIpTtlTag tag;
+  NS_ASSERT (ipv6 != 0);
+
+  tag.SetTtl (ttl);
+  packet->AddPacketTag (tag);
+  ipv6->Send (packet, src, dst, PROT_NUMBER, 0);
+}
+
+void Icmpv6L4Protocol::SendMessage (Ptr<Packet> packet, Ipv6Address dst, Icmpv6Header& icmpv6Hdr, uint8_t ttl)
+{
+  NS_LOG_FUNCTION (this << packet << dst << icmpv6Hdr << (uint32_t)ttl);
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  NS_ASSERT (ipv6 != 0 && ipv6->GetRoutingProtocol () != 0);
+  Ipv6Header header;
+  SocketIpTtlTag tag;
+  Socket::SocketErrno err;
+  Ptr<Ipv6Route> route;
+  uint32_t oif = 0; //specify non-zero if bound to a source address
+  
+  header.SetDestinationAddress (dst);
+  route = ipv6->GetRoutingProtocol ()->RouteOutput (packet, header, oif, err);
+ 
+  if (route != 0)
+  {
+    NS_LOG_LOGIC ("Route exists");
+    tag.SetTtl (ttl);
+    packet->AddPacketTag (tag);
+    Ipv6Address src = route->GetSource ();
+
+    icmpv6Hdr.CalculatePseudoHeaderChecksum (src, dst, packet->GetSize () + icmpv6Hdr.GetSerializedSize (), PROT_NUMBER);
+    packet->AddHeader (icmpv6Hdr);
+    ipv6->Send (packet, src, dst, PROT_NUMBER, route);
+  }
+  else
+  {
+    NS_LOG_WARN ("drop icmp message");
+  }
+}
+
+void Icmpv6L4Protocol::SendNA (Ipv6Address src, Ipv6Address dst, Address* hardwareAddress, uint8_t flags)
+{
+  NS_LOG_FUNCTION (this << src << dst << hardwareAddress << flags);
+  Ptr<Packet> p = Create<Packet> ();
+  Icmpv6NA na;
+  Icmpv6OptionLinkLayerAddress llOption (0, *hardwareAddress); /* not a source link layer */
+
+  NS_LOG_LOGIC ("Send NA ( from " << src << " to " << dst << " target " << src << ")");
+  na.SetIpv6Target (src);
+
+  if ((flags & 1))
+  {
+    na.SetFlagO (true);
+  }
+  if ((flags & 2) && src != Ipv6Address::GetAny ())
+  {
+    na.SetFlagS (true);
+  }
+  if ((flags & 4))
+  {
+    na.SetFlagR (true);
+  }
+
+  p->AddHeader (llOption);
+  na.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + na.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (na);
+
+  SendMessage (p, src, dst, 255);
+}
+
+void Icmpv6L4Protocol::SendEchoReply (Ipv6Address src, Ipv6Address dst, uint16_t id, uint16_t seq, Ptr<Packet> data)
+{
+  NS_LOG_FUNCTION (this << src << dst << id << seq << data);
+  Ptr<Packet> p = data->Copy ();
+  Icmpv6Echo reply (0); /* echo reply */
+
+  reply.SetId (id);
+  reply.SetSeq (seq);
+
+  reply.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + reply.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (reply);
+  SendMessage (p, src, dst, 64);
+}
+
+void Icmpv6L4Protocol::SendNS (Ipv6Address src, Ipv6Address dst, Ipv6Address target, Address hardwareAddress)
+{
+  NS_LOG_FUNCTION (this << src << dst << target << hardwareAddress);
+  Ptr<Packet> p = Create<Packet> ();
+  /* Ipv6Header ipHeader; */
+  Icmpv6NS ns (target);
+  Icmpv6OptionLinkLayerAddress llOption (1, hardwareAddress);  /* we give our mac address in response */
+
+  /* if the source is unspec, multicast the NA to all-nodes multicast */
+  if (src == Ipv6Address::GetAny ())
+  {
+    dst = Ipv6Address::GetAllNodesMulticast ();
+  }
+
+  NS_LOG_LOGIC ("Send NS ( from " << src << " to " << dst << " target " << target <<")");
+
+  p->AddHeader (llOption);
+  ns.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + ns.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (ns);
+  SendMessage (p, src, dst, 255);
+}
+
+void Icmpv6L4Protocol::SendRS (Ipv6Address src, Ipv6Address dst,  Address hardwareAddress)
+{
+  NS_LOG_FUNCTION (this << src << dst << hardwareAddress);
+  Ptr<Packet> p = Create<Packet> ();
+  Icmpv6RS rs;
+  Icmpv6OptionLinkLayerAddress llOption (1, hardwareAddress);  /* we give our mac address in response */
+
+  /* if the source is unspec, multicast the NA to all-nodes multicast */
+  if (src != Ipv6Address::GetAny ())
+  {
+    p->AddHeader (llOption);
+  }
+
+  NS_LOG_LOGIC ("Send RS ( from " << src << " to " << dst << ")");
+
+  rs.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + rs.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (rs);
+  SendMessage (p, src, dst, 255);
+}
+
+void Icmpv6L4Protocol::SendErrorDestinationUnreachable (Ptr<Packet> malformedPacket, Ipv6Address dst, uint8_t code)
+{
+  NS_LOG_FUNCTION (this << malformedPacket << dst << (uint32_t)code);
+  Ptr<Packet> p = Create<Packet> ();
+  uint32_t malformedPacketSize = malformedPacket->GetSize ();
+  Icmpv6DestinationUnreachable header;
+
+  NS_LOG_LOGIC ("Send Destination Unreachable ( to " << dst << " code " << (uint32_t)code << " )");
+
+  /* 48 = sizeof IPv6 header + sizeof ICMPv6 error header */
+  if (malformedPacketSize <= 1280 - 48)
+  {
+    header.SetPacket (malformedPacket);
+  }
+  else
+  {
+    Ptr<Packet> fragment = malformedPacket->CreateFragment (0, 1280 - 48);
+    header.SetPacket (fragment);
+  }
+
+  header.SetCode (code);
+  SendMessage (p, dst, header, 255);
+}
+
+void Icmpv6L4Protocol::SendErrorTooBig (Ptr<Packet> malformedPacket, Ipv6Address dst, uint32_t mtu)
+{
+  NS_LOG_FUNCTION (this << malformedPacket << dst << mtu);
+  Ptr<Packet> p = Create<Packet> ();
+  uint32_t malformedPacketSize = malformedPacket->GetSize ();
+  Icmpv6TooBig header;
+
+  NS_LOG_LOGIC ("Send Too Big ( to " << dst << " )");
+
+  /* 48 = sizeof IPv6 header + sizeof ICMPv6 error header */
+  if (malformedPacketSize <= 1280 - 48)  
+  {
+    header.SetPacket (malformedPacket);
+  }
+  else
+  {
+    Ptr<Packet> fragment = malformedPacket->CreateFragment (0, 1280 - 48);
+    header.SetPacket (fragment);
+  }
+
+  header.SetCode (0);
+  header.SetMtu (mtu);
+  SendMessage (p, dst, header, 255);
+}
+
+void Icmpv6L4Protocol::SendErrorTimeExceeded (Ptr<Packet> malformedPacket, Ipv6Address dst, uint8_t code)
+{
+  NS_LOG_FUNCTION (this<< malformedPacket << dst << code);
+  Ptr<Packet> p = Create<Packet> ();
+  uint32_t malformedPacketSize = malformedPacket->GetSize ();
+  Icmpv6TimeExceeded header;
+
+  NS_LOG_LOGIC ("Send Time Exceeded ( to " << dst << " code " << (uint32_t)code << " )");
+
+  /* 48 = sizeof IPv6 header + sizeof ICMPv6 error header */
+  if (malformedPacketSize <= 1280 - 48) 
+  {
+    header.SetPacket (malformedPacket);
+  }
+  else
+  {
+    Ptr<Packet> fragment = malformedPacket->CreateFragment (0, 1280 - 48);
+    header.SetPacket (fragment);
+  }
+
+  header.SetCode (code);
+  SendMessage (p, dst, header, 255);
+}
+
+void Icmpv6L4Protocol::SendErrorParameterError (Ptr<Packet> malformedPacket, Ipv6Address dst, uint8_t code, uint32_t ptr)
+{
+  NS_LOG_FUNCTION (this << malformedPacket << dst << code << ptr);
+  Ptr<Packet> p = Create<Packet> ();
+  uint32_t malformedPacketSize = malformedPacket->GetSize ();
+  Icmpv6ParameterError header;
+
+  NS_LOG_LOGIC ("Send Parameter Error ( to " << dst << " code " << (uint32_t)code << " )");
+
+  /* 48 = sizeof IPv6 header + sizeof ICMPv6 error header */
+  if (malformedPacketSize <= 1280 -48 )
+  {
+    header.SetPacket (malformedPacket);
+  }
+  else
+  {
+    Ptr<Packet> fragment = malformedPacket->CreateFragment (0, 1280 - 48);
+    header.SetPacket (fragment);
+  }
+
+  header.SetCode (code);
+  header.SetPtr (ptr);
+  SendMessage (p, dst, header, 255);
+}
+
+void Icmpv6L4Protocol::SendRedirection (Ptr<Packet> redirectedPacket, Ipv6Address dst, Ipv6Address redirTarget, Ipv6Address redirDestination, Address redirHardwareTarget)
+{
+  NS_LOG_FUNCTION (this << redirectedPacket << dst << redirTarget << redirDestination << redirHardwareTarget);
+  uint32_t llaSize = 0;
+  Ptr<Packet> p = Create<Packet> ();
+  uint32_t redirectedPacketSize = redirectedPacket->GetSize ();
+  Icmpv6OptionLinkLayerAddress llOption (0);
+
+  NS_LOG_LOGIC ("Send Redirection ( to " << dst << " target " << redirTarget << " destination " << redirDestination << " )");
+
+  Icmpv6OptionRedirected redirectedOptionHeader;
+
+  if ((redirectedPacketSize % 8) != 0)
+  {
+    redirectedPacket->AddPaddingAtEnd (8 - (redirectedPacketSize % 8));
+  }
+
+  if (redirHardwareTarget.GetLength ())
+  {
+    llOption.SetAddress (redirHardwareTarget);
+    llaSize = llOption.GetSerializedSize ();
+  }
+
+  /* 56 = sizeof IPv6 header + sizeof ICMPv6 error header + sizeof redirected option */
+  if (redirectedPacketSize <= (1280 - 56 - llaSize))
+  {
+    redirectedOptionHeader.SetPacket (redirectedPacket);
+  }
+  else
+  {
+    Ptr<Packet> fragment = redirectedPacket->CreateFragment (0, 1280 - 56 - llaSize);
+    redirectedOptionHeader.SetPacket (fragment);
+  }
+
+  p->AddHeader (redirectedOptionHeader);
+
+  if (llaSize)
+  {
+    p->AddHeader (llOption);
+  }
+
+  Icmpv6Redirection redirectionHeader;
+  redirectionHeader.SetTarget (redirTarget);
+  redirectionHeader.SetDestination (redirDestination);
+  SendMessage (p, dst, redirectionHeader, 64);
+}
+
+Ptr<Packet> Icmpv6L4Protocol::ForgeNA (Ipv6Address src, Ipv6Address dst, Address* hardwareAddress, uint8_t flags)
+{
+  NS_LOG_FUNCTION (this << src << dst << hardwareAddress << (uint32_t)flags);
+  Ptr<Packet> p = Create<Packet> ();
+  Ipv6Header ipHeader;
+  Icmpv6NA na;
+  Icmpv6OptionLinkLayerAddress llOption (0, *hardwareAddress);  /* we give our mac address in response */
+
+  NS_LOG_LOGIC ("Send NA ( from " << src << " to " << dst << ")");
+
+  /* forge the entire NA packet from IPv6 header to ICMPv6 link-layer option, so that the packet does not pass by Icmpv6L4Protocol::Lookup again */
+
+  p->AddHeader (llOption);
+  na.SetIpv6Target (src);
+
+  if ((flags & 1))
+  {
+    na.SetFlagO (true);
+  }
+  if ((flags & 2) && src != Ipv6Address::GetAny ())
+  {
+    na.SetFlagS (true);
+  }
+  if ((flags & 4))
+  {
+    na.SetFlagR (true);
+  }
+
+  na.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + na.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (na);
+
+  ipHeader.SetSourceAddress (src);
+  ipHeader.SetDestinationAddress (dst);
+  ipHeader.SetNextHeader (PROT_NUMBER);
+  ipHeader.SetPayloadLength (p->GetSize ());
+  ipHeader.SetHopLimit (255);
+  
+  p->AddHeader (ipHeader);
+
+  return p;
+}
+
+Ptr<Packet> Icmpv6L4Protocol::ForgeNS (Ipv6Address src, Ipv6Address dst, Ipv6Address target, Address hardwareAddress)
+{
+  NS_LOG_FUNCTION (this << src << dst << target << hardwareAddress);
+  Ptr<Packet> p = Create<Packet> ();
+  Ipv6Header ipHeader;
+  Icmpv6NS ns (target);
+  Icmpv6OptionLinkLayerAddress llOption (1, hardwareAddress);  /* we give our mac address in response */
+
+  /* if the source is unspec, multicast the NA to all-nodes multicast */
+  if (src == Ipv6Address::GetAny ())
+  {
+    dst = Ipv6Address::GetAllNodesMulticast ();
+  }
+
+  NS_LOG_LOGIC ("Send NS ( from " << src << " to " << dst << " target " << target <<")");
+
+  p->AddHeader (llOption);
+  ns.CalculatePseudoHeaderChecksum (src, dst, p->GetSize () + ns.GetSerializedSize (), PROT_NUMBER);
+  p->AddHeader (ns);
+
+  ipHeader.SetSourceAddress (src);
+  ipHeader.SetDestinationAddress (dst);
+  ipHeader.SetNextHeader (PROT_NUMBER);
+  ipHeader.SetPayloadLength (p->GetSize ());
+  ipHeader.SetHopLimit (255);
+
+  p->AddHeader (ipHeader);
+
+  return p;
+}
+
+Ptr<NdiscCache> Icmpv6L4Protocol::FindCache (Ptr<NetDevice> device)
+{
+  NS_LOG_FUNCTION (this << device);
+
+  for (CacheList::const_iterator i = m_cacheList.begin () ; i != m_cacheList.end () ; i++)
+  {
+    if ((*i)->GetDevice () == device)
+    {
+      return *i;
+    }
+  }
+
+  NS_ASSERT (false);
+  /* quiet compiler */
+  return 0;
+}
+
+Ptr<NdiscCache> Icmpv6L4Protocol::CreateCache (Ptr<NetDevice> device, Ptr<Ipv6Interface> interface)
+{
+  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();
+  Ptr<NdiscCache> cache = CreateObject<NdiscCache> ();
+  cache->SetDevice (device, interface);
+
+  /* XXX : make a list of callback in net-device.cc
+   * else we override IPv4 flushing ARP table...
+   */
+/* device->SetLinkChangeCallback (MakeCallback (&NdiscCache::Flush, cache)); */
+  m_cacheList.push_back (cache);
+  return cache;
+}
+
+bool Icmpv6L4Protocol::Lookup (Ptr<Packet> p, Ipv6Address dst, Ptr<NetDevice> device, Ptr<NdiscCache> cache, Address* hardwareDestination)
+{
+  NS_LOG_FUNCTION (this << p << dst << device << hardwareDestination);
+
+  if (!cache)
+  {
+    /* try to find the cache */
+    cache = FindCache (device);
+  }
+
+  NdiscCache::Entry* entry = cache->Lookup (dst);
+  if (entry)
+  {
+    if (entry->IsReachable () || entry->IsDelay ())
+    {
+      /* XXX check reachability time */
+      /* send packet */
+      *hardwareDestination = entry->GetMacAddress ();
+      return true;
+    }
+    else if (entry->IsStale ())
+    {
+      /* *hardwareDestination = entry->GetMacAddress (); */
+      /* start delay timer */
+      entry->StartDelayTimer ();
+      entry->MarkDelay ();
+      *hardwareDestination = entry->GetMacAddress ();
+      return true;
+    }
+    else /* PROBE */
+    {
+      /* queue packet */
+      entry->AddWaitingPacket (p);
+      return false;
+    }
+  }
+  else
+  {
+    /* we contact this node for the first time
+     * add it to the cache and send an NS
+     */
+    Ipv6Address addr;
+    NdiscCache::Entry* entry = cache->Add (dst);
+    entry->MarkIncomplete (p);
+    entry->SetRouter (false);
+
+    if (dst.IsLinkLocal ())
+    {
+      addr = cache->GetInterface ()->GetLinkLocalAddress ().GetAddress ();
+    }
+    else if (cache->GetInterface ()->GetNAddresses () == 1) /* an interface have at least one address (link-local) */
+    {
+      /* try to resolve global address without having global address so return! */
+      cache->Remove (entry);
+      return false;
+    }
+    else
+    {
+      /* find source address that match destination */
+      addr = cache->GetInterface ()->GetAddressMatchingDestination (dst).GetAddress (); 
+    }
+
+    SendNS (addr, Ipv6Address::MakeSolicitedAddress (dst), dst, cache->GetDevice ()->GetAddress ());
+
+    /* start retransmit timer */
+    entry->StartRetransmitTimer ();
+    return false;
+  }
+
+  return false;
+}
+
+void Icmpv6L4Protocol::FunctionDadTimeout (Ptr<Icmpv6L4Protocol> icmpv6, Ipv6Interface* interface, Ipv6Address addr)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  NS_LOG_LOGIC (interface << " " << addr);
+  Ipv6InterfaceAddress ifaddr;
+  bool found = false;
+  uint32_t i = 0;
+  uint32_t nb = interface->GetNAddresses ();
+  
+  for (i = 0 ; i < nb ; i++)
+  {
+    ifaddr = interface->GetAddress (i);
+
+    if (ifaddr.GetAddress () == addr)
+    {
+      found = true;
+      break;
+    }
+  }
+
+  /* for the moment, this function is always called, if we was victim of a DAD the address is INVALID 
+   * and we do not set it to PREFERRED
+   */
+  if (found && ifaddr.GetState () != Ipv6InterfaceAddress::INVALID)
+  {
+    interface->SetState (ifaddr.GetAddress (), Ipv6InterfaceAddress::PREFERRED);
+    NS_LOG_LOGIC ("DAD OK, interface in state PREFERRED");
+
+    /* send an RS if our interface is not forwarding (router) and if address is a link-local ones
+     * (because we will send RS with it)
+     */
+    Ptr<Ipv6> ipv6 = icmpv6->m_node->GetObject<Ipv6> ();
+
+    if (!ipv6->IsForwarding (ipv6->GetInterfaceForDevice (interface->GetDevice ())) && addr.IsLinkLocal ()) 
+    {
+      /* XXX because all nodes start at the same time, there will be many of RS arround 1 second of simulation time 
+       * TODO Add random delays before sending RS
+       */
+      Simulator::Schedule (Seconds (0.0), &Icmpv6L4Protocol::SendRS, PeekPointer (icmpv6), ifaddr.GetAddress (), Ipv6Address::GetAllRoutersMulticast (), interface->GetDevice ()->GetAddress ());
+    }
+  }
+}
+
+} /* namespace ns3 */
+