[PR #2912] [CLOSED] fix: keep CIDR prefix length when normalizing split tunneling entries #3011

Closed
opened 2026-10-14 21:06:51 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amnezia-client/pull/2912
Author: @antib0t
Created: 7/31/2026
Status: ❌ Closed

Base: dev ← Head: fix/split-tunneling-cidr-prefix


📝 Commits (1)

  • 4971e7d fix: keep CIDR prefix length when normalizing split tunneling entries

📊 Changes

1 file changed (+8 additions, -0 deletions)

View changed files

📝 client/core/controllers/ipSplitTunnelingController.cpp (+8 -0)

📄 Description

Problem

normalizeHostname() strips everything after the first slash. It exists to drop the path from a URL, but it runs on every entry, CIDR ranges included, and it runs before validation. So 91.108.4.0/22 gets stored as 91.108.4.0.

Downstream in localsocketcontroller.cpp:224-229, any address without a slash gets a /32:

} else {
    QJsonObject range;
    range.insert("address", ipRange);
    range.insert("range", 32);
    range.insert("isIpv6", false);
    jsAllowedIPAddesses.append(range);
}

A /22 subnet ends up as a host route to its own network address, which nothing talks to, and the rest of the subnet skips the tunnel. Both entry points hit this: addSite() at line 68 and importSitesFromJson() at line 215. An imported list of ranges lands in settings with every prefix gone.

Regression

4.8.21.0 had no normalizeHostname(). sitesController.cpp:45-46 stored the hostname as it was typed. The route building code in localsocketcontroller.cpp is identical in both versions, so only the input path changed.

Fix

Return CIDR ranges unchanged and leave URL handling alone.

Testing

Ran the normalization logic against the regexp for /16, /20, /22, /26 and /32 subnets, a bare IPv4 address, a domain, URLs with paths, and a malformed prefix like 1.2.3.4/999. Subnets keep their prefix. URLs still lose the path. The malformed one falls back to the bare address, as before.

Verified on macOS 26.5.2 with AmneziaVPN 5.0.0: a list of 2960 subnets imported from iplist.opencck.org was stored with all prefixes stripped, and the routing table showed no exclusion routes beyond the VPN endpoint itself.

Note for users

Lists imported by an affected build are already stored without prefixes, and upgrading does not bring them back. The list has to be imported again.

Related: #2904, #2907


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amnezia-client/pull/2912 **Author:** [@antib0t](https://github.com/antib0t) **Created:** 7/31/2026 **Status:** ❌ Closed **Base:** `dev` ← **Head:** `fix/split-tunneling-cidr-prefix` --- ### 📝 Commits (1) - [`4971e7d`](https://github.com/amnezia-vpn/amnezia-client/commit/4971e7dda9ccf60709e370fdd70e0e4cd093d2eb) fix: keep CIDR prefix length when normalizing split tunneling entries ### 📊 Changes **1 file changed** (+8 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `client/core/controllers/ipSplitTunnelingController.cpp` (+8 -0) </details> ### 📄 Description ## Problem `normalizeHostname()` strips everything after the first slash. It exists to drop the path from a URL, but it runs on every entry, CIDR ranges included, and it runs before validation. So `91.108.4.0/22` gets stored as `91.108.4.0`. Downstream in `localsocketcontroller.cpp:224-229`, any address without a slash gets a `/32`: ```cpp } else { QJsonObject range; range.insert("address", ipRange); range.insert("range", 32); range.insert("isIpv6", false); jsAllowedIPAddesses.append(range); } ``` A /22 subnet ends up as a host route to its own network address, which nothing talks to, and the rest of the subnet skips the tunnel. Both entry points hit this: `addSite()` at line 68 and `importSitesFromJson()` at line 215. An imported list of ranges lands in settings with every prefix gone. ## Regression 4.8.21.0 had no `normalizeHostname()`. `sitesController.cpp:45-46` stored the hostname as it was typed. The route building code in `localsocketcontroller.cpp` is identical in both versions, so only the input path changed. ## Fix Return CIDR ranges unchanged and leave URL handling alone. ## Testing Ran the normalization logic against the regexp for /16, /20, /22, /26 and /32 subnets, a bare IPv4 address, a domain, URLs with paths, and a malformed prefix like `1.2.3.4/999`. Subnets keep their prefix. URLs still lose the path. The malformed one falls back to the bare address, as before. Verified on macOS 26.5.2 with AmneziaVPN 5.0.0: a list of 2960 subnets imported from iplist.opencck.org was stored with all prefixes stripped, and the routing table showed no exclusion routes beyond the VPN endpoint itself. ## Note for users Lists imported by an affected build are already stored without prefixes, and upgrading does not bring them back. The list has to be imported again. Related: #2904, #2907 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-14 21:06:51 +00:00
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#3011
No description provided.