Public networks are unrelated, and routed rather than bridged
Caught in review: every public address sat in one /24, which made the three of them look like one network. They are not. The internet is a very large number of unrelated networks routing to each other, and a machine in one is many hops from a machine in another with no shared broadcast domain between them. Putting them in one prefix would have quietly made four false things true in the lab: machines resolving each other by ARP and talking directly, TTL never decrementing, broadcast and multicast crossing between them, and any two being adjacent. The third is not hypothetical. The as-is layer records that mesh names are deliberately not multicast names, after a delay and a one-node-only failure mode. A lab where the internet is one broadcast domain would let a node discover a peer by multicast that it could never discover in production, and report success — the exact false green this effort exists to prevent. So a scenario has one public segment per public NETWORK, each with its own unrelated prefix, wired together through a router and never onto a shared bridge. That is a property of how the lab wires them rather than a field anyone sets, because no correct scenario has two public networks adjacent. Addresses now spread across all three RFC 5737 ranges plus RFC 3849 /48s, chosen to look nothing like each other, and a foreign private network uses someone else's RFC 1918 range rather than a documentation one. All three examples in the document rewritten, since two of them still showed a single flat internet segment and contradicted the new rule.
This commit is contained in:
@@ -17,6 +17,54 @@ everything in the lab hangs off, so it is worth getting small.
|
|||||||
It states what a hosting provider and a home router would provide, and nothing the mesh is
|
It states what a hosting provider and a home router would provide, and nothing the mesh is
|
||||||
responsible for ([ADR 0031](../../02-DECISIONS/0031-the-lab-provides-the-underlay.md)).
|
responsible for ([ADR 0031](../../02-DECISIONS/0031-the-lab-provides-the-underlay.md)).
|
||||||
|
|
||||||
|
## Public networks are unrelated, and routed rather than bridged
|
||||||
|
|
||||||
|
The internet is not a network. It is a very large number of unrelated networks that route to
|
||||||
|
each other, and a machine in one is **many hops** from a machine in another with no shared
|
||||||
|
broadcast domain between them.
|
||||||
|
|
||||||
|
So a scenario does not have *an* internet segment. It has **one public segment per public
|
||||||
|
network**, each with its own unrelated prefix, and the lab wires them together **through a
|
||||||
|
router, never onto a shared bridge**.
|
||||||
|
|
||||||
|
That distinction is load-bearing, and putting several public addresses in one prefix would
|
||||||
|
quietly make four things true that are false in reality:
|
||||||
|
|
||||||
|
| If public addresses share a segment | Reality |
|
||||||
|
|---|---|
|
||||||
|
| machines resolve each other by ARP and talk directly | they are routed, many hops apart |
|
||||||
|
| TTL never decrements | every hop decrements it |
|
||||||
|
| broadcast and multicast reach across | neither crosses a router |
|
||||||
|
| any two are adjacent | adjacency is the exception, not the rule |
|
||||||
|
|
||||||
|
The third is not hypothetical here. The mesh has already been bitten by multicast name
|
||||||
|
resolution — [`00-as-is/01-mesh-and-transport.md`](../00-as-is/01-mesh-and-transport.md)
|
||||||
|
records that mesh names are deliberately not multicast names, after a delay and a
|
||||||
|
one-node-only failure mode. A lab where "the internet" is one broadcast domain would let a node
|
||||||
|
discover a peer by multicast that it could never discover in production, and report success.
|
||||||
|
|
||||||
|
**The rule: `kind: public` segments are routed to one another and never bridged.** It is a
|
||||||
|
property of how the lab wires them, not a field anyone sets, because there is no correct
|
||||||
|
scenario in which two public networks are adjacent.
|
||||||
|
|
||||||
|
### Which addresses to use
|
||||||
|
|
||||||
|
RFC 5737 reserves three ranges and RFC 3849 reserves one v6 prefix. Each public network takes
|
||||||
|
its own, and they are chosen to look nothing like each other — because in reality they would
|
||||||
|
not:
|
||||||
|
|
||||||
|
| Public network | v4 | v6 |
|
||||||
|
|---|---|---|
|
||||||
|
| a hosting provider | `192.0.2.0/24` | `2001:db8:a::/48` |
|
||||||
|
| a household ISP | `198.51.100.0/24` | `2001:db8:b::/48` |
|
||||||
|
| a mobile or foreign network | `203.0.113.0/24` | `2001:db8:c::/48` |
|
||||||
|
|
||||||
|
Three is enough for the topologies that matter, and a fourth public network can subnet one of
|
||||||
|
them — an ISP handing out `198.51.100.0/25` and `198.51.100.128/25` to two customers is exactly
|
||||||
|
what really happens.
|
||||||
|
|
||||||
|
Private segments still use RFC 1918 and stay byte-identical to production.
|
||||||
|
|
||||||
## What NAT does, and why the design turns on it
|
## What NAT does, and why the design turns on it
|
||||||
|
|
||||||
A household or office has **one** address the outside world can see, and **many** machines
|
A household or office has **one** address the outside world can see, and **many** machines
|
||||||
@@ -103,53 +151,57 @@ The v6 documentation prefix is `2001:db8::/32` (RFC 3849) — the exact counterp
|
|||||||
scenario: roaming-and-published
|
scenario: roaming-and-published
|
||||||
|
|
||||||
segments:
|
segments:
|
||||||
internet:
|
hosting: # one public network
|
||||||
kind: public # RFC 5737 for v4, RFC 3849 for v6
|
kind: public
|
||||||
cidr: [203.0.113.0/24, 2001:db8::/32]
|
cidr: [192.0.2.0/24, 2001:db8:a::/48]
|
||||||
|
|
||||||
|
isp-home: # another, unrelated — routed to it, not bridged
|
||||||
|
kind: public
|
||||||
|
cidr: [198.51.100.0/24, 2001:db8:b::/48]
|
||||||
|
|
||||||
home:
|
home:
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [192.168.1.0/24, 2001:db8:1::/64]
|
cidr: [192.168.1.0/24, 2001:db8:b:1::/64]
|
||||||
mtu: 1500
|
mtu: 1500
|
||||||
gateway:
|
gateway:
|
||||||
to: internet
|
to: isp-home
|
||||||
address: 203.0.113.50 # what the world sees this network as, on v4
|
address: [198.51.100.7, 2001:db8:b::7] # what the world sees this network as
|
||||||
nat: [v4] # v4 is translated; v6 is routed, not translated
|
nat: [v4] # v4 is translated; v6 is routed, not translated
|
||||||
forwardable: true
|
forwardable: true
|
||||||
mapping_ttl: 120s # an unused inbound mapping is forgotten after this
|
mapping_ttl: 120s # an unused inbound mapping is forgotten after this
|
||||||
|
|
||||||
elsewhere: # a network we do not control
|
cafe: # a network we do not control
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [198.51.100.0/24] # v4 only — no v6 offered here at all
|
cidr: [10.50.0.0/16] # v4 only — no v6 offered here at all
|
||||||
mtu: 1400 # a tunnelled path, smaller than standard
|
mtu: 1400 # a tunnelled path, smaller than standard
|
||||||
gateway:
|
gateway:
|
||||||
to: internet
|
to: hosting # it reaches the world via a different public network
|
||||||
address: 203.0.113.80
|
address: [192.0.2.200]
|
||||||
nat: [v4]
|
nat: [v4]
|
||||||
forwardable: false # café wifi, or carrier-grade NAT
|
forwardable: false # café wifi, or carrier-grade NAT
|
||||||
mapping_ttl: 30s # aggressive, as carrier NAT tends to be
|
mapping_ttl: 30s # aggressive, as carrier NAT tends to be
|
||||||
|
|
||||||
machines:
|
machines:
|
||||||
anchor:
|
anchor:
|
||||||
at: { segment: internet, address: [203.0.113.10, 2001:db8::10] }
|
at: { segment: hosting, address: [192.0.2.10, 2001:db8:a::10] }
|
||||||
|
|
||||||
home-server:
|
home-server:
|
||||||
at: { segment: home, address: [192.168.1.135, 2001:db8:1::135] }
|
at: { segment: home, address: [192.168.1.135, 2001:db8:b:1::135] }
|
||||||
published:
|
published:
|
||||||
- { port: 443, on: home } # v4 only: 203.0.113.50:443 → 192.168.1.135:443
|
- { port: 443, on: home } # v4 only: 198.51.100.7:443 → 192.168.1.135:443
|
||||||
inbound: allow # v6 is routable here, so this decides whether it is reachable
|
inbound: allow # v6 is routable here, so this decides whether it is reachable
|
||||||
|
|
||||||
workstation:
|
workstation:
|
||||||
at: { segment: home, address: [192.168.1.250, 2001:db8:1::250] }
|
at: { segment: home, address: [192.168.1.250, 2001:db8:b:1::250] }
|
||||||
inbound: deny # a host firewall: dials out, accepts nothing
|
inbound: deny # a host firewall: dials out, accepts nothing
|
||||||
|
|
||||||
laptop:
|
laptop:
|
||||||
at: { segment: home, address: [192.168.1.98, 2001:db8:1::98] }
|
at: { segment: home, address: [192.168.1.98, 2001:db8:b:1::98] }
|
||||||
|
|
||||||
border: # a machine on two segments at once
|
border: # a machine on two segments at once
|
||||||
at:
|
at:
|
||||||
- { segment: home, address: [192.168.1.2] }
|
- { segment: home, address: [192.168.1.2] }
|
||||||
- { segment: internet, address: [203.0.113.60] }
|
- { segment: isp-home, address: [198.51.100.60] }
|
||||||
|
|
||||||
place:
|
place:
|
||||||
all: [host]
|
all: [host]
|
||||||
@@ -160,13 +212,14 @@ snapshot: raised
|
|||||||
|
|
||||||
## What each part means, precisely
|
## What each part means, precisely
|
||||||
|
|
||||||
**`segments`** — a broadcast domain with an address range, and a `kind:`.
|
**`segments`** — a broadcast domain with an address range, and a `kind:`. A segment is a single
|
||||||
|
broadcast domain, which is precisely why several public networks cannot be one segment.
|
||||||
|
|
||||||
`kind: public` marks the segment that stands in for the internet. `kind: private` is everything
|
`kind: public` marks a segment that stands in for a public network — and there is normally more
|
||||||
else. This is stated rather than inferred, and the earlier version inferred it — *a segment with
|
than one, unrelated to each other. `kind: private` is everything else. This is stated rather than inferred, and the earlier version inferred it — *a segment with
|
||||||
no gateway is the internet* — which made an **isolated network inexpressible**: a LAN with no
|
no gateway is the internet* — which made an **isolated network inexpressible**: a LAN with no
|
||||||
route out is a private segment with no gateway, and would have been read as the internet and
|
route out is a private segment with no gateway, and would have been read as public and forced
|
||||||
forced to use documentation addresses. A mesh spanning a site with no internet access is a real
|
to use documentation addresses. A mesh spanning a site with no internet access is a real
|
||||||
topology, and the model has to be able to say it.
|
topology, and the model has to be able to say it.
|
||||||
|
|
||||||
**`gateway:`** — how a segment reaches its parent, and this is where the previous version was
|
**`gateway:`** — how a segment reaches its parent, and this is where the previous version was
|
||||||
@@ -217,7 +270,7 @@ rather than a port list. `{ port: 443, on: home }` means the `home` gateway forw
|
|||||||
`203.0.113.50:443` to this machine's `443`. The resulting public endpoint is derivable, which is
|
`203.0.113.50:443` to this machine's `443`. The resulting public endpoint is derivable, which is
|
||||||
the point: a scenario never writes an endpoint down, and the mesh has to discover it.
|
the point: a scenario never writes an endpoint down, and the mesh has to discover it.
|
||||||
|
|
||||||
A machine may be published on **any gateway between it and the internet** — which is how *"our
|
A machine may be published on **any gateway between it and a public network** — which is how *"our
|
||||||
LAN also has a public IP"* is expressed, and why `on:` names the gateway rather than being
|
LAN also has a public IP"* is expressed, and why `on:` names the gateway rather than being
|
||||||
implied. It cannot be published at all on a gateway the scenario models as foreign; attempting
|
implied. It cannot be published at all on a gateway the scenario models as foreign; attempting
|
||||||
it is a declaration error, because that is precisely the constraint being reproduced.
|
it is a declaration error, because that is precisely the constraint being reproduced.
|
||||||
@@ -252,13 +305,8 @@ segment meant to be routable makes a would-be hub test as unreachable, and **the
|
|||||||
never forms** — no error, no failed step, just a mesh that does not exist. Research 004 calls
|
never forms** — no error, no failed step, just a mesh that does not exist. Research 004 calls
|
||||||
this the single most important fact in its analysis.
|
this the single most important fact in its analysis.
|
||||||
|
|
||||||
RFC 5737 reserves three ranges, which is exactly enough for the topology above:
|
Which range goes where is covered above, under *public networks are unrelated*: one range per
|
||||||
|
public network, chosen to look nothing like each other.
|
||||||
| Range | Used for |
|
|
||||||
|---|---|
|
|
||||||
| `203.0.113.0/24` | the internet segment itself — directly attached machines, and gateway addresses |
|
|
||||||
| `198.51.100.0/24` | a foreign network, so a roaming machine's apparent address is plainly not ours |
|
|
||||||
| `192.0.2.0/24` | spare — a second foreign network, or a second site |
|
|
||||||
|
|
||||||
Private segments use RFC 1918 and can be **byte-identical to production**, because those
|
Private segments use RFC 1918 and can be **byte-identical to production**, because those
|
||||||
addresses mean the same thing everywhere. Only the public side is substituted, and only because
|
addresses mean the same thing everywhere. Only the public side is substituted, and only because
|
||||||
@@ -352,11 +400,11 @@ segments:
|
|||||||
trusted:
|
trusted:
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [192.168.1.0/24]
|
cidr: [192.168.1.0/24]
|
||||||
gateway: { to: internet, address: 203.0.113.50, nat: [v4], forwardable: true }
|
gateway: { to: isp-home, address: [198.51.100.7], nat: [v4], forwardable: true }
|
||||||
devices:
|
devices:
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [192.168.30.0/24]
|
cidr: [192.168.30.0/24]
|
||||||
gateway: { to: internet, address: 203.0.113.50, nat: [v4], forwardable: true }
|
gateway: { to: isp-home, address: [198.51.100.7], nat: [v4], forwardable: true }
|
||||||
```
|
```
|
||||||
|
|
||||||
Identical gateway declarations mean **one gateway machine**, not two. The lab materialises a
|
Identical gateway declarations mean **one gateway machine**, not two. The lab materialises a
|
||||||
@@ -398,18 +446,29 @@ out completely, with every field the model has.
|
|||||||
scenario: the-ordinary-shape
|
scenario: the-ordinary-shape
|
||||||
|
|
||||||
segments:
|
segments:
|
||||||
internet:
|
# ---- three unrelated public networks. Routed to each other, never bridged. ----
|
||||||
|
|
||||||
|
hosting: # where the always-on machine lives
|
||||||
kind: public
|
kind: public
|
||||||
cidr: [203.0.113.0/24, 2001:db8::/32]
|
cidr: [192.0.2.0/24, 2001:db8:a::/48]
|
||||||
mtu: 1500
|
|
||||||
|
isp-home: # the household's uplink
|
||||||
|
kind: public
|
||||||
|
cidr: [198.51.100.0/24, 2001:db8:b::/48]
|
||||||
|
|
||||||
|
isp-mobile: # wherever the roaming machine happens to be
|
||||||
|
kind: public
|
||||||
|
cidr: [203.0.113.0/24, 2001:db8:c::/48]
|
||||||
|
|
||||||
|
# ---- private networks behind them ----
|
||||||
|
|
||||||
home: # the household network
|
home: # the household network
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [192.168.1.0/24, 2001:db8:1::/64]
|
cidr: [192.168.1.0/24, 2001:db8:b:1::/64]
|
||||||
mtu: 1492 # PPPoE on the uplink; 1500 if the line is not PPPoE
|
mtu: 1492 # PPPoE on the uplink; 1500 if the line is not PPPoE
|
||||||
gateway:
|
gateway:
|
||||||
to: internet
|
to: isp-home
|
||||||
address: 203.0.113.50 # the address the ISP hands the household
|
address: [198.51.100.7, 2001:db8:b::7]
|
||||||
nat: [v4] # v4 translated, v6 routed — the modern default
|
nat: [v4] # v4 translated, v6 routed — the modern default
|
||||||
forwardable: true # the household router is ours to configure
|
forwardable: true # the household router is ours to configure
|
||||||
mapping_ttl: 120s
|
mapping_ttl: 120s
|
||||||
@@ -418,21 +477,21 @@ segments:
|
|||||||
kind: private
|
kind: private
|
||||||
cidr: [192.168.30.0/24]
|
cidr: [192.168.30.0/24]
|
||||||
gateway:
|
gateway:
|
||||||
to: internet
|
to: isp-home
|
||||||
address: 203.0.113.50 # identical → the SAME gateway machine
|
address: [198.51.100.7, 2001:db8:b::7] # identical → the SAME gateway machine
|
||||||
nat: [v4]
|
nat: [v4]
|
||||||
forwardable: true
|
forwardable: true
|
||||||
mapping_ttl: 120s
|
mapping_ttl: 120s
|
||||||
|
|
||||||
elsewhere: # wherever the roaming machine happens to be
|
cafe: # a network we do not control
|
||||||
kind: private
|
kind: private
|
||||||
cidr: [198.51.100.0/24]
|
cidr: [10.50.0.0/16] # RFC 1918 — someone else's private range
|
||||||
mtu: 1400
|
mtu: 1400
|
||||||
gateway:
|
gateway:
|
||||||
to: internet
|
to: isp-mobile
|
||||||
address: 203.0.113.80
|
address: [203.0.113.129]
|
||||||
nat: [v4]
|
nat: [v4]
|
||||||
forwardable: false # someone else's network, or carrier-grade NAT
|
forwardable: false # carrier-grade NAT, or simply not ours
|
||||||
mapping_ttl: 30s
|
mapping_ttl: 30s
|
||||||
|
|
||||||
policy:
|
policy:
|
||||||
@@ -441,21 +500,21 @@ policy:
|
|||||||
|
|
||||||
machines:
|
machines:
|
||||||
anchor: # routable, nothing in front of it
|
anchor: # routable, nothing in front of it
|
||||||
at: { segment: internet, address: [203.0.113.10, 2001:db8::10] }
|
at: { segment: hosting, address: [192.0.2.10, 2001:db8:a::10] }
|
||||||
inbound: allow
|
inbound: allow
|
||||||
|
|
||||||
home-server: # publicly named, behind the household connection
|
home-server: # publicly named, behind the household connection
|
||||||
at: { segment: home, address: [192.168.1.135, 2001:db8:1::135] }
|
at: { segment: home, address: [192.168.1.135, 2001:db8:b:1::135] }
|
||||||
published:
|
published:
|
||||||
- { port: 443, on: home } # v4 reaches it only through the forward
|
- { port: 443, on: home } # v4 reaches it only through the forward
|
||||||
inbound: allow # and v6 reaches it directly, so this matters
|
inbound: allow # and v6 reaches it directly, so this matters
|
||||||
|
|
||||||
workstation: # on the household network, not published
|
workstation: # on the household network, not published
|
||||||
at: { segment: home, address: [192.168.1.250, 2001:db8:1::250] }
|
at: { segment: home, address: [192.168.1.250, 2001:db8:b:1::250] }
|
||||||
inbound: deny
|
inbound: deny
|
||||||
|
|
||||||
laptop: # starts at home; moves during the run
|
laptop: # starts at home; moves during the run
|
||||||
at: { segment: home, address: [192.168.1.98, 2001:db8:1::98] }
|
at: { segment: home, address: [192.168.1.98, 2001:db8:b:1::98] }
|
||||||
inbound: deny
|
inbound: deny
|
||||||
|
|
||||||
place:
|
place:
|
||||||
@@ -489,13 +548,18 @@ it is observed.
|
|||||||
### Running it
|
### Running it
|
||||||
|
|
||||||
```
|
```
|
||||||
move laptop → { segment: elsewhere, address: [198.51.100.23] } # it leaves the house
|
move laptop → { segment: cafe, address: [10.50.3.23] } # it leaves the house
|
||||||
move laptop → detached # it sleeps
|
move laptop → detached # it sleeps
|
||||||
move laptop → { segment: home, address: [192.168.1.98, 2001:db8:1::98] }
|
move laptop → { segment: home, address: [192.168.1.98, 2001:db8:b:1::98] }
|
||||||
```
|
```
|
||||||
|
|
||||||
One identity, four positions, one run. The `mapping_ttl: 30s` on `elsewhere` means a connection
|
One identity, four positions, one run. The `mapping_ttl: 30s` on `cafe` means a connection held
|
||||||
held without refreshing dies while it is out — which is the point of putting a number there.
|
without refreshing dies while it is out — which is the point of putting a number there.
|
||||||
|
|
||||||
|
Note that the laptop's three positions are on **three different public networks**: at home it
|
||||||
|
appears as `198.51.100.7`, at the café as `203.0.113.129`, and the machine it is trying to reach
|
||||||
|
is on a fourth. None of them are adjacent, all of them are routed. That is the situation the
|
||||||
|
mesh actually faces.
|
||||||
|
|
||||||
## Is this general? — the axes a setup can vary along
|
## Is this general? — the axes a setup can vary along
|
||||||
|
|
||||||
@@ -515,7 +579,8 @@ how the mesh behaves**. Bandwidth does not change correctness; MTU does.
|
|||||||
| **Gateway state** | permanent · expiring mappings | yes | `mapping_ttl:` |
|
| **Gateway state** | permanent · expiring mappings | yes | `mapping_ttl:` |
|
||||||
| **Path MTU** | standard · reduced | yes | `segments[].mtu` |
|
| **Path MTU** | standard · reduced | yes | `segments[].mtu` |
|
||||||
| **Overlapping ranges** | distinct · two sites both on `192.168.1.0/24` | yes | segments may carry the same range |
|
| **Overlapping ranges** | distinct · two sites both on `192.168.1.0/24` | yes | segments may carry the same range |
|
||||||
| **Segment count** | one · many · isolated island | yes | `kind:` distinguishes an island from the internet |
|
| **Segment count** | one · many · isolated island | yes | `kind:` distinguishes an island from a public network |
|
||||||
|
| **Adjacency** | same segment · routed · unrelated public networks | yes | public segments are routed, never bridged — so no two are adjacent unless declared so |
|
||||||
| **Gateway depth** | direct · one gateway · nested | **partly** | `to:` chains, so nesting exists; `published:` names one gateway, so forwarding through two does not |
|
| **Gateway depth** | direct · one gateway · nested | **partly** | `to:` chains, so nesting exists; `published:` names one gateway, so forwarding through two does not |
|
||||||
| **Address stability** | static · dynamic · changes mid-run | **partly** | a machine can be *moved*; an address changing under it in place cannot be stated |
|
| **Address stability** | static · dynamic · changes mid-run | **partly** | a machine can be *moved*; an address changing under it in place cannot be stated |
|
||||||
| **Path quality** | latency · loss · bandwidth | **no**, deliberately | changes performance, not correctness — modelling it makes a network simulator, not a fixture |
|
| **Path quality** | latency · loss · bandwidth | **no**, deliberately | changes performance, not correctness — modelling it makes a network simulator, not a fixture |
|
||||||
|
|||||||
Reference in New Issue
Block a user