Background
In this chapter we provide background information about the technologies used in this project. While there are several books, papers, and presentations explaining these technolo-gies in-depth, we provide some general information and point the reader to the different references for further reading. Section 2.1 shortly describes the Netfilter framework as well as how it can be extended. Section 2.2 introduces the Lua scripting environment.
Netfilter
Netfilter [1] is the component of the Linux kernel that is used when the network traffic needs to be inspected and/or manipulated. More specifically, Netfilter inserts five hooks into the networking stack (see Figure 2.1):
• PREROUTING: All packets traverse this hook. It is called before any routing decision is made, but after all IP header sanity checks have succeeded. Typically, Port Address Translation (PAT), the redirection of packets, as well as Destination Network Address Translation (DNAT) are implemented in the PREROUTING hook.
• INPUT: All incoming packets that are destined to the local machine pass this hook. This is the last hook traversed by incoming packets.
• FORWARD: All packets that are not destined to the local machine traverse this hook. This hook is typically used for implementing firewalls.
• OUTPUT: This is the first hook that is traversed by outgoing packets. All packets that leave the local machine pass this hook.
• POSTROUTING: All packets that leave the local machine traverse this hook. It is called after any routing decision. The POSTROUTING hook is typically used to implement Source Network Address Translation (SNAT).
Figure 2.1 illustrates the Netfilter’s hook-system. Netfilter provides an API for registering and unregistering a callback function to a given hook. Such callback functions typically return a value, the verdict that controls how Netfilter should further proceed with the packet. The following verdicts are currently defined in Netfilter:
• ACCEPT: The packet should also traverse any further hook.
• DROP: The packet should be silently discarded.
• QUEUE: The packet is passed to a userspace program, which will handle the packet.
• REPEAT: This verdict forces the packet to traverse the same hook again.
• STOLEN: The packet is silently held until something happens. This verdict enables that packets can be collected for further processing. This is used for dealing with fragmented IP packets.
Kernel modules such as ip_tables, arp_tables, and ebtables use these hooks to provide a more convenient way for defining rules for filtering and transforming packets. A well-known userspace tool for inserting such rules is iptables, which we also extend for loading and unloading the Lua scripts.
Having a closer look at the Netfilter internals, we can see that Netfilter itself does not provide a lot of functionality. It rather offers a framework, where several Linux Kernel Modules (LKM) register their services. In fact, packet matching as well as packet processing (as used for NAT) functionalities are implemented in several LKMs being loaded when needed. In order to simplify the development and integration of such LKMs, the project Xtables-addons [16] was set up. Using Xtables-addons, there is no need to patch or recompile the kernel. Furthermore, this framework can be used to easily install Netfilter extensions that are not yet accepted in the main kernel/iptables packages. PacketScript was hence developed using the Xtables-addons. Typically, Netfilter extension development involves the implementation of a LKM containing all important packet processing functionalities, as well as a userspace plugin needed by iptables when a new rule is injected. The following subsections describe these two software parts.
Userspace Plugin
The word “plugin” implies that there is a userspace application to be extended. In the case of PacketScript, the userspace tool iptables is extended by the PacketScript userspace plugin. This is necessary in order for iptables to load the corresponding LKM into memory as well as for knowing the proper format to copy the provided parameters from userspace to the LKM. Additionally, the plugin may provide functionality for validating the parameters and for presenting information on the command line about the usage of the module. Xtables-addons also simplifies the development of the userspace plugin.
Linux Kernel Module
Once a rule is validated by the userspace plugin, the data is copied from userspace to the LKM. The LKM provides a checkentry function being invoked whenever data has been copied to the LKM. Usually, this function does additional validation and some initialization work. For calling the checkentry function the LKM must already be loaded. Loading and unloading is normally done by some userspace tools, such as for example modprobe, rmmod; or in the Netfilter case, iptables may automatically load the LKM. The LKM provides a module_init function, which is automatically called when the LKM is loaded. Additionally, it provides the module_exit function, being called when the LKM is unloaded. These functions are typically used to initialize and shutdown the LKM. For instance a Netfilter extension (un)registers several callback functions within these functions:
• The checkentry function is called when a new rule copies some data from userspace to the LKM. This function validates the passed data, and may initialize some data needed within the target/match function.
• The destroy function is called when a rule is deleted. This function is typically used for freeing the resources allocated within the checkentry function.
• The target/match function is called when a packet is passed to the match/target extension. This function is used to process the packet in order to change its content or just for deciding whether the packet gets either accepted or dropped.
There are a few other functions, which are rarely used and remain unused within PacketScript.
Further information about developing Netfilter extensions can be found in [17, 18].
Lua
Lua is an imperative scripting language released under the MIT license (since version 5.1). It comes with a lightweight script interpreter written in ANSI C that can be easily embedded in every C program. This enables that an application can be partially programmed in Lua. The Lua scripts used can be modified without recompiling the whole application, thus enabling a rapid development process. A very simple use case is the configuration of a program by a Lua script. More sophisticated is the ability to write a whole part of an application in Lua. Several professional applications, such as Adobe Lightroom or Blizzard’s World of Warcraft, are partly developed in Lua [19]. There are several reasons for the success of Lua: On one hand it has several features C does not lend itself to: a good hardware abstraction, dynamic structures, no redundancies, ease of testing and debugging; on the other hand Lua comes with a safe execution environment, garbage collection, and facilities for handling strings and other data types with dynamic size.
The inventors of Lua decided to provide very expressive language constructs instead of blowing up the language with unnecessary language features, thus reducing the size of the language, its interpreter, and its API. Thanks to the expressiveness of the Lua syntax and the power of some language constructs, several programming paradigms are possible. Lua can be seen as a “multi-paradigm” programming language that enables common procedural programming, object oriented programming, and functional programming [20]. Since Lua does not target one single programming paradigm, it has no explicit support for object
orientation and inheritance, but with its metatables mechanism it easily enables their im-plementation. The same applies for namespaces and classes. Lua is a dynamically typed programming language, supporting only a few atomic data structures such as boolean values, numbers (double-precision floating point by default), and strings. Common data structures such as arrays, sets, lists, and records are represented with a Lua table. The Lua table is actually the single native data structure, which is basically a heterogeneous associative array [2].
Metatables
As mentioned before, Lua has some very expressive language constructs. One of these constructs are metatables and its corresponding metamethods. Such metatables provide some “type”-features to Lua tables, typically only available for numbers and strings. Such features are arithmetic and relational operators, but also concatenation as well as methods to obtain the size and string representation of a variable. Using metatables it is possible to define these operators also for Lua tables. It is quite useful to have such operators for tables, but they are mainly syntactical sugar to simplify the development. Besides these “common” operators, Lua provides a way to influence the normal behavior of a table during the query and modification of absent fields. This enables for instance, to write a metamethod that automatically creates a new value if an absent table field was queried. Or the function may lookup another table for the query. This is one of the basic building blocks for implementing inheritance. More details about Lua metatables can be found in Programming in Lua.
C API
Lua is designed to be embedded in C or C++ applications, but it was also used to extend software developed in Java, C#, Smalltalk, Fortran, Ada and Erlang. Lua offers the devel-oper a rich API enabling a strong integration with code written in other languages. Lua manages a global stack being used for transferring parameters between Lua and C functions. As a result, Lua provides simple functions to push and pop most common C data types. Furthermore, calling Lua functions from C is also done using the stack. In this case the name of the Lua function and all its arguments are pushed onto the stack. A call to the proper C API function, for instance lua_call will then invoke this function with the given arguments. The Figure 2.2 depicts such a scenario, where the Lua function max is called with two integer values as arguments.
1 Introduction
1.1 Problem Description
1.2 Related Work
1.3 Thesis Outline
2 Background
2.1 Netfilter
2.1.1 Userspace Plugin
2.1.2 Linux Kernel Module
2.2 Lua
2.2.1 Metatables
2.2.2 C API
3 PacketScript
3.1 Specification
3.1.1 Functionality
3.1.2 External Interfaces
3.1.3 Performance
3.1.4 Nonfunctional Requirements
3.1.5 Design Constraints
3.2 Architecture
3.2.1 Iptables Userspace Plugin
3.2.2 Netfilter Extension
3.2.3 Software Interrupt Context vs. Process Context
3.3 Object Oriented Packet Scripting
3.3.1 Generation of Protocol Classes
3.3.2 Field Modifiers
3.4 Protocol Buffers
3.4.1 The Raw Approach
3.4.2 Structured Packet Access
3.5 Dynamic Protocol Buffers
3.6 Byte Arrays in Lua
3.6.1 The byte_array object
3.6.2 The Bytes Library
3.7 The Netfilter Library
3.7.1 Deferring Work
3.7.2 Sending Packets
4 Experiments
4.1 Network Address Translation
4.2 Application Level Packet Cache
4.2.1 TFTP Cache
4.2.2 HTTP Cache
4.3 Discussion / Conclusions
4.3.1 NAT Experiment
4.3.2 Cache Experiment
5 Conclusion
5.1 Future Work
5.2 Implications of Research
Bibliography