.. include:: replace.txt .. highlight:: cpp Internet Stack -------------- Internet stack aggregation ************************** A bare class :cpp:class:`Node` is not very useful as-is; other objects must be aggregated to it to provide useful node functionality. The |ns3| source code directory ``src/internet`` provides implementation of TCP/IPv4- and IPv6-related components. These include IPv4, ARP, UDP, TCP, IPv6, Neighbor Discovery, and other related protocols. Internet Nodes are not subclasses of class Node; they are simply Nodes that have had a bunch of IP-related objects aggregated to them. They can be put together by hand, or via a helper function :cpp:func:`InternetStackHelper::Install ()` which does the following to all nodes passed in as arguments:: void InternetStackHelper::Install (Ptr node) const { if (m_ipv4Enabled) { /* IPv4 stack */ if (node->GetObject () != 0) { NS_FATAL_ERROR ("InternetStackHelper::Install (): Aggregating " "an InternetStack to a node with an existing Ipv4 object"); return; } CreateAndAggregateObjectFromTypeId (node, "ns3::ArpL3Protocol"); CreateAndAggregateObjectFromTypeId (node, "ns3::Ipv4L3Protocol"); CreateAndAggregateObjectFromTypeId (node, "ns3::Icmpv4L4Protocol"); // Set routing Ptr ipv4 = node->GetObject (); Ptr ipv4Routing = m_routing->Create (node); ipv4->SetRoutingProtocol (ipv4Routing); } if (m_ipv6Enabled) { /* IPv6 stack */ if (node->GetObject () != 0) { NS_FATAL_ERROR ("InternetStackHelper::Install (): Aggregating " "an InternetStack to a node with an existing Ipv6 object"); return; } CreateAndAggregateObjectFromTypeId (node, "ns3::Ipv6L3Protocol"); CreateAndAggregateObjectFromTypeId (node, "ns3::Icmpv6L4Protocol"); // Set routing Ptr ipv6 = node->GetObject (); Ptr ipv6Routing = m_routingv6->Create (node); ipv6->SetRoutingProtocol (ipv6Routing); /* register IPv6 extensions and options */ ipv6->RegisterExtensions (); ipv6->RegisterOptions (); } if (m_ipv4Enabled || m_ipv6Enabled) { /* UDP and TCP stacks */ CreateAndAggregateObjectFromTypeId (node, "ns3::UdpL4Protocol"); node->AggregateObject (m_tcpFactory.Create ()); Ptr factory = CreateObject (); node->AggregateObject (factory); } } Where multiple implementations exist in |ns3| (TCP, IP routing), these objects are added by a factory object (TCP) or by a routing helper (m_routing). Note that the routing protocol is configured and set outside this function. By default, the following protocols are added:: void InternetStackHelper::Initialize () { SetTcp ("ns3::TcpL4Protocol"); Ipv4StaticRoutingHelper staticRouting; Ipv4GlobalRoutingHelper globalRouting; Ipv4ListRoutingHelper listRouting; Ipv6ListRoutingHelper listRoutingv6; Ipv6StaticRoutingHelper staticRoutingv6; listRouting.Add (staticRouting, 0); listRouting.Add (globalRouting, -10); listRoutingv6.Add (staticRoutingv6, 0); SetRoutingHelper (listRouting); SetRoutingHelper (listRoutingv6); } By default, IPv4 and IPv6 are enabled. Internet Node structure +++++++++++++++++++++++ An IP-capable Node (an |ns3| Node augmented by aggregation to have one or more IP stacks) has the following internal structure. Layer-3 protocols ~~~~~~~~~~~~~~~~~ At the lowest layer, sitting above the NetDevices, are the "layer 3" protocols, including IPv4, IPv6, ARP and so on. The class :cpp:class:`Ipv4L3Protocol` is an implementation class whose public interface is typically class :cpp:class:`Ipv4`, but the Ipv4L3Protocol public API is also used internally at present. In class Ipv4L3Protocol, one method described below is ``Receive ()``:: /** * Lower layer calls this method after calling L3Demux::Lookup * The ARP subclass needs to know from which NetDevice this * packet is coming to: * - implement a per-NetDevice ARP cache * - send back arp replies on the right device */ void Receive( Ptr device, Ptr p, uint16_t protocol, const Address &from, const Address &to, NetDevice::PacketType packetType); First, note that the ``Receive ()`` function has a matching signature to the ReceiveCallback in the class :cpp:class:`Node`. This function pointer is inserted into the Node's protocol handler when ``AddInterface ()`` is called. The actual registration is done with a statement such as follows:: RegisterProtocolHandler ( MakeCallback (&Ipv4Protocol::Receive, ipv4), Ipv4L3Protocol::PROT_NUMBER, 0); The Ipv4L3Protocol object is aggregated to the Node; there is only one such Ipv4L3Protocol object. Higher-layer protocols that have a packet to send down to the Ipv4L3Protocol object can call ``GetObject ()`` to obtain a pointer, as follows:: Ptr ipv4 = m_node->GetObject (); if (ipv4 != 0) { ipv4->Send (packet, saddr, daddr, PROT_NUMBER); } This class nicely demonstrates two techniques we exploit in |ns3| to bind objects together: callbacks, and object aggregation. Once IPv4 routing has determined that a packet is for the local node, it forwards it up the stack. This is done with the following function:: void Ipv4L3Protocol::LocalDeliver (Ptr packet, Ipv4Header const&ip, uint32_t iif) The first step is to find the right Ipv4L4Protocol object, based on IP protocol number. For instance, TCP is registered in the demux as protocol number 6. Finally, the ``Receive()`` function on the Ipv4L4Protocol (such as ``TcpL4Protocol::Receive`` is called. We have not yet introduced the class Ipv4Interface. Basically, each NetDevice is paired with an IPv4 representation of such device. In Linux, this class :cpp:class:`Ipv4Interface` roughly corresponds to the ``struct in_device``; the main purpose is to provide address-family specific information (addresses) about an interface. All the classes have appropriate traces in order to track sent, received and lost packets. The users is encouraged to use them so to find out if (and where) a packet is dropped. A common mistake is to forget the effects of local queues when sending packets, e.g., the ARP queue. This can be particularly puzzling when sending jumbo packets or packet bursts using UDP. The ARP cache pending queue is limited (3 datagrams) and IP packets might be fragmented, easily overfilling the ARP cache queue size. In those cases it is useful to increase the ARP cache pending size to a proper value, e.g.:: Config::SetDefault ("ns3::ArpCache::PendingQueueSize", UintegerValue (MAX_BURST_SIZE/L2MTU*3)); The IPv6 implementation follows a similar architecture. Dual-stacked nodes (one with support for both IPv4 and IPv6) will allow an IPv6 socket to receive IPv4 connections as a standard dual-stacked system does. A socket bound and listening to an IPv6 endpoint can receive an IPv4 connection and will return the remote address as an IPv4-mapped address. Support for the IPV6_V6ONLY socket option does not currently exist. Layer-4 protocols and sockets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We next describe how the transport protocols, sockets, and applications tie together. In summary, each transport protocol implementation is a socket factory. An application that needs a new socket For instance, to create a UDP socket, an application would use a code snippet such as the following:: Ptr udpSocketFactory = GetNode ()->GetObject (); Ptr m_socket = socketFactory->CreateSocket (); m_socket->Bind (m_local_address); ... The above will query the node to get a pointer to its UDP socket factory, will create one such socket, and will use the socket with an API similar to the C-based sockets API, such as ``Connect ()`` and ``Send ()``. The address passed to the ``Bind ()``, ``Connect ()``, or ``Send ()`` functions may be a :cpp:class:`Ipv4Address`, :cpp:class:`Ipv6Address`, or :cpp:class:`Address`. If a :cpp:class:`Address` is passed in and contains anything other than a :cpp:class:`Ipv4Address` or :cpp:class:`Ipv6Address`, these functions will return an error. The ``Bind (void)`` and ``Bind6 (void)`` functions bind to "0.0.0.0" and "::" respectively. See the chapter on |ns3| sockets for more information. We have described so far a socket factory (e.g. ``class Udp``) and a socket, which may be specialized (e.g., class :cpp:class:`UdpSocket`). There are a few more key objects that relate to the specialized task of demultiplexing a packet to one or more receiving sockets. The key object in this task is class :cpp:class:`Ipv4EndPointDemux`. This demultiplexer stores objects of class :cpp:class:`Ipv4EndPoint`. This class holds the addressing/port tuple (local port, local address, destination port, destination address) associated with the socket, and a receive callback. This receive callback has a receive function registered by the socket. The ``Lookup ()`` function to Ipv4EndPointDemux returns a list of Ipv4EndPoint objects (there may be a list since more than one socket may match the packet). The layer-4 protocol copies the packet to each Ipv4EndPoint and calls its ``ForwardUp ()`` method, which then calls the ``Receive ()`` function registered by the socket. An issue that arises when working with the sockets API on real systems is the need to manage the reading from a socket, using some type of I/O (e.g., blocking, non-blocking, asynchronous, ...). |ns3| implements an asynchronous model for socket I/O; the application sets a callback to be notified of received data ready to be read, and the callback is invoked by the transport protocol when data is available. This callback is specified as follows:: void Socket::SetRecvCallback (Callback, Ptr, const Address&> receivedData); The data being received is conveyed in the Packet data buffer. An example usage is in class :cpp:class:`PacketSink`:: m_socket->SetRecvCallback (MakeCallback(&PacketSink::HandleRead, this)); To summarize, internally, the UDP implementation is organized as follows: * a ``UdpImpl`` class that implements the UDP socket factory functionality * a ``UdpL4Protocol`` class that implements the protocol logic that is socket-independent * a ``UdpSocketImpl`` class that implements socket-specific aspects of UDP * a class called ``Ipv4EndPoint`` that stores the addressing tuple (local port, local address, destination port, destination address) associated with the socket, and a receive callback for the socket. IP-capable node interfaces ++++++++++++++++++++++++++ Many of the implementation details, or internal objects themselves, of IP-capable Node objects are not exposed at the simulator public API. This allows for different implementations; for instance, replacing the native |ns3| models with ported TCP/IP stack code. The C++ public APIs of all of these objects is found in the ``src/network`` directory, including principally: * ``address.h`` * ``socket.h`` * ``node.h`` * ``packet.h`` These are typically base class objects that implement the default values used in the implementation, implement access methods to get/set state variables, host attributes, and implement publicly-available methods exposed to clients such as ``CreateSocket``. Example path of a packet ++++++++++++++++++++++++ These two figures show an example stack trace of how packets flow through the Internet Node objects. .. _internet-node-send: .. figure:: figures/internet-node-send.* Send path of a packet. .. _internet-node-recv: .. figure:: figures/internet-node-recv.* Receive path of a packet.