On Thu, Jan 18, 2024 at 01:01:31AM +0100, Alexandr Nedvedicky wrote:
> If I remember correct it's been pointed out in 2020.
> Unfortunately I'm not able to find details.
> 
> the diff was not accepted because we could not find
> a good way to establish concept of primary address (:0)
> and alias addresses in IPv6. It sort of works for IPv4 but
> in case of IPv6 there is some friction.
> 
> I think currently the :0 is assigned to the first
> IP address assigned to interface (hence primary)
> In case of IPv6 the :0 is always a link-local address.

There are certainly real world use cases where one or more modifiers similar
to :0 would be useful on interfaces with multiple IPv6 addresses.

We use cellular data links as redundant backups for fixed-line connections
which need high availability.  Typically the machines on the LAN have fixed
static IPs, some globally routable and others locally scoped.

Ideally, the backup link would provide access to the same block of global IPs,
(and there are indeed ISPs out there who are happy to arrange that at a cost),
but in reality the solution is usually a standard data service which provides
a random dynamically assigned /64.

(In the old days where the setup was generally 'leased line with ISDN backup'
 it was _much_ easier to get the same IP ranges on both.  But back then the
 whole thing was often IPv4 only anyway.)

This shouldn't pose a problem, as we can run NAT on the IPv6 block.  Not
one-to-many NAT, but just using the subnet part of the static addresses with
the dynamically assigned prefix.

Unfortunately, whilst that would be straightforward if the cellular link had
a static prefix, when it is assigned dynamically via SLAAC there is no way
to configure the necessary translation directly within pf.conf.

Ugly hacks are possible using shell scripts to monitor address changes and
update the pf config as necessary, but that didn't seem like a good long-term
solution.

Alternatively, it's possible to do one-to-many NAT over IPv6, and masquerade
all of the LAN machines to the router's global IPv6 address.

Except that the router usually has multiple globally-scoped IPv6 addresses,
as well as link-local and ULA addresses.  By default, with a simple NAT rule
in pf.conf, any of these except the link-local addresses might up being used
as the re-written source IP.

Unfortunately, this simple avoiding of the link-local address doesn't ensure
that the re-written address has sufficient scope to reach the destination,
because ULA addresses are not excluded from the pool.

However, as long as you don't have ULA addresses configured, you should always
reach the destination, but you can't force the same behaviour for NAT'ed
addresses that you would see by default on outbound connections originated
directly from the router, which would prefer the IPv6 temporary addresses
over the address derived from the NIC's MAC address.

In these situations, it would be nice to have a modifier like :0 that
returns 'the first non-deprecated IPv6 address with global scope'.

That way, you could do something like:

pass out on rge2 from foobar nat-to (rge2:1)

where :1 is the hypothetical new modifier described above.

Currently, if you do:

pass out on rge2 from foobar nat-to (rge2:0)

The link-local address will be used at the new source address, which is
useless in most cases.

Interestingly, there seems to be a bug or limitation in the handling of the
'bitmask' pool type in this case, because if you do:

pass out on rge2 from foobar nat-to (rge2:0) bitmask

The re-written source address does NOT have it's lower bits changed to match
those of the real source address.

OTOH, with:

pass out on rge2 from foobar nat-to 2001:db8:ffff::1/64 bitmask

The lower bits are correctly re-written.

Note that this is not the limitation described in the manual page that
'bitmask' is not permitted with more than one redirection address.  The rule
with '(rge2:0) bitmask' is correctly loaded with no errors, but the behaviour
is buggy.

Trying to use '(rge2) bitmask' will obviously fail with 'interface is not
supported by pool type', and makes no sense anyway, as the interface typically
has multiple IPv6 prefixes configured.

Using 'source-hash', it's usually possible to avoid the link-local addresses,
but there are no guarantees, as the ordering of configured addresses can
change:

pass out on rge2 from foobar nat-to 2001:db8:ffff::1/64 source-hash
0x00000000000000000000000000000001

Overall, it's possible to achieve a configuration that works, but there are
certainly times where a more comprehensive set of address modifiers that were
designed around these IPv6 specific scenarios would be very useful.

Reply via email to