System-generated Routes — Understanding GCP's Built-in Routing
Executive Summary
GCP automatically creates routes cho các special-case destinations:
- ✅ Subnet routes (0.0.0.0/0 for each subnet) — VPC-local
- ✅ Default route (0.0.0.0/0) — Internet egress
- ✅ Special routes (199.36.153.x/30) — Google APIs (Private Google Access)
- ✅ Reserved paths — GFE, IAP, DNS, serverless — không là routes nhưng affect packets
Hiểu những routes này là key để debug "tại sao traffic đi đây?" hoặc "tại sao bị block?"
Subnet Routes (System-generated)
Automatic Creation
Create subnet: prod-app (10.0.1.0/24) in us-central1
GCP automatically creates:
Route Name: prod-app
Destination Range: 10.0.1.0/24
Next Hop Type: Local network (same VPC)
Priority: 0 (highest, immutable)
Scope: Applies to all regions (global)
Enabled: Yes (immutable)
Result: Mọi VM trong VPC tự động reach 10.0.1.0/24 locally
Không cần firewall allow (traffic stays in VPC)
Không exit network (no egress charges)Route Lifecycle
Step 1: Create subnet 10.0.1.0/24
→ Immediate route creation
→ Propagates to all regions
→ Instances in ALL regions can reach 10.0.1.0/24
Step 2: Add secondary range 10.10.0.0/16 to same subnet
→ NEW route automatically created: 10.10.0.0/16 → local
→ Old route still exists: 10.0.1.0/24 → local
Step 3: Expand primary range from /24 to /23
→ Route updates: 10.0.1.0/23 (new) and 10.0.0.0/24 (old both valid)
Implication: Packets 10.0.0.0-10.0.1.255 match both routes
Most specific match wins (10.0.1.0/23 takes 10.0.1.x)
10.0.0.0/24 takes 10.0.0.x (from original subnet)
Step 4: Delete subnet 10.0.1.0/24
→ Route 10.0.1.0/24 deleted immediately
→ Instances in deleted subnet: packets drop or hit next route
→ If no explicit route, default 0.0.0.0/0 catches traffic
→ Packets exit VPC (unexpected!)Pitfall: Deleted Subnet Routing
Bad scenario:
Step 1: Create subnet 10.0.1.0/24, deploy instances
Route: 10.0.1.0/24 → local ✓
Step 2: Recreate subnet with different CIDR 10.0.2.0/24
Instances: still have old primary IPs 10.0.1.x
But new route only covers 10.0.2.0/24
Old route deleted
Result: Old VM IPs unreachable!
Traffic from old 10.0.1.x hits default route 0.0.0.0/0
Exits VPC to internet (security breach!)
Solution:
- Always plan CIDR upfront
- Don't delete/recreate subnets with different CIDRs
- Or manually add static route: 10.0.1.0/24 → discard
(blocks unexpected internet egress)Default Route (0.0.0.0/0)
System-generated Internet Gateway
Every VPC has automatic default route:
Route Name: default-route
Destination Range: 0.0.0.0/0
Next Hop Type: Default internet gateway
Priority: 65535 (lowest, system)
Enabled: Yes (cannot disable)
Effect:
- All traffic not matched by other routes → internet
- Requires external IP on sending VM
- Traffic exits GCP global backbone
- Subject to egress charges ($0.12/GB egress to internet)Traffic Path
VM 10.0.1.5 (external IP: 34.123.45.6) sends to 8.8.8.8:
1. VM routing table check:
Destination 8.8.8.8
Match: 0.0.0.0/0 (default route)
Next hop: default internet gateway
2. Source IP preserved: 10.0.1.5 (internal)
External IP: 34.123.45.6 (from metadata server)
3. SNAT at GCP edge:
Source IP: 34.123.45.6 (visible to 8.8.8.8)
Destination: 8.8.8.8
4. Packet exits Google network
Return: 8.8.8.8 → 34.123.45.6 → DNAT → 10.0.1.5Egress without External IP
Private VM 10.0.1.10 (no external IP) sends to 8.8.8.8:
1. VM routing table: 8.8.8.8 → default route
2. Default route requires external IP for SNAT
→ Packet dropped silently
3. VM sees: Connection timeout (no response)
Logs: No explicit error
Solution 1: Assign external IP
gcloud compute instances create ... --external-ip
Solution 2: Use Cloud NAT
gcloud compute routers add-nat nat-prod \
--router=router-prod \
--nat-all-subnet-ip-ranges \
--auto-allocate-nat-external-ips
Then: Packets → Cloud NAT → internet
NAT IP used for SNAT
No external IP needed on VMSpecial Routes: Private Google Access
199.36.153.x/30 Routes
GCP reserves three /30 blocks for Google API access:
Range 1: 199.36.153.4/30 (199.36.153.4-7)
Range 2: 199.36.153.8/30 (199.36.153.8-11)
Range 3: 199.36.153.12/30 (199.36.153.12-15)
Purpose: Route packets to Google APIs without internet egress
Virtual routing:
Traffic to 199.36.153.x → Special GCP handling
→ Routed to Google APIs (Cloud Storage, BigQuery, etc.)
→ No external IP needed
→ No egress chargesEnabling Private Google Access
Default behavior:
VM 10.0.1.5 (no external IP):
Request: gcloud auth login
→ Needs Google OAuth servers
→ Routing table: default route 0.0.0.0/0
→ Requires external IP (drops silently)
→ FAILS
Enable Private Google Access on subnet:
gcloud compute networks subnets update prod-app \
--region=us-central1 \
--enable-private-ip-google-access
Result:
VM 10.0.1.5 (still no external IP):
Request: gsutil ls gs://bucket/ (Cloud Storage)
→ Packet to 199.36.153.x:443
→ GCP special routing intercepted
→ Routed to Cloud Storage privately
→ SUCCEEDS
Special routes created automatically:
199.36.153.4/30 → private Google APIs
199.36.153.8/30 → private Google APIs
199.36.153.12/30 → private Google APIsUse Cases: Private APIs
Services reachable via Private Google Access:
✅ Cloud Storage (gsutil, SDK)
✅ BigQuery (bq CLI, Python API)
✅ Cloud Pub/Sub (publishing, subscribing)
✅ Cloud Datastore (APIs)
✅ Cloud Tasks (API)
✅ Google Cloud APIs (compute, storage, etc.)
❌ NOT available:
- External services (stripe.com, github.com, npm registry)
- GCP public endpoints via internetReserved Ranges: Implicit Handling
169.254.x.x/16 (Link-local)
169.254.x.x range = link-local (not routable outside host)
GCP uses for:
- Metadata server: 169.254.169.254 (GCP-specific)
- NodeLocal DNS cache: 169.254.20.10 (Kubernetes specific)
Traffic to 169.254.x.x:
- Stays on node
- Intercepted by node kernel
- Not forwarded to VPC
Example:
gcloud compute instances create vm1
Inside VM:
$ curl http://169.254.169.254/computeMetadata/v1/instance/id
→ Intercepted by node's kernel metadata proxy
→ Responded locally
→ Not sent to VPC224.0.0.0/4 (Multicast)
Multicast range = group communication
GCP behavior:
- Multicast traffic DROPPED at VPC boundary
- Cannot use for pod-to-pod communication
Not routable in standard GCP VPC
(Some limited use in on-premises via Cloud Interconnect)Special Paths: GFE, IAP, Serverless
Google Front End (GFE) Traffic
When using Global Load Balancer:
Client 203.0.113.5 → HTTPS → GLB (anycast IP 34.110.5.6)
GCP routing:
- GFE intercepts at Google edge
- Not subject to VPC routing tables
- Packet enters GCP network globally
- Routed to nearest backend
Result:
VPC routes don't apply to GLB-destined traffic
Firewall rules STILL apply (backend security)
Important: Global LB is not CRUD through VPC routes
Separate ingress path from computeIdentity-Aware Proxy (IAP)
Scenario: Private workload behind IAP
External client 203.0.113.5:
→ HTTPS → IAP service (managed by Google)
→ IAP validates identity
→ Routes to private backend 10.0.1.10 (no external IP)
VPC routing:
Backend 10.0.1.10 routes:
- No external IP needed
- No route 0.0.0.0/0 needed
- Firewall rule: allow tcp:8080 from IAP health check IPs
(35.191.0.0/16, 130.211.0.0/22)
Packet path is NOT through standard VPC routing
→ IAP service directly access backend
→ Separate Google-managed pathServerless NEGs (Cloud Run, App Engine)
Serverless services have NO VMs, NO VPC routes
When attaching serverless to LB:
Global LB → Serverless NEG (Cloud Run)
→ Cloud Run service deployed
→ No VPC IP
→ Routes to Cloud Run infrastructure
VPC routing doesn't apply:
- Cloud Run IPs NOT in VPC
- Cloud Run routes NOT in VPC routing tables
- But firewall rules can restrict access
(using VPC Service Controls or Cloud Armor)Reserved Ranges: Cannot Use
Blocked CIDR ranges (cannot use in subnets):
169.254.0.0/16 Link-local (169.254.169.254 metadata server)
224.0.0.0/4 Multicast
255.255.255.255/32 Broadcast
0.0.0.0/8 This network
127.0.0.0/8 Loopback (localhost)If you try:
bash
gcloud compute networks subnets create invalid-subnet \
--network=prod-vpc \
--region=us-central1 \
--range=127.0.0.0/24
ERROR: (gcloud.compute.networks.subnets.create)
Could not connect to the endpoint URL for compute
← Reserved IP range cannot be usedRouting Evaluation Order: Complete Picture
When packet arrives at VM, routing decision:
Step 1: Check subnet routes (priority 0)
10.0.1.0/24 → local?
10.10.0.0/16 → local?
Match: ROUTE
Step 2: Check static routes (priority 1-64999)
192.168.0.0/16 → vpn-gateway?
10.20.0.0/16 → instance-a?
Match: ROUTE (if multiple, most specific wins)
Step 3: Check dynamic routes (BGP, priority 200)
192.168.2.0/24 → cloud-router?
Match: ROUTE
Step 4: Check special routes
199.36.153.x/30?
Match: ROUTE to Private Google APIs
Step 5: Check default route (priority 65535)
0.0.0.0/0 → default-igw?
Match: ROUTE to internet
Step 6: No match
→ Packet DROPPED
→ VM sees ICMP: Destination unreachable
Example:
Packet dst=172.16.0.5
No subnet route
No static route
No dynamic route
Not 199.36.153.x
Match 0.0.0.0/0 (default)
→ Routes to internet gateway
→ Requires external IP
→ If no external IP: DROPPEDObservability & Debugging
List All System Routes
bash
gcloud compute routes list \
--filter="network:prod-vpc" \
--format=table
Output:
NAME NETWORK DEST_RANGE NEXT_HOP_IP PRIORITY
default-route prod-vpc 0.0.0.0/0 default-igw 65535
prod-app prod-vpc 10.0.1.0/24 local 0
prod-db prod-vpc 10.0.2.0/24 local 0
route-to-onprem prod-vpc 192.168.0.0/16 vpn-gateway 1000Trace Routing Decision
bash
# Get all routes and sort by priority:
gcloud compute routes list \
--filter="network:prod-vpc" \
--format="table(priority, destination_range, next_hop_ip, name)" \
--sort-by priority
# Manually check if CIDR matches:
# E.g., for destination 192.168.1.5:
# - 0.0.0.0/0 matches (priority 65535)
# - 192.168.0.0/16 matches (priority 1000) → winsMonitor Route Changes
bash
gcloud logging read \
'resource.type="gce_network" AND
protoPayload.methodName="compute.routes.create"' \
--limit=10 \
--format=jsonBest Practices
✅ Do:
- Understand subnet routes are automatic (don't double-configure)
- Enable Private Google Access for private workloads
- Use Cloud NAT instead of external IPs (cost + security)
- Plan CIDR layout upfront (avoid delete/recreate)
- Document default route behavior to team
❌ Don't:
- Assume all traffic matches explicit routes (default route catches remainder)
- Delete subnet and recreate with different CIDR (loses routes)
- Forget that default route requires external IP or Cloud NAT
- Try to use reserved ranges (169.254.x.x, 224.0.0.0/4)
- Assume system routes can be modified (immutable)
Conclusion
System-generated routes provide the safety net for VPC traffic:
- Subnet routes: Automatic VPC-local connectivity
- Default route: Internet egress (explicit path)
- Private Google Access: Secure API access without external IPs
- Reserved paths: Implicit handling (metadata, multicast)
Understanding these routes prevents unexpected traffic flows and security leaks.