Tore Anderson 06c567b9cb Merge pull request #49 from jmbaur/chain-priority
Decrease priority number for prerouting chain. This makes clatd work out of the box on NixOS, which does IPv6 reverse path filtering at priority `mangle + 10`.
2025-03-21 08:19:36 +01:00
2025-02-09 10:53:15 +01:00

=head1 NAME

B<clatd> - a CLAT / SIIT-DC Edge Relay implementation for Linux

=head1 DESCRIPTION

B<clatd> implements the CLAT component of the 464XLAT network architecture
specified in I<RFC 6877>. It allows an IPv6-only host to have IPv4 connectivity
that is translated to IPv6 before being routed to an upstream PLAT (which is
typically a Stateful NAT64 operated by the ISP) and there translated back to
IPv4 before being routed to the IPv4 internet. This is especially useful when
local applications on the host requires actual IPv4 connectivity or cannot
make use of DNS64 (for example because they use legacy AF_INET socket calls,
or if they are simply not using DNS64).

B<clatd> may also be used to implement an SIIT-DC Edge Relay as described in
I<RFC 7756>. In this scenario, the PLAT is in reality a SIIT-DC Border Relay
(see I<RFC 7755>) instead of a Stateful NAT64 (see I<RFC6146>). When used as a
SIIT-DC Edge Relay, you will probably want to manually configure the settings
I<clat-v4-addr>, I<clat-v6-addr>, and I<plat-prefix> to mirror the SIIT-DC
Border Relay's configuration.

It relies either on the software package TAYGA by Nathan Lutchansky or on the
kernel module nat46 by Andrew Yourtchenko for the actual translation of packets
between IPv4 and IPv6 (I<RFC 6145>) TAYGA may be downloaded from its home page
at L<http://www.litech.org/tayga/>, nat46 from its repository at
L<https://github.com/ayourtch/nat46>.

=head1 SYNOPSIS

B<clatd> [options]

=head1 OPTIONS

=over

=item -q

Quiet mode; suppress normal output. This is the same as setting B<quiet=1>.
Warnings and errors are still outputted, to silence those too, repeat I<-q>.

=item -d

Enable debugging output. This is the same as setting B<debug=1>. Repeat for
even more debugging output, which is the
equivalent of setting B<debug=2>.

=item -c conf-file

Read configuration settings from B<conf-file>. See section B<CONFIGURATION>
below for more info.

=item -h, --help

Print a brief usage help and exit.

=item key=value

Set configuration B<key> to I<value>, overriding any setting found in the
configuration file. Refer to the section B<CONFIGURATION> below for more info.

=back

=head1 INVOCATION

B<clatd> is meant to be run under a daemonising control process such as
systemd or similar. It is further meant to be (re)started whenever a
network interface goes up/down as this might mean a change in the PLAT
availability or which prefixes/addresses needs to be used for the CLAT to work.
It may also be run directly from the command line. It will run until killed
with SIGINT (^C) or SIGTERM, at which point it will clean up after itself and
exit gracefully.

See the I<scripts/> directory in the source distribution for some examples on
how to invoke it it.

=head1 INSTALLATION

The following commands will quickly download and install the latest version
of B<clatd> and its dependencies:

=over

=item git clone https://github.com/toreanderson/clatd

=item sudo make -C clatd install installdeps

=back

This will install B<clatd> to /usr/sbin, plus install systemd, and/or
NetworkManager scripts if your distribution appears to be using them, and
install all the dependencies. Note that TAYGA isn't available in all RPM-based
distros (in particular RHEL and its clones). It is however available in EPEL
(see L<https://fedoraproject.org/wiki/EPEL>).

=head1 CONFIGURATION

B<clatd> is designed to be able to run without any user-supplied configuration
in most cases. However, user-specified configuration settings may be added to
the configuration file, the path to which may be given on the command line
using the I<-c> option, or if it is not, the default location
I</etc/clatd.conf> is used. Configuration settings may also be given directly
on the command line when starting B<clatd>, which takes precedence over settings
in the configuration file.

Settings are of the form B<key=value>. A list of recognised keys and their
possible values follow below:

=over

=item B<quiet=integer> (default: I<0>)

Set this to 1 to suppress normal output from B<clatd>. This is the same as
providing the command line option I<-q>. Set it to 2 to additionally
suppress warnings and errors. Note that this does not suppress debugging
output.

=item B<debug=integer> (default: I<0>)

Set this to 1 to get debugging output from B<clatd>, or 2 to get even more of
the stuff. These are the equivalent of providing the command line option I<-d>
the specified number of times.

=item B<script-up=string> (no default)

Specify a custom script to be run when B<clatd> is starting up. The invocation
of this script is the last thing that happens before TAYGA starts up, so all
the preparations have been completed at that point (i.e., the B<clat-dev>
exists and has routing/addressing configured, forwarding has been enabled, and
so on).

The script is run by the system shell, so you can do everything you could in an
interactive shell: run multiple commands by separating them by semi-colon or
double ampersands, use standard if/else statements, use variable substitutions,
redirect output to files, set up command pipelines, and so on. However it must
all be on one line, so if you want to do complex things or use some other
programming language it's probably better to put the script itself in a
separate executable file and just make B<script-up> invoke that file instead.

If the script returns a nonzero exit status, this is considered a fatal error,
and B<clatd> will abort. This can be prevented by appending I<|| true> at the
end of the script.

All of B<clatd>'s configuration settings are available as standard variables in
the script's environment (hyphens are replaced with underscores).

Logging or debug messages from the script may simply be sent to stdout, where
it will be picked up by the init system along with B<clatd>'s own output. The
script may of course consult the I<$quiet> and I<$debug> environment variables
in order to determine how much output is appropriate.

The script should not be enclosed in quotes in the configuration file (even
though it contains whitespace). For example:

B<script-up=echo `date -Ins`: clatd started on $clat_dev | tee -a ~/clatd.log>

If on the other hand you want to supply a B<script-up> containing whitespace
directly B<clatd>'s command line, quoting is required in order to prevent the
shell from splitting it up and into multiple command line arguments. For
example:

B<clatd 'script-up=ip route add 192.0.2.0/24 dev $clat_dev || true'>

=item B<script-down=string> (no default)

This works exactly the same as B<script-up>, only that this script is run right
after TAYGA has exited, before the clean-up process of restoring any settings
that were changed.

An unsuccessful exit code from B<script-down> will cause B<clatd> to exit
unsuccessfully too. Beyond that an unsuccessful exit won't change anything,
because B<script-down> is invoked at a point in time where the only thing left
for B<clatd> to do is to clean up after itself and exit anyway.

=item B<clat-dev=string> (default: I<clat>)

The name of the network device used by the CLAT. There should be no reason to
change the default, unless you plan on running multiple instances of B<clatd>
simultaneously.

=item B<clat-v4-addr=ipv4-address> (default: I<192.0.0.1>)

The IPv4 address that will be assigned to the CLAT device. Local applications
will bind to this address when communicating with external IPv4 destinations.
In a standard 464XLAT environment with a stateful NAT64 serving as the PLAT,
there should be no need to change the default.

When using B<clatd> as an SIIT-DC Edge Relay (I<RFC 7756>), you will want to
set this to the IPv4 Service Address configured in the SIIT-DC Border Relay.
This way, local applications can correctly identify which public address
they'll be using on the IPv4 internet, and will be able to provide fully
functional references to it in application-level payload, and so on.

The default address is one from I<RFC 7335>.

=item B<clat-v6-addr=[ipv6-address|shared|derived> (default: I<shared>)

The IPv6 address of the CLAT. Traffic to/from the B<clat-v4-addr> will be
translated into this address. When using B<clatd> as an SIIT-DC Edge Relay, you
will want to set this to the same IPv6 address in the Explicit Address Mapping
configured in the SIIT-DC Border Relay.

When set to I<shared> (the default), B<clatd> will re-use and share the primary
IPv6 address used by the host OS when communicating with directly with the PLAT
prefix. This allows B<clatd> to operate without requiring a separate, dedicated
IPv6 address assigned to the CLAT function. While this mode is the most
compatible with various network deployments, it comes with certain trade-offs,
see the B<LIMITATIONS> section for more information.

When set to I<derived>, B<clatd> will attempt to figure out which network
device will be used for traffic towards the PLAT, see if there is any
SLAAC-based globally scoped addresses on it (i.e., a /64 with '0xfffe' in the
middle of the Interface ID), and will if so substitute that '0xfffe' value with
'0xc1a7' ("clat") to generate a CLAT IPv6 address.

If only a non-SLAAC global address is found on the PLAT-facing device, B<clatd>
will substitute its Interface ID with a random integer and use the result as
the CLAT IPv6 address. It will only do so if the prefix length is /120 or
smaller, as otherwise the risk of IID collisions is considered to be too high.
Note that on most Perl platforms, the I<rand()> function is limited to 48 bits,
which means that for longer IIDs, the least significant bits will be all 0.

The I<derived> mode is not guaranteed to result in a working configuration, see
the B<LIMITATIONS> section for more information.

If multiple addresses are found in either category, the one that shares the
longest common prefix with the PLAT prefix will be preferred when deriving
the CLAT IPv6 address according to the algorithm described above.

=item B<ctmark> (default: I<0xc1a7> if I<clat-v6-addr=shared>, otherwise I<0>)

If set to a non-zero integer, nftables will be used to mark outgoing
connections through the CLAT with this connection tracking mark, and the Linux
kernel routing policy is set up so that only response traffic from the PLAT
that matches this connection tracking mark is routed back to the CLAT.

This means that connections initiated from the external IPv4 network via the
PLAT will not reach the CLAT, therefore the use of this feature is incompatible
with using B<clatd> as a SIIT-DC Edge Relay (I<RFC 7756>).

=item B<dns64-servers=srv1,[srv2,..]> (default: use system resolver)

Comma-separated list of DNS64 servers to use when discovering the PLAT prefix
using the method described in I<RFC 7050>. By default, B<clatd> will first try
to determine if systemd-networkd is aware of a PLAT prefix (learned from the
PREF64 Router Advertisement option, cf. I<RFC 8781>), falling back on using
DNS64 discovery towards the system resolver if it isn't.

It might be useful to override this in case your network does not advertise the
PREF64 RA option, your ISP doesn't provide you with a DNS64-enabled name
server, and you want to test B<clatd> using any of the public DNS64/NAT64
instances on the internet. The first PLAT prefix encountered will be used.

=item B<cmd-ip=path> (default: assume in $PATH)

Path to the B<ip> binary from the iproute2 package available at
L<https://www.kernel.org/pub/linux/utils/net/iproute2>. Required.

=item B<cmd-networkctl=path> (default: assume in $PATH)

Path to the B<networkctl> binary from systemd-networkd. Required in order to
use any PLAT prefix discovered by systemd-networkd from the PREF64 Router
Advertisement option (see I<RFC 8781> and the I<UsePREF64> option in
I<systemd.network(5)> for more information). The first prefix returned is used,
any others are ignored.

To prevent PLAT prefix discovery via systemd-networkd from being attempted, set
this to an empty string.

=item B<cmd-nft=path> (default: assume in $PATH)

Path to the B<nft> binary from the nftables package available at
L<https://nftables.org/projects/nftables/>. Required if I<ctmark> is set.

=item B<cmd-tayga=path> (default: assume in $PATH)

Path to the B<tayga> binary from the TAYGA package available at
L<http://www.litech.org/tayga>. Required.

=item B<cmd-ufw=path> (default: assume in $PATH)

Path to the B<ufw> binary from the UFW local firewall framework available at
L<https://launchpad.net/ufw> commonly seen on Debian-based distributions. If
this command is present on the system, and B<ufw status> reports that the
firewall is active, B<clatd> will add firewall rules ensuring traffic between
the CLAT and the PLAT prefix is allowed when it is starting up, and remove them
when it is shutting down.

If you don't want B<clatd> to add and remove UFW firewall rules, set this to an
empty string.

=item B<forwarding-enable=bool> (default: I<yes>)

Controls whether or not B<clatd> should enable IPv6 forwarding if necessary.
IPv6 forwarding is necessary for B<clatd> to work correctly. It will also
ensure that the I<accept_ra> sysctl is to '2' for all devices have it set to
'1', in order to prevent any connectivity loss as a result of enabling
forwarding.

All sysctls that are modified will be restored to their original values when
B<clatd> is shutting down.

=item B<plat-dev> (default: auto-detect)

Which network device is facing the PLAT (NAT64). By default, this is
auto-detected by performing a route table lookup towards the PLAT prefix. This
setting is used when generating the CLAT IPv6 address, adding Proxy-ND entries,
and nftables rules.

=item B<plat-prefix> (default: auto-detect)

The IPv6 translation prefix into which the PLAT maps the IPv4 internet. See
I<RFC 6052> for a closer description. By default, this is auto-detected from
DNS64 answers using the method in I<RFC 7050>.

=item B<plat-fallback-prefix> (no default)

The IPv6 translation prefix fallback. This is used if no plat-prefix is set
or auto detected.

=item B<proxynd-enable> (default: I<no> if I<clat-v6-addr=shared>, otherwise I<yes>)

Controls whether or not B<clatd> should add a Proxy-ND entry for the CLAT IPv6
address on the network device facing the PLAT. This is probably necessary
on Ethernet networks (otherwise the upstream IPv6 router won't know where to
send packets to the CLAT's IPv6 address), but likely not necessary on
point-to-point links like PPP or 3GPP mobile broadband, as in those cases
IPv6 ND isn't used. However it doesn't hurt to add Proxy-ND entries in that
case, either.

If the CLAT is sharing an IPv6 address with the host OS, this is not necessary
as the host OS will be handling NDP anyway, so Proxy-ND does get enabled by
default when I<clat-v6-addr> is set to its default value I<shared>.

Any entries added wil be removed when B<clatd> is shutting down.

=item B<route-table> (default: I<0xc1a7>)

The Linux kernel routing table used to hold the route that directs IPv6 packets
from the PLAT to the CLAT. B<clatd> will add a custom routing policy entry
(using B<ip -6 rule add>) so that this routing table is used instead of the
default one.

=item B<tayga-conffile> (default: use a temporary file)

Where to write the TAYGA configuration file. By default, a temporary file will
be created (and also deleted when B<clatd> is shutting down), but you may also
specify an explicit configuration file here, which will not be deleted on
shutdown.

=item B<tayga-v4-addr> (default: I<192.0.0.2>)

The IPv4 address assigned to the TAYGA process. This is used for emitting
ICMPv4 errors back to the host (i.e., it will show up as the first hop when
tracerouting to IPv4 destinations), and you may also ping it to verify that
the TAYGA process is still alive and well.

The default address is one from I<RFC 7335>.

=item B<v4-conncheck-enable=bool> (default: I<yes>)

Whether or not to check if the system has IPv4 connectivity before starting
the CLAT. If it does, then B<clatd> will simply exit without doing anything.
This is meant so that you can always enable B<clatd> to the system startup
scripts or network-up event scripts (such as NetworkManager's dispatcher
scripts), but not have B<clatd> interfering with native IPv4 connectivity when
this is present.

If you want to always start the CLAT whenever possible, even though the
system has IPv4 connectivity, disable this setting. You may instead use the
B<v4-defaultroute-enable> and B<v4-defaultroute-metric> settings to prevent
B<clatd> from interfering with native IPv4 connectivity.

Note that enabling B<v4-defaultroute-replace> will override
B<v4-conncheck-enable> and unconditionally disable IPv4 connectivity checking.

=item B<v4-conncheck-delay=seconds> (default: I<10>)

When performing an IPv4 connectivity check, wait this number of seconds
before actually doing anything. This is to avoid a race condition where for
example IPv6 SLAAC finshes and triggers a network-up event script to start
B<clatd>, while IPv4 DHCPv4 is still running in the background. This is at
least a likely scenario when using NetworkManager, as it will start the
dispatcher scripts as soon as either IPv4 or IPv6 has completed, and
IPv6 SLAAC is typically faster than IPv4 DHCPv4.

Set it to 0 to perform the check immediately.

=item B<v4-defaultroute-enable=bool> (default: I<yes>)

Whether or not to add an IPv4 default route pointing to the CLAT. In a
typical 464XLAT environment, you want this. However when using B<clatd> in
an environment where native IPv4 connectivity is also present, you might want
to disable this and instead control manually which IPv4 destinations is
reached through the CLAT and which are not.

=item B<v4-defaultroute-replace=bool> (default: I<no>)

Instructs B<clatd> to remove any pre-existing IPv4 default routes, replacing it
with one pointing to the CLAT (assuming B<v4-defaultroute-enable> is I<yes>).
The replacement is temporary, any pre-existing routes that were removed will be
restored when B<clatd> is shutting down.

Note that nothing prevents software like a connection manager or a DHCPv4
client daemon from re-adding any replaced routes while B<clatd> is running.

If you enable B<v4-defaultroute-replace> while at the same time disabling
B<v4-defaultroute-enable>, B<clatd> will remove any pre-existing IPv4 default
routes but not add any of its own.

Setting B<v4-defaultroute-replace> to I<yes> will disable the IPv4 connectivity
check.

=item B<v4-defaultroute-metric=integer> (default: I<2048>)

The metric of the IPv4 default route pointing to the CLAT. The default is
chosen because it is higher than that of a native IPv4 default route added by
NetworkManager, which makes it so that the native IPv4 connectivity is
preferred if present.

=item B<v4-defaultroute-mtu=integer> (default: I<1260>)

The MTU of the default route pointing to the CLAT. The default is the default
IPv6 MTU used by TAYGA (1280, which in turn comes from I<RFC 6145>) minus 20 to
compensate for the difference in header size between IPv4 and IPv6. This
prevents outbound packets from having to be fragmented by TAYGA, and also
makes local applications advertise a TCP MSS to their remote peers that
prevent them from sending packets beck to us that would require fragmentation.

If you know that the IPv6 Path MTU between the host and the PLAT is larger
than 1280, you may increase this, but then you should also recompile TAYGA
with a larger B<ipv6_offlink_mtu> setting in I<conffile.c>.

=item B<v4-defaultroute-advmss=integer> (default: B<v4-defaultroute-mtu> - 40)

The "advmss" value assigned to the the default route potining to the CLAT. This
controls the advertised TCP MSS value for TCP connections made through the
CLAT.

You should normally not need to set this. By default the value is calculated by
taking the value of B<v4-defaultroute-mtu> and substracting 40 (20 bytes for
the IPv4 header + 20 bytes for the TCP header). If B<v4-defaultroute-mtu> is
unset or 0, there is no default.

=back

=head1 LIMITATIONS

When using B<clat-v6-addr=shared> (or when B<clat-v6-addr> is set to another
address assigned to a local interface) and B<ctmark> is set to 0, the host OS
will not be able to communicate bi-directionally with IPv4 destinations
directly through the PLAT (e.g., I<ping6 64:ff9b::192.0.2.1>). This is because
the response traffic will be routed back to the CLAT, and ultimately return to
the Linux kernel as an IPv4 packet, which does not match the outgoing IPv6
socket. Such direct communication is normal when using DNS64 synthesis for all
queries (as opposed to just I<ipv4only.arpa>).

When using B<clat-v6-addr=derived> and no IPv6 addresses on the PLAT-facing
device are EUI-64-derived (e.g., when using SLAAC with I<RFC 4941> or I<RFC
7217> privacy addressing or static addresses), B<clatd> will generate and use
an CLAT IPv6 address using a random Interface ID from the same subnet prefix
(if it is /120 or shorter).  I<RFC 6877> suggests DHCPv6 IA_PD should be
attempted in this case instead, but this isn't currently implemented.

B<clatd> will not attempt to perform Duplicate Address Detection for the IPv6
address it generates when using B<clat-v6-addr=derived>. This is a violation of
I<RFC 6877>.

There is no guarantee that the generated CLAT IPv6 address is in fact usable,
as the network might block its use.

If the upstream network is using DHCPv6, B<clatd> will not be able to generate
a CLAT IPv6 address at all, due to the fact that DHCPv6-assigned addresses do
not carry a prefix length.

B<clatd> will not attempt to perform a connectivity check to a discovered PLAT
prefix before setting up the CLAT, as I<RFC 7050> suggest it should.

=head1 BUGS

If you are experiencing any bugs or have any feature requests, head over to
L<https://github.com/toreanderson/clatd/issues> and submit a new issue (if
someone else hasn't already done so). Please make sure to include logs with
full debugging output (using I<-d -d> on the command line or B<debug=2> in the
configuration file) when reporting a bug.

=head1 LICENCE

Copyright (c) 2014-2025 Tore Anderson <tore@fud.no>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

=head1 SEE ALSO

ip(8), nft(8), systemd.network(5), tayga(8), tayga.conf(5)

RFC 6052, RFC 6145, RFC 6146, RFC 6877, RFC 7050, RFC 7335 RFC 7755, RFC 7756,
RFC 7757, RFC 8781

=cut
Description
No description provided
Readme 197 KiB
Languages
Perl 90.6%
Makefile 4.8%
Shell 4.6%