Skip to content

network: support IPv6 in the portable profile and its egress policies - #455

Merged
Pedro Henrique Penna (ppenna) merged 5 commits into
devfrom
agents/fix-issue-280-add-tests-validate-implementation
Oct 10, 2026
Merged

Pedro Henrique Penna (ppenna) merged 5 commits into
devfrom
agents/fix-issue-280-add-tests-validate-implementation

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #280 with nanvix/openvmm#126: the portable microVM network profile is dual-stack, and egress policies allow or deny IPv6 networks next to IPv4 ones, with deny precedence, family-separated rules, port ranges, and a deny default that blocks IPv6 too. The guest kernel and init configure the IPv6 identity, run and sandbox pass IPv6 rules through, egress policy files compile IPv6 networks and exclusions in the MXC shape, where a rule without to matches both families, aci_edge_sandboxes expresses IPv6 rules in OpenVMM, and a new test-microvm scenario sends IPv4 and IPv6 traffic through allowed and denied paths.

Why

The portable profile was IPv4-only. The guest kernel had no IPv6, the guest had no IPv6 identity, and OpenVMM's egress rules could name only IPv4 destinations: an active policy dropped every IPv6 frame, while an inactive one passed IPv6 to Consomme unfiltered, so no policy could admit an IPv6 destination or say how IPv6 relates to its IPv4 rules. Egress policy files rejected IPv6 networks, and the aci_edge_sandboxes OpenVMM backend rejected them with policy_validation, although MXC's network.egress rules name IPv4 or IPv6 CIDRs and a rule without to matches both families.

Commits

Commit OpenVMM pin Change
24e187c86 openvmm: pin IPv6 in the portable network profile and its egress rules e5bf79776 The dual-stack NIC and IPv6 rules of nanvix/openvmm#126, including IPv6 port ranges and platform-tier snapshots of the dual-stack identity; the run and sandbox help, the usage reference, and the run guide's native rule form and DNS server; the cold-boot and machine ABI designs
f745c4201 guest: configure the portable profile's IPv6 identity CONFIG_IPV6 without SIT, asserted by the kernel build and nvx.py verify; the common init's static IPv6 address and default route; virtio-net checks the identity on every guest; host-loopback-policy finds its now dual-stack guest listener; the run guide, the usage reference, and the Ubuntu guest guide
07c7506c6 network: compile IPv6 networks in egress policies MXC-shaped and flat rules take IPv6 networks and same-family exclusions, a rule without to covers both families, and each family lowers, with port ranges as native range rules, within the shared 256-rule limits; the run guide's schema
18a8a69c6 aci_edge_sandboxes: express IPv6 egress rules in OpenVMM The OpenVMM backend expands IPv6 rules exactly, with a port range as one range rule per network, and a rule without destinations matches both families; the generic Cidr; the validation, fake OpenVMM, and end-to-end tests; the README
90efb165c test-microvm: verify IPv6 egress policies The ipv6-egress-policy scenario in the default suite, with MXC-shaped policy files, its unit tests, and the validation design

Each commit passes nvx.py verify, the NVX unit tests, and ruff on its own.

Behavior

--net 10.0.0.2/24 gives the guest fd00::a00:2/120 behind the gateway fd00::a00:1: the IPv6 identity embeds the IPv4 one in the unique local prefix fd00::/96. The guest configures it statically, without router advertisements, autoconfiguration from them, or duplicate address detection. Besides the kernel's EUI-64 link-local address, which OpenVMM admits only as the source of Neighbor Solicitations, such as Linux's unicast reachability probes, the guest's only IPv6 address is the unique local one, so standard address selection still prefers IPv4 for destinations that have both. The IPv6 gateway answers Neighbor Discovery and echo requests, serves DNS, and maps TCP and UDP to ::1 like the IPv4 gateway maps them to 127.0.0.1.

Policy IPv4 IPv6
--network-egress allow without rules Allowed Allowed, unfiltered
deny without rules Denied Denied
deny with 2001:db8:1::/64:tcp:443 allowed and 2001:db8:1::123 denied, the issue's example Denied TCP 443 to 2001:db8:1::/64, except 2001:db8:1::123
deny with 2001:db8::/32:tcp:8000-8010 allowed Denied TCP 8000 through 8010 to 2001:db8::/32
deny with ::/0 allowed Denied Every destination
deny with 0.0.0.0/0 allowed Every destination Denied
deny with an MXC rule for TCP 443 without to TCP 443 to every destination TCP 443 to every destination
allow with fd00::/8 denied Allowed Denied in fd00::/8
Legacy --allow-host, --block-host, or --allow-endpoint As before Denied

A rule matches only its own family, deny rules take precedence, an IPv6 ICMP rule matches ICMPv6, and IPv6 rules take port ranges like IPv4 ones. Neighbor Discovery of the IPv6 gateway is allowed like ARP of the IPv4 gateway: under an allow default or with any IPv6 allow rule. IPv6 packets with extension headers and IPv4-mapped destinations are never forwarded, since a dual-stack host socket would follow a mapped address to IPv4. The guest names the IPv4 gateway as its DNS server when the policy allows DNS to it, and otherwise the IPv6 gateway when the policy allows DNS to that one. A snapshot taken before this change keeps its policy digest and restores IPv4-only, and a platform-tier snapshot treats the IPv6 bootstrap tokens as platform configuration, like the IPv4 ones.

The ipv6-egress-policy scenario boots nine guests through nvx.py run with MXC-shaped policy files, with the guest at 192.0.2.2/24, so fd00::c000:202/120 behind fd00::c000:201. Each guest first finds its address and IPv6 default route in the kernel's tables, then probes TCP on a named port and on the next one, UDP on the named port, and ICMPv6 echo toward the IPv6 gateway, and TCP on the named port over IPv4. The scenario requires exactly the expected probes to pass and the expected DNS server, and its observations were identical on bare-metal KVM, MSHV, and WHP hosts and on a local WHP host:

Case Default Rules TCP6, named TCP6, next UDP6, named ICMPv6 TCP4, named DNS server
allow-all allow none ✓ ✓ ✓ ✓ ✓ 192.0.2.1
deny-all deny none ✗ ✗ ✗ ✗ ✗ none
allow-ipv6-tcp-port deny allow the IPv6 gateway on TCP, named port ✓ ✗ ✗ ✗ ✗ none
allow-ipv6-tcp-range deny allow the IPv6 gateway on TCP, named through next port ✓ ✓ ✗ ✗ ✗ none
allow-ipv6-udp-icmp deny allow the IPv6 gateway on UDP and on ICMP ✗ ✗ ✓ ✓ ✗ fd00::c000:201
deny-ipv6-tcp-port deny allow ::/0; deny the IPv6 gateway on TCP, named port ✗ ✓ ✓ ✓ ✗ fd00::c000:201
allow-tcp-port-without-to deny allow TCP, named port, without to ✓ ✗ ✗ ✗ ✓ none
allow-ipv4-only deny allow 0.0.0.0/0 ✗ ✗ ✗ ✗ ✓ 192.0.2.1
deny-ipv6-only allow deny fd00::/8 ✗ ✗ ✗ ✗ ✓ 192.0.2.1

The host observed each TCP connection and UDP datagram in the ✓ cells and none in the ✗ cells; Consomme answers echo requests to the gateway itself, so only the guest observes ICMPv6. The results file records each case's compiled rules: the range case lowers to the single native rule fd00::c000:201/128:tcp:20000-20001, and the rule without to to 0.0.0.0/0:tcp:20000 and ::/0:tcp:20000. The scenario then snapshots a guest whose only rule admits IPv6 TCP to the named port, rejects a restore with the IPv4 rule for that port, and restores with the original rule, after which the guest reaches the host over IPv6 again. Finally, OpenVMM rejects an IPv6 rule with a /129 prefix before boot.

Review

Validation

  • OpenVMM, on e5bf79776: cargo nextest for net_backend_resources, consomme, net_consomme, openvmm_defs, openvmm_helpers, openvmm_entry, and virtio_net: 574 passed on Windows with Rust 1.95.0, and 622 passed, 1 skipped, on Linux on prometheus30. clippy --all-targets, rustdoc with -D warnings, and xtask fmt are clean for every modified package, and openvmm_core and petri type-check. nanvix/openvmm has started none of its OpenVMM workflows since 2026-10-08, neither OpenVMM PR for [repo-status] NVX Daily Status Report - 2026-09-19 #126, even after reopening it, nor OpenVMM CI for the main push of Harden source provenance integrity #124 and ci: materialize Windows release provenance from run head #125, so this PR's OpenVMM jobs carry the exact-pin CI.
  • NVX, on this head: nvx.py verify; the validate-nvx unit suites, 858 tests on Windows and on Linux (prometheus32), with HOME pointing to an empty directory so that a test that commits in a temporary repository does not inherit this machine's SSH commit signing, and the adversarial suite; ruff; and pyright for Linux and Windows. On 5a71fd69a, whose tree differs from this head only in dev's later baseline data and doc: document source-collection prerequisites and release archives #458's documentation: shellcheck and shfmt with the pinned images, the host-skill suite, and per-commit checks, in which each commit passes nvx.py verify, the unit suites, and ruff on its own. For aci_edge_sandboxes, with aci_edge_sandboxes: add settings for image references #452's settings merged: fmt --all, clippy with -D warnings for both feature sets, the tests (204 with all features, 141 with the defaults), the artifact bundling check, rustdoc with -D warnings, and the Rust 1.89 MSRV check.
  • Local WHP and bare metal (prometheus32 KVM, prometheus30 MSHV, prometheus28 WHP), with OpenVMM built from the pin on each host, on 758e77f7f, whose commits git range-diff shows identical to this head's, before the rebase onto aci_edge_sandboxes: add settings for image references #452 and later: the microVM steps that CI runs, that is, the full default test-microvm suite, which includes ipv6-egress-policy, the l3-l4-egress-* scenarios, host-loopback-policy, network-snapshot, and snapshot-tiers; managed sandbox acceptance (managed-exec-config and sandbox-lifecycle); CI's Ubuntu and Azure Linux sets; on KVM, CI's extended Ubuntu set; and the ten nvx.py test-aci-edge-sandboxes hypervisor tests, including ipv6_network_policies_are_enforced. On 5a71fd69a, after the rebase onto aci_edge_sandboxes: add settings for image references #452, doc: list every packaged guest artifact in the release layout #456, and doc: describe the Azure Linux guest in the design #457, the aci hypervisor tests, which exercise aci_edge_sandboxes: add settings for image references #452's agent change, and ipv6-egress-policy passed again on all four hosts. ipv6-egress-policy's observations were identical everywhere. The kernel and initramfs artifacts were built with build-guest --guest all, which passed the new kernel configuration assertion, from this head's kernel and guest sources except the init comment; the Ubuntu distro layer and smoke scratch for sandbox acceptance came from CI.

Dependencies

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The branch currently conflicts with dev, and its IPv6 documentation incorrectly omits the automatically generated link-local address.

3 open findings
What changed in this PR

Adds dual-stack networking and IPv6-aware egress policies across NVX, its guest, OpenVMM integration, and sandbox API.

Changes:

  • Enables and configures deterministic guest IPv6 identities.
  • Supports family-separated IPv4/IPv6 policy compilation and enforcement.
  • Adds unit, snapshot, and cross-backend IPv6 acceptance coverage.
  • Pins OpenVMM PR #126 (4f7306e9b), stacked on open PR #124.
File Description
aci_edge_sandboxes/​README.md Documents dual-stack sandbox networking.
aci_edge_sandboxes/​model/​src/​cidr.rs Generalizes CIDR operations to IPv6.
aci_edge_sandboxes/​src/​openvmm/​config.rs Documents derived IPv6 configuration.
aci_edge_sandboxes/​src/​openvmm/​mod.rs Updates backend capability documentation.
aci_edge_sandboxes/​src/​openvmm/​network.rs Expands IPv6 policy rules for OpenVMM.
aci_edge_sandboxes/​tests/​openvmm_e2e.rs Adds IPv6 policy enforcement tests.
aci_edge_sandboxes/​tests/​openvmm_fake.rs Verifies generated IPv6 arguments.
aci_edge_sandboxes/​tests/​validation.rs Accepts valid IPv6 policy inputs.
doc/​design/​cold-boot.md Documents IPv6 boot parameters.
doc/​design/​integrated-implementation-status.md Records dual-stack implementation status.
doc/​design/​machine-and-device-abi.md Defines the IPv6 network ABI.
doc/​design/​validation.md Describes IPv6 validation coverage.
doc/​guests/​ubuntu-guest.md Updates Ubuntu networking requirements.
doc/​run.md Documents IPv6 identities and rules.
doc/​usage.md Updates CLI networking reference.
guest/​common/​init Configures the guest IPv6 address and route.
kernel/​config-microvm Enables IPv6 without SIT tunneling.
openvmm Pins IPv6-capable OpenVMM revision.
scripts/​nvx.py Updates IPv6 policy CLI help.
scripts/​nvx_tools/​build.py Validates required network kernel options.
scripts/​nvx_tools/​build_constants.py Defines required network configuration.
scripts/​nvx_tools/​egress_policy.py Compiles IPv4 and IPv6 policy files.
scripts/​nvx_tools/​microvm_test_scripts/​host-loopback-policy.sh.in Detects dual-stack listeners.
scripts/​nvx_tools/​microvm_test_scripts/​ipv6-egress-policy.sh.in Probes IPv6 policy behavior.
scripts/​nvx_tools/​microvm_test_scripts/​ipv6-egress-snapshot.sh.in Tests IPv6 across restore.
scripts/​nvx_tools/​microvm_test_scripts/​virtio-net.sh Checks the guest IPv6 identity.
scripts/​nvx_tools/​microvm_tests.py Adds the IPv6 acceptance scenario.
scripts/​nvx_tools/​release.py Verifies release kernel networking options.
scripts/​test_egress_policy.py Tests IPv6 policy compilation.
scripts/​test_microvm_tests.py Tests IPv6 scenario orchestration.
scripts/​test_nvx_tools.py Tests kernel configuration assertions.

🧠 Review effort: Balanced


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

Comment thread doc/design/machine-and-device-abi.md Resolved
Comment thread doc/run.md Resolved
Comment thread guest/common/init Resolved
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from 76491f3 to f6490cb Compare October 9, 2026 20:00
Copilot AI balanced review requested due to automatic review settings October 9, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Existing review findings show the guest and ABI documentation incorrectly claim automatic IPv6 address generation is disabled while link-local generation remains enabled.

3 open findings

🧠 Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from f6490cb to c34e2b4 Compare October 9, 2026 20:38
Copilot AI balanced review requested due to automatic review settings October 9, 2026 20:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The pin lacks documented IPv6 port-range support, and guest initialization still creates an unintended link-local address.

4 open findings

🧠 Review effort: Balanced

Comment thread doc/run.md Resolved
Copilot AI balanced review requested due to automatic review settings October 9, 2026 20:59
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from c34e2b4 to 3a35b9b Compare October 9, 2026 20:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The description and validation evidence reference an older OpenVMM revision than the current gitlink.

2 open findings
3 resolved since last review

🧠 Review effort: Balanced

Comment thread scripts/nvx_tools/microvm_tests.py Resolved
Copilot AI balanced review requested due to automatic review settings October 9, 2026 22:54
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from 3a35b9b to 758e77f Compare October 9, 2026 22:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The OpenVMM pin and validation claims are out of sync, while exact-head CI remains incomplete.

1 open finding
2 resolved since last review

🧠 Review effort: Balanced

Comment thread scripts/nvx_tools/microvm_tests.py Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The cross-repository networking and ABI change depends on open OpenVMM PR #126, while exact-head OpenVMM CI remains in progress.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 10, 2026 00:08
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from 5a71fd6 to 019122f Compare October 10, 2026 00:08
e5bf79776 (nanvix/openvmm#126), on top of the port ranges that dev pins,
makes the portable profile's NIC dual-stack. OpenVMM derives the IPv6
identity from the IPv4 one by embedding the guest and gateway addresses
in fd00::/96 with a prefix of 96 plus the IPv4 prefix, so --net
10.0.0.2/24 gives the guest fd00::a00:2/120 behind fd00::a00:1, and it
passes the identity to the guest as the virtnet_ip6= and virtnet_gw6=
kernel tokens, which callers may not supply. Consomme answers Neighbor
Discovery and ICMPv6 echo for the gateway, which shares the IPv4
gateway's MAC address, and maps the gateway's TCP and UDP flows onto ::1
when host loopback is allowed. It drops IPv4-mapped destinations, which
host dual-stack sockets would otherwise follow to IPv4 addresses that
the policy never checked, and no longer panics on a truncated ICMPv6
message.

--network-egress-allow and --network-egress-deny accept IPv6 networks in
the same CIDR[:PROTOCOL[:PORT[-PORT]]] form, such as
2001:db8::/32:tcp:443, 2001:db8::/32:tcp:8000-8010, or ::/0. A rule
matches only destinations of its own family, deny rules still take
precedence, and a deny default blocks the IPv6 traffic that no IPv6 rule
allows; an IPv6 ICMP rule matches ICMPv6. The guest may only solicit
neighbors: the gateway under an allow default or with an IPv6 allow
rule, like ARP for the IPv4 gateway, and other on-link addresses that
the rules admit. Packets with IPv6 extension headers are denied, and the
legacy policy modes deny all IPv6. The guest names the IPv4 gateway as
its DNS server when the policy allows DNS to it, and otherwise the IPv6
gateway when it allows DNS to that one, so a policy that allows DNS over
IPv6 alone still gives the guest a resolver. Policies bound to the IPv6
identity use canonical encoding version 4, and snapshots record the
identity, whose tokens platform-tier snapshots accept as platform
configuration; a snapshot that predates IPv6 restores IPv4-only with its
version-3 digest.

run and sandbox pass these rules to OpenVMM unchanged, so the help of
their --network-egress-allow and --network-egress-deny now names IPv4 or
IPv6 CIDRs, the usage reference and the run guide document the rule
families and the DNS server, and the cold-boot and machine ABI designs
document the reserved tokens, the derived identity, the DNS server, and
the policy.

The guest kernel has no IPv6 yet, so guests ignore the new tokens until
the next commit configures them. nvx.py verify passes with this pin.

Part of #280.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pinned OpenVMM gives the portable profile's NIC an IPv6 identity,
but the microVM kernel was built without IPv6, so guests had no IPv6
path for the identity or for IPv6 egress rules (#280).

The kernel now enables CONFIG_IPV6. It keeps CONFIG_IPV6_SIT, which IPv6
enables by default, disabled: SIT would add an sit0 device that
decapsulates IPv6 from IPv4 packets, so the guest's interfaces stay lo
and the NIC. The kernel build asserts the network configuration that the
portable profile needs, CONFIG_INET, CONFIG_IPV6, CONFIG_VIRTIO_NET, and
no SIT, in the generated configuration, and nvx.py verify checks it in
the checked-in and the built configurations.

The common init reads virtnet_ip6= and virtnet_gw6=. Before it brings
the NIC up, it disables router advertisements, address autoconfiguration
from them, and duplicate address detection on it, so that, besides the
kernel's link-local address, the static identity is the link's only IPv6
configuration and is usable as soon as it is assigned. After the IPv4
configuration, it assigns the address and installs the IPv6 default
route with busybox ip, which the Alpine, Ubuntu, and Azure Linux guests
all ship, and reports a failure on the console like the IPv4 steps.
Without the tokens, the guest configures IPv4 alone.

The virtio-net scenario, which the Ubuntu and Azure Linux smoke tests
also run, now checks the tokens and reads the kernel's address and route
tables, so each guest's userland must have configured the identity. With
IPv6, busybox nc listens on a dual-stack socket, which the kernel lists
among IPv6 sockets, so the host-loopback-policy scenario now looks for
its guest listener in /proc/net/tcp6 as well as /proc/net/tcp. Unit
tests cover the network configuration assertion, the checked-in
configuration, and that listener check. The run guide and the usage
reference describe the dual-stack NIC, and the Ubuntu guest guide no
longer lists IPv6 among the kernel features that it omits.

Part of #280.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
An egress policy file accepted only IPv4 destinations and exclusions, so
a policy could not allow or deny the IPv6 destinations that the pinned
OpenVMM now filters (#280).

A destination's cidr and its except entries may now be IPv6 networks,
in the MXC shape and in the flat one, and each exclusion must belong to
its destination's family. The destinations of each family in a rule
lower to native rules of that family, and a rule without destinations
now matches every IPv4 and IPv6 destination, as MXC specifies. Each
family is lowered on its own, with the same canonical, covered-only
widening, protocol coverage, port ranges, and rule-local exclusions as
IPv4, and both families share the 256-rule limit of each action. Lowered rules list
IPv4 before IPv6. Host bits are normalized like IPv4 ones, and IPv6
scope IDs are rejected, because native rules cannot carry them.

Unit tests cover IPv6 destinations with each selector, one allowed IPv6
network with a denied address, the flat form with IPv6, the separation
of the families between rules, within a rule's destinations, and in a
rule's exclusions, IPv6 exclusions, scope IDs, the shared rule budget,
and a rule without destinations, which now also lowers to ::/0. The run
guide's schema, the usage reference, and the help of
--network-egress-policy-file describe both families.

Part of #280.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The OpenVMM backend rejected IPv6 network.egress destinations with
policy_validation before provisioning, because OpenVMM's rules were
IPv4-only. MXC allows both families (#280).

The pinned OpenVMM accepts IPv6 rules, so the backend now expands them
exactly like IPv4 ones: exceptions, which must belong to their
destination's family, are subtracted from it, a port range becomes one
OpenVMM range rule per network, and any with a port or a port range
becomes a TCP and a UDP rule. A rule without destinations now matches
every destination of both families, 0.0.0.0/0 and ::/0, where it matched
IPv4 alone. The model's Cidr now represents either family, so both share
the bounded subtraction, and expansions beyond 256 OpenVMM rules are
still rejected.

Unit tests cover IPv6 expansion with exceptions and mixed families, a
rule without destinations, and IPv6 subtraction. The validation tests
now accept IPv6 rules before probing and reject oversized expansions
with separate port ranges instead, and the fake OpenVMM test checks that
IPv6 rules reach OpenVMM's command line beside IPv4 port ranges. The new
end-to-end test ipv6_network_policies_are_enforced probes TCP to the
gateway's DNS service over IPv4 and IPv6 under eight policies: an allow
default, an IPv6 and an IPv4 rule that each admit only their own family,
the ::/0 wildcard, a rule without destinations, an IPv6 rule for another
network, deny precedence for the IPv6 gateway, and an IPv6 deny rule
under an allow default. The README and the backend's documentation
describe the dual-stack NIC and the gateway that the guest names as its
DNS server.

Part of #280.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No scenario sent IPv6 traffic through the portable profile or checked
that egress rules filter it (#280).

The ipv6-egress-policy scenario, in the default suite, boots one guest
per case through the public nvx.py run, with an MXC-shaped egress policy
file and ingress denied. The guest first requires the virtnet_ip6= and
virtnet_gw6= tokens and finds its address and IPv6 default route in the
kernel's tables, so a guest without a working IPv6 stack fails before it
probes, and reports the DNS server that its resolver names, which the
harness compares with the case's: the IPv4 gateway when the policy
allows DNS to it, otherwise the IPv6 gateway when it allows DNS to that
one, and none otherwise. It then probes TCP on a named port and on the
next one, UDP on the named port, and ICMPv6 echo toward the IPv6
gateway, and TCP on the named port over IPv4, and the harness requires
exactly the case's traffic to pass: the guest reports each TCP response
and ping, and the host observes each TCP connection and UDP datagram on
ports that it reserves on ::1 and 127.0.0.1. The cases are an allow
default without rules, which must pass every probe, a deny default
without rules, which must block both families, IPv6 rules for one TCP
port, for a range of TCP ports, which lowers to one native range rule,
and for UDP and ICMP, deny precedence within ::/0, a rule for one TCP
port without destinations, which must admit that port in both families
as MXC specifies, IPv4 rules under a deny default that must not admit
IPv6, and an IPv6 deny rule under an allow default that must not block
IPv4.

The scenario then captures a guest whose only rule admits IPv6 TCP to
the named port once it has reached the host, requires OpenVMM to reject
a restore with the IPv4 rule for the same port, and restores the
snapshot with the original rule, after which the guest must reach the
host over IPv6 again. Finally, OpenVMM must reject an IPv6 rule with a
/129 prefix before boot. Each case's policy, its compiled OpenVMM rules,
and the observations are recorded in ipv6-egress-policy-results.json.

Unit tests cover the identity, the cases, their compiled rules and DNS
servers, each case's public command and policy file, the snapshot and
rejection commands, leaked and missed traffic on either side, an
unexpected DNS server, a restore that accepts a changed policy or misses
the host, both guest scripts under a POSIX shell, the port reservation
with real sockets and without IPv6 loopback, and the scenario's
dispatch. The validation design describes the scenario.

Fixes #280.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-280-add-tests-validate-implementation branch from 019122f to 90efb16 Compare October 10, 2026 00:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It coordinates security-sensitive networking changes across NVX and a large OpenVMM revision while exact-head CI remains in progress.

0 open findings

🧠 Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 10, 2026 00:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The cross-repository networking and snapshot changes depend on an unmerged OpenVMM revision while exact-head hypervisor CI remains in progress.

0 open findings

🧠 Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) merged commit dcde299 into dev Oct 10, 2026
32 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the agents/fix-issue-280-add-tests-validate-implementation branch October 10, 2026 02:49
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.

Support IPv6 networking and egress policies

2 participants