GSOC2009NetworkAddressTranslationMidtermReport

From Nsnam
Jump to: navigation, search

Aim

The aim of this project is to implement Network Address Translation functionality for ns-3. Early on, we decided to build an extensible framework which can ease the task of adding firewall functionality to ns-3 and hence increase code reusability as well.

Background

I referred to the Linux kernel Netfilter framework for the ns-3 Netfilter implementation. Netfilter consists of five hooks which are possible places where the processing of the packet is handed over to the Netfilter framework. A developer can register functions at any of the hooks and those functions are called in turn as a packet traverses the hooks.

IPtables is a userspace utility that makes use of the Netfilter framework inside Linux kernel in order to facilitate the specification of firewall rules. However, IPtables does not interact directly with the Netfilter framework. Instead, there is a set of Linux kernel modules that mediate between IPtables and Netfilter. Modules are (un)pluggable pieces of code that run in kernel mode.

Connection tracking is an important part of the Netfilter framework. The framework provides a connection tracking API so that modules can register themselves as layer 3 protocol helpers, layer 4 protocol helpers, NAT helpers, protocol specific NAT helpers etc. IPv4 connection tracking is implemented by registerting connection tracking specific functions at each of the hooks except the forwarding one.

Milestones

The project milsestones are as follows.

  • Implementation of Hooks within the IP stack
  • Connection tracking implementation using hooks
  • Implementation of NAT using the connection tracking API

Connection tracking is the largest module in this project. It involves implementation of hook functions that are required for connection tracking and includes data structures such as a hash for maintaining state regarding existing connections. Moreover, each Packet should have some additional information (in the form of a byte tag) so that when it traverses a hook, those changes can be noticed by other hooks. This information is necessary in order to determine if a packet is a reply packet.

Connection tracking module also provides an API so that IPv4 and TCPv4 helpers can register themselves using this API. This has the advantage of extensibility, as helpers for other protocols like SCTP can be written and registered using the API. This way whenever an SCTP packet will be encountered by the connection tracking hook functions, it will be handed over to the SCTP helper for further processing and updating of the current state.

Implementation

Netfilter Hooks

Ipv4Netfilter is the main class which provides the functionality for registering and unregistering a function at a hook. Ipv4NetfilterHook class represents information that should be contained within a hook element before it is registered. For instance, a hook element must have the hook function that needs to be registered and will be invoked when a packet traverses this hook, the priority of the hook function, protocol family the hook is responsible for and which particular and the hook number where it should be registered. Hook numbers as represented as NF_INET_PREROUTING, NF_INET_POSTROUTING etc.

The priority of hook function is important because more than one function can be registered at a hook. Essentially, there is a priority list maintained at each hook. This is implemented using the NetfilterCallbackChain class.

When a hook function is registered using RegisterNetfilterHook, it is placed within the above mentioned priority list based on its priority. It will be invoked according to its priority when a packet traverses the hook at which this function was registered. Similarly, when a hook function is unregistered using UnRegisterNetfilterHook, the corresponding Ipv4NetfilterHook object is removed from the NetfilterCallbackChain for that hook.

Hooks have inserted at appropriate locations by inspecting ns-3 IP stack source code. For example, the PREROUTING hook has been placed in Ipv4L3Protocol::Receive method. It is important to note that this hook should be placed before the proceedings are handed over to the RouteInput method for whatever routing protocols have been registered. In order to insert the hooks, it was necessary to have a Ipv4Netfilter object as a private member of Ipv4L3Protocol class. I also had a detailed discussion with T.J. Kopena regarding this matter and we had concluded that it is better to add this as a member rather than aggregating an object of Ipv4Netfilter with Ipv4L3Protocol.

When a packet traverses a hook, it is handed over to the Netfilter framework using the Ipv4Netfilter::ProcessHook method. This method essentially invokes all registered callbacks in this hook's callback chain. As mentioned earlier, these callbacks are invoked in the order of priority.

The POSTROUTING hook is placed at multiple places, in Ipv4L3Protocol::Send and Ipv4L3Protocol::SendRealOut. This is so because there can be five different scenarios for sending the packet as mentioned in the comments of Ipv4L3Protocol::Send.

Although, I have not defined a log component for the Netfilter framework as yet, but I will definitely do that. In order to test the hooks, I had simply used the existing Ipv4L3Protocol log component. Also, I had registered functions at the Netfilter hooks within the Ipv4L3Protocol constructor. This will not stay the same and will be replaced by a user configuration API which will allow the script writer to register functions at any of the hooks. This will be done towards the end of the project as the most important part is to implement the core functionality of this framework.

The output from the log component clearly indicates that the hook functions are called at appropriate locations. I have used the example "first.cc" to demonstrate the hook functions. In this example, four packets are exchanged. This is obvious from the output below:

Ipv4L3Protocol:Ipv4L3Protocol()
Ipv4L3Protocol:SetIpForward(0x83547b0, 1)
Ipv4L3Protocol:SetupLoopback()
Ipv4L3Protocol:AddIpv4Interface(0x83547b0, 0x8354858)
Ipv4L3Protocol:SetRoutingProtocol(0x83547b0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:IsUp(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:Ipv4L3Protocol()
Ipv4L3Protocol:SetIpForward(0x8355090, 1)
Ipv4L3Protocol:SetupLoopback()
Ipv4L3Protocol:AddIpv4Interface(0x8355090, 0x8355188)
Ipv4L3Protocol:SetRoutingProtocol(0x8355090)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:IsUp(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:GetInterfaceForDevice(0x83547b0, 0)
Ipv4L3Protocol:AddInterface(0x83547b0, 0xbfadc414)
Ipv4L3Protocol:AddIpv4Interface(0x83547b0, 0x83554d8)
Ipv4L3Protocol:AddAddress(0x83547b0, 1, m_local=10.1.1.1; m_mask=255.255.255.0; m_broadcast=10.1.1.255; m_scope=2; m_secondary=0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:IsUp(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:SetMetric(1, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:SetUp(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetInterfaceForDevice(0x8355090, 0)
Ipv4L3Protocol:AddInterface(0x8355090, 0xbfadc414)
Ipv4L3Protocol:AddIpv4Interface(0x8355090, 0x83558a8)
Ipv4L3Protocol:AddAddress(0x8355090, 1, m_local=10.1.1.2; m_mask=255.255.255.0; m_broadcast=10.1.1.255; m_scope=2; m_secondary=0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:IsUp(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:SetMetric(1, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:SetUp(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetNetDevice(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:Send(0x83547b0, 0x8366758, 10.1.1.1, 10.1.1.2, 17, 0x8366738)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:Send(): Testing address 127.0.0.1 with mask 255.0.0.0
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:Send(): Testing address 10.1.1.1 with mask 255.255.255.0
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:Send(): Ipv4L3Protocol::Send case 3:  passed in with route
Ipv4L3Protocol:BuildHeader()
Ipv4L3Protocol:SendRealOut(0x83547b0, 0x8366758, 0xbfadbd00)
Ipv4L3Protocol:SampleHookFunction2(): Netfilter hook sample function 2 called
Ipv4L3Protocol:GetInterfaceForDevice(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:SendRealOut(): Send via NetDevice ifIndex 0 ipv4InterfaceIndex 1
Ipv4L3Protocol:SendRealOut(): Send to destination 10.1.1.2
Sent 1024 bytes to 10.1.1.2
Ipv4L3Protocol:Receive(0x8355090, 0xbfadbf58, 0x8366758, 2048, 02-06-00:00:00:00:00:01)
Ipv4L3Protocol:Receive(): Packet from 02-06-00:00:00:00:00:01 received on node 1
Ipv4L3Protocol:SampleHookFunction(): Netfilter hook sample function called
Ipv4L3Protocol:GetInterfaceForDevice(0x8355090, 0)
Ipv4L3Protocol:GetInterfaceForDevice(0x8355090, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:LocalDeliver(0x8355090, 0x8366668, 0xbfadbe48)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Received 1024 bytes from 10.1.1.1
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetNetDevice(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:Send(0x8355090, 0x8366758, 10.1.1.2, 10.1.1.1, 17, 0x83669d8)
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetAddress(0x8355090, 0, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:Send(): Testing address 127.0.0.1 with mask 255.0.0.0
Ipv4L3Protocol:GetNAddresses(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 0)
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:GetAddress(0x8355090, 1, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:Send(): Testing address 10.1.1.2 with mask 255.255.255.0
Ipv4L3Protocol:GetNAddresses(0x8355090, 1)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:Send(): Ipv4L3Protocol::Send case 3:  passed in with route
Ipv4L3Protocol:BuildHeader()
Ipv4L3Protocol:SendRealOut(0x8355090, 0x8366758, 0xbfadbad0)
Ipv4L3Protocol:SampleHookFunction2(): Netfilter hook sample function 2 called
Ipv4L3Protocol:GetInterfaceForDevice(0x8355090, 0)
Ipv4L3Protocol:GetInterface(0x8355090, 1)
Ipv4L3Protocol:SendRealOut(): Send via NetDevice ifIndex 0 ipv4InterfaceIndex 1
Ipv4L3Protocol:SendRealOut(): Send to destination 10.1.1.1
Ipv4L3Protocol:Receive(0x83547b0, 0xbfadbf58, 0x8366758, 2048, 02-06-00:00:00:00:00:02)
Ipv4L3Protocol:Receive(): Packet from 02-06-00:00:00:00:00:02 received on node 0
Ipv4L3Protocol:SampleHookFunction(): Netfilter hook sample function called
Ipv4L3Protocol:GetInterfaceForDevice(0x83547b0, 0)
Ipv4L3Protocol:GetInterfaceForDevice(0x83547b0, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetAddress(0x83547b0, 0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNAddresses(0x83547b0, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 0)
Ipv4L3Protocol:GetNInterfaces()
Ipv4L3Protocol:GetNAddresses(0x83547b0, 1)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:GetAddress(0x83547b0, 1, 0)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Ipv4L3Protocol:LocalDeliver(0x83547b0, 0x8366c20, 0xbfadbe48)
Ipv4L3Protocol:GetInterface(0x83547b0, 1)
Received 1024 bytes from 10.1.1.2
Ipv4L3Protocol:DoDispose(0x83547b0)
Ipv4L3Protocol:DoDispose(0x8355090)
Ipv4L3Protocol:~Ipv4L3Protocol(0x83547b0)
Ipv4L3Protocol:~Ipv4L3Protocol(0x8355090)

Connection Tracking

As mentioned earlier, connection tracking is implemented by registering appropriate connection tracking functions at each of the hooks except IP_FORWARD. Connection tracking is the largest and most essential component of this project. It significantly increases the code reusability, extensibility and ease of implementing a new helper class by providing an API to the developer.

The Ipv4Netfilter class maintains a list of layer 3 protocol helpers and layer 4 protocol helpers. These helpers are implemented by inheriting from NetfilterConntrackL3protocol and NetfilterConntrackL4Protocol respectively. When a packet traverses a hook, connection tracking looks up the layer 3 protocol helper from the list maintained by Ipv4Netfilter.

Ipv4Netfilter provides methods to register and unregister L3 protocol helpers using RegisterL3Protocol and UnRegisterL3Protocol respectively. Similar methods are available for registering and unregistering L4 Protocols.

Any L3 protocol helper needs to implement two callbacks, PacketToTuple, which is used to convert a packet to a tuple that will be used by connection tracking, and GetL4Protocol, which used to find out the L4 protocol for this packet such as TCP, UDP etc. This information is necessary in order to invoke the correct L4 helper. More importantly, each helper needs to register appropriate functions at each of the hooks. These functions maintain state in accordance with the protocol-specific rules.

Similar to L3 protocol helpers, each L4 protocol helper has to implement some callbacks which are invoked when a packet is handed over to the helper. These callbacks include, PacketToTupleCallback, which is the same as before (but contains additional information at layer 4 such as the ports) and PacketVerdictCallback, which determines whether this packet should continue to traverse the hooks, dropped or any of the other verdicts supported by the netfilter framework.

It is important to note that I am not focusing on implementing verdicts right now, because they will sidetrack me from the core functionality of this project. In order to implement, Network Address Translation, I do not need any kind of packet dropping support from the framework. Therefore, I will continue to implement modules which are relevant to NAT for now. Later on, after the project, the framework can be extended to include the support for verdicts and hence implement a firewall for ns-3.

Connection tracking needs to maintain state regarding on-going connections. For this purpose, a hash is maintained by the Ipv4Netfilter class. This hash stores connection tracking information regarding a four-tuple that consists of source IP address, source port, destination IP address and destination port. ns-3 code base does not have a hashing function for such a four-tuple. Therefore, I looked up the Linux kernel source code and found that it uses the Jenkins hash function.