59 Commits
v1.2 ... master

Author SHA1 Message Date
Tore Anderson
d9f274bbe7 Merge pull request #51 from UnderEu/patch-1
Update service status to better reflect 'no PLAT' state
2025-03-23 11:30:06 +01:00
Anderson Silva
ae8da007c6 Update service status to better reflect 'no PLAT' state
When checking the service status of clatd in systemctl, if no NAT64 prefix is identified, clatd prints _"No PLAT prefix could be discovered. Your ISP probably doesn't provide NAT64/DNS64 PLAT service. Exiting."_.
I understand the point of it but there are cases that a maintainer (i.e. yours truly) deploys a local PLAT on LAN for an IPv6-mostly network despite the ISP who this network upstreams traffic to the Internet has it or not. So, my intention is to reflect a better status, stating that the current connection, regardless of the medium (Ethernet, Wi-Fi, WWAN, mobilt tethering a.k.a. hotspot), has no PLAT available instead of blaming the ISP only.
2025-03-22 22:10:44 -03:00
Tore Anderson
5e085f1b17 Release clatd v2.1.0
New feature:

* Support for RFC 8781 PLAT prefix discovery through the PREF64 Router
  Advertisement option. Relies on systemd-networkd's support for the
  same being enabled. (See issue #32.)

Bugfixes:

* Create any missing leading directories in the Makefile's install
  target. (See pull request #47, thanks @DanielG!)
* Apply metadata mark earlier in the nftables pipeline. This makes clatd
  interoperate with the IPv6 reverse path filtering as implemented by
  NixOS. (See pull request #49, thanks @jmbaur!)
2025-03-21 08:46:54 +01:00
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
Jared Baur
f86f1cabb8 Decrease priority number for prerouting chain
This allows for clatd to work OOTB on distros shipping firewalls that do
reverse-path filtering based on conntrack marks in the "mangle" priority
(i.e. -150).
2025-03-20 15:20:14 -07:00
Tore Anderson
f812070f60 Separate DNS64 servers with spaces in debug output 2025-03-20 22:26:58 +01:00
Tore Anderson
f91d96b991 Get PLAT prefix from systemd-networkd, if possible
Makes clatd check if systemd-networkd is aware of any PLAT prefix (which
it may have learned from the PREF64 Router Advertisement option, cf. RFC
8781).

If a prefix is obtained from systemd-network, DNS64-based PLAT prefix
discovery is skipped, as mandated by
https://datatracker.ietf.org/doc/draft-ietf-v6ops-prefer8781/.

However, if the dns64-servers config option is set, clatd will use
DNS64-based PLAT prefix discovery towards the specified servers, and it
will not query systemd-networkd at all.

Closes #32
2025-02-22 10:00:45 +01:00
Tore Anderson
b93a5526a5 Merge pull request #47 from DanielG/fix-makefile
Fix Makefile for distros
2025-02-21 13:07:50 +01:00
Daniel Gröber
6f98967f0e Fix Makefile for distros
The first declared target (previously 'install') is use when make is called
without a target argument. This breaks the assumptions of distros.

Further install needs -D to creat the necessary target directories or it
behaves as cp and the target dirs need to exist already.
2025-02-21 12:49:05 +01:00
Tore Anderson
1c4ca683b5 Release clatd v2.0.0 and bump copyright year
The default behaviour is significantly changed since the previous
version, so upgrading might require changing the configuration file,
especially if not using it in a fairly standard 464XLAT environment
(e.g., as an SIIT-DC Edge Relay). According to the Semantic Versioning
standard, such changes require the major version to be bumped.
2025-02-09 10:53:17 +01:00
Tore Anderson
2ad52c57f8 Use IPv6 sockets when querying IPv4 nameservers
This works around a bug in Net::DNS (or arguably in IO::Socket::IP)
which causes it to refuse to send queries to IPv4 nameservers (even
localhost), when running on IPv6-only hosts. Since IPv6-only hosts are
the primary use case for clatd, and most modern Linux distributions are
shipping with systemd-resolved listening on 127.0.0.53 by default, this
prevents PLAT prefix discovery from working correctly out of the box.

Forcing Net::DNS to use an IPv6 socket, by simply substituting all IPv4
addresses in the name server as IPv4-mapped IPv6 addreses, successfully
works around this problem.

This bug has been present in Net::DNS since version 1.03 (more
specifically since SVN r1406, dated 2015-10-05). This version started
defaulting to using IO::Socket::IP for all sockets, so it is no longer
necessary to require it explicitly in clatd.

For more info:

https://rt.cpan.org/Public/Bug/Display.html?id=158714 (Net::DNS)
https://rt.cpan.org/Public/Bug/Display.html?id=132760 (IO::Socket::IP)
2025-02-09 10:53:17 +01:00
Tore Anderson
92afe35b0b Ensure CLAT-PLAT traffic is permitted in UFW
UFW is the standard local firewall framework in Debian-based
distributions. If it is installed and active, add rules that permit
routed traffic between the CLAT and the PLAT prefix. This traffic is
dropped by the default UFW ruleset, leading to issues such as #42.
2025-02-09 10:53:17 +01:00
Tore Anderson
33252dcb13 Share IPv6 address with host OS by default
Adds support for clat-v6-addr=shared and make this the default
behaviour. This makes the CLAT function share the address the host OS
uses for direct IPv6 connection towards the PLAT prefix, thus removing
the previous requirement for a secondary IPv6 address dedicated to the
CLAT function.

When using a shared address in this manner, enable connection tracking
marking by default (so that direct IPv6 connections from the host OS to
IPv4 destinations behind the PLAT keeps working) and disable Proxy-ND
(as there is no need for it, as the host OS kernel will handle NDP
interactions all on its own).

To use the previous default behaviour, use clat-v6-addr=derived.

Closes #25
Closes #46
2025-02-09 10:53:16 +01:00
Tore Anderson
03042228be Add missing space in Proxy-ND debug output 2025-02-09 10:53:16 +01:00
Tore Anderson
90d4ac6e70 Make use of tabs vs spaces consistent 2025-02-09 10:53:16 +01:00
Tore Anderson
f0d7c09adf Use routing policy and ctmark for routing to CLAT
This moves the route for IPv6 traffic from the PLAT to the CLAT into a
separate routing table, and uses the Linux kernel's routing policy
framework to redirect traffic into this routing table.

This makes it possible to set `clat-v6-addr` to an address also used by
the main host OS, removing the requirement for having a dedicated
secondary address assigned to the CLAT.

Additionally, support using nftables to set a connection tracking mark
on outbound connections from the CLAT, and ensuring only return traffic
matching that mark is returned back to the CLAT. This makes it possible
for the CLAT to share an IPv6 address with the main host OS without
breaking connectivity to DNS64-synthesised IPv6 addresses.

The trade-off of using a connection tracking mark is that the CLAT can
not receive unsolicited traffic from the IPv4 Internet via the PLAT.
However in the common case, where the PLAT is Stateful NAT64, that is
the case no matter what.

Closes #25.
2025-02-09 10:53:15 +01:00
Tore Anderson
05728771ca Update doc regarding limitations on non-SLAAC nets
The LIMITATIONS part of the documentation wasn't updated to take into
account the changes in 81f2c61, fix that.

Closes #24.
2025-02-09 10:53:15 +01:00
Tore Anderson
cc64d0c6f3 Remove upstart support
Upstart has been discontinued for more than a decade, and no modern
distributions are using it anymore, so get rid of it.
2025-02-09 10:53:15 +01:00
Tore Anderson
6a582bf1e4 Remove support for ip6tables
The ip6tables frameworks has been deprecated for quite a while now, as
it has been replaced by nftables. On modern distributions, rules added
with ip6tables are just converted to nftables rules and are added to an
ip6tables compatibility table there. This changed the name of the kernel
modules, breaking ip6tables auto-detection (cf. issues #42 and #44).

Unfortunately, due to the way nftables works, these rules can no longer
be relied upon to ensure the CLAT traffic is allowed. This is due to the
fact that in nftables, *all* chains that hook into a packet's path must
return an «accept» verdict in order for the packet to be ultimately
accepted, while it is enough for a single chain to return a «drop»
verdict in order for the packet to be dropped.

That means that the rules that accepts CLAT traffic might be overridden
by a «default drop» rule added to another chain in another table, e.g.,
by firewalld or similar local firewall frameworks. See #23 for an
example of this.

On the other hand, if there is no drop rule anywhere, the rules added by
clatd are superfluous to begin with, as the default kernel behaviour is
to accept the packages.

Therefore just remove support for ip6tables entirely. Users of
firewalld, ufw,  or similar local firewall frameworks will need to make
sure that rules are added in those frameworks that permit the CLAT
traffic, e.g., by using `script-up`, like so for ufw:

```
script-up=ufw route allow in on $clat_dev out on $plat_dev
```

Native support for adding rules to the most common local firewall
frameworks might be added in the future.

Closes #44 (no longer applicable).
2025-02-09 10:53:14 +01:00
Tore Anderson
6342488889 clatd@.systemd: sync with clatd.systemd
The following commits changed only clatd.systemd, but it makes sense to
applt them to clatd@.systemd as well.

eb27dd5 clatd.systemd: pull inn network-online.target
00fa0f3 SystemD service: ensure service starts after DNS queries (e.g. ipv4only.arpa) can be resolved.
2025-02-09 10:45:04 +01:00
Tore Anderson
eb27dd5ead clatd.systemd: pull inn network-online.target
According to systemd.special(7):

> Units that strictly require a configured network connection should
> pull in network-online.target (via a Wants= type dependency) and
> order themselves after it.

The Wants= dependency was missing, add it.
2025-02-09 10:11:33 +01:00
Tore Anderson
f763915903 Merge pull request #45 from jivanpal/patch-1
SystemD service: ensure service starts after DNS queries (e.g. ipv4only.arpa) can be resolved.
2025-02-09 10:07:21 +01:00
Jivan Pal
00fa0f3266 SystemD service: ensure service starts after DNS queries (e.g. ipv4only.arpa) can be resolved. 2025-02-09 10:04:56 +01:00
Tore Anderson
3ea303b521 Move plat-fallback-prefix logic into get_plat_prefix()
Feature introduced in 8aa8bfa was defective from day one, it seems.

Fixes #39
2024-08-17 08:48:06 +02:00
Tore Anderson
04062b282d Merge pull request #36 from oskar456/nat46_support
Add nat46 in-kernel translator support
2023-11-07 09:09:52 +01:00
Ondřej Caletka
6d2ad96c2f Add nat46 in-kernel translator support
This allows clatd to use [nat46](https://github.com/ayourtch/nat46)
kernel translator instead of TAYGA. It uses automatic detection - if
`nat46` module is loaded, it will get used, otherwise the application
falls back to using TAYGA.

Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
2023-11-05 12:32:19 +01:00
Greg Skinner
e6e0f4ecc1 Update clatd
In order to capture the exit code from a system() call, right shift $? by eight bits.
2023-11-04 14:07:39 +01:00
satan1st
a93f5ff491 add ifupdown integration (#31)
* add ifupdown integration

* fixup! add ifupdown integration

---------

Co-authored-by: satanist <satanist+fsmi@bureaucracy.de>
2023-08-08 08:34:50 +02:00
satan1st
b8a7092873 add instantiated systemd unit (#29)
* add instantiated systemd unit

* fixup! add instantiated systemd unit

---------

Co-authored-by: satanist <satanist+fsmi@bureaucracy.de>
2023-08-08 08:33:50 +02:00
satanist
8aa8bfa5d5 add fallback prefix 2023-08-08 08:32:38 +02:00
Tore Anderson
ff6aa57c4b Release clatd v1.6
Also update copyright year to 2023.
2023-03-05 11:33:00 +01:00
Tore Anderson
a416278570 Make NM/systemd integration more robust
This fixed the following dependency loop that could occur when the
system was booting up:

1. NetworkManager configures a network interface and fires off the clatd
   dispatcher script.
2. The dispatcher scripts tries to restart `clatd.service`.
3. However, `clatd.service` cannot be (re)started at this point because
   its dependency `network-online.target` has not yet been reached.
4. Therefore, the `systemctl restart clatd.service` command in the
   dispatcher script blocks, waiting for `network-online.target` to be
   reached.
5. But that won't happen until the dispatcher scripts finishes...

Adding `--no-block` allows the dispatcher script to finish immediately
without waiting for the restart to complete (instead, systemd will do it
in the background once `network-online.target` has been reached).

Furthermore, since `clatd.service` may end up being restarted several
times during a boot process (especially on a system with many network
interfaces handled by NetworkManager), it is also at risk of
inadvertently triggering the restart rate-limiting feature in systemd,
so disable that while we're at it.

Closes #22. Thanks to @patrakov for the report!
2023-03-05 10:58:29 +01:00
Michal Josef Špaček
4583c592d8 Rewrite obsolete module usage to new one (#20)
Perl module IO::Socket::INET6 is obsolete and recomended one is
IO::Socket::IP, which has compatibility to old one.

btw: Net::DNS was rewrited to use IO::Socket::IP.
2022-11-08 14:43:50 +01:00
Thomas Schäfer
41a312f908 Update Makefile (#14)
Correct iproute2 package name for Debian-based distributions.
2020-02-27 09:23:54 +01:00
Tore Anderson
66e1e5fc28 Bump version to v1.5
Most important change is 19c4042 - necessary to support newer Linux kernels.
2019-05-20 10:19:56 +02:00
Tore Anderson
1441a3ff4b Copyright year update to 2019 2019-05-20 10:18:56 +02:00
Tore Anderson
19c4042f1e Strip prefix length before route lookup for $plat_prefix
Recent Linux kernels have removed the ability to do a `ip -6 route get` lookup
for destinations with a prefix length:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0eff0a274104487938d741b5c37aca1795afd184

Strip the prefix length before doing this lookup, so that it is done for the
first address in `$plat_prefix` instead.

Closes #12.
2019-05-20 09:13:05 +02:00
Tore Anderson
888e30bd2b Copyright year update to 2018 2018-12-14 23:59:01 +01:00
Tore Anderson
b8f583a4e0 Correctly detect PLAT prefixes containing embedded WKAs
The previous code would fail to detect PLAT prefix if ipv4only.arpa resolved to
an address such as 2001:db8:c000:aa::c000:aa. It would get confused as to
whether the prefix was 2001:db8::/32 (incorrect) or 2001:db8:c000:aa::/96
(correct).

Bug reported by Kasper Dupont - thanks!
2018-12-14 23:48:32 +01:00
Tore Anderson
c228c2bb64 Copyright year update to 2017 2017-08-15 18:15:31 +02:00
Tore Anderson
ebd8487dd0 README: typofixes 2017-08-15 18:14:37 +02:00
Tore Anderson
1218ca7979 Remove doc saying TAYGA will be auto-built
Now that TAYGA is avaiable in EPEL, it is better to point people at that
instead of building TAYGA from source. In commit 18dca08 the build from source
functionality was removed. Update documentation accordingly.
2017-08-15 18:11:39 +02:00
Tore Anderson
3f73b5281e Makefile: Add DESTDIR, PREFIX and SYSCONFDIR variables
These variables can be used to control exactly where clatd gets installed.

Requested by @ingvarha.

Closes: #8
2017-08-15 18:00:47 +02:00
Tore Anderson
6c7c2d2a92 Don't hardcode commands paths
Don't assume that utilities such as systemctl or initctl is found in a certain
specific path. Instead look for them in $PATH. This applies to both the
Makefile and the NetworkManager dispatcher script.

Resolves #11.
2017-08-15 17:46:54 +02:00
Tore Anderson
18dca086c2 Update siit-dc/eam I-D references to RFC numbers
The three I-Ds that describe SIIT-DC and SIIT-EAM have been published as
RFCs, so update the references accordingly. Also update once occurrence
of old terminology ("Host Agent") with the new and final one ("Edge
Relay").
2016-03-28 08:38:56 +02:00
Tore Anderson
fb4587bfd6 nm-dispatcher: only act on iface up/down events
Ensure other events (like hostname changes, DHCP lease renewals, etc.)
are ignored as they are very unlikely to be relevant for clatd.
2016-03-28 08:26:20 +02:00
Tore Anderson
1abcec1285 Minor documentation fixes 2016-01-28 13:40:05 +01:00
Tore Anderson
9a1a4ae797 2016 licence update
Change "2014-2015" with "2014-2016". Also, I had forgotten to update the
licence comment in the clatd script itself in b5725c0, so fix that too.
Thanks to @ingvarha for noticing!
2016-01-28 11:55:02 +01:00
Tore Anderson
aa92cb0287 Bump version to v1.4 2015-10-23 13:43:34 +02:00
Tore Anderson
5e2297903a Improve handing of single-scalar calls to cmd()
Just pass the entire supplied command line, be it a single scalar or an array,
to system(). It'll do the right thing with it. This gets rid of a spurious
trailing space in debugging output when cmd() was called with a single scalar
as the command argument (leaving @cmdline undefined).
2015-10-23 11:49:51 +02:00
Tore Anderson
f976f46b57 Correct debug output for cfg() and cfgint() 2015-10-23 11:35:03 +02:00
Tore Anderson
e8cb719f10 Insert the effective default advmss into %CFG
This ensures that the effective value (rather than the configured value or lack
thereof) is available in the environment of script-up/down.
2015-10-23 11:33:35 +02:00
Tore Anderson
0bc3bbd797 New feature: script-up/down
Adds the possibility of making clatd run a custom script while starting up or
shutting down.
2015-10-23 11:22:00 +02:00
Tore Anderson
a80e2f30ab New feature: Set advmss on IPv4 default route
This ensures that TCP connections terminated through the CLAT gets a sensible
TCP MSS value negotiated. By default, the value is set to the MTU of the
defaultroute minus 40. There should be no reason to change this in the vast
majority of cases, but it can be overridden with the "v4-defaultroute-advmss"
setting.
2015-10-22 11:58:19 +02:00
Tore Anderson
9f1789f34d New feature: Replace original IPv4 default route
Adds "v4-defaultroute-replace" config/command-line setting (default disabled).
When enabled, it will unconditionally disable the pre-flight IPv4 connectivity
check, and ensure that any pre-existing IPv4 default routes are removed during
startup (and presumably replaced with the one pointing to the CLAT, unless
"v4-defaultroute-enable" has been disabled). Any IPv4 default routes that were
removed in this manner are restored when clatd is shutting down.
2015-10-22 11:12:34 +02:00
Tore Anderson
2f2a59ddf3 v1.3: new licence => new release 2015-07-23 13:40:17 +02:00
Tore Anderson
b5725c093c relicence to MIT to make lrintel happy 2015-07-23 13:38:30 +02:00
Tore Anderson
ad114b18cf Update for siit-dc ietf changes
- the siit-dc drafts have been adopted by the v6ops wg
- host agent renamed to edge relay
2015-07-23 13:29:28 +02:00
Tore Anderson
c48ad644c0 Avoid a restart loop with NM dispatcher scripts
Newer NetworkManager versions will run the dispatcher scripts when new
unknown interfaces show up, including 'clat'. That will cause a clatd
restart right after startup, and we'll never get to fully initialise. So
ensure the dispatcher script does nothing if the device in question is
our own 'clat' to prevent this.
2014-10-05 20:27:07 +02:00
9 changed files with 761 additions and 220 deletions

22
LICENCE
View File

@@ -1,5 +1,19 @@
Copyright (c) 2014 Tore Anderson <tore@fud.no>
Copyright (c) 2014-2025 Tore Anderson <tore@fud.no>
As long as you retain this notice, you may use this piece of software as
you wish. If you like it, and we happen to meet one day, you can buy me
a beer in return. If you really like it, make it an IPA.
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.

View File

@@ -1,20 +1,29 @@
DESTDIR=
PREFIX=/usr
SYSCONFDIR=/etc
APT_GET:=$(shell which apt-get)
DNF_OR_YUM:=$(shell which dnf || which yum)
SYSTEMCTL:=$(shell which systemctl)
TAYGA:=$(shell which tayga)
all:
install:
# Install the main script to /usr/sbin
install -m0755 clatd /usr/sbin/clatd
# Install the main script
install -D -m0755 clatd $(DESTDIR)$(PREFIX)/sbin/clatd
# Install manual page if pod2man is installed
pod2man --name clatd --center "clatd - a CLAT implementation for Linux" --section 8 README.pod /usr/share/man/man8/clatd.8 && gzip -f9 /usr/share/man/man8/clatd.8 || echo "pod2man is required to generate manual page"
pod2man --name clatd --center "clatd - a CLAT implementation for Linux" --section 8 README.pod $(DESTDIR)$(PREFIX)/share/man/man8/clatd.8 && gzip -f9 $(DESTDIR)$(PREFIX)/share/man/man8/clatd.8 || echo "pod2man is required to generate manual page"
# Install systemd service file if applicable for this system
if test -x /usr/bin/systemctl && test -d "/etc/systemd/system"; then install -m0644 scripts/clatd.systemd /etc/systemd/system/clatd.service && systemctl daemon-reload; fi
if test -e "/etc/systemd/system/clatd.service" && test ! -e "/etc/systemd/system/multi-user.target.wants/clatd.service"; then systemctl enable clatd.service; fi
# Install upstart service file if applicable for this system
if test -x /sbin/initctl && test -d "/etc/init"; then install -m0644 scripts/clatd.upstart /etc/init/clatd.conf; fi
if test -x "$(SYSTEMCTL)" && test -d "$(DESTDIR)$(SYSCONFDIR)/systemd/system"; then install -m0644 scripts/clatd.systemd $(DESTDIR)$(SYSCONFDIR)/systemd/system/clatd.service && $(SYSTEMCTL) daemon-reload; fi
if test -e "$(DESTDIR)$(SYSCONFDIR)/systemd/system/clatd.service" && test ! -e "$(DESTDIR)$(SYSCONFDIR)/systemd/system/multi-user.target.wants/clatd.service"; then $(SYSTEMCTL) enable clatd.service; fi
# Install NetworkManager dispatcher script if applicable
if test -d /etc/NetworkManager/dispatcher.d; then install -m0755 scripts/clatd.networkmanager /etc/NetworkManager/dispatcher.d/50-clatd; fi
if test -d $(DESTDIR)$(SYSCONFDIR)/NetworkManager/dispatcher.d; then install -m0755 scripts/clatd.networkmanager $(DESTDIR)$(SYSCONFDIR)/NetworkManager/dispatcher.d/50-clatd; fi
installdeps:
# .deb/apt-get based distros
if test -x /usr/bin/apt-get; then apt-get -y install perl-base perl-modules libnet-ip-perl libnet-dns-perl libio-socket-inet6-perl iproute iptables tayga; fi
# .rpm/YUM-based distros
if test -x /usr/bin/yum; then yum -y install perl perl-Net-IP perl-Net-DNS perl-IO-Socket-INET6 perl-File-Temp iproute iptables; fi
# to get TAYGA on .rpm/YUM-based distros, we unfortunately need to install from source
if test -x /usr/bin/yum && test ! -x /usr/sbin/tayga; then echo "TAYGA isn't packaged for YUM-based distros, will download and compile the source in 5 seconds (^C interrupts)" && sleep 5 && yum -y install gcc tar wget bzip2 && wget http://www.litech.org/tayga/tayga-0.9.2.tar.bz2 && bzcat tayga-0.9.2.tar.bz2 | tar x && cd tayga-0.9.2 && ./configure --prefix=/usr && make && make install && rm -rf ../tayga-0.9.2.tar.bz2 ../tayga-0.9.2; fi
if test -x "$(APT_GET)"; then $(APT_GET) -y install perl-base perl-modules libnet-ip-perl libnet-dns-perl libjson-perl iproute2 nftables tayga; fi
# .rpm/DNF/YUM-based distros
if test -x "$(DNF_OR_YUM)"; then $(DNF_OR_YUM) -y install perl perl-IPC-Cmd perl-Net-IP perl-Net-DNS perl-File-Temp perl-JSON iproute nftables; fi
# If necessary, try to install the TAYGA .rpm using dnf/yum. It is unfortunately not available in all .rpm based distros (in particular CentOS/RHEL).
if test -x "$(DNF_OR_YUM)" && test ! -x "$(TAYGA)"; then $(DNF_OR_YUM) -y install tayga || echo "ERROR: Failed to install TAYGA using dnf/yum, the package is probably not included in your distro. Try enabling the EPEL repo <URL: https://fedoraproject.org/wiki/EPEL> and try again, or install TAYGA <URL: http://www.litech.org/tayga> directly from source."; exit 1; fi

View File

@@ -1,6 +1,6 @@
=head1 NAME
B<clatd> - a CLAT / SIIT-DC Host Agent implementation for Linux
B<clatd> - a CLAT / SIIT-DC Edge Relay implementation for Linux
=head1 DESCRIPTION
@@ -13,16 +13,18 @@ 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).
It may also be used to implement an SIIT-DC Host Agent as defined by
I<I-D.anderson-v6ops-siit-dc-2xlat>. In this scenario, the PLAT is a SIIT-DC
Gateway (see I<I-D.anderson-v6ops-siit-dc>) instead of a Stateful NAT64 (see
I<RFC6146>). When used as a SIIT-DC Host Agent, 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 Gateway's configuration.
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 on the software package TAYGA by Nathan Lutchansky 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/>.
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
@@ -62,7 +64,7 @@ configuration file. Refer to the section B<CONFIGURATION> below for more info.
=head1 INVOCATION
B<clatd> is meant to be run under a daemonising control process such as
systemd, upstart, or similar. It is further meant to be (re)started whenever a
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
@@ -85,23 +87,23 @@ of B<clatd> and its dependencies:
=back
This will install B<clatd> to /usr/sbin, plus install systemd, upstart, and/or
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 RPM format,
so on RedHat/Fedora the installdeps target will install gcc and attempt to
compile TAYGA from source.
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 onfiguration settings may be added to
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 recogniced keys and their
Settings are of the form B<key=value>. A list of recognised keys and their
possible values follow below:
=over
@@ -119,6 +121,57 @@ 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
@@ -132,65 +185,110 @@ 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 Host Agent (cf.
I-D.draft-anderson-v6ops-siit-dc-2xlat), you will want to set this to the
IPv4 Service Address configured in the SIIT-DC Gateway. 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.
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> (default: auto-generated)
=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 Host Agent,
you will want to set this to the IPv6 address in the Static Address Mapping
configured in the SIIT-DC Gateway.
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.
By default, 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.
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.
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.
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 RFC 7050. By default, the system resolver is
used, but it might be useful to override this in case 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.
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-ip6tables=path> (default: assume in $PATH)
=item B<cmd-networkctl=path> (default: assume in $PATH)
Path to the B<ip6tables> binary from the netfilter package available at
L<http://netfilter.org>. Only required for adding ip6tables rules
(see the B<ip6tables-enable> configuration setting).
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.
@@ -202,22 +300,12 @@ forwarding.
All sysctls that are modified will be restored to their original values when
B<clatd> is shutting down.
=item B<ip6tables-enable=bool> (default: see below)
Controls whether or not B<clatd> should insert ip6tables rules that permit the
forwarding of IPv6 traffic between the CLAT and PLAT devices. Such forwarding
must be permitted for B<clatd> to work correctly. Any rules added will be
removed when B<clatd> is shutting down.
The default is I<yes> if the ip6tables_filter kernel module is loaded, I<no>
if it is not.
=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 setting up generating the CLAT IPv6 address, and
when setting up ip6tables rules and Proxy-ND entries.
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)
@@ -225,18 +313,34 @@ 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<proxynd-enable> (default: I<yes>)
=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 adderss), but likely not necessary on
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
@@ -267,6 +371,9 @@ 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
@@ -287,6 +394,23 @@ 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
@@ -307,16 +431,47 @@ 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
B<clatd> will not be able to acquire an IPv6 address for the CLAT if SLAAC
isn't used. I<RFC 6877> suggests DHCPv6 IA_PD should be attempted in this
case, but this isn't currently implemented.
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. This is a violation of I<RFC 6877>.
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.
@@ -331,18 +486,31 @@ configuration file) when reporting a bug.
=head1 LICENCE
Copyright (c) 2014 Tore Anderson <tore@fud.no>
Copyright (c) 2014-2025 Tore Anderson <tore@fud.no>
As long as you retain this notice, you may use this piece of software as
you wish. If you like it, and we happen to meet one day, you can buy me
a beer in return. If you really like it, make it an IPA.
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), ip6tables(8), tayga(8), tayga.conf(5)
ip(8), nft(8), systemd.network(5), tayga(8), tayga.conf(5)
RFC 6052, RFC 6145, RFC 6146, RFC 6877, RFC 7050, RFC 7335
I-D.anderson-v6ops-siit-dc, I-D.anderson-v6ops-siit-dc-2xlat
RFC 6052, RFC 6145, RFC 6146, RFC 6877, RFC 7050, RFC 7335 RFC 7755, RFC 7756,
RFC 7757, RFC 8781
=cut

489
clatd
View File

@@ -1,18 +1,33 @@
#! /usr/bin/perl -w
#
# Copyright (c) 2014 Tore Anderson <tore@fud.no>
# Copyright (c) 2014-2025 Tore Anderson <tore@fud.no>
#
# As long as you retain this notice, you may use this piece of software as
# you wish. If you like it, and we happen to meet one day, you can buy me
# a beer in return. If you really like it, make it an IPA.
# 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.
#
# See the file 'README.pod' in the source distribution or the manual page
# clatd(8) for more information.
#
use strict;
use IPC::Cmd qw(can_run);
use Net::IP;
my $VERSION = "1.2";
my $VERSION = "2.1.0";
#
# Populate the global config hash with the default values
@@ -20,25 +35,33 @@ my $VERSION = "1.2";
my %CFG;
$CFG{"quiet"} = 0; # suppress normal output
$CFG{"debug"} = 0; # debugging output level
$CFG{"script-up"} = undef; # sh script to run when starting up
$CFG{"script-down"} = undef; # sh script to run when shutting down
$CFG{"clat-dev"} = "clat"; # TUN interface name to use
$CFG{"clat-v4-addr"} = "192.0.0.1"; # from RFC 7335
$CFG{"clat-v6-addr"} = undef; # derive from existing SLAAC addr
$CFG{"clat-v6-addr"} = "shared"; # re-use primary address from host OS
$CFG{"dns64-servers"} = undef; # use system resolver by default
$CFG{"cmd-ip"} = "ip"; # assume in $PATH
$CFG{"cmd-ip6tables"} = "ip6tables"; # assume in $PATH
$CFG{"cmd-networkctl"} = "networkctl"; # assume in $PATH
$CFG{"cmd-nft"} = "nft"; # assume in $PATH
$CFG{"cmd-tayga"} = "tayga"; # assume in $PATH
$CFG{"cmd-ufw"} = "ufw"; # assume in $PATH
$CFG{"ctmark"} = undef; # match ctmark for routing pkts to CLAT
$CFG{"forwarding-enable"} = 1; # enable ipv6 forwarding?
$CFG{"ip6tables-enable"} = undef; # allow clat<->plat traffic?
$CFG{"plat-dev"} = undef; # PLAT-facing device, default detect
$CFG{"plat-prefix"} = undef; # detect using DNS64 by default
$CFG{"proxynd-enable"} = 1; # add proxy-nd entry for clat?
$CFG{"plat-fallback-prefix"} = undef; # fallback prefix if no prefix is found
$CFG{"proxynd-enable"} = undef; # add proxy-nd entry for clat?
$CFG{"route-table"} = 0xc1a7; # add route to CLAT in this table
$CFG{"tayga-conffile"} = undef; # make a temporary one by default
$CFG{"tayga-v4-addr"} = "192.0.0.2"; # from RFC 7335
$CFG{"v4-conncheck-enable"} = 1; # exit if there's already a defroute
$CFG{"v4-conncheck-delay"} = 10; # seconds before checking for v4 conn.
$CFG{"v4-defaultroute-enable"} = 1; # add a v4 defaultroute via the CLAT?
$CFG{"v4-defaultroute-replace"} = 0; # replace existing v4 defaultroute?
$CFG{"v4-defaultroute-metric"} = 2048; # metric for the IPv4 defaultroute
$CFG{"v4-defaultroute-mtu"} = 1260; # MTU for the IPv4 defaultroute
$CFG{"v4-defaultroute-advmss"} = 0; # TCP MSS for the IPv4 defaultroute
#
@@ -65,22 +88,21 @@ sub err {
#
# Runs a command. First argument is what subroutine to call to a message if
# the command doesn't exit successfully, second is the command itself, and
# any more is the command line arguments.
# any more are the command line arguments.
#
sub cmd {
my $msgsub = shift;
my $command = shift;
my @cmdline = @_;
my @cmd = @_;
d("cmd($command @cmdline)");
d("cmd(@cmd)");
if(system($command, @cmdline)) {
if(system(@cmd)) {
if($? == -1) {
&{$msgsub}("cmd($command @cmdline) failed to execute");
&{$msgsub}("cmd(@cmd) failed to execute");
} elsif($? & 127) {
&{$msgsub}("cmd($command @cmdline) died with signal ", ($? & 127));
&{$msgsub}("cmd(@cmd) died with signal ", ($? & 127));
} else {
&{$msgsub}("cmd($command @cmdline) returned ", ($? >> 127));
&{$msgsub}("cmd(@cmd) returned ", ($? >> 8));
}
}
return $?;
@@ -135,7 +157,7 @@ sub cfgbool {
#
sub cfgint {
my ($key) = @_;
d2("cfgstr($key)");
d2("cfgint($key)");
if(!exists($CFG{$key})) {
err("key '$key' doesn't exist in config hash");
}
@@ -150,7 +172,7 @@ sub cfgint {
#
sub cfg {
my ($key) = @_;
d2("cfgstr($key)");
d2("cfg($key)");
if(!exists($CFG{$key})) {
err("key '$key' doesn't exist in config hash");
}
@@ -202,9 +224,9 @@ sub sysctl {
#
# Look for either of the WKAs for ipv4only.arpa (192.0.0.170 and .171) in an
# IPv6 address at all of the locations RFC 6052 says it can occur. If it's
# present at any of those locations (but no more than once), return the
# inferred translation prefix.
# IPv6 address at all of the locations RFC 6052 says it can occur, starting at
# the longest prefix length. If it's present at any of those locations, return
# the inferred translation prefix.
#
sub find_rfc7050_wka {
my $AAAA = shift;
@@ -231,7 +253,7 @@ sub find_rfc7050_wka {
my $discovered_pfx_len;
for my $len (keys(%rfc6052table)) {
outer: for my $len (sort {$b <=> $a} keys(%rfc6052table)) {
d2("Looking for Well-Known Addresses at prefix length /$len");
my $maskedip = $ip->intip();
my $mask = Net::IP->new($rfc6052table{"$len"}{"mask"}, 6);
@@ -252,13 +274,9 @@ sub find_rfc7050_wka {
}
if($maskedip == $wkaint->intip) {
if($discovered_pfx_len) {
w("Found WKA at two locations in ", $ip->sort,
"(/$discovered_pfx_len and /$len) - ignoring");
return;
}
d2("Found it!");
$discovered_pfx_len = $len;
last outer;
} else {
d2("Didn't find it");
}
@@ -312,10 +330,9 @@ sub find_rfc7050_wka {
# up to see if the well-known hostname 'ipv4only.arpa' resolves to an IPv6
# address, if so there is a high chance of DNS64 being used.
#
sub get_plat_prefix {
sub get_plat_prefix_from_dns64 {
p("Performing DNS64-based PLAT prefix discovery (cf. RFC 7050)");
require IO::Socket::INET6; # needed by Net::DNS for querying IPv6 servers
require Net::DNS;
my @dns64_servers = split(",", cfg("dns64-servers") || "");
@@ -332,6 +349,17 @@ sub get_plat_prefix {
$res = Net::DNS::Resolver->new();
}
$res->dnssec(0); # RFC 7050 section 3
# Force use of AF_INET6 socket with IPv4-mapped addresses when querying
# an IPv4 name server (e.g., systemd-resolved listening on 127.0.0.53).
# This works around https://rt.cpan.org/Public/Bug/Display.html?id=158714
# (and/or https://rt.cpan.org/Public/Bug/Display.html?id=132760).
$res->nameservers(map {
Net::IP->new($_)->version() == 4 ? "::ffff:$_" : $_;
} $res->nameservers);
d2("Nameservers after Net::DNS bug workaround: ",
join(" ", $res->nameservers));
my $pkt = $res->query('ipv4only.arpa', 'AAAA');
if(!$pkt) {
d("No AAAA records was returned for 'ipv4only.arpa'");
@@ -360,26 +388,87 @@ sub get_plat_prefix {
}
if(@prefixes) {
return $prefixes[0];
} elsif($CFG{"plat-fallback-prefix"}) {
p("No PLAT prefix could be discovered, using fallback");
return $CFG{"plat-fallback-prefix"};
} else {
p("No PLAT prefix could be discovered. Your ISP probably doesn't provide",
p("No PLAT prefix could be discovered. Your connection probably doesn't provide",
" NAT64/DNS64 PLAT service. Exiting.");
cleanup_and_exit(0);
}
}
#
# This function attempts request a PLAT prefix from systemd-networkd, which
# systemd-networkd will know about if the Router Advertisements contain the
# PREF64 option defined in RFC 8781, and systemd-networkd is configured with
# UsePREF64=true (not the default). The first prefix seen is used, subsequent
# ones are ignored.
#
sub get_plat_prefix_from_networkd {
if(!can_run(cfg("cmd-networkctl"))) {
d(cfg("cmd-networkctl"), " is not installed or not exectutable, skipping");
return;
}
p("Attempting to query systemd-networkd for PLAT prefix (cf. RFC 8781)");
open(my $fd, '-|', cfg("cmd-networkctl"), qw(--json=short status))
or err(cfg("cmd-networkctl"), " failed to execute");
my @out = <$fd>;
if(!close($fd)) {
p("'networkctl status' failed, trying DNS64 PLAT prefix discovery");
return;
}
require JSON;
my $status = JSON::decode_json("@out");
return unless($status->{"Interfaces"});
for my $interface (@{$status->{"Interfaces"}}) {
next if(!$interface->{"NDisc"}->{"PREF64"});
d2($interface->{"Name"}, " PREF64 bytes: ",
join(" ", @{$interface->{"NDisc"}->{"PREF64"}->[0]->{"Prefix"}}),
" / ", $interface->{"NDisc"}->{"PREF64"}->[0]->{"PrefixLength"});
# PREF64 is an array of bytes, convert to IPv6 string representation
my @bytes = @{$interface->{"NDisc"}->{"PREF64"}->[0]->{"Prefix"}};
my $ipstr;
for (my $i = 0; $i < @bytes;) {
$ipstr .= sprintf("%02x%02x", $bytes[$i++], $bytes[$i++]);
$ipstr .= ":" if($i < @bytes);
}
$ipstr .= "/" . $interface->{"NDisc"}->{"PREF64"}->[0]->{"PrefixLength"};
d2("String representation: $ipstr");
# Run the result through Net::IP to ensure we have a valid IPv6 string
# and also to convert it to compact format.
my $ip = Net::IP->new($ipstr, 6)
or err("Failed to convert PREF64 bytes array to IPv6 string format");
my $prefix = $ip->short() . "/" . $ip->prefixlen();
d("Obtained PLAT prefix $prefix from systemd-networkd");
return $prefix;
}
}
#
# This function figures out which network interface on the system faces the
# PLAT/NAT64. We need this when generating an IPv6 address for the CLAT, when
# installing Proxy-ND entries, and when setting up ip6tables rules.
# installing Proxy-ND entries, and when setting up nft rules.
#
sub get_plat_dev {
d("get_plat_dev(): finding which network dev faces the PLAT");
my $plat_dev;
my $plat_dev_srcip;
my $plat_prefix = cfg("plat-prefix");
if(!$plat_prefix) {
err("get_plat_dev(): No PLAT prefix to work with");
}
$plat_prefix =~ s|/\d+$||;
open(my $fd, '-|', cfg("cmd-ip"), qw(-6 route get), $plat_prefix)
or err("get_plat_dev(): 'ip -6 route get $plat_prefix' failed to execute");
while(<$fd>) {
@@ -387,9 +476,13 @@ sub get_plat_dev {
d("get_plat_dev(): Found PLAT-facing device: $1");
$plat_dev = $1;
}
if(/ src (\S+) /) {
d("get_plat_dev(): Found PLAT-facing device source IP: $1");
$plat_dev_srcip = $1;
}
}
close($fd) or err("get_plat_dev(): 'ip -6 route get $plat_prefix' failed");
return $plat_dev;
return ($plat_dev, $plat_dev_srcip);
}
@@ -423,6 +516,15 @@ sub is_modified_eui64 {
return ($ip & $mask) != $mask;
}
sub cleanup_handler {
p("Cleaning up and exiting");
if(cfg("script-down")) {
d("Running custom shutdown script: ", cfg("script-down"));
cmd(\&err, cfg("script-down"));
}
cleanup_and_exit(0);
}
#
# This function considers any globally scoped IPv6 address on the PLAT-facing
@@ -562,19 +664,24 @@ sub get_clat_v6_addr {
# below gets set as we go along, so that the cleanup subroutine can restore
# stuff if necessary.
#
my $cleanup_remove_clat_dev; # true if having created it
my $cleanup_remove_tayga_clat_dev; # true if having created it
my $cleanup_remove_nat46_clat_dev; # true if having created it
my $cleanup_delete_taygaconf; # true if having made a temp confile
my $cleanup_zero_forwarding_sysctl; # zero forwarding sysctl if set
my @cleanup_accept_ra_sysctls; # accept_ra sysctls to be reset to '1'
my $cleanup_zero_proxynd_sysctl; # zero proxy_ndp sysctl if set
my $cleanup_remove_proxynd_entry, # true if having added proxynd entry
my $cleanup_remove_ip6tables_rules; # true if having added ip6tables rules
my $cleanup_remove_nftable; # true if having added an nftable
my $cleanup_remove_clat_iprule; # true if having added clat iprule
my $cleanup_remove_ufw_rules; # true if having added ufw rules
my $cleanup_restore_local_iprule_prio; # true if having reordered local iprule
my @cleanup_restore_v4_defaultroutes; # temporarily replaced defaultroutes
sub cleanup_and_exit {
my $exitcode = shift;
if(defined($cleanup_remove_clat_dev)) {
d("Cleanup: Removing CLAT device");
if(defined($cleanup_remove_tayga_clat_dev)) {
d("Cleanup: Removing TAYGA CLAT device");
cmd(\&w, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--rmtun");
}
if(defined($cleanup_delete_taygaconf)) {
@@ -582,6 +689,14 @@ sub cleanup_and_exit {
unlink(cfg("tayga-conffile"))
or w("unlink('", cfg("tayga-conffile"), "') failed");
}
if(defined($cleanup_remove_nat46_clat_dev)) {
d("Cleanup: Removing nat46 CLAT device");
my $nat46_control_fh;
open($nat46_control_fh, ">/proc/net/nat46/control") or
err("Could not open nat46 control socket for writing");
print $nat46_control_fh "del ", cfg("clat-dev"), "\n";
close($nat46_control_fh) or err("close($nat46_control_fh: $!");
}
if(defined($cleanup_zero_forwarding_sysctl)) {
d("Cleanup: Resetting forwarding sysctl to 0");
sysctl("net/ipv6/conf/all/forwarding", 0);
@@ -595,18 +710,35 @@ sub cleanup_and_exit {
sysctl("net/ipv6/conf/" . cfg("plat-dev") . "/proxy_ndp", 0);
}
if(defined($cleanup_remove_proxynd_entry)) {
d("Cleanup: Removing Proxy-ND entry for ", cfg("clat-v6-addr"), "on ",
d("Cleanup: Removing Proxy-ND entry for ", cfg("clat-v6-addr"), " on ",
cfg("plat-dev"));
cmd(\&w, cfg("cmd-ip"), qw(-6 neighbour delete proxy), cfg("clat-v6-addr"),
"dev", cfg("plat-dev"));
}
if(defined($cleanup_remove_ip6tables_rules)) {
d("Cleanup: Removing ip6tables rules allowing traffic between the CLAT ",
"and PLAT devices");
cmd(\&w, cfg("cmd-ip6tables"), qw(-D FORWARD -i), cfg("clat-dev"),
"-o", cfg("plat-dev"), qw(-j ACCEPT));
cmd(\&w, cfg("cmd-ip6tables"), qw(-D FORWARD -i), cfg("plat-dev"),
"-o", cfg("clat-dev"), qw(-j ACCEPT));
if(defined($cleanup_remove_nftable)) {
d("Cleanup: Removing clatd netfilter table");
cmd(\&w, cfg("cmd-nft"), "delete table ip6 clatd");
}
for my $rt (@cleanup_restore_v4_defaultroutes) {
d("Cleanup: Restoring temporarily replaced IPv4 default route");
cmd(\&w, cfg("cmd-ip"), qw(-4 route add), @{$rt});
}
if(defined($cleanup_restore_local_iprule_prio)) {
d("Cleanup: Restoring local ip rule priority to 0");
cmd(\&w, cfg("cmd-ip"), qw(-6 rule add prio 0 table local));
cmd(\&w, cfg("cmd-ip"), qw(-6 rule del prio 1 table local));
}
if(defined($cleanup_remove_clat_iprule)) {
d("Cleanup: Removing ip rule for redirecting inbound traffic to CLAT");
cmd(\&w, cfg("cmd-ip"), qw(-6 rule del prio 0 table), cfg("route-table"));
}
if(defined($cleanup_remove_ufw_rules)) {
cmd(\&w, cfg("cmd-ufw"), qw(route delete allow in on), cfg("clat-dev"),
"from", cfg("clat-v6-addr"), qw(out on), cfg("plat-dev"), "to",
cfg("plat-prefix"));
cmd(\&w, cfg("cmd-ufw"), qw(route delete allow in on), cfg("plat-dev"),
"from", cfg("plat-prefix"), qw(out on), cfg("clat-dev"), "to",
cfg("clat-v6-addr"));
}
exit($exitcode);
@@ -643,8 +775,8 @@ for (my $i = 0; $i < @ARGV;) {
next;
} elsif($ARGV[$i] =~ /^(-h|--help)$/) {
print <<"EOF";
clatd v$VERSION - a 464XLAT (RFC 6877) CLAT and SIIT-DC Host Agent
(I-D.anderson-v6ops-siit-dc-2xlat) implementation for Linux
clatd v$VERSION - a 464XLAT (RFC 6877) CLAT and SIIT-DC Edge Relay
(RFC 7756) implementation for Linux
EOF
print "\n";
print " Usage: clatd [-q] [-d [-d]] [-c config-file] ",
@@ -696,7 +828,9 @@ p("Starting clatd v$VERSION by Tore Anderson <tore\@fud.no>");
#
# Step 1: Fill in any essential blanks in the configuration by auto-detecting
# any missing values.
$CFG{"plat-prefix"} ||= get_plat_prefix();
$CFG{"plat-prefix"} ||= get_plat_prefix_from_networkd()
unless($CFG{"dns64-servers"});
$CFG{"plat-prefix"} ||= get_plat_prefix_from_dns64();
if(!$CFG{"plat-prefix"}) {
w("No PLAT prefix was discovered or specified; 464XLAT cannot work.");
exit 0;
@@ -717,20 +851,45 @@ if(!$CFG{"plat-prefix"}) {
}
p("Using PLAT (NAT64) prefix: $CFG{'plat-prefix'}");
}
$CFG{"plat-dev"} ||= get_plat_dev();
my @plat_dev = get_plat_dev();
$CFG{"plat-dev"} ||= $plat_dev[0];
p("Device facing the PLAT: ", $CFG{"plat-dev"});
$CFG{"clat-v6-addr"} ||= get_clat_v6_addr();
if($CFG{"clat-v6-addr"} eq "shared") {
if(!$plat_dev[1]) {
err("Could not determine source IP facing the PLAT, needed due to using ",
"clat-v6-addr=shared");
}
$CFG{"clat-v6-addr"} = $plat_dev[1];
if(!defined($CFG{"proxynd-enable"})) {
$CFG{"proxynd-enable"} = 0;
}
if(!defined($CFG{"ctmark"})) {
$CFG{"ctmark"} = 0xc1a7;
}
} elsif($CFG{"clat-v6-addr"} eq "derived") {
$CFG{"clat-v6-addr"} = get_clat_v6_addr();
}
# Fill defaults for proxynd-enable and ctmark for clat-v6-addr!=shared
if(!defined($CFG{"proxynd-enable"})) {
$CFG{"proxynd-enable"} = 1;
}
if(!defined($CFG{"ctmark"})) {
$CFG{"ctmark"} = 0;
}
p("Using CLAT IPv4 address: ", $CFG{"clat-v4-addr"});
p("Using CLAT IPv6 address: ", $CFG{"clat-v6-addr"});
if(!defined($CFG{"ip6tables-enable"})) {
$CFG{"ip6tables-enable"} = -e "/sys/module/ip6table_filter" ? 1 : 0;
if(!$CFG{"v4-defaultroute-advmss"} and cfgint("v4-defaultroute-mtu")) {
$CFG{"v4-defaultroute-advmss"} = $CFG{"v4-defaultroute-mtu"} - 40;
}
#
# Step 1: Detect if there is an IPv4 default route on the system from before.
# If so we have no need for 464XLAT, and we can just exit straight away
#
if(cfgbool("v4-conncheck-enable")) {
if(cfgbool("v4-conncheck-enable") and !cfgbool("v4-defaultroute-replace")) {
my $delay = cfgint("v4-conncheck-delay");
p("Checking if this system already has IPv4 connectivity ",
$delay ? "in $delay sec(s)" : "now");
@@ -748,35 +907,40 @@ if(cfgbool("v4-conncheck-enable")) {
d("Skipping IPv4 connectivity check at user request");
}
# Let's figure out if there's nat46 kernel module loaded
my $nat46_controlfile = "/proc/net/nat46/control";
my $use_nat46 = (-e $nat46_controlfile);
#
# Write out the TAYGA config file, either to the user-specified location,
# or to a temporary file (which we'll delete later)
#
my $tayga_conffile = cfg("tayga-conffile");
my $tayga_conffile_fh;
if(!$tayga_conffile) {
require File::Temp;
($tayga_conffile_fh, $tayga_conffile) = File::Temp::tempfile();
d2("Using temporary conffile for TAYGA: $tayga_conffile");
$CFG{"tayga-conffile"} = $tayga_conffile;
$cleanup_delete_taygaconf = 1;
} else {
open($tayga_conffile_fh, ">$tayga_conffile") or
err("Could not open TAYGA config file '$tayga_conffile' for writing");
unless($use_nat46) {
my $tayga_conffile = cfg("tayga-conffile");
my $tayga_conffile_fh;
if(!$tayga_conffile) {
require File::Temp;
($tayga_conffile_fh, $tayga_conffile) = File::Temp::tempfile();
d2("Using temporary conffile for TAYGA: $tayga_conffile");
$CFG{"tayga-conffile"} = $tayga_conffile;
$cleanup_delete_taygaconf = 1;
} else {
open($tayga_conffile_fh, ">$tayga_conffile") or
err("Could not open TAYGA config file '$tayga_conffile' for writing");
}
print $tayga_conffile_fh "# Ephemeral TAYGA config file written by $0\n";
print $tayga_conffile_fh "# This file may be safely deleted at any time.\n";
print $tayga_conffile_fh "tun-device ", cfg("clat-dev"), "\n";
print $tayga_conffile_fh "prefix ", cfg("plat-prefix"), "\n";
print $tayga_conffile_fh "ipv4-addr ", cfg("tayga-v4-addr"), "\n";
print $tayga_conffile_fh "map ", cfg("clat-v4-addr"), " ",
cfg("clat-v6-addr"),"\n";
close($tayga_conffile_fh) or err("close($tayga_conffile_fh: $!");
}
print $tayga_conffile_fh "# Ephemeral TAYGA config file written by $0\n";
print $tayga_conffile_fh "# This file may be safely deleted at any time.\n";
print $tayga_conffile_fh "tun-device ", cfg("clat-dev"), "\n";
print $tayga_conffile_fh "prefix ", cfg("plat-prefix"), "\n";
print $tayga_conffile_fh "ipv4-addr ", cfg("tayga-v4-addr"), "\n";
print $tayga_conffile_fh "map ", cfg("clat-v4-addr"), " ",
cfg("clat-v6-addr"),"\n";
close($tayga_conffile_fh) or err("close($tayga_conffile_fh: $!");
#
# Enable IPv6 forwarding if necessary
#
@@ -802,16 +966,27 @@ if(cfgbool("forwarding-enable")) {
}
#
# Add ip6tables rules permitting traffic between the PLAT and the CLAT
# Add firewall rules to allow PLAT-CLAT traffic if a supported local firewall
# framework is present and active
#
if(cfgbool("ip6tables-enable")) {
p("Adding ip6tables rules allowing traffic between the CLAT ",
"and PLAT devices");
cmd(\&w, cfg("cmd-ip6tables"), qw(-I FORWARD -i), cfg("clat-dev"),
"-o", cfg("plat-dev"), qw(-j ACCEPT));
cmd(\&w, cfg("cmd-ip6tables"), qw(-I FORWARD -i), cfg("plat-dev"),
"-o", cfg("clat-dev"), qw(-j ACCEPT));
$cleanup_remove_ip6tables_rules = 1;
if(can_run(cfg("cmd-ufw"))) {
open(my $fd, '-|', cfg("cmd-ufw"), "status")
or err("'ufw status' failed to execute");
while(<$fd>) {
if(/^Status: active$/) {
p("UFW local firewall framework active, allowing CLAT-PLAT traffic");
cmd(\&err, cfg("cmd-ufw"), qw(route allow in on), cfg("clat-dev"),
"from", cfg("clat-v6-addr"), qw(out on), cfg("plat-dev"), "to",
cfg("plat-prefix"));
cmd(\&err, cfg("cmd-ufw"), qw(route allow in on), cfg("plat-dev"),
"from", cfg("plat-prefix"), qw(out on), cfg("clat-dev"), "to",
cfg("clat-v6-addr"));
$cleanup_remove_ufw_rules = 1;
}
}
close($fd) or err("'ufw status' failed");
} else {
d("UFW command '", cfg("cmd-ufw"), "' not found, not installing UFW rules");
}
#
@@ -832,51 +1007,155 @@ if(cfgbool("proxynd-enable")) {
$cleanup_remove_proxynd_entry = 1;
}
# Move the default ip rule for local traffic to a higher priority, so we can
# override it. This is necessary if re-using the primary IPv6 address on the
# PLAT-facing device for the CLAT function.
open(my $fd, '-|', cfg("cmd-ip"), qw(-6 rule show prio 0 table local))
or err("'ip -6 rule show prio 0 table local' failed to execute");
while(<$fd>) {
d("Increasing prio of default local ip rule to 1");
cmd(\&err, cfg("cmd-ip"), qw(-6 rule add prio 1 table local));
cmd(\&err, cfg("cmd-ip"), qw(-6 rule del prio 0 table local));
$cleanup_restore_local_iprule_prio = 1;
last;
}
close($fd) or err("'ip -6 rule show prio 0 table local' failed");
#
# Create the CLAT tun interface, add the IPv4 address to it as well as the
# route to the corresponding IPv6 address, and possibly an IPv4 default route
#
p("Creating and configuring up CLAT device '", cfg("clat-dev"), "'");
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--mktun",
cfgint("debug") ? "-d" : "");
$cleanup_remove_clat_dev = 1;
if($use_nat46) {
my $nat46_control_fh;
open($nat46_control_fh, ">$nat46_controlfile") or
err("Could not open nat46 control socket for writing");
print $nat46_control_fh "add ", cfg("clat-dev"), "\n";
close($nat46_control_fh) or err("close($nat46_control_fh: $!");
$cleanup_remove_nat46_clat_dev = 1;
} else {
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--mktun",
cfgint("debug") ? "-d" : "");
$cleanup_remove_tayga_clat_dev = 1;
}
cmd(\&err, cfg("cmd-ip"), qw(link set up dev), cfg("clat-dev"));
cmd(\&err, cfg("cmd-ip"), qw(-4 address add), cfg("clat-v4-addr"),
"dev", cfg("clat-dev"));
cmd(\&err, cfg("cmd-ip"), qw(-6 route add), cfg("clat-v6-addr"),
"dev", cfg("clat-dev"));
"dev", cfg("clat-dev"), "table", cfgint("route-table"));
cmd(\&err, cfg("cmd-ip"), qw(-6 rule add prio 0 from),
cfg("plat-prefix"), "to", cfg("clat-v6-addr"),
cfgint("ctmark") ? ("fwmark", cfgint("ctmark")) : (),
"table", cfg("route-table"));
$cleanup_remove_clat_iprule = 1;
if(cfgint("ctmark")) {
p("Adding clatd nftable, using conntrack mark ", cfgint("ctmark"));
open(my $fd, '|-', cfg("cmd-nft"), "-f-")
or err("'nft -f-' failed to execute");
print $fd "add table ip6 clatd\n";
print $fd "add chain ip6 clatd prerouting ",
"{ type filter hook prerouting priority mangle; }\n";
print $fd "add rule ip6 clatd prerouting",
" iif ", cfg("clat-dev"),
" ip6 saddr ", cfg("clat-v6-addr"),
" ip6 daddr ", cfg("plat-prefix"),
" ct mark set ", cfgint("ctmark"),
# set meta mark as well, to placate firewalld's IPv6_rpfilter and NixOS' rpfilter rules
" meta mark set ", cfgint("ctmark"), " counter\n";
print $fd "add rule ip6 clatd prerouting",
" iif ", cfg("plat-dev"),
" ip6 saddr ", cfg("plat-prefix"),
" ip6 daddr ", cfg("clat-v6-addr"),
" ct mark ", cfgint("ctmark"),
" meta mark set ct mark counter\n";
close($fd) or err("'nft -f-' failed");
$cleanup_remove_nftable = 1;
}
if(cfgbool("v4-defaultroute-replace")) {
open(my $fd, '-|', cfg("cmd-ip"), qw(-4 route show default))
or err("'ip -4 route show default' failed to execute");
while(<$fd>) {
my @rt = split(/\s+/, $_);
d("Replacing pre-existing IPv4 default route: @rt");
cmd(\&err, cfg("cmd-ip"), qw(-4 route del), @rt);
push(@cleanup_restore_v4_defaultroutes, \@rt);
}
close($fd) or err("'ip -4 route show default' failed");
}
if(cfgbool("v4-defaultroute-enable")) {
my @cmdline = (qw(-4 route add default dev), cfg("clat-dev"));
if(cfgint("v4-defaultroute-metric")) {
push(@cmdline, ("metric", cfgint("v4-defaultroute-metric")))
}
if(cfgint("v4-defaultroute-mtu")) {
push(@cmdline, ("mtu", cfgint("v4-defaultroute-mtu")))
push(@cmdline, ("mtu", cfgint("v4-defaultroute-mtu")));
}
if(cfgint("v4-defaultroute-advmss")) {
push(@cmdline, ("advmss", cfgint("v4-defaultroute-advmss")));
}
p("Adding IPv4 default route via the CLAT");
cmd(\&err, cfg("cmd-ip"), @cmdline);
}
# Inject %CFG into %ENV and then run the up script
for my $key (sort keys(%CFG)) {
my $var = $key;
$var =~ y/-/_/;
d2(sprintf("Script env: %s=%s", $key, $CFG{$key} || ''));
$ENV{$var} = $CFG{$key};
}
if(cfg("script-up")) {
d("Running custom startup script: ", cfg("script-up"));
cmd(\&err, cfg("script-up"));
}
#
# All preparation done! We can now start TAYGA, which will handle the actual
# All preparation done! We can now start nat46 or TAYGA, which will handle the actual
# translation of IP packets.
#
p("Starting up TAYGA, using config file '$tayga_conffile'");
if($use_nat46){
p("Setting up nat46 kernel module");
my $nat46_control_fh;
open($nat46_control_fh, ">$nat46_controlfile") or
err("Could not open nat46 control socket for writing");
print $nat46_control_fh "config ", cfg("clat-dev"), " local.style NONE\n";
print $nat46_control_fh "config ", cfg("clat-dev"), " local.v4 ", cfg("clat-v4-addr"), "/32\n";
print $nat46_control_fh "config ", cfg("clat-dev"), " local.v6 ", cfg("clat-v6-addr"), "/128\n";
print $nat46_control_fh "config ", cfg("clat-dev"), " remote.style RFC6052\n";
print $nat46_control_fh "config ", cfg("clat-dev"), " remote.v6 ", cfg("plat-prefix"), "\n";
close($nat46_control_fh) or err("close($nat46_control_fh: $!");
# We don't want systemd etc. to actually kill this script when stopping the
# service, just TAYGA (so that we can get around to cleaning up after
# ourselves)
$SIG{'INT'} = 'IGNORE';
$SIG{'TERM'} = 'IGNORE';
# Nothing more to do here, we just set up a cleanup handler and sleep forever.
$SIG{'INT'} = \&cleanup_handler;
$SIG{'TERM'} = \&cleanup_handler;
sleep();
} else {
my $tayga_conffile = cfg("tayga-conffile");
p("Starting up TAYGA, using config file '$tayga_conffile'");
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--nodetach",
cfgint("debug") ? "-d" : "");
p("TAYGA terminated, cleaning up and exiting");
# We don't want systemd etc. to actually kill this script when stopping the
# service, just TAYGA (so that we can get around to cleaning up after
# ourselves)
$SIG{'INT'} = 'IGNORE';
$SIG{'TERM'} = 'IGNORE';
$SIG{'INT'} = 'DEFAULT';
$SIG{'TERM'} = 'DEFAULT';
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--nodetach",
cfgint("debug") ? "-d" : "");
p("TAYGA terminated, cleaning up and exiting");
#
# TAYGA exited, probably because we're shutting down. Cleanup and exit.
#
cleanup_and_exit(0);
$SIG{'INT'} = 'DEFAULT';
$SIG{'TERM'} = 'DEFAULT';
#
# TAYGA exited, probably because we're shutting down. Run the down script, then
# cleanup and exit.
#
if(cfg("script-down")) {
d("Running custom shutdown script: ", cfg("script-down"));
cmd(\&err, cfg("script-down"));
}
cleanup_and_exit(0);
}

49
scripts/clatd.ifupdown Executable file
View File

@@ -0,0 +1,49 @@
#!/bin/sh
# clatd(8) integration in interfaces(5)
# To use just copy this script to /etc/network/if-up.d/ and
# /etc/network/if-down.d/
# To configure which connection should include clatd the file
# /etc/default/clatd is used. To enable clatd on a interface
# include the interface in CLATD_IFACES (space sperated)
# For logical intefaces (i.e. wpa_supplicant integration) you
# can also set one CLATD_LOGICAL. See interfaces(5) for details.
. /etc/default/clatd
use_clatd=0
for iface in $CLATD_IFACES
do
if [ "$iface" = "$IFACE" ]
then
use_clatd=1
break
fi
done
if [ "$LOGICAL" = "$CLATD_LOGICAL" ]
then
use_clatd=1
fi
if [ "$use_clatd" -ne 1 ]
then
exit 0
fi
case "$PHASE" in
post-up)
systemctl start clatd@"$IFACE"
exit 0
;;
pre-down)
systemctl stop clatd@"$IFACE"
;;
*)
exit 0
;;
esac

View File

@@ -7,6 +7,18 @@
# Written by Tore Anderson <tore@fud.no>
#
# Newer NetworkManager versions will run the dispatcher scripts once
# a new unmanaged interface shows up, including the 'clat' interface
# created by clatd/TAYGA. So if we're being called due to our own
# interface showing up, do nothing, otherwise we will end up
# committing suicide from the restarts below
[ "$DEVICE_IFACE" = "clat" ] && exit 0
# We're only acting on interface "up" or "down" events. NM will run the
# dispatcher scripts for other events we're not interested in, like the
# hostname being set or a DHCP lease being renewed. Ignore those.
[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# We simply restart clatd in all situations, as no matter if an interface
# goes up or down, it may mean that the PLAT device changes, it may mean
# native IPv4 appearing or disappearing, or it may mean that DNS64 became
@@ -14,11 +26,6 @@
# start from scratch than to figure out if a restart is truly necessary
# systemd-based distros
if test -x /usr/bin/systemctl; then
/usr/bin/systemctl restart clatd.service
fi
# upstart-based distros
if test -x /sbin/initctl; then
/sbin/initctl restart clatd
if which systemctl &> /dev/null; then
systemctl --no-block restart clatd.service
fi

View File

@@ -11,7 +11,10 @@
[Unit]
Description=464XLAT CLAT daemon
Documentation=man:clatd(8)
Wants=network-online.target
After=network-online.target
After=nss-lookup.target
StartLimitIntervalSec=0
[Service]
Type=simple

View File

@@ -1,16 +0,0 @@
#
# clatd service file for upstart
#
# Install it to: /etc/init/clatd.conf
# Start it with: initctl start clatd
#
# Written by Tore Anderson <tore@fud.no>
#
description "464XLAT CLAT daemon"
author "Tore Anderson <tore@fud.no>"
start on net-device-up
stop on runlevel [!2345]
exec /usr/sbin/clatd

28
scripts/clatd@.systemd Normal file
View File

@@ -0,0 +1,28 @@
#
# clatd service file for systemd
#
# Install it to: /etc/systemd/system/clatd@.service
# Start it with: systemctl start clatd@iface.service
# Stop it with: systemctl stop clatd@iface.service
#
# This is to dynamicaly enable and disable the clat depending on the
# connected network.
#
# Written by Tore Anderson <tore@fud.no>
# Updated by satanist <satanist+fsmi@bureaucracy.de>
#
[Unit]
Description=464XLAT CLAT daemon
Documentation=man:clatd(8)
Wants=network-online.target
After=network-online.target
After=nss-lookup.target
StartLimitIntervalSec=0
[Service]
Type=simple
ExecStart=/usr/sbin/clatd -c /etc/clatd/%i.conf
[Install]
WantedBy=multi-user.target