NAT with pf
Subject:   Thank you, Jacek
Date:   2003-12-15 15:18:19
From:   anonymous2
Response to: Incorrect Information in this Article

Thanks for the offer, although I already have it working. I will explain it for other people's benefit, though.

I want to rdr certain ports to certain internal hosts using pf, at the same time as using NAT on the external interface. The rdr rules work fine for this, but as you noted, internal hosts cannot connect to the external ip address with the single rdr rule. So you add a rdr rule on the internal NIC, right? Wrong. That's what that quote from the pf documentation is about--such a method doesn't work.

3 Hosts, let's give them real IPs just to be clear:

Gateway ($int) ($ext)

We do NAT on ext, and redirect port 5555 to Host2:

nat on $ext proto $tcp from to any -> $ext

rdr on $ext proto tcp from any to $ext port 5555 -> Host2 port 5555

Any external host connects to, the port is forwarded using the rdr rule. Host2 sees the forward, responds to the external host, and everything works.

But what if Host3 connects to It doesn't work, so we add another rdr rule (as you suggested):

rdr on $int proto tcp from any to $ext port 5555 -> Host2 port 5555

Host3 sends a packet to, which comes in on $int. Packet looks like this:

Src:; Dest=

Then, pf redirects the packet to So far, so good. Host2 responds, with a packet like this:

Src:; Dest

Host3 sees this packet and throws it away. Host3 says, essentially "I never sent anything to is it responding?" The rdr rule will never work because the wrong host responds to Host3's request to connect to

The solution from OpenBSD's pf page is to add a NAT rule that changes the source ip:

nat on $int proto tcp from to Host2 port 5555 -> $int

What this does is make the initial packet appear to come from $int. Host2 then responds to $int, where pf's NAT will translate the source ip back to, so that Host3 thinks it's talking to As the page notes, this is not exactly an elegant solution, but it works (they add another rule to tighten it a bit).

I don't believe simply adding an rdr as you did in the article works, although I may have misunderstood your configuration. The internal rdr does forward the port, but the responding packet will be dropped as it comes from the wrong host. NAT similar to the external interfaces is needed in order to make the internal host believe it is talking to the external ip.


1 to 1 of 1
  1. That "$tcp" should be "tcp"
    2003-12-15 15:19:29  anonymous2 [View]

1 to 1 of 1