[GH-ISSUE #2939] [iOS 5.0.0.5] CIDR prefix is stripped from imported site-based split-tunneling entries #1343

Open
opened 2026-10-13 20:40:35 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @izhirkevich-coder on GitHub (Aug 4, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/2939

Describe the bug

When a site-based split-tunneling JSON file contains an IPv4 CIDR in the hostname field, AmneziaVPN 5.0.0.5 removes the prefix length during import.

For example:

{ "hostname": "1.192.0.0/10", "ip": "" }

is stored and passed to the iOS Network Extension as:

1.192.0.0

As a result, AmneziaWG creates a host route for only the network address instead of routing the intended subnet. A CIDR-based split-tunneling list therefore appears to import successfully but does not work.

This was reproduced on iOS with AmneziaWG 2. The UI mode, AllowedIPs, tunnel startup, and handshake are all correct; only the CIDR masks are lost.

To Reproduce

  1. Disconnect the VPN.
  2. Open Settings → Connection → Site-based split tunneling.
  3. Enable split tunneling.
  4. Select Only the sites listed here will be accessed through the VPN.
  5. Import the attached amnezia-cidr-import-minimal-repro.json using Replace existing list.
  6. Observe that the UI displays 1.192.0.0 and 2.16.0.0, without /10 and /13.
  7. Connect using AmneziaWG 2.
  8. Export the application log and inspect SplitTunnelSites.

Actual behavior

The CIDR prefix lengths are removed:

AllowedIPs = 0.0.0.0/0, ::/0
SplitTunnelType = 1
SplitTunnelSites = 1.192.0.0, 2.16.0.0, 1.1.1.1, 1.0.0.1

In a real list containing thousands of CIDRs, the exported log contained zero / characters in SplitTunnelSites. The Network Extension accepted the resulting routes and the VPN handshake succeeded, but traffic for the intended subnets was not routed through the VPN.

The attached before/after evidence was collected from the same iPhone, app build, AmneziaWG 2 profile, and route mode:

Import representation Split-tunnel entries Entries retaining CIDR Applied IPv4 routes Result
Official Amnezia format (CIDR in hostname) 3,241 0 3,242 Incorrect: subnet masks lost
Workaround (CIDR in ip) 2,988 including two DNS hosts 2,986 2,989 Correct: masks retained and tunnel works

The successful run logged:

AllowedIPs = 0.0.0.0/0, ::/0
SplitTunnelType = 1
SplitTunnelSites = 1.0.0.0/9, 1.192.0.0/10, ...
Network settings (start): ... ipv4 inc/exc=2989/1 ...
setNetworkSettings: applying
Tunnel interface is utun5
Received handshake response
handshake confirmed

This comparison rules out an iOS route-table limitation and an AmneziaWG CIDR parsing problem for this reproduction. The same 5.0.0.5 tunnel works when the importer does not pass CIDRs through normalizeHostname().

Expected behavior

CIDR entries must be preserved exactly during import and passed to the tunnel:

SplitTunnelSites = 1.192.0.0/10, 2.16.0.0/13, ...

Log files

Attach AmneziaVPN-CIDR-bug-sanitized.log. It contains sanitized evidence from both the failing and successful runs. The full AmneziaVPN 2.log is intentionally not attached because it contains the VPN server endpoint and internal connection identifiers.

Screenshots

Attach IMG_8192.PNG. It shows that imported CIDR entries are displayed without their prefix lengths while the correct split-tunneling mode is enabled.

Smartphone (please complete the following information):

  • Device: iPhone (exact model unknown)
  • OS: iOS 26.6 (arm64)
  • App version: AmneziaVPN 5.0.0.5, commit 4d28f819
  • Protocol: AmneziaWG 2 (amnezia-awg2)

Additional context / suspected root cause

The problem appears to be in IpSplitTunnelingController::normalizeHostname():

normalized = normalized.split("/", Qt::SkipEmptyParts).first();

importSitesFromJson() calls this function for every hostname, including valid CIDR strings. Therefore /10, /13, /24, etc. are treated like URL paths and removed before validation and storage.

Source:

https://github.com/amnezia-vpn/amnezia-client/blob/5.0.0.5/client/core/controllers/ipSplitTunnelingController.cpp#L140-L227

The current official Amnezia JSON format places CIDRs in the hostname field, for example:

{ "hostname": "77.40.0.0/17", "ip": "" }

See also:

https://github.com/amnezia-vpn/amnezia-client/issues/2578

Suggested fix

Preserve a valid IP/CIDR before applying URL/path normalization. For example, check the trimmed original value with NetworkUtilities::ipAddressWithSubnetRegExp() first, and only remove a URL path for non-IP hostnames.

Please add regression tests for:

  • importing and manually adding 1.192.0.0/10 without modification;
  • exporting and re-importing a CIDR list without losing prefixes;
  • normalizing https://example.com/path to example.com;
  • leaving a plain domain such as example.com unchanged.

Confirmed workaround

The CIDR is preserved if it is stored in the ip field while hostname contains a unique non-IP placeholder. This confirms that the tunnel implementation accepts CIDRs and that the loss occurs in the import/normalization path.

Importing the attached amnezia-cidr-import-minimal-workaround.json produces:

SplitTunnelSites = 1.192.0.0/10, 2.16.0.0/13, ...

The placeholder is not resolved; it only prevents the CIDR from being processed as a URL-like hostname. This is a diagnostic workaround, not a proposed permanent file format.

AmneziaVPN-CIDR-bug-sanitized.log
amnezia-cidr-import-minimal-repro.json
amnezia-cidr-import-minimal-workaround.json
Image

Originally created by @izhirkevich-coder on GitHub (Aug 4, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/2939 **Describe the bug** When a site-based split-tunneling JSON file contains an IPv4 CIDR in the `hostname` field, AmneziaVPN 5.0.0.5 removes the prefix length during import. For example: ```json { "hostname": "1.192.0.0/10", "ip": "" } ``` is stored and passed to the iOS Network Extension as: ```text 1.192.0.0 ``` As a result, AmneziaWG creates a host route for only the network address instead of routing the intended subnet. A CIDR-based split-tunneling list therefore appears to import successfully but does not work. This was reproduced on iOS with AmneziaWG 2. The UI mode, `AllowedIPs`, tunnel startup, and handshake are all correct; only the CIDR masks are lost. **To Reproduce** 1. Disconnect the VPN. 2. Open **Settings → Connection → Site-based split tunneling**. 3. Enable split tunneling. 4. Select **Only the sites listed here will be accessed through the VPN**. 5. Import the attached `amnezia-cidr-import-minimal-repro.json` using **Replace existing list**. 6. Observe that the UI displays `1.192.0.0` and `2.16.0.0`, without `/10` and `/13`. 7. Connect using AmneziaWG 2. 8. Export the application log and inspect `SplitTunnelSites`. **Actual behavior** The CIDR prefix lengths are removed: ```text AllowedIPs = 0.0.0.0/0, ::/0 SplitTunnelType = 1 SplitTunnelSites = 1.192.0.0, 2.16.0.0, 1.1.1.1, 1.0.0.1 ``` In a real list containing thousands of CIDRs, the exported log contained zero `/` characters in `SplitTunnelSites`. The Network Extension accepted the resulting routes and the VPN handshake succeeded, but traffic for the intended subnets was not routed through the VPN. The attached before/after evidence was collected from the same iPhone, app build, AmneziaWG 2 profile, and route mode: | Import representation | Split-tunnel entries | Entries retaining CIDR | Applied IPv4 routes | Result | |---|---:|---:|---:|---| | Official Amnezia format (`CIDR` in `hostname`) | 3,241 | 0 | 3,242 | Incorrect: subnet masks lost | | Workaround (`CIDR` in `ip`) | 2,988 including two DNS hosts | 2,986 | 2,989 | Correct: masks retained and tunnel works | The successful run logged: ```text AllowedIPs = 0.0.0.0/0, ::/0 SplitTunnelType = 1 SplitTunnelSites = 1.0.0.0/9, 1.192.0.0/10, ... Network settings (start): ... ipv4 inc/exc=2989/1 ... setNetworkSettings: applying Tunnel interface is utun5 Received handshake response handshake confirmed ``` This comparison rules out an iOS route-table limitation and an AmneziaWG CIDR parsing problem for this reproduction. The same 5.0.0.5 tunnel works when the importer does not pass CIDRs through `normalizeHostname()`. **Expected behavior** CIDR entries must be preserved exactly during import and passed to the tunnel: ```text SplitTunnelSites = 1.192.0.0/10, 2.16.0.0/13, ... ``` **Log files** Attach `AmneziaVPN-CIDR-bug-sanitized.log`. It contains sanitized evidence from both the failing and successful runs. The full `AmneziaVPN 2.log` is intentionally not attached because it contains the VPN server endpoint and internal connection identifiers. **Screenshots** Attach `IMG_8192.PNG`. It shows that imported CIDR entries are displayed without their prefix lengths while the correct split-tunneling mode is enabled. **Smartphone (please complete the following information):** - Device: iPhone (exact model unknown) - OS: iOS 26.6 (arm64) - App version: AmneziaVPN 5.0.0.5, commit `4d28f819` - Protocol: AmneziaWG 2 (`amnezia-awg2`) **Additional context / suspected root cause** The problem appears to be in `IpSplitTunnelingController::normalizeHostname()`: ```cpp normalized = normalized.split("/", Qt::SkipEmptyParts).first(); ``` `importSitesFromJson()` calls this function for every `hostname`, including valid CIDR strings. Therefore `/10`, `/13`, `/24`, etc. are treated like URL paths and removed before validation and storage. Source: https://github.com/amnezia-vpn/amnezia-client/blob/5.0.0.5/client/core/controllers/ipSplitTunnelingController.cpp#L140-L227 The current official Amnezia JSON format places CIDRs in the `hostname` field, for example: ```json { "hostname": "77.40.0.0/17", "ip": "" } ``` See also: https://github.com/amnezia-vpn/amnezia-client/issues/2578 **Suggested fix** Preserve a valid IP/CIDR before applying URL/path normalization. For example, check the trimmed original value with `NetworkUtilities::ipAddressWithSubnetRegExp()` first, and only remove a URL path for non-IP hostnames. Please add regression tests for: - importing and manually adding `1.192.0.0/10` without modification; - exporting and re-importing a CIDR list without losing prefixes; - normalizing `https://example.com/path` to `example.com`; - leaving a plain domain such as `example.com` unchanged. **Confirmed workaround** The CIDR is preserved if it is stored in the `ip` field while `hostname` contains a unique non-IP placeholder. This confirms that the tunnel implementation accepts CIDRs and that the loss occurs in the import/normalization path. Importing the attached `amnezia-cidr-import-minimal-workaround.json` produces: ```text SplitTunnelSites = 1.192.0.0/10, 2.16.0.0/13, ... ``` The placeholder is not resolved; it only prevents the CIDR from being processed as a URL-like `hostname`. This is a diagnostic workaround, not a proposed permanent file format. [AmneziaVPN-CIDR-bug-sanitized.log](https://github.com/user-attachments/files/30720172/AmneziaVPN-CIDR-bug-sanitized.log) [amnezia-cidr-import-minimal-repro.json](https://github.com/user-attachments/files/30720174/amnezia-cidr-import-minimal-repro.json) [amnezia-cidr-import-minimal-workaround.json](https://github.com/user-attachments/files/30720173/amnezia-cidr-import-minimal-workaround.json) <img width="660" height="1434" alt="Image" src="https://github.com/user-attachments/assets/85d4daba-606c-445f-96e4-eefece1bf55e" />
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#1343
No description provided.