Skip to content

consomme: present IPv6 host peers inside the static subnet - #135

Merged
ppenna merged 1 commit into
mainfrom
agents/fix-issue-276-ipv6-host-peers
Oct 13, 2026
Merged

ppenna merged 1 commit into
mainfrom
agents/fix-issue-276-ipv6-host-peers

Conversation

@ppenna

@ppenna ppenna commented Oct 13, 2026

Copy link
Copy Markdown

Summary

Host-to-guest UDP over IPv6 loopback failed whenever a microVM egress policy was active, for example under --network-egress deny. Consomme presented IPv6 host peers to the guest at link-local virtual addresses (fe80::ff:fe00:N:1), and a guest UDP server on a wildcard socket answers a link-local peer from its own link-local address, which the egress policy rejects as a source. TCP was unaffected because it replies from the address that the SYN reached. IPv4 host peers already appear inside the guest's subnet.

  • under a static IPv6 identity, allocate IPv6 host-peer virtual addresses inside the static subnet, downward from its last address, skipping the gateway and the guest: fd00::a00:ff beside 10.0.0.254. IPv6 has no broadcast address, so even the /126 that a /30 IPv4 network derives keeps an address for a host peer
  • create_virtual_address follows suit, so one host address keeps one virtual address; endpoints without a static IPv6 identity keep link-local addresses
  • remove the two link-local cases that only served the old addresses: the gateway's Neighbor Advertisements for link-local virtual addresses (added in microVM: allow bidirectional host-loopback access #134) and the egress policy's ingress allowance for Neighbor Solicitations of link-local targets

Part of microsoft/nvx#276; follows #134.

Revision

Validation

On Windows:

  • cargo clippy --all-targets for consomme, net_consomme, and net_backend_resources: no lints
  • cargo doc --no-deps with -D warnings for the same packages
  • cargo nextest run --profile agent for net_backend_resources, consomme, net_consomme, openvmm_defs, and openvmm_entry: 476 passed
  • cargo xtask fmt --fix: clean
  • NVX on Windows/WHP: a host UDP client on ::1 gets the guest's reply under egress deny, which failed before this commit; the host-loopback-policy scenario, extended in Support bidirectional host-loopback access microsoft/nvx#512 with an IPv6 UDP exchange, passes with this commit and fails with a44fcc2a ("guest UDP server did not answer through ::1"); private-network-ingress, ipv6-egress-policy, directional-network-policy, network-snapshot, and endpoint-policy-snapshot pass; the ACI Edge Sandboxes end-to-end suite passes 18/18

Part of microsoft/nvx#276.

Consomme presents a host-local peer, such as host loopback, to the guest
at a virtual address, so that the guest replies through its NIC. IPv4
peers get an address inside the guest's subnet, but IPv6 peers got a
link-local address. A guest answers a link-local peer from its own
link-local address: TCP replies from the address that a SYN reached,
but UDP servers on a wildcard socket choose their source address for
the destination. Under a static IPv6 identity, whose egress policy
admits only the guest's address in the subnet as a source, the UDP
reply of a guest service to a host client on IPv6 loopback was
therefore dropped whenever a policy was active, as under egress deny.

Under a static IPv6 identity, allocate those virtual addresses inside
the static subnet instead, downward from its last address, which IPv6
does not reserve for broadcast, skipping the gateway and the guest:
fd00::a00:ff beside 10.0.0.254. Even the /126 that a /30 IPv4 network
derives thus keeps an address for a host peer. The guest then answers
from its address in the subnet, and resolves the peer like any neighbor
there. create_virtual_address follows suit, so a host address keeps one
virtual address. Endpoints without a static IPv6 identity keep
link-local addresses.

The link-local cases that served the old addresses go: the gateway no
longer answers Neighbor Solicitations for link-local virtual addresses,
and an egress policy that allows ingress no longer admits Neighbor
Solicitations for link-local targets. Cover the allocation, including
the smallest static subnet, the presentation and resolution of an IPv6
host peer, and the tightened policy with tests, and update the Guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 13, 2026 18:49
@ppenna
ppenna merged commit 7f225e2 into main Oct 13, 2026
@ppenna
ppenna deleted the agents/fix-issue-276-ipv6-host-peers branch October 13, 2026 18:50
ppenna added a commit to microsoft/nvx that referenced this pull request Oct 13, 2026
Pin nanvix/openvmm#135 at 7f225e2cd635bbd1e1839e587e659996433396c8,
one commit on a44fcc2a.

OpenVMM presented host-loopback peers to the guest at link-local IPv6
addresses, and a guest UDP server answered them from its own link-local
address, which the egress policy rejects as a source. A host client on
::1 therefore got no UDP reply whenever an egress policy was active,
such as under egress deny. Under the static IPv6 identity, OpenVMM now
presents IPv6 host peers inside the guest's subnet, as it presents IPv4
ones, so the guest answers from its address there.

Index the new pin in the roadmap and update the network ABI description.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

4 open findings
What changed in this PR

Updates Consomme’s IPv6 host-peer presentation so host loopback peers appear inside the guest’s static IPv6 subnet (instead of link-local), fixing UDP reply behavior under egress policies like --network-egress deny.

Changes:

  • Allocate IPv6 host-peer virtual addresses from the high end of the static IPv6 subnet (skipping gateway/guest), and update flow / virtual address creation to match.
  • Remove NDP/egress-policy special-cases that existed solely to support link-local host-peer virtual addresses.
  • Extend/update tests and documentation to validate and describe the new IPv6 behavior (including smallest /126 subnet).
File Description
vm/​devices/​net/​net_consomme/​consomme/​src/​tests/​static_ipv6.rs Updates static IPv6 tests to assert host peers are allocated inside the static subnet; adds coverage for /126.
vm/​devices/​net/​net_consomme/​consomme/​src/​ndp.rs Removes NDP on-link handling for legacy link-local virtual host-peer addresses.
vm/​devices/​net/​net_consomme/​consomme/​src/​local_addr_map.rs Adds allocation of host-peer IPv6 virtual addresses inside the static subnet from the high end; adds unit tests.
vm/​devices/​net/​net_consomme/​consomme/​src/​lib.rs Switches IPv6 local peer mapping and create_virtual_address to prefer static-subnet allocation when available.
vm/​devices/​net/​net_backend_resources/​src/​egress/​ipv6/​tests.rs Updates egress-policy tests for new subnet-based host-peer addressing and removes legacy link-local expectations.
vm/​devices/​net/​net_backend_resources/​src/​egress/​ipv6.rs Simplifies ingress resolvability logic to only consider on-link (subnet) neighbors for NDP.
Guide/​src/​reference/​backends/​consomme.md Updates docs to explain subnet-based host-peer virtual addresses and rationale for correct UDP behavior.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

/// Next available IPv6 host offset from the end of a static IPv6 subnet,
/// counting downward from 1 for its last address, which IPv6, unlike
/// IPv4, does not reserve for broadcast.
next_ipv6_offset_from_end: u8,
Comment on lines +219 to +227
if self.next_ipv6_offset_from_end == 0 {
tracelimit::warn_ratelimited!(
real_addr = %real_addr,
"Local IPv6 virtual address pool exhausted"
);
return None;
}
let offset_from_end = self.next_ipv6_offset_from_end;
self.next_ipv6_offset_from_end = offset_from_end.checked_add(1).unwrap_or(0);
Comment on lines +109 to +114
family, allocated downward from its high end, so that the guest answers them
from its address in that subnet. The endpoint's egress policy evaluates the
guest's traffic to such an address at the host address to which Consomme
routes it, and for host loopback at the gateway of its family, so a peer's
virtual address reaches no host-loopback port that the rules deny at the
gateway. Without ingress `allow`, OpenVMM appends
Comment on lines +545 to +562
// A static IPv6 identity's subnet is on link, as the IPv4
// subnet is, so a host peer presented there is answered from
// the guest's address in it, which a link-local peer would
// not be.
let virtual_ip = match (params.gateway_ipv6, params.client_ip_ipv6_routable) {
(Some(gateway), Some(client)) => local_addr_map.get_or_allocate_v6_in_subnet(
*v6.ip(),
gateway,
params.prefix_len_ipv6,
client,
),
_ => local_addr_map.get_or_allocate_v6(
*v6.ip(),
params.gateway_link_local_ipv6,
params.client_ip_ipv6,
params.client_ip_ipv6_routable,
),
};
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