Setting up

We assume that the machine has acquired another network card or at any rate you have set up a network connection from your local network, via PPP or other means. We will not consider the specific interface configurations.

For the discussion and examples below, only the interface names will differ between a PPP setup and an Ethernet one, and we will do our best to get rid of the actual interface names as quickly as possible.

First, we need to turn on gatewaying in order to let the machine forward the network traffic it receives on one interface to other networks via a separate interface. Initially we will do this on the command line with sysctl, for traditional IP version four

# sysctl net.inet.ip.forwarding=1

and if we need to forward IP version six traffic, the command is

# sysctl net.inet6.ip6.forwarding=1

In order for this to work once you reboot the computer at some time in the future, you need to enter these settings into the relevant configuration files.

In OpenBSD and NetBSD, you do this by editing /etc/sysctl.conf, by changing the lines you need, like this

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1

On FreeBSD, you conventionally do the corresponding change by putting these lines in your /etc/rc.conf

gateway_enable="YES" #for ipv4
ipv6_gateway_enable="YES" #for ipv6

The net effect is identical, the FreeBSD rc script sets the two values via the sysctl command. However, a larger part of the FreeBSD configuration is centralized into the rc.conf file.

Are both of the interfaces you intend to use up and running? Use ifconfig -a, or ifconfig interface_name to find out.

If you still intend to allow any traffic initiated by machines on the inside, your /etc/pf.conf could look roughly like this[1]:

ext_if = "ep0" # macro for external interface - use tun0 for PPPoE
int_if = "ep1"  # macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
match out on $ext_if from $localnet nat-to ($ext_if)
block all
pass inet proto tcp from { self, $localnet } 

or if you are running a pre-OpenBSD 4.7 version:

ext_if = "ep0" # macro for external interface - use tun0 for PPPoE
int_if = "ep1"  # macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -> ($ext_if) 
block all
pass inet proto tcp from { self, $localnet } 

Note the use of macros to assign logical names to the network interfaces. Here 3Com cards are used, but this is the last time during this lecture we will find this of any interest whatsoever. In truly simple setups like this one, we may not gain very much by using macros like these, but once the rule sets grow somewhat larger, you will learn to appreciate the readability this adds to the rule sets

Also note the rules where the network address translations happens. match rules were introduced in OpenBSD 4.6 as a way to apply actions without affecting the block or pass status. In OpenBSD 4.7, nat was demoted to nat-to, one of several possible actions on match or pass rules. In both cases this is where we handle the network address translation from the non-routable address inside your local net to the sole official address we assume has been assigned to you.

The parentheses surrounding the last part of the nat rule ($ext_if) serve to compensate for the possibility that the IP address of the external interface may be dynamically assigned. This detail will ensure that your network traffic runs without serious interruptions even if the external IP address changes.

On the other hand, this rule set probably allows more traffic than what you actually want to pass out of your network. In one network where I have done a fair bit of work over the years, the equivalent macro is

client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http,\
                https, 446, cvspserver, 2628, 5999, 8000, 8080 }"

with the rule

pass inet proto tcp from $localnet to port $client_out

This may be a somewhat peculiar selection of ports, but it's exactly what the users in that network need. Some of the numbered ports were needed for specific applications. Your needs probably differ in some specifics, but this should cover at least some of the more useful services.

In addition, we have a few other pass rules. We will be returning to some of the more interesting ones rather soon. One pass rule which is useful to those of us who want the ability to administer our machines from elsewhere is

pass in inet proto tcp to port ssh

or for that matter

pass in inet proto tcp to $ext_if port ssh

whichever you like. Lastly we need to make the name service and time keeping work for our clients

udp_services = "{ domain, ntp }"

supplemented with a rule which passes the traffic we want through our firewall:

pass quick inet proto { tcp, udp } to port $udp_services keep state

Note the quick keyword in this rule. We have started writing rule sets which consist of several rules, and it is time to take a look at the relationships between the rules in a rule set. The rules are evaluated from top to bottom, in the sequence they are written in the configuration file. For each packet or connection evaluated by PF, the last matching rule in the rule set is the one which is applied. The quick keyword offers an escape from the ordinary sequence. When a packet matches a quick rule, the packet is treated according to the present rule. The rule processing stops without considering any further rules which might have matched the packet. Quite handy when you need a few isolated exceptions to your general rules.

It is worth noting that we used one rule for both the domain name service (domain and time synchronization (ntp). The most important reason for this is that both protocols under certain circumstances communicate alternately over TCP and UDP.

Notes

[1]

For dialup users who conventionally use some variant of PPP for their Internet connections, the external interface is the tun0 pseudo-device. Broadband users such as ADSL subscribers tend to have an Ethernet interface to play with, however for a significant subset of ADSL users, specifically those using PPP over Ethernet (PPPoE), the correct external interface will be the tun0 pseudo-device, not the physical Ethernet interface.