From: Christoph Heiss <c.heiss@proxmox.com>
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 [thread overview]
Message-ID: <20260508184546.113293-3-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260508184546.113293-1-c.heiss@proxmox.com>
As they are dealing with network, they make more sense over there.
No functional changes.
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
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.
-#
-# {
-# <ifname> => {
-# mac => <mac address>,
-# index => <index>,
-# name => <ifname>,
-# state => <UP|DOWN|LOWERLAYERDOWN|DORMANT|TESTING|NOTPRESENT|UNKNOWN>,
-# pinned_id => <sequential numerical ID>,
-# driver => <driver name>,
-# addresses => [
-# family => <inet|inet6>,
-# address => <mac address>,
-# prefix => <length>,
-# ],
-# },
-# }
-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 => <ipv4>,
-# dev => <ifname>,
-# },
-# gateway6 => {
-# dst => "default",
-# gateway => <ipv6>,
-# dev => <ifname>,
-# },
-# }
-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 => <first dns entry>,
-#
-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 => <default upper limit for the ZFS ARC size in MiB>,
# disks => <see Proxmox::Sys::Block::hd_list()>,
# network => {
-# interfaces => <see query_netdevs()>,
-# routes => <see query_routes()>,
-# dns => <see query_dns()>,
+# interfaces => <see Proxmox::Sys::Net::query_netdevs()>,
+# routes => <see Proxmox::Sys::Net::query_routes()>,
+# dns => <see Proxmox::Sys::Net::query_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.
+#
+# {
+# <ifname> => {
+# mac => <mac address>,
+# index => <index>,
+# name => <ifname>,
+# state => <UP|DOWN|LOWERLAYERDOWN|DORMANT|TESTING|NOTPRESENT|UNKNOWN>,
+# pinned_id => <sequential numerical ID>,
+# driver => <driver name>,
+# addresses => [
+# family => <inet|inet6>,
+# address => <mac address>,
+# prefix => <length>,
+# ],
+# },
+# }
+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 => <ipv4>,
+# dev => <ifname>,
+# },
+# gateway6 => {
+# dst => "default",
+# gateway => <ipv6>,
+# dev => <ifname>,
+# },
+# }
+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 => <domain name or undefined, if not found>
+# dns => [<dns servers>..],
+# }
+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
next prev parent reply other threads:[~2026-05-08 18:46 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 18:44 [PATCH installer 0/8] add IPv6 SLAAC and v6-only support Christoph Heiss
2026-05-08 18:44 ` [PATCH installer 1/8] install: drop trivial fromjs() wrapper and use JSON::from_json() Christoph Heiss
2026-05-08 18:44 ` Christoph Heiss [this message]
2026-05-08 18:44 ` [PATCH installer 3/8] gui: use run_env->{network} instead of old run_env->{ipconf} Christoph Heiss
2026-05-08 18:44 ` [PATCH installer 4/8] sys: net: drop the now-unused `ipconf` runtime environment configuration Christoph Heiss
2026-05-08 18:44 ` [PATCH installer 5/8] sys: net: allow up to /128 netmask for IPv6 Christoph Heiss
2026-05-08 18:44 ` [PATCH RFC installer 6/8] sys: net: ignore ipv6 nameservers with zone identifiers Christoph Heiss
2026-05-08 18:44 ` [PATCH installer 7/8] common: options: rework network address setup to handle ipv6-only Christoph Heiss
2026-05-08 18:44 ` [PATCH installer 8/8] unconfigured: try to retrieve IPv6 SLAAC addresses on startup Christoph Heiss
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260508184546.113293-3-c.heiss@proxmox.com \
--to=c.heiss@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox