public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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





  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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal