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} = {};
}
}
-
+ foreach my $ifname (@$active_ifaces) {
+ if (my $iface = $ifaces->{$ifname}) {
+ $iface->{active} = 1;
+ }
+ }
if (!$ifaces->{lo}) {
$ifaces->{lo}->{priority} = 1;
$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 {
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 = [
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;
--- /dev/null
+lo
+eth0
+vmbr0
+++ /dev/null
-00000000000000000000000000000001 01 80 10 80 lo
-fe80000000000000ae9e17fffe846a7a 03 40 20 80 vmbr0
-fc050000000000000000000010000001 03 70 00 80 vmbr0
## 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;
}