Skip to content

Comments

fix: continuing ipv6 effort, part 3: make the allocator family-aware#217

Merged
chet merged 1 commit intoNVIDIA:mainfrom
chet:ipv6_support_part3_still_at_it
Feb 10, 2026
Merged

fix: continuing ipv6 effort, part 3: make the allocator family-aware#217
chet merged 1 commit intoNVIDIA:mainfrom
chet:ipv6_support_part3_still_at_it

Conversation

@chet
Copy link
Contributor

@chet chet commented Feb 10, 2026

Description

Continuing the IPv6 support work. This is the third PR.

Work thus far has included:

  • Moving to IpNetwork and IpAddress throughout (#192).
  • Accepting IPv6 site prefixes and network segments. (#204).

And today: adding IPv6 support to IP address allocation.

Similar to the previous PRs, this does NOT enable IPv6 support (i.e. there won't be any DHCPv6 traffic coming in yet), but this DOES contribute to the foundational work of supporting IPv6. Eventually, we'll be able to start actually accepting requests and config for using IPv6, but there's a lot of backend work that needs to be done to get there. Trying to take it a layer at a time, and pieces of each layer at that. This is PR #3 of what is going to be many.

In any case, as mentioned, this PR is focused on adding IPv6 support to address allocation, specifically the IpAllocator, which is being made IPv6-capable by removing all IPv4-only guards, [re]introducing AddressSelectionStrategy to cleanly drive allocation behavior, and unblocking IPv6 allocation across all code paths, including:

  • Machine interfaces (underlay).
  • Instance addresses (tenant/overlay)

The goal is to prepare the allocation layer so that when DHCPv6 is added later, everything beneath it already works -- existing deployments and config are unaffected, as existing config is all parsed as IPv4 and uses [the same] IPv4 code paths and allocation strategies (which are verified with both existing AND new tests).

One of the particularly exciting highlights here is starting to use the AddressSelectionStrategy for something. Previously, we just passed around AddressSelectionStrategy::Automatic for everything. Now, we actually use it for address selection, by saying how we want to select the next IP.

  • ::NextAvailableIp is synonymous with ::Automatic -- we pick the next /32 or /128 depending on the address family.
  • ::NextAvailablePrefix(uint) gives us support for allocating a prefix that is wider than /32 or /128.

This allowed us to remove the prefix_length from the IpAllocator constructor. I was never fully happy with it being there. It seemed to make sense at the time, but it also seemed like there was something better we could do. Making it a part of AddressSelectionStrategy is actually what should have happened to begin with.

It's also worth noting the SQL next_machine_interface_v4_ip fast-path got removed here. It was a nice idea to have, but it didn't play well with dual-stacking interfaces (it actually got rid of dual stack support entirely when it was introduced), and it was only ever going to support IPv4. If we have performance concerns about quickly allocating many machines, we can definitely revisit it, but unfortunately it won't work as-is for now. The previous logic has been put back, with some enhancements for dual stacking.

Some callouts around backwards compatibility (in addition to tests):

DHCP is still IPv4-only

carbide-dhcp speaks DHCPv4 exclusively — it parses Ipv4Addr::from_str(&forge_response.address) on the response and calls set_yiaddr(allocated_address). Even if a tenant segment is configured to support IPv6 prefixes, the DHCP server will never request an IPv6 lease. The allocator is ready, but the trigger isn't there yet.

Machine interface (underlay) IPv4 AND IPv6 work end-to-end

If an operator configures an underlay/admin segment with IPv6 prefixes, machine_interface::create() will allocate IPv6 addresses via the IpAllocator. The hostname generation, DB writes, and uniqueness constraints all work correctly, in addition to IPv4 continuing to work (using the SQL-based IPv4 allocator when relevant).

Existing IPv4 behavior is untouched

Again, NextAvailableIp resolves to /32 for IPv4 — identical to the old hardcoded prefix_length: 32. All existing IPv4 unit and integration tests continue to pass unmodified.

Tests added:

Test Name Purpose
test_ip_allocation_ipv6 IPv6 /112 prefix allocates a /128 correctly and respects reserved addresses.
test_ipv6_allocation_single_address Allocates from /112 correctly w/ reserved and used IPs + verifies correct skip to ::4.
test_ipv6_num_free_capped num_free() caps at u32::MAX for large IPv6 prefixes (/64).
test_ipv6_build_allocated_networks Gateway, reserved, network/broadcast exclusions all use /128 for IPv6.
test_address_to_hostname_v4 Ensures IPv4 hostname formatting unchanged.
test_address_to_hostname_v6 Ensures IPv6 hostname formatting is expanded to full form with dashes.
test_address_to_hostname_v6_loopback Ensures ::1 correctly expands to 0000-0000-...-0001.
test_next_machine_interface_v4_ip Ensures existing IPv4 SQL fast-path still works.
test_machine_interface_create_with_ipv6_prefix Full end-to-end: creates underlay segment with IPv6 /112, allocates two machine interfaces, verifies sequential IPv6 addresses and correct hostnames.

Tests updated:

  • test_ip_allocation_ipv4_and_6
  • test_get_network_size

Signed-off-by: Chet Nichols III chetn@nvidia.com

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

Related Issues (Optional)

#84

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

Additional Notes

Continuing the [IPv6 support](NVIDIA/bare-metal-manager-core#84) work. This is the third PR.

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout.
- Accepting IPv6 site prefixes and network segments.

And today: adding IPv6 support to IP address allocation.

Similar to the previous PRs, this does NOT enable IPv6 support (i.e. there won't be any `DHCPv6` traffic coming in yet), but this DOES contribute to the foundational work of supporting IPv6. Eventually, we'll be able to start actually accepting requests and config for using IPv6, but there's a lot of backend work that needs to be done to get there. Trying to take it a layer at a time, and pieces of each layer at that. This is PR #3 of what is going to be many.

In any case, as mentioned, this PR is focused on adding IPv6 support to address allocation, specifically the `IpAllocator`, which is being made IPv6-capable by removing all IPv4-only
guards, [re]introducing `AddressSelectionStrategy` to cleanly drive allocation behavior, and unblocking IPv6 allocation across all code paths, including:
- Machine interfaces (underlay).
- Instance addresses (tenant/overlay)

The goal is to prepare the allocation layer so that when DHCPv6 is added later, everything beneath it already works -- existing deployments and config are unaffected, as existing config is all parsed as IPv4 and uses [the same] IPv4 code paths and allocation strategies (which are verified with both existing AND new tests).

One of the particularly exciting highlights here is starting to use the `AddressSelectionStrategy` for *something*. Previously, we just passed around `AddressSelectionStrategy::Automatic` for *everything*. Now, we actually use it for address selection, by saying how we want to select the next IP.
- `::NextAvailableIp` is synonymous with `::Automatic` -- we pick the next `/32` or `/128` depending on the address family.
- `::NextAvailablePrefix(uint)` gives us support for allocating a prefix that is wider than `/32` or `/128`.

This allowed us to remove the `prefix_length` from the `IpAllocator` constructor. I was never fully happy with it being there. It seemed to make sense at the time, but it also seemed like there was something better we could do. Making it a part of `AddressSelectionStrategy` is actually what should have happened to begin with.

It's also worth noting the SQL `next_machine_interface_v4_ip` fast-path got removed here. It was a nice idea to have, but it didn't play well with dual-stacking interfaces (it actually got rid of dual stack support entirely when it was introduced), and it was only ever going to support IPv4. If we have performance concerns about quickly allocating many machines, we can definitely revisit it, but unfortunately it won't work as-is for now. The previous logic has been put back, with some enhancements for dual stacking.

Some callouts around backwards compatibility (in addition to tests):

**DHCP is still IPv4-only**

`carbide-dhcp` speaks DHCPv4 exclusively — it parses `Ipv4Addr::from_str(&forge_response.address)` on the response and calls `set_yiaddr(allocated_address)`. Even if a tenant segment is configured to support IPv6 prefixes, the DHCP server will never request an IPv6 lease. The allocator is ready, but the trigger isn't there yet.

**Machine interface (underlay) IPv4 AND IPv6 work end-to-end**

If an operator configures an underlay/admin segment with IPv6 prefixes, `machine_interface::create()` will allocate IPv6 addresses via the `IpAllocator`. The hostname generation, DB writes, and uniqueness constraints all work correctly, in addition to IPv4 continuing to work (using the SQL-based IPv4 allocator when relevant).

**Existing IPv4 behavior is untouched**

Again, `NextAvailableIp` resolves to `/32` for IPv4 — identical to the old hardcoded `prefix_length: 32`. All existing IPv4 unit and integration tests continue to pass unmodified.

Tests added:

| Test Name | Purpose |
| --------- | ------- |
| `test_ip_allocation_ipv6` | IPv6 /112 prefix allocates a /128 correctly and respects reserved addresses. |
| `test_ipv6_allocation_single_address` | Allocates from /112 correctly w/ reserved and used IPs + verifies correct skip to `::4`. |
| `test_ipv6_num_free_capped` | `num_free()` caps at `u32::MAX` for large IPv6 prefixes (/64). |
| `test_ipv6_build_allocated_networks` | Gateway, reserved, network/broadcast exclusions all use /128 for IPv6. |
| `test_address_to_hostname_v4` | Ensures IPv4 hostname formatting unchanged. |
| `test_address_to_hostname_v6` | Ensures IPv6 hostname formatting is expanded to full form with dashes. |
| `test_address_to_hostname_v6_loopback` | Ensures `::1` correctly expands to `0000-0000-...-0001`. |
| `test_next_machine_interface_v4_ip` | Ensures existing IPv4 SQL fast-path still works. |
| `test_machine_interface_create_with_ipv6_prefix` | Full end-to-end: creates underlay segment with IPv6 /112, allocates two machine interfaces, verifies sequential IPv6 addresses and correct hostnames. |

Tests updated:
- `test_ip_allocation_ipv4_and_6`
- `test_get_network_size`

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet chet requested a review from a team as a code owner February 10, 2026 01:41
@chet chet changed the title fix: carbide ipv6 effort, part 3: make the allocator family-aware fix: continuing ipv6 effort, part 3: make the allocator family-aware Feb 10, 2026
Copy link
Contributor

@bcavnvidia bcavnvidia left a comment

Choose a reason for hiding this comment

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

Would be cool if instances would get a /64 or maybe if we could control the v6 prefix size they get.

@chet
Copy link
Contributor Author

chet commented Feb 10, 2026

Would be cool if instances would get a /64 or maybe if we could control the v6 prefix size they get.

Ohh yeah -- totally. Like, ::NextAvailableIp could become ::NextAvailableAssignment, and then v6 prefixes become configurable. ...or if they're configurable, then maybe everything just becomes ::NextAvailablePrefix anyway. Okay. I'll simmer and put up a follow-up OR around that.

@chet chet merged commit 39d4c31 into NVIDIA:main Feb 10, 2026
34 checks passed
// a /30 for a tenant instance (or, at least, something other than
// a /32). For now, hard-code 32 as the length -- the plan is to
// update the InstanceInterfaceConfig to request the prefix_length.
// TODO(chet): FNN will need to override prefix_length (e.g. /30
Copy link
Contributor

Choose a reason for hiding this comment

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

/31

chet added a commit that referenced this pull request Feb 11, 2026
## Description

This PR generalizes the VPC prefix allocator from `IPv4PrefixAllocator`
to `PrefixAllocator`, enabling it to work with both IPv4 and IPv6; it
removes the explicit IPv6 rejection guards that we had in place in the
instance allocation path. It is the next PR in a series focused on
enabling IPv6 support to Carbide (tracked in
[#84](#84))

For context, The VPC prefix allocator carves small subnets (FNN
"linknets") out of a larger VPC prefix and assigns them to network
segments during instance provisioning. Previously, it was entirely
hardcoded to IPv4 types (`Ipv4Addr`, `Ipv4Network`, and `u32`
arithmetic). With this PR, it has been generalized to `IpAddr` and
`IpNetwork`, with new `u128` arithmetic added to support both IPv4 and
IPv6.

This is a very similar approach taken for the `IpAllocator` work that
was done in
[#217](#217),
where we made the `IpAllocator` family-aware as well.

Primary changes to turn this into a `PrefixAllocator` include:
- The aforementioned `IpNetwork` and `IpAddr` changes (e.g.
`Ip4Network::new(addr, prefix)` simply becomes `IpNetwork::new(addr,
prefix)`).
- The aforementioned `u128` arithmetic changes, which is now the uniform
mechanism for doing math for addressing.
- Introducing a simple `max_prefix_bits()` to be the family-aware
replacement for `MAX_PREFIX_LEN`.
- Introducing a simple `networks_overlap()` to be the family-aware
replacement for `.overlaps()`.
- `total_network_possible` becomes `u128` instead of `u32`.

Additional changes include:
- `PrefixIterator` now operates against a `u128` instead of an
`Ipv4Addr`.
- `get_next_usable_address` now uses `IpAddr` and `IpNetwork` with
`u128` arithmetic.
- Gateway assignment is now family aware. Since IPv6 uses RAs, we won't
set one.
- `allocate_network()` now naturally extracts an `IpNetwork` of whatever
the family is.
- FNN "linknet" prefix length is family aware:
- IPv4 (/31) -- RFC 3021 ("Using 31-Bit Prefixes on IPv4 Point-to-Point
Links")
- IPv6 (/127) -- RFC 6164 ("Using 127-Bit IPv6 Prefixes on Inter-Router
Links")
- Added `total_linknet_segments` and `available_linknet_segments` stats
(which can also be displayed in the admin CLI) -- this is to work on
deprecating the `total_31_segments` and `available_31_segments`, but I
don't remove them yet.

**Considerations**

1. There's probably something we could do to make `PrefixAllocator`
generic over an address type/family trait, but it seemed better to do
`u128` arithmetic, since we can use the same arithmetic for both
families without needing a trait, e.g.
- IPv6 addresses are of course native `u128`.
- IPv4 addresses upcast to `u128`.
- All bit-shifting, comparison, and wrapping logic works identically.
- We're already doing similar work in `ip_allocator.rs`.

2. Introducing `networks_overlap()` could have also been turned into a
family-aware match on `Ipv4Network.overlaps()` or
`Ipv6Network.overlaps()` (since there's not one that exists for
`IpNetwork.overlaps()`, but I ended up doing this instead. If we want we
could do the other way.

The following tests were added:

| Test | Address Family | Description |
|------|----------------|-------------|
| `test_next_iter` | IPv4 | `/31` subnets step through `/29` VPC prefix
correctly. |
| `test_next_iter_overflow` | IPv4 | `/31` in `/30` wraps after 2
subnets. |
| `test_next_iter_ipv6` | IPv6 | `/127` subnets step through `/120` VPC
prefix (`fd00::100/120`). |
| `test_next_iter_ipv6_wrap_around` | IPv6 | `/127` in `/126` wraps
after 2 subnets. |
| `test_next_iter_ipv6_with_last_used` | IPv6 | Iterator resumes after
`last_used_prefix`, skipping already-allocated subnet. |

...and the existing `PrefixAllocator` tests were also updated to confirm
IPv4 behavior is unchanged.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>

## Type of Change
<!-- Check one that best describes this PR -->
- [x] **Add** - New feature or capability
- [ ] **Change** - Changes in existing functionality  
- [x] **Fix** - Bug fixes
- [ ] **Remove** - Removed features or deprecated functionality
- [x] **Internal** - Internal changes (refactoring, tests, docs, etc.)

## Related Issues (Optional)
#84

## Breaking Changes
- [ ] This PR contains breaking changes

<!-- If checked above, describe the breaking changes and migration steps
-->

## Testing
<!-- How was this tested? Check all that apply -->
- [x] Unit tests added/updated
- [x] Integration tests added/updated  
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes
<!-- Any additional context, deployment notes, or reviewer guidance -->

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 18, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84) work.

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).

This PR is a little less exciting, and removes a few additional `"if ip.is_ipv6()"` guards at the API layer. With the previous PRs in place to make the backend support dual stack environments, these guards are no longer needed: the downstream code (such as database queries and instance lookups) all work with `IpAddr` and `inet` columns natively.*

*Note: The DPA overlay/underlay guards in `dhcp/discover.rs` are **intentionally kept** here -- DPA will, for the foreseeable future, only support IPv4, because the Algo IP mechanism, and our inference of an underlay IP based on the `giaddr`, is IPv4 only; we don't even know how it will support IPv6 yet.*

Changes here include:
- Removed the IPv6 guard in the address finder `search()` function -- you can search IPv6 addresses for `StaticData`, `ResourcePools`, InstanceAddresses`, `MachineAddresses`, `BmcIp`, `ExploredEndpoint`, `LoopbackIp`, `NetworkSegment`, `RouteServers`, and `DpaAddresses` (even though DPA won't have an IPv6 address). The match on `NetworkSegment` did get small tweak -- it was hard-coded to `/32`, for single interfaces, so now it will do `/32` or `/128`. In practice, and as we get closer with IPv6, I'm not sure what prefix we'll allocate to single interfaces, so the `/128` will probably change here.
- Removed the IPv6 guard from `get_cloud_init_instructions()` -- all of the downstream code from here is dual stack and supports IPv6. BUT, I did leave a note that, even though everything downstream is IPv6-capable, that it doesn't really mean much until we integrate DHCPv6 support to actually hand out IPv6 addresses, which once we do it, things should "just work".
- A small tweak in `admin-cli` tests around parsing IP addresses. Just made it support IPv6.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 18, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84) work.

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).

This PR is a little less exciting, and removes a few additional `"if ip.is_ipv6()"` guards at the API layer. With the previous PRs in place to make the backend support dual stack environments, these guards are no longer needed: the downstream code (such as database queries and instance lookups) all work with `IpAddr` and `inet` columns natively.*

*Note: The DPA overlay/underlay guards in `dhcp/discover.rs` are **intentionally kept** here -- DPA will, for the foreseeable future, only support IPv4, because the Algo IP mechanism, and our inference of an underlay IP based on the `giaddr`, is IPv4 only; we don't even know how it will support IPv6 yet.*

Changes here include:
- Removed the IPv6 guard in the address finder `search()` function -- you can search IPv6 addresses for `StaticData`, `ResourcePools`, InstanceAddresses`, `MachineAddresses`, `BmcIp`, `ExploredEndpoint`, `LoopbackIp`, `NetworkSegment`, `RouteServers`, and `DpaAddresses` (even though DPA won't have an IPv6 address). The match on `NetworkSegment` did get small tweak -- it was hard-coded to `/32`, for single interfaces, so now it will do `/32` or `/128`. In practice, and as we get closer with IPv6, I'm not sure what prefix we'll allocate to single interfaces, so the `/128` will probably change here.
- Removed the IPv6 guard from `get_cloud_init_instructions()` -- all of the downstream code from here is dual stack and supports IPv6. BUT, I did leave a note that, even though everything downstream is IPv6-capable, that it doesn't really mean much until we integrate DHCPv6 support to actually hand out IPv6 addresses, which once we do it, things should "just work".
- A small tweak in `admin-cli` tests around parsing IP addresses. Just made it support IPv6.
- Improve how we determine interface prefix lengths -- instead of just hard-coding values for IPv4 or IPv6, I'm seeing if we can get some mileage out of the `IdentifyAddressFamily` trait that we have hanging out, with an `.interface_prefix_len()` function on that. I had also considered an `InterfacePrefix` enum with `InterfacePrefix::Ipv4` and `InterfacePrefix::Ipv6`, but kept going back and forth.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 18, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84) work.

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).

This PR is a little less exciting, and removes a few additional `"if ip.is_ipv6()"` guards at the API layer. With the previous PRs in place to make the backend support dual stack environments, these guards are no longer needed: the downstream code (such as database queries and instance lookups) all work with `IpAddr` and `inet` columns natively.*

*Note: The DPA overlay/underlay guards in `dhcp/discover.rs` are **intentionally kept** here -- DPA will, for the foreseeable future, only support IPv4, because the Algo IP mechanism, and our inference of an underlay IP based on the `giaddr`, is IPv4 only; we don't even know how it will support IPv6 yet.*

Changes here include:
- Removed the IPv6 guard in the address finder `search()` function -- you can search IPv6 addresses for `StaticData`, `ResourcePools`, InstanceAddresses`, `MachineAddresses`, `BmcIp`, `ExploredEndpoint`, `LoopbackIp`, `NetworkSegment`, `RouteServers`, and `DpaAddresses` (even though DPA won't have an IPv6 address). The match on `NetworkSegment` did get small tweak -- it was hard-coded to `/32`, for single interfaces, so now it will do `/32` or `/128`. In practice, and as we get closer with IPv6, I'm not sure what prefix we'll allocate to single interfaces, so the `/128` will probably change here.
- Removed the IPv6 guard from `get_cloud_init_instructions()` -- all of the downstream code from here is dual stack and supports IPv6. BUT, I did leave a note that, even though everything downstream is IPv6-capable, that it doesn't really mean much until we integrate DHCPv6 support to actually hand out IPv6 addresses, which once we do it, things should "just work".
- A small tweak in `admin-cli` tests around parsing IP addresses. Just made it support IPv6.
- Improve how we determine interface prefix lengths -- instead of just hard-coding values for IPv4 or IPv6, I'm seeing if we can get some mileage out of the `IdentifyAddressFamily` trait that we have hanging out, with an `.interface_prefix_len()` function on that. I had also considered an `InterfacePrefix` enum with `InterfacePrefix::Ipv4` and `InterfacePrefix::Ipv6`, but kept going back and forth.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit that referenced this pull request Feb 18, 2026
## Description

Continuing to chip away at [IPv6
support](#84) work.

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout
([#192](#192)).
- Accepting IPv6 site prefixes and network segments.
([#204](#204)).
- Making the IP allocator family-aware
([#217](#217)).
- Making the prefix allocator family-aware
([#237](#237)).

This PR is a little less exciting, and removes a few additional `"if
ip.is_ipv6()"` guards at the API layer. With the previous PRs in place
to make the backend support dual stack environments, these guards are no
longer needed: the downstream code (such as database queries and
instance lookups) all work with `IpAddr` and `inet` columns natively.*

*Note: The DPA overlay/underlay guards in `dhcp/discover.rs` are
**intentionally kept** here -- DPA will, for the foreseeable future,
only support IPv4, because the Algo IP mechanism, and our inference of
an underlay IP based on the `giaddr`, is IPv4 only; we don't even know
how it will support IPv6 yet.*

Changes here include:
- Removed the IPv6 guard in the address finder `search()` function --
you can search IPv6 addresses for `StaticData`, `ResourcePools`,
`InstanceAddresses`, `MachineAddresses`, `BmcIp`, `ExploredEndpoint`,
`LoopbackIp`, `NetworkSegment`, `RouteServers`, and `DpaAddresses` (even
though DPA won't have an IPv6 address). The match on `NetworkSegment`
did get small tweak -- it was hard-coded to `/32`, for single
interfaces, so now it will do `/32` or `/128`. In practice, and as we
get closer with IPv6, I'm not sure what prefix we'll allocate to single
interfaces, so the `/128` will probably change here.
- Removed the IPv6 guard from `get_cloud_init_instructions()` -- all of
the downstream code from here is dual stack and supports IPv6. BUT, I
did leave a note that, even though everything downstream is
IPv6-capable, that it doesn't really mean much until we integrate DHCPv6
support to actually hand out IPv6 addresses, which once we do it, things
should "just work".
- A small tweak in `admin-cli` tests around parsing IP addresses. Just
made it support IPv6.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>

## Type of Change
<!-- Check one that best describes this PR -->
- [x] **Add** - New feature or capability
- [x] **Change** - Changes in existing functionality  
- [ ] **Fix** - Bug fixes
- [ ] **Remove** - Removed features or deprecated functionality
- [x] **Internal** - Internal changes (refactoring, tests, docs, etc.)

## Related Issues (Optional)
<!-- If applicable, provide GitHub Issue. -->

## Breaking Changes
- [ ] This PR contains breaking changes

<!-- If checked above, describe the breaking changes and migration steps
-->

## Testing
<!-- How was this tested? Check all that apply -->
- [x] Unit tests added/updated
- [ ] Integration tests added/updated  
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes
<!-- Any additional context, deployment notes, or reviewer guidance -->

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 18, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).

So, *this* PR enables end-to-end `AAAA` record support (for IPv6 addresses) in the `carbide-dns` layer. Previously, all DNS records defaulted to type `A` regardless of the address family. With this change, the record type is now derived from the IP address (IPv6 addresses produce `AAAA` records, IPv4 addresses produce `A` records).

What's nice was the DNS implementation(s) were basically `AAAA`-aware throughout, sans the database views and some query defaults, e.g.
- In the `dns-record` crate, `DnsResourceRecordType::AAAA` existed, with `DNS_QTYPE_AAAA = 28`.
- The new PowerDNS backend stuff already correctly routes `AAAA` queries.
- API handlers have already been made agnostic to record type per the prevous PRs.
- Within the model, `q_type` was already a `String` and allowed for working with any type.

Changes here include:
- A SQL migration to switch to a family-aware `q_type`, which involved updating the existing DNS record views to return either `A` or `AAAA`, instead of just `A` like it used to. This includes query defaults/fallback -- if we can't figure it out from the `q_type` or `resource_record`, we'll always fall back to `A`.
- Support for IPv6 hostname formatting -- like how we have `-` separated hostnames for IPv4 addresses, doing something similar for IPv6. Totally not as nice as IPv4, and we can always adjust this later, but it's making me miss our fun friendly names. Examples are `fd00::1` → `fd00--1.example.com` and `2001:db8::8a2e:370:7334` → `2001-db8--8a2e-370-7334.example.com`.
- `AAAA` support in our `dns::legacy` integration -- we now match on `A` or `AAAA` and pass on the matching `q_type`, use `IpAddr` instead of `Ipv4Addr`, and use `RData::A` or `RData::AAAA` with the correct `RecordType`.

DNS Views Updated (for reference):
- `dns_records_adm_combined`
- `dns_records_bmc_host_id`
- `dns_records_bmc_dpu_id`
- `dns_records_shortname_combined`
- `dns_records_instance`

It's worth noting that we do have to DROP the `dns_records` view here and re-create it. However, since this happens during the migration, the API server isn't serving yet, AND since the data is just a view, we're not losing data. It just feels weird to DROP something to me, so I wanted to call it out, even though it has no impact (and has been tested with the existing + additional tests).

Updated the existing `test_dns` test to seet explicit `q_type` values to verify the IPv4 records are still correctly typed, and introduced a new `test_dns_aaaa` test which provides a full end-to-end test for `AAAA` records, where it:
- Creates a managed host.
- Inserts an IPv6 address (`fd00::1`) into `machine_interface_addresses` for the same interface, simulating a dual-stack environment.
- Queries the "admin" view (aka `{machine_id}.adm.{domain}`) and asserts:
  - Both record types returned (`A` + `AAAA`).
  - The `AAAA` record has `content = "fd00::1"`.
  - The `A` record is still present with an IPv4 address
- Queries the fun/friendly name view (`{hostname}.{domain}.`) and asserts the same:
  - `AAAA` record is present with correct content.
  - `A` record is also present.

This exercises the new migration's `dns_records` view (which I mention above), running the new migration and verifying everything is all a-ok.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).

So, *this* PR enables end-to-end `AAAA` record support (for IPv6 addresses) in the `carbide-dns` layer. Previously, all DNS records defaulted to type `A` regardless of the address family. With this change, the record type is now derived from the IP address (IPv6 addresses produce `AAAA` records, IPv4 addresses produce `A` records).

What's nice was the DNS implementation(s) were basically `AAAA`-aware throughout, sans the database views and some query defaults, e.g.
- In the `dns-record` crate, `DnsResourceRecordType::AAAA` existed, with `DNS_QTYPE_AAAA = 28`.
- The new PowerDNS backend stuff already correctly routes `AAAA` queries.
- API handlers have already been made agnostic to record type per the prevous PRs.
- Within the model, `q_type` was already a `String` and allowed for working with any type.

Changes here include:
- A SQL migration to switch to a family-aware `q_type`, which involved updating the existing DNS record views to return either `A` or `AAAA`, instead of just `A` like it used to. This includes query defaults/fallback -- if we can't figure it out from the `q_type` or `resource_record`, we'll always fall back to `A`.
- Support for IPv6 hostname formatting -- like how we have `-` separated hostnames for IPv4 addresses, doing something similar for IPv6. Totally not as nice as IPv4, and we can always adjust this later, but it's making me miss our fun friendly names. Examples are `fd00::1` → `fd00--1.example.com` and `2001:db8::8a2e:370:7334` → `2001-db8--8a2e-370-7334.example.com`.
- `AAAA` support in our `dns::legacy` integration -- we now match on `A` or `AAAA` and pass on the matching `q_type`, use `IpAddr` instead of `Ipv4Addr`, and use `RData::A` or `RData::AAAA` with the correct `RecordType`.

DNS Views Updated (for reference):
- `dns_records_adm_combined`
- `dns_records_bmc_host_id`
- `dns_records_bmc_dpu_id`
- `dns_records_shortname_combined`
- `dns_records_instance`

It's worth noting that we do have to DROP the `dns_records` view here and re-create it. However, since this happens during the migration, the API server isn't serving yet, AND since the data is just a view, we're not losing data. It just feels weird to DROP something to me, so I wanted to call it out, even though it has no impact (and has been tested with the existing + additional tests).

Updated the existing `test_dns` test to seet explicit `q_type` values to verify the IPv4 records are still correctly typed, and introduced a new `test_dns_aaaa` test which provides a full end-to-end test for `AAAA` records, where it:
- Creates a managed host.
- Inserts an IPv6 address (`fd00::1`) into `machine_interface_addresses` for the same interface, simulating a dual-stack environment.
- Queries the "admin" view (aka `{machine_id}.adm.{domain}`) and asserts:
  - Both record types returned (`A` + `AAAA`).
  - The `AAAA` record has `content = "fd00::1"`.
  - The `A` record is still present with an IPv4 address
- Queries the fun/friendly name view (`{hostname}.{domain}.`) and asserts the same:
  - `AAAA` record is present with correct content.
  - `A` record is also present.

This exercises the new migration's `dns_records` view (which I mention above), running the new migration and verifying everything is all a-ok.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).

*This* PR updates the DHCP discovery path to be family-aware, so that DHCPv4 continues to work correctly in dual-stack environments, AND so a future DHCPv6 server can reuse [some of] the same generic code path. Rather than filtering IPv6 out of the `machine_dhcp_records` view (which would prevent reuse), there is now an added an `IpAddressFamily` parameter to the query and derive the
family from the relay (`giaddr`) address; DHCPv4 relays are obviously IPv4, and DHCPv6 relays will be IPv6.

This parameter gets plumbed through, starting in `discover_dhcp`, and eventually gets leveraged as part of an updated Postgres query that does a `family(address)` check and compares it to the target `IpAddressFamily`, which we derive from the relay that relayed us the DHCP message. This ensures we can correctly support dual-stack environments.

I did also leave comments on some existing "DHCPv4" code where it made sense, explaining why it will never be for DHCPv6. There are a lot of differences between the two, and that's going to be worthy of a design doc pretty soon with regards to stateful vs. stateless DHCPv6, if we want to support one or the other or both, and how to implement it in a way that continues to keep dependencies on the underlying physical network down.

*Note that we're still guarding around DPA (just a little more obvious now), since DPA is IPv4 only for the foreseeable future.*

Existing tests continue to pass as expected, which is nice, and I added a new `test_dhcp_record_address-family` test which verifies that the updated `find_by_mac_address` query correctly filters by address family in a dual-stack scenario. The test:
1. Creates a machine via DHCPv4 discovery (gives an IPv4 relay address).
2. Inserts an IPv6 address (`fd00::42`) for the same interface to dual stack it.
3. Queries with `IpAddressFamily::Ipv4` — asserts only the IPv4 record is returned.
4. Queries with `IpAddressFamily::Ipv6` — asserts only the IPv6 record is returned.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).

*This* PR updates the DHCP discovery path to be family-aware, so that DHCPv4 continues to work correctly in dual-stack environments, AND so a future DHCPv6 server can reuse [some of] the same generic code path. Rather than filtering IPv6 out of the `machine_dhcp_records` view (which would prevent reuse), there is now an added an `IpAddressFamily` parameter to the query and derive the
family from the relay (`giaddr`) address; DHCPv4 relays are obviously IPv4, and DHCPv6 relays will be IPv6.

This parameter gets plumbed through, starting in `discover_dhcp`, and eventually gets leveraged as part of an updated Postgres query that does a `family(address)` check and compares it to the target `IpAddressFamily`, which we derive from the relay that relayed us the DHCP message. This ensures we can correctly support dual-stack environments.

I did also leave comments on some existing "DHCPv4" code where it made sense, explaining why it will never be for DHCPv6. There are a lot of differences between the two, and that's going to be worthy of a design doc pretty soon with regards to stateful vs. stateless DHCPv6, if we want to support one or the other or both, and how to implement it in a way that continues to keep dependencies on the underlying physical network down.

*Note that we're still guarding around DPA (just a little more obvious now), since DPA is IPv4 only for the foreseeable future.*

Existing tests continue to pass as expected, which is nice, and I added a new `test_dhcp_record_address-family` test which verifies that the updated `find_by_mac_address` query correctly filters by address family in a dual-stack scenario. The test:
1. Creates a machine via DHCPv4 discovery (gives an IPv4 relay address).
2. Inserts an IPv6 address (`fd00::42`) for the same interface to dual stack it.
3. Queries with `IpAddressFamily::Ipv4` — asserts only the IPv4 record is returned.
4. Queries with `IpAddressFamily::Ipv6` — asserts only the IPv6 record is returned.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).

*This* PR updates the DHCP discovery path to be family-aware, so that DHCPv4 continues to work correctly in dual-stack environments, AND so a future DHCPv6 server can reuse [some of] the same generic code path. Rather than filtering IPv6 out of the `machine_dhcp_records` view (which would prevent reuse), there is now an added an `IpAddressFamily` parameter to the query and derive the
family from the relay (`giaddr`) address; DHCPv4 relays are obviously IPv4, and DHCPv6 relays will be IPv6.

This parameter gets plumbed through, starting in `discover_dhcp`, and eventually gets leveraged as part of an updated Postgres query that does a `family(address)` check and compares it to the target `IpAddressFamily`, which we derive from the relay that relayed us the DHCP message. This ensures we can correctly support dual-stack environments.

I did also leave comments on some existing "DHCPv4" code where it made sense, explaining why it will never be for DHCPv6. There are a lot of differences between the two, and that's going to be worthy of a design doc pretty soon with regards to stateful vs. stateless DHCPv6, if we want to support one or the other or both, and how to implement it in a way that continues to keep dependencies on the underlying physical network down.

*Note that we're still guarding around DPA (just a little more obvious now), since DPA is IPv4 only for the foreseeable future.*

Existing tests continue to pass as expected, which is nice, and I added a new `test_dhcp_record_address-family` test which verifies that the updated `find_by_mac_address` query correctly filters by address family in a dual-stack scenario. The test:
1. Creates a machine via DHCPv4 discovery (gives an IPv4 relay address).
2. Inserts an IPv6 address (`fd00::42`) for the same interface to dual stack it.
3. Queries with `IpAddressFamily::Ipv4` — asserts only the IPv4 record is returned.
4. Queries with `IpAddressFamily::Ipv6` — asserts only the IPv6 record is returned.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit that referenced this pull request Feb 19, 2026
## Description

Continuing to chip away at [IPv6
support](#84)!

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout
([#192](#192)).
- Accepting IPv6 site prefixes and network segments.
([#204](#204)).
- Making the IP allocator family-aware
([#217](#217)).
- Making the prefix allocator family-aware
([#237](#237)).
- Removing some more API guards and enhancing the
`IdentifyAddressFamily` trait
([#324](#324)).
- Adding `AAAA` record support to DNS
([#332](#332)).

*This* PR updates the DHCP discovery path to be family-aware, so that
DHCPv4 continues to work correctly in dual-stack environments, AND so a
future DHCPv6 server can reuse [some of] the same generic code path.
Rather than filtering IPv6 out of the `machine_dhcp_records` view (which
would prevent reuse), there is now an added an `IpAddressFamily`
parameter to the query and derive the family from the relay (`giaddr`)
address; DHCPv4 relays are obviously IPv4, and DHCPv6 relays will be
IPv6.

This parameter gets plumbed through, starting in `discover_dhcp`, and
eventually gets leveraged as part of an updated Postgres query that does
a `family(address)` check and compares it to the target
`IpAddressFamily`, which we derive from the relay that relayed us the
DHCP message. This ensures we can correctly support dual-stack
environments.

I don't expect this to be it, either. There will presumably be a fair
amount of new code we'll need to add for DHCPv6 support, but I'm hoping
we can keep some of the generic flows generic enough to happily handle
both.

I also left comments on some existing "DHCPv4" code where it made sense,
explaining why it will never be for DHCPv6. There are a lot of
differences between the two, and that's going to be worthy of a design
doc pretty soon with regards to stateful vs. stateless DHCPv6, if we
want to support one or the other or both, and how to implement it in a
way that continues to keep dependencies on the underlying physical
network down.

*Note that we're still guarding around DPA (just a little more obvious
now), since DPA is IPv4 only for the foreseeable future.*

Existing tests continue to pass as expected, which is nice, and I added
a new `test_dhcp_record_address-family` test which verifies that the
updated `find_by_mac_address` query correctly filters by address family
in a dual-stack scenario. The test:
1. Creates a machine via DHCPv4 discovery (gives an IPv4 relay address).
2. Inserts an IPv6 address (`fd00::42`) for the same interface to dual
stack it.
3. Queries with `IpAddressFamily::Ipv4` — asserts only the IPv4 record
is returned.
4. Queries with `IpAddressFamily::Ipv6` — asserts only the IPv6 record
is returned.

And again, reminder that we're not done here. Still much to do!

Signed-off-by: Chet Nichols III <chetn@nvidia.com>

## Type of Change
<!-- Check one that best describes this PR -->
- [x] **Add** - New feature or capability
- [ ] **Change** - Changes in existing functionality  
- [x] **Fix** - Bug fixes
- [ ] **Remove** - Removed features or deprecated functionality
- [x] **Internal** - Internal changes (refactoring, tests, docs, etc.)

## Related Issues (Optional)
<!-- If applicable, provide GitHub Issue. -->

## Breaking Changes
- [ ] This PR contains breaking changes

<!-- If checked above, describe the breaking changes and migration steps
-->

## Testing
<!-- How was this tested? Check all that apply -->
- [x] Unit tests added/updated
- [x] Integration tests added/updated  
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes
<!-- Any additional context, deployment notes, or reviewer guidance -->

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
As part of the [IPv6 support](NVIDIA#84), I noticed a couple of `Ipv4Addr` things that no longer need to be explicitly IPv4 due to all of the other work that has been done thus far ([NVIDIA#192](NVIDIA#192), [NVIDIA#204](NVIDIA#204), [NVIDIA#217](NVIDIA#217), [NVIDIA#237](NVIDIA#237), [NVIDIA#324](NVIDIA#324), [NVIDIA#332](NVIDIA#332), and [NVIDIA#335](NVIDIA#335).

Added a simple `test_find_machine_by_ipv6` unit test to compliment the `test_find_machine` test so we have IPv4 and IPv6 coverage for `find_by_query`.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
As part of the [IPv6 support](NVIDIA#84), I noticed a couple of `Ipv4Addr` things that no longer need to be explicitly IPv4 due to all of the other work that has been done thus far ([NVIDIA#192](NVIDIA#192), [NVIDIA#204](NVIDIA#204), [NVIDIA#217](NVIDIA#217), [NVIDIA#237](NVIDIA#237), [NVIDIA#324](NVIDIA#324), [NVIDIA#332](NVIDIA#332), and [NVIDIA#335](NVIDIA#335).

Added a simple `test_find_machine_by_ipv6` unit test to compliment the `test_find_machine` test so we have IPv4 and IPv6 coverage for `find_by_query`.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit that referenced this pull request Feb 19, 2026
## Description

As part of the [IPv6
support](#84), I noticed a
couple of `Ipv4Addr` things that no longer need to be explicitly IPv4
due to all of the other work that has been done thus far
([#192](#192),
[#204](#204),
[#217](#217),
[#237](#237),
[#324](#324),
[#332](#332), and
[#335](#335).

Added a simple `test_find_machine_by_ipv6` unit test to compliment the
`test_find_machine` test so we have IPv4 and IPv6 coverage for
`find_by_query`.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>

## Type of Change
<!-- Check one that best describes this PR -->
- [x] **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.)

## Related Issues (Optional)
<!-- If applicable, provide GitHub Issue. -->

## Breaking Changes
- [ ] This PR contains breaking changes

<!-- If checked above, describe the breaking changes and migration steps
-->

## Testing
<!-- How was this tested? Check all that apply -->
- [x] Unit tests added/updated
- [ ] Integration tests added/updated  
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes
<!-- Any additional context, deployment notes, or reviewer guidance -->

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 19, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type alongside the existing `Ipv4` and `Integer` types. IPv6 address pools can now be defined via CIDR prefix or explicit range, just like IPv4 pools today.

No existing pools/logic are changed -- this is a new type in addition to the existing types, making the capability available for when IPv6 loopback pools, VTEP pools, or other IPv6 address pools are needed. Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new `resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()` functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of `fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to 255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4` produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is rejected.

Note that while this handles allocating individual IPv6 addresses, I think there will also be a real-world application for allocating prefixes (effectively prefix delegation from a new `ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit that referenced this pull request Feb 20, 2026
## Description

Continuing to chip away at [IPv6
support](#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout
([#192](#192)).
- Accepting IPv6 site prefixes and network segments.
([#204](#204)).
- Making the IP allocator family-aware
([#217](#217)).
- Making the prefix allocator family-aware
([#237](#237)).
- Removing some more API guards and enhancing the
`IdentifyAddressFamily` trait
([#324](#324)).
- Adding `AAAA` record support to DNS
([#332](#332)).
- Backend DHCP plumbing updates
([#335](#335)).

*This* PR adds `ResourcePoolType::Ipv6` as a new resource pool type
alongside the existing `Ipv4` and `Integer` types. IPv6 address pools
can now be defined via CIDR prefix or explicit range, just like IPv4
pools today.

_Note that while this handles allocating individual IPv6 addresses,
there will probably also be a real-world use case for allocating
prefixes (think: prefix delegation from a new
`ResourcePoolType::Ipv6Prefix`), so I'm planning on doing that next as a
separate PR._

No existing pools/logic are changed -- this is a new type in addition to
the existing types, making the capability available for when IPv6
loopback pools, VTEP pools, or other IPv6 address pools are needed.
Introduced a number of tests, including a test to verify the full
lifecycle works to define, populate, allocate, and release. Since the
resource pool architecture is designed around generics and string
storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was
all pretty straightfoward to implement.

Corresponding changes include:
- Adding the new `Ipv6` variant to the `ResourcePoolType` enum.
- Protobuf changes to make sure the `ResourcePoolType` proto enum has a
matching variant.
- An equivalent `ValueType` variant.
- A database migration to introduce `"ipv6"` as a new
`resource_pool_type`.
- Complimentary `expand_ipv6_prefix()` and `expand_ipv6_range()`
functions.
- Wiring in `define_by_prefix` and `define_by_range`.

All existing tests still passing, and 7 new tests added to cover various
angles of this, as well as a full "end to end" test to make sure we can
define a pool, allocate an address, and release it. Tests are as
follows:
- `test_ipv6_pool_define_allocate_release`: This tests the full
lifecycle.
- `test_ipv6_pool_define_by_range`: Define pool range of
`fd00::1`-`fd00::11` and verify correct number of addresses.
- `test_expand_ipv6_prefix_120`: Make sure a `/120` prefix expands to
255 addresses with the correct start/end IPs.
- `test_expand_ipv6_prefix_rejects_large_prefix`: Make sure a `/64` is
rejected because it's too large to enumerate.
- `test_expand_ipv6_prefix_rejects_ipv4`: Make sure an IPv4 prefix is
rejected for an IPv6 pool.
- `test_expand_ipv6_range`: Make sure a range of `fd00::1`–`fd00::4`
produces 3 correct addresses.
- `test_expand_ipv6_range_rejects_ipv4`: Make sure an IPv4 range is
rejected.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>

## Type of Change
<!-- Check one that best describes this PR -->
- [x] **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.)

## Related Issues (Optional)
<!-- If applicable, provide GitHub Issue. -->

## Breaking Changes
- [ ] This PR contains breaking changes

<!-- If checked above, describe the breaking changes and migration steps
-->

## Testing
<!-- How was this tested? Check all that apply -->
- [x] Unit tests added/updated
- [x] Integration tests added/updated  
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes
<!-- Any additional context, deployment notes, or reviewer guidance -->

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#344](NVIDIA#344)).

*This* PR is a follow-up to the `ResourcePoolType::Ipv6` introduction, adding `ResourcePoolType::Ipv6Prefix` as a new resource pool type alongside the existing `Ipv4`, `Ipv6`, and `Integer` types. Where the `Ipv6` type added support for pools of individual IPv6 addresses, this PR adds pools of entire sub-prefixes (e.g., carving/allocating multiple `/64` sub-prefixes out of a `/48`, or a range of `/120` between two `/120` prefixes.

The idea is we can have a large prefix block that we can effectively allocate delegations from -- whether it's network segment prefixes, point-to-point linknets, or loopback prefix blocks. An example would be we have a `/48`, and then delegate a bunch of `/64` prefixes out of it (65k delegations).

Similar to the previous change to introduce `ResourcePoolType::Ipv6`, this is a completely new type.

Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement, just like it was for `Ipv6`.

Corresponding changes include:
- The new `Ipv6Prefix` variant to `ResourcePoolType`, both in the protobuf and API model.
- A matching `Ipv6Prefix` variant to `ValueType` for the `resource_pool_type` in Postgres.
- An optional `delegate_prefix_len` to `ResourcePoolDef` for use when allocating sub-prefixes.
- A new `expand_ipv6_prefix_delegation` function, which takes a parent prefix (e.g. `"fd00:abcd::/48"`) and a `delegation_prefix_length` (e.g. `64`), and enumerates all `/64` subprefixes from that `/48`.
- A new `expand_ipv6_prefix_range` function, which takes two prefixes of matching size (e.g. `"fd00::100/120` to `"fd00:300/120"`) and enumerates all of the `/120` prefixes between them. This is to maintain the ability of "range" support for `Ipv6Prefix`, e.g.
  - `expand_ip_range("10.0.0.1", "10.0.0.4")` gives us 3 addresses.
  - `expand_ipv6_range("fd00::1", "fd00::4")` gives us 3 addresses.
  - `expand_ipv6_prefix_range("fd00::100/120", "fd00::300/120")` gives us 2 prefixes.
- Updated `define_by_prefix(...)` so that it now also matches on `ResourcePoolType::Ipv6Prefix`.

All existing tests still passing, and 10 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_expand_ipv6_prefix_delegation_48_to_64`: Tests delegation of `/64`'s out of a `/48` (65k prefixes).
- `test_expand_ipv6_prefix_delegation_112_to_120`: Tests delegation of `/120`'s out of a `/112` (256 prefixes).
- `test_expand_ipv6_prefix_delegation_rejects_invalid`: If the delegation prefix is less than the parent prefix, error.
- `test_expand_ipv6_prefix_delegation_rejects_too_large`: Making sure we reject things that would enumerate too many prefixes.
- `test_expand_ipv6_prefix_delegation_rejects_ipv4`: Yell if we get IPv4.
- `test_expand_ipv6_prefix_range`: Enumerate a range of `/120` and make sure the result is correct.
- `test_expand_ipv6_prefix_range_rejects_mismatched_len`: Yell if we get mismatched prefix lengths (and verify it's an `InvalidArgument`).
- `test_expand_ipv6_prefix_range_rejects_unaligned`: If the prefixes aren't aligned, yell with an `InvalidArgument`.
- `test_ipv6_prefix_pool_define_allocate_release`: Run through a full lifecycle: define a `/112` pool with `/120` delegations, then allocate + release.
- `test_ipv6_prefix_pool_define_by_range`: Do the same, but with a range of `/120`, and verify the 16 prefixes are allocated + released.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 20, 2026
Continuing to chip away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#344](NVIDIA#344)).

*This* PR is a follow-up to the `ResourcePoolType::Ipv6` introduction, adding `ResourcePoolType::Ipv6Prefix` as a new resource pool type alongside the existing `Ipv4`, `Ipv6`, and `Integer` types. Where the `Ipv6` type added support for pools of individual IPv6 addresses, this PR adds pools of entire sub-prefixes (e.g., carving/allocating multiple `/64` sub-prefixes out of a `/48`, or a range of `/120` between two `/120` prefixes.

The idea is we can have a large prefix block that we can effectively allocate delegations from -- whether it's network segment prefixes, point-to-point linknets, or loopback prefix blocks. An example would be we have a `/48`, and then delegate a bunch of `/64` prefixes out of it (65k delegations).

Similar to the previous change to introduce `ResourcePoolType::Ipv6`, this is a completely new type.

Introduced a number of tests, including a test to verify the full lifecycle works to define, populate, allocate, and release. Since the resource pool architecture is designed around generics and string storage (`ResourcePool<T>`, `.populate()`, and `.allocate()`), this was all pretty straightfoward to implement, just like it was for `Ipv6`.

Corresponding changes include:
- The new `Ipv6Prefix` variant to `ResourcePoolType`, both in the protobuf and API model.
- A matching `Ipv6Prefix` variant to `ValueType` for the `resource_pool_type` in Postgres.
- An optional `delegate_prefix_len` to `ResourcePoolDef` for use when allocating sub-prefixes.
- A new `expand_ipv6_prefix_delegation` function, which takes a parent prefix (e.g. `"fd00:abcd::/48"`) and a `delegation_prefix_length` (e.g. `64`), and enumerates all `/64` subprefixes from that `/48`.
- A new `expand_ipv6_prefix_range` function, which takes two prefixes of matching size (e.g. `"fd00::100/120` to `"fd00:300/120"`) and enumerates all of the `/120` prefixes between them. This is to maintain the ability of "range" support for `Ipv6Prefix`, e.g.
  - `expand_ip_range("10.0.0.1", "10.0.0.4")` gives us 3 addresses.
  - `expand_ipv6_range("fd00::1", "fd00::4")` gives us 3 addresses.
  - `expand_ipv6_prefix_range("fd00::100/120", "fd00::300/120")` gives us 2 prefixes.
- Updated `define_by_prefix(...)` so that it now also matches on `ResourcePoolType::Ipv6Prefix`.

All existing tests still passing, and 10 new tests added to cover various angles of this, as well as a full "end to end" test to make sure we can define a pool, allocate an address, and release it. Tests are as follows:
- `test_expand_ipv6_prefix_delegation_48_to_64`: Tests delegation of `/64`'s out of a `/48` (65k prefixes).
- `test_expand_ipv6_prefix_delegation_112_to_120`: Tests delegation of `/120`'s out of a `/112` (256 prefixes).
- `test_expand_ipv6_prefix_delegation_rejects_invalid`: If the delegation prefix is less than the parent prefix, error.
- `test_expand_ipv6_prefix_delegation_rejects_too_large`: Making sure we reject things that would enumerate too many prefixes.
- `test_expand_ipv6_prefix_delegation_rejects_ipv4`: Yell if we get IPv4.
- `test_expand_ipv6_prefix_range`: Enumerate a range of `/120` and make sure the result is correct.
- `test_expand_ipv6_prefix_range_rejects_mismatched_len`: Yell if we get mismatched prefix lengths (and verify it's an `InvalidArgument`).
- `test_expand_ipv6_prefix_range_rejects_unaligned`: If the prefixes aren't aligned, yell with an `InvalidArgument`.
- `test_ipv6_prefix_pool_define_allocate_release`: Run through a full lifecycle: define a `/112` pool with `/120` delegations, then allocate + release.
- `test_ipv6_prefix_pool_define_by_range`: Do the same, but with a range of `/120`, and verify the 16 prefixes are allocated + released.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 22, 2026
Still chipping away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6` type ([NVIDIA#344](NVIDIA#344)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#345](NVIDIA#345)).

All of that work has been on the "core" side -- the API, database, allocator, DNS, resource pools, etc. *This* PR is the first in a new series of changes focused on the agent side. The goal is, of course, to have IPv6 support plumbed through the agent, so it can make its way to HBN (via `nvue`) for confguring addresses, routes, ACLs, BGP, etc.

Similar to "core" efforts, I'm taking a simple starting point by adjusting the `loopback_ip` fields from `Ipv4Addr` to `IpAddr` across the agent's config structs (and corresponding CLI argument types). Similar to "core" changes like this being a noop, this is no different, and the test-verified rendered template output remains the same.

Changes here include:
- `InterfacesConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. This only gets used as `conf.loopback_ip.to_string()` to feed templates.
- `FrrConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. Same thing as above -- only used as `conf.loopback_ip.to_string()` to feed templates.
- `loopback_ip` going from `Ipv4Addr` to `IpAddr` in `NvueOptions`, `FrrOptions`, and `InterfacesOptions`. Since these are all parsed as `IpAddr::from_str`, they continued to work as-is.
- Dropping some comments, mostly about IPv4 things that will always be IPv4 (mainly around DHCPv4).

The existing tests (including `test_write_frr`) continues to pass, rendering templates as expected. IPv6 templates are NOT being introduced here yet -- for now, the important bit is widening types to `IpAddr` and making sure it continues to work as a drop-in with IPv6.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 22, 2026
Still chipping away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6` type ([NVIDIA#344](NVIDIA#344)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#345](NVIDIA#345)).

All of that work has been on the "core" side -- the API, database, allocator, DNS, resource pools, etc. *This* PR is the first in a new series of changes focused on the agent side. The goal is, of course, to have IPv6 support plumbed through the agent, so it can make its way to HBN (via `nvue`) for confguring addresses, routes, ACLs, BGP, etc.

Similar to "core" efforts, I'm taking a simple starting point by adjusting the `loopback_ip` fields from `Ipv4Addr` to `IpAddr` across the agent's config structs (and corresponding CLI argument types). Similar to "core" changes like this being a noop, this is no different, and the test-verified rendered template output remains the same.

Changes here include:
- `InterfacesConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. This only gets used as `conf.loopback_ip.to_string()` to feed templates.
- `FrrConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. Same thing as above -- only used as `conf.loopback_ip.to_string()` to feed templates.
- `loopback_ip` going from `Ipv4Addr` to `IpAddr` in `NvueOptions`, `FrrOptions`, and `InterfacesOptions`. Since these are all parsed as `IpAddr::from_str`, they continued to work as-is.
- Dropping some comments, mostly about IPv4 things that will always be IPv4 (mainly around DHCPv4).

The existing tests (including `test_write_frr`) continues to pass, rendering templates as expected. IPv6 templates are NOT being introduced here yet -- for now, the important bit is widening types to `IpAddr` and making sure it continues to work as a drop-in with IPv6.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 22, 2026
Still chipping away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6` type ([NVIDIA#344](NVIDIA#344)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#345](NVIDIA#345)).

All of that work has been on the "core" side -- the API, database, allocator, DNS, resource pools, etc. *This* PR is the first in a new series of changes focused on the agent side. The goal is, of course, to have IPv6 support plumbed through the agent, so it can make its way to HBN (via `nvue`) for configuring addresses, routes, ACLs, BGP, etc.

Similar to "core" efforts, I'm taking a simple starting point by widening from `Ipv4Addr` to `IpAddr` across some of the more low-hanging agent config structs, CLI argument types, and service address types. Just like previous changes being a noop, this is no different, and the test-verified rendered template output remains the same.

Changes included:
- `InterfacesConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. This only gets used as `conf.loopback_ip.to_string()` to feed templates.
- `FrrConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. Same thing as above -- only used as `conf.loopback_ip.to_string()` to feed templates.
- `loopback_ip` going from `Ipv4Addr` to `IpAddr` in `NvueOptions`, `FrrOptions`, and `InterfacesOptions`. Since these are all parsed via `IpAddr::from_str`, they continued to work as-is.
- `ServiceAddresses.pxe_ip` going from `Ipv4Addr` to `IpAddr`. Despite the field name, this is the UEFI HTTP boot server address, which would carry over to DHCPv6 just fine.
- `ServiceAddresses.ntpservers` going from `Vec<Ipv4Addr>` to `Vec<IpAddr>`. NTP servers are still a DHCPv6 "feature".
- At the `dhcp::build_server_config()` call site, added IPv4 filtering for `pxe_ip`, `ntpservers`, and `nameservers` (which was already filtered) before passing to the DHCPv4 config builder. `ServiceAddresses` now holds both families, but DHCPv4 options can only carry IPv4 addresses.
- Left comments on things that are intentionally staying IPv4 for now -- the `DhcpOptions` CLI struct (DHCPv4-specific), and the internal HBN bridge prefix parsing (`169.254.x.x` link-local plumbing, though an IPv6 equivalent may be needed in the future).

The existing tests (including `test_write_frr`) continue to pass, rendering templates as expected. IPv6 templates are NOT being introduced here yet -- for now, the important bit is widening types to `IpAddr` and making sure it continues to work as a drop-in with IPv4

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
chet added a commit to chet/bare-metal-manager-core that referenced this pull request Feb 22, 2026
Still chipping away at [IPv6 support](NVIDIA#84).

Work thus far has included:
- Moving to `IpNetwork` and `IpAddress` throughout ([NVIDIA#192](NVIDIA#192)).
- Accepting IPv6 site prefixes and network segments. ([NVIDIA#204](NVIDIA#204)).
- Making the IP allocator family-aware ([NVIDIA#217](NVIDIA#217)).
- Making the prefix allocator family-aware ([NVIDIA#237](NVIDIA#237)).
- Removing some more API guards and enhancing the `IdentifyAddressFamily` trait ([NVIDIA#324](NVIDIA#324)).
- Adding `AAAA` record support to DNS ([NVIDIA#332](NVIDIA#332)).
- Backend DHCP plumbing updates ([NVIDIA#335](NVIDIA#335)).
- Adding a new `ResourcePoolType::Ipv6` type ([NVIDIA#344](NVIDIA#344)).
- Adding a new `ResourcePoolType::Ipv6Prefix` type ([NVIDIA#345](NVIDIA#345)).

All of that work has been on the "core" side -- the API, database, allocator, DNS, resource pools, etc. *This* PR is the first in a new series of changes focused on the agent side. The goal is, of course, to have IPv6 support plumbed through the agent, so it can make its way to HBN (via `nvue`) for configuring addresses, routes, ACLs, BGP, etc.

Similar to "core" efforts, I'm taking a simple starting point by widening from `Ipv4Addr` to `IpAddr` across some of the more low-hanging agent config structs, CLI argument types, and service address types. Just like previous changes being a noop, this is no different, and the test-verified rendered template output remains the same.

Changes included:
- `InterfacesConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. This only gets used as `conf.loopback_ip.to_string()` to feed templates.
- `FrrConfig.loopback_ip` going from `Ipv4Addr` to `IpAddr`. Same thing as above -- only used as `conf.loopback_ip.to_string()` to feed templates.
- `loopback_ip` going from `Ipv4Addr` to `IpAddr` in `NvueOptions`, `FrrOptions`, and `InterfacesOptions`. Since these are all parsed via `IpAddr::from_str`, they continued to work as-is.
- `ServiceAddresses.pxe_ip` going from `Ipv4Addr` to `IpAddr`. Despite the field name, this is the UEFI HTTP boot server address, which would carry over to DHCPv6 just fine.
- `ServiceAddresses.ntpservers` going from `Vec<Ipv4Addr>` to `Vec<IpAddr>`. NTP servers are still a DHCPv6 "feature".
- At the `dhcp::build_server_config()` call site, added IPv4 filtering for `pxe_ip`, `ntpservers`, and `nameservers` (which was already filtered) before passing to the DHCPv4 config builder. `ServiceAddresses` now holds both families, but DHCPv4 options can only carry IPv4 addresses.
- Left comments on things that are intentionally staying IPv4 for now -- the `DhcpOptions` CLI struct (DHCPv4-specific), and the internal HBN bridge prefix parsing (`169.254.x.x` link-local plumbing, though an IPv6 equivalent may be needed in the future).

The existing tests (including `test_write_frr`) continue to pass, rendering templates as expected. IPv6 templates are NOT being introduced here yet -- for now, the important bit is widening types to `IpAddr` and making sure it continues to work as a drop-in with IPv4

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
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.

2 participants