]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/LXC/Setup/Debian.pm
1d146067b0342363952b26f08b1747145444b033
1 package PVE
::LXC
::Setup
::Debian
;
6 use PVE
::Tools
qw($IPV6RE);
11 use PVE
::LXC
::Setup
::Base
;
13 use base
qw(PVE::LXC::Setup::Base);
16 my ($class, $conf, $rootdir) = @_;
18 my $version = PVE
::Tools
::file_read_firstline
("$rootdir/etc/debian_version");
20 die "unable to read version info\n" if !defined($version);
22 # translate testing version names
23 $version = 9.1 if $version eq 'stretch/sid';
24 $version = 10 if $version eq 'buster/sid';
25 $version = 11 if $version eq 'bullseye/sid';
27 die "unable to parse version info '$version'\n"
28 if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/;
32 die "unsupported debian version '$version'\n"
33 if !($version >= 4 && $version <= 11);
35 my $self = { conf
=> $conf, rootdir
=> $rootdir, version
=> $version };
37 $conf->{ostype
} = "debian";
39 return bless $self, $class;
42 # Debian doesn't support the /dev/lxc/ subdirectory.
48 my ($self, $conf) = @_;
50 my $systemd = $self->ct_readlink('/sbin/init');
51 if (defined($systemd) && $systemd =~ m
@/systemd$@) {
52 $self->setup_container_getty_service($conf);
55 my $filename = "/etc/inittab";
56 return if !$self->ct_file_exists($filename);
58 my $ttycount = PVE
::LXC
::Config-
>get_tty_count($conf);
59 my $inittab = $self->ct_file_get_contents($filename);
63 !/^\s*\d+:\d*:[^:]*:.*getty/ &&
66 } split(/\n/, $inittab);
68 $inittab = join("\n", @lines) . "\n";
70 $inittab .= "p0::powerfail:/sbin/init 0\n";
72 my $version = $self->{version
};
73 for (my $id = 1; $id <= $ttycount; $id++) {
74 next if $id == 7; # reserved for X11
75 my $levels = ($id == 1) ?
'2345' : '23';
77 $inittab .= "$id:$levels:respawn:/sbin/getty -L 38400 tty$id\n";
79 $inittab .= "$id:$levels:respawn:/sbin/getty --noclear 38400 tty$id\n";
83 $self->ct_file_set_contents($filename, $inittab);
86 sub remove_gateway_scripts
{
88 my $length = scalar(@$attr);
90 my $found_section = 0;
93 if ($_ eq '# --- BEGIN PVE ---') {
97 } elsif ($_ eq '# --- END PVE ---') {
100 0; # remove this line
106 return if $found_section;
107 # XXX: To deal with existing setups we perform two types of removal for
108 # now. Newly started containers have their routing sections marked with
109 # begin/end comments. For older containers we perform a strict matching on
110 # the routing rules we added. We can probably remove this part at some point
111 # when it is unlikely that old debian setups are still around.
113 for (my $i = 0; $i < $length-3; ++$i) {
114 next if $attr->[$i+0] !~ m
@^\s*post-up\s
+ip\s
+route\s
+add\s
+(\S
+)\s
+dev\s
+(\S
+)$@;
115 my ($ip, $dev) = ($1, $2);
116 if ($attr->[$i+1] =~ m
@^\s*post-up\s
+ip\s
+route\s
+add\s
+default\s
+via\s
+(\S
+)\s
+dev\s
+(\S
+)$@ &&
117 ($ip eq $1 && $dev eq $2) &&
118 $attr->[$i+2] =~ m
@^\s*pre-down\s
+ip\s
+route\s
+del\s
+default\s
+via\s
+(\S
+)\s
+dev\s
+(\S
+)$@ &&
119 ($ip eq $1 && $dev eq $2) &&
120 $attr->[$i+3] =~ m
@^\s*pre-down\s
+ip\s
+route\s
+del\s
+(\S
+)\s
+dev\s
+(\S
+)$@ &&
121 ($ip eq $1 && $dev eq $2))
123 splice @$attr, $i, 4;
130 sub make_gateway_scripts
{
131 my ($ifname, $gw) = @_;
134 \tpost-up ip route add $gw dev $ifname
135 \tpost-up ip route add default via $gw dev $ifname
136 \tpre-down ip route del default via $gw dev $ifname
137 \tpre-down ip route del $gw dev $ifname
143 my ($self, $conf) = @_;
146 foreach my $k (keys %$conf) {
147 next if $k !~ m/^net(\d+)$/;
149 my $d = PVE
::LXC
::Config-
>parse_lxc_network($conf->{$k});
153 if (defined($d->{ip
})) {
154 if ($d->{ip
} =~ /^(?:dhcp|manual)$/) {
155 $net->{address
} = $d->{ip
};
157 my $ipinfo = PVE
::LXC
::parse_ipv4_cidr
($d->{ip
});
158 $net->{address
} = $ipinfo->{address
};
159 $net->{netmask
} = $ipinfo->{netmask
};
163 if (defined($d->{'gw'})) {
164 $net->{gateway
} = $d->{'gw'};
165 if (defined($cidr) && !PVE
::Network
::is_ip_in_cidr
($d->{gw
}, $cidr, 4)) {
166 # gateway is not reachable, need an extra route
167 $net->{needsroute
} = 1;
171 if (defined($d->{ip6
})) {
172 if ($d->{ip6
} =~ /^(?:auto|dhcp|manual)$/) {
173 $net->{address6
} = $d->{ip6
};
174 } elsif ($d->{ip6
} !~ /^($IPV6RE)\/(\d
+)$/) {
175 die "unable to parse ipv6 address/prefix\n";
177 $net->{address6
} = $1;
178 $net->{netmask6
} = $2;
182 if (defined($d->{'gw6'})) {
183 $net->{gateway6
} = $d->{'gw6'};
184 if (defined($cidr) && !PVE
::Network
::is_ip_in_cidr
($d->{gw6
}, $cidr, 6) &&
185 !PVE
::Network
::is_ip_in_cidr
($d->{gw6
}, 'fe80::/10', 6)) {
186 # gateway is not reachable, need an extra route
187 $net->{needsroute6
} = 1;
190 $networks->{$d->{name
}} = $net if keys %$net;
194 return if !scalar(keys %$networks);
196 my $filename = "/etc/network/interfaces";
202 my $done_v4_hash = {};
203 my $done_v6_hash = {};
205 my $print_section = sub {
208 my $ifname = $section->{ifname
};
209 my $net = $networks->{$ifname};
211 if (!$done_auto->{$ifname}) {
212 $interfaces .= "auto $ifname\n";
213 $done_auto->{$ifname} = 1;
216 if ($section->{type
} eq 'ipv4') {
217 $done_v4_hash->{$ifname} = 1;
219 if (!defined($net->{address
})) {
220 # no address => no iface line
221 } elsif ($net->{address
} =~ /^(dhcp|manual)$/) {
222 $interfaces .= "iface $ifname inet $1\n\n";
224 $interfaces .= "iface $ifname inet static\n";
225 $interfaces .= "\taddress $net->{address}\n" if defined($net->{address
});
226 $interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask
});
228 remove_gateway_scripts
($section->{attr
});
229 if (defined(my $gw = $net->{gateway
})) {
230 if ($net->{needsroute
}) {
231 $interfaces .= make_gateway_scripts
($ifname, $gw);
233 $interfaces .= "\tgateway $gw\n";
236 foreach my $attr (@{$section->{attr
}}) {
237 $interfaces .= "\t$attr\n";
241 } elsif ($section->{type
} eq 'ipv6') {
242 $done_v6_hash->{$ifname} = 1;
244 if (!defined($net->{address6
})) {
245 # no address => no iface line
246 } elsif ($net->{address6
} =~ /^(auto|dhcp|manual)$/) {
247 $interfaces .= "iface $ifname inet6 $1\n\n";
249 $interfaces .= "iface $ifname inet6 static\n";
250 $interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6
});
251 $interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6
});
252 remove_gateway_scripts
($section->{attr
});
253 if (defined(my $gw = $net->{gateway6
})) {
254 if ($net->{needsroute6
}) {
255 $interfaces .= make_gateway_scripts
($ifname, $gw);
257 $interfaces .= "\tgateway $net->{gateway6}\n" if defined($net->{gateway6
});
260 foreach my $attr (@{$section->{attr
}}) {
261 $interfaces .= "\t$attr\n";
266 die "unknown section type '$section->{type}'";
272 if (my $fh = $self->ct_open_file_read($filename)) {
273 while (defined (my $line = <$fh>)) {
275 if ($line =~ m/^# --- (?:BEGIN|END) PVE ---/) {
276 # Include markers in the attribute section so
277 # remove_gateway_scripts() can find them.
278 push @{$section->{attr
}}, $line if $section;
281 if ($line =~ m/^#/) {
282 $interfaces .= "$line\n";
285 if ($line =~ m/^\s*$/) {
289 $interfaces .= "$line\n";
293 if ($line =~ m/^\s*iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
295 &$print_section(); # print previous section
296 if (!$networks->{$ifname}) {
297 $interfaces .= "$line\n";
300 $section = { type
=> 'ipv4', ifname
=> $ifname, attr
=> []};
303 if ($line =~ m/^\s*iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) {
305 &$print_section(); # print previous section
306 if (!$networks->{$ifname}) {
307 $interfaces .= "$line\n";
310 $section = { type
=> 'ipv6', ifname
=> $ifname, attr
=> []};
314 if ($line =~ m/^\s*auto\s*(.*)$/) {
315 foreach my $iface (split(/\s+/, $1)) {
316 $done_auto->{$iface} = 1;
319 $interfaces .= "$line\n";
322 # Handle other section delimiters:
323 if ($line =~ m
/^\s
*(?
:mapping\s
329 $interfaces .= "$line\n";
332 if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) {
333 my ($adata, $aname) = ($1, $2);
334 if ($aname eq 'address' || $aname eq 'netmask' ||
335 $aname eq 'gateway' || $aname eq 'broadcast') {
338 push @{$section->{attr
}}, $adata;
343 $interfaces .= "$line\n";
348 my $need_separator = length($interfaces) && ($interfaces !~ /\n\n$/);
349 foreach my $ifname (sort keys %$networks) {
350 my $net = $networks->{$ifname};
352 if (!$done_v4_hash->{$ifname} && defined($net->{address
})) {
353 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
354 $section = { type
=> 'ipv4', ifname
=> $ifname, attr
=> []};
357 if (!$done_v6_hash->{$ifname} && defined($net->{address6
})) {
358 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
359 $section = { type
=> 'ipv6', ifname
=> $ifname, attr
=> []};
364 # older templates (< Debian 8) do not configure the loopback interface
365 # if not explicitly told to do so
366 if (!$done_auto->{lo
}) {
367 $interfaces = "auto lo\niface lo inet loopback\n" .
368 "iface lo inet6 loopback\n\n" .
372 $self->ct_file_set_contents($filename, $interfaces);