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.
host foo and !port ftp and !port ftp-dataThis 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:
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-dataBoth these expressions mean ``pass packets that are bound for the FTP control or data ports''.
The primitive expressions are:
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.
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.
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 port port matches only TCP packets from port port.
, /, &, |, <<, 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 |
| 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 |
| Protocol | Source port | Destination port |
|---|---|---|
| TCP | tcp[0:2] | tcp[2:2] |
| UDP | udp[0:2] | udp[2:2] |
| 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 |
RFC 768, RFC 791, RFC 792, RFC 793, RFC 1060, RFC 1122, RFC 1579
``Obtaining RFCs from the Internet''
no_telnet ! port telnetThe 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)
some_tcp1 !(tcp[0:2] < 1024 and tcp[13:1] & 0x10 == 0) some_tcp2 !(tcp[0:2] < 1024 and tcp[2:2] < 1024 and tcp[13:1] & 0x10 == 0)For example, rcp(1tcp), rlogin(1tcp), and rsh(1tcp) clients always initiate connections using source ports below 1024. The rlogind(1Mtcp) and rshd(1Mtcp) server daemons expect this as part of the scheme for authenticating a client.
The following filter blocks all incoming TCP connection requests to non-privileged ports:
stop_tcp_in !((tcp[2:2] > 1023) and (tcp[13:1] & 0x10 == 0))Such a scheme is desirable if you want to prevent access to services, such as the X Windows server, which live at port numbers greater than 1023. Unfortunately, it also blocks data transfer from external FTP servers which try to open a port on a local FTP client. If you apply such a filter, local FTP clients must turn on passive mode to allow data transfers. See ftp(1tcp) and RFC 1579 for more information.
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 ``\''.
!(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.
!(dst port tftp)
!(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.
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[2:2] == 6000)
!(icmp[0] == 3 or icmp[0] == 5)
!((ip[0] & 0xf > 5) and \
(ip[20:1] == 0x83 or ip[20:1] == 0x89))
!(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 and dst net 152.124)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 and dst net 200.20.34)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 and dst net 200.23)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 and 200.23.15 for the source and destination network addresses:
!(src net 200.23.14 and dst net 200.23.14) and \ !(src net 200.23.15 and dst net 200.23.15) and \ !(src net 200.23.14 and dst net 200.23.15) and \ !(src net 200.23.15 and dst net 200.23.14)Obviously, such a test becomes unwieldy if you have many contiguous CIDR network addresses.