Skip to content

fix(api-db,rpc): report ipv6 free ip counts beyond u32#3750

Merged
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2243
Jul 21, 2026
Merged

fix(api-db,rpc): report ipv6 free ip counts beyond u32#3750
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2243

Conversation

@chet

@chet chet commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

An IPv6 /64 contains 2^64 addresses, but get_network_size returned a u32 and saturated anything larger at u32::MAX. Allocation still worked; the free count reported to operators and clients did not. The database path also populated only the first prefix, so a dual-stack segment could report one real count and omit the other.

This change keeps free-IP accounting exact and per-prefix until it reaches a boundary that cannot represent the value. AddressCount carries either a normal u128 count or FullIpv6Space for 2^128, IpAllocator::num_free calculates each prefix independently, and Option<u128> keeps skipped accounting distinct from an exact zero.

Wire compatibility is preserved rather than requiring existing clients to move in lockstep. Protobuf tag 8 remains the legacy capped uint32; new clients can read free_ip_count_v2 and free_ip_count_saturated. The admin CLI prefers the wider value and prints Effectively unlimited when the count exceeds u64, while the existing usize metrics boundary saturates instead of wrapping.

Related issues

This supports #2243

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Focused coverage includes IPv4 /0 and IPv6 /0, /63, and /64 arithmetic; IPv6-first dual-stack SQL rows; skipped, exact-zero, legacy, v2, and saturated RPC counts; and CLI fallback and display behavior.

Local verification passes cargo make format-nightly, cargo make clippy, cargo make carbide-lints, cargo make check-workspace-deps, cargo make --no-workspace check-rest-core-proto-sync, affected Rust tests, and Core and REST protobuf build and branch-parent compatibility checks.

Additional Notes

The protobuf change is additive:

tag 8   uint32 free_ip_count          unchanged
tag 10  optional uint64 count         added
tag 11  saturation flag               added

NetworkSegmentMetrics still reports only prefixes[0], so dual-stack values depend on prefix order until the metric schema can emit one series per address family. That separate metrics design is intentionally outside this count fix.

Closes #2243

@chet
chet requested a review from a team as a code owner July 20, 2026 23:51
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Free-IP accounting now uses exact per-prefix wide arithmetic, optional u128 model values, and protobuf fields for v2 counts and saturation. Database population, RPC conversion, CLI display, metrics, creation requests, and tests are updated.

Changes

Free-IP accounting

Layer / File(s) Summary
Accounting contracts and RPC encoding
crates/api-model/..., crates/rpc/proto/..., rest-api/proto/..., crates/rpc/src/model/...
Free-IP values are optional and wide, with legacy, v2, and saturation representations.
Exact per-prefix allocation arithmetic
crates/api-db/src/ip_allocator.rs
IpAllocator calculates exact u128 free counts per prefix and handles full IPv6 space without u32 capping.
Per-prefix segment population
crates/api-db/src/network_segment.rs
Requested free-IP counts are populated independently for every prefix.
API display and metric handling
crates/admin-cli/..., crates/network-segment-controller/..., crates/api-core/src/tests/...
CLI and metrics handle wide, optional, and saturated counts; API tests cover included and excluded values.
Network-prefix request shape migration
crates/admin-cli/..., crates/api-core/..., crates/machine-a-tron/..., crates/test-harness/...
Creation payloads and fixtures initialize the new protobuf fields consistently.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SegmentQuery
  participant IpAllocator
  participant NetworkPrefixModel
  participant RpcEncoder
  participant AdminCLI
  SegmentQuery->>IpAllocator: calculate num_free(prefix.id)
  IpAllocator-->>SegmentQuery: return exact u128 count
  SegmentQuery->>NetworkPrefixModel: store optional num_free_ips
  NetworkPrefixModel->>RpcEncoder: encode free-IP count
  RpcEncoder-->>AdminCLI: return legacy, v2, and saturation fields
  AdminCLI->>AdminCLI: format free-IP count for display
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes widen free-count math to u128, add wider RPC fields, and preserve compatibility, satisfying #2243.
Out of Scope Changes check ✅ Passed The CLI, RPC, proto, model, and test updates all support the free-count fix and do not introduce unrelated behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly states the main fix: reporting IPv6 free IP counts beyond u32.
Description check ✅ Passed The description matches the changeset and accurately summarizes the widened free-IP accounting and compatibility updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-07-20 23:54:28 UTC | Commit: a705e8d

@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.

🧹 Nitpick comments (1)
crates/network-segment-controller/src/handler.rs (1)

196-200: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the conversion test table-driven.

This is a total input-to-output mapping; use check_values or value_scenarios! rather than individual assertions.

Proposed test structure
 #[cfg(test)]
 mod tests {
+    use carbide_test_support::{Check, check_values};
+
     use super::available_ip_metric_value;
 
     #[test]
     fn available_ip_metric_preserves_or_saturates_counts() {
-        assert_eq!(available_ip_metric_value(None), 0);
-        assert_eq!(available_ip_metric_value(Some(42)), 42);
-        assert_eq!(available_ip_metric_value(Some(u128::MAX)), usize::MAX);
+        struct Row {
+            count: Option<u128>,
+        }
+
+        check_values(
+            [
+                Check {
+                    scenario: "omitted count",
+                    input: Row { count: None },
+                    expect: 0,
+                },
+                Check {
+                    scenario: "representable count",
+                    input: Row { count: Some(42) },
+                    expect: 42,
+                },
+                Check {
+                    scenario: "overflowing count",
+                    input: Row {
+                        count: Some(u128::MAX),
+                    },
+                    expect: usize::MAX,
+                },
+            ],
+            |row| available_ip_metric_value(row.count),
+        );
     }
 }

As per coding guidelines, “Use table-driven tests for functions mapping inputs to outputs”; as per path instructions, new mapping logic must have table-driven coverage.

🤖 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/network-segment-controller/src/handler.rs` around lines 196 - 200,
Convert available_ip_metric_preserves_or_saturates_counts into a table-driven
test using the existing check_values or value_scenarios! helper. Represent the
None, ordinary value, and u128::MAX-to-usize::MAX mappings as test cases, then
run them through the helper instead of separate assert_eq! statements.

Sources: Coding guidelines, Path instructions

🤖 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.

Nitpick comments:
In `@crates/network-segment-controller/src/handler.rs`:
- Around line 196-200: Convert available_ip_metric_preserves_or_saturates_counts
into a table-driven test using the existing check_values or value_scenarios!
helper. Represent the None, ordinary value, and u128::MAX-to-usize::MAX mappings
as test cases, then run them through the helper instead of separate assert_eq!
statements.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8f9db13d-a260-4a79-b672-7c9cba8f3575

📥 Commits

Reviewing files that changed from the base of the PR and between dcdd5da and a705e8d.

⛔ Files ignored due to path filters (1)
  • rest-api/proto/core/gen/v1/nico_nico.pb.go is excluded by !**/*.pb.go, !**/gen/**, !rest-api/**/*.pb.go
📒 Files selected for processing (20)
  • crates/admin-cli/src/network_segment/create/args.rs
  • crates/admin-cli/src/network_segment/show/cmd.rs
  • crates/admin-cli/src/rpc.rs
  • crates/api-core/src/test_support/network_segment.rs
  • crates/api-core/src/tests/common/network_segment.rs
  • crates/api-core/src/tests/machine_dhcp.rs
  • crates/api-core/src/tests/network_segment.rs
  • crates/api-core/src/tests/network_segment_find.rs
  • crates/api-core/src/tests/network_segment_lifecycle.rs
  • crates/api-db/src/ip_allocator.rs
  • crates/api-db/src/network_segment.rs
  • crates/api-model/src/network_prefix.rs
  • crates/api-model/src/network_segment/mod.rs
  • crates/machine-a-tron/src/api_client.rs
  • crates/network-segment-controller/src/handler.rs
  • crates/rpc/proto/forge.proto
  • crates/rpc/src/model/network_prefix.rs
  • crates/rpc/src/model/network_segment.rs
  • crates/test-harness/src/network/controller.rs
  • rest-api/proto/core/src/v1/nico_nico.proto

@chet chet changed the title fix(api-db,rpc): stop capping ipv6 free ip counts at u32::MAX fix(api-db,rpc): report ipv6 free ip counts beyond u32 Jul 21, 2026
`get_network_size` saturated every address space larger than `u32`, and
network-segment queries populated only the first prefix. Allocation
still worked, but IPv6 and dual-stack capacity reporting was wrong.

Keep per-prefix accounting exact as `u128`, with an explicit
`FullIpv6Space` case for `2^128`. Preserve omitted versus exact-zero
counts, retain protobuf tag 8 for old clients, and expose the wider
value plus saturation state to new clients and the CLI.

The existing first-prefix metrics model remains a separate dual-stack
design question.

This supports NVIDIA#2243

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
// not requested; clients also see it absent in responses from older servers.
// When `free_ip_count_saturated` is true, this is `2^64 - 1` and the exact
// count is larger.
optional uint64 free_ip_count_v2 = 10;

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.

"Show me what backwards compatibility looks like"
"This is what backwards compatibility looks like"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

😆

@chet
chet merged commit fa0ad9b into NVIDIA:main Jul 21, 2026
119 checks passed
@chet
chet deleted the gh-issue-2243 branch July 21, 2026 17:52
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.

[IPv6] IPv6 segment free-counts are capped at u32::MAX

2 participants