Maximizing IP Filter performance with group statements


Solaris 10 ships with the IP filter firewall package, which is a fast, flexible and verstile firewall engine. IP filter by default will read /etc/ipf/ipf.conf to get the list of rules (e.g., pass in all) to apply to traffic as it traverses the interfaces in a system, and these rules will be applied in a top down fashion. If rules are not optimally ordered and grouped, CPU cycles will be wasted, since IP filter will have to check the packet against a set of rules that don’t apply to the type of traffic that is passing through the interface. The following rule set is a good example of a poorly written IP filter configuration file:

# Allow all packets to flow on the loopback interface (wish we had skip
on here)
pass in quick on lo0 all
pass out quick on lo0 all

# Set up default block all policy for eri0
block in log on eri0 all

# Set up default block all policy for eri1
block in log on eri1 all

# Apply rules
pass in quick on eri0 proto tcp from any to 192.168.1.3/32 port = 22
keep state
pass in quick on eri0 proto udp from any to 192.168.1.3/32 port = 53
keep state
pass in quick on eri0 proto tcp from any to 192.168.1.3/32 port = 80
keep state
pass in quick on eri0 proto tcp from any to 192.168.1.3/32 port = 110
keep state
pass in quick on eri0 proto tcp from any to 192.168.1.3/32 port = 143
keep state
pass in quick on eri0 proto tcp from any to 192.168.1.3/32 port = 443
keep state
pass in quick on eri1 proto tcp from any to 192.168.1.3/32 port = 993
keep state
pass in quick on eri1 proto tcp from any to 192.168.1.3/32 port = 995
keep state

When a sytem using this rule set receives a new connection on TCP port 995 (secure POP), the IP Filter firewall engine will need to evaluate 8 rules prior to allowing the connection through. As the number of rules increases, the CPU overhead required to enforce policy can quickly exceed the resources in a system. IP filter solves this problem by allowing rules to be grouped and assigned to an object (e.g, network interface) with the “group” statement. The following example shows how to arrange the above rules with better performing group statements:

# Allow all packets to flow on the loopback interface (wish we had skip
on here)
pass in quick on lo0 all
pass out quick on lo0 all

# Set up default block all policy for eri0, and associate rule group
100 to interface
block in log on eri0 all head 100

# Set up default block all policy for eri1, and associate rule group
200 to interface
block in log on eri1 all head 200

# Associate the following rules with group 100, and order by usage
pass in quick proto udp from any to 192.168.1.3/32 port = 53 keep state
group 100
pass in quick proto tcp from any to 192.168.1.3/32 port = 80 keep state
group 100
pass in quick proto tcp from any to 192.168.1.3/32 port = 443 keep
state group 100
pass in quick proto tcp from any to 192.168.1.3/32 port = 110 keep
state group 100
pass in quick proto tcp from any to 192.168.1.3/32 port = 143 keep
state group 100
pass in quick proto tcp from any to 192.168.1.3/32 port = 22 keep state
group 100

Associate the following rules with group 200, and order by usage
pass in quick proto tcp from any to 192.168.1.3/32 port = 993 keep
state group 200
pass in quick proto tcp from any to 192.168.1.3/32 port = 995 keep
state group 200

When this rule set is used, a connection to TCP port 995 will be matched by the second rule in group 200. Since we avoided six rule checks in this example, IP filter had to perform less work to get the same results (an allowed connection). This might not seem like a big deal on a workstation or lightly loaded server, but firewalls that handle thousands of connections each minute can definitely benefit from group statements and rule placement optimizations.

This article was posted by Matty on 2006-03-29 21:08:00 -0400 -0400