Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f564300918 | ||
|
|
1c4ca683b5 | ||
|
|
2ad52c57f8 | ||
|
|
92afe35b0b | ||
|
|
33252dcb13 | ||
|
|
03042228be | ||
|
|
90d4ac6e70 | ||
|
|
f0d7c09adf | ||
|
|
05728771ca | ||
|
|
cc64d0c6f3 | ||
|
|
6a582bf1e4 | ||
|
|
6342488889 | ||
|
|
eb27dd5ead | ||
|
|
f763915903 | ||
|
|
00fa0f3266 | ||
|
|
3ea303b521 | ||
|
|
04062b282d | ||
|
|
6d2ad96c2f | ||
|
|
e6e0f4ecc1 | ||
|
|
a93f5ff491 | ||
|
|
b8a7092873 | ||
|
|
8aa8bfa5d5 | ||
|
|
ff6aa57c4b | ||
|
|
a416278570 | ||
|
|
4583c592d8 | ||
|
|
41a312f908 | ||
|
|
66e1e5fc28 | ||
|
|
1441a3ff4b | ||
|
|
19c4042f1e | ||
|
|
888e30bd2b | ||
|
|
b8f583a4e0 | ||
|
|
c228c2bb64 | ||
|
|
ebd8487dd0 | ||
|
|
1218ca7979 | ||
|
|
3f73b5281e | ||
|
|
6c7c2d2a92 | ||
|
|
18dca086c2 | ||
|
|
fb4587bfd6 | ||
|
|
1abcec1285 | ||
|
|
9a1a4ae797 | ||
|
|
aa92cb0287 | ||
|
|
5e2297903a | ||
|
|
f976f46b57 | ||
|
|
e8cb719f10 | ||
|
|
0bc3bbd797 | ||
|
|
a80e2f30ab | ||
|
|
9f1789f34d | ||
|
|
2f2a59ddf3 | ||
|
|
b5725c093c | ||
|
|
ad114b18cf | ||
|
|
c48ad644c0 | ||
|
|
81f2c61364 | ||
|
|
54dd5ac854 | ||
|
|
6f359a8d60 | ||
|
|
058061525e | ||
|
|
2d2cbd8c19 | ||
|
|
85002b6a26 | ||
|
|
da7b48e779 |
22
LICENCE
22
LICENCE
@@ -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
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
you wish. If you like it, and we happen to meet one day, you can buy me
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
a beer in return. If you really like it, make it an IPA.
|
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.
|
||||||
|
|||||||
33
Makefile
33
Makefile
@@ -1,20 +1,27 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# Install the main script to /usr/sbin
|
# Install the main script
|
||||||
install -m0755 clatd /usr/sbin/clatd
|
install -m0755 clatd $(DESTDIR)$(PREFIX)/sbin/clatd
|
||||||
# Install manual page if pod2man is installed
|
# 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
|
# 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 -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 "/etc/systemd/system/clatd.service" && test ! -e "/etc/systemd/system/multi-user.target.wants/clatd.service"; then systemctl enable clatd.service; 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 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
|
|
||||||
# Install NetworkManager dispatcher script if applicable
|
# 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:
|
installdeps:
|
||||||
# .deb/apt-get based distros
|
# .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
|
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/YUM-based distros
|
# .rpm/DNF/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
|
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
|
||||||
# to get TAYGA on .rpm/YUM-based distros, we unfortunately need to install from source
|
# 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 /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 "$(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
|
||||||
|
|||||||
329
README.pod
329
README.pod
@@ -1,6 +1,6 @@
|
|||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
B<clatd> - a CLAT implementation for Linux
|
B<clatd> - a CLAT / SIIT-DC Edge Relay implementation for Linux
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
@@ -13,15 +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,
|
make use of DNS64 (for example because they use legacy AF_INET socket calls,
|
||||||
or if they are simply not using DNS64).
|
or if they are simply not using DNS64).
|
||||||
|
|
||||||
It may also be used in combination with a stateless PLAT as defined by
|
B<clatd> may also be used to implement an SIIT-DC Edge Relay as described in
|
||||||
I<I-D.anderson-siit-dc> to give the otherwise IPv6-only host a public IPv4
|
I<RFC 7756>. In this scenario, the PLAT is in reality a SIIT-DC Border Relay
|
||||||
address with connectivity to the IPv4 internet. This may be useful in a
|
(see I<RFC 7755>) instead of a Stateful NAT64 (see I<RFC6146>). When used as a
|
||||||
server environment that are using legacy IPv4-only applications as described
|
SIIT-DC Edge Relay, you will probably want to manually configure the settings
|
||||||
above.
|
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
|
It relies either on the software package TAYGA by Nathan Lutchansky or on the
|
||||||
translation of packets between IPv4 and IPv6 (I<RFC 6145>) TAYGA may be
|
kernel module nat46 by Andrew Yourtchenko for the actual translation of packets
|
||||||
downloaded from its home page at L<http://www.litech.org/tayga/>.
|
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
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ B<clatd> [options]
|
|||||||
|
|
||||||
=item -q
|
=item -q
|
||||||
|
|
||||||
Quiet mode; suppress normal output This is the same as setting B<quiet=1>.
|
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>.
|
Warnings and errors are still outputted, to silence those too, repeat I<-q>.
|
||||||
|
|
||||||
=item -d
|
=item -d
|
||||||
@@ -61,7 +64,7 @@ configuration file. Refer to the section B<CONFIGURATION> below for more info.
|
|||||||
=head1 INVOCATION
|
=head1 INVOCATION
|
||||||
|
|
||||||
B<clatd> is meant to be run under a daemonising control process such as
|
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
|
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.
|
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
|
It may also be run directly from the command line. It will run until killed
|
||||||
@@ -84,23 +87,23 @@ of B<clatd> and its dependencies:
|
|||||||
|
|
||||||
=back
|
=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
|
NetworkManager scripts if your distribution appears to be using them, and
|
||||||
install all the dependencies. Note that TAYGA isn't available in RPM format,
|
install all the dependencies. Note that TAYGA isn't available in all RPM-based
|
||||||
so on RedHat/Fedora the installdeps target will install gcc and attempt to
|
distros (in particular RHEL and its clones). It is however available in EPEL
|
||||||
compile TAYGA from source.
|
(see L<https://fedoraproject.org/wiki/EPEL>).
|
||||||
|
|
||||||
=head1 CONFIGURATION
|
=head1 CONFIGURATION
|
||||||
|
|
||||||
B<clatd> is designed to be able to run without any user-supplied 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
|
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
|
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
|
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
|
on the command line when starting B<clatd>, which takes precedence over settings
|
||||||
in the configuration file.
|
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:
|
possible values follow below:
|
||||||
|
|
||||||
=over
|
=over
|
||||||
@@ -118,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 stuff. These are the equivalent of providing the command line option I<-d>
|
||||||
the specified number of times.
|
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>)
|
=item B<clat-dev=string> (default: I<clat>)
|
||||||
|
|
||||||
The name of the network device used by the CLAT. There should be no reason to
|
The name of the network device used by the CLAT. There should be no reason to
|
||||||
@@ -129,74 +183,129 @@ simultaneously.
|
|||||||
The IPv4 address that will be assigned to the CLAT device. Local applications
|
The IPv4 address that will be assigned to the CLAT device. Local applications
|
||||||
will bind to this address when communicating with external IPv4 destinations.
|
will bind to this address when communicating with external IPv4 destinations.
|
||||||
In a standard 464XLAT environment with a stateful NAT64 serving as the PLAT,
|
In a standard 464XLAT environment with a stateful NAT64 serving as the PLAT,
|
||||||
there should be no need to change the default, but if the PLAT is a stateless
|
there should be no need to change the default.
|
||||||
translator (a la I-D.draft-anderson-siit-dc), you might want to set this to
|
|
||||||
the true external address used externally, so the the local applications can
|
|
||||||
correctly identify which public address they'll be using on the IPv4 internet.
|
|
||||||
|
|
||||||
The default address is one from I<I-D.draft-byrne-v6ops-clatip>.
|
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.
|
||||||
|
|
||||||
=item B<clat-v6-addr=ipv6-address> (default: auto-generated)
|
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
|
The IPv6 address of the CLAT. Traffic to/from the B<clat-v4-addr> will be
|
||||||
translated into this address. By default, B<clatd> will attempt to figure out
|
translated into this address. When using B<clatd> as an SIIT-DC Edge Relay, you
|
||||||
which network device will be used for traffic towards the PLAT, see if there
|
will want to set this to the same IPv6 address in the Explicit Address Mapping
|
||||||
is any SLAAC-configured addresses on it, and if so substitute the '0xfffe'
|
configured in the SIIT-DC Border Relay.
|
||||||
value in the middle of the Interface ID for '0xc1a7' to generate a new
|
|
||||||
address for the CLAT. If you're not using SLAAC you will have to set this
|
When set to I<shared> (the default), B<clatd> will re-use and share the primary
|
||||||
manually.
|
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)
|
=item B<dns64-servers=srv1,[srv2,..]> (default: use system resolver)
|
||||||
|
|
||||||
Comma-separated list of DNS64 servers to use when discovering the PLAT prefix
|
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
|
using the method described in I<RFC 7050>. By default, B<clatd> will first try
|
||||||
used, but it might be useful to override this in case your ISP doesn't provide
|
to determine if systemd-networkd is aware of a PLAT prefix (learned from the
|
||||||
you with a DNS64-enabled name server, and you want to test B<clatd> using any of
|
PREF64 Router Advertisement option, cf. I<RFC 8781>), falling back on using
|
||||||
the public DNS64/NAT64 instances on the internet. The first PLAT prefix
|
DNS64 discovery towards the system resolver if it isn't.
|
||||||
encountered will be used.
|
|
||||||
|
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)
|
=item B<cmd-ip=path> (default: assume in $PATH)
|
||||||
|
|
||||||
Path to the B<ip> binary from the iproute2 package available at
|
Path to the B<ip> binary from the iproute2 package available at
|
||||||
L<https://www.kernel.org/pub/linux/utils/net/iproute2>. Required.
|
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
|
Path to the B<networkctl> binary from systemd-networkd. Required in order to
|
||||||
L<http://netfilter.org>. Only required for adding ip6tables rules
|
use any PLAT prefix discovered by systemd-networkd from the PREF64 Router
|
||||||
(see the B<ip6tables-enable> configuration setting).
|
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)
|
=item B<cmd-tayga=path> (default: assume in $PATH)
|
||||||
|
|
||||||
Path to the B<tayga> binary from the TAYGA package available at
|
Path to the B<tayga> binary from the TAYGA package available at
|
||||||
L<http://www.litech.org/tayga>. Required.
|
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>)
|
=item B<forwarding-enable=bool> (default: I<yes>)
|
||||||
|
|
||||||
Controls whether or not B<clatd> should enable IPv6 forwarding if necessary. IPv6
|
Controls whether or not B<clatd> should enable IPv6 forwarding if necessary.
|
||||||
forwarding is necessary for B<clatd> to work correctly. It will also ensure that
|
IPv6 forwarding is necessary for B<clatd> to work correctly. It will also
|
||||||
the I<accept_ra> sysctl is to '2' for all devices have it set to '1', in order
|
ensure that the I<accept_ra> sysctl is to '2' for all devices have it set to
|
||||||
to prevent any connectivity loss as a result of enabling forwarding.
|
'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
|
All sysctls that are modified will be restored to their original values when
|
||||||
B<clatd> is shutting down.
|
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)
|
=item B<plat-dev> (default: auto-detect)
|
||||||
|
|
||||||
Which network device is facing the PLAT (NAT64). By default, this is
|
Which network device is facing the PLAT (NAT64). By default, this is
|
||||||
auto-detecting by performing a route table lookup towards the PLAT prefix.
|
auto-detected by performing a route table lookup towards the PLAT prefix. This
|
||||||
This setting is used when setting up generating the CLAT IPv6 address, and
|
setting is used when generating the CLAT IPv6 address, adding Proxy-ND entries,
|
||||||
when setting up ip6tables rules and Proxy-ND entries.
|
and nftables rules.
|
||||||
|
|
||||||
=item B<plat-prefix> (default: auto-detect)
|
=item B<plat-prefix> (default: auto-detect)
|
||||||
|
|
||||||
@@ -204,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
|
I<RFC 6052> for a closer description. By default, this is auto-detected from
|
||||||
DNS64 answers using the method in I<RFC 7050>.
|
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
|
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
|
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
|
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
|
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
|
IPv6 ND isn't used. However it doesn't hurt to add Proxy-ND entries in that
|
||||||
case, either.
|
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.
|
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)
|
=item B<tayga-conffile> (default: use a temporary file)
|
||||||
|
|
||||||
Where to write the TAYGA configuration file. By default, a temporary file will
|
Where to write the TAYGA configuration file. By default, a temporary file will
|
||||||
@@ -230,7 +355,7 @@ 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
|
tracerouting to IPv4 destinations), and you may also ping it to verify that
|
||||||
the TAYGA process is still alive and well.
|
the TAYGA process is still alive and well.
|
||||||
|
|
||||||
The default address is one from I<I-D.draft-byrne-v6ops-clatip>.
|
The default address is one from I<RFC 7335>.
|
||||||
|
|
||||||
=item B<v4-conncheck-enable=bool> (default: I<yes>)
|
=item B<v4-conncheck-enable=bool> (default: I<yes>)
|
||||||
|
|
||||||
@@ -246,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<v4-defaultroute-enable> and B<v4-defaultroute-metric> settings to prevent
|
||||||
B<clatd> from interfering with native IPv4 connectivity.
|
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>)
|
=item B<v4-conncheck-delay=seconds> (default: I<10>)
|
||||||
|
|
||||||
When performing an IPv4 connectivity check, wait this number of seconds
|
When performing an IPv4 connectivity check, wait this number of seconds
|
||||||
@@ -266,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
|
to disable this and instead control manually which IPv4 destinations is
|
||||||
reached through the CLAT and which are not.
|
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>)
|
=item B<v4-defaultroute-metric=integer> (default: I<2048>)
|
||||||
|
|
||||||
The metric of the IPv4 default route pointing to the CLAT. The default is
|
The metric of the IPv4 default route pointing to the CLAT. The default is
|
||||||
@@ -286,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
|
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>.
|
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
|
=back
|
||||||
|
|
||||||
=head1 LIMITATIONS
|
=head1 LIMITATIONS
|
||||||
|
|
||||||
B<clatd> will not be able to acquire an IPv6 address for the CLAT if SLAAC
|
When using B<clat-v6-addr=shared> (or when B<clat-v6-addr> is set to another
|
||||||
isn't used. I<RFC 6877> suggests DHCPv6 IA_PD should be attempted in this
|
address assigned to a local interface) and B<ctmark> is set to 0, the host OS
|
||||||
case, but this isn't currently implemented.
|
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
|
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
|
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.
|
prefix before setting up the CLAT, as I<RFC 7050> suggest it should.
|
||||||
@@ -310,18 +486,31 @@ configuration file) when reporting a bug.
|
|||||||
|
|
||||||
=head1 LICENCE
|
=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
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
you wish. If you like it, and we happen to meet one day, you can buy me
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
a beer in return. If you really like it, make it an IPA.
|
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
|
=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 6052, RFC 6145, RFC 6146, RFC 6877, RFC 7050, RFC 7335 RFC 7755, RFC 7756,
|
||||||
|
RFC 7757, RFC 8781
|
||||||
I-D.anderson-siit-dc, I-D.byrne-v6ops-clatip
|
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|||||||
605
clatd
605
clatd
@@ -1,18 +1,33 @@
|
|||||||
#! /usr/bin/perl -w
|
#! /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
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# you wish. If you like it, and we happen to meet one day, you can buy me
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
# a beer in return. If you really like it, make it an IPA.
|
# 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
|
# See the file 'README.pod' in the source distribution or the manual page
|
||||||
# clatd(8) for more information.
|
# clatd(8) for more information.
|
||||||
#
|
#
|
||||||
use strict;
|
use strict;
|
||||||
|
use IPC::Cmd qw(can_run);
|
||||||
use Net::IP;
|
use Net::IP;
|
||||||
|
|
||||||
my $VERSION = "1.1";
|
my $VERSION = "2.0.0";
|
||||||
|
|
||||||
#
|
#
|
||||||
# Populate the global config hash with the default values
|
# Populate the global config hash with the default values
|
||||||
@@ -20,25 +35,33 @@ my $VERSION = "1.1";
|
|||||||
my %CFG;
|
my %CFG;
|
||||||
$CFG{"quiet"} = 0; # suppress normal output
|
$CFG{"quiet"} = 0; # suppress normal output
|
||||||
$CFG{"debug"} = 0; # debugging output level
|
$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-dev"} = "clat"; # TUN interface name to use
|
||||||
$CFG{"clat-v4-addr"} = "192.0.0.1"; # from I-D.draft-byrne-v6ops-clatip
|
$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{"dns64-servers"} = undef; # use system resolver by default
|
||||||
$CFG{"cmd-ip"} = "ip"; # assume in $PATH
|
$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-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{"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-dev"} = undef; # PLAT-facing device, default detect
|
||||||
$CFG{"plat-prefix"} = undef; # detect using DNS64 by default
|
$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-conffile"} = undef; # make a temporary one by default
|
||||||
$CFG{"tayga-v4-addr"} = "192.0.0.2"; # from I-D.draft-byrne-v6ops-clatip
|
$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-enable"} = 1; # exit if there's already a defroute
|
||||||
$CFG{"v4-conncheck-delay"} = 10; # seconds before checking for v4 conn.
|
$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-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-metric"} = 2048; # metric for the IPv4 defaultroute
|
||||||
$CFG{"v4-defaultroute-mtu"} = 1260; # MTU 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
|
# 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
|
# 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 {
|
sub cmd {
|
||||||
my $msgsub = shift;
|
my $msgsub = shift;
|
||||||
my $command = shift;
|
my @cmd = @_;
|
||||||
my @cmdline = @_;
|
|
||||||
|
|
||||||
d("cmd($command @cmdline)");
|
d("cmd(@cmd)");
|
||||||
|
|
||||||
if(system($command, @cmdline)) {
|
if(system(@cmd)) {
|
||||||
if($? == -1) {
|
if($? == -1) {
|
||||||
&{$msgsub}("cmd($command @cmdline) failed to execute");
|
&{$msgsub}("cmd(@cmd) failed to execute");
|
||||||
} elsif($? & 127) {
|
} elsif($? & 127) {
|
||||||
&{$msgsub}("cmd($command @cmdline) died with signal ", ($? & 127));
|
&{$msgsub}("cmd(@cmd) died with signal ", ($? & 127));
|
||||||
} else {
|
} else {
|
||||||
&{$msgsub}("cmd($command @cmdline) returned ", ($? >> 127));
|
&{$msgsub}("cmd(@cmd) returned ", ($? >> 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $?;
|
return $?;
|
||||||
@@ -89,7 +111,8 @@ sub cmd {
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Reads in key=value pairs from a configuration file, overwriting the default
|
# Reads in key=value pairs from a configuration file, overwriting the default
|
||||||
# setting in the %CFG hash. The key must exist, or we
|
# setting in the %CFG hash. The key must exist in the built-in hash, or we
|
||||||
|
# ignore the setting in the config file.
|
||||||
#
|
#
|
||||||
sub readconf {
|
sub readconf {
|
||||||
d("readconf('@_')");
|
d("readconf('@_')");
|
||||||
@@ -134,7 +157,7 @@ sub cfgbool {
|
|||||||
#
|
#
|
||||||
sub cfgint {
|
sub cfgint {
|
||||||
my ($key) = @_;
|
my ($key) = @_;
|
||||||
d2("cfgstr($key)");
|
d2("cfgint($key)");
|
||||||
if(!exists($CFG{$key})) {
|
if(!exists($CFG{$key})) {
|
||||||
err("key '$key' doesn't exist in config hash");
|
err("key '$key' doesn't exist in config hash");
|
||||||
}
|
}
|
||||||
@@ -149,7 +172,7 @@ sub cfgint {
|
|||||||
#
|
#
|
||||||
sub cfg {
|
sub cfg {
|
||||||
my ($key) = @_;
|
my ($key) = @_;
|
||||||
d2("cfgstr($key)");
|
d2("cfg($key)");
|
||||||
if(!exists($CFG{$key})) {
|
if(!exists($CFG{$key})) {
|
||||||
err("key '$key' doesn't exist in config hash");
|
err("key '$key' doesn't exist in config hash");
|
||||||
}
|
}
|
||||||
@@ -201,9 +224,9 @@ sub sysctl {
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Look for either of the WKAs for ipv4only.arpa (192.0.0.170 and .171) in an
|
# 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
|
# IPv6 address at all of the locations RFC 6052 says it can occur, starting at
|
||||||
# present at any of those locations (but no more than once), return the
|
# the longest prefix length. If it's present at any of those locations, return
|
||||||
# inferred translation prefix.
|
# the inferred translation prefix.
|
||||||
#
|
#
|
||||||
sub find_rfc7050_wka {
|
sub find_rfc7050_wka {
|
||||||
my $AAAA = shift;
|
my $AAAA = shift;
|
||||||
@@ -230,7 +253,7 @@ sub find_rfc7050_wka {
|
|||||||
|
|
||||||
my $discovered_pfx_len;
|
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");
|
d2("Looking for Well-Known Addresses at prefix length /$len");
|
||||||
my $maskedip = $ip->intip();
|
my $maskedip = $ip->intip();
|
||||||
my $mask = Net::IP->new($rfc6052table{"$len"}{"mask"}, 6);
|
my $mask = Net::IP->new($rfc6052table{"$len"}{"mask"}, 6);
|
||||||
@@ -251,13 +274,9 @@ sub find_rfc7050_wka {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($maskedip == $wkaint->intip) {
|
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!");
|
d2("Found it!");
|
||||||
$discovered_pfx_len = $len;
|
$discovered_pfx_len = $len;
|
||||||
|
last outer;
|
||||||
} else {
|
} else {
|
||||||
d2("Didn't find it");
|
d2("Didn't find it");
|
||||||
}
|
}
|
||||||
@@ -311,10 +330,9 @@ sub find_rfc7050_wka {
|
|||||||
# up to see if the well-known hostname 'ipv4only.arpa' resolves to an IPv6
|
# 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.
|
# 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)");
|
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;
|
require Net::DNS;
|
||||||
|
|
||||||
my @dns64_servers = split(",", cfg("dns64-servers") || "");
|
my @dns64_servers = split(",", cfg("dns64-servers") || "");
|
||||||
@@ -331,6 +349,16 @@ sub get_plat_prefix {
|
|||||||
$res = Net::DNS::Resolver->new();
|
$res = Net::DNS::Resolver->new();
|
||||||
}
|
}
|
||||||
$res->dnssec(0); # RFC 7050 section 3
|
$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: ", $res->nameservers);
|
||||||
|
|
||||||
my $pkt = $res->query('ipv4only.arpa', 'AAAA');
|
my $pkt = $res->query('ipv4only.arpa', 'AAAA');
|
||||||
if(!$pkt) {
|
if(!$pkt) {
|
||||||
d("No AAAA records was returned for 'ipv4only.arpa'");
|
d("No AAAA records was returned for 'ipv4only.arpa'");
|
||||||
@@ -359,6 +387,9 @@ sub get_plat_prefix {
|
|||||||
}
|
}
|
||||||
if(@prefixes) {
|
if(@prefixes) {
|
||||||
return $prefixes[0];
|
return $prefixes[0];
|
||||||
|
} elsif($CFG{"plat-fallback-prefix"}) {
|
||||||
|
p("No PLAT prefix could be discovered, using fallback");
|
||||||
|
return $CFG{"plat-fallback-prefix"};
|
||||||
} else {
|
} else {
|
||||||
p("No PLAT prefix could be discovered. Your ISP probably doesn't provide",
|
p("No PLAT prefix could be discovered. Your ISP probably doesn't provide",
|
||||||
" NAT64/DNS64 PLAT service. Exiting.");
|
" NAT64/DNS64 PLAT service. Exiting.");
|
||||||
@@ -367,18 +398,76 @@ sub get_plat_prefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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
|
# 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
|
# 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 {
|
sub get_plat_dev {
|
||||||
d("get_plat_dev(): finding which network dev faces the PLAT");
|
d("get_plat_dev(): finding which network dev faces the PLAT");
|
||||||
my $plat_dev;
|
my $plat_dev;
|
||||||
|
my $plat_dev_srcip;
|
||||||
my $plat_prefix = cfg("plat-prefix");
|
my $plat_prefix = cfg("plat-prefix");
|
||||||
if(!$plat_prefix) {
|
if(!$plat_prefix) {
|
||||||
err("get_plat_dev(): No PLAT prefix to work with");
|
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)
|
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");
|
or err("get_plat_dev(): 'ip -6 route get $plat_prefix' failed to execute");
|
||||||
while(<$fd>) {
|
while(<$fd>) {
|
||||||
@@ -386,9 +475,13 @@ sub get_plat_dev {
|
|||||||
d("get_plat_dev(): Found PLAT-facing device: $1");
|
d("get_plat_dev(): Found PLAT-facing device: $1");
|
||||||
$plat_dev = $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");
|
close($fd) or err("get_plat_dev(): 'ip -6 route get $plat_prefix' failed");
|
||||||
return $plat_dev;
|
return ($plat_dev, $plat_dev_srcip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -422,14 +515,25 @@ sub is_modified_eui64 {
|
|||||||
return ($ip & $mask) != $mask;
|
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 /64 address on the PLAT-facing
|
# This function considers any globally scoped IPv6 address on the PLAT-facing
|
||||||
# device, checks to see if it is base on Modified EUI-64, and generates a
|
# device, and derives an CLAT IPv6 address from the best match (longest
|
||||||
# new address for the CLAT by substituting the "0xfffe" bits in the middle
|
# common prefix with PLAT prefix). Addresses based on Modified EUI-64 are
|
||||||
# of the Interface ID with 0xc1a7 ("clat"). This keeps the last 24 bits
|
# preferred, and if found, it generates a new address for the CLAT by
|
||||||
# unchanged, which has the added bonus of not requiring the host to join
|
# substituting the "0xfffe" bits in the middle of the Interface ID with
|
||||||
# another Solicited-Node multicast group.
|
# 0xc1a7 ("clat"). This keeps the last 24 bits unchanged, which has the added
|
||||||
|
# bonus of not requiring the host to join another Solicited-Node multicast
|
||||||
|
# group. If no EUI-64 address is seen, it'll use a random IID instead.
|
||||||
#
|
#
|
||||||
sub get_clat_v6_addr {
|
sub get_clat_v6_addr {
|
||||||
my $plat_dev = cfg("plat-dev");
|
my $plat_dev = cfg("plat-dev");
|
||||||
@@ -445,42 +549,108 @@ sub get_clat_v6_addr {
|
|||||||
err("Failed to convert plat prefix to bigint");
|
err("Failed to convert plat prefix to bigint");
|
||||||
}
|
}
|
||||||
my $ip; # will contain the best candidate ip in bigint format
|
my $ip; # will contain the best candidate ip in bigint format
|
||||||
my $best_score;
|
my $ip_plen; # will contain the prefix length of the best candidate ip
|
||||||
|
my $best_score; # will contain the score of the best candidate seen
|
||||||
|
my $seen_eui64; # set if we've seen an eui-64 based address
|
||||||
|
|
||||||
p("Attempting to derive a CLAT IPv6 address from a EUI-64 address on ",
|
p("Attempting to derive a CLAT IPv6 address from an IPv6 address on ",
|
||||||
"'$plat_dev'");
|
"'$plat_dev'");
|
||||||
open(my $fd, '-|', cfg("cmd-ip"), qw(-6 address list scope global dev),
|
open(my $fd, '-|', cfg("cmd-ip"), qw(-6 address list scope global dev),
|
||||||
$plat_dev)
|
$plat_dev)
|
||||||
or err("'ip -6 address list scope global dev $plat_dev' failed to execute");
|
or err("'ip -6 address list scope global dev $plat_dev' failed to execute");
|
||||||
while(<$fd>) {
|
while(<$fd>) {
|
||||||
if(m| inet6 (\S+)/64 scope global |) {
|
if(m| inet6 (\S+)/(\d{1,3}) scope global |) {
|
||||||
my $candidate = $1;
|
my $candidate = $1;
|
||||||
next unless(is_modified_eui64($candidate));
|
my $plen = $2;
|
||||||
d2("Saw EUI-64 based address: $candidate");
|
d2("Saw a candidate address on '$plat_dev': $candidate/$plen");
|
||||||
my $candidate_int = Net::IP->new($candidate, 6)->intip();
|
my $candidate_int = Net::IP->new($candidate, 6)->intip();
|
||||||
if(!$candidate_int) {
|
if(!$candidate_int) {
|
||||||
err("Failed to convert plat prefix to bigint");
|
err("Failed to convert plat prefix to bigint");
|
||||||
}
|
}
|
||||||
if(!$best_score or $best_score > ($plat_prefix_int ^ $candidate_int)) {
|
|
||||||
d2("$candidate has so far the longest common prefix with plat prefix");
|
if($plen > 120) {
|
||||||
|
# We'll need a subnet with some space if we are to generate a random
|
||||||
|
# IID and don't have too large risk of collisions... /120 seems like
|
||||||
|
# an OK limit
|
||||||
|
d2("Refusing to use random IIDs for prefix lengths > /120");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
# True if the candidate under consideration is EUI-64 based
|
||||||
|
my $is_eui64 = ($plen == 64) && is_modified_eui64($candidate);
|
||||||
|
|
||||||
|
# If this is the first time we're considering an EUI-64 based address,
|
||||||
|
# we unconditionally prefer it (even if it doesn't have the longest
|
||||||
|
# matching prefix), because we consider deriving the CLAT IPv6
|
||||||
|
# address from an EUI-64 based candidate to be safer than generating
|
||||||
|
# a truly random CLAT IPv6 address.
|
||||||
|
if($is_eui64 and !$seen_eui64++) {
|
||||||
|
d2("Preferring $candidate/$plen; it's the first EUI-64 seen");
|
||||||
$best_score = $plat_prefix_int ^ $candidate_int;
|
$best_score = $plat_prefix_int ^ $candidate_int;
|
||||||
$ip = $candidate_int;
|
$ip = $candidate_int;
|
||||||
|
$ip_plen = $plen;
|
||||||
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If we already have found an EUI-64 based address, we can reject this
|
||||||
|
# candidate outright, as it is *not* EUI-64 based.
|
||||||
|
if(!$is_eui64 and $seen_eui64) {
|
||||||
|
d2("Rejecting $candidate/$plen; we have better EUI-64 candidates");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Otherwise, we'll be comparing EUI-64 to EUI-64, or non EUI-64 to
|
||||||
|
# non EUI-64. If so, we prefer the current candidate if it has a better
|
||||||
|
# score than the current best match (or if there is no current best
|
||||||
|
# match).
|
||||||
|
if(!$best_score or $best_score > ($plat_prefix_int ^ $candidate_int)) {
|
||||||
|
d2("Preferring $candidate/$plen; best match so far");
|
||||||
|
$best_score = $plat_prefix_int ^ $candidate_int;
|
||||||
|
$ip = $candidate_int;
|
||||||
|
$ip_plen = $plen;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
d2("Rejecting $candidate/$plen; we've seen better matches");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close($fd)
|
close($fd)
|
||||||
or err("'ip -6 address list scope global dev $plat_dev' failed");
|
or err("'ip -6 address list scope global dev $plat_dev' failed");
|
||||||
|
|
||||||
# First clear the middle 0xfffe bits of the interface ID
|
if(!$ip) {
|
||||||
my $mask = Net::IP->new("ffff:ffff:ffff:ffff:ffff:ff00:00ff:ffff");
|
err("Could not find a global IPv6 address on $plat_dev from which ",
|
||||||
$mask = $mask->intip();
|
"to derive a CLAT IPv6 address (try setting 'clat-v6-addr')");
|
||||||
$ip &= $mask;
|
}
|
||||||
|
|
||||||
# Next set them to the value 0xc1a7 and return
|
if($seen_eui64) {
|
||||||
$mask = Net::IP->new("::c1:a700:0", 6) or err(Net::IP::Error());
|
# If the chosen candidate IP is EUI-64 based, we derive a CLAT IPv6
|
||||||
$mask = $mask->intip();
|
# address by replacing the 0xffe in the middle of the Interface ID with
|
||||||
$ip |= $mask;
|
# 0xc1a7 ("CLAT").
|
||||||
|
|
||||||
|
# First clear the middle 0xfffe bits of the interface ID
|
||||||
|
my $mask = Net::IP->new("ffff:ffff:ffff:ffff:ffff:ff00:00ff:ffff");
|
||||||
|
$mask = $mask->intip();
|
||||||
|
$ip &= $mask;
|
||||||
|
|
||||||
|
# Next set them to the value 0xc1a7
|
||||||
|
$mask = Net::IP->new("::c1:a700:0", 6) or err(Net::IP::Error());
|
||||||
|
$mask = $mask->intip();
|
||||||
|
$ip |= $mask;
|
||||||
|
} else {
|
||||||
|
# If the chosen candidate IP is NOT EUI-64 based, we'll just make up a
|
||||||
|
# random interface ID. There is no guarantee that this will actually
|
||||||
|
# work, but it's the best thing we can try...
|
||||||
|
|
||||||
|
# First zero out the entire Interface ID
|
||||||
|
$ip >>= (128-$ip_plen);
|
||||||
|
$ip <<= (128-$ip_plen);
|
||||||
|
|
||||||
|
my $iid = int(rand(2**(128-$ip_plen)));
|
||||||
|
d2(sprintf("Using random interface ID: %x", $iid));
|
||||||
|
$ip |= $iid;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert back the BigInt to a regular Net::IP object and return
|
||||||
$ip = Net::IP->new(Net::IP::ip_bintoip(Net::IP::ip_inttobin($ip, 6), 6));
|
$ip = Net::IP->new(Net::IP::ip_bintoip(Net::IP::ip_inttobin($ip, 6), 6));
|
||||||
return $ip->short() if $ip;
|
return $ip->short() if $ip;
|
||||||
|
|
||||||
@@ -493,19 +663,24 @@ sub get_clat_v6_addr {
|
|||||||
# below gets set as we go along, so that the cleanup subroutine can restore
|
# below gets set as we go along, so that the cleanup subroutine can restore
|
||||||
# stuff if necessary.
|
# 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_delete_taygaconf; # true if having made a temp confile
|
||||||
my $cleanup_zero_forwarding_sysctl; # zero forwarding sysctl if set
|
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_accept_ra_sysctls; # accept_ra sysctls to be reset to '1'
|
||||||
my $cleanup_zero_proxynd_sysctl; # zero proxy_ndp sysctl if set
|
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_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 {
|
sub cleanup_and_exit {
|
||||||
my $exitcode = shift;
|
my $exitcode = shift;
|
||||||
|
|
||||||
if(defined($cleanup_remove_clat_dev)) {
|
if(defined($cleanup_remove_tayga_clat_dev)) {
|
||||||
d("Cleanup: Removing CLAT device");
|
d("Cleanup: Removing TAYGA CLAT device");
|
||||||
cmd(\&w, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--rmtun");
|
cmd(\&w, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--rmtun");
|
||||||
}
|
}
|
||||||
if(defined($cleanup_delete_taygaconf)) {
|
if(defined($cleanup_delete_taygaconf)) {
|
||||||
@@ -513,6 +688,14 @@ sub cleanup_and_exit {
|
|||||||
unlink(cfg("tayga-conffile"))
|
unlink(cfg("tayga-conffile"))
|
||||||
or w("unlink('", cfg("tayga-conffile"), "') failed");
|
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)) {
|
if(defined($cleanup_zero_forwarding_sysctl)) {
|
||||||
d("Cleanup: Resetting forwarding sysctl to 0");
|
d("Cleanup: Resetting forwarding sysctl to 0");
|
||||||
sysctl("net/ipv6/conf/all/forwarding", 0);
|
sysctl("net/ipv6/conf/all/forwarding", 0);
|
||||||
@@ -526,18 +709,35 @@ sub cleanup_and_exit {
|
|||||||
sysctl("net/ipv6/conf/" . cfg("plat-dev") . "/proxy_ndp", 0);
|
sysctl("net/ipv6/conf/" . cfg("plat-dev") . "/proxy_ndp", 0);
|
||||||
}
|
}
|
||||||
if(defined($cleanup_remove_proxynd_entry)) {
|
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"));
|
cfg("plat-dev"));
|
||||||
cmd(\&w, cfg("cmd-ip"), qw(-6 neighbour delete proxy), cfg("clat-v6-addr"),
|
cmd(\&w, cfg("cmd-ip"), qw(-6 neighbour delete proxy), cfg("clat-v6-addr"),
|
||||||
"dev", cfg("plat-dev"));
|
"dev", cfg("plat-dev"));
|
||||||
}
|
}
|
||||||
if(defined($cleanup_remove_ip6tables_rules)) {
|
if(defined($cleanup_remove_nftable)) {
|
||||||
d("Cleanup: Removing ip6tables rules allowing traffic between the CLAT ",
|
d("Cleanup: Removing clatd netfilter table");
|
||||||
"and PLAT devices");
|
cmd(\&w, cfg("cmd-nft"), "delete table ip6 clatd");
|
||||||
cmd(\&w, cfg("cmd-ip6tables"), qw(-D FORWARD -i), cfg("clat-dev"),
|
}
|
||||||
"-o", cfg("plat-dev"), qw(-j ACCEPT));
|
for my $rt (@cleanup_restore_v4_defaultroutes) {
|
||||||
cmd(\&w, cfg("cmd-ip6tables"), qw(-D FORWARD -i), cfg("plat-dev"),
|
d("Cleanup: Restoring temporarily replaced IPv4 default route");
|
||||||
"-o", cfg("clat-dev"), qw(-j ACCEPT));
|
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);
|
exit($exitcode);
|
||||||
@@ -573,8 +773,10 @@ for (my $i = 0; $i < @ARGV;) {
|
|||||||
splice(@ARGV, $i, 2);
|
splice(@ARGV, $i, 2);
|
||||||
next;
|
next;
|
||||||
} elsif($ARGV[$i] =~ /^(-h|--help)$/) {
|
} elsif($ARGV[$i] =~ /^(-h|--help)$/) {
|
||||||
print "clatd v$VERSION - a 464XLAT (RFC 6877) CLAT implementation for ",
|
print <<"EOF";
|
||||||
"Linux\n";
|
clatd v$VERSION - a 464XLAT (RFC 6877) CLAT and SIIT-DC Edge Relay
|
||||||
|
(RFC 7756) implementation for Linux
|
||||||
|
EOF
|
||||||
print "\n";
|
print "\n";
|
||||||
print " Usage: clatd [-q] [-d [-d]] [-c config-file] ",
|
print " Usage: clatd [-q] [-d [-d]] [-c config-file] ",
|
||||||
"[conf-key=val ...]\n";
|
"[conf-key=val ...]\n";
|
||||||
@@ -625,7 +827,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
|
# Step 1: Fill in any essential blanks in the configuration by auto-detecting
|
||||||
# any missing values.
|
# 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"}) {
|
if(!$CFG{"plat-prefix"}) {
|
||||||
w("No PLAT prefix was discovered or specified; 464XLAT cannot work.");
|
w("No PLAT prefix was discovered or specified; 464XLAT cannot work.");
|
||||||
exit 0;
|
exit 0;
|
||||||
@@ -646,20 +850,45 @@ if(!$CFG{"plat-prefix"}) {
|
|||||||
}
|
}
|
||||||
p("Using PLAT (NAT64) prefix: $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"});
|
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 IPv4 address: ", $CFG{"clat-v4-addr"});
|
||||||
p("Using CLAT IPv6 address: ", $CFG{"clat-v6-addr"});
|
p("Using CLAT IPv6 address: ", $CFG{"clat-v6-addr"});
|
||||||
if(!defined($CFG{"ip6tables-enable"})) {
|
if(!$CFG{"v4-defaultroute-advmss"} and cfgint("v4-defaultroute-mtu")) {
|
||||||
$CFG{"ip6tables-enable"} = -e "/sys/module/ip6table_filter" ? 1 : 0;
|
$CFG{"v4-defaultroute-advmss"} = $CFG{"v4-defaultroute-mtu"} - 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Step 1: Detect if there is an IPv4 default route on the system from before.
|
# 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 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");
|
my $delay = cfgint("v4-conncheck-delay");
|
||||||
p("Checking if this system already has IPv4 connectivity ",
|
p("Checking if this system already has IPv4 connectivity ",
|
||||||
$delay ? "in $delay sec(s)" : "now");
|
$delay ? "in $delay sec(s)" : "now");
|
||||||
@@ -677,35 +906,40 @@ if(cfgbool("v4-conncheck-enable")) {
|
|||||||
d("Skipping IPv4 connectivity check at user request");
|
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,
|
# Write out the TAYGA config file, either to the user-specified location,
|
||||||
# or to a temporary file (which we'll delete later)
|
# or to a temporary file (which we'll delete later)
|
||||||
#
|
#
|
||||||
my $tayga_conffile = cfg("tayga-conffile");
|
unless($use_nat46) {
|
||||||
my $tayga_conffile_fh;
|
my $tayga_conffile = cfg("tayga-conffile");
|
||||||
if(!$tayga_conffile) {
|
my $tayga_conffile_fh;
|
||||||
require File::Temp;
|
if(!$tayga_conffile) {
|
||||||
($tayga_conffile_fh, $tayga_conffile) = File::Temp::tempfile();
|
require File::Temp;
|
||||||
d2("Using temporary conffile for TAYGA: $tayga_conffile");
|
($tayga_conffile_fh, $tayga_conffile) = File::Temp::tempfile();
|
||||||
$CFG{"tayga-conffile"} = $tayga_conffile;
|
d2("Using temporary conffile for TAYGA: $tayga_conffile");
|
||||||
$cleanup_delete_taygaconf = 1;
|
$CFG{"tayga-conffile"} = $tayga_conffile;
|
||||||
} else {
|
$cleanup_delete_taygaconf = 1;
|
||||||
open($tayga_conffile_fh, ">$tayga_conffile") or
|
} else {
|
||||||
err("Could not open TAYGA config file '$tayga_conffile' for writing");
|
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
|
# Enable IPv6 forwarding if necessary
|
||||||
#
|
#
|
||||||
@@ -731,16 +965,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")) {
|
if(can_run(cfg("cmd-ufw"))) {
|
||||||
p("Adding ip6tables rules allowing traffic between the CLAT ",
|
open(my $fd, '-|', cfg("cmd-ufw"), "status")
|
||||||
"and PLAT devices");
|
or err("'ufw status' failed to execute");
|
||||||
cmd(\&w, cfg("cmd-ip6tables"), qw(-I FORWARD -i), cfg("clat-dev"),
|
while(<$fd>) {
|
||||||
"-o", cfg("plat-dev"), qw(-j ACCEPT));
|
if(/^Status: active$/) {
|
||||||
cmd(\&w, cfg("cmd-ip6tables"), qw(-I FORWARD -i), cfg("plat-dev"),
|
p("UFW local firewall framework active, allowing CLAT-PLAT traffic");
|
||||||
"-o", cfg("clat-dev"), qw(-j ACCEPT));
|
cmd(\&err, cfg("cmd-ufw"), qw(route allow in on), cfg("clat-dev"),
|
||||||
$cleanup_remove_ip6tables_rules = 1;
|
"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");
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -761,51 +1006,155 @@ if(cfgbool("proxynd-enable")) {
|
|||||||
$cleanup_remove_proxynd_entry = 1;
|
$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
|
# 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
|
# route to the corresponding IPv6 address, and possibly an IPv4 default route
|
||||||
#
|
#
|
||||||
p("Creating and configuring up CLAT device '", cfg("clat-dev"), "'");
|
p("Creating and configuring up CLAT device '", cfg("clat-dev"), "'");
|
||||||
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--mktun",
|
if($use_nat46) {
|
||||||
cfgint("debug") ? "-d" : "");
|
my $nat46_control_fh;
|
||||||
$cleanup_remove_clat_dev = 1;
|
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(link set up dev), cfg("clat-dev"));
|
||||||
cmd(\&err, cfg("cmd-ip"), qw(-4 address add), cfg("clat-v4-addr"),
|
cmd(\&err, cfg("cmd-ip"), qw(-4 address add), cfg("clat-v4-addr"),
|
||||||
"dev", cfg("clat-dev"));
|
"dev", cfg("clat-dev"));
|
||||||
cmd(\&err, cfg("cmd-ip"), qw(-6 route add), cfg("clat-v6-addr"),
|
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 0; }\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
|
||||||
|
" 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")) {
|
if(cfgbool("v4-defaultroute-enable")) {
|
||||||
my @cmdline = (qw(-4 route add default dev), cfg("clat-dev"));
|
my @cmdline = (qw(-4 route add default dev), cfg("clat-dev"));
|
||||||
if(cfgint("v4-defaultroute-metric")) {
|
if(cfgint("v4-defaultroute-metric")) {
|
||||||
push(@cmdline, ("metric", cfgint("v4-defaultroute-metric")))
|
push(@cmdline, ("metric", cfgint("v4-defaultroute-metric")))
|
||||||
}
|
}
|
||||||
if(cfgint("v4-defaultroute-mtu")) {
|
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");
|
p("Adding IPv4 default route via the CLAT");
|
||||||
cmd(\&err, cfg("cmd-ip"), @cmdline);
|
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.
|
# 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
|
# Nothing more to do here, we just set up a cleanup handler and sleep forever.
|
||||||
# service, just TAYGA (so that we can get around to cleaning up after
|
$SIG{'INT'} = \&cleanup_handler;
|
||||||
# ourselves)
|
$SIG{'TERM'} = \&cleanup_handler;
|
||||||
$SIG{'INT'} = 'IGNORE';
|
sleep();
|
||||||
$SIG{'TERM'} = 'IGNORE';
|
} 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",
|
# We don't want systemd etc. to actually kill this script when stopping the
|
||||||
cfgint("debug") ? "-d" : "");
|
# service, just TAYGA (so that we can get around to cleaning up after
|
||||||
p("TAYGA terminated, cleaning up and exiting");
|
# ourselves)
|
||||||
|
$SIG{'INT'} = 'IGNORE';
|
||||||
|
$SIG{'TERM'} = 'IGNORE';
|
||||||
|
|
||||||
$SIG{'INT'} = 'DEFAULT';
|
cmd(\&err, cfg("cmd-tayga"), "--config", cfg("tayga-conffile"), "--nodetach",
|
||||||
$SIG{'TERM'} = 'DEFAULT';
|
cfgint("debug") ? "-d" : "");
|
||||||
|
p("TAYGA terminated, cleaning up and exiting");
|
||||||
|
|
||||||
#
|
$SIG{'INT'} = 'DEFAULT';
|
||||||
# TAYGA exited, probably because we're shutting down. Cleanup and exit.
|
$SIG{'TERM'} = 'DEFAULT';
|
||||||
#
|
|
||||||
cleanup_and_exit(0);
|
#
|
||||||
|
# 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
49
scripts/clatd.ifupdown
Executable 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
|
||||||
@@ -7,18 +7,25 @@
|
|||||||
# Written by Tore Anderson <tore@fud.no>
|
# 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
|
# We simply restart clatd in all situations, as no matter if an interface
|
||||||
# goes up or down, it may mean that the PLAT devices changes, it may mean
|
# 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
|
# native IPv4 appearing or disappearing, or it may mean that DNS64 became
|
||||||
# available or unavailable...it's far easier to simply restart always and
|
# available or unavailable...it's far easier to simply restart always and
|
||||||
# start from scratch than to figure out if a restart is truly necessary
|
# start from scratch than to figure out if a restart is truly necessary
|
||||||
|
|
||||||
# systemd-based distros
|
# systemd-based distros
|
||||||
if test -x /usr/bin/systemctl; then
|
if which systemctl &> /dev/null; then
|
||||||
/usr/bin/systemctl restart clatd.service
|
systemctl --no-block restart clatd.service
|
||||||
fi
|
|
||||||
|
|
||||||
# upstart-based distros
|
|
||||||
if test -x /sbin/initctl; then
|
|
||||||
/sbin/initctl restart clatd
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -11,7 +11,10 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=464XLAT CLAT daemon
|
Description=464XLAT CLAT daemon
|
||||||
Documentation=man:clatd(8)
|
Documentation=man:clatd(8)
|
||||||
|
Wants=network-online.target
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
|
After=nss-lookup.target
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@@ -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
28
scripts/clatd@.systemd
Normal 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
|
||||||
Reference in New Issue
Block a user