The configuration of the PERS target is done in userspace with the iptables command and an associated dynamic library for specific parameters. This library adds new options for setting up the PERS target; one of the options allows the user to specify a configuration file containing all the parameters needed to emulate a particular operating system. Hence by using different configuration files for each different netfilter rule, one can easily choose to look like a particular OS for some sources or destination addresses, for a specific interface, and/or for other matching criterias.
Command line options are passed to the target when adding a rule using it, for instance with a syntax like the following one:
iptables -A <chain> -s <source> -d <destination> -j PERS <options>
[Refer to iptables documentation for more information on the global syntax]
The following options are recognized by the library:
The parameters for emulation of a particular operating system are specified in the configuration file. This file has a syntax similar to named.conf, inspired from C. Options are grouped together in logical blocks (delimited with { and }), each block corresponding to a different kind of packet rewriting operation. Each option is composed of an identifier followed by one or more arguments, and ended by a ;. Options and blocks can be specified in any order.
The first item of the configuration file is an identification for the system being described. It is a string at most 20 characters long. Syntax is as follows:
id "FakeOS";
These parameters are grouped together in a block named tcp. Example:
tcp {
incoming yes;
outgoing no;
max-window 65536;
}
The incoming parameter sets whether you wish to enable TCP connections modifications (ISN, window size, options) for incoming connections to the protected zone. It can either be set to yes or no.
The outgoing parameter has the same meaning but for outgoing connections
The max-window parameter controls window size rewriting for TCP packets from connections matching the previous options. If it is set to a non-null value, then for every new connection with a window size greater than the given value, an offset is computed and applied to every packet to set the window size below the specified value for the length of the connection.
These parameters are grouped together in a block named tcp_isn. Example:
tcp_isn {
type random-inc 10000;
initial-value 2600;
}
The type parameter sets the type of generator to emulate and possible options for it. The following types are supported:
The initial-value sets the initial value to use for the emulated generator. A numeric value can be specified or random which will pick this number randomly when loading the rule. This parameter is of little importance on strong generators.
These parameters are grouped together in a block named ip_id. Example:
ip_id {
type broken-inc 1;
initial-value 2600;
}
The type parameter sets the type of generator to emulate and possible options for it. The same types as for the ISN generator are available, with an additional one, broken-inc number: it is an incremented counter of the specified value, but the result is saved in the packet in little endian order instead of network order.
These parameters are grouped together in a block named tcp_options. Example:
tcp_options {
keep-unknown yes;
keep-unused no;
isolated-packets yes;
timestamp-scale 100;
code {
<code...>
}
}
This block defines how TCP options of a packet should be rewritten. The code subsection contains a simple program written in a langage close to C (see below), which is compiled by the libipt_PERS.so module. This code is passed to the virtual machine that fills an option buffer (part of its state) as it runs it. When the execution is over, the new options buffer is used to replace the original options buffer of the packet.
The keep-unknown parameter specifies if "unknown" options in the original packet (hence that can't be handled in the code) should be added at the end of the new options buffer so they are kept. It can be set to either yes or no.
The keep-unused parameter specifies if options from the original packet that haven't been used (probed or copied) by the code should be added at the end of the new options buffer so they are kept. It can be set to either yes or no. This allows one to use a very simple code to reorder a few options while keeping the other ones functionnal.
The isolated-packets parameter specifies if options reordering should be performed for packets that do not belong to any known connection. It can be set to either yes or no. (defaults to no).
The timestamp-scale parameter specifies if the timestamp options of TCP packets related to the local machine should be changed to a new frequency. Its argument is the new frequency to use. (if it is null or equal to the base frequency it is ignored).
These parameters are grouped together in a block named tcp_decoy. Example:
tcp_decoy {
code {
<code...>
}
}
This block only contains a code subsection like the previous one, that defines tests to perform on packets in order to recognize pathological packets from analysis tools and decide the way to handle them. The language used is the same as before (see below).
These parameters are grouped together in a block named udp_unreach. Example:
udp_unreach {
reply yes;
df no;
max-len 56;
tos 0;
mangle-original {
ip-len 21;
ip-id same;
ip-csum zero;
udp-len 308;
udp-csum zero;
udp-data same;
}
}
The reply parameter sets if you want an ICMP "port unreachable" message to be sent when receiving an UDP datagram for a port not listening. It can be set to either yes or no. The other parametres of this block only apply if this is enabled.
The df parameters specifies whether the "Don't Fragment" bit should be set on generated ICMP messages.
The max-len parameter specifies the maximum length of the generated ICMP messages.
The tos parameters specifies the value for the "Type Of service" field of the IP header of the generated ICMP messages.
When sending an ICMP "port unreachable" message, part of the original packet is sent back along. The mangle-original subsection specify how this part should be handled and mangled. The following parameters are available:
The tcp_options and tcp_decoy blocks both have a code subsection that can contain a program. As seen previously, this program is compiled by the dynamic library extending iptables into pseudo-code that is interpreted in the kernel module by a simple virtual machine. It operates over a TCP packet and handles an internal state, composed of the following:
Code from the tcp_options subsection is applied to an incoming TCP packet, and after running the program the options buffer from the virtual machine state is used as the new options buffer for that packet.
Code from the tcp_decoy section is also applied to an incoming TCP packet, but the packet is not modifed. Depending on the result of running the program a new packet can be built from the state of the virtual machine and sent to the source of the original packet. The original packet can also be dropped, or passed as is to the next rules.
These programs are written in a language close to C. Some conditionnal tests can be performed on the original packets in order to adjust behavior depending on its contents/status
A test looks like:
if (test) {
<action>
}
or
if (test) {
<action>
} else {
<action>
}
A test is composed of one or more conditions, separated by logical operators && and ||, and grouped together with parentheses where needed. The following conditions are available:
The language has several instructions to handle the internal state of the virtual machine:
Hence, such a language allows one to precisely define behavior to reorder options, and also to generate appropriate replies for pathological tests from network analysis tools.
But we can notice the following points: