[pve-devel] [PATCH v2 common] Fix #882: active state of interfaces depends on ipv6

Wolfgang Bumiller w.bumiller at proxmox.com
Wed Feb 10 10:59:07 CET 2016


read_etc_network_interfaces used the content of
/proc/net/if_inet6 to decide whether an interface's state is
"active", which means an interface is only active when it
has an ipv6 address, thus using net.ipv6.conf.*.disable_ipv6
on an interface will cause it to show as inactive in the web
interface.

We now filter the interfaces found in /proc/dev/net by their
IFF_UP flag read via an SIOCGIFFLAGS ioctl().
---
Changes since v1:
  Rather than reading all /sys/class/net/*/operstate files and
  accepting an 'unknown' and 'up' string we now query the interface's
  flags via an ioctl().

 src/PVE/INotify.pm                            | 21 ++++++----------
 src/PVE/Network.pm                            | 36 +++++++++++++++++++++++++++
 test/etc_network_interfaces/active_interfaces |  3 +++
 test/etc_network_interfaces/proc_net_if_inet6 |  3 ---
 test/etc_network_interfaces/runtest.pl        | 11 ++++----
 5 files changed, 52 insertions(+), 22 deletions(-)
 create mode 100644 test/etc_network_interfaces/active_interfaces
 delete mode 100644 test/etc_network_interfaces/proc_net_if_inet6

diff --git a/src/PVE/INotify.pm b/src/PVE/INotify.pm
index 6cec84b..74a0fe1 100644
--- a/src/PVE/INotify.pm
+++ b/src/PVE/INotify.pm
@@ -785,12 +785,12 @@ my $extract_ovs_option = sub {
 sub read_etc_network_interfaces {
     my ($filename, $fh) = @_;
     my $proc_net_dev = IO::File->new('/proc/net/dev', 'r');
-    my $proc_net_if_inet6 = IO::File->new('/proc/net/if_inet6', 'r');
-    return __read_etc_network_interfaces($fh, $proc_net_dev, $proc_net_if_inet6);
+    my $active = PVE::Network::get_active_interfaces();
+    return __read_etc_network_interfaces($fh, $proc_net_dev, $active);
 }
 
 sub __read_etc_network_interfaces {
-    my ($fh, $proc_net_dev, $proc_net_if_inet6) = @_;
+    my ($fh, $proc_net_dev, $active_ifaces) = @_;
 
     my $config = {};
     my $ifaces = $config->{ifaces} = {};
@@ -905,7 +905,11 @@ sub __read_etc_network_interfaces {
 	}
     }
 
-
+    foreach my $ifname (@$active_ifaces) {
+	if (my $iface = $ifaces->{$ifname}) {
+	    $iface->{active} = 1;
+	}
+    }
 
     if (!$ifaces->{lo}) {
 	$ifaces->{lo}->{priority} = 1;
@@ -997,15 +1001,6 @@ sub __read_etc_network_interfaces {
 	$d->{families} ||= ['inet'];
     }
 
-    if ($proc_net_if_inet6) {
-	while (defined ($line = <$proc_net_if_inet6>)) {
-	    if ($line =~ m/^[a-f0-9]{32}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+[a-f0-9]{2}\s+(\S+)$/) {
-		$ifaces->{$1}->{active} = 1 if defined($ifaces->{$1});
-	    }
-	}
-	close ($proc_net_if_inet6);
-    }
-
     # OVS bridges create "allow-$BRIDGE $IFACE" lines which we need to remove
     # from the {options} hash for them to be removed correctly.
     @$options = grep {defined($_)} map {
diff --git a/src/PVE/Network.pm b/src/PVE/Network.pm
index 8cfac0d..dd36505 100644
--- a/src/PVE/Network.pm
+++ b/src/PVE/Network.pm
@@ -11,6 +11,12 @@ use POSIX qw(ECONNREFUSED);
 
 use Net::IP;
 
+require "sys/ioctl.ph";
+use Socket qw(IPPROTO_IP);
+
+use constant IFF_UP => 1;
+use constant IFNAMSIZ => 16;
+
 # host network related utility functions
 
 our $ipv4_reverse_mask = [
@@ -514,4 +520,34 @@ sub is_ip_in_cidr {
     return $cidr_obj->overlaps($ip_obj) == $Net::IP::IP_B_IN_A_OVERLAP;
 }
 
+# struct ifreq { // FOR SIOCGIFFLAGS:
+#   char ifrn_name[IFNAMSIZ]
+#   short ifru_flags
+# };
+my $STRUCT_IFREQ_SIOCGIFFLAGS = 'Z' . IFNAMSIZ . 's1';
+sub get_active_interfaces {
+    # Use the interface name list from /proc/net/dev
+    open my $fh, '<', '/proc/net/dev'
+	or die "failed to open /proc/net/dev: $!\n";
+    # And filter by IFF_UP flag fetched via a PF_INET6 socket ioctl:
+    socket my $sock, PF_INET6, SOCK_DGRAM, &IPPROTO_IP
+	or die "failed to open socket\n";
+
+    my $ifaces = [];
+    while(defined(my $line = <$fh>)) {
+	next if $line !~ /^\s*([^:\s]+):/;
+	my $ifname = $1;
+	my $ifreq = pack($STRUCT_IFREQ_SIOCGIFFLAGS, $1, 0);
+	if (!defined(ioctl($sock, &SIOCGIFFLAGS, $ifreq))) {
+	    warn "failed to get interface flags for: $ifname\n";
+	    next;
+	}
+	my ($name, $flags) = unpack($STRUCT_IFREQ_SIOCGIFFLAGS, $ifreq);
+	push @$ifaces, $1 if ($flags & IFF_UP);
+    }
+    close $fh;
+    close $sock;
+    return $ifaces;
+}
+
 1;
diff --git a/test/etc_network_interfaces/active_interfaces b/test/etc_network_interfaces/active_interfaces
new file mode 100644
index 0000000..dd3634d
--- /dev/null
+++ b/test/etc_network_interfaces/active_interfaces
@@ -0,0 +1,3 @@
+lo
+eth0
+vmbr0
diff --git a/test/etc_network_interfaces/proc_net_if_inet6 b/test/etc_network_interfaces/proc_net_if_inet6
deleted file mode 100644
index 441e1d2..0000000
--- a/test/etc_network_interfaces/proc_net_if_inet6
+++ /dev/null
@@ -1,3 +0,0 @@
-00000000000000000000000000000001 01 80 10 80       lo
-fe80000000000000ae9e17fffe846a7a 03 40 20 80    vmbr0
-fc050000000000000000000010000001 03 70 00 80    vmbr0
diff --git a/test/etc_network_interfaces/runtest.pl b/test/etc_network_interfaces/runtest.pl
index ba26dcd..92cd7f8 100755
--- a/test/etc_network_interfaces/runtest.pl
+++ b/test/etc_network_interfaces/runtest.pl
@@ -59,16 +59,15 @@ sub flush_files() {
 ## Interface parsing:
 ##
 
-# Read an interfaces file with optional /proc/net/dev and /proc/net/if_inet6
-# file content strings, which default to the provided ones.
+# Read an interfaces file with optional /proc/net/dev file content string and
+# the list of active interfaces, which otherwise default
 sub r($;$$) {
-    my ($ifaces, $proc_net_dev, $proc_net_if_inet6) = @_;
+    my ($ifaces, $proc_net_dev, $active) = @_;
     $proc_net_dev //= load('proc_net_dev');
-    $proc_net_if_inet6 //= load('proc_net_if_inet6');
+    $active //= [split(/\s+/, load('active_interfaces'))];
     open my $fh1, '<', \$ifaces;
     open my $fh2, '<', \$proc_net_dev;
-    open my $fh3, '<', \$proc_net_if_inet6;
-    $config = PVE::INotify::__read_etc_network_interfaces($fh1, $fh2, $fh3);
+    $config = PVE::INotify::__read_etc_network_interfaces($fh1, $fh2, $active);
     close $fh1;
 }
 
-- 
2.1.4





More information about the pve-devel mailing list