Files
unison/doc/manual/node.texi
2008-12-27 13:58:12 -08:00

359 lines
14 KiB
Plaintext

@node Node and Internet Stack
@chapter Node and Internet Stack
@anchor{chap:Node}
This chapter describes how ns-3 nodes are put together, and provides
a walk-through of how packets traverse an internet-based Node.
@float Figure,fig:node
@caption{High-level node architecture.}
@image{figures/node,5in}
@end float
In ns-3, nodes are instances of @code{class Node}. This class
may be subclassed, but instead, the conceptual model is that
we @emph{aggregate} or insert objects to it rather than define
subclasses.
One might think of a bare ns-3 node as a shell of a computer,
to which one may add NetDevices (cards) and other innards including
the protocols and applications. @ref{fig:node} illustrates
that Node objects contain a list of Applications (initially,
the list is empty), a list of NetDevices (initially, the list
is empty), a unique integer ID, and a system ID (for
distributed simulation).
The design tries to avoid putting too many dependencies on the
base class Node, Application, or NetDevice for the following:
@itemize @bullet
@item IP version, or whether IP is at all even used in the Node.
@item implementation details of the IP stack
@end itemize
From a software perspective, the lower interface of applications
corresponds to the C-based sockets API. The upper interface
of NetDevice objects corresponds to the device independent
sublayer of the Linux stack. Everything in between can be
aggregated and plumbed together as needed.
Let's look more closely at the protocol demultiplexer. We want
incoming frames at layer-2 to be delivered to the right layer-3
protocol such as Ipv4. The
function of this demultiplexer is to register callbacks for
receiving packets. The callbacks are indexed based on the
@uref{http://en.wikipedia.org/wiki/EtherType,,EtherType}
in the layer-2 frame.
Many different types of higher-layer protocols may be
connected to the NetDevice, such as IPv4, IPv6, ARP,
MPLS, IEEE 802.1x, and packet sockets. Therefore, the
use of a callback-based demultiplexer avoids the need to
use a common base class for all of these protocols, which
is problematic because of the different types of objects
(including packet sockets) expected to be registered there.
Each NetDevice delivers packets to a callback with the following
signature:
@verbatim
/**
* \param device a pointer to the net device which is calling this callback
* \param packet the packet received
* \param protocol the 16 bit protocol number associated with this packet.
* This protocol number is expected to be the same protocol number
* given to the Send method by the user on the sender side.
* \param address the address of the sender
* \returns true if the callback could handle the packet successfully,
* false otherwise.
*/
typedef Callback<bool, Ptr<NetDevice>, Ptr<Packet>, uint16_t,
const Address &> ReceiveCallback;
@end verbatim
There is a function in class Node that matches that signature:
@verbatim
private:
bool ReceiveFromDevice (Ptr<NetDevice> device, Ptr<Packet>,
uint16_t protocol, const Address &from);
@end verbatim
However, users do not need to access this function directly. Instead,
when users call @code{uint32_t AddDevice (Ptr<NetDevice> device)},
the implementation of this function sets the callback (and the
function returns the ifIndex of the NetDevice on that Node).
But what does the ReceiveFromDevice function do? Here, it looks
up another callback, in its list of callbacks, corresponding to the
matching EtherType. This callback is called a ProtocolHandler, and
is specified as follows:
@verbatim
typedef Callback<void, Ptr<NetDevice>, Ptr<Packet>, uint16_t,
const Address &> ProtocolHandler;
@end verbatim
Upper-layer protocols or objects are expected to provide such a function.
and register it with the list of ProtocolHandlers by calling
@code{Node::RegisterProtocolHandler ();}
For instance, if Ipv4 is aggregated to a Node, then the Ipv4 receive
function can be registered with the protocol handler by calling:
@verbatim
RegisterProtocolHandler (
MakeCallback (&Ipv4L3Protocol::Receive, ipv4),
Ipv4L3Protocol::PROT_NUMBER, 0);
@end verbatim
and likewise for Ipv6, Arp, etc.
@section NodeList
Every Node created is automatically added to the ns-3 @code{NodeList}.
The NodeList class provides an @code{Add()} method and C++ iterators
to allow one to walk the node list or fetch a Node pointer by
its integer identifier.
@section Internet stack aggregation
The above @code{class Node} is not very useful as-is; other objects
must be aggregated to it to provide useful node functionality.
The ns-3 source code directory @code{src/internet-stack} provides
implmentation of TCP/IPv4-related components. These include IPv4,
ARP, UDP, TCP, 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 @code{AddInternetStack ()} which does the
following:
@verbatim
void AddInternetStack (Ptr<Node> node)
{
// Create layer-3 protocols
Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
Ptr<ArpL3Protocol> arp = CreateObject<ArpL3Protocol> ();
ipv4->SetNode (node);
arp->SetNode (node);
// Create an L4 demux
Ptr<Ipv4L4Demux> ipv4L4Demux = CreateObject<Ipv4L4Demux> ();
// Create transport protocols and insert them into the demux
Ptr<UdpL4Protocol> udp = CreateObject<UdpL4Protocol> ();
Ptr<TcpL4Protocol> tcp = CreateObject<TcpL4Protocol> ();
ipv4L4Demux->SetNode (node);
udp->SetNode (node);
tcp->SetNode (node);
ipv4L4Demux->Insert (udp);
ipv4L4Demux->Insert (tcp);
// Add factories for instantiating transport protocol sockets
Ptr<UdpSocketFactoryImpl> udpFactory = CreateObject<UdpSocketFactoryImpl> ();
Ptr<TcpSocketFactoryImpl> tcpFactory = CreateObject<TcpSocketFactoryImpl> ();
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
udpFactory->SetUdp (udp);
tcpFactory->SetTcp (tcp);
ipv4Impl->SetIpv4 (ipv4);
// Aggregate all of these new objects to the node
node->AggregateObject (ipv4);
node->AggregateObject (arp);
node->AggregateObject (ipv4Impl);
node->AggregateObject (udpFactory);
node->AggregateObject (tcpFactory);
node->AggregateObject (ipv4L4Demux);
}
@end verbatim
@subsection Internet Node structure
The Internet Node (an ns-3 Node augmented by aggregation to have one or more
IP stacks) has the following internal structure.
@subsubsection Layer-3 protocols
At the lowest layer, sitting above the NetDevices, are the "layer 3"
protocols, including IPv4, IPv6, and ARP. These protocols provide
the following key methods and data members:
@verbatim
class Ipv4L3Protocol : public Object
{
public:
// Add an Ipv4 interface corresponding to the provided NetDevice
uint32_t AddInterface (Ptr<NetDevice> device);
// Receive function that can be bound to a callback, for receiving
// packets up the stack
void Receive( Ptr<NetDevice> device, Ptr<Packet> p, uint16_t protocol,
const Address &from);
// Higher-level layers call this method to send a packet
// down the stack to the MAC and PHY layers
//
void Send (Ptr<Packet> packet, Ipv4Address source,
Ipv4Address destination, uint8_t protocol);
private:
Ipv4InterfaceList m_interfaces;
// Protocol handlers
}
@end verbatim
There are many more functions (such as @code{Forward ()}) but we will
focus on the above four items from an architectural perspective.
First, note that the @code{Receive ()} function has a matching signature
to the ReceiveCallback in the @code{class Node}. This function pointer
is inserted into the Node's protocol handler when
@code{AddInterface ()} is called. The actual registration is done
with a statement such as:
follows:
@verbatim
RegisterProtocolHandler ( MakeCallback (&Ipv4Protocol::Receive, ipv4),
Ipv4L3Protocol::PROT_NUMBER, 0);
@end verbatim
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
@code{GetObject<Ipv4L3Protocol> ()} to obtain a pointer, as follows:
@verbatim
Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol> ();
if (ipv4 != 0)
{
ipv4->Send (packet, saddr, daddr, PROT_NUMBER);
}
@end verbatim
This class nicely demonstrates two techniques we exploit in
ns-3 to bind objects together: callbacks, and object aggregation.
Once IPv4 has determined that a packet is for the local node, it
forwards it up the stack. This is done with the following function:
@verbatim
void
Ipv4L3Protocol::ForwardUp (Ptr<Packet> p, Ipv4Header const&ip,
Ptr<Ipv4Interface> incomingInterface)
{
NS_LOG_FUNCTION (this << p << &ip);
Ptr<Ipv4L4Demux> demux = m_node->GetObject<Ipv4L4Demux> ();
Ptr<Ipv4L4Protocol> protocol = demux->GetProtocol (ip.GetProtocol ());
protocol->Receive (p, ip.GetSource (), ip.GetDestination (), incomingInterface);
}
@end verbatim
The first step is to find the aggregated Ipv4L4Demux object. Then, this
object is consulted to look up the right Ipv4L4Protocol, based on IP protocol
number. For instance, TCP is registered in the demux as protocol number 6.
Finally, the @code{Receive()} function on the Ipv4L4Protocol (such as
@code{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 @code{class Ipv4Interface} roughly corresponds to
the @code{struct in_device}; the main purpose is to provide
address-family specific information (addresses) about an interface.
@subsubsection 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:
@verbatim
Ptr<Udp> udpSocketFactory = GetNode ()->GetObject<Udp> ();
Ptr<Socket> m_socket = socketFactory->CreateSocket ();
m_socket->Bind (m_local_address);
...
@end verbatim
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 @code{Connect ()}
and @code{Send ()}. See the chapter on ns-3 sockets for more information.
We have described so far a socket factory (e.g. @code{class Udp}) and
a socket, which may be specialized (e.g., @code{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 @code{class Ipv4EndPointDemux}.
This demultiplexer stores objects of @code{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 @code{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 @code{ForwardUp ()} method, which then calls the @code{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, ...).
ns-3 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:
@verbatim
void Socket::SetRecvCallback (Callback<void, Ptr<Socket>,
Ptr<Packet>, const Address&> receivedData);
@end verbatim
The data being received is conveyed in the Packet data buffer. An example
usage is in @code{class PacketSink}:
@verbatim
m_socket->SetRecvCallback (MakeCallback(&PacketSink::HandleRead, this));
@end verbatim
To summarize, internally, the UDP implementation is organized as follows:
@itemize @bullet
@item a @code{UdpImpl} class that implements the Udp socket factory
functionality
@item a @code{UdpL4Protocol} class that implements the protocol logic
that is socket-independent
@item a @code{UdpSocketImpl} class that implements socket-specific aspects
of UDP
@item a class called @code{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.
@end itemize
@subsection Internet Node interfaces
Many of the implementation details, or internal objects themselves,
of Internet Node objects are not exposed at the simulator public
API. This allows for different implementations; for instance,
replacing the native ns-3 models with ported TCP/IP stack code.
The C++ public APIs of all of these objects is found in the
@code{src/node} directory, including principally:
@itemize @bullet
@item @code{socket.h}
@item @code{tcp.h}
@item @code{udp.h}
@item @code{ipv4.h}
@end itemize
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 @code{CreateSocket}.
@subsection Example path of a packet
These two figures show an example stack trace of how packets flow
through the Internet Node objects.
@float Figure,fig:internet-node-send
@caption{Send path of a packet.}
@image{figures/internet-node-send,5in}
@end float
@float Figure,fig:internet-node-recv
@caption{Receive path of a packet.}
@image{figures/internet-node-recv,5in}
@end float