Skip to content

Instantly share code, notes, and snippets.

@dlenski
Last active October 7, 2026 15:41
Show Gist options
  • Select an option

  • Save dlenski/89028b8341ac5ac95b045dcf2c0a2386 to your computer and use it in GitHub Desktop.

Select an option

Save dlenski/89028b8341ac5ac95b045dcf2c0a2386 to your computer and use it in GitHub Desktop.
Windows Powershell script to route some traffic away from a VPN (poor man's https://github.com/dlenski/vpn-slice for Windows)
#Requires -RunAsAdministrator
#####
#
# This is a Windows Powershell script to modify your computer's routing table to send traffic to certain IPv4 addresses
# or network ranges through a physical network adapter rather than through a VPN.
#
# It's basically a poor-man's Windows-specific version of https://github.com/dlenski/vpn-slice
#
# For example, let's say that your workplace VPN blocks SSH connections to external hosts, even though
# you use Azure DevOps git repositories for work (real use case for me 🤷🏻‍♂️), then you can use this to
# route traffic to Azure DevOps git/ssh servers around the VPN.
#
# © 2026- Daniel Lenski <dlenski@gmail.com>
#
# Basic steps to use:
#
# 0) Modify the $dests array below to contain your desired list of VPN-avoiding hosts.
# 1) AFTER connecting your workplace VPN...
# 2) Run this script as Administrator. For example, using https://github.com/gerardog/gsudo:
# `gsudo powershell -File "C:\Path\To\ThisScript.ps1"`
# 3) Enjoy working network connections that bypass the VPN.
#
#####
$dests = @(
# List of IPv4 addresses (e.g. 1.2.3.4),. IPv4 network ranges (e.g. 1.2.3.0/24)
# or hostnames (e.g. server.company.com) to route via the physical adapter.
# Hostnames will be resolved once, when this script is executed, via default
# DNS servers, and all matching IPv4 addresses will be routed.
"140.82.112.0/20", # ssh to github.com (see https://api.github.com/meta)
"172.65.251.78", # ssh to gitlab.com (seems to be fixed IP)
"ssh.dev.azure.com", "vs-ssh.visualstudio.com" # Azure DevOps git ssh
)
###########
# Find all network adapters, including the first real and virtual adapters.
$realnet = $null
$vpnnet = $null
foreach ($net in Get-NetAdapter | Where-Object Status -eq 'Up' | Sort-Object ifIndex) {
if ($net.Virtual -and $vpnnet -eq $null) { $vpnnet = $net }
elseif (!$net.Virtual -and $realnet -eq $null) { $realnet = $net }
}
if ($realnet -eq $null) {
Write-Error "No currently-UP physical network adapter found, cannot proceed"
exit 1
}
if ($vpnnet -eq $null) {
Write-Warning "No currently-UP virtual network adapter found. This script will not have the desired effect until after VPN is already started."
exit 1
}
# Find the default IPv4 route of the "real" network adapter
$defroute = (Get-NetRoute -AddressFamily IPv4 `
-InterfaceIndex $realnet.ifIndex `
-DestinationPrefix '0.0.0.0/0' `
-ErrorAction SilentlyContinue)
if ($defroute -eq $null) {
Write-Error "No default IPv4 route (0.0.0.0/0) found for network adapter $($realnet.ifIndex) ($($realnet.Name))"
exit 1
}
$gw = $defroute.NextHop
Write-Host "Found nexthop/gateway of $gw for default IPv4 route (0.0.0.0/0) on network adapter $($realnet.ifIndex) ($($realnet.Name))"
# Add or modify routes
foreach ($dest in $dests) {
if ($dest -match "(\d+\.\d+\.\d+.\d+)(?:/(\d+))?") {
# It's already an IP address or IP network range
$netaddr = $Matches[1]
if ($Matches[2] -eq $null) { $netmask = 32 } else { $netmask = $Matches[2] }
Write-Host "Routing $netaddr/$netmask via $gw"
try {
Set-NetRoute -AddressFamily IPv4 -DestinationPrefix "$netaddr/$netmask" -InterfaceIndex $realnet.ifIndex -NextHop $gw -ErrorAction Stop
Write-Host " (updated existing route)"
} catch {
New-NetRoute -AddressFamily IPv4 -DestinationPrefix "$netaddr/$netmask" -InterfaceIndex $realnet.ifIndex -NextHop $gw -ErrorAction Stop
}
} else {
# Need to resolve hostname to IP address(es)
$ips = (Resolve-DnsName -Type A $dest).IPAddress
Write-Host "Got $($ips.Count) IPv4 addresses for $dest, routing via $gw"
foreach ($ip in $ips) {
try {
Set-NetRoute -AddressFamily IPv4 -DestinationPrefix "$ip/32" -InterfaceIndex $realnet.ifIndex -NextHop $gw -ErrorAction Stop
Write-Host " (updated existing route)"
} catch {
New-NetRoute -AddressFamily IPv4 -DestinationPrefix "$ip/32" -InterfaceIndex $realnet.ifIndex -NextHop $gw -ErrorAction Stop
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment