From ac487a888b7fc17a73e702586bd162a5527014b5 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Sat, 18 Sep 2021 14:38:59 +0200 Subject: [PATCH] net: add get_local_ip helper Sometimes we need to have a fallback for gai (get_ip_from_hostname) but cannot yet rely on configured networks (get_reachable_networks) from kernel POV (those may not have been configured yet, e.g., on boot), so the ones configured in /etc/network/interfaces would be nice too then, as they're the ones that will get configured soon anyway on boot. Add a new helper that takes in all those sources and allows to return a single (first found) or all of those addresses. Still prioritize the address we get from getaddrinfo, as there the admin has control through /etc/hosts, DNS and gai.conf and treat the remaining ones as fallback. Signed-off-by: Thomas Lamprecht --- src/PVE/Network.pm | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/PVE/Network.pm b/src/PVE/Network.pm index 948e059..e8142bb 100644 --- a/src/PVE/Network.pm +++ b/src/PVE/Network.pm @@ -626,6 +626,60 @@ sub get_reachable_networks { return $res; } +# get one or all local IPs that are not loopback ones, able to pick up the following ones (in order) +# - the hostname primary resolves too, follows gai.conf (admin controlled) and will be prioritised +# - all configured in the interfaces configuration +# - all currently networks known to the kernel in the current (root) namespace +# returns a single address if no parameter is passed, and all found, grouped by type, if `all => 1` +# is passed. +sub get_local_ip { + my (%param) = @_; + + my $nodename = PVE::INotify::nodename(); + my $resolved_host = eval { get_ip_from_hostname($nodename) }; + + return $resolved_host if defined($resolved_host) && !$param{all}; + + my $all = { v4 => {}, v6 => {} }; # hash to avoid duplicates and group by type + + my $ifaces = PVE::INotify::read_file('interfaces', 1)->{data}->{ifaces}; + for my $if (values $ifaces->%*) { + next if $if->{type} eq 'loopback' || (!defined($if->{address}) && !defined($if->{address6})); + my ($v4, $v6) = ($if->{address}, $if->{address6}); + + return ($v4 // $v6) if !$param{all}; # prefer v4, admin can override $resolved_host via hosts/gai.conf + + $all->{v4}->{$v4} = 1 if defined($v4); + $all->{v6}->{$v6} = 1 if defined($v6); + } + + my $live = get_reachable_networks(); + for my $info ($live->@*) { + my $addr = $info->{addr}; + + return $addr if !$param{all}; + + if ($info->{family} eq 'inet') { + $all->{v4}->{$addr} = 1; + } else { + $all->{v6}->{$addr} = 1; + } + } + + return undef if !$param{all}; # getting here means no early return above triggered -> no IPs + + my $res = []; # order gai.conf controlled first, then group v4 and v6, simply lexically sorted + if ($resolved_host) { + push $res->@*, $resolved_host; + delete $all->{v4}->{$resolved_host}; + delete $all->{v6}->{$resolved_host}; + } + push $res->@*, sort { $a cmp $b } keys $all->{v4}->%*; + push $res->@*, sort { $a cmp $b } keys $all->{v6}->%*; + + return $res; +} + sub get_local_ip_from_cidr { my ($cidr) = @_; -- 2.39.2