Add nat46 in-kernel translator support

This allows clatd to use [nat46](https://github.com/ayourtch/nat46)
kernel translator instead of TAYGA. It uses automatic detection - if
`nat46` module is loaded, it will get used, otherwise the application
falls back to using TAYGA.

Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
This commit is contained in:
Ondřej Caletka
2023-11-05 12:21:51 +01:00
parent a93f5ff491
commit 6d2ad96c2f
2 changed files with 105 additions and 52 deletions

View File

@@ -20,9 +20,11 @@ SIIT-DC Edge Relay, you will probably want to manually configure the settings
I<clat-v4-addr>, I<clat-v6-addr>, and I<plat-prefix> to mirror the SIIT-DC I<clat-v4-addr>, I<clat-v6-addr>, and I<plat-prefix> to mirror the SIIT-DC
Border Relay's configuration. 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

149
clatd
View File

@@ -26,7 +26,7 @@
use strict; use strict;
use Net::IP; use Net::IP;
my $VERSION = "1.6"; my $VERSION = "1.6nat46";
# #
# Populate the global config hash with the default values # Populate the global config hash with the default values
@@ -438,6 +438,15 @@ 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 IPv6 address on the PLAT-facing # This function considers any globally scoped IPv6 address on the PLAT-facing
@@ -577,7 +586,8 @@ 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'
@@ -589,8 +599,8 @@ 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)) {
@@ -598,6 +608,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);
@@ -772,35 +790,40 @@ if(cfgbool("v4-conncheck-enable") and !cfgbool("v4-defaultroute-replace")) {
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
# #
@@ -861,9 +884,18 @@ if(cfgbool("proxynd-enable")) {
# 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"));
@@ -908,30 +940,49 @@ if(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. Run the down script, then $SIG{'TERM'} = 'DEFAULT';
# cleanup and exit.
# #
if(cfg("script-down")) { # TAYGA exited, probably because we're shutting down. Run the down script, then
d("Running custom shutdown script: ", cfg("script-down")); # cleanup and exit.
cmd(\&err, cfg("script-down")); #
if(cfg("script-down")) {
d("Running custom shutdown script: ", cfg("script-down"));
cmd(\&err, cfg("script-down"));
}
cleanup_and_exit(0);
} }
cleanup_and_exit(0);