On Wed, Jul 09, 2025 at 02:34:26PM +0200, Filip Schauer wrote: > Containers that do not use the default `/sbin/init` entrypoint may lack
^ This commit message needs updating to reflect the changes in this version. > in-container network management. A previous commit already handles > static IP addresses. Now this commit also handles DHCP. This is done > using a `dhclient` process for each network interface. > > Signed-off-by: Filip Schauer <f.scha...@proxmox.com> > --- > Changed since v2: > * rebase onto newest master (5a8b3f962f16) and re-format with > proxmox-perltidy > * add an "ipmanagehost" property to pct.conf to control whether network > interface IP configuration should be handled by the host. > * manage_dhclient: add a FIXME comment regarding the AppArmor profile: > "use a profile that confines writes to /var/lib/lxc/$vmid and rootfs" > * kill_dhclients: untaint pid from pidfile > * fix manage_dhclient called with 'stop' instead of 'start' for IPv6 > when container is started > > src/PVE/LXC.pm | 103 +++++++++++++++++++++++++++++++++++++----- > src/PVE/LXC/Config.pm | 17 ++++--- > 2 files changed, 101 insertions(+), 19 deletions(-) > > diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm > index 7c9caf6..4f19a21 100644 > --- a/src/PVE/LXC.pm > +++ b/src/PVE/LXC.pm > @@ -837,15 +837,12 @@ sub update_lxc_config { > } > > if ((!defined($d->{link_down}) || $d->{link_down} != 1) && > $conf->{ipmanagehost}) { > - if (defined($d->{ip})) { > - die "$k: DHCP is not supported with a custom entrypoint\n" > if $d->{ip} eq 'dhcp'; > - $raw .= "lxc.net.$ind.ipv4.address = $d->{ip}\n" if $d->{ip} > ne 'manual'; > - } > + $raw .= "lxc.net.$ind.ipv4.address = $d->{ip}\n" > + if defined($d->{ip}) && $d->{ip} !~ /^(dhcp|manual)$/; > $raw .= "lxc.net.$ind.ipv4.gateway = $d->{gw}\n" if > defined($d->{gw}); > if (defined($d->{ip6})) { > - die "$k: DHCPv6 and SLAAC are not supported with a custom > entrypoint\n" > - if $d->{ip6} =~ /^(auto|dhcp)$/; > - $raw .= "lxc.net.$ind.ipv6.address = $d->{ip6}\n" if > $d->{ip6} ne 'manual'; > + die "$k: SLAAC is not supported with a custom entrypoint\n" > if $d->{ip6} eq 'auto'; > + $raw .= "lxc.net.$ind.ipv6.address = $d->{ip6}\n" if > $d->{ip6} !~ /^(dhcp|manual)$/; > } > $raw .= "lxc.net.$ind.ipv6.gateway = $d->{gw6}\n" if > defined($d->{gw6}); > $raw .= "lxc.net.$ind.flags = up\n"; > @@ -1028,6 +1025,8 @@ sub vm_stop_cleanup { > PVE::Storage::deactivate_volumes($storage_cfg, $vollist); > }; > warn $@ if $@; # avoid errors - just warn > + > + kill_dhclients($vmid, '*') if $conf->{ipmanagehost}; > } > > sub net_tap_plug : prototype($$) { > @@ -1259,6 +1258,52 @@ sub get_interfaces { > return $res; > } > > +sub manage_dhclient { > + my ($action, $vmid, $ipversion, $eth, $rootdir) = @_; > + > + File::Path::make_path("/var/lib/lxc/$vmid/hook") if $action eq 'start'; > + my $pidfile = "/var/lib/lxc/$vmid/hook/dhclient$ipversion-$eth.pid"; > + my $leasefile = "/var/lib/lxc/$vmid/hook/dhclient$ipversion-$eth.leases"; > + my $scriptfile = '/usr/share/lxc/hooks/dhclient-script'; > + PVE::Tools::run_command([ > + 'lxc-attach', > + '-n', > + $vmid, > + '-s', > + 'NETWORK|UTSNAME', > + '--', > + 'aa-exec', > + '-p', > + 'unconfined', # FIXME: use a profile that confines writes to > /var/lib/lxc/$vmid and rootfs > + '/sbin/dhclient', > + $action eq 'start' ? '-1' : '-r', > + "-$ipversion", > + '-pf', > + $pidfile, > + '-lf', > + $leasefile, > + '-e', > + "ROOTFS=$rootdir", > + '-sf', > + $scriptfile, > + $eth, > + ]); > +} > + > +sub kill_dhclients { > + my ($vmid, $eth) = @_; > + > + foreach my $pidfile (glob("/var/lib/lxc/$vmid/hook/dhclient*-$eth.pid")) > { > + my $pid = eval { file_get_contents($pidfile) }; > + if (!$@) { > + chomp $pid; > + next if $pid !~ /^(\d+)$/; > + kill 9, $1; > + unlink($pidfile); > + } > + } > +} > + > sub update_ipconfig { > my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_; > > @@ -1295,11 +1340,21 @@ sub update_ipconfig { > > # step 1: add new IP, if this fails we cancel > my $is_real_ip = ($newip && $newip !~ /^(?:auto|dhcp|manual)$/); > - if ($change_ip && $is_real_ip) { > - eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); > }; > - if (my $err = $@) { > - warn $err; > - return; > + if ($change_ip) { > + if ($conf->{ipmanagehost}) { > + if ($newip && $newip eq 'dhcp') { > + manage_dhclient('start', $vmid, $ipversion, $eth, > $rootdir); > + } elsif ($oldip && $oldip eq 'dhcp') { > + manage_dhclient('stop', $vmid, $ipversion, $eth, > $rootdir); > + } > + } > + > + if ($is_real_ip) { > + eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', > $eth); }; > + if (my $err = $@) { > + warn $err; > + return; > + } > } > } > > @@ -2827,6 +2882,30 @@ sub vm_start { > } > PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start'); > > + if ($conf->{ipmanagehost}) { > + my @dhcpv4_interfaces = (); > + my @dhcpv6_interfaces = (); > + foreach my $k (sort keys %$conf) { > + next if $k !~ m/^net(\d+)$/; > + my $d = PVE::LXC::Config->parse_lxc_network($conf->{$k}); > + push @dhcpv4_interfaces, $d->{name} if $d->{ip} && $d->{ip} eq > 'dhcp'; > + push @dhcpv6_interfaces, $d->{name} if $d->{ip6} && $d->{ip6} eq > 'dhcp'; > + } > + > + my $pid = PVE::LXC::find_lxc_pid($vmid); > + my $rootdir = "/proc/$pid/root"; > + > + foreach my $eth (@dhcpv4_interfaces) { > + eval { manage_dhclient('start', $vmid, 4, $eth, $rootdir) }; > + PVE::RESTEnvironment::log_warn("DHCP failed - $@") if $@; > + } > + > + foreach my $eth (@dhcpv6_interfaces) { > + eval { manage_dhclient('start', $vmid, 6, $eth, $rootdir) }; > + PVE::RESTEnvironment::log_warn("DHCP failed - $@") if $@; > + } > + } > + > return; > } > > diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm > index c2f56e4..39c3243 100644 > --- a/src/PVE/LXC/Config.pm > +++ b/src/PVE/LXC/Config.pm > @@ -1291,12 +1291,13 @@ sub update_pct_config { > if ($mtu > $bridge_mtu); > } > > - if ((!defined($res->{link_down}) || $res->{link_down} != 1) && > $conf->{ipmanagehost}) { > - die "$opt: DHCP is not supported with a custom entrypoint\n" > - if defined($res->{ip}) && $res->{ip} eq 'dhcp'; > - > - die "$opt: DHCPv6 and SLAAC are not supported with a custom > entrypoint\n" > - if defined($res->{ip6}) && $res->{ip6} =~ > /^(auto|dhcp)$/; > + if ( > + (!defined($res->{link_down}) || $res->{link_down} != 1) > + && $conf->{ipmanagehost} > + && defined($res->{ip6}) > + && $res->{ip6} eq 'auto' > + ) { > + die "$opt: SLAAC is not supported with a custom > entrypoint\n"; > } > } elsif ($opt =~ m/^dev(\d+)$/) { > my $device = $class->parse_device($value); > @@ -1575,9 +1576,11 @@ sub vmconfig_hotplug_pending { > $cgroup->change_cpu_shares(undef); > } elsif ($opt =~ m/^net(\d)$/) { > my $netid = $1; > + my $net = parse_lxc_network($conf->{$opt}); > + PVE::LXC::kill_dhclients($vmid, $net->{name}) if > $conf->{ipmanagehost}; > + > PVE::Network::veth_delete("veth${vmid}i$netid"); > if ($have_sdn) { > - my $net = > PVE::LXC::Config->parse_lxc_network($conf->{$opt}); > print "delete ips from $opt\n"; > eval { > PVE::Network::SDN::Vnets::del_ips_from_mac( > -- > 2.47.2 > > > > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel > > _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel