[pve-devel] [PATCH pve-container 3/3] fix ipv4 replacement of running containers

Wolfgang Bumiller w.bumiller at proxmox.com
Thu Jul 30 10:54:01 CEST 2015


While the previous strategy was enough for ipv6, ipv4 has
the concept of primary and secondary addresses (and
permanent, deprecated, ...), and so adding additional ip
addresses marked them as 'secondary'. The side effect of
this is that deleting the primary ip deletes all other ip
addresses within the same subnet. To avoid this we need to
temporarily set /proc/sys/net/ipv4/conf/promote_secondaries
to 1 before deleting the old IP address.
---
 src/PVE/LXC.pm | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 98b73be..bea70cc 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1356,10 +1356,11 @@ sub update_ipconfig {
     my $optdata = $conf->{$opt};
     my $deleted = [];
     my $added = [];
-    my $netcmd = sub {
-	my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', @_];
-	PVE::Tools::run_command($cmd);
+    my $nscmd = sub {
+	my $cmdargs = shift;
+	PVE::Tools::run_command(['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', @_], %$cmdargs);
     };
+    my $ipcmd = sub { &$nscmd({}, '/sbin/ip', @_) };
 
     my $change_ip_config = sub {
 	my ($ipversion) = @_;
@@ -1380,7 +1381,7 @@ sub update_ipconfig {
 
 	# step 1: add new IP, if this fails we cancel
 	if ($change_ip && $newip && $newip !~ /^(?:auto|dhcp)$/) {
-	    eval { &$netcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); };
+	    eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); };
 	    if (my $err = $@) {
 		warn $err;
 		return;
@@ -1394,19 +1395,19 @@ sub update_ipconfig {
 	# Note: 'ip route replace' can add
 	if ($change_gw) {
 	    if ($newgw) {
-		eval { &$netcmd($family_opt, 'route', 'replace', 'default', 'via', $newgw); };
+		eval { &$ipcmd($family_opt, 'route', 'replace', 'default', 'via', $newgw); };
 		if (my $err = $@) {
 		    warn $err;
 		    # the route was not replaced, the old IP is still available
 		    # rollback (delete new IP) and cancel
 		    if ($change_ip) {
-			eval { &$netcmd($family_opt, 'addr', 'del', $newip, 'dev', $eth); };
+			eval { &$ipcmd($family_opt, 'addr', 'del', $newip, 'dev', $eth); };
 			warn $@ if $@; # no need to die here
 		    }
 		    return;
 		}
 	    } else {
-		eval { &$netcmd($family_opt, 'route', 'del', 'default'); };
+		eval { &$ipcmd($family_opt, 'route', 'del', 'default'); };
 		# if the route was not deleted, the guest might have deleted it manually
 		# warn and continue
 		warn $@ if $@;
@@ -1416,8 +1417,22 @@ sub update_ipconfig {
 	# from this point on we save the configuration
 	# step 3: delete old IP ignoring errors
 	if ($change_ip && $oldip && $oldip !~ /^(?:auto|dhcp)$/) {
-	    eval { &$netcmd($family_opt, 'addr', 'del', $oldip, 'dev', $eth); };
+	    # We need to enable promote_secondaries, otherwise our newly added
+	    # address will be removed along with the old one.
+	    my $promote = 0;
+	    eval {
+		if ($ipversion == 4) {
+		    &$nscmd({ outfunc => sub { $promote = int(shift) } },
+			    'cat', "/proc/sys/net/ipv4/conf/$eth/promote_secondaries");
+		    &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=1");
+		}
+		&$ipcmd($family_opt, 'addr', 'del', $oldip, 'dev', $eth);
+	    };
 	    warn $@ if $@; # no need to die here
+
+	    if ($ipversion == 4) {
+		&$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=$promote");
+	    }
 	}
 
 	foreach my $property ($ip, $gw) {
-- 
2.1.4




More information about the pve-devel mailing list