Skip to content

fix(utils): parse and render IPv6 hosts in HostPortPair#3012

Open
CTOmari360 wants to merge 1 commit into
NVIDIA:mainfrom
CTOmari360:fix/host-port-pair-ipv6
Open

fix(utils): parse and render IPv6 hosts in HostPortPair#3012
CTOmari360 wants to merge 1 commit into
NVIDIA:mainfrom
CTOmari360:fix/host-port-pair-ipv6

Conversation

@CTOmari360

Copy link
Copy Markdown
Contributor

Description

HostPortPair::from_str split its input on every :, so any IPv6 literal host yielded more fields than the one or two it expected and was rejected as InvalidString. None of the standard ways to write an IPv6 host could be parsed:

  • bracketed host+port [2001:db8::1]:443
  • bracketed host-only [2001:db8::1]
  • bare literal 2001:db8::1 / ::1

…even though every IPv4 host:port parsed fine.

This is reachable from admin input: the site-explorer.bmc_proxy dynamic setting is parsed through this from_str (crates/api-core/src/handlers/api.rs), so an IPv6 BMC proxy could not be configured at all.

Display had the matching defect — HostAndPort("2001:db8::1", 443) rendered as the ambiguous 2001:db8::1:443, which then failed to parse back, breaking the Serialize/Deserialize round trip (the value is stored back out via Serialize).

Fix

  • from_str now accepts the canonical bracketed forms and treats a bare string that parses as an Ipv6Addr as a host-only value. The host is still stored unbracketed, so existing callers that read .host() and bracket it themselves (e.g. the nv-redfish / bmc-proxy URL builders) are unaffected.
  • Display brackets an IPv6 literal host in the host:port form so it is unambiguous and round-trips.
  • IPv4 and hostname inputs are unchanged.

Part of #2237 (IPv6 Bug Scan Bucket).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Testing

  • Extended the existing test_proxy_address_parsing table with bracketed/bare IPv6 cases (including malformed [2001:db8::1]:, [2001:db8::1, []).
  • Added test_ipv6_display_round_trips asserting Display output and the Displayfrom_str round trip.
  • Verified both new tests fail before this change (Display produced 2001:db8::1:443; from_str("[2001:db8::1]:8443") returned Err(InvalidString)) and pass after.
  • cargo test -p carbide-utils, cargo clippy -p carbide-utils --all-targets -- -D warnings, and cargo fmt -p carbide-utils -- --check all green.

`HostPortPair::from_str` split the input on every `:`, so any IPv6
literal host produced more than the one or two fields it expected and
was rejected as `InvalidString`. The canonical bracketed forms
(`[2001:db8::1]` and `[2001:db8::1]:443`) and bare IPv6 literals
(`2001:db8::1`, `::1`) could not be parsed at all, even though every
IPv4 `host:port` worked. This is reachable from admin input: the
`site-explorer.bmc_proxy` setting is parsed via this `from_str`
(crates/api-core/src/handlers/api.rs), so an IPv6 BMC proxy could not
be configured.

`Display` had the matching defect: `HostAndPort("2001:db8::1", 443)`
rendered as the ambiguous `2001:db8::1:443`, which then failed to parse
back, breaking the serialize/deserialize round trip (the value is
stored back through `Serialize`).

Fix `from_str` to accept the bracketed forms and to treat a bare
string that parses as an `Ipv6Addr` as a host-only value; the host is
still stored unbracketed so existing callers that read `.host()` (and
bracket it themselves) are unaffected. Bracket an IPv6 literal host in
`Display` so the `host:port` form is unambiguous and round-trips.
IPv4 and hostname inputs are unchanged.

Extend the parsing table with bracketed/bare IPv6 cases and add a
Display round-trip test; the bracketed `[2001:db8::1]:8443` case fails
before this change.

Part of NVIDIA#2237 (IPv6 Bug Scan Bucket).

Signed-off-by: Omar Refai <omar@refai.org>
@CTOmari360 CTOmari360 requested a review from a team as a code owner June 30, 2026 06:00
@copy-pr-bot

copy-pr-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • Bug Fixes
    • Improved address parsing to correctly handle IPv6 host values, including bracketed forms like [2001:db8::1]:8443.
    • IPv6 addresses now display in a clear bracketed format when paired with a port, avoiding ambiguity and preserving round-trip behavior.
    • Added coverage for invalid IPv6 input formats and confirmed formatting/parsing consistency with tests.

Walkthrough

IPv6 support is added to HostPortPair::from_str by replacing the prior colon-split approach with dedicated handling for bracketed ([ipv6]:port) and bare IPv6 literals. Display is updated to emit the bracketed form for IPv6 hosts. Unit tests are extended with success, failure, and round-trip cases.

IPv6 Parsing and Display

Layer / File(s) Summary
IPv6 parsing logic and Display formatting
crates/utils/src/host_port_pair.rs
Ipv6Addr is imported; from_str replaces colon-splitting with explicit bracketed-IPv6 and bare-IPv6 branches; Display emits [ipv6]:port when the host parses as an Ipv6Addr.
IPv6 test cases and round-trip assertions
crates/utils/src/host_port_pair.rs
InvalidString is added to test imports; parse tests are extended with bracketed host:port, host-only, bare IPv6, and invalid-bracket/port failure cases; a new test_ipv6_display_round_trips test asserts Displayfrom_str round-trips.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: IPv6 parsing and rendering in HostPortPair.
Description check ✅ Passed The description is directly aligned with the changeset and accurately explains the IPv6 parsing and display fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/utils/src/host_port_pair.rs`:
- Around line 71-96: The Redfish proxy URL builder in nv_redfish::mod::build
proxy URL logic is using the raw host from HostPortPair, so bare IPv6 literals
are rendered without brackets and produce an invalid URL. Update the URL
construction to bracket IPv6 hosts before interpolation, or switch the URL
assembly to use url::Url so host formatting is handled correctly. Use the
HostPortPair value from the existing host/port handling path to detect IPv6
literals and ensure the final https URL is valid for both IPv4/hostname and IPv6
cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0aac2bcb-8a2f-4e2e-9e45-8af1c98bae63

📥 Commits

Reviewing files that changed from the base of the PR and between 18af75a and e1d39f1.

📒 Files selected for processing (1)
  • crates/utils/src/host_port_pair.rs

Comment on lines +71 to +96
// A bracketed host is the canonical way to write an IPv6 literal, e.g.
// `[2001:db8::1]` or `[2001:db8::1]:443`. The host is stored without the
// brackets so callers can use it directly. The plain `split(':')` below
// can't handle this because an IPv6 address itself contains colons.
if let Some(rest) = s.strip_prefix('[') {
let (host, after) = rest.split_once(']').ok_or(InvalidString)?;
if host.is_empty() {
return Err(InvalidString);
}
return match after {
"" => Ok(HostPortPair::HostOnly(host.to_string())),
_ => {
let port = after.strip_prefix(':').ok_or(InvalidString)?;
let port = port
.parse::<u16>()
.map_err(|_| InvalidPort(port.to_string()))?;
Ok(HostPortPair::HostAndPort(host.to_string(), port))
}
};
}

// A bare (unbracketed) IPv6 literal is a host with no port; its colons
// would otherwise be misread as host/port separators below.
if s.parse::<Ipv6Addr>().is_ok() {
return Ok(HostPortPair::HostOnly(s.to_string()));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how create_bmc / proxy_address build URLs from HostPortPair host fields.
fd -t f 'mod.rs' crates/redfish | xargs rg -nP -C4 'format!\("https://' 
# Broader scan for any HostPortPair host interpolation lacking IPv6 bracketing.
rg -nP -C3 'HostPortPair::(HostAndPort|HostOnly|PortOnly)' --type=rust

Repository: NVIDIA/infra-controller

Length of output: 1249


Bracket IPv6 hosts before building the Redfish proxy URL crates/redfish/src/nv_redfish/mod.rs:149-151

HostPortPair stores IPv6 literals without brackets, but this interpolation uses the raw host string, so 2001:db8::1 becomes https://2001:db8::1:443, which is not a valid URL. Add brackets here or construct the URL via url::Url.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/utils/src/host_port_pair.rs` around lines 71 - 96, The Redfish proxy
URL builder in nv_redfish::mod::build proxy URL logic is using the raw host from
HostPortPair, so bare IPv6 literals are rendered without brackets and produce an
invalid URL. Update the URL construction to bracket IPv6 hosts before
interpolation, or switch the URL assembly to use url::Url so host formatting is
handled correctly. Use the HostPortPair value from the existing host/port
handling path to detect IPv6 literals and ensure the final https URL is valid
for both IPv4/hostname and IPv6 cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant