🌐 Cloud Networking Essentials: Addresses, Subnets, Routes and Rules

The seven ideas behind every cloud network (address space, subnets, routes, gateways, firewall rules, DNS, connections), with the address and routing maths run for real, and what Azure, AWS, Google Cloud and Oracle Cloud call each of them

💡 How this page was made. The maths on this page was run for real: the address calculations, the subnet plan, the route lookups and the firewall-rule demo are Python programs, run on 6 October 2026 with the ipaddress module that comes with Python (nothing was downloaded). The firewall and route demos are small models I wrote to show the idea, not the clouds' real engines. The facts about each cloud (reserved addresses, defaults, limits) are from the providers' documentation, read on 6 October 2026, and nothing was run on a cloud account. The same ideas appear in the services maps and in the FreeBSD Corner's pf firewall, WireGuard and NFS pages.

Seven ideas

A cloud network is a software copy of an ordinary network. Learn these seven ideas once and the four clouds are one subject with four vocabularies:

  1. An address space: the block of private addresses that is yours (written in CIDR, like 10.0.0.0/16).
  2. Subnets: slices of that block, where things are placed.
  3. Routes: the table that says where each destination's traffic goes next.
  4. Gateways: the doors out: to the internet, to the internet but outbound only (NAT), to another network.
  5. Firewall rules: what traffic is allowed in and out.
  6. Names: DNS, inside and outside.
  7. Connections: peering, VPNs and private links between networks.

What each cloud calls the first one:

Addresses: CIDR in five minutes

A network address is written as an address plus a slash and a number, such as 10.0.0.0/16. The number says how many bits are fixed (the "network part"). The rest is free for hosts. A bigger number means a smaller network: /16 is 65,536 addresses, /24 is 256, /28 is 16. Real output from Python:

>>> import ipaddress
>>> vpc = ipaddress.ip_network('10.0.0.0/16')
>>> vpc.num_addresses
65536
>>> vpc.netmask
IPv4Address('255.255.0.0')
>>> vpc.is_private
True
>>> subnets = list(vpc.subnets(new_prefix=24))
>>> len(subnets)
256
>>> [str(s) for s in subnets[:3]]
['10.0.0.0/24', '10.0.1.0/24', '10.0.2.0/24']
>>> str(subnets[-1])
'10.0.255.0/24'
>>> ipaddress.ip_network('10.0.1.0/24').num_addresses
256
>>> ipaddress.ip_address('10.0.1.77') in ipaddress.ip_network('10.0.1.0/24')
True
>>> ipaddress.ip_address('10.0.2.77') in ipaddress.ip_network('10.0.1.0/24')
False
>>> ipaddress.ip_network('8.8.8.0/24').is_private
False
>>> ipaddress.ip_network('100.64.0.0/10').is_private
False

The reserved addresses

Every cloud keeps a few addresses in each subnet for itself. From the documentation (read on 6 October 2026):

So the usable size is smaller than the block. The table shows how fast that bites for small subnets (computed, not copied):

>>> import ipaddress
>>> for p in (16, 24, 28, 29, 30):
...     n = ipaddress.ip_network('10.0.0.0/%d' % p)
...     t = n.num_addresses
...     print('/%d  total %6d   AWS/Azure usable %6d   GCP usable %6d   OCI usable %6d' % (p, t, t-5, t-4, t-3))
... 
/16  total  65536   AWS/Azure usable  65531   GCP usable  65532   OCI usable  65533
/24  total    256   AWS/Azure usable    251   GCP usable    252   OCI usable    253
/28  total     16   AWS/Azure usable     11   GCP usable     12   OCI usable     13
/29  total      8   AWS/Azure usable      3   GCP usable      4   OCI usable      5
/30  total      4   AWS/Azure usable     -1   GCP usable      0   OCI usable      1

Overlaps: the number-one planning mistake

Two networks that overlap cannot be connected sensibly: routes become ambiguous. AWS's documentation says the subnets of one VPC cannot overlap each other, and the same logic applies between networks you peer, join by VPN or reach from your office. Check with a calculation, not by eye:

>>> import ipaddress
>>> a = ipaddress.ip_network('10.0.0.0/16')
>>> b = ipaddress.ip_network('10.0.5.0/24')
>>> c = ipaddress.ip_network('10.1.0.0/16')
>>> a.overlaps(b)
True
>>> a.overlaps(c)
False
>>> office = ipaddress.ip_network('192.168.0.0/16')
>>> [(str(n), n.overlaps(office)) for n in (a, c, ipaddress.ip_network('192.168.10.0/24'))]
[('10.0.0.0/16', False), ('10.1.0.0/16', False), ('192.168.10.0/24', True)]
>>> list(ipaddress.collapse_addresses([ipaddress.ip_network('10.0.0.0/24'), ipaddress.ip_network('10.0.1.0/24')]))
[IPv4Network('10.0.0.0/23')]
>>> str(ipaddress.ip_network('10.0.1.0/25').supernet(new_prefix=16))
'10.0.0.0/16'

The habit: choose address space before you create anything, write the plan down, and give each network, region and environment its own non-overlapping block. You can often add ranges later, but you cannot easily change a range that is in use (Google Cloud, for example, lets you expand a subnet's primary range, but not replace or shrink it).

Subnets

A subnet is a slice of the address space where resources are placed. The scope differs between clouds, and it matters for resilience:

Public and private

The words mean "can this thing be reached from the internet, and can it reach out?" The details are Oracle-specific and AWS-specific:

A good default design: public subnets for things that must be reached (load balancers), private subnets for everything else (application servers, databases), and a separate subnet for databases.

>>> import ipaddress
>>> vpc = ipaddress.ip_network('10.0.0.0/16')
>>> blocks = list(vpc.subnets(new_prefix=24))
>>> plan = {'public-a': blocks[0], 'public-b': blocks[1], 'private-a': blocks[10], 'private-b': blocks[11], 'database-a': blocks[20], 'database-b': blocks[21]}
>>> for name, net in plan.items():
...     print('%-11s %-14s %3d usable (AWS/Azure)' % (name, net, net.num_addresses - 5))
... 
public-a    10.0.0.0/24    251 usable (AWS/Azure)
public-b    10.0.1.0/24    251 usable (AWS/Azure)
private-a   10.0.10.0/24   251 usable (AWS/Azure)
private-b   10.0.11.0/24   251 usable (AWS/Azure)
database-a  10.0.20.0/24   251 usable (AWS/Azure)
database-b  10.0.21.0/24   251 usable (AWS/Azure)
>>> first = ipaddress.ip_network('10.0.0.0/24')
>>> h = list(first)
>>> print('network', h[0], '| router/gateway', h[1], '| DNS', h[2], '| reserved', h[3], '| first usable', h[4], '| broadcast', h[-1])
network 10.0.0.0 | router/gateway 10.0.0.1 | DNS 10.0.0.2 | reserved 10.0.0.3 | first usable 10.0.0.4 | broadcast 10.0.0.255

Here is a plan for a 10.0.0.0/16 network across two zones: two public, two private and two database subnets, each a /24 with 251 usable addresses (on AWS or Azure). The numbers are spaced out (0, 10, 20) so that there is room to grow and each tier is easy to recognize. The last two lines show the reserved addresses of the first subnet, as AWS documents them: the router at .1, DNS at .2 (the VPC base plus two), .3 reserved, and the first usable address at .4.

Routes

A route table says where traffic for each destination goes. Each rule has a destination range and a target (a gateway, a peering connection, a VPN, a network card). When several rules match, the most specific one wins (the longest prefix). A small model, with typical routes:

>>> import ipaddress
>>> routes = [('10.0.0.0/16', 'local'), ('10.0.10.0/24', 'peering-to-dev'), ('192.168.0.0/16', 'vpn-to-office'), ('0.0.0.0/0', 'internet-gateway')]
>>> table = [(ipaddress.ip_network(n), t) for n, t in routes]
>>> def lookup(ip):
...     ip = ipaddress.ip_address(ip)
...     best = max((r for r in table if ip in r[0]), key=lambda r: r[0].prefixlen, default=None)
...     if best is None: return '%s -> no route: the packet is dropped' % ip
...     return '%s -> %s (matched %s)' % (ip, best[1], best[0])
... 
>>> for dest in ('10.0.3.9', '10.0.10.4', '192.168.4.20', '93.184.216.34'):
...     print(lookup(dest))
... 
10.0.3.9 -> local (matched 10.0.0.0/16)
10.0.10.4 -> peering-to-dev (matched 10.0.10.0/24)
192.168.4.20 -> vpn-to-office (matched 192.168.0.0/16)
93.184.216.34 -> internet-gateway (matched 0.0.0.0/0)
>>> table = [r for r in table if r[1] != 'internet-gateway']
>>> print(lookup('93.184.216.34'))
93.184.216.34 -> no route: the packet is dropped

How the clouds do it:

Gateways

Firewall rules

Every cloud filters traffic with rules. Three words decide how they behave:

What the documentation says for each:

A model of priority, first match wins and defaults, in the Azure style (my own demo, with rules like the ones above):

>>> rules = [(100, 'Allow', 'tcp', 22, '203.0.113.0/24'), (200, 'Deny', 'tcp', 22, '0.0.0.0/0'), (300, 'Allow', 'tcp', 443, '0.0.0.0/0'), (65000, 'Allow', 'any', None, 'VirtualNetwork'), (65500, 'Deny', 'any', None, '0.0.0.0/0')]
>>> import ipaddress
>>> def decide(proto, port, src):
...     ip = ipaddress.ip_address(src)
...     for prio, act, p, prt, source in sorted(rules):
...         if p not in ('any', proto): continue
...         if prt not in (None, port): continue
...         if source == 'VirtualNetwork': ok = ip in ipaddress.ip_network('10.0.0.0/16')
...         else: ok = ip in ipaddress.ip_network(source)
...         if ok: return '%s by rule %d' % (act, prio)
... 
>>> decide('tcp', 22, '203.0.113.9')
'Allow by rule 100'
>>> decide('tcp', 22, '198.51.100.7')
'Deny by rule 200'
>>> decide('tcp', 443, '198.51.100.7')
'Allow by rule 300'
>>> decide('tcp', 3306, '10.0.4.4')
'Allow by rule 65000'
>>> decide('tcp', 3306, '198.51.100.7')
'Deny by rule 65500'

Rules of thumb that hold on every cloud:

Names: DNS

Connecting networks

Load balancers, in one paragraph

A load balancer spreads incoming traffic over several machines and checks which are healthy. Each cloud has a layer 7 one that understands web requests (AWS Application Load Balancer, Azure Application Gateway, Google Cloud Load Balancing, Oracle Load Balancer) and a layer 4 one for plain TCP and UDP (AWS Network Load Balancer, Azure Load Balancer, Oracle Network Load Balancer). Put them in the public subnets and the machines behind them in private ones. They bill for existing, so count them in your costs.

What costs money in a network

Common mistakes

What this page did not cover

IPv6, private endpoints and service endpoints, transit routing and hybrid DNS, egress filtering and firewall appliances, and network observability (flow logs). Each cloud's networking documentation covers these in depth.

The cheat sheet

← Terraform Basics: Describe It, Plan It, Apply It · ↑ All tutorials · Cloud IAM Essentials: Who Can Do What, and Where →