# color schemes for man pages man() { LESS_TERMCAP_mb=$'\e[1;34m' \ LESS_TERMCAP_md=$'\e[1;32m' \ LESS_TERMCAP_so=$'\e[1;33m' \ LESS_TERMCAP_us=$'\e[1;4;31m' \ LESS_TERMCAP_me=$'\e[0m' \ LESS_TERMCAP_se=$'\e[0m' \ LESS_TERMCAP_ue=$'\e[0m' \ command man "$@" }
Now, source your file to load the new command. man will color certain keywords appropriately.
# update your shell's environment with the man() wrapper [student@host]$ source ~/.bashrc # check out the manual page for iptables [student@host]$ man iptables
iptables is a configuration tool for the kernel packet filter.
The system as a whole provides many functionalities that are grouped by tables: filter, nat, mangle, raw, security. If you want to alter a packet header, you place a rule in the mangle table. If you want to mask the private IP address of an internal host with the external IP address of the default gateway, you place a rule in the nat table. Depending on the table you choose, you will gain or lose access to some chains. If not specified, the default is the filter table.
Chains are basically lists of rules. The five built-in chains are PREROUTING, FORWARD, POSTROUTING, INPUT, OUTPUT. Each of these corresponds to certain locations in the network stack where packets trigger Netfilter hooks (here is the PREROUTING kernel hook as an example – not that hard to add one, right?) For a selected chain, the order in which the rules are evaluated is determined primarily by the priority of their tables and secondarily by the user's discretionary arrangement (i.e.: order in which rules are inserted).
A rule consists of two entities: a sequence of match criteria and a jump target.
The jump target represents an action to be taken. You are most likely familiar with the built-in actions such as ACCEPT or DROP. These actions decide the ultimate fate of the packet and are final (i.e.: rule iteration stops when these are invoked). However, there are also extended actions (see man iptables-extensions(8)) that are not terminal verdicts and can be used for various tasks such as auditing, forced checksum recalculation or removal of Explicit Congestion Notification (ECN) bits.
The match criteria of every rule are checked to determine if the jump target is applied. The way this is designed is very elegant: every type of feature (e.g.: layer 3 IP address vs layer 4 port) that you can check has a match callback function defined in the kernel. If you want, you can write your own such function in a Linux Kernel Module (LKM) and thus extend the functionality of iptables (Writing Netfilter Modules with code example). However, you will need to implement a userspace shared library counterpart. When you start an iptables process, it searches in /usr/lib/xtables/ and automatically loads certain shared libraries (note: this path can be overwritten or extended using the XTABLES_LIBDIR environment variable). Each library there must do three things:
iptables --help is called (its help message is an amalgamation of each library's help snippet).So when you want to test the efficiency of the iptables rule evaluation process, keep in mind that each rule may imply the invocation of multiple callbacks such as this.
Before writing our own match module, here's a small task to freshen your memory on how to use iptables.
Write an iptables rule according to the following specifications:
How to test:
$ sudo curl www.google.com $ sudo dmesg
multiport, owner modules
$ man 8 iptables-extensions
xtables is the backbone of iptables and provides a protocol-agnostic infrastructure for adding match modules. We touched on this topic earlier in the exercise but now we're going to jump right in. Following this brief introduction will be three subsections detailing the data structures, the user space shared library and the kernel module. All these are partially implemented in the 02/ directory and you will have specific TODOs in the source files. By the end, you will have implemented a match filter for requested domains in DNS queries (i.e.: you can block DNS queries for “google.com” but not “kernel.org”, for example). Note that you can solve this task either in the VM, or on your localhost. We won't be doing anything dangerous, like overwriting kernel structures.
We mentioned before that an iptables extension has two components. A kernel module implementing the verification of a rule and a user space library that is able to parse the user's rules (in iptables “syntax”). Considering that our module will be named xt_dns_name (the xt_ part conforming to the naming convention), what links these two elements is the include/xt_dns_name.h header. In it, the xt_dns_name_mtinfo structure will hold all information necessary to the kernel module to match a packet. This structure will be initialized in user space and transferred over to the xtables framework by iptables. Looking closer, we notice two fields. While name will hold the queried domain name, flags specifies what features are enabled for verification. Our module is simple and has only one match criteria, but note that iptables can invert a selection by specifying the ! symbol before it. So we can match a packet either on a queried domain name match, or a mismatch.
The plugin consists of a shared library compiled from plugin/libxt_dns_name.c. The source is broken down into four sections:
API: These are prototypes of functions that must be made available to iptables to invoke when it needs certain things done. Here is a description of each function:dns_name_mt_help(): When you invoke iptables --help, each plugin will print its own help message. This is our contribution.dns_name_mt_init(): Called before the argument parsing begins. Will zero out our xt_dns_name_mtinfo structure.dns_name_mt_parse(): Based on optarg, this function will be invoked for each argument that has something to do with our plugin. It will update the xt_dns_name_mtinfo structure accordingly on each pass.dns_name_mt_check(): Final check before sending the structure in kernel space. Will verify that all required arguments were provided.dns_name_mt_print(): When invoking iptables -L, this function will print out a rule's match criteria.dns_name_mt_save(): Given a xt_dns_name_mtinfo structure, this function will print out the CLI arguments that would generate such a function. Called upon by iptables-save.MODULE SPECIFICATION STRUCTURES: Here we declare two structures:dns_name_mt_opts : A structure defining the CLI arguments that we accept (see man 3 getopt for details).dns_name_mt_reg : A structure containing function pointers for specific tasks that the module needs to accomplish. We initialize it with the function described in the API section. This kind of structure is usually called a virtual table (or vtable).IPTABLES MODULE CALLBACKS: this is where we actually implement the functions that we added to the vtable. Here, you will search for TODOs.LIBRARY MANAGEMENT FUNCTIONS: When loaded, each library (i.e.: a .so file) can have a number of constructors defined. These constructors are functions called upon by the loader once the library was mapped in virtual memory. Our constructor will invoke xtables_register_match() in order to register the vtable with iptables, letting it know that it has yet another plugin at its disposal.
The kernel module source is organized similarly to the user space plugin. The dns_name_mt_reg structure acts as a vtable but also includes information about permissible chains and layer 3 protocols that work with our implementation. Specifically, any rule that makes use of this module can be inserted only in the OUTPUT chain, meaning that we can only catch requests originating from our localhost. Moreover, we implement support only for IPv4, not for IPv6. As we can see, this structure is used on module initialization, in dns_name_mt_init(), to register our module with the xtables framework via xt_register_match().
dns_name_check() and dns_name_mt() implement the functionalities required of our module. The former performs checks on each newly inserted rule, or at least on the part that pertains to this module. In other words, it must make sure that a valid domain name (i.e.: ”.”s replaced with length of following label, etc.) was inserted, for example. The latter function is called upon to verify if a packet matches a certain rule. Its first argument does not represent the packet itself, but a socket buffer structure (see also this, and possibly this) that contains this information, in addition to much, much more. We made sure to provide you with pointers to our xt_dns_name_mtinfo structure, but also to the beginning of the IPv4 header. However, it is up to you to implement this logic and obtain a working match module.
[student@host]$ sudo insmod xt_dns_name.ko # depending on your distro, libxt_*.so may be installed in different places [student@host]$ sudo XTABLES_LIBDIR="$(pwd):/usr/lib/xtables:/usr/lib/x86_64-linux-gnu/xtables" \ iptables \ -m dns_name \ -I OUTPUT \ --domain 'fep.grid.pub.ro' \ -j DROP [student@host]$ dig +short fep.grid.pub.ro @8.8.8.8 [student@host]$ sudo iptables -F OUTPUT [student@host]$ sudo rmmod xt_dns_name
Remember that although your host is most likely little-endian, the Internet is big-endian. So when accessing data that is larger than 1 byte (e.g.: port number), use the htons() family of functions. They should be readily available to you in the kernel module.