HOWTO use IP interface indexes

From Nsnam
Jump to: navigation, search

How to find the IP interface index of an incoming packet

Suppose you have a node with multiple IP interfaces, each one belonging to a different NetDevice. Each NetDevice might have multiple IP addresses, and you're receiving a packet on a Socket.

The problem we will address is: how can I find out what's the IP-level interface index ?

One solution might be to open one socket per NetDevice, and bind them to the NetDevice throughSocket::BindToNetDevice ();

However, this will not necessarily work as intended. Moreover, it's cumbersome. It require to have another structure to hold the Socket / interface pairing, and it could get messy when interfaces are added/removed at runtime.

Fortunately, another solution exists.

By setting

 Socket::SetRecvPktInfo (true);

each received packet will be tagged with either an Ipv4PacketInfoTag or an Ipv6PacketInfoTag (depending on if it's an IPv4 or IPv6 packet).

The tags will carry the NetDevice interface index (i.e., NetDevice::GetIfIndex ()). This index, however, must not be confused with the IP level interface index, as the two might be different.

The NetDevice index is the index of the interface on the node. It is a progressive index and it depends on the order the NetDevices are added to the node.

The IP interface index is a progressive index as well, but it depends on how the interfaces areadded to the IP level.

They might do not match !

Let's do a practical example (I'll use IPv6, but for IPv4 is the same):

 // Create the nodes
 Ptr<Node> src = CreateObject<Node> ();
 Ptr<Node> dst = CreateObject<Node> ();
 NodeContainer net (src, dst);
 // Install the NetDevices
 CsmaHelper csma;
 csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));
 csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
 NetDeviceContainer ndc = csma.Install (net);
 // Add IPv6
 InternetStackHelper internetv6Nodes;
 internetv6Nodes.SetIpv4StackInstall (false);
 internetv6Nodes.Install (nodes);
 // Add the addresses
 Ipv6AddressHelper ipv6;
 ipv6.SetBase (Ipv6Address ("2001:1::"), Ipv6Prefix (64));
 Ipv6InterfaceContainer iic = ipv6.Assign (ndc);

Here we first setup the nodes, then the channels, then IP. However the following is also legal:

 // Create the nodes
 Ptr<Node> src = CreateObject<Node> ();
 Ptr<Node> dst = CreateObject<Node> ();
 NodeContainer net (src, dst);
 // Add IPv6
 InternetStackHelper internetv6Nodes;
 internetv6Nodes.SetIpv4StackInstall (false);
 internetv6Nodes.Install (nodes);
 // Install the NetDevices
 CsmaHelper csma;
 csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));
 csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
 NetDeviceContainer ndc = csma.Install (net);
 // Add the addresses
 Ipv6AddressHelper ipv6;
 ipv6.SetBase (Ipv6Address ("2001:1::"), Ipv6Prefix (64));
 Ipv6InterfaceContainer iic = ipv6.Assign (ndc);


In this second case, we invert the order. We fist create the nodes, then we add IP, then the NetDevices, then the IP addresses.


It is all perfectly fine, and both code snippets do exactly the same thing. The only difference is:

  • In the first case the CsmaNetDevice will have index 0, and the LoopbackNetDevice will have index 1.
  • In the second case, it is the opposite, since the LoopbackNetDevice will be added to the node before the CsmaNetDevice.

The root of the problem is the LoopbackNetDevice. This NetDevice is automatically added by IP and it will always have index 0 (for IP). If the NetDevices are added before IP to the node, then the NetDevice index 0 will be given to the first interface added, and the Loopback will have the last index. On the contrary, for IP the Loopback will have index 0, and the other NetDevices will start from 1.

If the NetDevices are added after IP, then the Loopback will have an index 0 for both IP and the node, and the indexes will be the same for both IP and the node.

Of course, you can't write your protocols and applications assuming that the user will create the NetDevices in a given order...

The IP-level interface number is very important, as it's used by IP instead of the node's NetDevice index. E.g.

 Ipv6RoutingProtocol::NotifyInterfaceUp (uint32_t interface);

How to discover the IP-level interface index in a fool-proof way since the Ipv4PacketInfoTag (or Ipv6PacketInfoTag) carry the NetDevice index ?

Easy (but not so easy).

 Ipv6PacketInfoTag interfaceInfo;
 if (!packet->RemovePacketTag (interfaceInfo))
 {
   NS_ABORT_MSG ("No incoming interface on RIPng message, aborting.");
 }
 uint32_t incomingIf = interfaceInfo.GetRecvIf ();
 Ptr<Node> node = this->GetObject<Node> ();
 Ptr<NetDevice> dev = node->GetDevice (incomingIf);
 Ptr<Ipv6> ipv6 = GetObject<Ipv6> ();
 uint32_t ipInterfaceIndex = ipv6->GetInterfaceForDevice (dev);

In this way you'll always know the right IP-level interface index. This is especially relevant for routing, or when you have to forward a packet and you do not want to send it back on the interface it came from.

A practical example is this, you have two interfaces and you want to forward packets. You open two "sending" sockets, each bound to one interface (make sure to disable receiving on these sockets). You open a "receiving" socket, bound to "any" address. From the receiving socket, you get a packet. You discover the interface it was received, and you can safely forward the packet on the other one.

By the way, in order to correctly bind the socket to the corresponding NetDevice, the NetDevice smart pointer mist be known. To retrieve it, only knowing the IP-level interface index, the correct method is:

 Ptr<Ipv6> ipv6 = GetObject<Ipv6> ();
 socket->BindToNetDevice (ipv6->GetNetDevice (i));

Happy coding !

--Tommaso (talk) 16:20, 29 January 2014 (EST)