From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 6C0E21FF09C for ; Mon, 02 Nov 2026 13:48:20 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 4FFB9218C3; Mon, 02 Nov 2026 13:47:21 +0100 (CET) From: Christoph Heiss To: pve-devel@lists.proxmox.com Subject: [PATCH installer v4 16/30] install: network: set up vlan on management interface if requested Date: Mon, 2 Nov 2026 13:45:22 +0100 Message-ID: <20261102124539.1008075-17-c.heiss@proxmox.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261102124539.1008075-1-c.heiss@proxmox.com> References: <20261102124539.1008075-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: 1793623625592 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.203 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust 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: J2HHF7W5XGJCYZXEFCM6BQ6IEOYFJ5Y6 X-Message-ID-Hash: J2HHF7W5XGJCYZXEFCM6BQ6IEOYFJ5Y6 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: Adds the necessary configuration to /etc/network/interfaces when a VLAN ID was configured by the user in the installation config. Depending on the rest of the network configuration (bridged, bonded), a separated (dotted) VLAN interface will be created and integrated in the rest of the configuration as needed. This follows our recommended approach for setting up such a network configuration. Signed-off-by: Christoph Heiss --- Had a discussion with @Stefan about this, and this way of configuring this was the best / most user-friendly we plotted out. Changes v3 -> v4: * no changes Changes v2 -> v3: * new patch Proxmox/Install.pm | 45 +++++++-- test/generate-ifupdown2-config.pl | 159 ++++++++++++++++++++++++++---- 2 files changed, 175 insertions(+), 29 deletions(-) diff --git a/Proxmox/Install.pm b/Proxmox/Install.pm index 9e167d8..b39df6f 100644 --- a/Proxmox/Install.pm +++ b/Proxmox/Install.pm @@ -889,14 +889,40 @@ sub generate_ifupdown2_config { && ($_->{protocol} // '') eq 'ra'; } $gateways6->@*; - if ($iso_env->{cfg}->{bridged_network}) { - if ($bond_enabled) { - $ifaces .= "auto $ethdev\n" # - . "iface $ethdev $ntype manual\n" # - . $bond_config; - } else { - $ifaces .= "iface $ethdev $ntype manual\n"; - } + my $vlan_enabled = Proxmox::Install::Config::get_mngmt_vlan_enabled(); + my $vlan_id = Proxmox::Install::Config::get_mngmt_vlan_opt('vid'); + + # the live environment is not on the management VLAN, so its routes say nothing about it + $is_gateway6_from_ra = 0 if $vlan_enabled; + + my $link_config = + $bond_enabled + ? "auto $ethdev\niface $ethdev $ntype manual\n$bond_config" + : "iface $ethdev $ntype manual\n"; + + my $get_vlan_config = sub { + my ($raw_device) = @_; + + return + "\nauto $raw_device.$vlan_id\niface $raw_device.$vlan_id $ntype static\n" + . "\taddress $cidr\n" + . "\tgateway $gateway\n"; + }; + + if ($iso_env->{cfg}->{bridged_network} && $vlan_enabled) { + $ifaces .= $link_config; + + $ifaces .= + "\nauto vmbr0\niface vmbr0 $ntype manual\n" + . "\tbridge-ports $ethdev\n" + . "\tbridge-stp off\n" + . "\tbridge-fd 0\n" + . "\tbridge-vlan-aware yes\n" + . "\tbridge-vids 2-4094\n"; + + $ifaces .= $get_vlan_config->('vmbr0'); + } elsif ($iso_env->{cfg}->{bridged_network}) { + $ifaces .= $link_config; $ifaces .= "\nauto vmbr0\niface vmbr0 $ntype static\n"; @@ -908,6 +934,9 @@ sub generate_ifupdown2_config { $ifaces .= "\tbridge-ports $ethdev\n" # . "\tbridge-stp off\n" # . "\tbridge-fd 0\n"; + } elsif ($vlan_enabled) { + $ifaces .= $bond_enabled ? $link_config : "auto $ethdev\n$link_config"; + $ifaces .= $get_vlan_config->($ethdev); } else { # if bonding is enabled, this becomes the bond interface stanza # otherwise, it's the physical interface stanza diff --git a/test/generate-ifupdown2-config.pl b/test/generate-ifupdown2-config.pl index f40689c..9ac9b3d 100755 --- a/test/generate-ifupdown2-config.pl +++ b/test/generate-ifupdown2-config.pl @@ -34,6 +34,10 @@ my sub set_config { Proxmox::Install::Config::merge({ mngmt_nic => $cfg{bond} ? 'bond0' : 'eno1', + mngmt_vlan => { + enabled => $cfg{vlan}, + vid => $cfg{vlan} ? 5 : 0, + }, mngmt_bond => { members => $cfg{bond} ? ['12:34:56:ab:cd:ef', 'ab:cd:ef:12:34:56'] : [], mode => 'active-backup', @@ -54,7 +58,7 @@ $expected source /etc/network/interfaces.d/* EOT - "$msg (bridge:$current_cfg->{bridge} bond:$current_cfg->{bond})", + "$msg (bridge:$current_cfg->{bridge} bond:$current_cfg->{bond} vlan:$current_cfg->{vlan})", ); } @@ -84,7 +88,7 @@ $proxmox_install_runenv->redefine( }; }, ); -set_config(bridge => 0, bond => 0); +set_config(bridge => 0, bond => 0, vlan => 0); Proxmox::Install::Config::merge({ cidr => '192.168.0.10/24', gateway => '192.168.0.1', @@ -93,18 +97,22 @@ Proxmox::Install::Config::merge({ ### # There's essentially a matrix to test w.r.t the overall networking setup: # -# +-------+--------+------+ -# | test# | bridge | bond | -# +=======+========+======+ -# | 1 | 0 | 0 | -# | 2 | 0 | 1 | -# | 3 | 1 | 0 | -# | 4 | 1 | 1 | -# +-------+--------+------+ +# +-------+--------+------+------+ +# | test# | bridge | bond | vlan | +# +=======+========+======+======+ +# | 1 | 0 | 0 | 0 | +# | 2 | 0 | 0 | 1 | +# | 3 | 0 | 1 | 0 | +# | 4 | 0 | 1 | 1 | +# | 5 | 1 | 0 | 0 | +# | 6 | 1 | 0 | 1 | +# | 7 | 1 | 1 | 0 | +# | 8 | 1 | 1 | 1 | +# +-------+--------+------+------+ ### # 1. -set_config(bridge => 0, bond => 0); +set_config(bridge => 0, bond => 0, vlan => 0); is_generated( < 0, bond => 1); +set_config(bridge => 0, bond => 0, vlan => 1); +is_generated( + < 0, bond => 1, vlan => 0); is_generated( < 1, bond => 0); +# 4. +set_config(bridge => 0, bond => 1, vlan => 1); +is_generated( + < 1, bond => 0, vlan => 0); is_generated( < 1, bond => 1); +# 6. +set_config(bridge => 1, bond => 0, vlan => 1); +is_generated( + < 1, bond => 1, vlan => 0); is_generated( < 1, bond => 1, vlan => 1); +is_generated( + < 'eno1', network_interface_pin_map => $pin_map, }); -set_config(bridge => 1, bond => 0); +set_config(bridge => 1, bond => 0, vlan => 0); is_generated( <redefine( }; }, ); -set_config(bridge => 1, bond => 0); +set_config(bridge => 1, bond => 0, vlan => 0); is_generated( < '2001:db8::1/64', gateway => 'fe80::1', }); -set_config(bridge => 0, bond => 1); +set_config(bridge => 0, bond => 1, vlan => 0); is_generated( < '192.168.0.10/24', gateway => '192.168.0.1', }); -set_config(bridge => 0, bond => 0); +set_config(bridge => 0, bond => 0, vlan => 0); $proxmox_install_runenv->redefine( get => sub { return { @@ -330,7 +435,7 @@ EOT , 'unpinnable names keep their kernel name', ); -set_config(bridge => 1, bond => 1); +set_config(bridge => 1, bond => 1, vlan => 0); Proxmox::Install::Config::merge({ mngmt_nic => 'bond0', mngmt_bond => { @@ -421,4 +526,16 @@ is( 'unknown bond member is an error', ); +for my $id (0, 1, 4095, 'abc', '1.5') { + eval { Proxmox::Install::Config::set_mngmt_vlan_opt('vid', $id) }; + like($@, qr/^invalid VLAN tag '\Q$id\E'/, "VLAN tag '$id' is rejected"); +} + +Proxmox::Install::Config::set_mngmt_vlan_opt('vid', '0005'); +is( + Proxmox::Install::Config::get_mngmt_vlan_opt('vid'), + 5, + 'VLAN tag with leading zeros is normalized', +); + done_testing(); -- 2.55.0