There are four methods for using an address pool:
Except for the round-robin method, the address pool must be expressed as a CIDR (Classless Inter-Domain Routing) network block. The round-robin method will accept multiple individual addresses using a list or table.
The sticky-address option can be used with the random and round-robin pool types to ensure that a particular source address is always mapped to the same redirection address.
In this example, a pool of two addresses is being used to translate outgoing packets. For each outgoing connection, PF will rotate through the addresses in a round-robin manner.
match out on egress inet nat-to { 192.0.2.5, 192.0.2.10 }
One drawback with this method is that successive connections from the
same internal address will not always be translated to the same
translation address.
This can cause interference, for example, when browsing websites that track
user logins based on IP address.
An alternate approach is to use the source-hash method so that
each internal address is always translated to the same translation address.
To do this, the address pool must be a
CIDR
network block.
This rule uses the address pool 192.0.2.4/31 (192.0.2.4 - 192.0.2.5) as the translation address for outgoing packets. Each internal address will always be translated to the same translation address because of the source-hash keyword.match out on egress inet nat-to 192.0.2.4/31 source-hash
web_servers = "{ 10.0.0.10, 10.0.0.11, 10.0.0.13 }"
match in on egress proto tcp to port 80 rdr-to $web_servers \
round-robin sticky-address
Successive connections will be redirected to the web servers in a
round-robin manner with connections from the same source being sent to
the same web server.
This "sticky connection" will exist as long as there are states that
refer to this connection.
Once the states expire, so will the sticky connection.
Further connections from that host will be redirected to the next web
server in the round robin.
One additional piece of information that's needed to do this is the IP address of the adjacent router on each Internet connection. This is fed to the route-to option to control the destination of outgoing packets.
The following example balances outgoing traffic across two Internet connections:
lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "198.51.100.100"
ext_gw2 = "203.0.113.200"
pass in on $int_if from $lan_net route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin
The route-to option is used on traffic coming in on the
internal interface to specify the outgoing network interfaces
that traffic will be balanced across along with their respective gateways.
Note that the route-to option must be present on each filter
rule that traffic is to be balanced for (it cannot be used with match
rules).
To ensure that packets with a source address belonging to $ext_if1 are always routed to $ext_gw1 (and similarly for $ext_if2 and $ext_gw2), the following two lines should be included in the ruleset:
Finally, NAT can also be used on each outgoing interface:pass out on $ext_if1 from $ext_if2 route-to ($ext_if2 $ext_gw2) pass out on $ext_if2 from $ext_if1 route-to ($ext_if1 $ext_gw1)
match out on $ext_if1 from $lan_net nat-to ($ext_if1) match out on $ext_if2 from $lan_net nat-to ($ext_if2)
A complete example that load balances outgoing traffic might look something like this:
lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "198.51.100.100"
ext_gw2 = "203.0.113.200"
# nat outgoing connections on each internet interface
match out on $ext_if1 from $lan_net nat-to ($ext_if1)
match out on $ext_if2 from $lan_net nat-to ($ext_if2)
# default deny
block in
block out
# pass all outgoing packets on internal interface
pass out on $int_if to $lan_net
# pass in quick any packets destined for the gateway itself
pass in quick on $int_if from $lan_net to $int_if
# load balance outgoing traffic from internal network.
pass in on $int_if from $lan_net \
route-to { ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } \
round-robin
# keep https traffic on a single connection; some web applications,
# especially "secure" ones, don't allow it to change mid-session
pass in on $int_if proto tcp from $lan_net to port https \
route-to ($ext_if1 $ext_gw1)
# general "pass out" rules for external interfaces
pass out on $ext_if1
pass out on $ext_if2
# route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
# $ext_if2 and $ext_gw2
pass out on $ext_if1 from $ext_if2 route-to ($ext_if2 $ext_gw2)
pass out on $ext_if2 from $ext_if1 route-to ($ext_if1 $ext_gw1)