1698 lines
59 KiB
Groff
1698 lines
59 KiB
Groff
.\" $NetBSD: ipf.5,v 1.5 2015/12/28 19:32:22 khorben Exp $
|
|
.\"
|
|
.TH IPF 5
|
|
.SH NAME
|
|
ipf, ipf.conf \- IPFilter firewall rules file format
|
|
.SH DESCRIPTION
|
|
.PP
|
|
The ipf.conf file is used to specify rules for the firewall, packet
|
|
authentication and packet accounting components of IPFilter. To load rules
|
|
specified in the ipf.conf file, the ipf(8) program is used.
|
|
.PP
|
|
For use as a firewall, there are two important rule types: those that block
|
|
and drop packets (block rules) and those that allow packets through (pass
|
|
rules.) Accompanying the decision to apply is a collection of statements
|
|
that specify under what conditions the result is to be applied and how.
|
|
.PP
|
|
The simplest rules that can be used in ipf.conf are expressed like this:
|
|
.PP
|
|
.nf
|
|
block in all
|
|
pass out all
|
|
.fi
|
|
.PP
|
|
Each rule must contain at least the following three components
|
|
.RS
|
|
.IP *
|
|
a decision keyword (pass, block, etc.)
|
|
.IP *
|
|
the direction of the packet (in or out)
|
|
.IP *
|
|
address patterns or "all" to match any address information
|
|
.RE
|
|
.SS Long lines
|
|
.PP
|
|
For rules lines that are particularly long, it is possible to split
|
|
them over multiple lines implicity like this:
|
|
.PP
|
|
.nf
|
|
pass in on bgeo proto tcp from 1.1.1.1 port > 1000
|
|
to 2.2.2.2 port < 5000 flags S keep state
|
|
.fi
|
|
.PP
|
|
or explicitly using the backslash ('\\') character:
|
|
.PP
|
|
.nf
|
|
pass in on bgeo proto tcp from 1.1.1.1 port > 1000 \\
|
|
to 2.2.2.2 port < 5000 flags S keep state
|
|
.fi
|
|
.SS Comments
|
|
.PP
|
|
Comments in the ipf.conf file are indicated by the use of the '#' character.
|
|
This can either be at the start of the line, like this:
|
|
.PP
|
|
.nf
|
|
# Allow all ICMP packets in
|
|
pass in proto icmp from any to any
|
|
.fi
|
|
.PP
|
|
Or at the end of a like, like this:
|
|
.PP
|
|
.nf
|
|
pass in proto icmp from any to any # Allow all ICMP packets in
|
|
.fi
|
|
.SH Firewall rules
|
|
.PP
|
|
This section goes into detail on how to construct firewall rules that
|
|
are placed in the ipf.conf file.
|
|
.PP
|
|
It is beyond the scope of this document to describe what makes a good
|
|
firewall rule set or which packets should be blocked or allowed in.
|
|
Some suggestions will be provided but further reading is expected to
|
|
fully understand what is safe and unsafe to allow in/out.
|
|
.SS Filter rule keywords
|
|
.PP
|
|
The first word found in any filter rule describes what the eventual outcome
|
|
of a packet that matches it will be. Descriptions of the many and various
|
|
sections that can be used to match on the contents of packet headers will
|
|
follow on below.
|
|
.PP
|
|
The complete list of keywords, along with what they do is as follows:
|
|
.RS
|
|
.HP
|
|
pass
|
|
rules that match a packet indicate to ipfilter that it should be
|
|
allowed to continue on in the direction it is flowing.
|
|
.HP
|
|
block
|
|
rules are used when it is desirable to prevent a packet from going
|
|
any further. Packets that are blocked on the "in" side are never seen by
|
|
TCP/IP and those that are blocked going "out" are never seen on the wire.
|
|
.HP
|
|
log
|
|
when IPFilter successfully matches a packet against a log rule a log
|
|
record is generated and made available for ipmon(8) to read. These rules
|
|
have no impact on whether or not a packet is allowed through or not.
|
|
So if a packet first matched a block rule and then matched a log rule,
|
|
the status of the packet after the log rule is that it will still be
|
|
blocked.
|
|
.HP
|
|
count
|
|
rules provide the administrator with the ability to count packets and
|
|
bytes that match the criteria laid out in the configuration file.
|
|
The count rules are applied after NAT and filter rules on the inbound
|
|
path. For outbound packets, count rules are applied before NAT and
|
|
before the packet is dropped. Thus the count rule cannot be used as
|
|
a true indicator of link layer
|
|
.HP
|
|
auth
|
|
rules cause the matching packet to be queued up for processing by a
|
|
user space program. The user space program is responsible for making
|
|
an ioctl system call to collect the information about the queued
|
|
packet and another ioctl system call to return the verdict (block,
|
|
pass, etc) on what to do with the packet. In the event that the queue
|
|
becomes full, the packets will end up being dropped.
|
|
.HP
|
|
call
|
|
provides access to functions built into IPFilter that allow for more
|
|
complex actions to be taken as part of the decision making that goes
|
|
with the rule.
|
|
.HP
|
|
decapsulate
|
|
rules instruct ipfilter to remove any
|
|
other headers (IP, UDP, AH) and then process what is inside as a new packet.
|
|
For non-UDP packets, there are builtin checks that are applied in addition
|
|
to whatever is specified in the rule, to only allow decapsulation of
|
|
recognised protocols. After decapsulating the inner packet, any filtering
|
|
result that is applied to the inner packet is also applied to the other
|
|
packet.
|
|
.PP
|
|
The default way in which filter rules are applied is for the last
|
|
matching rule to be used as the decision maker. So even if the first
|
|
rule to match a packet is a pass, if there is a later matching rule
|
|
that is a block and no further rules match the packet, then it will
|
|
be blocked.
|
|
.SS Matching Network Interfaces
|
|
.PP
|
|
On systems with more than one network interface, it is necessary
|
|
to be able to specify different filter rules for each of them.
|
|
In the first instance, this is because different networks will send us
|
|
packets via each network interface but it is also because of the hosts,
|
|
the role and the resulting security policy that we need to be able to
|
|
distinguish which network interface a packet is on.
|
|
.PP
|
|
To accomodate systems where the presence of a network interface is
|
|
dynamic, it is not necessary for the network interface named in a
|
|
filter rule to be present in the system when the rule is loaded.
|
|
This can lead to silent errors being introduced and unexpected
|
|
behaviour with the simplest of keyboard mistakes - for example,
|
|
typing in hem0 instead of hme0 or hme2 instead of hme3.
|
|
.PP
|
|
On Solaris systems prior to Solaris 10 Update 4, it is not possible
|
|
to filter packets on the loopback interface (lo0) so filter rules
|
|
that specify it will have no impact on the corresponding flow of
|
|
packets. See below for Solaris specific tips on how to enable this.
|
|
.PP
|
|
Some examples of including the network interface in filter rules are:
|
|
.PP
|
|
.nf
|
|
block in on bge0 all
|
|
pass out on bge0 all
|
|
.fi
|
|
.SS Address matching (basic)
|
|
.PP
|
|
The first and most basic part of matching for filtering rules is to
|
|
specify IP addresses and TCP/UDP port numbers. The source address
|
|
information is matched by the "from" information in a filter rule
|
|
and the destination address information is matched with the "to"
|
|
information in a filter rule.
|
|
.PP
|
|
The typical format used for IP addresses is CIDR notation, where an
|
|
IP address (or network) is followed by a '/' and a number representing
|
|
the size of the netmask in bits. This notation is used for specifying
|
|
address matching in both IPv4 and IPv6. If the '/' and bitmask size
|
|
are excluded from the matching string, it is assumed that the address
|
|
specified is a host address and that the netmask applied should be
|
|
all 1's.
|
|
.PP
|
|
Some examples of this are:
|
|
.PP
|
|
.nf
|
|
pass in from 10.1.0.0/24 to any
|
|
block out from any to 10.1.1.1
|
|
.fi
|
|
.PP
|
|
It is not possible to specify a range of addresses that does not
|
|
have a boundary that can be defined by a standard subnet mask.
|
|
.IP
|
|
.B Names instead of addresses
|
|
.RS
|
|
.PP
|
|
Hostnames, resolved either via DNS or /etc/hosts, or network names,
|
|
resolved via /etc/networks, may be used in place of actual addresses
|
|
in the filter rules. WARNING: if a hostname expands to more than one
|
|
address, only the *first* is used in building the filter rule.
|
|
.PP
|
|
Caution should be exercised when relying on DNS for filter rules in
|
|
case the sending and receiving of DNS packets is blocked when ipf(8)
|
|
is processing that part of the configuration file, leading to long
|
|
delays, if not errors, in loading the filter rules.
|
|
.RE
|
|
.SS Protocol Matching
|
|
.PP
|
|
To match packets based on TCP/UDP port information, it is first necessary
|
|
to indicate which protocol the packet must be. This is done using the
|
|
"proto" keyword, followed by either the protocol number or a name which
|
|
is mapped to the protocol number, usually through the /etc/protocols file.
|
|
.PP
|
|
.nf
|
|
pass in proto tcp from 10.1.0.0/24 to any
|
|
block out proto udp from any to 10.1.1.1
|
|
pass in proto icmp from any to 192.168.0.0/16
|
|
.fi
|
|
.SS Sending back error packets
|
|
.PP
|
|
When a packet is just discarded using a block rule, there is no feedback given
|
|
to the host that sent the packet. This is both good and bad. If this is the
|
|
desired behaviour and it is not desirable to send any feedback about packets
|
|
that are to be denied. The catch is that often a host trying to connect to a
|
|
TCP port or with a UDP based application will send more than one packet
|
|
because it assumes that just one packet may be discarded so a retry is
|
|
required. The end result being logs can become cluttered with duplicate
|
|
entries due to the retries.
|
|
.PP
|
|
To address this problem, a block rule can be qualified in two ways.
|
|
The first of these is specific to TCP and instructs IPFilter to send back
|
|
a reset (RST) packet. This packet indicates to the remote system that the
|
|
packet it sent has been rejected and that it shouldn't make any further
|
|
attempts to send packets to that port. Telling IPFilter to return a TCP
|
|
RST packet in response to something that has been received is achieved
|
|
with the return-rst keyword like this:
|
|
.PP
|
|
.nf
|
|
block return-rst in proto tcp from 10.0.0.0/8 to any
|
|
.fi
|
|
.PP
|
|
When sending back a TCP RST packet, IPFilter must construct a new packet
|
|
that has the source address of the intended target, not the source address
|
|
of the system it is running on (if they are different.)
|
|
.PP
|
|
For all of the other protocols handled by the IP protocol suite, to send
|
|
back an error indicating that the received packet was dropped requires
|
|
sending back an ICMP error packet. Whilst these can also be used for TCP,
|
|
the sending host may not treat the received ICMP error as a hard error
|
|
in the same way as it does the TCP RST packet. To return an ICMP error
|
|
it is necessary to place return-icmp after the block keyword like this:
|
|
.PP
|
|
.nf
|
|
block return-icmp in proto udp from any to 192.168.0.1/24
|
|
.fi
|
|
.PP
|
|
When electing to return an ICMP error packet, it is also possible to
|
|
select what type of ICMP error is returned. Whilst the full compliment
|
|
of ICMP unreachable codes can be used by specifying a number instead of
|
|
the string below, only the following should be used in conjunction with
|
|
return-icmp. Which return code to use is a choice to be made when
|
|
weighing up the pro's and con's. Using some of the codes may make it
|
|
more obvious that a firewall is being used rather than just the host
|
|
not responding.
|
|
.RS
|
|
.HP
|
|
filter-prohib
|
|
(prohibited by filter)
|
|
sending packets to the destination given in the received packet is
|
|
prohibited due to the application of a packet filter
|
|
.HP
|
|
net-prohib
|
|
(prohibited network)
|
|
sending packets to the destination given in the received packet is
|
|
administratively prohibited.
|
|
.HP
|
|
host-unk
|
|
(host unknown)
|
|
the destination host address is not known by the system receiving
|
|
the packet and therefore cannot be reached.
|
|
.HP
|
|
host-unr
|
|
(host unreachable)
|
|
it is not possible to reach the host as given by the destination address
|
|
in the packet header.
|
|
.HP
|
|
net-unk
|
|
(network unknown)
|
|
the destination network address is not known by the system receiving
|
|
the packet and therefore cannot be reached.
|
|
.HP
|
|
net-unr
|
|
(network unreachable)
|
|
it is not possible to forward the packet on to its final destination
|
|
as given by the destination address
|
|
.HP
|
|
port-unr
|
|
(port unreachable)
|
|
there is no application using the given destination port and therefore
|
|
it is not possible to reach that port.
|
|
.HP
|
|
proto-unr
|
|
(protocol unreachable)
|
|
the IP protocol specified in the packet is not available to receive
|
|
packets.
|
|
.DE
|
|
.PP
|
|
An example that shows how to send back a port unreachable packet for
|
|
UDP packets to 192.168.1.0/24 is as follows:
|
|
.PP
|
|
.nf
|
|
block return-icmp(port-unr) in proto udp from any to 192.168.1.0/24
|
|
.fi
|
|
.PP
|
|
In the above examples, when sending the ICMP packet, IPFilter will construct
|
|
a new ICMP packet with a source address of the network interface used to
|
|
send the packet back to the original source. This can give away that there
|
|
is an intermediate system blocking packets. To have IPFilter send back
|
|
ICMP packets where the source address is the original destination, regardless
|
|
of whether or not it is on the local host, return-icmp-as-dest is used like
|
|
this:
|
|
.PP
|
|
.nf
|
|
block return-icmp-as-dest(port-unr) in proto udp \\
|
|
from any to 192.168.1.0/24
|
|
.fi
|
|
.SS TCP/UDP Port Matching
|
|
.PP
|
|
Having specified which protocol is being matched, it is then possible to
|
|
indicate which port numbers a packet must have in order to match the rule.
|
|
Due to port numbers being used differently to addresses, it is therefore
|
|
possible to match on them in different ways. IPFilter allows you to use
|
|
the following logical operations:
|
|
.IP "< x"
|
|
is true if the port number in the packet is less than x
|
|
.IP "<= x"
|
|
is true if the port number in the packet is less than or equal to x
|
|
.IP "> x"
|
|
is true if the port number in the packet is greater than x
|
|
.IP ">= x"
|
|
is true if the port number in the packet is greater or equal to x
|
|
.IP "= x"
|
|
is true if the port number in the packet is equal to x
|
|
.IP "!= x"
|
|
is true if the port number in the packet is not equal to x
|
|
.PP
|
|
Additionally, there are a number of ways to specify a range of ports:
|
|
.IP "x <> y"
|
|
is true if the port number is less than x and greater than y
|
|
.IP "x >< y"
|
|
is true if the port number is greater than x and less than y
|
|
.IP "x:y"
|
|
is true if the port number is greater than or equal to x and less than or
|
|
equal to y
|
|
.PP
|
|
Some examples of this are:
|
|
.PP
|
|
.nf
|
|
block in proto tcp from any port >= 1024 to any port < 1024
|
|
pass in proto tcp from 10.1.0.0/24 to any port = 22
|
|
block out proto udp from any to 10.1.1.1 port = 135
|
|
pass in proto udp from 1.1.1.1 port = 123 to 10.1.1.1 port = 123
|
|
pass in proto tcp from 127.0.0.0/8 to any port 6000:6009
|
|
.fi
|
|
.PP
|
|
If there is no desire to mention any specific source or destintion
|
|
information in a filter rule then the word "all" can be used to
|
|
indicate that all addresses are considered to match the rule.
|
|
.SS IPv4 or IPv6
|
|
.PP
|
|
If a filter rule is constructed without any addresses then IPFilter
|
|
will attempt to match both IPv4 and IPv6 packets with it. In the
|
|
next list of rules, each one can be applied to either network protocol
|
|
because there is no address specified from which IPFilter can derive
|
|
with network protocol to expect.
|
|
.PP
|
|
.nf
|
|
pass in proto udp from any to any port = 53
|
|
block in proto tcp from any port < 1024 to any
|
|
.fi
|
|
.PP
|
|
To explicitly match a particular network address family with a specific
|
|
rule, the family must be added to the rule. For IPv4 it is necessary to
|
|
add family inet and for IPv6, family inet6. Thus the next rule will
|
|
block all packets (both IPv4 and IPv6:
|
|
.PP
|
|
.nf
|
|
block in all
|
|
.fi
|
|
.PP
|
|
but in the following example, we block all IPv4 packets and only allow
|
|
in IPv6 packets:
|
|
.PP
|
|
.nf
|
|
block in family inet all
|
|
pass in family inet6 all
|
|
.fi
|
|
.PP
|
|
To continue on from the example where we allowed either IPv4 or IPv6
|
|
packets to port 53 in, to change that such that only IPv6 packets to
|
|
port 53 need to allowed blocked then it is possible to add in a
|
|
protocol family qualifier:
|
|
.PP
|
|
.nf
|
|
pass in family inet6 proto udp from any to any port = 53
|
|
.fi
|
|
.SS First match vs last match
|
|
.PP
|
|
To change the default behaviour from being the last matched rule decides
|
|
the outcome to being the first matched rule, the word "quick" is inserted
|
|
to the rule.
|
|
.SH Extended Packet Matching
|
|
.SS Beyond using plain addresses
|
|
.PP
|
|
On firewalls that are working with large numbers of hosts and networks
|
|
or simply trying to filter discretely against various hosts, it can
|
|
be an easier administration task to define a pool of addresses and have
|
|
a filter rule reference that address pool rather than have a rule for
|
|
each address.
|
|
.PP
|
|
In addition to being able to use address pools, it is possible to use
|
|
the interface name(s) in the from/to address fields of a rule. If the
|
|
name being used in the address section can be matched to any of the
|
|
interface names mentioned in the rule's "on" or "via" fields then it
|
|
can be used with one of the following keywords for extended effect:
|
|
.HP
|
|
broadcast
|
|
use the primary broadcast address of the network interface for matching
|
|
packets with this filter rule;
|
|
.IP
|
|
.nf
|
|
pass in on fxp0 proto udp from any to fxp0/broadcast port = 123
|
|
.fi
|
|
.HP
|
|
peer
|
|
use the peer address on point to point network interfaces for matching
|
|
packets with this filter rule. This option typically only has meaningful
|
|
use with link protocols such as SLIP and PPP.
|
|
For example, this rule allows ICMP packets from the remote peer of ppp0
|
|
to be received if they're destined for the address assigned to the link
|
|
at the firewall end.
|
|
.IP
|
|
.nf
|
|
pass in on ppp0 proto icmp from ppp0/peer to ppp0/32
|
|
.fi
|
|
.HP
|
|
netmasked
|
|
use the primary network address, with its netmask, of the network interface
|
|
for matching packets with this filter rule. If a network interface had an
|
|
IP address of 192.168.1.1 and its netmask was 255.255.255.0 (/24), then
|
|
using the word "netmasked" after the interface name would match any
|
|
addresses that would match 192.168.1.0/24. If we assume that bge0 has
|
|
this IP address and netmask then the following two rules both serve
|
|
to produce the same effect:
|
|
.IP
|
|
.nf
|
|
pass in on bge0 proto icmp from any to 192.168.1.0/24
|
|
pass in on bge0 proto icmp from any to bge0/netmasked
|
|
.fi
|
|
.HP
|
|
network
|
|
using the primary network address, and its netmask, of the network interface,
|
|
construct an address for exact matching. If a network interface has an
|
|
address of 192.168.1.1 and its netmask is 255.255.255.0, using this
|
|
option would only match packets to 192.168.1.0.
|
|
.IP
|
|
.nf
|
|
pass in on bge0 proto icmp from any to bge0/network
|
|
.fi
|
|
.PP
|
|
Another way to use the name of a network interface to get the address
|
|
is to wrap the name in ()'s. In the above method, IPFilter
|
|
looks at the interface names in use and to decide whether or not
|
|
the name given is a hostname or network interface name. With the
|
|
use of ()'s, it is possible to tell IPFilter that the name should
|
|
be treated as a network interface name even though it doesn't
|
|
appear in the list of network interface that the rule ias associated
|
|
with.
|
|
.IP
|
|
.nf
|
|
pass in proto icmp from any to (bge0)/32
|
|
.fi
|
|
.SS Using address pools
|
|
.PP
|
|
Rather than list out multiple rules that either allow or deny specific
|
|
addresses, it is possible to create a single object, call an address
|
|
pool, that contains all of those addresses and reference that in the
|
|
filter rule. For documentation on how to write the configuration file
|
|
for those pools and load them, see ippool.conf(5) and ippool(8).
|
|
There are two types of address pools that can be defined in ippool.conf(5):
|
|
trees and hash tables. To refer to a tree defined in ippool.conf(5),
|
|
use this syntax:
|
|
.PP
|
|
.nf
|
|
pass in from pool/trusted to any
|
|
.fi
|
|
.PP
|
|
Either a name or number can be used after the '/', just so long as it
|
|
matches up with something that has already been defined in ipool.conf(5)
|
|
and loaded in with ippool(8). Loading a filter rule that references a
|
|
pool that does not exist will result in an error.
|
|
.PP
|
|
If hash tables have been used in ippool.conf(5) to store the addresses
|
|
in instead of a tree, then replace the word pool with hash:
|
|
.IP
|
|
.nf
|
|
pass in from any to hash/webservers
|
|
.fi
|
|
.PP
|
|
There are different operational characteristics with each, so there
|
|
may be some situations where a pool works better than hash and vice
|
|
versa.
|
|
.SS Matching TCP flags
|
|
.PP
|
|
The TCP header contains a field of flags that is used to decide if the
|
|
packet is a connection request, connection termination, data, etc.
|
|
By matching on the flags in conjunction with port numbers, it is
|
|
possible to restrict the way in which IPFilter allows connections to
|
|
be created. A quick overview of the TCP
|
|
flags is below. Each is listed with the letter used in IPFilter
|
|
rules, followed by its three or four letter pneumonic.
|
|
.HP
|
|
S
|
|
SYN - this bit is set when a host is setting up a connection.
|
|
The initiator typically sends a packet with the SYN bit and the
|
|
responder sends back SYN plus ACK.
|
|
.HP
|
|
A
|
|
ACK - this bit is set when the sender wishes to acknowledge the receipt
|
|
of a packet from another host
|
|
.HP
|
|
P
|
|
PUSH - this bit is set when a sending host has send some data that
|
|
is yet to be acknowledged and a reply is sought
|
|
.HP
|
|
F
|
|
FIN - this bit is set when one end of a connection starts to close
|
|
the connection down
|
|
.HP
|
|
U
|
|
URG - this bit is set to indicate that the packet contains urgent data
|
|
.HP
|
|
R
|
|
RST - this bit is set only in packets that are a reply to another
|
|
that has been received but is not targetted at any open port
|
|
.HP
|
|
C
|
|
CWN
|
|
.HP
|
|
E
|
|
ECN
|
|
.PP
|
|
When matching TCP flags, it is normal to just list the flag that you
|
|
wish to be set. By default the set of flags it is compared against
|
|
is "FSRPAU". Rules that say "flags S" will be displayed by ipfstat(8)
|
|
as having "flags S/FSRPAU". This is normal.
|
|
The last two flags, "C" and "E", are optional - they
|
|
may or may not be used by an end host and have no bearing on either
|
|
the acceptance of data nor control of the connection. Masking them
|
|
out with "flags S/FSRPAUCE" may cause problems for remote hosts
|
|
making a successful connection.
|
|
.PP
|
|
.nf
|
|
pass in quick proto tcp from any to any port = 22 flags S/SAFR
|
|
pass out quick proto tcp from any port = 22 to any flags SA
|
|
.fi
|
|
.PP
|
|
By itself, filtering based on the TCP flags becomes more work but when
|
|
combined with stateful filtering (see below), the situation changes.
|
|
.SS Matching on ICMP header information
|
|
.PP
|
|
The TCP and UDP are not the only protocols for which filtering beyond
|
|
just the IP header is possible, extended matching on ICMP packets is
|
|
also available. The list of valid ICMP types is different for IPv4
|
|
vs IPv6.
|
|
.PP
|
|
As a practical example, to allow the ping command to work
|
|
against a specific target requires allowing two different types of
|
|
ICMP packets, like this:
|
|
.PP
|
|
.nf
|
|
pass in proto icmp from any to webserver icmp-type echo
|
|
pass out proto icmp from webserver to any icmp-type echorep
|
|
.fi
|
|
.PP
|
|
The ICMP header has two fields that are of interest for filtering:
|
|
the ICMP type and code. Filter rules can accept either a name or
|
|
number for both the type and code. The list of names supported for
|
|
ICMP types is listed below, however only ICMP unreachable errors
|
|
have named codes (see above.)
|
|
.PP
|
|
The list of ICMP types that are available for matching an IPv4 packet
|
|
are as follows:
|
|
.PP
|
|
echo (echo request),
|
|
echorep (echo reply),
|
|
inforeq (information request),
|
|
inforep (information reply),
|
|
maskreq (mask request),
|
|
maskrep (mask reply),
|
|
paramprob (parameter problem),
|
|
redir (redirect),
|
|
routerad (router advertisement),
|
|
routersol (router solicit),
|
|
squence (source quence),
|
|
timest (timestamp),
|
|
timestreq (timestamp reply),
|
|
timex (time exceeded),
|
|
unreach (unreachable).
|
|
.PP
|
|
The list of ICMP types that are available for matching an IPv6 packet
|
|
are as follows:
|
|
.PP
|
|
echo (echo request),
|
|
echorep (echo reply),
|
|
fqdnquery (FQDN query),
|
|
fqdnreply (FQDN reply),
|
|
inforeq (information request),
|
|
inforep (information reply),
|
|
listendone (MLD listener done),
|
|
listendqry (MLD listener query),
|
|
listendrep (MLD listener reply),
|
|
neighadvert (neighbour advert),
|
|
neighborsol (neighbour solicit),
|
|
paramprob (parameter problem),
|
|
redir (redirect),
|
|
renumber (router renumbering),
|
|
routerad (router advertisement),
|
|
routersol (router solicit),
|
|
timex (time exceeded),
|
|
toobig (packet too big),
|
|
unreach (unreachable,
|
|
whoreq (WRU request),
|
|
whorep (WRU reply).
|
|
.SH Stateful Packet Filtering
|
|
.PP
|
|
Stateful packet filtering is where IPFilter remembers some information from
|
|
one or more packets that it has seen and is able to apply it to future
|
|
packets that it receives from the network.
|
|
.PP
|
|
What this means for each transport layer protocol is different.
|
|
For TCP it means that if IPFilter
|
|
sees the very first packet of an attempt to make a connection, it has enough
|
|
information to allow all other subsequent packets without there needing to
|
|
be any explicit rules to match them. IPFilter uses the TCP port numbers,
|
|
TCP flags, window size and sequence numbers to determine which packets
|
|
should be matched. For UDP, only the UDP port numbers are available.
|
|
For ICMP, the ICMP types can be combined with the ICMP id field to
|
|
determine which reply packets match a request/query that has already
|
|
been seen. For all other protocols, only matching on IP address and
|
|
protocol number is available for determining if a packet received is a mate
|
|
to one that has already been let through.
|
|
.PP
|
|
The difference this makes is a reduction in the number of rules from
|
|
2 or 4 to 1. For example, these 4 rules:
|
|
.PP
|
|
.nf
|
|
pass in on bge0 proto tcp from any to any port = 22
|
|
pass out on bge1 proto tcp from any to any port = 22
|
|
pass in on bge1 proto tcp from any port = 22 to any
|
|
pass out on bge0 proto tcp from any port = 22 to any
|
|
.fi
|
|
.PP
|
|
can be replaced with this single rule:
|
|
.PP
|
|
.nf
|
|
pass in on bge0 proto tcp from any to any port = 22 flags S keep state
|
|
.fi
|
|
.PP
|
|
Similar rules for UDP and ICMP might be:
|
|
.PP
|
|
.nf
|
|
pass in on bge0 proto udp from any to any port = 53 keep state
|
|
pass in on bge0 proto icmp all icmp-type echo keep state
|
|
.fi
|
|
.PP
|
|
When using stateful filtering with TCP it is best to add "flags S" to the
|
|
rule to ensure that state is only created when a packet is seen that is
|
|
an indication of a new connection. Although IPFilter can gather some
|
|
information from packets in the middle of a TCP connection to do stateful
|
|
filtering, there are some options that are only sent at the start of the
|
|
connection which alter the valid window of what TCP accepts. The end result
|
|
of trying to pickup TCP state in mid connection is that some later packets
|
|
that are part of the connection may not match the known state information
|
|
and be dropped or blocked, causing problems. If a TCP packet matches IP
|
|
addresses and port numbers but does not fit into the recognised window,
|
|
it will not be automatically allowed and will be flagged inside of
|
|
IPFitler as "out of window" (oow). See below, "Extra packet attributes",
|
|
for how to match on this attribute.
|
|
.PP
|
|
Once a TCP connection has reached the established state, the default
|
|
timeout allows for it to be idle for 5 days before it is removed from
|
|
the state table. The timeouts for the other TCP connection states
|
|
vary from 240 seconds to 30 seconds.
|
|
Both UDP and ICMP state entries have asymetric timeouts where the timeout
|
|
set upon seeing packets in the forward direction is much larger than
|
|
for the reverse direction. For UDP the default timeouts are 120 and
|
|
12 seconds, for ICMP 60 and 6 seconds. This is a reflection of the
|
|
use of these protocols being more for query-response than for ongoing
|
|
connections. For all other protocols the
|
|
timeout is 60 seconds in both directions.
|
|
.SS Stateful filtering options
|
|
.PP
|
|
The following options can be used with stateful filtering:
|
|
.HP
|
|
limit
|
|
limit the number of state table entries that this rule can create to
|
|
the number given after limit. A rule that has a limit specified is
|
|
always permitted that many state table entries, even if creating an
|
|
additional entry would cause the table to have more entries than the
|
|
otherwise global limit.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(limit 100)
|
|
.fi
|
|
.HP
|
|
age
|
|
sets the timeout for the state entry when it sees packets going through
|
|
it. Additionally it is possible to set the tieout for the reply packets
|
|
that come back through the firewall to a different value than for the
|
|
forward path. allowing a short timeout to be set after the reply has
|
|
been seen and the state no longer required.
|
|
.RS
|
|
.PP
|
|
.nf
|
|
pass in quick proto icmp all icmp-type echo \\
|
|
keep state (age 3)
|
|
pass in quick proto udp from any \\
|
|
to any port = 53 keep state (age 30/1)
|
|
.fi
|
|
.RE
|
|
.HP
|
|
strict
|
|
only has an impact when used with TCP. It forces all packets that are
|
|
allowed through the firewall to be sequential: no out of order delivery
|
|
of packets is allowed. This can cause significant slowdown for some
|
|
connections and may stall others. Use with caution.
|
|
.IP
|
|
.nf
|
|
pass in proto tcp ... keep state(strict)
|
|
.fi
|
|
.HP
|
|
noicmperr
|
|
prevents ICMP error packets from being able to match state table entries
|
|
created with this flag using the contents of the original packet included.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(noicmperr)
|
|
.fi
|
|
.HP
|
|
sync
|
|
indicates to IPFilter that it needs to provide information to the user
|
|
land daemons responsible for syncing other machines state tables up
|
|
with this one.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(sync)
|
|
.fi
|
|
.HP
|
|
nolog
|
|
do not generate any log records for the creation or deletion of state
|
|
table entries.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(nolog)
|
|
.fi
|
|
.HP
|
|
icmp-head
|
|
rather than just precent ICMP error packets from being able to match
|
|
state table entries, allow an ACL to be processed that can filter in or
|
|
out ICMP error packets based as you would with normal firewall rules.
|
|
The icmp-head option requires a filter rule group number or name to
|
|
be present, just as you would use with head.
|
|
.RS
|
|
.PP
|
|
.nf
|
|
pass in quick proto tcp ... keep state(icmp-head 101)
|
|
block in proto icmp from 10.0.0.0/8 to any group 101
|
|
.fi
|
|
.RE
|
|
.HP
|
|
max-srcs
|
|
allows the number of distinct hosts that can create a state entry to
|
|
be defined.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(max-srcs 100)
|
|
pass ... keep state(limit 1000, max-srcs 100)
|
|
.fi
|
|
.HP
|
|
max-per-src
|
|
whilst max-srcs limits the number of individual hosts that may cause
|
|
the creation of a state table entry, each one of those hosts is still
|
|
table to fill up the state table with new entries until the global
|
|
maximum is reached. This option allows the number of state table entries
|
|
per address to be limited.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(max-srcs 100, max-per-src 1)
|
|
pass ... keep state(limit 100, max-srcs 100, max-per-src 1)
|
|
.fi
|
|
.IP
|
|
Whilst these two rules might seem identical, in that they both
|
|
ultimately limit the number of hosts and state table entries created
|
|
from the rule to 100, there is a subtle difference: the second will
|
|
always allow up to 100 state table entries to be created whereas the
|
|
first may not if the state table fills up from other rules.
|
|
.IP
|
|
Further, it is possible to specify a netmask size after the per-host
|
|
limit that enables the per-host limit to become a per-subnet or
|
|
per-network limit.
|
|
.IP
|
|
.nf
|
|
pass ... keep state(max-srcs 100, max-per-src 1/24)
|
|
.fi
|
|
.IP
|
|
If there is no IP protocol implied by addresses or other features of
|
|
the rule, IPFilter will assume that no netmask is an all ones netmask
|
|
for both IPv4 and IPv6.
|
|
.SS Tieing down a connection
|
|
.PP
|
|
For any connection that transits a firewall, each packet will be seen
|
|
twice: once going in and once going out. Thus a connection has 4 flows
|
|
of packets:
|
|
.HP
|
|
forward
|
|
inbound packets
|
|
.HP
|
|
forward
|
|
outbound packets
|
|
.HP
|
|
reverse
|
|
inbound packets
|
|
.HP
|
|
reverse
|
|
outbound packets
|
|
.PP
|
|
IPFilter allows you to define the network interface to be used at all
|
|
four points in the flow of packets. For rules that match inbound packets,
|
|
out-via is used to specify which interfaces the packets go out, For rules
|
|
that match outbound packets, in-via is used to match the inbound packets.
|
|
In each case, the syntax generalises to this:
|
|
.PP
|
|
.nf
|
|
pass ... in on forward-in,reverse-in \\
|
|
out-via forward-out,reverse-out ...
|
|
|
|
pass ... out on forward-out,reverse-out \\
|
|
in-via forward-in,reverse-in ...
|
|
.fi
|
|
.PP
|
|
An example that pins down all 4 network interfaces used by an ssh
|
|
connection might look something like this:
|
|
.PP
|
|
.nf
|
|
pass in on bge0,bge1 out-via bge1,bge0 proto tcp \\
|
|
from any to any port = 22 flags S keep state
|
|
.fi
|
|
.SS Working with packet fragments
|
|
.PP
|
|
Fragmented packets result in 1 packet containing all of the layer 3 and 4
|
|
header information whilst the data is split across a number of other packets.
|
|
.PP
|
|
To enforce access control on fragmented packets, one of two approaches
|
|
can be taken. The first is to allow through all of the data fragments
|
|
(those that made up the body of the original packet) and rely on matching
|
|
the header information in the "first" fragment, when it is seen. The
|
|
reception of body fragments without the first will result in the receiving
|
|
host being unable to completely reassemble the packet and discarding all
|
|
of the fragments. The following three rules deny all fragmented packets
|
|
from being received except those that are UDP and even then only allows
|
|
those destined for port 2049 to be completed.
|
|
.PP
|
|
.nf
|
|
block in all with frags
|
|
pass in proto udp from any to any with frag-body
|
|
pass in proto udp from any to any port = 2049 with frags
|
|
.fi
|
|
.PP
|
|
Another mechanism that is available is to track "fragment state".
|
|
This relies on the first fragment of a packet that arrives to be
|
|
the fragment that contains all of the layer 3 and layer 4 header
|
|
information. With the receipt of that fragment before any other,
|
|
it is possible to determine which other fragments are to be allowed
|
|
through without needing to explicitly allow all fragment body packets.
|
|
An example of how this is done is as follows:
|
|
.PP
|
|
.nf
|
|
pass in proto udp from any prot = 2049 to any with frags keep fags
|
|
.fi
|
|
.SH Building a tree of rules
|
|
.PP
|
|
Writing your filter rules as one long list of rules can be both inefficient
|
|
in terms of processing the rules and difficult to understand. To make the
|
|
construction of filter rules easier, it is possible to place them in groups.
|
|
A rule can be both a member of a group and the head of a new group.
|
|
.PP
|
|
Using filter groups requires at least two rules: one to be in the group
|
|
one one to send matchign packets to the group. If a packet matches a
|
|
filtre rule that is a group head but does not match any of the rules
|
|
in that group, then the packet is considered to have matched the head
|
|
rule.
|
|
.PP
|
|
Rules that are a member of a group contain the word group followed by
|
|
either a name or number that defines which group they're in. Rules that
|
|
form the branch point or starting point for the group must use the
|
|
word head, followed by either a group name or number. If rules are
|
|
loaded in that define a group but there is no matching head then they
|
|
will effectively be orphaned rules. It is possible to have more than
|
|
one head rule point to the same group, allowing groups to be used
|
|
like subroutines to implement specific common policies.
|
|
.PP
|
|
A common use of filter groups is to define head rules that exist in the
|
|
filter "main line" for each direction with the interfaces in use. For
|
|
example:
|
|
.PP
|
|
.nf
|
|
block in quick on bge0 all head 100
|
|
block out quick on bge0 all head 101
|
|
block in quick on fxp0 all head internal-in
|
|
block out quick on fxp0 all head internal-out
|
|
pass in quick proto icmp all icmp-type echo group 100
|
|
.fi
|
|
.PP
|
|
In the above set of rules, there are four groups defined but only one
|
|
of them has a member rule. The only packets that would be allowed
|
|
through the above ruleset would be ICMP echo packets that are
|
|
received on bge0.
|
|
.PP
|
|
Rules can be both a member of a group and the head of a new group,
|
|
allowing groups to specialise.
|
|
.PP
|
|
.nf
|
|
block in quick on bge0 all head 100
|
|
block in quick proto tcp all head 1006 group 100
|
|
.fi
|
|
.PP
|
|
Another use of filter rule groups is to provide a place for rules to
|
|
be dynamically added without needing to worry about their specific
|
|
ordering amongst the entire ruleset. For example, if I was using this
|
|
simple ruleset:
|
|
.PP
|
|
.nf
|
|
block in quick all with bad
|
|
block in proto tcp from any to any port = smtp head spammers
|
|
pass in quick proto tcp from any to any port = smtp flags S keep state
|
|
.fi
|
|
.PP
|
|
and I was getting lots of connections to my email server from 10.1.1.1
|
|
to deliver spam, I could load the following rule to complement the above:
|
|
.IP
|
|
.nf
|
|
block in quick from 10.1.1.1 to any group spammers
|
|
.fi
|
|
.SS Decapsulation
|
|
.PP
|
|
Rule groups also form a different but vital role for decapsulation rules.
|
|
With the following simple rule, if IPFilter receives an IP packet that has
|
|
an AH header as its layer 4 payload, IPFilter would adjust its view of the
|
|
packet internally and then jump to group 1001 using the data beyond the
|
|
AH header as the new transport header.
|
|
.PP
|
|
.nf
|
|
decapsulate in proto ah all head 1001
|
|
.fi
|
|
.PP
|
|
For protocols that
|
|
are recognised as being used with tunnelling or otherwise encapsulating
|
|
IP protocols, IPFilter is able to decide what it has on the inside
|
|
without any assistance. Some tunnelling protocols use UDP as the
|
|
transport mechanism. In this case, it is necessary to instruct IPFilter
|
|
as to what protocol is inside UDP.
|
|
.PP
|
|
.nf
|
|
decapsulate l5-as(ip) in proto udp from any \\
|
|
to any port = 1520 head 1001
|
|
.fi
|
|
.PP
|
|
Currently IPFilter only supports finding IPv4 and IPv6 headers
|
|
directly after the UDP header.
|
|
.PP
|
|
If a packet matches a decapsulate rule but fails to match any of the rules
|
|
that are within the specified group, processing of the packet continues
|
|
to the next rule after the decapsulate and IPFilter's internal view of the
|
|
packet is returned to what it was prior to the decapsulate rule.
|
|
.PP
|
|
It is possible to construct a decapsulate rule without the group
|
|
head at the end that ipf(8) will accept but such rules will not
|
|
result in anything happening.
|
|
.SS Policy Based Routing
|
|
.PP
|
|
With firewalls being in the position they often are, at the boundary
|
|
of different networks connecting together and multiple connections that
|
|
have different properties, it is often desirable to have packets flow
|
|
in a direction different to what the routing table instructs the kernel.
|
|
These decisions can often be extended to changing the route based on
|
|
both source and destination address or even port numbers.
|
|
.PP
|
|
To support this kind of configuration, IPFilter allows the next hop
|
|
destination to be specified with a filter rule. The next hop is given
|
|
with the interface name to use for output. The syntax for this is
|
|
interface:ip.address. It is expected that the address given as the next
|
|
hop is directly connected to the network to which the interface is.
|
|
.PP
|
|
.nf
|
|
pass in on bge0 to bge1:1.1.1.1 proto tcp \\
|
|
from 1.1.2.3 to any port = 80 flags S keep state
|
|
.fi
|
|
.PP
|
|
When this feature is combined with stateful filtering, it becomes
|
|
possible to influence the network interface used to transmit packets
|
|
in both directions because we now have a sense for what its reverse
|
|
flow of packets is.
|
|
.PP
|
|
.nf
|
|
pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\
|
|
proto tcp from 1.1.2.3 to any port = 80 flags S keep state
|
|
.fi
|
|
.PP
|
|
If the actions of the routing table are perfectly acceptable, but
|
|
you would like to mask the presence of the firewall by not changing
|
|
the TTL in IP packets as they transit it, IPFilter can be instructed
|
|
to do a "fastroute" action like this:
|
|
.PP
|
|
.nf
|
|
pass in on bge0 fastroute proto icmp all
|
|
.fi
|
|
.PP
|
|
This should be used with caution as it can lead to endless packet
|
|
loops. Additionally, policy based routing does not change the IP
|
|
header's TTL value.
|
|
.PP
|
|
A variation on this type of rule supports a duplicate of the original
|
|
packet being created and sent out a different network. This can be
|
|
useful for monitoring traffic and other purposes.
|
|
.PP
|
|
.nf
|
|
pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\
|
|
dup-to fxp0:10.0.0.1 proto tcp from 1.1.2.3 \\
|
|
to any port = 80 flags S keep state
|
|
.fi
|
|
.SS Matching IPv4 options
|
|
.PP
|
|
The design for IPv4 allows for the header to be upto 64 bytes long,
|
|
however most traffic only uses the basic header which is 20 bytes long.
|
|
The other 44 bytes can be uesd to store IP options. These options are
|
|
generally not necessary for proper interaction and function on the
|
|
Internet today. For most people it is sufficient to block and drop
|
|
all packets that have any options set. This can be achieved with this
|
|
rule:
|
|
.PP
|
|
.nf
|
|
block in quick all with ipopts
|
|
.fi
|
|
.PP
|
|
This rule is usually placed towards the top of the configuration
|
|
so that all incoming packets are blocked.
|
|
.PP
|
|
If you wanted to allow in a specific IP option type, the syntax
|
|
changes slightly:
|
|
.PP
|
|
.nf
|
|
pass in quick proto igmp all with opt rtralrt
|
|
.fi
|
|
.PP
|
|
The following is a list of IP options that most people encounter and
|
|
what their use/threat is.
|
|
.HP
|
|
lsrr
|
|
(loose source route) the sender of the packet includes a list of addresses
|
|
that they wish the packet to be routed through to on the way to the
|
|
destination. Because replies to such packets are expected to use the
|
|
list of addresses in reverse, hackers are able to very effectively use
|
|
this header option in address spoofing attacks.
|
|
.HP
|
|
rr
|
|
(record route) the sender allocates some buffer space for recording the
|
|
IP address of each router that the packet goes through. This is most often
|
|
used with ping, where the ping response contains a copy of all addresses
|
|
from the original packet, telling the sender what route the packet took
|
|
to get there. Due to performance and security issues with IP header
|
|
options, this is almost no longer used.
|
|
.HP
|
|
rtralrt
|
|
(router alert) this option is often used in IGMP messages as a flag to
|
|
routers that the packet needs to be handled differently. It is unlikely
|
|
to ever be received from an unknown sender. It may be found on LANs or
|
|
otherwise controlled networks where the RSVP protocol and multicast
|
|
traffic is in heavy use.
|
|
.HP
|
|
ssrr
|
|
(strict source route) the sender of the packet includes a list of addresses
|
|
that they wish the packet to be routed through to on the way to the
|
|
destination. Where the lsrr option allows the sender to specify only
|
|
some of the nodes the packet must go through, with the ssrr option,
|
|
every next hop router must be specified.
|
|
.PP
|
|
The complete list of IPv4 options that can be matched on is:
|
|
addext (Address Extention),
|
|
cipso (Classical IP Security Option),
|
|
dps (Dynamic Packet State),
|
|
e-sec (Extended Security),
|
|
eip (Extended Internet Protocol),
|
|
encode (ENCODE),
|
|
finn (Experimental Flow Control),
|
|
imitd (IMI Traffic Descriptor),
|
|
lsrr (Loose Source Route),
|
|
mtup (MTU Probe - obsolete),
|
|
mtur (MTU response - obsolete),
|
|
nop (No Operation),
|
|
nsapa (NSAP Address),
|
|
rr (Record Route),
|
|
rtralrt (Router Alert),
|
|
satid (Stream Identifier),
|
|
sdb (Selective Directed Broadcast),
|
|
sec (Security),
|
|
ssrr (Strict Source Route),
|
|
tr (Tracerote),
|
|
ts (Timestamp),
|
|
ump (Upstream Multicast Packet),
|
|
visa (Experimental Access Control)
|
|
and zsu (Experimental Measurement).
|
|
.SS Security with CIPSO and IPSO
|
|
.PP
|
|
IPFilter supports filtering on IPv4 packets using security attributes embedded
|
|
in the IP options part of the packet. These options are usually only used on
|
|
networks and systems that are using lablled security. Unless you know that
|
|
you are using labelled security and your networking is also labelled, it
|
|
is highly unlikely that this section will be relevant to you.
|
|
.PP
|
|
With the traditional IP Security Options (IPSO), packets can be tagged with
|
|
a security level. The following keywords are recognised and match with the
|
|
relevant RFC with respect to the bit patterns matched:
|
|
confid (confidential),
|
|
rserve-1 (1st reserved value),
|
|
rserve-2 (2nd reserved value),
|
|
rserve-3 (3rd reserved value),
|
|
rserve-4 (4th reserved value),
|
|
secret (secret),
|
|
topsecret (top secret),
|
|
unclass (unclassified).
|
|
.PP
|
|
.nf
|
|
block in quick all with opt sec-class unclass
|
|
pass in all with opt sec-class secret
|
|
.fi
|
|
.SS Matching IPv6 extension headers
|
|
.PP
|
|
Just as it is possible to filter on the various IPv4 header options,
|
|
so too it is possible to filter on the IPv6 extension headers that are
|
|
placed between the IPv6 header and the transport protocol header.
|
|
.PP
|
|
dstopts (destination options),
|
|
esp (encrypted, secure, payload),
|
|
frag (fragment),
|
|
hopopts (hop-by-hop options),
|
|
ipv6 (IPv6 header),
|
|
mobility (IP mobility),
|
|
none,
|
|
routing.
|
|
.SS Logging
|
|
.PP
|
|
There are two ways in which packets can be logged with IPFilter. The
|
|
first is with a rule that specifically says log these types of packets
|
|
and the second is a qualifier to one of the other keywords. Thus it is
|
|
possible to both log and allow or deny a packet with a single rule.
|
|
.PP
|
|
.nf
|
|
pass in log quick proto tcp from any to any port = 22
|
|
.fi
|
|
.PP
|
|
When using stateful filtering, the log action becomes part of the result
|
|
that is remembered about a packet. Thus if the above rule was qualified
|
|
with keep state, every packet in the connection would be logged. To only
|
|
log the first packet from every packet flow tracked with keep state, it
|
|
is necessary to indicate to IPFilter that you only wish to log the first
|
|
packet.
|
|
.PP
|
|
.nf
|
|
pass in log first quick proto tcp from any to any port = 22 \\
|
|
flags S keep state
|
|
.fi
|
|
.PP
|
|
If it is a requirement that the logging provide an accurate representation
|
|
of which connections are allowed, the log action can be qualified with the
|
|
option or-block. This allows the administrator to instruct IPFilter to
|
|
block the packet if the attempt to record the packet in IPFilter's kernel
|
|
log records (which have an upper bound on size) failed. Unless the system
|
|
shuts down or reboots, once a log record is written into the kernel buffer,
|
|
it is there until ipmon(8) reads it.
|
|
.PP
|
|
.nf
|
|
block in log proto tcp from any to any port = smtp
|
|
pass in log or-block first quick proto tcp from any \\
|
|
to any port = 22 flags S keep state
|
|
.fi
|
|
.PP
|
|
By default, IPFilter will only log the header portion of a packet received
|
|
on the network. A portion of the body of a packet, upto 128 bytes, can also
|
|
be logged with the body keyword. ipmon(8) will display the contents of the
|
|
portion of the body logged in hex.
|
|
.PP
|
|
.nf
|
|
block in log body proto icmp all
|
|
.fi
|
|
.PP
|
|
When logging packets from ipmon(8) to syslog, by default ipmon(8) will
|
|
control what syslog facility and priority a packet will be logged with.
|
|
This can be tuned on a per rule basis like this:
|
|
.PP
|
|
.nf
|
|
block in quick log level err all with bad
|
|
pass in log level local1.info proto tcp \\
|
|
from any to any port = 22 flags S keep state
|
|
.fi
|
|
.PP
|
|
ipfstat(8) reports how many packets have been successfully logged and how
|
|
many failed attempts to log a packet there were.
|
|
.SS Filter rule comments
|
|
.PP
|
|
If there is a desire to associate a text string, be it an administrative
|
|
comment or otherwise, with an IPFilter rule, this can be achieved by giving
|
|
the filter rule a comment. The comment is loaded with the rule into the
|
|
kernel and can be seen when the rules are listed with ipfstat.
|
|
.PP
|
|
.nf
|
|
pass in quick proto tcp from any \\
|
|
to port = 80 comment "all web server traffic is ok"
|
|
pass out quick proto tcp from any port = 80 \\
|
|
to any comment "all web server traffic is ok"
|
|
.fi
|
|
.SS Tags
|
|
.PP
|
|
To enable filtering and NAT to correctly match up packets with rules,
|
|
tags can be added at with NAT (for inbound packets) and filtering (for
|
|
outbound packets.) This allows a filter to be correctly mated with its
|
|
NAT rule in the event that the NAT rule changed the packet in a way
|
|
that would mean it is not obvious what it was.
|
|
.PP
|
|
For inbound packets, IPFilter can match the tag used in the filter
|
|
rules with that set by NAT. For outbound rules, it is the reverse,
|
|
the filter sets the tag and the NAT rule matches up with it.
|
|
.PP
|
|
.nf
|
|
pass in ... match-tag(nat=proxy)
|
|
pass out ... set-tag(nat=proxy)
|
|
.fi
|
|
.PP
|
|
Another use of tags is to supply a number that is only used with logging.
|
|
When packets match these rules, the log tag is carried over into the
|
|
log file records generated by ipmon(8). With the correct use of tools
|
|
such as grep, extracting log records of interest is simplified.
|
|
.PP
|
|
.nf
|
|
block in quick log ... set-tag(log=33)
|
|
.fi
|
|
.SH Filter Rule Expiration
|
|
.PP
|
|
IPFilter allows rules to be added into the kernel that it will remove after
|
|
a specific period of time by specifying rule-ttl at the end of a rule.
|
|
When listing rules in the kernel using ipfstat(8), rules that are going
|
|
to expire will NOT display "rule-ttl" with the timeout, rather what will
|
|
be seen is a comment with how many ipfilter ticks left the rule has to
|
|
live.
|
|
.PP
|
|
The time to live is specified in seconds.
|
|
.PP
|
|
.nf
|
|
pass in on fxp0 proto tcp from any \\
|
|
to port = 22 flags S keep state rule-ttl 30
|
|
.fi
|
|
.SH Internal packet attributes
|
|
.PP
|
|
In addition to being able to filter on very specific network and transport
|
|
header fields, it is possible to filter on other attributes that IPFilter
|
|
attaches to a packet. These attributes are placed in a rule after the
|
|
keyword "with", as can be seen with frags and frag-body above. The
|
|
following is a list of the other attributes available:
|
|
.HP
|
|
oow
|
|
the packet's IP addresses and TCP ports match an existing entry in the
|
|
state table but the sequence numbers indicate that it is outside of the
|
|
accepted window.
|
|
.IP
|
|
.nf
|
|
block return-rst in quick proto tcp from any to any with not oow
|
|
.fi
|
|
.HP
|
|
bcast
|
|
this is set by IPFilter when it receives notification that the link
|
|
layer packet was a broadcast packet. No checking of the IP addresses
|
|
is performned to determine if it is a broadcast destination or not.
|
|
.IP
|
|
.nf
|
|
block in quick proto udp all with bcast
|
|
.fi
|
|
.HP
|
|
mcast
|
|
this is set by IPFilter when it receives notification that the link
|
|
layer packet was a multicast packet. No checking of the IP addresses
|
|
is performned to determine if it is a multicast destination or not.
|
|
.IP
|
|
.nf
|
|
pass in quick proto udp from any to any port = dns with mcast
|
|
.fi
|
|
.HP
|
|
mbcast
|
|
can be used to match a packet that is either a multicast or broadcast
|
|
packet at the link layer, as indicated by the operating system.
|
|
.IP
|
|
.nf
|
|
pass in quick proto udp from any to any port = ntp with mbcast
|
|
.fi
|
|
.HP
|
|
nat
|
|
the packet positively matched a NAT table entry.
|
|
.HP
|
|
bad
|
|
sanity checking of the packet failed. This could indicate that the
|
|
layer 3/4 headers are not properly formed.
|
|
.HP
|
|
bad-src
|
|
when reverse path verification is enabled, this flag will be set when
|
|
the interface the packet is received on does not match that which would
|
|
be used to send a packet out of to the source address in the received
|
|
packet.
|
|
.HP
|
|
bad-nat
|
|
an attempt to perform NAT on the packet failed.
|
|
.HP
|
|
not
|
|
each one of the attributes matched using the "with" keyword can also be
|
|
looked for to not be present. For example, to only allow in good packets,
|
|
I can do this:
|
|
.PP
|
|
.nf
|
|
block in all
|
|
pass in all with not bad
|
|
.fi
|
|
.SH Tuning IPFilter
|
|
.PP
|
|
The ipf.conf file can also be used to tune the behaviour of IPFilter,
|
|
allowing, for example, timeouts for the NAT/state table(s) to be set
|
|
along with their sizes. The presence and names of tunables may change
|
|
from one release of IPFilter to the next. The tunables that can be
|
|
changed via ipf.conf is the same as those that can be seen and modified
|
|
using the -T command line option to ipf(8).
|
|
.PP
|
|
NOTE: When parsing ipf.conf, ipf(8) will apply the settings before
|
|
loading any rules. Thus if your settings are at the top, these may
|
|
be applied whilst the rules not applied if there is an error further
|
|
down in the configuration file.
|
|
.PP
|
|
To set one of the values below, the syntax is simple: "set", followed
|
|
by the name of the tuneable to set and then the value to set it to.
|
|
.PP
|
|
.nf
|
|
set state_max 9999;
|
|
set state_size 10101;
|
|
.fi
|
|
.PP
|
|
A list of the currently available variables inside IPFilter that may
|
|
be tuned from ipf.conf are as follows:
|
|
.HP
|
|
active
|
|
set through -s command line switch of ipf(8). See ipf(8) for detals.
|
|
.HP
|
|
chksrc
|
|
when set, enables reverse path verification on source addresses and
|
|
for filters to match packets with bad-src attribute.
|
|
.HP
|
|
control_forwarding
|
|
when set turns off kernel forwarding when IPFilter is disabled or unloaded.
|
|
.HP
|
|
default_pass
|
|
the default policy - whether packets are blocked or passed, etc - is
|
|
represented by the value of this variable. It is a bit field and the
|
|
bits that can be set are found in <netinet/ip_fil.h>. It is not
|
|
recommended to tune this value directly.
|
|
.HP
|
|
ftp_debug
|
|
set the debugging level of the in-kernel FTP proxy.
|
|
Debug messages will be printed to the system console.
|
|
.HP
|
|
ftp_forcepasv
|
|
when set the FTP proxy must see a PASV/EPSV command before creating
|
|
the state/NAT entries for the 227 response.
|
|
.HP
|
|
ftp_insecure
|
|
when set the FTP proxy will not wait for a user to login before allowing
|
|
data connections to be created.
|
|
.HP
|
|
ftp_pasvonly
|
|
when set the proxy will not create state/NAT entries for when it
|
|
sees either the PORT or EPRT command.
|
|
.HP
|
|
ftp_pasvrdr
|
|
when enabled causes the FTP proxy to create very insecure NAT/state
|
|
entries that will allow any connection between the client and server
|
|
hosts when a 227 reply is seen. Use with extreme caution.
|
|
.HP
|
|
ftp_single_xfer
|
|
when set the FTP proxy will only allow one data connection at a time.
|
|
.HP
|
|
hostmap_size
|
|
sets the size of the hostmap table used by NAT to store address mappings
|
|
for use with sticky rules.
|
|
.HP
|
|
icmp_ack_timeout
|
|
default timeout used for ICMP NAT/state when a reply packet is seen for
|
|
an ICMP state that already exists
|
|
.HP
|
|
icmp_minfragmtu
|
|
sets the minimum MTU that is considered acceptable in an ICMP error
|
|
before deciding it is a bad packet.
|
|
.HP
|
|
icmp_timeout
|
|
default timeout used for ICMP NAT/state when the packet matches the rule
|
|
.HP
|
|
ip_timeout
|
|
default timeout used for NAT/state entries that are not TCP/UDP/ICMP.
|
|
.HP
|
|
ipf_flags
|
|
.HP
|
|
ips_proxy_debug
|
|
this sets the debugging level for the proxy support code.
|
|
When enabled, debugging messages will be printed to the system console.
|
|
.HP
|
|
log_all
|
|
when set it changes the behaviour of "log body" to log the entire packet
|
|
rather than just the first 128 bytes.
|
|
.HP
|
|
log_size
|
|
sets the size of the in-kernel log buffer in bytes.
|
|
.HP
|
|
log_suppress
|
|
when set, IPFilter will check to see if the packet it is logging is
|
|
similar to the one it previously logged and if so, increases
|
|
the occurance count for that packet. The previously logged packet
|
|
must not have yet been read by ipmon(8).
|
|
.HP
|
|
min_ttl
|
|
is used to set the TTL value that packets below will be marked with
|
|
the low-ttl attribute.
|
|
.HP
|
|
nat_doflush
|
|
if set it will cause the NAT code to do a more aggressive flush of the
|
|
NAT table at the next opportunity. Once the flush has been done, the
|
|
value is reset to 0.
|
|
.HP
|
|
nat_lock
|
|
this should only be changed using ipfs(8)
|
|
.HP
|
|
nat_logging
|
|
when set, NAT will create log records that can be read from /dev/ipnat.
|
|
.HP
|
|
nat_maxbucket
|
|
maximum number of entries allowed to exist in each NAT hash bucket.
|
|
This prevents an attacker trying to load up the hash table with
|
|
entries in a single bucket, reducing performance.
|
|
.HP
|
|
nat_rules_size
|
|
size of the hash table to store map rules.
|
|
.HP
|
|
nat_table_max
|
|
maximum number of entries allowed into the NAT table
|
|
.HP
|
|
nat_table_size
|
|
size of the hash table used for NAT
|
|
.HP
|
|
nat_table_wm_high
|
|
when the fill percentage of the NAT table exceeds this mark, more
|
|
aggressive flushing is enabled.
|
|
.HP
|
|
nat_table_wm_low
|
|
this sets the percentage at which the NAT table's agressive flushing
|
|
will turn itself off at.
|
|
.HP
|
|
rdr_rules_size
|
|
size of the hash table to store rdr rules.
|
|
.HP
|
|
state_lock
|
|
this should only be changed using ipfs(8)
|
|
.HP
|
|
state_logging
|
|
when set, the stateful filtering will create log records
|
|
that can be read from /dev/ipstate.
|
|
.HP
|
|
state_max
|
|
maximum number of entries allowed into the state table
|
|
.HP
|
|
state_maxbucket
|
|
maximum number of entries allowed to exist in each state hash bucket.
|
|
This prevents an attacker trying to load up the hash table with
|
|
entries in a single bucket, reducing performance.
|
|
.HP
|
|
state_size
|
|
size of the hash table used for stateful filtering
|
|
.HP
|
|
state_wm_freq
|
|
this controls how often the agressive flushing should be run once the
|
|
state table exceeds state_wm_high in percentage full.
|
|
.HP
|
|
state_wm_high
|
|
when the fill percentage of the state table exceeds this mark, more
|
|
aggressive flushing is enabled.
|
|
.HP
|
|
state_wm_low
|
|
this sets the percentage at which the state table's agressive flushing
|
|
will turn itself off at.
|
|
.HP
|
|
tcp_close_wait
|
|
timeout used when a TCP state entry reaches the FIN_WAIT_2 state.
|
|
.HP
|
|
tcp_closed
|
|
timeout used when a TCP state entry is ready to be removed after either
|
|
a RST packet is seen.
|
|
.HP
|
|
tcp_half_closed
|
|
timeout used when a TCP state entry reaches the CLOSE_WAIT state.
|
|
.HP
|
|
tcp_idle_timeout
|
|
timeout used when a TCP state entry reaches the ESTABLISHED state.
|
|
.HP
|
|
tcp_last_ack
|
|
timeout used when a TCP NAT/state entry reaches the LAST_ACK state.
|
|
.HP
|
|
tcp_syn_received
|
|
timeout applied to a TCP NAT/state entry after SYN-ACK packet has been seen.
|
|
.HP
|
|
tcp_syn_sent
|
|
timeout applied to a TCP NAT/state entry after SYN packet has been seen.
|
|
.HP
|
|
tcp_time_wait
|
|
timeout used when a TCP NAT/state entry reaches the TIME_WAIT state.
|
|
.HP
|
|
tcp_timeout
|
|
timeout used when a TCP NAT/state entry reaches either the half established
|
|
state (one ack is seen after a SYN-ACK) or one side is in FIN_WAIT_1.
|
|
.HP
|
|
udp_ack_timeout
|
|
default timeout used for UDP NAT/state when a reply packet is seen for
|
|
a UDP state that already exists
|
|
.HP
|
|
udp_timeout
|
|
default timeout used for UDP NAT/state when the packet matches the rule
|
|
.HP
|
|
update_ipid
|
|
when set, turns on changing the IP id field in NAT'd packets to a random
|
|
number.
|
|
.SS Table of visible variables
|
|
.PP
|
|
A list of all of the tunables, their minimum, maximum and current
|
|
values is as follows.
|
|
.PP
|
|
.nf
|
|
Name Min Max Current
|
|
active 0 0 0
|
|
chksrc 0 1 0
|
|
control_forwarding 0 1 0
|
|
default_pass 0 MAXUINT 134217730
|
|
ftp_debug 0 10 0
|
|
ftp_forcepasv 0 1 1
|
|
ftp_insecure 0 1 0
|
|
ftp_pasvonly 0 1 0
|
|
ftp_pasvrdr 0 1 0
|
|
ftp_single_xfer 0 1 0
|
|
hostmap_size 1 MAXINT 2047
|
|
icmp_ack_timeout 1 MAXINT 12
|
|
icmp_minfragmtu 0 1 68
|
|
icmp_timeout 1 MAXINT 120
|
|
ip_timeout 1 MAXINT 120
|
|
ipf_flags 0 MAXUINT 0
|
|
ips_proxy_debug 0 10 0
|
|
log_all 0 1 0
|
|
log_size 0 524288 32768
|
|
log_suppress 0 1 1
|
|
min_ttl 0 1 4
|
|
nat_doflush 0 1 0
|
|
nat_lock 0 1 0
|
|
nat_logging 0 1 1
|
|
nat_maxbucket 1 MAXINT 22
|
|
nat_rules_size 1 MAXINT 127
|
|
nat_table_max 1 MAXINT 30000
|
|
nat_table_size 1 MAXINT 2047
|
|
nat_table_wm_high 2 100 99
|
|
nat_table_wm_low 1 99 90
|
|
rdr_rules_size 1 MAXINT 127
|
|
state_lock 0 1 0
|
|
state_logging 0 1 1
|
|
state_max 1 MAXINT 4013
|
|
state_maxbucket 1 MAXINT 26
|
|
state_size 1 MAXINT 5737
|
|
state_wm_freq 2 999999 20
|
|
state_wm_high 2 100 99
|
|
state_wm_low 1 99 90
|
|
tcp_close_wait 1 MAXINT 480
|
|
tcp_closed 1 MAXINT 60
|
|
tcp_half_closed 1 MAXINT 14400
|
|
tcp_idle_timeout 1 MAXINT 864000
|
|
tcp_last_ack 1 MAXINT 60
|
|
tcp_syn_received 1 MAXINT 480
|
|
tcp_syn_sent 1 MAXINT 480
|
|
tcp_time_wait 1 MAXINT 480
|
|
tcp_timeout 1 MAXINT 480
|
|
udp_ack_timeout 1 MAXINT 24
|
|
udp_timeout 1 MAXINT 240
|
|
update_ipid 0 1 0
|
|
.fi
|
|
.SH Calling out to internal functions
|
|
.PP
|
|
IPFilter provides a pair of functions that can be called from a rule
|
|
that allow for a single rule to jump out to a group rather than walk
|
|
through a list of rules to find the group. If you've got multiple
|
|
networks, each with its own group of rules, this feature may help
|
|
provide better filtering performance.
|
|
.PP
|
|
The lookup to find which rule group to jump to is done on either the
|
|
source address or the destination address but not both.
|
|
.PP
|
|
In this example below, we are blocking all packets by default but then
|
|
doing a lookup on the source address from group 1010. The two rules in
|
|
the ipf.conf section are lone members of their group. For an incoming
|
|
packet that is from 1.1.1.1, it will go through three rules: (1) the
|
|
block rule, (2) the call rule and (3) the pass rule for group 1020.
|
|
For a packet that is from 3.3.2.2, it will also go through three rules:
|
|
(1) the block rule, (2) the call rule and (3) the pass rule for group
|
|
1030. Should a packet from 3.1.1.1 arrive, it will be blocked as it
|
|
does not match any of the entries in group 1010, leaving it to only
|
|
match the first rule.
|
|
.PP
|
|
.nf
|
|
from ipf.conf
|
|
-------------
|
|
block in all
|
|
call now srcgrpmap/1010 in all
|
|
pass in proto tcp from any to any port = 80 group 1020
|
|
pass in proto icmp all icmp-type echo group 1030
|
|
|
|
from ippool.conf
|
|
----------------
|
|
group-map in role=ipf number=1010
|
|
{ 1.1.1.1 group = 1020, 3.3.0.0/16 group = 1030; };
|
|
.fi
|
|
.SS IPFilter matching expressions
|
|
.PP
|
|
An experimental feature that has been added to filter rules is to use
|
|
the same expression matching that is available with various commands
|
|
to flush and list state/NAT table entries. The use of such an expression
|
|
precludes the filter rule from using the normal IP header matching.
|
|
.PP
|
|
.nf
|
|
pass in exp { "tcp.sport 23 or tcp.sport 50" } keep state
|
|
.fi
|
|
.SS Filter rules with BPF
|
|
.PP
|
|
On platforms that have the BPF built into the kernel, IPFilter can be
|
|
built to allow BPF expressions in filter rules. This allows for packet
|
|
matching to be on arbitrary data in the packt. The use of a BPF expression
|
|
replaces all of the other protocol header matching done by IPFilter.
|
|
.PP
|
|
.nf
|
|
pass in bpf-v4 { "tcp and (src port 23 or src port 50)" } \\
|
|
keep state
|
|
.fi
|
|
.PP
|
|
These rules tend to be
|
|
write-only because the act of compiling the filter expression into the
|
|
BPF instructions loaded into the kernel can make it difficut to
|
|
accurately reconstruct the original text filter. The end result is that
|
|
while ipf.conf() can be easy to read, understanding the output from
|
|
ipfstat might not be.
|
|
.SH VARIABLES
|
|
.PP
|
|
This configuration file, like all others used with IPFilter, supports the
|
|
use of variable substitution throughout the text.
|
|
.PP
|
|
.nf
|
|
nif="ppp0";
|
|
pass in on $nif from any to any
|
|
.fi
|
|
.PP
|
|
would become
|
|
.PP
|
|
.nf
|
|
pass in on ppp0 from any to any
|
|
.fi
|
|
.PP
|
|
Variables can be used recursively, such as 'foo="$bar baz";', so long as
|
|
$bar exists when the parser reaches the assignment for foo.
|
|
.PP
|
|
See
|
|
.B ipf(8)
|
|
for instructions on how to define variables to be used from a shell
|
|
environment.
|
|
.DT
|
|
.SH FILES
|
|
/dev/ipf
|
|
/etc/ipf.conf
|
|
.br
|
|
/usr/share/examples/ipf Directory with examples.
|
|
.SH SEE ALSO
|
|
ipf(8), ipfstat(8), ippool.conf(5), ippool(8)
|