OmniOS IP Filter
IPFilter, also known as IPF, is a cross-platform, open source firewall which has been ported to several operating systems, including distributions based on the illumos project, FreeBSD, NetBSD, OpenBSD and Solaris.
The following is an introductory guide to get up to speed with IP Filter. This has been adapted from the FreeBSD Handbook for use with OmniOS and I have previously uploaded this to the OmniOS website.
IP Filter on OmniOS
IPFilter, also known as IPF, is a cross-platform, open source firewall which has been ported to several operating systems, including distributions based on the illumos project, FreeBSD, NetBSD, OpenBSD and Solaris.
IPF is a kernel-side firewall and NAT mechanism that can be controlled and monitored by userland programs.
Firewall rules can be set or deleted using
ipf, NAT rules can be set or deleted using
ipnat, run-time statistics for the kernel parts of IPF can be printed using
ipmon can be used to log IPF actions to the system log files.
IPF was originally written using a rule processing logic of “the last matching rule wins” and only used stateless rules. Since then, IPF has been enhanced to include the quick and keep state options.
IPF is included in the base OmniOS installation, therefore does not need to be installed.
Gather information about IPF configuration files
Before starting IPF, run the following command to determine the configuration file names and locations.
1# svccfg -s ipfilter:default listprop | grep file 2config/ipf6_config_file astring /etc/ipf/ipf6.conf 3config/ipnat_config_file astring /etc/ipf/ipnat.conf 4config/ippool_config_file astring /etc/ipf/ippool.conf 5firewall_config_default/custom_policy_file astring /etc/ipf/ipf.conf 6firewall_config_default/custom_policy_file_6 astring /etc/ipf/ipf6.conf 7restarter/logfile astring /var/svc/log/network-ipfilter:default.log
Enable IP Forwarding (optional)
If IPF is required to forward IP packets, enable IPv4 or IPv6 Forwarding, depending on requirements.
1# svcadm enable ipv4-forwarding 2# svcadm enable ipv6-forwarding
routeadm can verify these new changes:
1# routeadm 2 Configuration Current Current 3 Option Configuration System State 4--------------------------------------------------------------- 5 IPv4 routing disabled disabled 6 IPv6 routing disabled disabled 7 IPv4 forwarding enabled enabled 8 IPv6 forwarding enabled enabled 9 10 Routing services "route:default ripng:default" 11 12Routing daemons: 13 14 STATE FMRI 15 disabled svc:/network/routing/ripng:default 16 disabled svc:/network/routing/legacy-routing:ipv4 17 disabled svc:/network/routing/legacy-routing:ipv6 18 disabled svc:/network/routing/ndp:default 19 disabled svc:/network/routing/rdisc:default 20 disabled svc:/network/routing/route:default
Then, to start IPF:
1# svcadm enable network/ipfilter
IPF is now running. The default
/etc/ipf/ipf.conf file does not contain any rules. Therefore, no filtering will occur. The default
/etc/ipf/ipf.conf file is the same as having a rule set that reads:
1pass in all 2pass out all
Making changes to the ruleset whilst IPF is active
To update the firewall rules, specify the name of the updated ruleset file using IPF. The following command can be used to replace the currently running firewall rules:
1# ipf -Fa -f /etc/ipf/ipf.conf
-Fa flushes all the internal rules tables and
-f specifies the file containing the rules to load.
This provides the ability to make changes to a custom ruleset and update the running firewall with a fresh copy of the rules without having to reboot the system. This method is convenient for testing new rules as the procedure can be executed as many times as needed.
Refer to ipf(1) for details on the other flags available with this command.
Display the current running IPF ruleset
ipfstat reports on packet filter statistics and filter lists for IPF. The following command displays the current running filter lists used for both the input and output side of IPF.
1# ipfstat -io 2empty list for ipfilter(out) 3empty list for ipfilter(in)
As IPF is running the default
/etc/ipf/ipf.conf file at this time, statistics for an empty list are displayed.
IPF Rule Syntax
This section describes the IPF rule syntax used to create stateful rules. When creating rules, keep in mind that unless the quick keyword appears in a rule, every rule is read in order, with the last matching rule being the one that is applied. This means that even if the first rule to match a packet is a pass, if there is a later matching rule that is a block, the packet will be dropped. Sample rulesets can be found in
When creating rules, a # character is used to mark the start of a comment and may appear at the end of a rule, to explain that rule's function, or on its own line. Any blank lines are ignored.
The keywords which are used in rules must be written in a specific order, from left to right. Some keywords are mandatory while others are optional. Some keywords have sub-options which may be keywords themselves and also include more sub-options. The keyword order is as follows, where the words shown in uppercase represent a variable and the words shown in lowercase must precede the variable that follows it:
ACTION DIRECTION OPTIONS proto PROTO_TYPE from SRC_ADDR SRC_PORT to DST_ADDR DST_PORT TCP_FLAG|ICMP_TYPE keep state STATE
This section describes each of these keywords and their options. It is not an exhaustive list of every possible option.
Refer to ipf(4) for a complete description of the rule syntax that can be used when creating IPF rules and examples for using each keyword.
The action keyword indicates what to do with the packet if it matches that rule. Every rule must have an action.
The following actions are recognised:
block : indicates that the packet should be flagged to be dropped.
pass : will flag the packet to be let through the filter.
log : causes the packet to be logged and has no effect on whether the packet will be allowed through the filter.
count : causes the packet to be included in the accounting statistics kept by the filter, and has no effect on whether the packet will be allowed through the filter.
auth : this allows authentication to be performed by a user-space program running and waiting for packet information to validate.
call : this action is used to invoke the named function in the kernel, which must conform to a specific calling interface.
Next, each rule must explicitly state the direction of traffic using one of these keywords:
in : each packet moving through the kernel is either inbound (just been received on an interface, and moving towards the kernel's protocol processing) or outbound (transmitted or forwarded by the stack, and on its way to an interface).
out : see in.
all : the rule is essentially a synonym for "from any to any" with no other match parameters.
If the system has multiple interfaces, the interface can be specified along with the direction. An example would be
in on e1000g0.
Options are optional. However, if multiple options are specified, they must be used in the order shown here.
log : indicates that, should this be the last matching rule, the packet header will be written to the IPF log.
quick : allows "short-cut" rules in order to speed up the filter or override later rules. If a packet matches a filter rule which is marked as quick, this rule will be the last rule checked.
on : allows an interface name to be incorporated into the matching procedure. Interface names are as printed by
netstat -i. If this option is used, the rule will only match if the packet is going through that interface in the specified direction (in/out).
The protocol type is optional. However, it is mandatory if the rule needs to specify a SRC_PORT or a DST_PORT as it defines the type of protocol. When specifying the type of protocol, use the proto keyword followed by either a protocol number or name from
/etc/protocols . Example protocol names include tcp , udp , or icmp.
If PROTO_TYPE is specified but no SRC_PORT or DST_PORT is specified, all port numbers for that protocol will match that rule.
The from keyword is mandatory and is followed by a keyword which represents the source of the packet. The source can be a hostname, an IP address followed by the CIDR mask, an address pool, or the keyword all. Refer to ipf(1) for examples.
There is no way to match ranges of IP addresses which do not express themselves easily using the dotted numeric form/mask-length notation.
The port number of the source is optional. However, if it is used, it requires PROTO_TYPE to be first defined in the rule. The port number must also be preceded by the proto keyword.
A number of different comparison operators are supported: = (equal to), != (not equal to), < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to).
To specify port ranges, place the two port numbers between <> (less than and greater than ), >< (greater than and less than ), or : (greater than or equal to and less than or equal to).
The to keyword is mandatory and is followed by a keyword which represents the destination of the packet. Similar to SRC_ADDR, it can be a hostname, an IP address followed by the CIDR mask, an address pool, or the keyword all.
Similar to SRC_PORT, the port number of the destination is optional. However, if it is used, it requires PROTO_TYPE to be first defined in the rule. The port number must also be preceded by the proto keyword.
If tcp is specified as the PROTO_TYPE, flags can be specified as letters, where each letter represents one of the possible TCP flags used to determine the state of a connection. Possible values are: S (SYN), A (ACK), P (PSH), F (FIN), U (URG), R (RST), C (CWN), and E (ECN).
If icmp is specified as the PROTO_TYPE, the ICMP type to match can be specified. Refer to ipf(1) for the allowable types.
If a pass rule contains keep state , IPF will add an entry to its dynamic state table and allow subsequent packets that match the connection. IPF can track state for TCP, UDP, and ICMP sessions. Any packet that IPF can be certain is part of an active session, even if it is a different protocol, will be allowed.
In IPF, packets destined to go out through the interface connected to the public Internet are first checked against the dynamic state table. If the packet matches the next expected packet comprising an active session conversation, it exits the firewall and the state of the session conversation flow is updated in the dynamic state table. Packets that do not belong to an already active session are checked against the outbound ruleset.
Packets coming in from the interface connected to the public Internet are first checked against the dynamic state table. If the packet matches the next expected packet comprising an active session, it exits the firewall and the state of the session conversation flow is updated in the dynamic state table. Packets that do not belong to an already active session are checked against the inbound ruleset.
Several keywords can be added after keep state . If used, these keywords set various options that control stateful filtering, such as setting connection limits or connection age. Refer to ipf(1) for the list of available options and their descriptions.
IPF Example Ruleset
This section demonstrates how to create an example ruleset which only allows services that match pass rules and blocks all others.
OmniOS uses the loopback interface (lo0) and the IP address 127.0.0.1 for internal communication. The firewall ruleset must contain rules to allow free movement of these internally used packets:
1# no restrictions on loopback interface 2pass in quick on lo0 all 3pass out quick on lo0 all
The public interface connected to the Internet is used to authorise and control access of all outbound and inbound connections. If one or more interfaces are cabled to private networks, those internal interfaces may require rules to allow packets originating from the LAN to flow between the internal networks or to the interface attached to the Internet. The ruleset should be organised into three major sections: any trusted internal interfaces, outbound connections through the public interface, and inbound connections through the public interface.
These two rules allow all traffic to pass through a trusted LAN interface named
1# no restrictions on inside LAN interface for private network 2pass out quick on e1000g0 all 3pass in quick on e1000g0 all
The rules for the public interface's outbound and inbound sections should have the most frequently matched rules placed before less commonly matched rules, with the last rule in the section blocking and logging all packets for that interface and direction.
This set of rules defines the outbound section of the public interface named
rge0 . These rules keep state and identify the specific services that internal systems are authorised for public Internet access. All the rules use quick and specify the appropriate port numbers and, where applicable, destination addresses.
1# interface facing Internet (outbound) 2# Matches session start requests originating from or behind the 3# firewall, destined for the Internet. 4# Allow outbound access to public DNS servers. 5# Replace x.x.x. with address listed in /etc/resolv.conf. 6# Repeat for each DNS server. 7pass out quick on rge0 proto tcp from any to x.x.x. port = 53 flags S keep state 8pass out quick on rge0 proto udp from any to xxx port = 53 keep state 9 10# Allow access to ISP's specified DHCP server for cable or DSL networks. 11# Use the first rule, then check log for the IP address of DHCP server. 12# Then, uncomment the second rule, replace z.z.z.z with the IP address, 13# and comment out the first rule 14pass out log quick on rge0 proto udp from any to any port = 67 keep state 15 16#pass out quick on rge0 proto udp from any to z.z.z.z port = 67 keep state 17# Allow HTTP and HTTPS 18pass out quick on rge0 proto tcp from any to any port = 80 flags S keep state 19pass out quick on rge0 proto tcp from any to any port = 443 flags S keep state 20 21# Allow email 22pass out quick on rge0 proto tcp from any to any port = 110 flags S keep state 23pass out quick on rge0 proto tcp from any to any port = 25 flags S keep state 24 25# Allow NTP 26pass out quick on rge0 proto tcp from any to any port = 37 flags S keep state 27 28# Allow FTP 29pass out quick on rge0 proto tcp from any to any port = 21 flags S keep state 30# Allow SSH 31pass out quick on rge0 proto tcp from any to any port = 22 flags S keep state 32 33# Allow ping 34pass out quick on rge0 proto icmp from any to any icmp-type 8 keep state 35 36# Block and log everything else 37block out log first quick on rge0 all
This example of the rules in the inbound section of the public interface blocks all undesirable packets first. This reduces the number of packets that are logged by the last rule.
1# interface facing Internet (inbound) 2# Block all inbound traffic from non-routable or reserved address spaces 3block in quick on rge0 from 192.168.0.0/16 to any #RFC 1918 private IP 4block in quick on rge0 from 172.16.0.0/12 to any #RFC 1918 private IP 5block in quick on rge0 from 10.0.0.0/8 to any #RFC 1918 private IP 6block in quick on rge0 from 127.0.0.0/8 to any #loopback 7block in quick on rge0 from 0.0.0.0/8 to any #loopback 8block in quick on rge0 from 169.254.0.0/16 to any #DHCP auto-config 9block in quick on rge0 from 192.0.2.0/24 to any #reserved for docs 10block in quick on rge0 from 184.108.40.206/23 to any #Sun cluster interconnect 11block in quick on rge0 from 220.127.116.11/3 to any #Class D & E multicast 12 13# Block fragments and too short tcp packets 14block in quick on rge0 all with frags 15block in quick on rge0 proto tcp all with short 16 17# block source routed packets 18block in quick on rge0 all with opt lsrr 19block in quick on rge0 all with opt ssrr 20 21# Block OS fingerprint attempts and log first occurrence 22block in log first quick on rge0 proto tcp from any to any flags FUP 23 24# Block anything with special options 25block in quick on rge0 all with ipopts 26 27# Block public pings and ident 28block in quick on rge0 proto icmp all icmp-type 8 29block in quick on rge0 proto tcp from any to any port = 113 30 31# Block incoming Netbios services 32block in log first quick on rge0 proto tcp/udp from any to any port = 137 33block in log first quick on rge0 proto tcp/udp from any to any port = 138 34block in log first quick on rge0 proto tcp/udp from any to any port = 139 35block in log first quick on rge0 proto tcp/udp from any to any port = 81
Any time there are logged messages on a rule with the log first option, run
ipfstat -hio to evaluate how many times the rule has been matched. A large number of matches may indicate that the system is under attack.
The rest of the rules in the inbound section define which connections are allowed to be initiated from the Internet.
The last rule denies all connections which were not explicitly allowed by previous rules in this section.
1# Allow traffic in from ISP's DHCP server. Replace z.z.z.z with 2# the same IP address used in the outbound section. 3pass in quick on rge0 proto udp from z.z.z.z to any port = 68 keep state 4 5# Allow public connections to specified internal web server 6pass in quick on rge0 proto tcp from any to x.x.x.x port = 80 flags S keep state 7 8# Block and log only first occurrence of all remaining traffic. 9block in log first quick on rge0 all
NAT sets up mapping rules that translate source and destination IP addresses into other Internet or intranet addresses. These rules modify the source and destination addresses of incoming or outgoing IP packets and send the packets on. You can also use NAT to redirect traffic from one port to another port. NAT maintains the integrity of the packet during any modification or redirection done on the packet.
You can create NAT rules either at the command line, using the
ipnat command, or in a NAT configuration file. You must create the NAT configuration file and set its pathname as the value of the config/ipnat_config_file property of the service. The default file is
/etc/ipf/ipnat.conf and is loaded when IPF is started, if the file exists.
NAT rules are flexible and can accomplish many different things to fit the needs of both commercial and home users. The rule syntax presented here has been simplified to demonstrate common usage. For a complete rule syntax description, refer to ipnat(4).
The basic syntax for a NAT rule is as follows, where map starts the rule and IF should be replaced with the name of the external interface:
1map IF LAN_IP_RANGE -> PUBLIC_ADDRESS
The LAN_IP_RANGE is the range of IP addresses used by internal clients. Usually, it is a private address range such as 192.168.1.0/24 . The PUBLIC_ADDRESS can either be the static external IP address or the keyword 0/32 which represents the IP address assigned to IF.
In IPF, when a packet arrives at the firewall from the LAN with a public destination, it first passes through the outbound rules of the firewall ruleset. Then, the packet is passed to the NAT ruleset which is read from the top down, where the first matching rule wins. IPF tests each NAT rule against the packet's interface name and source IP address. When a packet's interface name matches a NAT rule, the packet's source IP address in the private LAN is checked to see if it falls within the IP address range specified in LAN_IP_RANGE. On a match, the packet has its source IP address rewritten with the public IP address specified by PUBLIC_ADDRESS. IPF posts an entry in its internal NAT table so that when the packet returns from the Internet, it can be mapped back to its original private IP address before being passed to the firewall rules for further processing.
Updating the NAT ruleset and viewing NAT statistics
Whenever the file containing the NAT rules is edited, run
ipnat -CF to delete the current NAT rules and flush the contents of the dynamic translation table. Include
-f and specify the name of the NAT ruleset to load:
1# ipnat -CF -f /etc/ipnat.rules
To display the NAT statistics:
1# ipnat -s
To list the NAT table's current mappings:
1# ipnat -l
To turn verbose mode on and display information relating to rule processing and active rules and table entries:
1# ipnat -v
Viewing IPF Statistics
IPF includes ipfstat(1) which can be used to retrieve and display statistics which are gathered as packets match rules as they go through the firewall. Statistics are accumulated since the firewall was last started or since the last time they were reset to zero using
ipf -Z .
The default ipfstat output looks like this:
1# ipfstat 2bad packets: in 0 out 0 3 IPv6 packets: in 0 out 0 4 input packets: blocked 51 passed 302702 nomatch 292725 counted 0 short 0 5output packets: blocked 2962 passed 292402 nomatch 281745 counted 0 short 0 6 input packets logged: blocked 51 passed 2 7output packets logged: blocked 2962 passed 2 8 packets logged: input 0 output 0 9 log failures: input 0 output 0 10fragment state(in): kept 0 lost 0 not fragmented 0 11fragment state(out): kept 0 lost 0 not fragmented 0 12packet state(in): kept 0 lost 0 13packet state(out): kept 693 lost 0 14ICMP replies: 0 TCP RSTs sent: 0 15Invalid source(in): 0 16Result cache hits(in): 1219 (out): 1957 17IN Pullups succeeded: 207 failed: 0 18OUT Pullups succeeded: 662 failed: 0 19Fastroute successes: 0 failures: 0 20TCP cksum fails(in): 0 (out): 0 21IPF Ticks: 134173 22Packet log flags set: (0) 23 none
Several options are available. When supplied with either
-i for inbound or
-o for outbound, the command will retrieve and display the appropriate list of filter rules currently installed and in use by the kernel. To also see the rule numbers, include
-n. For example, ipfstat
-on displays the outbound rules table with rule numbers:
1@1 pass out on e1000g0 from any to any 2@2 block out on rge0 from any to any 3@3 pass out quick on rge0 proto tcp/udp from any to any keep state
-h to prefix each rule with a count of how many times the rule was matched. For example,
ipfstat -oh displays the outbound internal rules table, prefixing each rule with its usage count:
12451423 pass out on e1000g0 from any to any 2354727 block out on rge0 from any to any 3430918 pass out quick on rge0 proto tcp/udp from any to any keep state
ipmon, which can be used to write the firewall's logging information in a human readable format.
By default, all log information for IPF is recorded in the syslogd file. You should set up a log file to record IPF traffic information separately from other data that might be logged in the default log file.
/etc/syslog.conf file by adding the following two lines:
1# Save IPFilter log output to its own file 2local0.debug /var/log/ipfilter
Note: Make sure to use the Tab key, not the Spacebar, to separate
Create the new log file & restart the system-log service.
1# touch /var/log/ipfilter 2# svcadm restart system-log
ipmon -Ds mode uses
local0 as the logging facility. The following logging levels can be used to further segregate the logged data:
LOG_INFO : packets logged using the "log" keyword as the action rather than pass or block. LOG_NOTICE : packets logged which are also passed LOG_WARNING : packets logged which are also blocked LOG_ERR : packets which have been logged and which can be considered short due to an incomplete header
Logging provides the ability to review, after the fact, information such as which packets were dropped, what addresses they came from, and where they were going. This information is useful in tracking down attackers.
Once the logging facility is enabled, IPF will only log the rules which contain the log keyword. The firewall administrator decides which rules in the ruleset should be logged and normally only deny rules are logged. It is customary to include the log keyword in the last rule in the ruleset. This makes it possible to see all the packets that did not match any of the rules in the ruleset.
View log files with
View the state, NAT, or normal log files. To view a log file, type the following command, using the appropriate option:
1# ipmon -o [S|N|I]
S : Displays the state log file.
N : Displays the NAT log file.
I : Displays the normal IP log file.
To view all state, NAT, and normal log files, use all the options. Sample output is as follows:
1# ipmon -o SNI 228/06/2020 18:57:47.089622 @1 NAT:MAP 192.168.1.100,43183 <- -> 192.168.43.100,43183 [18.104.22.168,53] 328/06/2020 18:57:47.089607 STATE:NEW 192.168.1.100,43183 -> 22.214.171.124,53 PR udp 428/06/2020 18:57:52.179848 rge0 @0:12 b 192.168.1.100,40702 -> 126.96.36.199,993 PR tcp len 20 60 -S OUT 528/06/2020 18:57:53.239346 rge0 @0:12 b 192.168.1.100,40702 -> 188.8.131.52,993 PR tcp len 20 60 -S OUT 628/06/2020 18:57:54.169657 rge0 @0:12 b 192.168.1.100,8698 -> 184.108.40.206,123 PR udp len 20 76 OUT 728/06/2020 18:57:55.169376 rge0 @0:12 b 192.168.1.100,8698 -> 220.127.116.11,123 PR udp len 20 76 OUT 828/06/2020 18:57:55.287404 rge0 @0:12 b 192.168.1.100,40702 -> 18.104.22.168,993 PR tcp len 20 60 -S OUT 928/06/2020 18:57:54.775256 @1 NAT:MAP 192.168.1.100,50365 <- -> 192.168.43.100,50365 [22.214.171.124,53] 1028/06/2020 18:57:54.775677 @1 NAT:MAP 192.168.1.100,35506 <- -> 192.168.43.100,35506 [126.96.36.199,53] 1128/06/2020 18:57:54.842030 @1 NAT:MAP 192.168.1.100,42866 <- -> 192.168.43.100,42866 [188.8.131.52,443] 1228/06/2020 18:57:55.517148 @1 NAT:MAP 192.168.1.100,33754 <- -> 192.168.43.100,33754 [184.108.40.206,53] 1328/06/2020 18:57:54.775242 STATE:NEW 192.168.1.100,50365 -> 220.127.116.11,53 PR udp 1428/06/2020 18:57:54.775665 STATE:NEW 192.168.1.100,35506 -> 18.104.22.168,53 PR udp 1528/06/2020 18:57:54.842024 STATE:NEW 192.168.1.100,42866 -> 22.214.171.124,443 PR tcp 1628/06/2020 18:57:55.517141 STATE:NEW 192.168.1.100,33754 -> 126.96.36.199,53 PR udp
Messages generated by ipmon consist of data fields separated by white space. Fields common to all messages are:
- The date of packet receipt.
- The time of packet receipt. This is in the form HH:MM:SS.F, for hours, minutes, seconds, and fractions of a second.
- The name of the interface that processed the packet.
- The group and rule number of the rule in the format @0:17.
- The action: p for passed, b for blocked, S for a short packet, n did not match any rules, and L for a log rule.
- The addresses written as three fields: the source address and port separated by a comma, the -> symbol, and the destination address and port. For example: 188.8.131.52,80 -> 184.108.40.206,1722.
- PR followed by the protocol name or number: for example, PR tcp.
- len followed by the header length and total length of the packet: for example, len 20 40.
If the packet is a TCP packet, there will be an additional field starting with a hyphen followed by letters corresponding to any flags that were set. Refer to ipf(4) for a list of letters and their flags.
If the packet is an ICMP packet, there will be two fields at the end: the first always being “icmp” and the next being the ICMP message and sub-message type, separated by a slash. For example:
icmp 3/3 for a port unreachable message.