Privacy Tools Guide

When you connect multiple VPNs simultaneously or route traffic through overlapping network ranges, subnet conflicts become a real headache. These conflicts break connectivity, cause mysterious routing failures, and frustrate even experienced developers. This guide explains exactly how VPN subnet conflicts happen and provides actionable solutions to fix them.

What Is a Subnet Conflict?

A subnet conflict occurs when two or more networks assign the same IP address range to different interfaces or VPN tunnels. When your operating system cannot determine which route to use, packets get dropped, misruled, or trapped in endless loops.

Consider this scenario: Your home network uses 192.168.1.0/24, your corporate VPN uses 192.168.1.0/24, and your split-tunnel VPN also uses 192.168.1.0/24. All three claim the same address space. Your machine simply cannot distinguish between them.

How VPN Subnet Conflicts Happen

1. Multiple Simultaneous VPN Connections

The most common cause is connecting to two VPNs at once. Corporate VPNs often use 10.0.0.0/8 or 172.16.0.0/12 ranges. Consumer VPNs frequently use 192.168.0.0/16 for their tunnel interfaces. When these overlap with your local network or with each other, routing becomes ambiguous.

2. Default Gateway Conflicts

When a VPN client pushes a default route (0.0.0.0/0), it overrides your existing default gateway. If you connect to a second VPN without proper split-tunneling configuration, you end up with two default routes. The operating system typically chooses the first one it sees, breaking connectivity to the other VPN’s network.

3. VPN Server-Side Configuration

VPN providers configure their server pools with specific subnets. Many providers use the same common ranges—10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16—because they are RFC 1918 private addresses. When you connect to multiple VPNs using the same private range, conflicts emerge.

4. Local Network Overlap

Your home router likely uses 192.168.1.0/24 or 192.168.0.0/24. Many VPN clients default to the same ranges for their virtual interfaces. The result: your VPN tunnel claims the same addresses your local network already uses.

Detecting Subnet Conflicts

First, identify the conflict. On Linux or macOS, check your routing table:

# macOS
netstat -nr | grep -E "Destination|UG"

# Linux
ip route show

Look for duplicate entries or overlapping networks. You might see something like this:

192.168.1.0/24 dev eth0  scope link  src 192.168.1.100
192.168.1.0/24 dev tun0  scope link  src 10.8.0.2

Both claim 192.168.1.0/24—a clear conflict.

On Windows, use:

route print

Check for identical destination networks under “IPv4 Route Table.”

Practical Solutions

Solution 1: Use Split Tunneling

Split tunneling sends only specific traffic through the VPN while letting other traffic use your regular connection. Most VPN clients support this. Configure your VPN to route only necessary subnets.

For OpenVPN, add to your client config:

route 10.0.0.0 255.0.0.0 vpn_gateway
route 172.16.0.0 255.240.0.0 vpn_gateway

This tells OpenVPN to route only 10.0.0.0/8 and 172.16.0.0/12 through the tunnel, leaving your local 192.168.x.x network unaffected.

For WireGuard, configure AllowedIPs in your peer section:

[Peer]
PublicKey = <server-public-key>
AllowedIPs = 10.0.0.0/8, 172.16.0.0/12
Endpoint = vpn.example.com:51820

Solution 2: Change VPN Client Address Ranges

If your VPN client allows custom tunnel addresses, assign it a range that does not conflict with your local network. Common non-overlapping ranges include:

For OpenVPN, set custom tunnel addresses:

ifconfig 10.200.0.2 10.200.0.1

This assigns 10.200.0.2 to your tunnel interface.

For WireGuard, specify the address in your interface config:

[Interface]
PrivateKey = <your-private-key>
Address = 10.200.0.2/24
ListenPort = 51820

Solution 3: Adjust Your Local Network

If possible, change your home router or office network to use an uncommon subnet. Many routers default to 192.168.1.0/24 or 192.168.0.0/24. Switching to something like 10.99.0.0/24 or 172.31.100.0/24 reduces conflict probability.

Access your router’s admin panel and find the LAN settings. Change the DHCP range to something less common.

Solution 4: Use Metric Configuration (Windows)

On Windows, you can assign metrics to prioritize routes. Lower metrics take precedence. Force your VPN to use a higher metric so your local network takes priority for local addresses:

netsh interface ipv4 set interface <interface-name> metric=50

Find your interface names with:

netsh interface ipv4 show interfaces

Solution 5: Implement Route Tables Manually

For advanced control, manually manage your routing table. On Linux:

# Delete conflicting routes
sudo ip route del 192.168.1.0/24 dev tun0

# Add specific route through VPN
sudo ip route add 10.50.0.0/16 via 10.8.0.1 dev tun0

Create persistent routes by adding to /etc/sysconfig/network-scripts/route-eth0 (RHEL/CentOS) or /etc/network/interfaces (Debian).

Solution 6: Use a VPN Kill Switch with Routing Policy

Some VPN clients include policy-based routing. Configure kill switches to only route traffic matching specific criteria:

For Linux with iptables:

# Allow only VPN traffic
iptables -P INPUT DROP
iptables -A INPUT -i tun0 -j ACCEPT
iptables -A OUTPUT -o tun0 -j ACCEPT

# Drop traffic that should go through VPN but doesn't
iptables -A OUTPUT -o eth0 -d 10.0.0.0/8 -j REJECT

This ensures only properly routed traffic escapes, preventing leaks that cause conflicts.

Preventing Future Conflicts

Diagnosing Conflicts with Advanced Tools

When basic route inspection shows conflicts, use specialized tools:

# Linux: Use netstat with verbose output
netstat -rne | sort -k1

# macOS: Detailed route information
netstat -nr | awk '{print $1}' | sort | uniq -c | grep -v "1 "

# Windows: Route with gateway information
route print -4 | findstr /C:"0.0.0.0"

# Check MTU compatibility
ip link show | grep mtu
# Conflicts can occur if MTU differs: ensure all tunnels have MTU >= 1500

For complex environments, visualize your network topology:

#!/usr/bin/env python3
import subprocess
import re

def parse_routing_table():
    """Parse routing table and identify overlaps."""
    result = subprocess.run(
        ['route', '-n'], capture_output=True, text=True
    )

    routes = {}
    for line in result.stdout.split('\n'):
        if '/' in line:
            parts = line.split()
            if len(parts) >= 3:
                destination = parts[0]
                gateway = parts[2]
                routes[destination] = gateway

    # Check for overlapping routes
    destinations = list(routes.keys())
    conflicts = []

    from ipaddress import ip_network

    for i, dest1 in enumerate(destinations):
        for dest2 in destinations[i+1:]:
            try:
                net1 = ip_network(dest1, strict=False)
                net2 = ip_network(dest2, strict=False)

                if net1.overlaps(net2):
                    conflicts.append((dest1, dest2, routes[dest1], routes[dest2]))
            except:
                pass

    if conflicts:
        print("[ALERT] Routing conflicts detected:")
        for dest1, dest2, gw1, gw2 in conflicts:
            print(f"  {dest1} via {gw1}")
            print(f"  overlaps with")
            print(f"  {dest2} via {gw2}")
    else:
        print("[OK] No routing conflicts detected")

    return conflicts

parse_routing_table()

Dynamic Conflict Resolution

Create a script that automatically detects and fixes conflicts:

#!/bin/bash
# auto-fix-vpn-conflicts.sh

CONFLICT_DETECTED=0

# Function: Check for routing conflicts
check_conflicts() {
    echo "Checking for routing conflicts..."

    # Get all routes
    ROUTES=$(ip route show)

    # Check for duplicate destinations
    DUPLICATES=$(echo "$ROUTES" | awk '{print $1}' | sort | uniq -c | awk '$1 > 1')

    if [ -n "$DUPLICATES" ]; then
        CONFLICT_DETECTED=1
        echo "[CONFLICT] Duplicate routes detected:"
        echo "$DUPLICATES"
    fi
}

# Function: Resolve conflicts
resolve_conflicts() {
    if [ $CONFLICT_DETECTED -eq 0 ]; then
        return
    fi

    echo "Attempting automatic conflict resolution..."

    # Remove all VPN-related routes
    VPN_ROUTES=$(ip route | grep -E "tun|tap|wg")
    echo "$VPN_ROUTES" | while read route; do
        echo "Removing: $route"
        DEST=$(echo $route | awk '{print $1}')
        sudo ip route del $DEST 2>/dev/null || true
    done

    # Re-add routes with proper metrics
    echo "Re-adding routes with conflict-free configuration..."
    # This should be customized based on your VPN setup
}

# Main execution
check_conflicts
resolve_conflicts

Windows-Specific Subnet Conflict Resolution

Windows handles routing differently than Linux. Use these Windows-specific commands:

# View all routes with interface details
route print -4 -v

# Find overlapping routes
netsh routing ip show scope

# Set route metrics to prioritize VPN
netsh interface ipv4 set route "0.0.0.0/0" interface=VPN metric=10
netsh interface ipv4 set route "0.0.0.0/0" interface=Ethernet metric=100

# Force specific services through VPN
netsh int ipv4 add route 10.0.0.0/8 1.2.3.4 metric=1

# Verify routes
route print | find "10.0.0.0"

Multi-VPN Management Framework

For users connecting to multiple VPNs simultaneously (corporate + privacy VPN):

#!/usr/bin/env python3
import subprocess
from dataclasses import dataclass
from typing import List

@dataclass
class VPNConnection:
    name: str
    tunnel_interface: str
    tunnel_subnet: str
    allowed_ips: List[str]
    priority: int

class MultiVPNManager:
    def __init__(self, vpn_connections: List[VPNConnection]):
        self.vpns = vpn_connections
        self.vpns.sort(key=lambda x: x.priority)

    def assign_non_overlapping_subnets(self):
        """Assign subnets to VPNs without conflicts."""
        used_subnets = set()

        for vpn in self.vpns:
            from ipaddress import ip_network

            try:
                net = ip_network(vpn.tunnel_subnet, strict=False)
                if any(used_subnets & ip_network(f"{net.network_address}/{net.prefixlen}")):
                    print(f"[CONFLICT] {vpn.name} subnet conflicts with existing VPN")
                    # Assign alternative subnet
                    alt_subnet = self.find_free_subnet(used_subnets)
                    print(f"  Reassigning to {alt_subnet}")
                    vpn.tunnel_subnet = alt_subnet

                used_subnets.add(vpn.tunnel_subnet)
            except Exception as e:
                print(f"Error processing {vpn.name}: {e}")

    def find_free_subnet(self, used_subnets):
        """Find an available /24 subnet."""
        from ipaddress import ip_network

        for i in range(200, 255):
            candidate = f"10.{i}.0.0/24"
            if candidate not in used_subnets:
                return candidate

        return "10.255.0.0/24"

    def generate_config(self):
        """Generate conflict-free VPN configuration."""
        for vpn in self.vpns:
            print(f"\n# {vpn.name}")
            print(f"Interface: {vpn.tunnel_interface}")
            print(f"Subnet: {vpn.tunnel_subnet}")
            print(f"AllowedIPs: {', '.join(vpn.allowed_ips)}")

# Usage
vpns = [
    VPNConnection(
        name="Corporate VPN",
        tunnel_interface="tun0",
        tunnel_subnet="10.0.0.0/24",
        allowed_ips=["10.0.0.0/8", "172.16.0.0/12"],
        priority=1
    ),
    VPNConnection(
        name="Privacy VPN",
        tunnel_interface="tun1",
        tunnel_subnet="192.168.200.0/24",
        allowed_ips=["0.0.0.0/0"],
        priority=2
    )
]

manager = MultiVPNManager(vpns)
manager.assign_non_overlapping_subnets()
manager.generate_config()

Testing Subnet Conflict Fixes

After implementing fixes, verify connectivity:

#!/bin/bash
# Verify subnet conflict resolution

echo "=== SUBNET CONFLICT VERIFICATION ==="

# 1. Test connectivity to each VPN
echo "Testing VPN 1 connectivity..."
ping -c 3 10.0.0.1 && echo "  [OK]" || echo "  [FAIL]"

echo "Testing VPN 2 connectivity..."
ping -c 3 10.1.0.1 && echo "  [OK]" || echo "  [FAIL]"

# 2. Verify routing decisions
echo ""
echo "Route table:"
ip route show | grep -E "tun|10\."

# 3. Test split tunneling works
echo ""
echo "Testing split tunnel traffic..."
# Should route to VPN 1
curl -s https://api.ipify.org --interface 10.0.0.2
# Should route to VPN 2
curl -s https://api.ipify.org --interface 10.1.0.2

# 4. Verify no default route conflicts
echo ""
echo "Checking default route:"
ip route show | grep "^default"

Built by theluckystrike — More at zovo.one