From 12a235d624c04795a542703a48f65d6d1a22c474 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 10 Feb 2016 10:59:07 +0100 Subject: [PATCH] Fix #882: active state of interfaces depends on ipv6 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(). --- 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.39.2