232 lines
9.4 KiB
ReStructuredText
232 lines
9.4 KiB
ReStructuredText
.. include:: replace.txt
|
|
|
|
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-stack`` 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 IPv4-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> node) const
|
|
{
|
|
if (node->GetObject<Ipv4> () != 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");
|
|
CreateAndAggregateObjectFromTypeId (node, "ns3::UdpL4Protocol");
|
|
node->AggregateObject (m_tcpFactory.Create<Object> ());
|
|
Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory> ();
|
|
node->AggregateObject (factory);
|
|
// Set routing
|
|
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
|
|
Ptr<Ipv4RoutingProtocol> ipv4Routing = m_routing->Create (node);
|
|
ipv4->SetRoutingProtocol (ipv4Routing);
|
|
}
|
|
|
|
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 to Ipv4:::
|
|
|
|
InternetStackHelper::InternetStackHelper ()
|
|
{
|
|
SetTcp ("ns3::TcpL4Protocol");
|
|
static Ipv4StaticRoutingHelper staticRouting;
|
|
static Ipv4GlobalRoutingHelper globalRouting;
|
|
static Ipv4ListRoutingHelper listRouting;
|
|
listRouting.Add (staticRouting, 0);
|
|
listRouting.Add (globalRouting, -10);
|
|
SetRoutingHelper (listRouting);
|
|
}
|
|
|
|
By default, IPv4 and IPv6 are enabled.
|
|
|
|
Internet Node structure
|
|
+++++++++++++++++++++++
|
|
|
|
An IPv4-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 (in the future), and ARP. The class
|
|
:cpp:class:`Ipv4L3Protocol` is an implementation class whose public interface is
|
|
typically class :cpp:class:`Ipv4` (found in src/node directory), but the
|
|
Ipv4L3Protocol public API is also used internally in the src/internet-stack
|
|
directory 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<NetDevice> device, Ptr<const Packet> 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<Ipv4L3Protocol> ()`` to obtain a
|
|
pointer, as follows:::
|
|
|
|
Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol> ();
|
|
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<const Packet> 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.
|
|
|
|
The IPv6 implementation follows a similar architecture.
|
|
|
|
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<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
|
|
Ptr<Socket> 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 ()``. 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<void, Ptr<Socket>,
|
|
Ptr<Packet>, 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.
|
|
|
|
Ipv4-capable node interfaces
|
|
++++++++++++++++++++++++++++
|
|
|
|
Many of the implementation details, or internal objects themselves, of
|
|
Ipv4-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/node``
|
|
directory, including principally:
|
|
|
|
* ``socket.h``
|
|
* ``tcp.h``
|
|
* ``udp.h``
|
|
* ``ipv4.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.
|