NS3 WiFi環(huán)境中Packet從Socket到NetDevice的調(diào)用過程

從我的另一篇文章:NS3 WiFiNetDevice結(jié)構(gòu)大致可以了解到Packet從NetDevice到物理層之間的調(diào)用過程。

本章文章說一說Packet從Socket到NetDevice的發(fā)送和接收過程识啦。

前言

1继效、調(diào)用過程,以Udp協(xié)議為基礎(chǔ)敞葛,因?yàn)閁dp協(xié)議簡單些,Tcp類似。
2毒租、需要了解到NS3 Node聚合對象說明,這篇文章說明了對象的聚合關(guān)系。
3箱叁、需要了解到NS3 Socket發(fā)送Packet的過程,這篇文章說明了socket對象的創(chuàng)建過程和socket發(fā)送packet的簡單過程墅垮。

客戶端代碼

      InternetStackHelper internet;
      internet.SetIpv6StackInstall(false);
      internet.Install (c);

      Ipv4AddressHelper ipv4;
      ipv4.SetBase ("10.1.1.0", "255.255.255.0");
      Ipv4InterfaceContainer i = ipv4.Assign (devices);

      TypeId tid=UdpSocketFactory::GetTypeId();

      Ptr<Socket> recvSink = Socket::CreateSocket (c.Get (0), tid);
      InetSocketAddress local = InetSocketAddress (Ipv4Address ("10.1.1.1"), 80);
      recvSink->Bind (local);
      recvSink->SetRecvCallback (MakeCallback (&ReceivePacket));

      Ptr<Socket> source = Socket::CreateSocket (c.Get (1), tid);
      InetSocketAddress remote = InetSocketAddress (Ipv4Address ("10.1.1.2"), 80);
      source->SetAllowBroadcast (false);
      source->Bind(remote);
      source->Connect (local);

      Simulator::ScheduleWithContext (source->GetNode ()->GetId (),
                                      Seconds (startSec), &GenerateTraffic,
                                      source, PpacketSize, numPackets, interPacketInterval);

其中GenerateTraffic方法如下:

/**
 * 發(fā)送分組回調(diào)方法
 */
static void GenerateTraffic (Ptr<Socket> socket, uint32_t pktSize,
                             uint32_t pktCount, Time pktInterval )
{
  NS_LOG_FUNCTION(socket<<pktSize<<pktCount<<pktInterval);
  if (pktCount > 0)
    {
      startTime=Simulator::Now();
      NS_LOG_LOGIC("start time:"<<startTime);
      socket->Send (Create<Packet> (pktSize));
      Simulator::Schedule (pktInterval, &GenerateTraffic,
                           socket, pktSize,pktCount-1, pktInterval);
    }
  else
    {
      socket->Close ();
    }
}

Socket發(fā)送Packet到NetDevice過程

NS3 Socket發(fā)送Packet的過程,這篇文章了解到UdpSocketImpl才是真正的socket對象,實(shí)現(xiàn)socket的功能耕漱。

GenerateTraffic 方法調(diào)用UdpSocketImpl對象的Send方法發(fā)送packet對象算色。代碼如下:

int 
Socket::Send (Ptr<Packet> p)
{
  return Send (p, 0);
}

int 
UdpSocketImpl::Send (Ptr<Packet> p, uint32_t flags)
{
  if (!m_connected)
    {
      m_errno = ERROR_NOTCONN;
      return -1;
    }

  return DoSend (p);
}

int 
UdpSocketImpl::DoSend (Ptr<Packet> p)
{
  NS_LOG_FUNCTION (this << p);
  ......

  if (Ipv4Address::IsMatchingType (m_defaultAddress))
    {
      return DoSendTo (p, Ipv4Address::ConvertFrom (m_defaultAddress), m_defaultPort, GetIpTos ());
    }
  else if (Ipv6Address::IsMatchingType (m_defaultAddress))
    {
      return DoSendTo (p, Ipv6Address::ConvertFrom (m_defaultAddress), m_defaultPort);
    }

  m_errno = ERROR_AFNOSUPPORT;
  return(-1);
}


Send方法調(diào)用DoSend方法,其中DoSend方法做了一些判斷螟够,判斷socket實(shí)現(xiàn)已經(jīng)bind和connection灾梦。

因?yàn)镾ocket通信是有三次握手和結(jié)束通信時(shí)的三次揮手峡钓。如果不知道這個(gè)的,請自行補(bǔ)腦socket通信若河。上面的代碼我把這部分去掉了能岩。

UdpSocketImpl::DoSend方法又調(diào)用了DoSendTo 方法,分為兩種情況萧福,一種是IPV4拉鹃,一種是IPV6.

這里我們還是以Ipv4為例說明。

int
UdpSocketImpl::DoSendTo (Ptr<Packet> p, Ipv4Address dest, uint16_t port, uint8_t tos)
{
 .....
 ....
 ....
  /*
   * 如果dest設(shè)置為有限廣播地址(全部)鲫忍,則將其轉(zhuǎn)換為將每個(gè)接口的數(shù)據(jù)包副本作為子網(wǎng)定向廣播發(fā)送膏燕。
   * 例外:如果接口有一個(gè)/ 32地址,那么沒有有效的子網(wǎng)定向廣播饲窿,所以發(fā)送它作為有限廣播煌寇。
   * 還要注意,一些系統(tǒng)只會從“默認(rèn)”接口發(fā)送有限的廣播數(shù)據(jù)包; 這里我們發(fā)送所有接口
   */
  if (dest.IsBroadcast ())
    {
       ....
    }
  else if (m_endPoint->GetLocalAddress () != Ipv4Address::GetAny ())
    {
      m_udp->Send (p->Copy (), m_endPoint->GetLocalAddress (), dest,
                   m_endPoint->GetLocalPort (), port, 0);
      NotifyDataSent (p->GetSize ());
      NotifySend (GetTxAvailable ());
      return p->GetSize ();
    }
  else if (ipv4->GetRoutingProtocol () != 0)
    {
      .....
    }
  else
    {
      NS_LOG_ERROR ("ERROR_NOROUTETOHOST");
      m_errno = ERROR_NOROUTETOHOST;
      return -1;
    }

  return 0;
}

UdpSocketImpl::DoSendTo方法會對目的地地址做一個(gè)判斷逾雄,如果目的地是廣播做一個(gè)處理阀溶,不是廣播地址再做一個(gè)處理。

以不是廣播來處理鸦泳,就會調(diào)用

m_udp->Send (p->Copy (), m_endPoint->GetLocalAddress (), dest,
                   m_endPoint->GetLocalPort (), port, 0);

其中的m_udp對象就是UdpL4Protocol類對象银锻。

void
UdpL4Protocol::Send (Ptr<Packet> packet, 
                     Ipv4Address saddr, Ipv4Address daddr, 
                     uint16_t sport, uint16_t dport, Ptr<Ipv4Route> route)
{

  UdpHeader udpHeader;
  if(Node::ChecksumEnabled ())
    {
      udpHeader.EnableChecksums ();
      udpHeader.InitializeChecksum (saddr,
                                    daddr,
                                    PROT_NUMBER);
    }
  udpHeader.SetDestinationPort (dport);
  udpHeader.SetSourcePort (sport);

  packet->AddHeader (udpHeader);

  m_downTarget (packet, saddr, daddr, PROT_NUMBER, route);
}

UdpL4Protocol::Send 方法調(diào)用了一個(gè)m_downTarget 的回調(diào)地址。m_downTarget 的值的設(shè)置代碼如下:

void
UdpL4Protocol::NotifyNewAggregate ()
{
  NS_LOG_FUNCTION (this);
  Ptr<Node> node = this->GetObject<Node> ();
  Ptr<Ipv4> ipv4 = this->GetObject<Ipv4> ();
  Ptr<Ipv6> ipv6 = node->GetObject<Ipv6> ();

  if (m_node == 0)
    {
      if ((node != 0) && (ipv4 != 0 || ipv6 != 0))
        {
          this->SetNode (node);
          Ptr<UdpSocketFactoryImpl> udpFactory = CreateObject<UdpSocketFactoryImpl> ();
          udpFactory->SetUdp (this);
          node->AggregateObject (udpFactory);
        }
    }
  
  if (ipv4 != 0 && m_downTarget.IsNull())
    {
      ipv4->Insert (this);
      this->SetDownTarget (MakeCallback (&Ipv4::Send, ipv4));
    }
  
  IpL4Protocol::NotifyNewAggregate ();
}

UdpL4Protocol在于其他對象如Node對象 UdpSocketFactoryImpl對象聚合過程中做鹰,對m_downTarget 回調(diào)地址進(jìn)行了設(shè)置击纬,SetDownTarget 方法便是。

其中的Ipv4::Send就是Ipv4L3Protocol::Send钾麸。

void 
Ipv4L3Protocol::Send (Ptr<Packet> packet, 
                      Ipv4Address source,
                      Ipv4Address destination,
                      uint8_t protocol,
                      Ptr<Ipv4Route> route)
{
  .....
  .....
  // Handle a few cases:
  // 1) packet is destined to limited broadcast address
  // 2) packet is destined to a subnet-directed broadcast address
  // 3) packet is not broadcast, and is passed in with a route entry
  // 4) packet is not broadcast, and is passed in with a route entry but route->GetGateway is not set 
  // 5) packet is not broadcast, and route is NULL (e.g., a raw socket call, or ICMP)

  // 1) packet is destined to limited broadcast address or link-local multicast address
  // 目的地址是廣播或者本地組播
  if (destination.IsBroadcast () || destination.IsLocalMulticast ())
    {
      .......
      return;
    }

  // 2) check: packet is destined to a subnet-directed broadcast address
  // 數(shù)據(jù)包注定為子網(wǎng)定向的廣播地址
  uint32_t ifaceIndex = 0;
  for (Ipv4InterfaceList::iterator ifaceIter = m_interfaces.begin ();
       ifaceIter != m_interfaces.end (); ifaceIter++, ifaceIndex++)
    {
      ......
    }

  // 3) packet is not broadcast, and is passed in with a route entry
  //    with a valid Ipv4Address as the gateway
  if (route && route->GetGateway () != Ipv4Address ())
    {
      ......
    } 
  // 4) packet is not broadcast, and is passed in with a route entry but route->GetGateway is not set 
  if (route && route->GetGateway () == Ipv4Address ())
    {
      ..........
      NS_FATAL_ERROR ("Ipv4L3Protocol::Send case 4: This case not yet implemented");
    }
  // 5) packet is not broadcast, and route is NULL (e.g., a raw socket call)
  NS_LOG_LOGIC ("Ipv4L3Protocol::Send case 5:  passed in with no route " << destination);
  Socket::SocketErrno errno_; 
  Ptr<NetDevice> oif (0); // unused for now
  ipHeader = BuildHeader (source, destination, protocol, packet->GetSize (), ttl, tos, mayFragment);
  Ptr<Ipv4Route> newRoute;
  if (m_routingProtocol != 0)
    {
      newRoute = m_routingProtocol->RouteOutput (packet, ipHeader, oif, errno_);
    }
  else
    {
      NS_LOG_ERROR ("Ipv4L3Protocol::Send: m_routingProtocol == 0");
    }
  if (newRoute)
    {
      int32_t interface = GetInterfaceForDevice (newRoute->GetOutputDevice ());
      m_sendOutgoingTrace (ipHeader, packet, interface);
      SendRealOut (newRoute, packet->Copy (), ipHeader);
    }
  else
    {
      NS_LOG_WARN ("No route to host.  Drop.");
      m_dropTrace (ipHeader, packet, DROP_NO_ROUTE, m_node->GetObject<Ipv4> (), 0);
    }
}

Ipv4L3Protocol::Send發(fā)送packet分了五鐘情況更振,五種如果分別來說,太麻煩了饭尝。

針對客戶端代碼中的設(shè)置肯腕,經(jīng)過測試,packet會經(jīng)過第五種情況向下層傳遞钥平。
也就是會調(diào)用SendRealOut (newRoute, packet->Copy (), ipHeader);方法实撒。

void
Ipv4L3Protocol::SendRealOut (Ptr<Ipv4Route> route,
                             Ptr<Packet> packet,
                             Ipv4Header const &ipHeader)
{
  NS_LOG_FUNCTION (this << route << packet << &ipHeader);
  ......
  Ptr<NetDevice> outDev = route->GetOutputDevice ();
  int32_t interface = GetInterfaceForDevice (outDev);
  NS_ASSERT (interface >= 0);
  Ptr<Ipv4Interface> outInterface = GetInterface (interface);
  NS_LOG_LOGIC ("Send via NetDevice ifIndex " << outDev->GetIfIndex () << " ipv4InterfaceIndex " << interface);

  if (!route->GetGateway ().IsEqual (Ipv4Address ("0.0.0.0")))
    {
          .....
          .....
    }
  else 
    {
      if (outInterface->IsUp ())
        {
          NS_LOG_LOGIC ("Send to destination " << ipHeader.GetDestination ());
          if ( packet->GetSize () + ipHeader.GetSerializedSize () > outInterface->GetDevice ()->GetMtu () )
            {
              .......
            }
          else
            {
              CallTxTrace (ipHeader, packet, m_node->GetObject<Ipv4> (), interface);
              outInterface->Send (packet, ipHeader, ipHeader.GetDestination ());
            }
        }
      else
        {
          NS_LOG_LOGIC ("Dropping -- outgoing interface is down: " << ipHeader.GetDestination ());
          m_dropTrace (ipHeader, packet, DROP_INTERFACE_DOWN, m_node->GetObject<Ipv4> (), interface);
        }
    }
}

其中packet會經(jīng)過 outInterface->Send (packet, ipHeader, ipHeader.GetDestination ());方法傳遞。

outInterface對象是Ipv4Interface類對象涉瘾。

void
Ipv4Interface::Send (Ptr<Packet> p, const Ipv4Header & hdr, Ipv4Address dest)
{
  NS_LOG_FUNCTION (this << *p << dest);
  .........
  
  // 是這個(gè)數(shù)據(jù)包針對本地接口知态?
  for (Ipv4InterfaceAddressListCI i = m_ifaddrs.begin (); i != m_ifaddrs.end (); ++i)
    {
      if (dest == (*i).GetLocal ())
        {
          p->AddHeader (hdr);
          m_tc->Receive (m_device, p, Ipv4L3Protocol::PROT_NUMBER,
                         m_device->GetBroadcast (),
                         m_device->GetBroadcast (),
                         NetDevice::PACKET_HOST);
          return;
        }
    }
  if (m_device->NeedsArp ())
    {
      NS_LOG_LOGIC ("Needs ARP" << " " << dest);
      Ptr<ArpL3Protocol> arp = m_node->GetObject<ArpL3Protocol> ();
      Address hardwareDestination;
      bool found = false;
      if (dest.IsBroadcast ())
        {
         .....
        }
      else if (dest.IsMulticast ())
        {
          ......
        }
      else
        {
          for (Ipv4InterfaceAddressListCI i = m_ifaddrs.begin (); i != m_ifaddrs.end (); ++i)
            {
              if (dest.IsSubnetDirectedBroadcast ((*i).GetMask ()))
                {
                  ......
                }
            }
          if (!found)
            {
              NS_LOG_LOGIC ("ARP Lookup");
              found = arp->Lookup (p, hdr, dest, m_device, m_cache, &hardwareDestination);
            }
        }

      if (found)
        {
          NS_LOG_LOGIC ("Address Resolved.  Send.");
          m_tc->Send (m_device, Create<Ipv4QueueDiscItem> (p, hardwareDestination, 
                      Ipv4L3Protocol::PROT_NUMBER, hdr));
        }
    }
  else
    {
      NS_LOG_LOGIC ("Doesn't need ARP");
      m_tc->Send (m_device, Create<Ipv4QueueDiscItem> (p, m_device->GetBroadcast (), 
                    Ipv4L3Protocol::PROT_NUMBER, hdr));
    }
}

Ipv4Interface的Send方法會做一些判斷,廣播立叛,多播负敏、子網(wǎng)廣播、ARP等囚巴,處理不同原在。
對于packet的發(fā)送友扰,基本都是通過m_tc對象的Send方法彤叉。m_tc對象就是TrafficControlLayer類對象庶柿。

void
TrafficControlLayer::Send (Ptr<NetDevice> device, Ptr<QueueDiscItem> item)
{
  .....

  if (ndi->second.rootQueueDisc == 0)
    {
      // 設(shè)備沒有附加的隊(duì)列磁盤,因此將報(bào)頭添加到數(shù)據(jù)包秽浇,如果所選隊(duì)列未停止浮庐,
      // 則將其直接發(fā)送到設(shè)備
      if (!devQueueIface->GetTxQueue (txq)->IsStopped ())
        {
          item->AddHeader ();
          // a single queue device makes no use of the priority tag
          if (devQueueIface->GetNTxQueues () == 1)
            {
              SocketPriorityTag priorityTag;
              item->GetPacket ()->RemovePacketTag (priorityTag);
            }
          device->Send (item->GetPacket (), item->GetAddress (), item->GetProtocol ());
        }
    }
  else
    {
     ......
    }
}

TrafficControlLayer::Send方法會通過device->Send發(fā)送packet。
其中的device對象就是WiFiNetDevice對象柬焕。

由此审残,完成了packet從socket對象到netdevice的發(fā)送過程。

總結(jié)一下發(fā)送過程就是:

   Socket::Send
          |
UdpSocketImpl::Send
          |
UdpSocketImpl::DoSend
          |
UdpSocketImpl::DoSendTo
          |
UdpL4Protocol::Send
          |
Ipv4L3Protocol::Send
          |
Ipv4L3Protocol::SendRealOut
          |
Ipv4Interface::Send
          |
TrafficControlLayer::Send
          |
NetDevice->Send

NetDevice接收Packet到Socket的過程

這個(gè)過程比較復(fù)雜斑举,這個(gè)過程不像上面packet的發(fā)送過程那樣搅轿,一步一步接下來就成。

packet的接收過程用到了大量的回調(diào)富玷。比較復(fù)雜一些璧坟。

收到看WiFiNetDevice的packet接收:

void
WifiNetDevice::ForwardUp (Ptr<Packet> packet, Mac48Address from, Mac48Address to)
{
  NS_LOG_FUNCTION (this << packet << from << to);
  LlcSnapHeader llc;
  enum NetDevice::PacketType type;
  ......
  if (type != NetDevice::PACKET_OTHERHOST)
    {
      m_mac->NotifyRx (packet);
      packet->RemoveHeader (llc);
      m_forwardUp (this, packet, llc.GetType (), from);
    }
  else
    {
      packet->RemoveHeader (llc);
    }
    
  if (!m_promiscRx.IsNull ())
    {
      m_mac->NotifyPromiscRx (packet);
      m_promiscRx (this, packet, llc.GetType (), from, to, type);
    }
}

從上面的代碼中可以看出,從WifiNetDevice向上層協(xié)議傳輸?shù)倪^程赎懦,是通過兩個(gè)回調(diào)函數(shù)來完成的雀鹃。
一個(gè)是m_forwardUp ,另一個(gè)是m_promiscRx 励两。這兩個(gè)都是回調(diào)函數(shù)指針黎茎。

這兩個(gè)值的設(shè)定在哪里呢?
是在Node.cc中完成的当悔。

uint32_t
Node::AddDevice (Ptr<NetDevice> device)
{
  NS_LOG_FUNCTION (this << device);
  uint32_t index = m_devices.size ();
  m_devices.push_back (device);
  device->SetNode (this);
  device->SetIfIndex (index);
  device->SetReceiveCallback (MakeCallback (&Node::NonPromiscReceiveFromDevice, this));
  Simulator::ScheduleWithContext (GetId (), Seconds (0.0), 
                                  &NetDevice::Initialize, device);
  NotifyDeviceAdded (device);
  return index;
}

上面的代碼device->SetReceiveCallback 設(shè)置的就是m_forwardUp 回調(diào)地址值傅瞻。
對于第二個(gè)m_promiscRx 的值,默認(rèn)情況下盲憎,不會進(jìn)行設(shè)置嗅骄,但是需要明白的是,該值也是在Node類中完成設(shè)置的焙畔。

由此掸读,我們知道,WifiNetDevice::ForwardUp 直接把packet上傳到Node::NonPromiscReceiveFromDevice方法當(dāng)中執(zhí)行宏多。

bool
Node::NonPromiscReceiveFromDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                                   const Address &from)
{
  NS_LOG_FUNCTION (this << device << packet << protocol << &from);

  return ReceiveFromDevice (device, packet, protocol, from, device->GetAddress (),
                            NetDevice::PacketType (0), false);
}

bool
Node::ReceiveFromDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                 const Address &from, const Address &to, NetDevice::PacketType packetType, bool promiscuous)
{
  
  bool found = false;
  for (ProtocolHandlerList::iterator i = m_handlers.begin ();
       i != m_handlers.end (); i++)
    {
      if (i->device == 0 ||
          (i->device != 0 && i->device == device))
        {
          if (i->protocol == 0 || 
              i->protocol == protocol)
            {
              if (promiscuous == i->promiscuous)
                {
                  i->handler (device, packet, protocol, from, to, packetType);
                  found = true;
                }
            }
        }
    }
  NS_LOG_DEBUG("m_handlers found:"<<found);
  return found;
}

Node::NonPromiscReceiveFromDevice方法調(diào)用Node::ReceiveFromDevice方法儿惫,方法內(nèi)部會循環(huán)判斷是否是合適的處理packet的協(xié)議,如果有found為true伸但,則就有該協(xié)議來處理packet肾请。

m_handlers是一個(gè)列表,針對文章開始的客戶端代碼更胖,m_handlers的size是3.也就是說有三個(gè)這樣的協(xié)議能夠處理packet铛铁。

m_handlers的值的設(shè)置是通過Node::RegisterProtocolHandler方法設(shè)置的隔显。代碼如下:

void
Node::RegisterProtocolHandler (ProtocolHandler handler,
                               uint16_t protocolType,
                               Ptr<NetDevice> device,
                               bool promiscuous)
{
  NS_LOG_FUNCTION (this << &handler << protocolType << device << promiscuous);
  struct Node::ProtocolHandlerEntry entry;
  entry.handler = handler;
  entry.protocol = protocolType;
  entry.device = device;
  entry.promiscuous = promiscuous;

  // On demand enable promiscuous mode in netdevices
  if (promiscuous)
    {
      if (device == 0)
        {
          for (std::vector<Ptr<NetDevice> >::iterator i = m_devices.begin ();
               i != m_devices.end (); i++)
            {
              Ptr<NetDevice> dev = *i;
              dev->SetPromiscReceiveCallback (MakeCallback (&Node::PromiscReceiveFromDevice, this));
            }
        }
      else
        {
          device->SetPromiscReceiveCallback (MakeCallback (&Node::PromiscReceiveFromDevice, this));
        }
    }

  m_handlers.push_back (entry);
}

通過以上的代碼m_handlers中就添加了一些處理協(xié)議。然后在Node::NonPromiscReceiveFromDevice方法中調(diào)用m_handlers中注冊的處理方法饵逐。

通過全局搜索的話括眠,調(diào)用Node::RegisterProtocolHandler方法的地方有很多,針對文章開頭給出的客戶端代碼來說倍权,調(diào)用Node::RegisterProtocolHandler方法的地方有三處:

uint32_t 
Ipv4L3Protocol::AddInterface (Ptr<NetDevice> device)
{
  NS_LOG_FUNCTION (this << device);
  NS_ASSERT (m_node != 0);

  Ptr<TrafficControlLayer> tc = m_node->GetObject<TrafficControlLayer> ();

  NS_ASSERT (tc != 0);

  m_node->RegisterProtocolHandler (MakeCallback (&TrafficControlLayer::Receive, tc),
                                   Ipv4L3Protocol::PROT_NUMBER, device);
  m_node->RegisterProtocolHandler (MakeCallback (&TrafficControlLayer::Receive, tc),
                                   ArpL3Protocol::PROT_NUMBER, device);

  tc->RegisterProtocolHandler (MakeCallback (&Ipv4L3Protocol::Receive, this),
                               Ipv4L3Protocol::PROT_NUMBER, device);
  tc->RegisterProtocolHandler (MakeCallback (&ArpL3Protocol::Receive, PeekPointer (GetObject<ArpL3Protocol> ())),
                               ArpL3Protocol::PROT_NUMBER, device);

  Ptr<Ipv4Interface> interface = CreateObject<Ipv4Interface> ();
  interface->SetNode (m_node);
  interface->SetDevice (device);
  interface->SetTrafficControl (tc);
  interface->SetForwarding (m_ipForward);
  tc->SetupDevice (device);
  return AddIpv4Interface (interface);
}

-------------
void
Ipv4L3Protocol::SetupLoopback (void)
{
  NS_LOG_FUNCTION (this);

  Ptr<Ipv4Interface> interface = CreateObject<Ipv4Interface> ();
  Ptr<LoopbackNetDevice> device = 0;
  // First check whether an existing LoopbackNetDevice exists on the node
  for (uint32_t i = 0; i < m_node->GetNDevices (); i++)
    {
      if ((device = DynamicCast<LoopbackNetDevice> (m_node->GetDevice (i))))
        {
          break;
        }
    }
  if (device == 0)
    {
      device = CreateObject<LoopbackNetDevice> ();
      m_node->AddDevice (device);
    }
  interface->SetDevice (device);
  interface->SetNode (m_node);
  Ipv4InterfaceAddress ifaceAddr = Ipv4InterfaceAddress (Ipv4Address::GetLoopback (), Ipv4Mask::GetLoopback ());
  interface->AddAddress (ifaceAddr);
  uint32_t index = AddIpv4Interface (interface);
  Ptr<Node> node = GetObject<Node> ();
  node->RegisterProtocolHandler (MakeCallback (&Ipv4L3Protocol::Receive, this), 
                                 Ipv4L3Protocol::PROT_NUMBER, device);
  interface->SetUp ();
  if (m_routingProtocol != 0)
    {
      m_routingProtocol->NotifyInterfaceUp (index);
    }
}

Ipv4L3Protocol::AddInterface和Ipv4L3Protocol::SetupLoopback兩個(gè)方法中掷豺,總共三處使用node->RegisterProtocolHandler 來注冊處理方法的。

m_node->RegisterProtocolHandler (MakeCallback (&TrafficControlLayer::Receive, tc),
                                   Ipv4L3Protocol::PROT_NUMBER, device);
m_node->RegisterProtocolHandler (MakeCallback (&TrafficControlLayer::Receive, tc),
                                   ArpL3Protocol::PROT_NUMBER, device);
node->RegisterProtocolHandler (MakeCallback (&Ipv4L3Protocol::Receive, this), 
                                 Ipv4L3Protocol::PROT_NUMBER, device);

前兩種處理方式薄声,都是通過TrafficControlLayer::Receive來完成的当船,只不過協(xié)議不同。

后一種是通過Ipv4L3Protocol::Receive來完成的默辨,協(xié)議是Ipv4L3Protocol德频。不過,需要注意的是device缩幸,這里的device指的是LoopbackNetDevice壹置,這與文章開頭設(shè)置的WiFiNetDevice不同,所以這種情況不會調(diào)用桌粉,除非你設(shè)置有LoopbackNetDevice蒸绩。

void
TrafficControlLayer::Receive (Ptr<NetDevice> device, Ptr<const Packet> p,
                              uint16_t protocol, const Address &from, const Address &to,
                              NetDevice::PacketType packetType)
{
  NS_LOG_FUNCTION (this << device << p << protocol << from << to << packetType);

  bool found = false;

  for (ProtocolHandlerList::iterator i = m_handlers.begin ();
       i != m_handlers.end (); i++)
    {
      if (i->device == 0
          || (i->device != 0 && i->device == device))
        {
          if (i->protocol == 0
              || i->protocol == protocol)
            {
              NS_LOG_DEBUG ("Found handler for packet " << p << ", protocol " <<
                            protocol << " and NetDevice " << device <<
                            ". Send packet up");
              i->handler (device, p, protocol, from, to, packetType);
              found = true;
            }
        }
    }

  if (! found)
    {
      NS_FATAL_ERROR ("Handler for protocol " << p << " and device " << device <<
                      " not found. It isn't forwarded up; it dies here.");
    }
}

TrafficControlLayer::Receive中的m_handlers與上面提到的Node中的m_handlers類似,代碼如下:

void
TrafficControlLayer::RegisterProtocolHandler (Node::ProtocolHandler handler,
                                              uint16_t protocolType, Ptr<NetDevice> device)
{
  NS_LOG_FUNCTION (this << protocolType << device);

  struct ProtocolHandlerEntry entry;
  entry.handler = handler;
  entry.protocol = protocolType;
  entry.device = device;
  entry.promiscuous = false;

  m_handlers.push_back (entry);

  NS_LOG_DEBUG ("Handler for NetDevice: " << device << " registered for protocol " <<
                protocolType << ".");
}

TrafficControlLayer::RegisterProtocolHandler這個(gè)方法的調(diào)用铃肯,就是上面提到的Ipv4L3Protocol::AddInterface方法中的這兩行代碼:

tc->RegisterProtocolHandler (MakeCallback (&Ipv4L3Protocol::Receive, this),
                               Ipv4L3Protocol::PROT_NUMBER, device);
  tc->RegisterProtocolHandler (MakeCallback (&ArpL3Protocol::Receive, PeekPointer (GetObject<ArpL3Protocol> ())),
                               ArpL3Protocol::PROT_NUMBER, device);

具體執(zhí)行哪里患亿,要看使用的協(xié)議。對于packet數(shù)據(jù)包來說押逼,處理起來就是Ipv4L3Protocol::Receive步藕,如果使用到了ARP協(xié)議,處理起來就是ArpL3Protocol::Receive挑格。

這里以Ipv4L3Protocol::Receive為例說明咙冗。

void 
Ipv4L3Protocol::Receive ( Ptr<NetDevice> device, Ptr<const Packet> p, uint16_t protocol, const Address &from,
                          const Address &to, NetDevice::PacketType packetType)
{
   .....
   .....
   .....

  for (SocketList::iterator i = m_sockets.begin (); i != m_sockets.end (); ++i)
    {
      NS_LOG_LOGIC ("Forwarding to raw socket");
      Ptr<Ipv4RawSocketImpl> socket = *i;
      socket->ForwardUp (packet, ipHeader, ipv4Interface);
    }

  NS_ASSERT_MSG (m_routingProtocol != 0, "Need a routing protocol object to process packets");
  if (!m_routingProtocol->RouteInput (packet, ipHeader, device,
                                      MakeCallback (&Ipv4L3Protocol::IpForward, this),
                                      MakeCallback (&Ipv4L3Protocol::IpMulticastForward, this),
                                      MakeCallback (&Ipv4L3Protocol::LocalDeliver, this),
                                      MakeCallback (&Ipv4L3Protocol::RouteInputError, this)
                                      ))
    {
      NS_LOG_WARN ("No route found for forwarding packet.  Drop.");
      m_dropTrace (ipHeader, packet, DROP_NO_ROUTE, m_node->GetObject<Ipv4> (), interface);
    }
}

Ipv4L3Protocol::Receive代碼中有一個(gè)Ipv4RawSocketImpl。對于文章開頭給出的客戶端代碼來說漂彤,沒有使用Ipv4RawSocketImpl相關(guān)的類雾消,所以for循環(huán)中不會執(zhí)行。

接著會執(zhí)行m_routingProtocol->RouteInput 方法挫望,RouteInput 方法中有幾個(gè)回調(diào)參數(shù)立润,會執(zhí)行Ipv4L3Protocol::LocalDeliver,就會回到Ipv4L3Protocol類中媳板。

void
Ipv4L3Protocol::LocalDeliver (Ptr<const Packet> packet, Ipv4Header const&ip, uint32_t iif)
{
  .....
  .....

  Ptr<IpL4Protocol> protocol = GetProtocol (ipHeader.GetProtocol (), iif);
  if (protocol != 0)
    {
      Ptr<Packet> copy = p->Copy ();
      enum IpL4Protocol::RxStatus status = 
        protocol->Receive (p, ipHeader, GetInterface (iif));//這里調(diào)用傳輸層協(xié)議Receive方法桑腮。
      switch (status) {
        case IpL4Protocol::RX_OK:
        // fall through
        case IpL4Protocol::RX_ENDPOINT_CLOSED:
        // fall through
        case IpL4Protocol::RX_CSUM_FAILED:
          break;
        case IpL4Protocol::RX_ENDPOINT_UNREACH:
          if (ipHeader.GetDestination ().IsBroadcast () == true ||
              ipHeader.GetDestination ().IsMulticast () == true)
            {
              break; // Do not reply to broadcast or multicast
            }
          .....
        }
    }
}

上面關(guān)鍵的代碼是這幾行:

Ptr<IpL4Protocol> protocol = GetProtocol (ipHeader.GetProtocol (), iif);
Ptr<Packet> copy = p->Copy ();
enum IpL4Protocol::RxStatus status = 
        protocol->Receive (p, ipHeader, GetInterface (iif));//這里調(diào)用傳輸層協(xié)議Receive方法。

其中的protocol 就是UdpL4Protocol蛉幸。會執(zhí)行它的Receive方法破讨。

enum IpL4Protocol::RxStatus
UdpL4Protocol::Receive (Ptr<Packet> packet,
                        Ipv4Header const &header,
                        Ptr<Ipv4Interface> interface)
{
  .....
  .....
  Ipv4EndPointDemux::EndPoints endPoints =
    m_endPoints->Lookup (header.GetDestination (), udpHeader.GetDestinationPort (),
                         header.GetSource (), udpHeader.GetSourcePort (), interface);
  if (endPoints.empty ())
    {
      if (this->GetObject<Ipv6L3Protocol> () != 0)
        {
          NS_LOG_LOGIC ("  No Ipv4 endpoints matched on UdpL4Protocol, trying Ipv6 "<<this);
          .....
        }

      NS_LOG_LOGIC ("RX_ENDPOINT_UNREACH");
      return IpL4Protocol::RX_ENDPOINT_UNREACH;
    }

  packet->RemoveHeader(udpHeader);
  for (Ipv4EndPointDemux::EndPointsI endPoint = endPoints.begin ();
       endPoint != endPoints.end (); endPoint++)
    {
      (*endPoint)->ForwardUp (packet->Copy (), header, udpHeader.GetSourcePort (), 
                              interface);
    }
  return IpL4Protocol::RX_OK;
}

其中就會執(zhí)行 (*endPoint)->ForwardUp丛晦。

void 
Ipv4EndPoint::ForwardUp (Ptr<Packet> p, const Ipv4Header& header, uint16_t sport,
                         Ptr<Ipv4Interface> incomingInterface)
{
  NS_LOG_FUNCTION (this << p << &header << sport << incomingInterface);
  
  if (!m_rxCallback.IsNull ())
    {
      m_rxCallback (p, header, sport, incomingInterface);
    }
}

其中的m_rxCallback 的設(shè)置是通過來設(shè)置的:

int
UdpSocketImpl::FinishBind (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  bool done = false;
  if (m_endPoint != 0)
    {
      m_endPoint->SetRxCallback (MakeCallback (&UdpSocketImpl::ForwardUp, Ptr<UdpSocketImpl> (this)));
      m_endPoint->SetIcmpCallback (MakeCallback (&UdpSocketImpl::ForwardIcmp, Ptr<UdpSocketImpl> (this)));
      m_endPoint->SetDestroyCallback (MakeCallback (&UdpSocketImpl::Destroy, Ptr<UdpSocketImpl> (this)));
      done = true;
    }
  if (m_endPoint6 != 0)
    {
      m_endPoint6->SetRxCallback (MakeCallback (&UdpSocketImpl::ForwardUp6, Ptr<UdpSocketImpl> (this)));
      m_endPoint6->SetIcmpCallback (MakeCallback (&UdpSocketImpl::ForwardIcmp6, Ptr<UdpSocketImpl> (this)));
      m_endPoint6->SetDestroyCallback (MakeCallback (&UdpSocketImpl::Destroy6, Ptr<UdpSocketImpl> (this)));
      done = true;
    }
  if (done)
    {
      return 0;
    }
  return -1;
}

m_rxCallback 也就是調(diào)用UdpSocketImpl::ForwardUp:

void 
UdpSocketImpl::ForwardUp (Ptr<Packet> packet, Ipv4Header header, uint16_t port,
                          Ptr<Ipv4Interface> incomingInterface)
{
  ....
  ....

  SocketPriorityTag priorityTag;
  packet->RemovePacketTag (priorityTag);

  if ((m_rxAvailable + packet->GetSize ()) <= m_rcvBufSize)
    {
      Address address = InetSocketAddress (header.GetSource (), port);
      m_deliveryQueue.push (std::make_pair (packet, address));
      m_rxAvailable += packet->GetSize ();
      NotifyDataRecv ();
    }
  else
    {
      NS_LOG_WARN ("No receive buffer space available.  Drop.");
      m_dropTrace (packet);
    }
}

接著調(diào)用NotifyDataRecv ();通知數(shù)據(jù)接收。該方法有父類Socket類完成提陶。

void 
Socket::NotifyDataRecv (void)
{
  NS_LOG_FUNCTION (this);
  if (!m_receivedData.IsNull ())
    {
      m_receivedData (this);
    }
}

m_receivedData 是一個(gè)回調(diào)函數(shù)指針烫沙。客戶端代碼可以設(shè)置搁骑,例如文章開頭的代碼:

 recvSink->SetRecvCallback (MakeCallback (&ReceivePacket));

就是將ReceivePacket函數(shù)地址設(shè)置給m_receivedData 回調(diào)函數(shù)指針斧吐。

至此又固,將packet從netdevice接收到socket的過程順著捋了一遍仲器。

總結(jié)一下這個(gè)過程:

WifiNetDevice::ForwardUp
          |
Node::NonPromiscReceiveFromDevice
          |
Node::ReceiveFromDevice
          |
TrafficControlLayer::Receive 
          |
Ipv4L3Protocol::Receive
          |
Ipv4L3Protocol::LocalDeliver
          |
UdpL4Protocol::Receive
          |
Ipv4EndPoint::ForwardUp
          |
UdpSocketImpl::ForwardUp
          |
Socket::NotifyDataRecv

總結(jié)

 Socket::Send                                                                            Socket::NotifyDataRecv
          |                                                                                            |
UdpSocketImpl::Send                                                                      UdpSocketImpl::ForwardUp
          |                                                                                            |
UdpSocketImpl::DoSend                                                                  Ipv4EndPoint::ForwardUp
          |                                                                                            |
UdpSocketImpl::DoSendTo                                                                                |
          |                                                                                            |
UdpL4Protocol::Send                                                                       UdpL4Protocol::Receive
          |                                                                                            |
Ipv4L3Protocol::Send                                                                      UdpL4Protocol::Receive
          |                                                                                            |
Ipv4L3Protocol::SendRealOut                                                               Ipv4L3Protocol::Receive
          |                                                                                            |
Ipv4Interface::Send                                                                                    |
          |                                                                                            |
TrafficControlLayer::Send                                                              TrafficControlLayer::Receive
          |                                                                                            |
          |                                                                             Node::NonPromiscReceiveFromDevice
          |                                                                                            |
  NetDevice->Send            --------------------------------->                           WifiNetDevice::ForwardUp
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市仰冠,隨后出現(xiàn)的幾起案子乏冀,更是在濱河造成了極大的恐慌,老刑警劉巖洋只,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辆沦,死亡現(xiàn)場離奇詭異,居然都是意外死亡识虚,警方通過查閱死者的電腦和手機(jī)肢扯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來担锤,“玉大人蔚晨,你說我怎么就攤上這事「匮” “怎么了铭腕?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長多糠。 經(jīng)常有香客問我累舷,道長,這世上最難降的妖魔是什么夹孔? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任被盈,我火速辦了婚禮,結(jié)果婚禮上搭伤,老公的妹妹穿的比我還像新娘只怎。我一直安慰自己,他們只是感情好闷畸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布尝盼。 她就那樣靜靜地躺著,像睡著了一般佑菩。 火紅的嫁衣襯著肌膚如雪盾沫。 梳的紋絲不亂的頭發(fā)上裁赠,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音赴精,去河邊找鬼佩捞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蕾哟,可吹牛的內(nèi)容都是我干的一忱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼谭确,長吁一口氣:“原來是場噩夢啊……” “哼帘营!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逐哈,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芬迄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后昂秃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禀梳,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年肠骆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了算途。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚀腿,死狀恐怖嘴瓤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唯咬,我是刑警寧澤纱注,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胆胰,受9級特大地震影響狞贱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜀涨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一瞎嬉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厚柳,春花似錦氧枣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春烧董,著一層夾襖步出監(jiān)牢的瞬間毁靶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工逊移, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留预吆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓胳泉,卻偏偏與公主長得像拐叉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子扇商,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 這篇文章介紹一下Socket發(fā)送Packet的過程凤瘦。NS3的socket類以及子類,工廠類很多钳吟,調(diào)用過程復(fù)雜...
    shawn168閱讀 7,618評論 1 3
  • 最近在學(xué)習(xí)Python看了一篇文章寫得不錯(cuò)廷粒,是在腳本之家里的,原文如下红且,很有幫助: 一、網(wǎng)絡(luò)知識的一些介紹 soc...
    qtruip閱讀 2,717評論 0 6
  • 1三個(gè)相關(guān)數(shù)據(jù)結(jié)構(gòu). 關(guān)于socket的創(chuàng)建涤姊,首先需要分析socket這個(gè)結(jié)構(gòu)體暇番,這是整個(gè)的核心。 104 str...
    ice_camel閱讀 2,829評論 1 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理思喊,服務(wù)發(fā)現(xiàn)壁酬,斷路器,智...
    卡卡羅2017閱讀 134,661評論 18 139
  • 最近恨课,侯哥(大學(xué)舍友)的到訪著實(shí)讓我開心許多舆乔。作為一個(gè)朋友,我們互相都是稱職的剂公,你很難想象大家坦言歡笑希俩,毫無半點(diǎn)社...
    一百次回憶閱讀 225評論 0 2