Skip to content

[management, client] Add IPv6 overlay support#5631

Open
lixmal wants to merge 66 commits intomainfrom
proto-ipv6-overlay
Open

[management, client] Add IPv6 overlay support#5631
lixmal wants to merge 66 commits intomainfrom
proto-ipv6-overlay

Conversation

@lixmal
Copy link
Copy Markdown
Collaborator

@lixmal lixmal commented Mar 19, 2026

Describe your changes

This PR implements IPv6 overlay support in client and management.

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

netbirdio/docs#667

Summary by CodeRabbit

  • New Features

    • Broad IPv6 overlay support across status, peer info, UI (Disable IPv6 checkbox), CLI flags, and persistent config.
    • SSH can bind/listen on IPv6 overlay addresses; dual‑stack device and netstack handling improved.
    • Reverse DNS (PTR) generation now supports IPv6.
  • Bug Fixes

    • Improved dual‑stack DNS, routing, conntrack, MTU and device address assignment with graceful IPv6 fallbacks.
  • Refactor

    • Modernized/typed address handling and compact IP prefix encoding for robust v4/v6 support.
  • Tests

    • Added/updated unit and integration tests covering IPv6 flows and compact encoding.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 19, 2026

Important

Review skipped

Too many files!

This PR contains 161 files, which is 11 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8897f41-d30b-4f78-adb4-86602e288c62

📥 Commits

Reviewing files that changed from the base of the PR and between fa77768 and e19d0c7.

⛔ Files ignored due to path filters (1)
  • client/proto/daemon.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (161)
  • .gitignore
  • client/android/client.go
  • client/android/route_command.go
  • client/anonymize/anonymize.go
  • client/anonymize/anonymize_test.go
  • client/cmd/ssh.go
  • client/cmd/ssh_test.go
  • client/firewall/iptables/acl_linux.go
  • client/firewall/iptables/manager_linux.go
  • client/firewall/iptables/router_linux.go
  • client/firewall/iptables/rule.go
  • client/firewall/iptables/state_linux.go
  • client/firewall/manager/firewall.go
  • client/firewall/manager/routerpair.go
  • client/firewall/nftables/acl_linux.go
  • client/firewall/nftables/addr_family_linux.go
  • client/firewall/nftables/external_chain_monitor_integration_linux_test.go
  • client/firewall/nftables/external_chain_monitor_linux.go
  • client/firewall/nftables/external_chain_monitor_linux_test.go
  • client/firewall/nftables/manager_linux.go
  • client/firewall/nftables/manager_linux_test.go
  • client/firewall/nftables/router_linux.go
  • client/firewall/nftables/router_linux_test.go
  • client/firewall/uspfilter/allow_netbird_windows.go
  • client/firewall/uspfilter/conntrack/common.go
  • client/firewall/uspfilter/conntrack/common_test.go
  • client/firewall/uspfilter/conntrack/icmp.go
  • client/firewall/uspfilter/conntrack/icmp_test.go
  • client/firewall/uspfilter/filter.go
  • client/firewall/uspfilter/filter_bench_test.go
  • client/firewall/uspfilter/filter_filter_test.go
  • client/firewall/uspfilter/filter_test.go
  • client/firewall/uspfilter/forwarder/endpoint.go
  • client/firewall/uspfilter/forwarder/forwarder.go
  • client/firewall/uspfilter/forwarder/icmp.go
  • client/firewall/uspfilter/forwarder/tcp.go
  • client/firewall/uspfilter/forwarder/udp.go
  • client/firewall/uspfilter/hooks_filter.go
  • client/firewall/uspfilter/localip.go
  • client/firewall/uspfilter/localip_bench_test.go
  • client/firewall/uspfilter/localip_test.go
  • client/firewall/uspfilter/nat.go
  • client/firewall/uspfilter/nat_bench_test.go
  • client/firewall/uspfilter/nat_test.go
  • client/firewall/uspfilter/tracer.go
  • client/iface/configurer/usp.go
  • client/iface/device/adapter.go
  • client/iface/device/device_android.go
  • client/iface/device/device_windows.go
  • client/iface/iface.go
  • client/iface/wgproxy/bind/proxy.go
  • client/internal/acl/manager.go
  • client/internal/connect.go
  • client/internal/debug/debug.go
  • client/internal/debug/debug_test.go
  • client/internal/dns/network_manager_unix.go
  • client/internal/dns/service.go
  • client/internal/dns/service_listener.go
  • client/internal/dns/upstream.go
  • client/internal/dns/upstream_android.go
  • client/internal/dns/upstream_general.go
  • client/internal/dns/upstream_ios.go
  • client/internal/dnsfwd/manager.go
  • client/internal/ebpf/ebpf/dns_fwd_linux.go
  • client/internal/ebpf/manager/manager.go
  • client/internal/engine.go
  • client/internal/engine_ssh.go
  • client/internal/engine_test.go
  • client/internal/lazyconn/activity/listener_bind.go
  • client/internal/netflow/conntrack/conntrack.go
  • client/internal/peer/status.go
  • client/internal/peer/status_test.go
  • client/internal/profilemanager/config.go
  • client/internal/relay/relay.go
  • client/internal/rosenpass/manager.go
  • client/internal/rosenpass/manager_test.go
  • client/internal/routemanager/client/client.go
  • client/internal/routemanager/dnsinterceptor/handler.go
  • client/internal/routemanager/dynamic/route.go
  • client/internal/routemanager/dynamic/route_ios.go
  • client/internal/routemanager/fakeip/fakeip.go
  • client/internal/routemanager/fakeip/fakeip_test.go
  • client/internal/routemanager/ipfwdstate/ipfwdstate.go
  • client/internal/routemanager/manager.go
  • client/internal/routemanager/notifier/notifier_android.go
  • client/internal/routemanager/notifier/notifier_ios.go
  • client/internal/routemanager/notifier/notifier_other.go
  • client/internal/routemanager/server/server.go
  • client/internal/routemanager/systemops/systemops.go
  • client/internal/routemanager/systemops/systemops_generic.go
  • client/internal/routemanager/systemops/systemops_linux.go
  • client/ios/NetBirdSDK/client.go
  • client/proto/daemon.proto
  • client/server/network.go
  • client/server/server.go
  • client/server/setconfig_test.go
  • client/server/trace.go
  • client/ssh/config/manager.go
  • client/ssh/config/manager_test.go
  • client/ssh/proxy/proxy.go
  • client/ssh/server/port_forwarding.go
  • client/ssh/server/server.go
  • client/system/info.go
  • client/ui/client_ui.go
  • client/ui/network.go
  • client/wasm/cmd/main.go
  • client/wasm/internal/ssh/client.go
  • combined/cmd/config.go
  • management/internals/modules/reverseproxy/service/manager/l4_port_test.go
  • management/internals/modules/reverseproxy/service/manager/manager.go
  • management/internals/modules/reverseproxy/service/manager/manager_test.go
  • management/internals/shared/grpc/conversion.go
  • management/internals/shared/grpc/server.go
  • management/server/account.go
  • management/server/account/manager.go
  • management/server/account/manager_mock.go
  • management/server/account_test.go
  • management/server/activity/codes.go
  • management/server/group.go
  • management/server/group_ipv6_test.go
  • management/server/group_test.go
  • management/server/http/handlers/accounts/accounts_handler.go
  • management/server/http/handlers/accounts/accounts_handler_test.go
  • management/server/http/handlers/dns/nameservers_handler.go
  • management/server/http/handlers/dns/nameservers_handler_test.go
  • management/server/http/handlers/groups/groups_handler_test.go
  • management/server/http/handlers/peers/peers_handler.go
  • management/server/http/handlers/peers/peers_handler_test.go
  • management/server/http/testing/testing_tools/tools.go
  • management/server/mock_server/account_mock.go
  • management/server/peer.go
  • management/server/peer/peer.go
  • management/server/peer/peer_test.go
  • management/server/peer_test.go
  • management/server/policy_test.go
  • management/server/route_test.go
  • management/server/settings/manager.go
  • management/server/settings/manager_mock.go
  • management/server/store/sql_store.go
  • management/server/store/sql_store_get_account_test.go
  • management/server/store/sql_store_test.go
  • management/server/store/sqlstore_bench_test.go
  • management/server/store/store.go
  • management/server/store/store_mock.go
  • management/server/types/account.go
  • management/server/types/account_components.go
  • management/server/types/account_test.go
  • management/server/types/firewall_rule.go
  • management/server/types/firewall_rule_test.go
  • management/server/types/ipv6_groups_test.go
  • management/server/types/network.go
  • management/server/types/network_test.go
  • management/server/types/networkmap_comparison_test.go
  • management/server/types/networkmap_components.go
  • management/server/types/networkmap_components_correctness_test.go
  • management/server/types/networkmap_golden_test.go
  • management/server/types/networkmapbuilder.go
  • management/server/types/networkmapbuilder_route_fw_test.go
  • management/server/types/settings.go
  • management/server/user.go
  • proxy/cmd/proxy/cmd/debug.go

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds IPv6 overlay support and a DisableIPv6 toggle across client, device, DNS, netflow, SSH, status, UI, and protobuf schemas; introduces compact netip encoding utilities; migrates WireGuard iface/address types to carry IPv6; and adds targeted nolint suppressions for a backward-compatible PeerIP field.

Changes

Cohort / File(s) Summary
Backward-compatibility lint suppressions
client/internal/acl/manager.go, client/internal/debug/debug.go, management/internals/shared/grpc/conversion.go, management/server/peer_test.go
Added //nolint:staticcheck comments on legacy PeerIP usages preserved for backward compatibility.
Protobuf & schema updates
shared/management/proto/management.proto, client/proto/daemon.proto
Added DisableIPv6 flags, PeerCapability enum/capabilities reporting, address_v6 for peer config, deprecated FirewallRule.PeerIP, and new firewall fields (customProtocol, sourcePrefixes).
Compact IP encoder + tests
shared/netiputil/compact.go, shared/netiputil/compact_test.go
New compact encode/decode helpers for netip.Prefix/netip.Addr (5-byte v4, 17-byte v6) with round-trip and error tests; v4-mapped unmapping handled.
wgaddr and address handling
client/iface/wgaddr/address.go, client/iface/wgaddr/address_test_helpers.go
Extended wgaddr.Address with IPv6 fields and helpers (HasIPv6, IPv6String, SetIPv6FromCompact, ClearIPv6, Prefix); added MustParseWGAddress test helper.
Iface API & constructors
client/iface/iface.go, client/internal/iface_common.go, client/iface/iface_new*.go, client/iface/iface_test.go
Switched WGIFace address types from string → wgaddr.Address; UpdateAddr accepts wgaddr.Address; platform constructors stop parsing addresses; tests updated to use MustParseWGAddress.
Device implementations & dual-stack assignment
client/iface/device/... (darwin, ios, netstack, windows, wg_link_linux.go, wg_link_freebsd.go, device_usp_unix.go, kernel_module_*.go)
Added IPv6 assignment attempts with graceful fallback (clear IPv6 on failure), dual-stack MTU/route handling, changed assignAddr to pointer in call sites, renamed USPDevice→TunDevice, and reorganized non-Linux kernel-module build files.
Netstack TUN multi-address
client/iface/netstack/tun.go
NetStack TUN now stores and accepts a slice of addresses (v4 and optional v6) instead of a single address.
Engine, connect, and runtime IPv6 logic
client/internal/engine.go, client/internal/connect.go, client/internal/engine_test.go, client/internal/engine_ssh.go
EngineConfig.WgAddr typed to wgaddr.Address with DisableIPv6 flag; IPv6 change detection, AllowedIPs split/filtering, IPv6 reverse-zone support, SSH IPv6 listener/DNAT handling, and updated tests.
Network change listener
client/internal/listener/network_change.go, client/internal/connect_android_default.go
Added SetInterfaceIPv6(string) callback to NetworkChangeListener and a no-op Android implementation.
DNS reverse/PTR and platform plumbing
client/internal/dns.go, client/internal/dns_test.go, client/internal/dns/* (darwin, network_manager_unix.go, systemd_linux.go, upstream_ios.go, server_test.go)
Extended PTR/reverse-zone generation to IPv6 (ip6.arpa nibble format), include AAAA in PTR collection, adjusted systemd/NetworkManager inputs for IPv6, and comprehensive tests.
Netflow / conntrack / logger
client/internal/netflow/types/types.go, client/internal/netflow/conntrack/conntrack.go, client/internal/netflow/logger/logger.go, client/internal/netflow/manager.go, client/internal/netflow/logger/logger_test.go
Added ICMPv6 constant; conntrack and logger consider IPv6 overlay membership; logger accepts IPv6 prefix and uses unified overlay detection.
SSH server & config changes
client/ssh/server/server.go, client/ssh/config/manager.go, client/ssh/config/manager_test.go
SSH server supports multiple listeners (AddListener), tracks extra listeners, validates connections against both IPv4/IPv6 overlay addresses; PeerSSHInfo now holds netip.Addr for IP and IPv6.
Peer status, status output, and tests
client/internal/peer/status.go, client/status/status.go, client/status/status_test.go, client/internal/peer/status_test.go
Added IPv6 fields to peer/local state, updated Status.AddPeer signature, serialize IPv6 into protobuf/status outputs, and updated tests/fixtures.
Profile/config/flags/UI/CLI/embedding/daemon
client/internal/profilemanager/config.go, client/internal/auth/auth.go, client/server/server.go, client/server/setconfig_test.go, client/cmd/*, client/ui/*, client/embed/embed.go, client/wasm/*, client/proto/daemon.proto
Added DisableIPv6 config input/field and CLI flag (--disable-ipv6), threaded flag into auth/metadata and SetConfig/GetConfig, UI checkbox wiring, embed options propagation, wasm parsing, and protobuf fields for disable_ipv6 and peer ipv6.
Clients (Android/iOS/SDK/WASM)
client/android/*, client/ios/NetBirdSDK/*, client/wasm/*
PeerInfo extended with IPv6, preference getters/setters for DisableIPv6 added, and host:port formatting corrected to use net.JoinHostPort where applicable.
Miscellaneous small fixes & tests
client/internal/rosenpass/manager.go, client/internal/routemanager/*, various tests
Replaced manual host:port formatting with net.JoinHostPort, adjusted tests to use wgaddr.MustParseWGAddress, updated call-sites for signature changes and added/updated unit tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Engine
    participant Device
    participant Management
    participant DNS
    Client->>Engine: Start with EngineConfig{WgAddr: wgaddr.Address, DisableIPv6: bool}
    Engine->>Device: Create/Update interface with wgaddr.Address (may include IPv6)
    Device-->>Engine: Report assigned v4/v6 (or clear IPv6 on failure)
    Engine->>Management: Login/register with System Info (Flags.DisableIPv6, Capabilities)
    Management-->>Client: Send PeerConfig (address_v6, sourcePrefixes, capabilities)
    Client->>DNS: Generate reverse zones for v4 and v6 based on Engine.WgAddr
    Client->>Engine: Apply peer AllowedIPs filtered by local IPv6 availability
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • pappz
  • pascal-fischer

Poem

🐰
I nibble bytes of v6 delight,
Compact prefixes snug and tight,
Devices try v6 then kindly yield,
DNS and SSH join the dual-stack field,
Hooray — the rabbit hops, overlay in sight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly identifies the main feature being added: IPv6 overlay support across management and client components.
Description check ✅ Passed The PR description follows the required template with all major sections populated.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch proto-ipv6-overlay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lixmal lixmal changed the title [management, shared] Add proto fields for IPv6 overlay and compact prefix encoding [management, client, shared] Add proto fields for IPv6 overlay and compact prefix encoding Mar 19, 2026
@lixmal lixmal changed the title [management, client, shared] Add proto fields for IPv6 overlay and compact prefix encoding [management, client] Add proto fields for IPv6 overlay and compact prefix encoding Mar 19, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
shared/management/proto/management.proto (1)

338-340: Proto field addition looks good.

The address_v6 field appropriately extends PeerConfig for dual-stack peer support.

Minor documentation consideration: The comment says "16 bytes IP + 1 byte prefix length" which matches the compact format, but for a peer's overlay address the prefix length is typically implied (e.g., always /128 for IPv6). Consider clarifying whether the prefix length byte is meaningful here or if it could be simplified to just 16 bytes for the address since DecodeAddr (from netiputil) discards the prefix length anyway.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/management/proto/management.proto` around lines 338 - 340, The comment
for the new bytes field address_v6 on PeerConfig is ambiguous about the prefix
byte; either explicitly document that the extra prefix-length byte is ignored by
DecodeAddr (netiputil) or remove it and change the wire-format expectation to a
plain 16-byte IPv6 address. Update the proto comment for address_v6 to state the
intended format (e.g., "16 bytes IPv6 address, no prefix byte" or "16 bytes IP +
1 byte prefix (prefix is meaningful)"), and if you choose to remove the prefix
byte, update any decoding/encoding logic that reads/writes address_v6 (search
for DecodeAddr and usages in netiputil and consumers of PeerConfig) to handle
the 16-byte-only form. Ensure the comment and code for address_v6, PeerConfig,
and DecodeAddr/netiputil are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shared/netiputil/compact.go`:
- Around line 24-42: The Gosec G602 false positives in DecodePrefix arise from
slice access checks that are actually guarded by the switch on len(b); suppress
them by adding a nolint directive: annotate the relevant slice-access lines in
DecodePrefix (the cases handling length 5 and 17, i.e., the uses of b[:4], b[4],
b[:16], and b[16]) with "//nolint:gosec // G602 false positive: length is
checked by switch case" so the analyzer ignores these safe accesses while
preserving the existing guard logic.

---

Nitpick comments:
In `@shared/management/proto/management.proto`:
- Around line 338-340: The comment for the new bytes field address_v6 on
PeerConfig is ambiguous about the prefix byte; either explicitly document that
the extra prefix-length byte is ignored by DecodeAddr (netiputil) or remove it
and change the wire-format expectation to a plain 16-byte IPv6 address. Update
the proto comment for address_v6 to state the intended format (e.g., "16 bytes
IPv6 address, no prefix byte" or "16 bytes IP + 1 byte prefix (prefix is
meaningful)"), and if you choose to remove the prefix byte, update any
decoding/encoding logic that reads/writes address_v6 (search for DecodeAddr and
usages in netiputil and consumers of PeerConfig) to handle the 16-byte-only
form. Ensure the comment and code for address_v6, PeerConfig, and
DecodeAddr/netiputil are consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 172f0e99-f851-4579-a9e4-d40f6310a23d

📥 Commits

Reviewing files that changed from the base of the PR and between a1858a9 and 12cffdb.

⛔ Files ignored due to path filters (1)
  • shared/management/proto/management.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (7)
  • client/internal/acl/manager.go
  • client/internal/debug/debug.go
  • management/internals/shared/grpc/conversion.go
  • management/server/peer_test.go
  • shared/management/proto/management.proto
  • shared/netiputil/compact.go
  • shared/netiputil/compact_test.go

Comment thread shared/netiputil/compact.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
shared/netiputil/compact.go (1)

23-42: Consider validating prefix length bounds for robustness.

The decoded prefix length (last byte) is not validated against valid bounds. For IPv4, valid bits are 0-32; for IPv6, 0-128. If the input contains out-of-range values, netip.PrefixFrom returns an invalid prefix silently, requiring callers to check IsValid().

For a wire protocol decoder, explicit validation with a clear error message improves debuggability when malformed data is received.

🛡️ Proposed fix to validate prefix length
 func DecodePrefix(b []byte) (netip.Prefix, error) {
 	switch len(b) {
 	case 5:
 		var ip4 [4]byte
 		copy(ip4[:], b)
-		return netip.PrefixFrom(netip.AddrFrom4(ip4), int(b[len(b)-1])), nil
+		bits := int(b[len(b)-1])
+		if bits > 32 {
+			return netip.Prefix{}, fmt.Errorf("invalid IPv4 prefix length %d (max 32)", bits)
+		}
+		return netip.PrefixFrom(netip.AddrFrom4(ip4), bits), nil
 	case 17:
 		var ip6 [16]byte
 		copy(ip6[:], b)
 		addr := netip.AddrFrom16(ip6).Unmap()
 		bits := int(b[len(b)-1])
+		maxBits := 128
+		if addr.Is4() {
+			maxBits = 32
+		}
+		if bits > maxBits {
+			return netip.Prefix{}, fmt.Errorf("invalid prefix length %d (max %d)", bits, maxBits)
+		}
 		if addr.Is4() && bits > 32 {
 			bits = 32
 		}
 		return netip.PrefixFrom(addr, bits), nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/netiputil/compact.go` around lines 23 - 42, DecodePrefix does not
validate the prefix length byte and can produce an invalid netip.Prefix; update
DecodePrefix to read the trailing byte into bits and explicitly validate bounds:
for the 5-byte (IPv4) case ensure 0 <= bits <= 32 (return a clear fmt.Errorf if
not), and for the 17-byte (IPv6) case ensure 0 <= bits <= 128 (return error if
out of range); keep the existing addr.Unmap() and the special case that an
IPv6-mapped IPv4 address should not claim >32 bits (either validate before or
after Unmap and return an error if inconsistent), and only call netip.PrefixFrom
when bits are within the validated range so callers never receive silently
invalid prefixes from DecodePrefix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@shared/netiputil/compact.go`:
- Around line 23-42: DecodePrefix does not validate the prefix length byte and
can produce an invalid netip.Prefix; update DecodePrefix to read the trailing
byte into bits and explicitly validate bounds: for the 5-byte (IPv4) case ensure
0 <= bits <= 32 (return a clear fmt.Errorf if not), and for the 17-byte (IPv6)
case ensure 0 <= bits <= 128 (return error if out of range); keep the existing
addr.Unmap() and the special case that an IPv6-mapped IPv4 address should not
claim >32 bits (either validate before or after Unmap and return an error if
inconsistent), and only call netip.PrefixFrom when bits are within the validated
range so callers never receive silently invalid prefixes from DecodePrefix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c0facdd-fccf-46f0-b534-18bb5d96c612

📥 Commits

Reviewing files that changed from the base of the PR and between 12cffdb and d7dbe40.

📒 Files selected for processing (1)
  • shared/netiputil/compact.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
shared/netiputil/compact.go (1)

16-21: Consider validating input prefix.

The implementation is correct for valid prefixes. However, if an invalid/zero netip.Prefix is passed, p.Bits() returns -1 (becomes 255 as byte) and a zero Addr produces a 1-byte result. Both would fail on decode, so it's fail-safe, but explicit validation could provide clearer error messages at the source.

💡 Optional: Add validation for invalid prefixes
 // EncodePrefix encodes a netip.Prefix into compact bytes.
 // The address is always unmapped before encoding.
-func EncodePrefix(p netip.Prefix) []byte {
+func EncodePrefix(p netip.Prefix) ([]byte, error) {
+	if !p.IsValid() {
+		return nil, fmt.Errorf("invalid prefix")
+	}
 	addr := p.Addr().Unmap()
-	return append(addr.AsSlice(), byte(p.Bits()))
+	return append(addr.AsSlice(), byte(p.Bits())), nil
 }

Note: This would require updating callers to handle the error. Given the current usage context where callers likely pass valid prefixes, keeping the current signature may be acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared/netiputil/compact.go` around lines 16 - 21, Validate the input prefix
in EncodePrefix by checking netip.Prefix validity and bit length before
encoding: change EncodePrefix(p netip.Prefix) to return ([]byte, error), verify
p.IsValid() and p.Bits() >= 0 (or other appropriate checks) and return a
descriptive error when invalid instead of producing a malformed byte slice;
update all callers to handle the error accordingly so decoding won't receive
impossible values like 255 bits or a one-byte address.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@shared/netiputil/compact.go`:
- Around line 16-21: Validate the input prefix in EncodePrefix by checking
netip.Prefix validity and bit length before encoding: change EncodePrefix(p
netip.Prefix) to return ([]byte, error), verify p.IsValid() and p.Bits() >= 0
(or other appropriate checks) and return a descriptive error when invalid
instead of producing a malformed byte slice; update all callers to handle the
error accordingly so decoding won't receive impossible values like 255 bits or a
one-byte address.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58f63604-5572-410f-90a1-859c6db3bd11

📥 Commits

Reviewing files that changed from the base of the PR and between d7dbe40 and 0eaa9ad.

📒 Files selected for processing (2)
  • shared/netiputil/compact.go
  • shared/netiputil/compact_test.go
✅ Files skipped from review due to trivial changes (1)
  • shared/netiputil/compact_test.go

pascal-fischer
pascal-fischer previously approved these changes Mar 19, 2026
@lixmal lixmal force-pushed the proto-ipv6-overlay branch from b7be56f to e2f7748 Compare March 22, 2026 06:16
@lixmal
Copy link
Copy Markdown
Collaborator Author

lixmal commented Apr 10, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

lixmal added 11 commits April 10, 2026 12:32
…arams

- Add IPv6 router dispatch to AddOutputDNAT/RemoveOutputDNAT in both
  nftables and iptables managers (was hardcoded to v4 router only).
- Fix all DNAT and AddDNATRule dispatch methods to check Is6() first,
  then error with ErrIPv6NotInitialized if v6 components are missing.
  Previously the hasIPv6() && Is6() pattern silently fell through to
  the v4 router for v6 addresses when v6 was not initialized.
- Add ErrIPv6NotInitialized sentinel error, replace all ad-hoc
  "IPv6 not initialized" format strings across both managers.
- Rename sourcePort/targetPort to originalPort/translatedPort in all
  DNAT method signatures to reflect actual DNAT semantics.
- Remove stale "localAddr must be IPv4" comments from interface.
removeFromServerNetwork and CleanUp hardcoded useNewDNSRoute=false
when building the router pair for RemoveNatRule. This meant the
destination was a Prefix (0.0.0.0/0) instead of a DomainSet, so the
IsSet() branch in RemoveNatRule that removes the v6 duplicate never
triggered. The v6 NAT rule leaked until the next full Reset.

Store useNewDNSRoute on the Router from UpdateRoutes and use it
consistently in removeFromServerNetwork and CleanUp, making add
and remove symmetric.
The legacy DNS resolver path creates NAT pairs with destination
0.0.0.0/0 (a prefix, not a DomainSet). The v6 NAT duplication only
triggered for DomainSets, so legacy dynamic routes never got a v6
NAT rule.

Extract NeedsV6NATDuplicate and ToV6NatPair helpers that detect both
DomainSets and the v4 default wildcard 0.0.0.0/0. Both nftables and
iptables managers now use these for Add/RemoveNatRule, ensuring v6
NAT duplication works for both modern and legacy DNS resolver paths.
The v6 NAT duplication only triggered for DomainSet destinations
(modern DNS path). Legacy dynamic routes use a 0.0.0.0/0 prefix
destination, so the v6 NAT rule was never created.

Add a Dynamic field to RouterPair so the firewall manager can
distinguish dynamic routes from exit nodes (both use /0 prefixes).
Set it from route.IsDynamic() in routeToRouterPair and propagate
through GetInversePair. Both nftables and iptables managers check
pair.Dynamic instead of destination shape.

Also accumulate errors in RemoveNatRule so v6 cleanup is attempted
even if v4 removal fails.
The cleanup path tried to delete the jump rule to NETBIRD-NAT-OUTPUT
unconditionally, producing a noisy debug log when the chain was never
created (common for the v6 router when no OutputDNAT rules exist).
Check ChainExists first, consistent with the chain deletion loop
below.
The packet tracer resolved 'self' to the v4 overlay address
unconditionally, causing "mixed address families" errors when tracing
v6 traffic. Pick the self address matching the peer's address family.

Add Engine.GetWgV6Addr() and rework parseAddress into
resolveTraceAddresses which parses the non-self address first to
determine the family, then resolves self accordingly.
# Conflicts:
#	management/server/types/networkmap_components.go
…outesToSync

Extract deleteAccountUsers from DeleteAccount (complexity 21 -> ~14).
Extract processResourcePolicies and getResourcePolicyPeers from
getNetworkResourcesRoutesToSync (complexity 31 -> ~15).

Fixes SonarCloud S3776 violations.
# Conflicts:
#	management/server/types/networkmap_components.go
Comment thread client/proto/daemon.pb.go
pappz
pappz previously approved these changes Apr 13, 2026
Resolve conflict in setupAndroidRoutes: merge IPv6 fake IP route
with the explicit fake IP route storage from #5865.

Notifier now stores a slice of fake IP routes (v4 + v6) via
SetFakeIPRoutes to preserve the stale route re-injection fix.
pappz
pappz previously approved these changes Apr 15, 2026
@sonarqubecloud
Copy link
Copy Markdown

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.

3 participants