use strict;
use warnings;
-use Data::Dumper;
+
use PVE::Tools qw($IPV6RE);
use PVE::LXC;
use PVE::Network;
die "unable to read version info\n" if !defined($version);
- die "unable to parse version info\n"
+ # translate testing version names
+ $version = 9.1 if $version eq 'stretch/sid';
+ $version = 10 if $version eq 'buster/sid';
+
+ die "unable to parse version info '$version'\n"
if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/;
$version = $1;
die "unsupported debian version '$version'\n"
- if !($version >= 4 && $version < 9);
+ if !($version >= 4 && $version <= 10);
my $self = { conf => $conf, rootdir => $rootdir, version => $version };
return bless $self, $class;
}
+# Debian doesn't support the /dev/lxc/ subdirectory.
+sub devttydir {
+ return '';
+}
+
sub setup_init {
my ($self, $conf) = @_;
+ my $systemd = $self->ct_readlink('/sbin/init');
+ if (defined($systemd) && $systemd =~ m@/systemd$@) {
+ $self->setup_container_getty_service($conf);
+ }
+
my $filename = "/etc/inittab";
return if !$self->ct_file_exists($filename);
- my $ttycount = PVE::LXC::get_tty_count($conf);
+ my $ttycount = PVE::LXC::Config->get_tty_count($conf);
my $inittab = $self->ct_file_get_contents($filename);
my @lines = grep {
# remove getty lines
- !/^\s*\d+:\d+:[^:]*:.*getty/ &&
+ !/^\s*\d+:\d*:[^:]*:.*getty/ &&
# remove power lines
!/^\s*p[fno0]:/
} split(/\n/, $inittab);
sub remove_gateway_scripts {
my ($attr) = @_;
my $length = scalar(@$attr);
- for (my $i = 0; $i < $length; ++$i) {
- my $a = $attr->[$i];
- if ($a =~ m@^\s*post-up\s+.*route.*add.*default.*(?:gw|via)\s+(\S+)@) {
- my $gw = $1;
- if ($i > 0 && $attr->[$i-1] =~ m@^\s*post-up\s+.*route.*add.*\Q$1\E@) {
- --$i;
- splice @$attr, $i, 2;
- $length -= 2;
- } else {
- splice @$attr, $i, 1;
- $length -= 1;
- }
- --$i;
- next;
+
+ my $found_section = 0;
+ my $keep = 1;
+ @$attr = grep {
+ if ($_ eq '# --- BEGIN PVE ---') {
+ $found_section = 1;
+ $keep = 0;
+ 0; # remove this line
+ } elsif ($_ eq '# --- END PVE ---') {
+ $found_section = 1;
+ $keep = 1;
+ 0; # remove this line
+ } else {
+ $keep;
}
- if ($a =~ m@^\s*pre-down\s+.*route.*del.*default.*(?:gw|via)\s+(\S+)@) {
- my $gw = $1;
- if ($attr->[$i+1] =~ m@^\s*pre-down\s+.*route.*del.*\Q$1\E@) {
- splice @$attr, $i, 2;
- $length -= 2;
- } else {
- splice @$attr, $i, 1;
- $length -= 1;
- }
+ } @$attr;
+
+ return if $found_section;
+ # XXX: To deal with existing setups we perform two types of removal for
+ # now. Newly started containers have their routing sections marked with
+ # begin/end comments. For older containers we perform a strict matching on
+ # the routing rules we added. We can probably remove this part at some point
+ # when it is unlikely that old debian setups are still around.
+
+ for (my $i = 0; $i < $length-3; ++$i) {
+ next if $attr->[$i+0] !~ m@^\s*post-up\s+ip\s+route\s+add\s+(\S+)\s+dev\s+(\S+)$@;
+ my ($ip, $dev) = ($1, $2);
+ if ($attr->[$i+1] =~ m@^\s*post-up\s+ip\s+route\s+add\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2) &&
+ $attr->[$i+2] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2) &&
+ $attr->[$i+3] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+(\S+)\s+dev\s+(\S+)$@ &&
+ ($ip eq $1 && $dev eq $2))
+ {
+ splice @$attr, $i, 4;
+ $length -= 4;
--$i;
- next;
}
}
}
sub make_gateway_scripts {
my ($ifname, $gw) = @_;
return <<"SCRIPTS";
+# --- BEGIN PVE ---
\tpost-up ip route add $gw dev $ifname
-\tpost-up ip route add default via $gw
-\tpre-down ip route del default via $gw
+\tpost-up ip route add default via $gw dev $ifname
+\tpre-down ip route del default via $gw dev $ifname
\tpre-down ip route del $gw dev $ifname
+# --- END PVE ---
SCRIPTS
}
foreach my $k (keys %$conf) {
next if $k !~ m/^net(\d+)$/;
my $ind = $1;
- my $d = PVE::LXC::parse_lxc_network($conf->{$k});
+ my $d = PVE::LXC::Config->parse_lxc_network($conf->{$k});
if ($d->{name}) {
my $net = {};
my $cidr;
}
if (defined($d->{'gw6'})) {
$net->{gateway6} = $d->{'gw6'};
- if (defined($cidr) && !PVE::Network::is_ip_in_cidr($d->{gw6}, $cidr, 6)) {
+ if (defined($cidr) && !PVE::Network::is_ip_in_cidr($d->{gw6}, $cidr, 6) &&
+ !PVE::Network::is_ip_in_cidr($d->{gw6}, 'fe80::/10', 6)) {
# gateway is not reachable, need an extra route
$net->{needsroute6} = 1;
}
if ($section->{type} eq 'ipv4') {
$done_v4_hash->{$ifname} = 1;
- if ($net->{address} =~ /^(dhcp|manual)$/) {
- $interfaces .= "iface $ifname inet $1\n";
+ if (!defined($net->{address})) {
+ # no address => no iface line
+ } elsif ($net->{address} =~ /^(dhcp|manual)$/) {
+ $interfaces .= "iface $ifname inet $1\n\n";
} else {
$interfaces .= "iface $ifname inet static\n";
$interfaces .= "\taddress $net->{address}\n" if defined($net->{address});
$interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask});
+
+ remove_gateway_scripts($section->{attr});
if (defined(my $gw = $net->{gateway})) {
- remove_gateway_scripts($section->{attr});
if ($net->{needsroute}) {
$interfaces .= make_gateway_scripts($ifname, $gw);
} else {
foreach my $attr (@{$section->{attr}}) {
$interfaces .= "\t$attr\n";
}
+ $interfaces .= "\n";
}
-
- $interfaces .= "\n";
-
} elsif ($section->{type} eq 'ipv6') {
$done_v6_hash->{$ifname} = 1;
- if ($net->{address6} =~ /^(auto|dhcp|manual)$/) {
- $interfaces .= "iface $ifname inet6 $1\n";
+ if (!defined($net->{address6})) {
+ # no address => no iface line
+ } elsif ($net->{address6} =~ /^(auto|dhcp|manual)$/) {
+ $interfaces .= "iface $ifname inet6 $1\n\n";
} else {
$interfaces .= "iface $ifname inet6 static\n";
$interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6});
$interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6});
+ remove_gateway_scripts($section->{attr});
if (defined(my $gw = $net->{gateway6})) {
- remove_gateway_scripts($section->{attr});
if ($net->{needsroute6}) {
$interfaces .= make_gateway_scripts($ifname, $gw);
} else {
foreach my $attr (@{$section->{attr}}) {
$interfaces .= "\t$attr\n";
}
+ $interfaces .= "\n";
}
-
- $interfaces .= "\n";
} else {
die "unknown section type '$section->{type}'";
}
$section = undef;
};
- if (my $fh = $self->ct_open_file($filename, "r")) {
+ if (my $fh = $self->ct_open_file_read($filename)) {
while (defined (my $line = <$fh>)) {
chomp $line;
+ if ($line =~ m/^# --- (?:BEGIN|END) PVE ---/) {
+ # Include markers in the attribute section so
+ # remove_gateway_scripts() can find them.
+ push @{$section->{attr}}, $line if $section;
+ next;
+ }
if ($line =~ m/^#/) {
$interfaces .= "$line\n";
next;
}
}
+ # older templates (< Debian 8) do not configure the loopback interface
+ # if not explicitly told to do so
+ if (!$done_auto->{lo}) {
+ $interfaces = "auto lo\niface lo inet loopback\n" .
+ "iface lo inet6 loopback\n\n" .
+ $interfaces;
+ }
+
$self->ct_file_set_contents($filename, $interfaces);
}