X-Git-Url: https://git.proxmox.com/?p=pve-common.git;a=blobdiff_plain;f=src%2FPVE%2FNetwork.pm;h=cc754452aa19faafe752b5b3d554b1d2b5a75c61;hp=b760c42a7df2a8ebaa58c0e0323f4be8e73a6bf7;hb=9bbc4e174ccf064b394b35b697c5f80c716c8191;hpb=a712bf6e378dc0ab56647bfe7649072eb4ee394f diff --git a/src/PVE/Network.pm b/src/PVE/Network.pm index b760c42..cc75445 100644 --- a/src/PVE/Network.pm +++ b/src/PVE/Network.pm @@ -7,12 +7,15 @@ use PVE::ProcFSTools; use PVE::INotify; use File::Basename; use IO::Socket::IP; +use Socket qw(NI_NUMERICHOST NI_NUMERICSERV); use POSIX qw(ECONNREFUSED); use Net::IP; # host network related utility functions +our $PHYSICAL_NIC_RE = qr/(?:eth\d+|en[^:.]+|ib\d+)/; + our $ipv4_reverse_mask = [ '0.0.0.0', '128.0.0.0', @@ -99,7 +102,7 @@ sub setup_tc_rate_limit { run_command("/sbin/tc filter add dev $iface parent ffff: " . "prio 50 basic " . "police rate ${rate}bps burst ${burst}b mtu 64kb " . - "drop flowid :1"); + "drop"); if ($debug) { print "DEBUG tc settings\n"; @@ -162,20 +165,60 @@ my $compute_fwbr_names = sub { return ($fwbr, $vethfw, $vethfwpeer, $ovsintport); }; +sub iface_delete($) { + my ($iface) = @_; + run_command(['/sbin/ip', 'link', 'delete', 'dev', $iface], noerr => 1) + == 0 or die "failed to delete interface '$iface'\n"; +} + +sub iface_create($$@) { + my ($iface, $type, @args) = @_; + run_command(['/sbin/ip', 'link', 'add', $iface, 'type', $type, @args], noerr => 1) + == 0 or die "failed to create interface '$iface'\n"; +} + +sub iface_set($@) { + my ($iface, @opts) = @_; + run_command(['/sbin/ip', 'link', 'set', $iface, @opts], noerr => 1) + == 0 or die "failed to set interface options for '$iface' (".join(' ', @opts).")\n"; +} + +# helper for nicer error messages: +sub iface_set_master($$) { + my ($iface, $master) = @_; + if (defined($master)) { + eval { iface_set($iface, 'master', $master) }; + die "can't enslave '$iface' to '$master'\n" if $@; + } else { + eval { iface_set($iface, 'nomaster') }; + die "can't unenslave '$iface'\n" if $@; + } +} + my $cond_create_bridge = sub { my ($bridge) = @_; if (! -d "/sys/class/net/$bridge") { - system("/sbin/brctl addbr $bridge") == 0 || - die "can't add bridge '$bridge'\n"; + iface_create($bridge, 'bridge'); + disable_ipv6($bridge); } }; +sub disable_ipv6 { + my ($iface) = @_; + return if !-d '/proc/sys/net/ipv6'; # ipv6 might be completely disabled + my $file = "/proc/sys/net/ipv6/conf/$iface/disable_ipv6"; + open(my $fh, '>', $file) or die "failed to open $file for writing: $!\n"; + print {$fh} "1\n" or die "failed to disable link-local ipv6 for $iface\n"; + close($fh); +} + my $bridge_add_interface = sub { my ($bridge, $iface, $tag, $trunks) = @_; - system("/sbin/brctl addif $bridge $iface") == 0 || - die "can't add interface 'iface' to bridge '$bridge'\n"; + # drop link local address (it can't be used when on a bridge anyway) + disable_ipv6($iface); + iface_set_master($iface, $bridge); my $vlan_aware = PVE::Tools::file_read_firstline("/sys/class/net/$bridge/bridge/vlan_filtering"); @@ -190,11 +233,11 @@ my $bridge_add_interface = sub { } else { system("/sbin/bridge vlan add dev $iface vid 2-4094") == 0 || die "unable to add default vlan tags to interface $iface\n" if !$trunks; - } + } if ($trunks) { my @trunks_array = split /;/, $trunks; - foreach my $trunk (@trunks_array) { + foreach my $trunk (@trunks_array) { system("/sbin/bridge vlan add dev $iface vid $trunk") == 0 || die "unable to add vlan $trunk to interface $iface\n"; } @@ -215,6 +258,7 @@ my $ovs_bridge_add_port = sub { $cmd .= " -- set Interface $iface type=internal" if $internal; system($cmd) == 0 || die "can't add ovs port '$iface'\n"; + disable_ipv6($iface); }; my $activate_interface = sub { @@ -231,8 +275,9 @@ sub tap_create { my $bridgemtu = &$read_bridge_mtu($bridge); - eval { - PVE::Tools::run_command("/sbin/ifconfig $iface 0.0.0.0 promisc up mtu $bridgemtu"); + eval { + disable_ipv6($iface); + PVE::Tools::run_command(['/sbin/ip', 'link', 'set', $iface, 'up', 'promisc', 'on', 'mtu', $bridgemtu]); }; die "interface activation failed\n" if $@; } @@ -246,12 +291,14 @@ sub veth_create { # create veth pair if (! -d "/sys/class/net/$veth") { - my $cmd = "/sbin/ip link add name $veth type veth peer name $vethpeer mtu $bridgemtu"; + my $cmd = "/sbin/ip link add name $veth mtu $bridgemtu type veth peer name $vethpeer mtu $bridgemtu"; $cmd .= " addr $mac" if $mac; system($cmd) == 0 || die "can't create interface $veth\n"; } # up vethpair + disable_ipv6($veth); + disable_ipv6($vethpeer); &$activate_interface($veth); &$activate_interface($vethpeer); } @@ -260,9 +307,9 @@ sub veth_delete { my ($veth) = @_; if (-d "/sys/class/net/$veth") { - run_command("/sbin/ip link delete dev $veth", outfunc => sub {}, errfunc => sub {}); + iface_delete($veth); } - + eval { tap_unplug($veth) }; } my $create_firewall_bridge_linux = sub { @@ -300,8 +347,8 @@ my $create_firewall_bridge_ovs = sub { &$activate_interface($ovsintport); # set the same mtu for ovs int port - PVE::Tools::run_command("/sbin/ifconfig $ovsintport mtu $bridgemtu"); - + PVE::Tools::run_command(['/sbin/ip', 'link', 'set', $ovsintport, 'mtu', $bridgemtu]); + &$bridge_add_interface($fwbr, $ovsintport); }; @@ -309,7 +356,7 @@ my $cleanup_firewall_bridge = sub { my ($iface) = @_; my ($vmid, $devid) = &$parse_tap_device_name($iface, 1); - return if !defined($vmid); + return if !defined($vmid); my ($fwbr, $vethfw, $vethfwpeer, $ovsintport) = &$compute_fwbr_names($vmid, $devid); # cleanup old port config from any openvswitch bridge @@ -322,8 +369,7 @@ my $cleanup_firewall_bridge = sub { # cleanup fwbr bridge if (-d "/sys/class/net/$fwbr") { - run_command("/sbin/ip link set dev $fwbr down", outfunc => sub {}, errfunc => sub {}); - run_command("/sbin/brctl delbr $fwbr", outfunc => sub {}, errfunc => sub {}); + iface_delete($fwbr); } }; @@ -374,11 +420,9 @@ sub tap_unplug { #avoid insecure dependency; ($bridge) = $bridge =~ /(\S+)/; - system("/sbin/brctl delif $bridge $iface") == 0 || - die "can't del interface '$iface' from bridge '$bridge'\n"; - + iface_set_master($iface, undef); } - + &$cleanup_firewall_bridge($iface); #cleanup old port config from any openvswitch bridge eval {run_command("/usr/bin/ovs-vsctl del-port $iface", outfunc => sub {}, errfunc => sub {}) }; @@ -389,7 +433,7 @@ sub copy_bridge_config { return if $br0 eq $br1; - my $br_configs = [ 'ageing_time', 'stp_state', 'priority', 'forward_delay', + my $br_configs = [ 'ageing_time', 'stp_state', 'priority', 'forward_delay', 'hello_time', 'max_age', 'multicast_snooping', 'multicast_querier']; foreach my $sysname (@$br_configs) { @@ -407,11 +451,14 @@ sub copy_bridge_config { sub activate_bridge_vlan_slave { my ($bridgevlan, $iface, $tag) = @_; my $ifacevlan = "${iface}.$tag"; - + # create vlan on $iface is not already exist if (! -d "/sys/class/net/$ifacevlan") { - system("/sbin/ip link add link $iface name ${iface}.${tag} type vlan id $tag") == 0 || + system("/sbin/ip link add link $iface name $ifacevlan type vlan id $tag") == 0 || die "can't add vlan tag $tag to interface $iface\n"; + + # remove ipv6 link-local address before activation + disable_ipv6($ifacevlan); } # be sure to have the $ifacevlan up @@ -457,8 +504,7 @@ sub activate_bridge_vlan { lock_network(sub { # add bridgevlan if it doesn't already exist if (! -d "/sys/class/net/$bridgevlan") { - system("/sbin/brctl addbr $bridgevlan") == 0 || - die "can't add bridge $bridgevlan\n"; + iface_create($bridgevlan, 'bridge'); } # for each physical interface (eth or bridge) bind them to bridge vlan @@ -468,9 +514,10 @@ sub activate_bridge_vlan { #fixme: set other bridge flags + # remove ipv6 link-local address before activation + disable_ipv6($bridgevlan); # be sure to have the bridge up - system("/sbin/ip link set $bridgevlan up") == 0 || - die "can't up bridge $bridgevlan\n"; + &$activate_interface($bridgevlan); }); return $bridgevlan; } @@ -534,6 +581,58 @@ sub is_ip_in_cidr { return $cidr_obj->overlaps($ip_obj) == $Net::IP::IP_B_IN_A_OVERLAP; } + +sub get_local_ip_from_cidr { + my ($cidr) = @_; + + my $cmd = ['/sbin/ip', 'address', 'show', 'to', $cidr, 'up']; + + my $IPs = []; + + my $code = sub { + my $line = shift; + + if ($line =~ m!^\s*inet(?:6)?\s+($PVE::Tools::IPRE)(?:/\d+|\s+peer\s+)!) { + push @$IPs, $1; + } + }; + + PVE::Tools::run_command($cmd, outfunc => $code); + + return $IPs; +} + +sub addr_to_ip { + my ($addr) = @_; + my ($err, $host, $port) = Socket::getnameinfo($addr, NI_NUMERICHOST | NI_NUMERICSERV); + die "failed to get numerical host address: $err\n" if $err; + return ($host, $port) if wantarray; + return $host; +} + +sub get_ip_from_hostname { + my ($hostname, $noerr) = @_; + + my ($family, $ip); + + eval { + my @res = PVE::Tools::getaddrinfo_all($hostname); + $family = $res[0]->{family}; + $ip = addr_to_ip($res[0]->{addr}) + }; + if ($@) { + die "hostname lookup '$hostname' failed - $@" if !$noerr; + return undef; + } + + if ($ip =~ m/^127\.|^::1$/) { + die "hostname lookup '$hostname' failed - got local IP address '$ip'\n" if !$noerr; + return undef; + } + + return wantarray ? ($ip, $family) : $ip; +} + sub lock_network { my ($code, @param) = @_; my $res = lock_file('/var/lock/pve-network.lck', 10, $code, @param);