GSOC2009Netfilter

From Nsnam
Jump to: navigation, search

Hooks

Hooks are places in the IP stack where a packet is handed over to the netfilter framework. There are five such places.

NF_INET_PRE_ROUTING

When a packet is received by a node, this hook is the first one to receive the packet. As the name implies, an incoming packet is processed by this hook even before a routing decision determines whether or not the packet is destined for the current node.

In case of destination Network Address Translation, the destination IP address should be changed at this hook so that the routing decision can send the packet to the correct interface.

In the ns-3 IP stack, this hook is placed in Ipv4L3Protocol::Receive method.

netfilter.ProcessHook ((uint8_t)1, NF_INET_PRE_ROUTING, packet, device, device);

The above method belongs to Ipv4Netfilter class which implement core functionality of the netfilter framework. This method hands over the packet to the netfilter framework. Each of the hooks is actually a callback chain implemented by the class NetfilterCallbackChain.

The first argument is the protocol family, which is just 1 right now. Later on, this can be changed to PF_INET and PF_INET6 for IPv4 and IPv6 respectively. The second argument denotes the hook number, third is the packet that should traverse this hook, fourth and fifth arguments indicate the incoming and outgoing NetDevice for this packet. These arguments may be NULL depending on which hook is considered. The last argument is by default a NULL callback that returns an unsigned int and has one argument which is the packet. This is termed as the ContinueCallback. We will talk about this when callback chains are introduced.

NF_INET_LOCAL_IN

The incoming packets that have the destination IP address of the node receiving the packet traverse this hook. Typically, this happens after the routing decision determines that the packet is destined for the node. In this case, Ipv4L3Protocol::LocalDeliver is invoked. Therefore, this hook is placed inside this method.

Callback<uint32_t, Ptr<Packet> > ccb = MakeCallback (&Ipv4Netfilter::NetfilterConntrackConfirm, &netfilter);
netfilter.ProcessHook ((uint8_t)1, NF_INET_LOCAL_IN, pkt, device, device, ccb); 

ccb above is the ContinueCallback which will be discussed in detail later on. The next line calls is the actual hook where packet is handed over to the netfilter framework.

NF_INET_FORWARD

This is meant for packets that are destined for nodes other than the one currently receiving the packet. Packets will traverse this hook if the node receiving the packet is acting as a router. Currently, this hook has not been added to the ns-3 IP stack.

NF_INET_LOCAL_OUT

Packets that are created and sent out by a node traverse this hook. This is only meant for outgoing packets. This hook is placed before a routing decision has been made regarding an outgoing packet. The Ipv4L3Protocol::Send method has various cases for outgoing packets. This hook is placed at appropriate locations for each of these cases. I have only tested "case 3" where a route is passed along with the packet.

netfilter.ProcessHook ((uint8_t)1, NF_INET_LOCAL_OUT, packetCopy, outDev, outDev);

NF_INET_POST_ROUTING

This is the last hook on the outgoing path. Outgoing packets traverse this hook after a routing decision has been made. Source Network Address Translation (SNAT) is performed at this hook. This hook is placed inside Ipv4L3Protocol::SendRealOut as this method is invoked after RouteOutput method has been called for the current routing protocol.

Callback<uint32_t, Ptr<Packet> > ccb = MakeCallback (&Ipv4Netfilter::NetfilterConntrackConfirm, &netfilter);
netfilter.ProcessHook ((uint8_t)1, NF_INET_POST_ROUTING, packet, outDev, outDev, ccb);

It should be noted that Ipv4Netfilter::NetfilterConntrackConfirm has been specified as the ContinueCallback for two hooks, NF_INET_LOCAL_IN and NF_INET_POST_ROUTING. The netfilter framwork can support dropping of packets as well. This means that a packet which traverses the NF_INET_LOCAL_OUT hook might get dropped because of a rule match. Therefore, this packet will never make it to NF_INET_POST_ROUTING and hence it does not make any sense to maintain state regarding the connection this packet belongs to. Only when an outgoing packet traverses both NF_INET_LOCAL_OUT and NF_INET_POST_ROUTING should we expect a reply. So, the NetfilterConntrackConfirm callback "confirms" the connection by creating an entry in the confirmed hash (m_hash) belonging to the Ipv4Netfilter class.

Similarly, for incoming packets, the NF_INET_LOCAL_IN hook has NetfilterConntrackConfirm set as the ContinueCallback. Both the hooks that have this callback set as the continue callback are last entry (NF_INET_LOCAL_IN) and exit points in the netfilter framework. Therefore, it makes sense to "confirm" a connection at these hooks.

Callback Chains

Each of the hooks are equipped with a chain of callbacks provided by NetfilterCallbackChain class. A developer can register callbacks with a specified priority into this chain. When a packet is handed over to a hook, the callbacks registered in the associated chain are invoked in the order of priority.

All the features including connection tracking are provided by registering callbacks at the hooks. For instance, connection tracking is a set of callbacks registered at four of the hooks excluding NF_INET_FORWARD. The callbacks for connection tracking have a certain priority.

Callback Priority

Priority of callbacks plays a very important role in the netfilter framework. It determines the order in which the callback will be invoked when a packet traverses the hook. Network Address Translation makes use of information provided by connection tracking to determine when to create a new NAT mapping. For example, connection tracking indicates that it has encountered a new connection by changing changing the status of ConntrackTag for the packet to IP_CT_NEW. This is used by NAT module to create a new NAT mapping for this connection. As you may have guessed, NAT callbacks have a lower priority than connection tracking callbacks because NAT makes use of information provided by connection tracking. This information could only be available to NAT if connection tracking callbacks have already been invoked on the packet.

Similarly, in the future, when defragmentation support is added, the defragmentation callbacks can be added with a priority higher than that of connection tracking. This would make sure that packets processed by connection tracking are defragmented.

The hook priorities used by Linux kernel are shown here for reference. The ns-3 implementation also uses the same priorities.

typedef enum {
  NF_IP_PRI_FIRST = INT_MIN,
  NF_IP_PRI_CONNTRACK_DEFRAG = -400,
  NF_IP_PRI_RAW = -300,
  NF_IP_PRI_SELINUX_FIRST = -225,
  NF_IP_PRI_CONNTRACK = -200,
  NF_IP_PRI_MANGLE = -150,
  NF_IP_PRI_NAT_DST = -100,
  NF_IP_PRI_FILTER = 0,
  NF_IP_PRI_SECURITY = 50,
  NF_IP_PRI_NAT_SRC = 100,
  NF_IP_PRI_SELINUX_LAST = 225,
  NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
  NF_IP_PRI_LAST = INT_MAX,
} NetfilterIpv4HookPriorities;

Netfilter Callbacks

These are implemented by Ipv4NetfilterHook class. It has a priority, protocol family, hook number and callback. I think this class is a misnomer and will change it to Ipv4NetfilterHookCallback. The Ipv4Netfilter::RegisterNetfilterHook method can be used to register a hook callback. Again, this method will also be renamed to RegisterNetfilterHookCallback.

Connection Tracking

This module is used to maintain state regarding a connection. It is important to understand that it does not modify a packet in any way.

7-Tuple

Every packet is converted to a 7-tuple which serves as the key to the hashes maintained by the Ipv4Netfilter class.

Ipv4Address m_l3Source;
uint16_t m_l3Protocol;
uint16_t m_l4Source;

Ipv4Address m_l3Destination;
uint16_t m_l4Destination;
uint8_t m_protocolNumber;
uint8_t m_direction;

Hashes

The Ipv4Netfilter class maintains two 7-tuple hashes.

TupleHash m_unconfirmed;
TupleHash m_hash;

m_unconfirmed is used to store connection tracking information for packets that are "unconfirmed". By this we mean that for outgoing packets, the packet has not traversed the NF_INET_POST_ROUTING hook and for incoming packets, the packet has not traversed the NF_INET_LOCAL_IN hook. One of the reasons this could happen is because the packet gets dropped before it makes to the above mentioned hooks.

The tuple corresponding to a packet is added to the m_confirmed hash only when it has traversed the above mentioned hooks. The Ipv4Netfilter::NetfilterConntrackGetTuple method is used to get a tuple corresponding to a packet. Both of the above mentioned hashes have the 7-tuple corresponding to a packet as the key and an object of IpConntrackInfo as an element. This indicates the current connection tracking information for the connection that corresponds to the packet being processed.

The Linux kernel uses Jenkin's hash function for hashing 7-tuples. The same code has been borrowed from the Linux kernel for use in ns-3. However, it has not been tested extensively.

IpConntrackInfo

The objects of this class are elements for the hashes mentioned above. It maintains the connection tracking status for a connection. For instance, if the IPS_SEEN_REPLY_BIT is set, it indicates that connection tracking has seen a reply for this connection. That is, it has encountered packets in the direction other than the original. For instance, if a UDP packet was sent to a remote node, a reply UDP packet from the remote node will set this bit, indicating that the reply has been seen.

Connection tracking hook callbacks

Connection tracking makes use of the netfilter hooks by registering functions/methods that perform the actual work at 4 of the hooks except NF_INET_FORWARD. This is done in the constructor of Ipv4Netfilter as shown below:

NetfilterHookCallback preRouting = MakeCallback (&Ipv4Netfilter::NetfilterConntrackIn, this);
NetfilterHookCallback localIn = MakeCallback (&Ipv4ConntrackL3Protocol::Ipv4Confirm, PeekPointer (ipv4));

Ipv4NetfilterHook nfh = Ipv4NetfilterHook (1, NF_INET_PRE_ROUTING, 1, preRouting);
Ipv4NetfilterHook nfh1 = Ipv4NetfilterHook (1, NF_INET_LOCAL_OUT, 1, preRouting);
Ipv4NetfilterHook nfh2 = Ipv4NetfilterHook (1, NF_INET_POST_ROUTING, 1, localIn);
Ipv4NetfilterHook nfh3 = Ipv4NetfilterHook (1, NF_INET_LOCAL_IN, 1, localIn);

this->RegisterNetfilterHook (nfh);
this->RegisterNetfilterHook (nfh1);
this->RegisterNetfilterHook (nfh2);
this->RegisterNetfilterHook (nfh3);

Protocol Helpers

Connection tracking makes use of protocol helpers to get its job done. The Ipv4Netfilter class maintains vectors for L3 and L4 protocol helpers. When a packet is encountered by connection tracking, it determines the L3 protocol for that packet and looks for an L3 protocol helper for that packet. If, for instance, the L3 protocol is IPv4 (the only supported protocol right now), the protocol field within the IP header is consulted to determine the L4 protocol and thus the L4 protocol helper. Ipv4Netfilter::FindL4ProtocolHelper is used to perform this lookup.

Layer 3 Protocol Helpers

Each L3 protocol helper inherits from NetfilterConntrackL3Protocol. This class has two virtual functions PacketToTuple and InvertTuple. Every L3 protocol helper must implement these functions to give protocol specific ways of converting a packet to a tuple and inverting a tuple. Inverting a tuple results in a tuple is representing the reply packet. This is necessary in order to identify a reply.

Ipv4ConntrackL3Protocol implements the layer 3 protocol helper for IPv4.

Layer 4 Protocol Helpers

Similar to to L3 protocol helpers, each L4 protocol helper derives from NetfilterConntrackL4Protocol. This class also has the same virtual functions as L3 protocol helper. In addition to the above, it has a method GetProtocol which is used to get the L4 protocol number. The numbers are similar to Linux kernel. For example, TCP has layer 4 protocol number "6" which is respresented by IPPROTO_TCP.

In addition to the above, in order to support packet filtering/mangling, a L4 protocol helper must provide implementations of the following methods.

New

This is called when a new connection for this protocol is encountered. It can allocate/initialize protocol specific data structures regarding the new connection so that the protocol specific state can be maintained.

Error

This checks whether the current packet is valid according to the protocol. If the packet is not valid, its traversal through the framework can be cut short.

PacketVerdict

This updates the protocol specific data structures to maintain connection state. It is also used to increase the timeout value if connection tracking has seen a reply to a packet. If no reply is encountered, the timeout will not be extended and thus the state regarding that connection would be deleted.

Currently, the code does not implement the above three callbacks but they will be supported in the future to get maximum advantage.

TcpConntrackL4Protocol and UdpConntrackL4Protocol implement the L4 protocol for the respective protocols. The TCP L4 protocol helper can be made more intelligent as is the case in the Linux kernel. I did not delve into this because it would have sidetracked me from the main goal of this project.

Network Address Translation

NAT is also a set of hook callbacks that are registered at 4 of the hooks except NF_INET_FORWARD. This section will be updated with more information soon.