all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: "Michael Köppl" <m.koeppl@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [RFC PATCH pve-installer 6/6] closes #5757: common: add checks for valid IPv4 address within subnet
Date: Tue, 22 Apr 2025 18:27:39 +0200	[thread overview]
Message-ID: <20250422162739.255641-7-m.koeppl@proxmox.com> (raw)
In-Reply-To: <20250422162739.255641-1-m.koeppl@proxmox.com>

Implement check if the address entered by the user is valid within the
given subnet, i.e. not a network address or broadcast address.

Partially closes [0].

[0] https://bugzilla.proxmox.com/show_bug.cgi?id=5757

Signed-off-by: Michael Köppl <m.koeppl@proxmox.com>
---
Some input / discussion would be much appreciated here, since this might
again be considered too restrictive. Multiple questions came up during
in-person discussion:
* Is check for broadcast address desired or is it considered a valid
configuration for PVE?
* Is IPv6 check necessary and if so, is allowing to set the address to a
broadcast address a valid setting for IPv6?

 proxmox-installer-common/src/utils.rs | 47 +++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 6 deletions(-)

diff --git a/proxmox-installer-common/src/utils.rs b/proxmox-installer-common/src/utils.rs
index 49f1c9f..3d79749 100644
--- a/proxmox-installer-common/src/utils.rs
+++ b/proxmox-installer-common/src/utils.rs
@@ -16,6 +16,10 @@ pub enum CidrAddressParseError {
     InvalidAddr(AddrParseError),
     /// The mask could not be parsed.
     InvalidMask(Option<ParseIntError>),
+    /// The IP address is a network address.
+    IsNetworkAddr,
+    /// The IP address is a broadcast address.
+    IsBroadcastAddr,
 }
 
 impl fmt::Display for CidrAddressParseError {
@@ -26,6 +30,10 @@ impl fmt::Display for CidrAddressParseError {
             }
             CidrAddressParseError::InvalidAddr(addr_parse_error) => format!("{addr_parse_error}"),
             CidrAddressParseError::InvalidMask(parse_int_error) => format!("{:?}", parse_int_error),
+            CidrAddressParseError::IsNetworkAddr => String::from("Address is a network address"),
+            CidrAddressParseError::IsBroadcastAddr => {
+                String::from("Address is a broadcast address")
+            }
         };
 
         write!(f, "Invalid CIDR: {msg}")
@@ -62,10 +70,12 @@ impl CidrAddress {
         let addr = addr.into();
 
         if mask > mask_limit(&addr) {
-            Err(CidrAddressParseError::InvalidMask(None))
-        } else {
-            Ok(Self { addr, mask })
+            return Err(CidrAddressParseError::InvalidMask(None));
         }
+
+        check_addr_valid_in_subnet(&addr, mask)?;
+
+        Ok(Self { addr, mask })
     }
 
     /// Returns only the IP address part of the address.
@@ -104,10 +114,12 @@ impl FromStr for CidrAddress {
             .map_err(|err| CidrAddressParseError::InvalidMask(Some(err)))?;
 
         if mask > mask_limit(&addr) {
-            Err(CidrAddressParseError::InvalidMask(None))
-        } else {
-            Ok(Self { addr, mask })
+            return Err(CidrAddressParseError::InvalidMask(None));
         }
+
+        check_addr_valid_in_subnet(&addr, mask)?;
+
+        Ok(Self { addr, mask })
     }
 }
 
@@ -134,6 +146,29 @@ fn mask_limit(addr: &IpAddr) -> usize {
     if addr.is_ipv4() { 32 } else { 128 }
 }
 
+fn check_addr_valid_in_subnet(addr: &IpAddr, mask: usize) -> Result<(), CidrAddressParseError> {
+    match &addr {
+        IpAddr::V4(addr_v4) => {
+            let num_host_bits = 32 - mask;
+            let host_part_mask = (1u32 << num_host_bits) - 1;
+
+            let ip_bits = u32::from_be_bytes(addr_v4.octets());
+
+            let network_addr = ip_bits & !host_part_mask;
+            let broadcast_addr = network_addr | host_part_mask;
+
+            if ip_bits == network_addr {
+                Err(CidrAddressParseError::IsNetworkAddr)
+            } else if ip_bits == broadcast_addr {
+                Err(CidrAddressParseError::IsBroadcastAddr)
+            } else {
+                Ok(())
+            }
+        }
+        IpAddr::V6(_) => Ok(()),
+    }
+}
+
 /// Possible errors that might occur when parsing FQDNs.
 #[derive(Debug, Eq, PartialEq)]
 pub enum FqdnParseError {
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

  parent reply	other threads:[~2025-04-22 16:28 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-22 16:27 [pve-devel] [PATCH installer 0/6] add early disk and network sanity checks Michael Köppl
2025-04-22 16:27 ` [pve-devel] [PATCH pve-installer 1/6] auto: add early answer file sanity check for RAID configurations Michael Köppl
2025-04-28 11:25   ` Christoph Heiss
2025-04-28 14:31     ` Michael Köppl
2025-04-29  8:26       ` Christoph Heiss
2025-04-29  9:32         ` Michael Köppl
2025-04-29  9:40           ` Christoph Heiss
2025-04-22 16:27 ` [pve-devel] [PATCH pve-installer 2/6] common: use get_min_disks as single source of truth for RAID config checks Michael Köppl
2025-04-28 11:48   ` Christoph Heiss
2025-04-28 15:36     ` Michael Köppl
2025-04-22 16:27 ` [pve-devel] [RFC PATCH pve-installer 3/6] close #5887: add sanity check for LVM swapsize and maxroot Michael Köppl
2025-04-28 12:00   ` Christoph Heiss
2025-04-29 11:30     ` Michael Köppl
2025-04-22 16:27 ` [pve-devel] [PATCH pve-installer 4/6] run rustfmt Michael Köppl
2025-04-23 11:56   ` Christoph Heiss
2025-04-25 12:22     ` Michael Köppl
2025-04-22 16:27 ` [pve-devel] [PATCH pve-installer 5/6] common: add more descriptive errors for invalid network configs Michael Köppl
2025-04-28 12:20   ` Christoph Heiss
2025-04-22 16:27 ` Michael Köppl [this message]
2025-04-28 10:22   ` [pve-devel] [RFC PATCH pve-installer 6/6] closes #5757: common: add checks for valid IPv4 address within subnet Christoph Heiss
2025-04-28 14:20     ` Michael Köppl
2025-04-28 12:25 ` [pve-devel] [PATCH installer 0/6] add early disk and network sanity checks Christoph Heiss
2025-04-29 14:14   ` Michael Köppl

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=20250422162739.255641-7-m.koeppl@proxmox.com \
    --to=m.koeppl@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal