From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id EA9641FF14F for ; Fri, 08 May 2026 20:46:46 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id EAD7121598; Fri, 8 May 2026 20:46:38 +0200 (CEST) From: Christoph Heiss To: pve-devel@lists.proxmox.com Subject: [PATCH installer 2/8] install: move network subroutines to Proxmox::Sys::Net Date: Fri, 8 May 2026 20:44:05 +0200 Message-ID: <20260508184546.113293-3-c.heiss@proxmox.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260508184546.113293-1-c.heiss@proxmox.com> References: <20260508184546.113293-1-c.heiss@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778265854248 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.076 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: UPPHNRZ4VXMQEIDGGCSUKH6WJ4ICR4N7 X-Message-ID-Hash: UPPHNRZ4VXMQEIDGGCSUKH6WJ4ICR4N7 X-MailFrom: c.heiss@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: As they are dealing with network, they make more sense over there. No functional changes. Signed-off-by: Christoph Heiss --- Proxmox/Install/RunEnv.pm | 160 ++------------------------------------ Proxmox/Sys/Net.pm | 152 +++++++++++++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 155 deletions(-) diff --git a/Proxmox/Install/RunEnv.pm b/Proxmox/Install/RunEnv.pm index 40041ad..b83b9ff 100644 --- a/Proxmox/Install/RunEnv.pm +++ b/Proxmox/Install/RunEnv.pm @@ -80,154 +80,6 @@ sub query_cpu_info : prototype() { return $cpu_info; } -# Returns a hash. -# -# { -# => { -# mac => , -# index => , -# name => , -# state => , -# pinned_id => , -# driver => , -# addresses => [ -# family => , -# address => , -# prefix => , -# ], -# }, -# } -my sub query_netdevs : prototype() { - my $ifs = {}; - my $default; - - # FIXME: not the same as the battle proven way we used in the installer for years? - my $interfaces = from_json(qx/ip --details --json address show/, { utf8 => 1 }); - - my $pinned_counter = 0; - for my $if (@$interfaces) { - my ($index, $name, $state, $mac, $addresses) = - $if->@{qw(ifindex ifname operstate address addr_info)}; - - next if !$name || $name eq 'lo'; # could also check flags for LOOPBACK.. - if (!$mac) { - log_info("skipped interface $name, no mac address detected"); - next; - } - - my @valid_addrs; - if (uc($state) eq 'UP') { - for my $addr (@$addresses) { - next if $addr->{scope} eq 'link'; - - my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)}; - - push @valid_addrs, - { - family => $family, - address => $addr, - prefix => $prefix, - }; - } - } - - my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown'; - $driver =~ s!^.*/!!; - - $ifs->{$name} = { - index => $index, - name => $name, - mac => $mac, - state => uc($state), - driver => $driver, - }; - $ifs->{$name}->{addresses} = \@valid_addrs if @valid_addrs; - - # only set the `pinned_id` property if the interface actually can be pinned, - # i.e. is a physical link - my $is_pinnable = - Proxmox::Sys::Net::ip_link_is_physical($if) && !Proxmox::Sys::Net::iface_is_vf($name); - if ($is_pinnable) { - $ifs->{$name}->{pinned_id} = "${pinned_counter}"; - $pinned_counter++; - } - } - - return $ifs; -} - -# Returns a hash. -# -# { -# gateway4 => { -# dst => "default", -# gateway => , -# dev => , -# }, -# gateway6 => { -# dst => "default", -# gateway => , -# dev => , -# }, -# } -my sub query_routes : prototype() { - my ($gateway4, $gateway6); - - log_info("query routes"); - my $route4 = from_json(qx/ip -4 --json route show/, { utf8 => 1 }); - for my $route (@$route4) { - if ($route->{dst} eq 'default') { - $gateway4 = { - dev => $route->{dev}, - gateway => $route->{gateway}, - }; - last; - } - } - - my $route6 = from_json(qx/ip -6 --json route show/, { utf8 => 1 }); - for my $route (@$route6) { - if ($route->{dst} eq 'default') { - $gateway6 = { - dev => $route->{dev}, - gateway => $route->{gateway}, - }; - last; - } - } - - my $routes; - $routes->{gateway4} = $gateway4 if $gateway4; - $routes->{gateway6} = $gateway6 if $gateway6; - - return $routes; -} - -# If `/etc/resolv.conf` fails to open this returns nothing. -# Otherwise it returns a hash: -# { -# dns => , -# -my sub query_dns : prototype() { - log_info("query DNS from resolv.conf (managed by DHCP client)"); - open my $fh, '<', '/etc/resolv.conf' or return; - - my @dns; - my $domain; - while (defined(my $line = <$fh>)) { - if ($line =~ /^nameserver\s+(\S+)/) { - push @dns, $1; - } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) { - $domain = $1; - } - } - - my $output = { - domain => $domain, - @dns ? (dns => \@dns) : (), - }; -} - # Uses `traceroute` and `geoiplookup`/`geoiplookup6` to figure out the current country. # Has a 10s timeout and uses the stops at the first entry found in the geoip database. my sub detect_country_tracing_to : prototype($$) { @@ -294,9 +146,9 @@ my sub detect_country_tracing_to : prototype($$) { # default_zfs_arc_max => , # disks => , # network => { -# interfaces => , -# routes => , -# dns => , +# interfaces => , +# routes => , +# dns => , # }, # } sub query_installation_environment : prototype() { @@ -315,14 +167,14 @@ sub query_installation_environment : prototype() { # else re-query everything my $output = {}; - my $routes = query_routes(); + my $routes = Proxmox::Sys::Net::query_routes(); log_info("query block devices"); $output->{disks} = Proxmox::Sys::Block::get_cached_disks(); $output->{network} = { - interfaces => query_netdevs(), + interfaces => Proxmox::Sys::Net::query_netdevs(), routes => $routes, - dns => query_dns(), + dns => Proxmox::Sys::Net::query_dns(), }; # avoid serializing out null or an empty string, that can trip up the UIs diff --git a/Proxmox/Sys/Net.pm b/Proxmox/Sys/Net.pm index e3b3b56..9571236 100644 --- a/Proxmox/Sys/Net.pm +++ b/Proxmox/Sys/Net.pm @@ -3,9 +3,10 @@ package Proxmox::Sys::Net; use strict; use warnings; +use Proxmox::Log; use Proxmox::Sys::Command; use Proxmox::Sys::Udev; -use JSON qw(); +use JSON qw(from_json); use base qw(Exporter); our @EXPORT_OK = qw( @@ -257,6 +258,155 @@ sub udevadm_netdev_details { return $result; } +# Returns a hash. +# +# { +# => { +# mac => , +# index => , +# name => , +# state => , +# pinned_id => , +# driver => , +# addresses => [ +# family => , +# address => , +# prefix => , +# ], +# }, +# } +sub query_netdevs : prototype() { + my $ifs = {}; + my $default; + + # FIXME: not the same as the battle proven way we used in the installer for years? + my $interfaces = from_json(qx/ip --details --json address show/, { utf8 => 1 }); + + my $pinned_counter = 0; + for my $if (@$interfaces) { + my ($index, $name, $state, $mac, $addresses) = + $if->@{qw(ifindex ifname operstate address addr_info)}; + + next if !$name || $name eq 'lo'; # could also check flags for LOOPBACK.. + if (!$mac) { + log_info("skipped interface $name, no mac address detected"); + next; + } + + my @valid_addrs; + if (uc($state) eq 'UP') { + for my $addr (@$addresses) { + next if $addr->{scope} eq 'link'; + + my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)}; + + push @valid_addrs, + { + family => $family, + address => $addr, + prefix => $prefix, + }; + } + } + + my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown'; + $driver =~ s!^.*/!!; + + $ifs->{$name} = { + index => $index, + name => $name, + mac => $mac, + state => uc($state), + driver => $driver, + }; + $ifs->{$name}->{addresses} = \@valid_addrs if @valid_addrs; + + # only set the `pinned_id` property if the interface actually can be pinned, + # i.e. is a physical link + my $is_pinnable = + Proxmox::Sys::Net::ip_link_is_physical($if) && !Proxmox::Sys::Net::iface_is_vf($name); + if ($is_pinnable) { + $ifs->{$name}->{pinned_id} = "${pinned_counter}"; + $pinned_counter++; + } + } + + return $ifs; +} + +# Returns a hash. +# +# { +# gateway4 => { +# dst => "default", +# gateway => , +# dev => , +# }, +# gateway6 => { +# dst => "default", +# gateway => , +# dev => , +# }, +# } +sub query_routes : prototype() { + my ($gateway4, $gateway6); + + log_info("query routes"); + my $route4 = from_json(qx/ip -4 --json route show/, { utf8 => 1 }); + for my $route (@$route4) { + if ($route->{dst} eq 'default') { + $gateway4 = { + dev => $route->{dev}, + gateway => $route->{gateway}, + }; + last; + } + } + + my $route6 = from_json(qx/ip -6 --json route show/, { utf8 => 1 }); + for my $route (@$route6) { + if ($route->{dst} eq 'default') { + $gateway6 = { + dev => $route->{dev}, + gateway => $route->{gateway}, + }; + last; + } + } + + my $routes; + $routes->{gateway4} = $gateway4 if $gateway4; + $routes->{gateway6} = $gateway6 if $gateway6; + + return $routes; +} + +# If `/etc/resolv.conf` fails to open this returns nothing. +# Otherwise it returns a hash: +# { +# domain => +# dns => [..], +# } +sub query_dns : prototype() { + log_info("query DNS from resolv.conf (managed by DHCP client)"); + open my $fh, '<', '/etc/resolv.conf' or return; + + my @dns; + my $domain; + while (defined(my $line = <$fh>)) { + if ($line =~ /^nameserver\s+(\S+)/) { + push @dns, $1; + } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) { + $domain = $1; + } + } + + my $output = { + domain => $domain, + @dns ? (dns => \@dns) : (), + }; +} + # Tries to detect the hostname for this system given via DHCP, if available. # The hostname _might_ also include the local domain name, depending on the # concrete DHCP server implementation. -- 2.53.0