A packet filter
provides a means of limiting what packets pass through, bringup
or reset the idle timer (if appropriate) for a network interface.
Files named for an individual MDI network
interface in the directory hierarchy
/etc/pf.d/protocol store
packet filter specifications for that interface.
For example, the file /etc/pf.d/IP/net0
would hold IP packet filter definitions
for the net0 interface.
The file /etc/pf.d/IP/ppp
stores packet filter specifications for the Internet protocols
running over the PPP serial-line protocol.
Packet filters for a LAN network adapter
can be loaded into the kernel using
pushfilter(1M).
Separate filters may be used for the incoming and outgoing streams of an
interface, or the same filter may be applied to both streams.
A filter file can contain several entries which must
be identified by tags which are unique to that file.
Each entry specifies the characteristics of the filter.
Only packets for which the expression evaluates as true are
considered to meet the selection criteria.
If no expression is present, all packets are considered
to meet the selection criteria, and any existing
filter for the interface is unloaded.
Each field is separated from others by white space or a tab.
Each entry may consist of up to 8192 characters, and can
extend beyond a single line by ending each line of the
entry except the last with a backslash (``\'').
Comments begin with a ``#'' and extend to the end of the line.
Blank lines, or lines beginning with a ``#'', are ignored.
For PPP interfaces, an IPCP entry in
the PPP configuration file
can include the specifications bringup,
keepup, passin and passout
to specify which filters should be configured
when the link is established.
The filters may be defined using the
ppptalk(1M)
command, but they will not take effect until the link is
taken down and brought up again.
For SLIP interfaces, the -p option with a
tag value as its argument should be specified to the
slattach(1Mtcp)
command).
Each PPP and SLIP
interface may specify its own filters or
two or more interfaces may share a filter.
Expressions
An expression consists of one or more primitives, and should
evaluate to true for packets that should be allowed to traverse the interface
(or bring up or keep up a PPP link).
and to false for packets that should be discarded.
Complex filter expressions may be built up by using the operators
and, and or
to combine primitives, and not (or !)
to negate the sense of primitives, for example:
host foo and !port ftp and !port ftp-data
This means ``pass packets going to or coming from the host named
foo provided that they not going to or coming from the
FTP control or data ports''.
Negation (not) has highest precedence. Alternation (or)
and concatenation (and) have equal precedence and associate left to
right. Note that concatenation requires explicit and tokens,
not just simple juxtaposition.
Primitives may be grouped using parentheses. For example, the previous
expression could be written as:
host foo and !(port ftp or port ftp-data)
Note that alternation and concatenation operators obey the usual
conversion rules with regard to the placement of the not operator
and parentheses:
notAand not B is equivalent to not (Aor B)
notAor not B is equivalent to not (Aand B)
Multiple primitives within the same entry which
only differ by the value of the primitive variable may be combined.
For example, the following expressions are equivalent:
dst port ftp or dst port ftp-data
dst port ftp or ftp-data
Both these expressions mean ``pass packets that are
bound for the FTP control or data ports''.
The primitive expressions are:
[ dst | src ] hosthost
True if the IP source or destination field of the packet
is host, which may be either an address or a host name.
If dst is specified, the expression is
true only if the destination field is
host. If src is specified,
the expression is true only if the source
field is host.
[ dst | src ] netnet
True if the IP source or destination field of the packet
includes a network number of net,
which may be either an address or a host name.
If dst is specified, the expression is
true only if the destination field
includes a network number of net.
If src is specified, the expression is true only if the source
field includes a network number of net.
[ tcp | udp ][ dst | src ] portport
True if the packet is IP/TCP or
IP/UDP and has a source or destination port value
of port, which can be a number or a name
defined in /etc/services.
If a name is used, both the port number and protocol are
checked. If a number or ambiguous name is used, only the
port number is checked. For example, dst port 513
will be true for both TCP rlogin and
UDP rwho traffic, and
port domain will be true for both TCP and
UDP DNS traffic.
If dst is specified, the expression is
true only if the destination field
has a destination port value of port.
If src is specified, the expression is true only if the source
field has a destination port value of port.
Specifying a protocol further limits the match.
protocol can one of icmp,
tcp. or udp.
For example , tcp src portport
matches only TCP packets from port port.
ipbroadcast
True if the packet is an IP broadcast packet. It checks for
both the all-zeroes and all-ones broadcast conventions and
looks up the local subnet mask.
ipmulticast
True if the packet is an IP multicast packet.
ipprotoprotocol
True if the packet is an IP packet of protocol
type protocol, which can be a number or one of
icmp, udp, or tcp.
icmp
Equivalent to ip proto icmp.
tcp
Equivalent to ip proto tcp.
udp
Equivalent to ip proto udp.
exprrelopexpr
True if the relation holds. The relational operator,
relop, is one of >, <, >=, <=, ==, or !=.
expr is an arithmetic expression composed of
integer constants, the
binary operators +, -, , /, &, |, <<, or >>,
the unary operator -,
the length function (len),
and special operators that can be used to access packet data.
To access data inside the packet, use one of the following syntax forms:
protocol[expr] protocol[expr:size]
protocol is one of ip, tcp, udp,
or icmp, and indicates the
protocol header to which the index operation will be applied.
expr is an offset in bytes relative to the start of
the header.
size is optional and indicates the number of bytes in the
field of interest; it can be either 1, 2, or 4.
If not specified, the default size is 1 byte.
Tests on TCP segments and UDP datagrams,
such as tcp[0] and udp[0],
are always applied to the TCP or UDP header.
They are never applied to an intervening fragment.
The following table shows useful tests for IP headers.
Test
Description
ip[0] & 0xf0 == 1024
Examine the protocol version, and match if this is 1024 (4<<8)
for IPv4
ip[0] & 0xf > 5
Examine the length of the header (number of 32-bit words), and
match all IP datagrams that have options defined
ip[0] & 0xf > 5 and ip[20] == code
Examine the IP option header, and match its value
against code. Example values are 0x7 for record route,
0x44 for timestamp, 0x83 for loose source routing, and 0x89 for
strict source routing
ip[1] & 0x10 != 0
Examine the type-of-service field, and match if its
``Minimize delay'' flag is set
ip[2:2] > len
Examine the total length of the datagram (in bytes), and
match if this is greater than len
ip[6:2] & 0x1fff == 0
Examine the fragment offset of the datagram (in bytes),
and match only unfragmented datagrams and fragment zero of
fragmented IP datagrams
ip[6:2] & 0x2000 != 0
Match if the ``More fragments'' flag is set.
Unfragmented datagrams and final fragments do not set this
ip[6:2] & 0x3fff == 0
Match unfragmented datagrams only
ip[6:2] & 0x4000 != 0
Match if the ``Don't fragment'' flag is set
ip[9:1] == protocol
Examine the protocol field, and match its value against protocol.
Example values are 1 for ICMP, 2 for IGMP,
6 for TCP, and 17 for UDP
A generic test for ICMP message types is
icmp[0] == type [ and icmp[1] == code ].
The following table shows some useful types and codes.
Type
Code
Description
0
0
Echo reply (used by ping)
3
0-15
Destination unreachable
0
Network unreachable
1
Host unreachable
3
Port unreachable
4
Fragmentation prevented by set ``Don't fragment'' bit
5
Source route failed
9
Destination network prohibited
10
Destination host prohibited
13
Communication prohibited by filtering
4
0
Source quench
5
0-3
Redirect
0
Redirect for network
1
Redirect for host
8
0
Echo request (used by ping)
9
0
Router advertisement
10
0
Router solicitation
11
0
Time-to-live fell to 0 during transit (used by traceroute)
1
Time-to-live fell to 0 during reassembly
12
0-1
Parameter problem
13
0
Timestamp request
14
0
Timestamp reply
17
0
Address mask request
18
0
Address mask reply
The following tests access the source and destination
port numbers defined in TCP and UDP headers.
Protocol
Source port
Destination port
TCP
tcp[0:2]
tcp[2:2]
UDP
udp[0:2]
udp[2:2]
A generic test for flag bits being set in a TCP header is
tcp[13:1] & flag != 0 where flag
can be any of the following values ORed together:
Flag
Bit
Meaning if set
0x01
FIN
Finished sending data
0x02
SYN
Synchronize sequence numbers
0x04
RST
Reset a connection
0x08
PSH
Push the data to an application
0x10
ACK
Acknowledge a sequence number
0x20
URG
There is urgent data
len
A function that returns the length of the packet in bytes. This function
should be used in a relational expression to return a true or false value.
lesslength
True if the packet has a length less than or equal to
length.
This is equivalent to len <= length.
greaterlength
True if the packet has a length greater than or equal
to length.
This is equivalent to len >= length.
byteindexoperatorvalue
Evaluate the truth of applying operatorvalue to the
byte at offset index inside the packet. Possible
operators are & (bitwise and), | (bitwise or), < (less than),
> (greater than), and = (equals).
index and value must both be integer values.
The following specification blocks all incoming and outgoing
telnet packets:
no_telnet ! port telnet
The following example filter blocks packets from the
telnet server port that are trying to initiate a
TCP connection to a port above 1023:
some_telnet !(src port telnet and tcp[2:2] > 1023 and tcp[13:1] & 0x10 == 0)
These packets are blocked because the telnetd server should not be
initiating TCP connections.
The ACK bit (referenced by tcp[13:1] & 0x10)
is only set to 0 in the first packet sent to establish a connection).
A situation in which such packets are arriving might indicate an attack.
A general purpose filter to block such packets from ports below 1024 would be:
some_tcp !(tcp[0:2] < 1024 and tcp[2:2] > 1023 and tcp[13:1] & 0x10 == 0)
The following filters could be used with an outgoing PPP link:
bringup !(port ntp or who or route or timed or bgp) and \
!(ip proto 8 or 63 or 89) and \
!(icmp[0] == 9 or icmp[0] == 10)
passin
passout
keepup !(port ntp or who or route or timed or bgp) and \
!(ip proto 8 or 63 or 89) and \
!(icmp[0] == 9 or icmp[0] == 10)
This specification does not allow ntp, rwhod,
routed, timed, gated (BGP,
EGP, HELLO, and OSPF), and ICMP
router advertisement or solicitation (Internet Router Discovery)
packets to bring up or keep up the link.
All packets are allowed to pass through the link in both directions.
The final example shows filter components that
could be used to implement a simple firewall
on the incoming stream of a gateway
interface that is configured for routing.
(For PPP, it would be applied as
the passin filter for the interface.)
These components should be combined using and,
and newlines should be escaped using ``\''.
Limit DNS enquiries
to a number of well-known name servers (such as root domain servers and
those of your service provider), and other servers that need to
access name service in your domain.
!(dst port domain and \
! host server_1 and \
! host server_2 and \
. . .
! host server_N)
You can also use the xfrnets
directive in the named.boot file on the primary name server to
define the only name servers to which it will send zone transfers.
If the gateway machine is configured as a fake primary name server
for your domain, it only needs to define A and PTR
records for hosts that should be visible to the outside world plus
MX records needed to deliver email.
Any machine that needs to access external servers that perform
double-reverse lookups (for example, FTP servers) will
also need to be listed.
To prevent outsiders gaining information about hosts on your
internal networks, the fake name server does need not to define
the hosts by the names by which they are known internally.
All that is required is that the IP address and name
for each host are consistent regardless of whether an IP address
is used to look up a host name or a host name is used to look up an
IP address. If external servers need to perform double-reverse
lookups, you can choose not to filter DNS requests
at all, or you can add the servers' IP addresses to the list of
servers with which DNS messages (queries and responses)
can be exchanged.
Real name servers within the domain must use the
forwarders and options forward-only directives in their
named.boot file to point to the fake name server. It will
handle DNS messages (queries and responses) on their behalf.
The resolv.conf files on the hosts within the domain should
point at the real internal name servers not at the fake name
server. The resolv.conf file on
the fake name server should also point at internal
name servers so that client applications
such as agents for mail delivery can work correctly.
Stop attempts to gain access to files using TFTP.
!(dst port tftp)
Block ports that are commonly used in attacks on systems.
!(dst port link) and \
!(dst port sunrpc or 2049) and \
!(tcp dst port exec or login or shell) and \
!(dst port telnet) and \
!(dst port printer) and \
!(dst port uucp)
The TCP ports 513 (login) and 514 (shell)
are used with rlogin and rcmd.
You may choose to enable access to these for certain source hosts.
NOTE:
In these examples,
we specify filter elements that block
certain destination service ports,
and we apply the filter to the
incoming stream of the gateway interface.
This discards incoming request packets
for these services from outside.
Normally, we do not want to block outgoing
requests for these service.
You might choose to unblock the telnet port
to allow certain hosts to have access to your site.
!((tcp dst port telnet) and \
! host friend_1 and \
! host friend_2 and \
. . .
! host friend_N)
TCP port 6000 is normally
used by the first X Windows server on
systems.
This port can also be protected from attack.
!(tcp[2:2] == 6000)
Block ICMP ``Destination unreachable'' and ``Redirect'' messages.
!(icmp[0] == 3 or icmp[0] == 5)
Deny access to loose and strict source-routed packets by
examining the code value of the IP option header.
This can also be achieved by using inconfig to set the value
of ipnonlocalsrcroute to 0 on the gateway machine.
!((ip[0] & 0xf > 5) and \
(ip[20:1] == 0x83 or ip[20:1] == 0x89))
Protect against ``IP spoofing'' attacks by filtering out packets
whose source and destination addresses are both on your local
network. These packets would only appear on a gateway interface
if an external host was pretending to be a local machine. If the
host addresses at the remote end of the gateway link should genuinely be
in the IP address space of your network, do not apply this test.
!(src net local_net and dst net local_net)
As an example,
if your internal networks used subnetted addresses formed from the
class B address 152.124.0.0, this filter would be appropriate:
!(src net 152.124.0.0 and dst net 152.124.0.0)
Similarly, the following filter would be appropriate if your
internal network addresses were based on the class C address 200.20.34.0:
!(src net 200.20.34.0 and dst net 200.20.34.0)
If your internal networks used the two contiguous CIDR
addresses 200.23.14.0 and 200.23.15.0, you could apply the following filter:
!(src net 200.23.0.0 and dst net 200.23.0.0)
However, this filter has the disadvantage of blocking access
to and from all external addresses that also begin with 200.23.
An alternative method is to specify all possible
combinations of 200.23.14.0 and 200.23.15.0 for the
source and destination network addresses:
!(src net 200.23.14.0 and dst net 200.23.14.0) and \
!(src net 200.23.15.0 and dst net 200.23.15.0) and \
!(src net 200.23.14.0 and dst net 200.23.15.0) and \
!(src net 200.23.15.0 and dst net 200.23.14.0)
Obviously, such a test becomes unwieldy if you have many
contiguous CIDR network addresses.
NOTE:
SCO TCP/IP in
UnixWare 7, SCO OpenServer Release 5, and UnixWare 2.1 support randomization of
initial TCP sequence numbers. This makes it very unlikely that
an attacker could guess the correct sequence number to use.