void Icmpv6L4Protocol::HandleNS()

in simulation/src/internet/model/icmpv6-l4-protocol.cc [488:613]


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;
    }

  NdiscCache::Entry* entry = 0;
  Ptr<NdiscCache> cache = FindCache (interface->GetDevice ());
  uint8_t flags = 0;

  /* search all options following the NS header */
  Icmpv6OptionLinkLayerAddress sllaoHdr (true);

  bool next = true;
  bool hasSllao = false;

  while (next == true)
    {
      uint8_t type;
      packet->CopyData (&type, sizeof (type));

      switch (type)
        {
          case Icmpv6Header::ICMPV6_OPT_LINK_LAYER_SOURCE:
            if (!hasSllao)
              {
                packet->RemoveHeader (sllaoHdr);
                hasSllao = true;
              }
            break;
          default:
            /* unknow option, quit */
            next = false;
        }
      if (packet->GetSize () == 0)
        {
          next = false;
        }
    }

  Address replyMacAddress;

  if (src != Ipv6Address::GetAny ())
    {
      entry = cache->Lookup (src);
      if (!entry)
        {
          if (!hasSllao)
            {
              NS_LOG_LOGIC ("Icmpv6L4Protocol::HandleNS: NS without SLLAO and we do not have a NCE, discarding.");
              return;
            }
          entry = cache->Add (src);
          entry->SetRouter (false);
          entry->MarkStale (sllaoHdr.GetAddress ());
          replyMacAddress = sllaoHdr.GetAddress ();
        }
      else if (hasSllao && (entry->GetMacAddress () != sllaoHdr.GetAddress ()))
        {
          entry->MarkStale (sllaoHdr.GetAddress ());
          replyMacAddress = sllaoHdr.GetAddress ();
        }
      else
        {
          replyMacAddress = entry->GetMacAddress ();
        }

      flags = 3; /* S + O flags */
    }
  else
    {
      /* it's a DAD */
      flags = 1; /* O flag */
      replyMacAddress = interface->GetDevice ()->GetMulticast (dst);
    }

  /* send a NA to src */
  Ptr<Ipv6L3Protocol> ipv6 = m_node->GetObject<Ipv6L3Protocol> ();

  if (ipv6->IsForwarding (ipv6->GetInterfaceForDevice (interface->GetDevice ())))
    {
      flags += 4; /* R flag */
    }

  Address hardwareAddress = interface->GetDevice ()->GetAddress ();
  NdiscCache::Ipv6PayloadHeaderPair p = ForgeNA (target.IsLinkLocal () ? interface->GetLinkLocalAddress ().GetAddress () : ifaddr.GetAddress (),
                                                 src.IsAny () ? dst : src, // DAD replies must go to the multicast group it was sent to.
                                                 &hardwareAddress,
                                                 flags );

  // We must bypass the IPv6 layer, as a NA must be sent regardless of the NCE status (and not change it beyond what we did already).
  Ptr<Packet> pkt = p.first;
  pkt->AddHeader (p.second);
  interface->GetDevice ()->Send (pkt, replyMacAddress, Ipv6L3Protocol::PROT_NUMBER);
}