Use routing policy and ctmark for routing to CLAT
This moves the route for IPv6 traffic from the PLAT to the CLAT into a separate routing table, and uses the Linux kernel's routing policy framework to redirect traffic into this routing table. This makes it possible to set `clat-v6-addr` to an address also used by the main host OS, removing the requirement for having a dedicated secondary address assigned to the CLAT. Additionally, support using nftables to set a connection tracking mark on outbound connections from the CLAT, and ensuring only return traffic matching that mark is returned back to the CLAT. This makes it possible for the CLAT to share an IPv6 address with the main host OS without breaking connectivity to DNS64-synthesised IPv6 addresses. The trade-off of using a connection tracking mark is that the CLAT can not receive unsolicited traffic from the IPv4 Internet via the PLAT. However in the common case, where the PLAT is Stateful NAT64, that is the case no matter what. Closes #25.
This commit is contained in:
4
Makefile
4
Makefile
@@ -20,8 +20,8 @@ install:
|
||||
|
||||
installdeps:
|
||||
# .deb/apt-get based distros
|
||||
if test -x "$(APT_GET)"; then $(APT_GET) -y install perl-base perl-modules libnet-ip-perl libnet-dns-perl libio-socket-ip-perl iproute2 tayga; fi
|
||||
if test -x "$(APT_GET)"; then $(APT_GET) -y install perl-base perl-modules libnet-ip-perl libnet-dns-perl libio-socket-ip-perl iproute2 nftables tayga; fi
|
||||
# .rpm/DNF/YUM-based distros
|
||||
if test -x "$(DNF_OR_YUM)"; then $(DNF_OR_YUM) -y install perl perl-Net-IP perl-Net-DNS perl-IO-Socket-IP perl-File-Temp iproute; fi
|
||||
if test -x "$(DNF_OR_YUM)"; then $(DNF_OR_YUM) -y install perl perl-Net-IP perl-Net-DNS perl-IO-Socket-IP perl-File-Temp iproute nftables; fi
|
||||
# If necessary, try to install the TAYGA .rpm using dnf/yum. It is unfortunately not available in all .rpm based distros (in particular CentOS/RHEL).
|
||||
if test -x "$(DNF_OR_YUM)" && test ! -x "$(TAYGA)"; then $(DNF_OR_YUM) -y install tayga || echo "ERROR: Failed to install TAYGA using dnf/yum, the package is probably not included in your distro. Try enabling the EPEL repo <URL: https://fedoraproject.org/wiki/EPEL> and try again, or install TAYGA <URL: http://www.litech.org/tayga> directly from source."; exit 1; fi
|
||||
|
||||
37
README.pod
37
README.pod
@@ -218,6 +218,17 @@ 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: 0)
|
||||
|
||||
If set to a non-zero integer, nftables will be used to mark outgoing
|
||||
connections through the CLAT with this connection tracking mark, and the Linux
|
||||
kernel routing policy is set up so that only response traffic from the PLAT
|
||||
that matches this connection tracking mark is routed back to the CLAT.
|
||||
|
||||
This means that connections initiated from the external IPv4 network via the
|
||||
PLAT will not reach the CLAT, therefore the use of this feature is incompatible
|
||||
with using B<clatd> as a SIIT-DC Edge Relay (I<RFC 7756>).
|
||||
|
||||
=item B<dns64-servers=srv1,[srv2,..]> (default: use system resolver)
|
||||
|
||||
Comma-separated list of DNS64 servers to use when discovering the PLAT prefix
|
||||
@@ -232,6 +243,11 @@ encountered will be used.
|
||||
Path to the B<ip> binary from the iproute2 package available at
|
||||
L<https://www.kernel.org/pub/linux/utils/net/iproute2>. Required.
|
||||
|
||||
=item B<cmd-nft=path> (default: assume in $PATH)
|
||||
|
||||
Path to the B<nft> binary from the nftables package available at
|
||||
L<https://nftables.org/projects/nftables/>. Required if I<ctmark> is set.
|
||||
|
||||
=item B<cmd-tayga=path> (default: assume in $PATH)
|
||||
|
||||
Path to the B<tayga> binary from the TAYGA package available at
|
||||
@@ -252,7 +268,8 @@ B<clatd> is shutting down.
|
||||
|
||||
Which network device is facing the PLAT (NAT64). By default, this is
|
||||
auto-detected by performing a route table lookup towards the PLAT prefix. This
|
||||
setting is used when generating the CLAT IPv6 address and Proxy-ND entries.
|
||||
setting is used when generating the CLAT IPv6 address, adding Proxy-ND entries,
|
||||
and nftables rules.
|
||||
|
||||
=item B<plat-prefix> (default: auto-detect)
|
||||
|
||||
@@ -277,6 +294,13 @@ case, either.
|
||||
|
||||
Any entries added wil be removed when B<clatd> is shutting down.
|
||||
|
||||
=item B<route-table> (default: I<0xc1a7>)
|
||||
|
||||
The Linux kernel routing table used to hold the route that directs IPv6 packets
|
||||
from the PLAT to the CLAT. B<clatd> will add a custom routing policy entry
|
||||
(using B<ip -6 rule add>) so that this routing table is used instead of the
|
||||
default one.
|
||||
|
||||
=item B<tayga-conffile> (default: use a temporary file)
|
||||
|
||||
Where to write the TAYGA configuration file. By default, a temporary file will
|
||||
@@ -399,6 +423,15 @@ 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.
|
||||
|
||||
If B<clat-v6-addr> is set to an address assigned to a local interface and
|
||||
B<ctmark> is not set, the host OS will not be able to communicate
|
||||
bi-directionally with IPv4 destinations directly through the PLAT (e.g.,
|
||||
I<ping6 64:ff9b::192.0.2.1>). This is because the response traffic will be
|
||||
routed back to the CLAT, and ultimately return to the Linux kernel as an IPv4
|
||||
packet, which does not match the outgoing IPv6 socket. Such direct
|
||||
communication is normal when using DNS64 synthesis for all queries (as opposed
|
||||
to just I<ipv4only.arpa>).
|
||||
|
||||
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.
|
||||
|
||||
@@ -434,7 +467,7 @@ SOFTWARE.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
ip(8), tayga(8), tayga.conf(5)
|
||||
ip(8), nft(8), tayga(8), tayga.conf(5)
|
||||
|
||||
RFC 6052, RFC 6145, RFC 6146, RFC 6877, RFC 7050, RFC 7335 RFC 7755, RFC 7756,
|
||||
RFC 7757
|
||||
|
||||
70
clatd
70
clatd
@@ -41,12 +41,15 @@ $CFG{"clat-v4-addr"} = "192.0.0.1"; # from RFC 7335
|
||||
$CFG{"clat-v6-addr"} = undef; # derive from existing SLAAC addr
|
||||
$CFG{"dns64-servers"} = undef; # use system resolver by default
|
||||
$CFG{"cmd-ip"} = "ip"; # assume in $PATH
|
||||
$CFG{"cmd-nft"} = "nft"; # assume in $PATH
|
||||
$CFG{"cmd-tayga"} = "tayga"; # assume in $PATH
|
||||
$CFG{"ctmark"} = 0; # match ctmark for routing pkts to CLAT
|
||||
$CFG{"forwarding-enable"} = 1; # enable ipv6 forwarding?
|
||||
$CFG{"plat-dev"} = undef; # PLAT-facing device, default detect
|
||||
$CFG{"plat-prefix"} = undef; # detect using DNS64 by default
|
||||
$CFG{"plat-fallback-prefix"} = undef; # fallback prefix if no prefix is found
|
||||
$CFG{"proxynd-enable"} = 1; # add proxy-nd entry for clat?
|
||||
$CFG{"route-table"} = 0xc1a7; # add route to CLAT in this table
|
||||
$CFG{"tayga-conffile"} = undef; # make a temporary one by default
|
||||
$CFG{"tayga-v4-addr"} = "192.0.0.2"; # from RFC 7335
|
||||
$CFG{"v4-conncheck-enable"} = 1; # exit if there's already a defroute
|
||||
@@ -385,8 +388,8 @@ sub get_plat_prefix {
|
||||
|
||||
#
|
||||
# This function figures out which network interface on the system faces the
|
||||
# PLAT/NAT64. We need this when generating an IPv6 address for the CLAT and
|
||||
# when installing Proxy-ND entries.
|
||||
# PLAT/NAT64. We need this when generating an IPv6 address for the CLAT, when
|
||||
# installing Proxy-ND entries, and when setting up nft rules.
|
||||
#
|
||||
sub get_plat_dev {
|
||||
d("get_plat_dev(): finding which network dev faces the PLAT");
|
||||
@@ -594,6 +597,9 @@ my $cleanup_zero_forwarding_sysctl; # zero forwarding sysctl if set
|
||||
my @cleanup_accept_ra_sysctls; # accept_ra sysctls to be reset to '1'
|
||||
my $cleanup_zero_proxynd_sysctl; # zero proxy_ndp sysctl if set
|
||||
my $cleanup_remove_proxynd_entry, # true if having added proxynd entry
|
||||
my $cleanup_remove_nftable; # true if having added an nftable
|
||||
my $cleanup_remove_clat_iprule; # true if having added clat iprule
|
||||
my $cleanup_restore_local_iprule_prio; # true if having reordered local iprule
|
||||
my @cleanup_restore_v4_defaultroutes; # temporarily replaced defaultroutes
|
||||
|
||||
sub cleanup_and_exit {
|
||||
@@ -634,10 +640,23 @@ sub cleanup_and_exit {
|
||||
cmd(\&w, cfg("cmd-ip"), qw(-6 neighbour delete proxy), cfg("clat-v6-addr"),
|
||||
"dev", cfg("plat-dev"));
|
||||
}
|
||||
if(defined($cleanup_remove_nftable)) {
|
||||
d("Cleanup: Removing clatd netfilter table");
|
||||
cmd(\&w, cfg("cmd-nft"), "delete table ip6 clatd");
|
||||
}
|
||||
for my $rt (@cleanup_restore_v4_defaultroutes) {
|
||||
d("Cleanup: Restoring temporarily replaced IPv4 default route");
|
||||
cmd(\&w, cfg("cmd-ip"), qw(-4 route add), @{$rt});
|
||||
}
|
||||
if(defined($cleanup_restore_local_iprule_prio)) {
|
||||
d("Cleanup: Restoring local ip rule priority to 0");
|
||||
cmd(\&w, cfg("cmd-ip"), qw(-6 rule add prio 0 table local));
|
||||
cmd(\&w, cfg("cmd-ip"), qw(-6 rule del prio 1 table local));
|
||||
}
|
||||
if(defined($cleanup_remove_clat_iprule)) {
|
||||
d("Cleanup: Removing ip rule for redirecting inbound traffic to CLAT");
|
||||
cmd(\&w, cfg("cmd-ip"), qw(-6 rule del prio 0 table), cfg("route-table"));
|
||||
}
|
||||
|
||||
exit($exitcode);
|
||||
}
|
||||
@@ -854,6 +873,21 @@ if(cfgbool("proxynd-enable")) {
|
||||
$cleanup_remove_proxynd_entry = 1;
|
||||
}
|
||||
|
||||
# Move the default ip rule for local traffic to a higher priority, so we can
|
||||
# override it. This is necessary if re-using the primary IPv6 address on the
|
||||
# PLAT-facing device for the CLAT function.
|
||||
open(my $fd, '-|', cfg("cmd-ip"), qw(-6 rule show prio 0 table local))
|
||||
or err("'ip -6 rule show prio 0 table local' failed to execute");
|
||||
while(<$fd>) {
|
||||
d("Increasing prio of default local ip rule to 1");
|
||||
cmd(\&err, cfg("cmd-ip"), qw(-6 rule add prio 1 table local));
|
||||
cmd(\&err, cfg("cmd-ip"), qw(-6 rule del prio 0 table local));
|
||||
$cleanup_restore_local_iprule_prio = 1;
|
||||
last;
|
||||
}
|
||||
close($fd) or err("'ip -6 rule show prio 0 table local' failed");
|
||||
|
||||
|
||||
#
|
||||
# Create the CLAT tun interface, add the IPv4 address to it as well as the
|
||||
# route to the corresponding IPv6 address, and possibly an IPv4 default route
|
||||
@@ -875,7 +909,37 @@ cmd(\&err, cfg("cmd-ip"), qw(link set up dev), cfg("clat-dev"));
|
||||
cmd(\&err, cfg("cmd-ip"), qw(-4 address add), cfg("clat-v4-addr"),
|
||||
"dev", cfg("clat-dev"));
|
||||
cmd(\&err, cfg("cmd-ip"), qw(-6 route add), cfg("clat-v6-addr"),
|
||||
"dev", cfg("clat-dev"));
|
||||
"dev", cfg("clat-dev"), "table", cfgint("route-table"));
|
||||
cmd(\&err, cfg("cmd-ip"), qw(-6 rule add prio 0 from),
|
||||
cfg("plat-prefix"), "to", cfg("clat-v6-addr"),
|
||||
cfgint("ctmark") ? ("fwmark", cfgint("ctmark")) : (),
|
||||
"table", cfg("route-table"));
|
||||
$cleanup_remove_clat_iprule = 1;
|
||||
|
||||
if(cfgint("ctmark")) {
|
||||
p("Adding clatd nftable, using conntrack mark ", cfgint("ctmark"));
|
||||
open(my $fd, '|-', cfg("cmd-nft"), "-f-")
|
||||
or err("'nft -f-' failed to execute");
|
||||
print $fd "add table ip6 clatd\n";
|
||||
print $fd "add chain ip6 clatd prerouting ",
|
||||
"{ type filter hook prerouting priority 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");
|
||||
|
||||
Reference in New Issue
Block a user