On Wed, Apr 8, 2026 at 3:30 PM Jacob Welsh <[email protected]> wrote:

> In this case, if the host holds both public and private addresses, it will
> incorrectly select the private one as default source address for outbound
> connections.
>
> Am I to understand that an OpenBSD router cannot be made to route packets
> as it's told? Why not?
>

OpenBSD routes packets as it's told, but it takes its instructions from the
packets
being routed. You want to give OpenBSD alternative instructions. This is
doable
but you need to be clear about when OpenBSD should override what was asked.

First, some background for people who find this thread later via a keyword
search,
for whom some context needs to be set:

General practice is to give a host one address. If you give it the private
address
then there will be a gateway or proxy that directs packets sent to the
public
address to the designated host on its private address. OpenBSD can do this,
one possible way is using PF:

    pass in on egress inet to {host1-public} rdr-to {host1-private}

If you give the host the public address then that host needs to be "on" the
Internet. In my experience this is normally done by setting up a "dmz"
network
for hosts to attach to, which is in turn bridged to your Internet uplink
through
some kind of firewall (e.g. OpenBSD configured for bridge filtering).

If you want to give a host multiple addresses, the general practice is to
use multiple interfaces. The host interface connected to your internal
network
will have its private IP address, and the host interface connected to the
dmz
network will have its public IP address. You can have a router on the
internal
network that routes packets from that host to the Internet, and you can have
a bridging firewall that bridges the dmz network to the actual Internet.
You can even have a single OpenBSD gateway do both, but it is most
conveniently done by having 4 interfaces on your OpenBSD gateway: two
interfaces configured for IP forwarding with NAT, and two interfaces set
up for bridging. And your PF rules would apply the appropriate rules
based on whether the bridging or forwarding interfaces were in use.

If the host is trying to use both IP addresses on the same interface, then
when
you receive packets on the internal network from that host, you will need to
do something different for each packet depending on whether the packet has
its source IP set to the private or public address. Effectively some packets
will need to be routed while other packets will need to be bridged. The
key complication here is that your router needs to know how to forward
replies. When routing, that means your router needs to have an interface
on the correct network, which in this case means your router's internal
interface needs to have an external IP address so it can send packets to
other external IP addresses that happen to be held by internal hosts.
Which in turn means your routing table has to know that some external
IPs get routed out the external interface, while other external IPs get
routed out the internal interface. This is not how routing is normally set
up, for good reasons, but you've been handling it with -iface.

In this specific case, when you see that an internal host is initiating an
outbound
connection from its private address, you can NAT that connection so that on
the
outside it appears to have come from the public IP instead of the private
IP. But
instead of using the IP of your NAT pool, use the dedicated public IP for
that
specific host instead:

    pass out on egress inet from {host1-private} nat-to {host1-public}

My understanding of the flow of packets is generally:

1. receive packet on an interface
2. if state exists, accept packet. otherwise PF 'in' rules are applied.
3. kernel directs packet to another interface based on bridging or routing
rules.
4. if state exists, send packet from interface. otherwise PF 'out' rules
are applied.

Step 4 is where nat-to rules are used, but at that point bridging or
routing decisions
are already done. If you want to influence bridging or routing decisions,
you need to
do it in step 2, e.g. using rdr-to.

-ken

Reply via email to