chomp $line;
if ($line =~ m/^(search|domain)\s+(\S+)\s*/) {
$res->{search} = $2;
- } elsif ($line =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*/) {
+ } elsif ($line =~ m/^\s*nameserver\s+($PVE::Tools::IPRE)\s*/) {
$nscount++;
if ($nscount <= 3) {
$res->{"dns$nscount"} = $1;
# }
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);
+}
+
+sub __read_etc_network_interfaces {
+ my ($fh, $proc_net_dev, $proc_net_if_inet6) = @_;
my $config = {};
my $ifaces = $config->{ifaces} = {};
my $line;
- if (my $fd2 = IO::File->new("/proc/net/dev", "r")) {
- while (defined ($line = <$fd2>)) {
+ if ($proc_net_dev) {
+ while (defined ($line = <$proc_net_dev>)) {
if ($line =~ m/^\s*(eth\d+):.*/) {
$ifaces->{$1}->{exists} = 1;
}
}
- close($fd2);
+ close($proc_net_dev);
}
# we try to keep order inside the file
my $f = { method => $3 }; # by family, merged to $d with a $suffix
(my $suffix = $family) =~ s/^inet//;
- my $d = $ifaces->{$i};
+ my $d = $ifaces->{$i} ||= {};
$d->{priority} = $priority++ if !$d->{priority};
push @{$d->{families}}, $family;
}
} elsif ($id eq 'bridge_fd') {
$d->{$id} = $value;
+ } elsif ($id eq 'bridge_vlan_aware') {
+ $d->{$id} = 1;
} elsif ($id eq 'bond_miimon') {
$d->{$id} = $value;
} elsif ($id eq 'bond_xmit_hash_policy') {
$ifaces->{$1}->{exists} = 0;
$d->{exists} = 0;
}
+ } elsif ($iface =~ m/^(\S+)\.\d+$/) {
+ $d->{type} = 'vlan';
+ if (defined ($ifaces->{$1})) {
+ $d->{exists} = $ifaces->{$1}->{exists};
+ } else {
+ $ifaces->{$1}->{exists} = 0;
+ $d->{exists} = 0;
+ }
} elsif ($iface =~ m/^eth\d+$/) {
if (!$d->{ovs_type}) {
$d->{type} = 'eth';
$d->{method} = 'manual' if !$d->{method};
$d->{method6} = 'manual' if !$d->{method6};
+
+ $d->{families} ||= ['inet'];
}
- if (my $fd2 = IO::File->new("/proc/net/if_inet6", "r")) {
- while (defined ($line = <$fd2>)) {
+ 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 ($fd2);
+ 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 {
+ my ($pri, $line) = @$_;
+ if ($line =~ /^allow-(\S+)\s+(.*)$/) {
+ my $bridge = $1;
+ my @ports = split(/\s+/, $2);
+ if (defined(my $br = $ifaces->{$bridge})) {
+ # if this port is part of a bridge, remove it
+ my %in_ovs_ports = map {$_=>1} split(/\s+/, $br->{ovs_ports});
+ @ports = grep { not $in_ovs_ports{$_} } @ports;
+ }
+ # create the allow line for the remaining ports, or delete if empty
+ if (@ports) {
+ [$pri, "allow-$bridge " . join(' ', @ports)];
+ } else {
+ undef;
+ }
+ } else {
+ # don't modify other lines
+ $_;
+ }
+ } @$options;
+
return $config;
}
$v = defined($d->{bridge_fd}) ? $d->{bridge_fd} : 0;
$raw .= "\tbridge_fd $v\n";
$done->{bridge_fd} = 1;
+
+ if( defined($d->{bridge_vlan_aware})) {
+ $raw .= "\tbridge_vlan_aware yes\n";
+ }
+ $done->{bridge_vlan_aware} = 1;
} elsif ($d->{type} eq 'bond') {
return $raw;
}
+
sub write_etc_network_interfaces {
my ($filename, $fh, $config) = @_;
+ my $raw = __write_etc_network_interfaces($config);
+ PVE::Tools::safe_print($filename, $fh, $raw);
+}
+sub __write_etc_network_interfaces {
+ my ($config) = @_;
my $ifaces = $config->{ifaces};
my @options = @{$config->{options}};
$d->{type} eq 'OVSBond') {
my $brname = $used_ports->{$iface};
if (!$brname || !$ifaces->{$brname}) {
- delete $ifaces->{$iface};
+ if ($iface =~ /^eth/) {
+ $ifaces->{$iface} = { type => 'eth',
+ exists => 1,
+ method => 'manual',
+ families => ['inet'] };
+ } else {
+ delete $ifaces->{$iface};
+ }
next;
}
my $bd = $ifaces->{$brname};
my $n = $ifaces->{$p};
die "OVS bridge '$iface' - unable to find port '$p'\n"
if !$n;
+ $n->{autostart} = 0;
if ($n->{type} eq 'eth') {
$n->{type} = 'OVSPort';
$n->{ovs_bridge} = $iface;
}
}
- my $raw = "# network interface settings\n";
+ my $raw = <<'NETWORKDOC';
+# network interface settings; autogenerated
+# Please do NOT modify this file directly, unless you know what
+# you're doing.
+#
+# If you want to manage part of the network configuration manually,
+# please utilize the 'source' or 'source-directory' directives to do
+# so.
+# PVE will preserve these directives, but will NOT its network
+# configuration from sourced files, so do not attempt to move any of
+# the PVE managed interfaces into external files!
+
+NETWORKDOC
my $printed = {};
my $if_type_hash = {
- unknown => 0,
- loopback => 10,
- eth => 20,
- bond => 30,
- bridge => 40,
+ loopback => 100000,
+ eth => 200000,
+ bond => 300000,
+ bridge => 400000,
};
my $lookup_type_prio = sub {
my $iface = shift;
- my $alias = 0;
- if ($iface =~ m/^(\S+):\d+$/) {
+ my $child = 0;
+ if ($iface =~ m/^(\S+)(\.|:)\d+$/) {
$iface = $1;
- $alias = 1;
+ $child = 1;
}
my $pri;
if ($iface eq 'lo') {
$pri = $if_type_hash->{loopback};
} elsif ($iface =~ m/^eth\d+$/) {
- $pri = $if_type_hash->{eth} + $alias;
+ $pri = $if_type_hash->{eth} + $child;
} elsif ($iface =~ m/^bond\d+$/) {
- $pri = $if_type_hash->{bond} + $alias;
+ $pri = $if_type_hash->{bond} + $child;
} elsif ($iface =~ m/^vmbr\d+$/) {
- $pri = $if_type_hash->{bridge} + $alias;
+ $pri = $if_type_hash->{bridge} + $child;
}
- return $pri || ($if_type_hash->{unknown} + $alias);
+ return $pri;
};
foreach my $iface (sort {
my $ref1 = $ifaces->{$a};
my $ref2 = $ifaces->{$b};
- my $p1 = &$lookup_type_prio($a);
- my $p2 = &$lookup_type_prio($b);
+ my $tp1 = &$lookup_type_prio($a);
+ my $tp2 = &$lookup_type_prio($b);
- return $p1 <=> $p2 if $p1 != $p2;
+ # Only recognized types are in relation to each other. If one type
+ # is unknown then only consider the interfaces' priority attributes.
+ $tp1 = $tp2 = 0 if !defined($tp1) || !defined($tp2);
- $p1 = $ref1->{priority} || 100000;
- $p2 = $ref2->{priority} || 100000;
+ my $p1 = $tp1 + ($ref1->{priority} // 50000);
+ my $p2 = $tp2 + ($ref2->{priority} // 50000);
return $p1 <=> $p2 if $p1 != $p2;
return $a cmp $b;
- } keys %$ifaces) {
-
- my $d = $ifaces->{$iface};
-
+ } keys %$ifaces) {
next if $printed->{$iface};
- if (@options && $options[0]->[0] < $d->{priority}) {
+ my $d = $ifaces->{$iface};
+ my $pri = $d->{priority} // 0;
+ if (@options && $options[0]->[0] < $pri) {
do {
$raw .= (shift @options)->[1] . "\n";
- } while (@options && $options[0]->[0] < $d->{priority});
+ } while (@options && $options[0]->[0] < $pri);
$raw .= "\n";
}
}
$raw .= $_->[1] . "\n" foreach @options;
-
- PVE::Tools::safe_print($filename, $fh, $raw);
+ return $raw;
}
register_file('interfaces', "/etc/network/interfaces",