The Network Analysis Routines (NAR) perform a set based analysis of packet flows in a network. The NAR can be used as a standalone program that calculates the sets of packets that can flow between network segments. They can also be used by an Otter program to answer questions about sets of packets. The fundamental data structure used by this program is a set of bit strings.
Sets of bit strings can be described as IP packet sets or action lists. IP packet sets describe sets of bit strings in a form similar to that used by packet filters. Action lists provide a more general notation that does not favor the description of filters. Action lists are used by set operations, and IP packet sets can be translated into action lists.
An IP packet set is a square bracket delimited, comma separated, sequence of permit and deny directives. The syntax of the two directives is:
permit(<src_ip>,<src_mask>,<src_ports>,
<dst_ip>,<dst_mask>,<dst_ports>,<protocols>)deny(<src_ip>,<src_mask>,<src_ports>,
<dst_ip>,<dst_mask>,<dst_ports>,<protocols>)
The syntax used to give the source and destination IP address and their masks is a quoted IP address, such as:
"129.83.10.1"
Ports and protocols are given by integer range lists. An integer range list is a square bracket delimited, comma separated, sequence of integer ranges. The syntax of an integer range is:
[ <lower> | <upper> ]
When <lower> is the same as <upper>, the range can be written using a single integer.
Ports are 16-bit integers in the range 0-65355, and protocols are 2-bit integers. The protocol encoding is: TCP is 3, UDP is 2, ICMP is 1, and all others are 0.
A complete example follows.
% A firewall filter
% reject packets with 129.83.*.* as its source IP.
[deny("129.83.0.0", "0.0.255.255", [[0 | 65535]],
"0.0.0.0", "0.0.0.0", [[0 | 65535]], [[0|3]]),
% reject packets with 129.29.*.* as its source IP.
deny("129.29.0.0", "0.0.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [[0 | 65535]], [[0|3]]),
% reject packets that use the SNMP port in the destination.
deny("0.0.0.0", "255.255.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [161], [[0|3]]), % snmp port is 161
% accept everything else.
permit("0.0.0.0", "255.255.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [[0 | 65535]], [[0|3]])]
An action list is another syntax for describing sets of bit strings. An action list is a square bracket delimited, comma separated, sequence of accept and reject directives.
Each directive contains two bit strings. A bit string is represented by a string of hexadecimal digits. An example:
fa1
An accept directive is written:
accept(<pattern>,<mask>)
A bit string is included in the set if it matches the bits in
<pattern> in the positions given by the on bits in the
<mask>. Thus accept(1,5) accepts any bit string
that has its first bit on and its third bit off.
A reject directive is written:
reject(<pattern>,<mask>)
A bit string is removed from the set if it matches the bits in <pattern> in the positions given by the on bits in the <mask>.
An action list is a comma separated list of directives surrounded by square brackets. An example:
[accept(f,f), accept(0,3), accept(2,7), accept(6,f)]
Here is another way to describe the same set of bit strings.
[reject(e,f), accept(0,1), accept(f,f)]
A frame set is an action list that, in principle, could have been described as an IP packet set.
A filter geometry describes the location of filters in a network. It is used to compute the packets that can flow between network segments. Given a packet filter that allows some packets on <source> to flow to <destination> which is defined by <actions>, this part of a filter geometry is given by:
reachable(<source>,<destination>,<actions>).
The source and destination may identify a network segment, however, they also may identify other parts of a network. For example, in a host that operates as a router, filtering may operate on more than one interface. In this case, either the source or the destination may identify a host.
A complete example:
reachable(a,b,[accept(f,f),accept(0,3),
accept(2,7),accept(6,f)]).
reachable(b,a,[reject(e,f),accept(0,1),
accept(f,f)]).
reachable(b,c,[accept(0,1)]).
Note that the filter can also described as an IP packet set.
reachable(a,b, ippkts(
[deny("129.83.0.0", "0.0.255.255", [[0 | 65535]],
"0.0.0.0", "0.0.0.0", [[0 | 65535]], [[0|3]]),
deny("129.29.0.0", "0.0.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [[0 | 65535]], [[0|3]]),
deny("0.0.0.0", "255.255.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [161], [[0|3]]), % snmp port is 161
permit("0.0.0.0", "255.255.255.255", [[0 | 65535]],
"0.0.0.0", "255.255.255.255", [[0 | 65535]], [[0|3]])])).
The nar program takes a filter geometry and produces a
new filter geometry that is the result of propagating all of the sets
through all possible packet filters. For example, the result of
running the program on the previous filter geometry specified using
action lists is:
reachable(b,c,[accept(0,1)]).
reachable(b,a,[accept(f,f),reject(e,f),
accept(0,1)]).
reachable(a,c,[reject(e,f),accept(0,1)]).
reachable(a,b,[accept(f,f),reject(e,f),
accept(0,1)]).
Notice that no packets flow from source c, as one
would expect, but packets do flow from a to c.
The NAR can be connected to Otter via a socket and used to rewrite Otter terms. The NAR functions are invoked through the savant interface. Note that both IP packet sets and action lists are legal terms in Otter. To ask the NAR to rewrite the IP packet set <ip_pkts> into an action list, one writes:
$SAVANT("/tmp/savant", ippkts(<ip_pkts>))
If <al1> and <al2> are two action lists, one can find out if they intersect or one is a subset of the other by writing:
$SAVANT("/tmp/savant", not_empty_intersection(<al1>, <al2>))$SAVANT("/tmp/savant", subset(<al1>, <al2>))
The NAR will rewrite these terms into one of $T or
$F.
When the NAR program is not in server mode, it performs the frame
propagation computation. When the NAR program is in server mode
(-srv supplied), it implements the savant.
The usage message for the nar program follows:
$ nar -help nar: unknown option `--help'. Usage: nar [options] [input [output]] -utn int - unique table number (default 1) -uts int - unique table size (default 509) -ctn int - computed table number (default 1) -cts int - computed table size (default 509) -srv str - path of a Unix domain socket (input and output args ignored) -- - treat remaining args as file names
The table number and size parameters refer to the hash tables used in the BDD module. For most users, the default values will suffice.
The input to the NAR program must be in pure prefix notation and
cannot use list notation. You can convert a file into pure prefix
notation using the -d switch with Otter, and convert from
pure prefix notation into list notation notation using the -p
switch with Otter.