Network Wiki
AS207258 (ARTEMIS) — IPv6-only BGP network, RIPE NCC, AS-SET AS207258:AS-ALL
Network
Overview
AS207258 (named ARTEMIS) is a personal autonomous system registered with the RIPE NCC under organisation ORG-CLB2-RIPE. It runs a small, fully routed IPv6-only BGP network with four nodes across three countries.
| Field | Value |
|---|---|
| ASN | AS207258 |
| Name | ARTEMIS |
| Registry | RIPE NCC |
| Org | ORG-CLB2-RIPE |
| AS-SET | AS207258:AS-ALL |
| Protocol | IPv6-only (no IPv4) |
What is BGP?
The internet is made up of thousands of independently run networks. BGP (Border Gateway Protocol) is the system they all use to tell each other “I can reach these addresses, send traffic this way.” Think of it as GPS for the internet: each network (called an autonomous system, or AS) broadcasts the destinations it can deliver to, and routers use that information to pick the best path.
An ASN (autonomous system number) is the unique identifier for each network. AS207258 is this network’s number — it’s what the rest of the internet knows us by.
Topology at a glance
Four routers, three of which connect to the outside world:
- fra (Frankfurt) — edge router, iFog transit + FogIXP
- man (Manchester) — edge router, two upstreams
- tor (Toronto) — edge router, Xenyth transit
- helios (colocation) — internal only, no external BGP peers
All four run a full-mesh iBGP over WireGuard tunnels, so every router knows every route even if it didn’t learn it directly.
What makes it interesting
- IPv6-only — no IPv4 at all, not even tunnelled
- Anycast — the same
/48is announced from three locations; BGP path selection routes users to the nearest node - RPKI-validated — all incoming routes are verified against cryptographic ROAs; invalid routes are dropped
- Authoritative DNS — CoreDNS serves PTR zones for both prefixes, announced anycast from all three edge routers
See Prefixes & Addressing for the address layout, or Anycast for how the multi-site prefix works.
Prefixes & Addressing
Address pool
Per-location unicast prefixes come from 2a06:9801:1600::/40, a /40 block from Lagrange (AS209735). Each location gets its own /48 slice, announced only from that location.
| Prefix | Router | Location |
|---|---|---|
2a06:9801:1600::/48 |
— | reserved |
2a06:9801:1601::/48 |
fra | Frankfurt, DE |
2a06:9801:1602::/48 |
man | Manchester, UK |
2a06:9801:1603::/48 |
tor | Toronto, CA |
A /40 contains 256 /48s. So far three are in active use; helios has no unicast /48 currently assigned.
Anycast prefix
A second block, 2a0f:85c1:d68::/48, is announced simultaneously from fra, man, and tor. This is the anycast prefix — the same addresses exist on all three nodes, and BGP path selection routes each user to their nearest one.
See Anycast for detail on how this works and what services run on it.
RPKI ROAs
Two Route Origin Authorisations (ROAs) cover the address space, ensuring no other AS can legitimately announce these prefixes:
2a06:9801:1600::/40 max-length /48 origin AS207258
2a0f:85c1:d68::/48 max-length /48 origin AS207258
The first ROA covers all current and future per-location /48s from the Lagrange block. The second covers the anycast prefix. Both are RPKI Valid.
Loopback / dummy addresses
Each router adds its assigned addresses to a dummy interface (dummy1 on Debian nodes, lo on helios):
fra (dummy1)
2a06:9801:1601::1/48— router loopback2a0f:85c1:d68::1/48— anycast node-unique address2a0f:85c1:d68::53/48,2a0f:85c1:d68::54/48— CoreDNS listeners2a0f:85c1:d68::80/48— Caddy HTTPS2a0f:85c1:d68::123/48— chrony NTP
man (dummy1)
2a06:9801:1602::1/482a0f:85c1:d68::2/48,::53/48,::54/48,::80/48,::123/48
tor (dummy1)
2a06:9801:1603::1/482a0f:85c1:d68::3/48,::53/48,::54/48,::80/48,::123/48
helios (lo)
- no unicast /48 currently assigned
Internal tunnel range
WireGuard tunnels use fd00:2072:5820::/48 — a ULA (Unique Local Address) block that never appears in the global routing table. iBGP sessions run over these addresses.
| Router | WG address |
|---|---|
| helios | fd00:2072:5820::1 |
| man | fd00:2072:5820::2 |
| fra | fd00:2072:5820::3 |
| tor | fd00:2072:5820::4 |
Reverse DNS zones
PTR records are served by CoreDNS (authoritative, anycast):
| Zone | Covers |
|---|---|
8.6.d.0.1.c.5.8.f.0.a.2.ip6.arpa |
2a0f:85c1:d68::/48 |
6.1.1.0.8.9.6.0.a.2.ip6.arpa |
2a06:9801:1600::/40 |
The Lagrange /40 zone delegation is pending (Lagrange self-service portal). See CoreDNS.
Anycast
What is anycast?
Normally one IP address belongs to one machine. Anycast breaks that rule: the same address is announced from multiple locations, and BGP routes each user to the nearest one automatically. Think of a chain restaurant: every branch has the same phone number in your contacts, and your call goes to whichever is closest.
There is no load balancer, no DNS tricks, no proxy. The routing happens at the BGP level — each network on the internet picks the shortest/cheapest path to the prefix, and that determines which of our three nodes they reach.
The anycast prefix
2a0f:85c1:d68::/48 is announced from fra (Frankfurt), man (Manchester), and tor (Toronto).
Each node adds the entire /48 to its dummy1 interface so the addresses are locally reachable, then BIRD exports the prefix to that node’s upstreams via the standard route export filter.
Services
| Address | Service | Hostname |
|---|---|---|
2a0f:85c1:d68::1 |
fra node-unique | — |
2a0f:85c1:d68::2 |
man node-unique | — |
2a0f:85c1:d68::3 |
tor node-unique | — |
2a0f:85c1:d68::53 |
CoreDNS (primary) | ns1.as207258.net |
2a0f:85c1:d68::54 |
CoreDNS (secondary) | ns2.as207258.net |
2a0f:85c1:d68::80 |
Caddy HTTPS | ip.as207258.net |
2a0f:85c1:d68::123 |
chrony NTP | ntp.as207258.net |
The node-unique addresses (::1, ::2, ::3) exist to distinguish which node responded during RIPE Atlas measurements. A traceroute to 2a0f:85c1:d68::1 will reach fra regardless of where the probe is; to ::2 will reach man, to ::3 will reach tor.
How traffic gets distributed
Because all three nodes announce an identical /48 with the same AS path length, the upstream networks pick routes based on their own topology and peering. We influence this distribution using BGP communities:
- fra uses iFog communities to suppress the anycast route at LINX LON, LoNAP, and AMS-IX (no-export) so GB traffic prefers man
- tor applies a 2× AS path prepend to Xenyth so European networks don’t route to Toronto
See Traffic Engineering for current distribution measurements and the community details.
Failure behaviour
If one node loses its upstream BGP sessions or goes down, its announcement is withdrawn. Traffic that was going to that node automatically reroutes to one of the other two. This happens via normal BGP convergence — typically within 30–90 seconds depending on hold timers and upstream propagation.
There is no health-check withdrawal — if a service on a node crashes but BGP stays up, traffic continues to reach the dead node. For stateless services (DNS, NTP) this is fine. For the Caddy probe, it’s acceptable because the probe returning an error is itself useful information.
Current distribution (measured Aug 2026)
| Country | Preferred node | Notes |
|---|---|---|
| GB | man (~86%) | fra anycast suppressed at LINX/LoNAP/AMS-IX |
| DE | fra (~83%) | Frankfurt wins naturally; tor prepended |
| NL | fra (~89%) | Frankfurt closer to NL than Manchester |
| US/CA | man (majority) | Xenyth (tor’s transit) has weak NA peering |
iBGP & WireGuard
eBGP vs iBGP
eBGP (external BGP) is how AS207258 talks to the outside world — other ASNs like iFog, Xenyth, and Colocation MAN1.
iBGP (internal BGP) is how our own routers share routing information with each other, so every router knows every route even if it didn’t learn it directly. When fra learns a new route from iFog, it re-announces that route to man, tor, and helios via iBGP, so they can all forward traffic for it.
Full mesh
All four routers run a full-mesh iBGP: each router has a direct iBGP session to every other router, six sessions total. This avoids the iBGP route reflector complexity and ensures every router has an independent view of the full table.
Sessions are named IBGP<PEER>_AS207258_v6 (e.g. IBGPFRA_AS207258_v6 from man’s perspective).
WireGuard tunnels
iBGP sessions run inside WireGuard encrypted tunnels. The tunnels use addresses from fd00:2072:5820::/48 (ULA — not globally routable), so iBGP traffic never traverses the public internet unencrypted.
| Router | WG address | Public endpoint |
|---|---|---|
| helios | fd00:2072:5820::1 |
37.114.53.96:51825 (IPv4) |
| man | fd00:2072:5820::2 |
103.146.103.241:51820 (IPv4, DHCP) |
| fra | fd00:2072:5820::3 |
118.91.186.64:51820 (IPv4) |
| tor | fd00:2072:5820::4 |
207.174.104.100:51820 (IPv4) |
WireGuard config: interface wg0, MTU 1420, keepalive 25s on all peers.
Why IPv4 endpoints?
All tunnels involving tor use IPv4 endpoint addresses. The IPv6 transit path between Toronto and Europe proved unreliable — BGP hold timer expiry occurred repeatedly on the IPv6 path during the August 2026 session, causing iBGP sessions to flap. Switching to IPv4 endpoints resolved the instability immediately.
Fra and man also use IPv4 endpoints for their tunnels with tor (fra 118.91.186.64, man 103.146.103.241).
DHCP warning: Man’s IPv4 is DHCP. If the lease renews with a new IP, the man↔tor WireGuard tunnel will break. Fix it by updating the endpoint on tor:
wg set wg0 peer <man-pubkey> endpoint <new-ip>:51820
Then persist to /etc/wireguard/wg0.conf on both sides.
Tunnel topology
helios ─── fra ─── man
│ ╲ ╱ │
│ ╲ ╱ │
└──── tor ────────┘
Six tunnels for four nodes (n*(n-1)/2 = 6).
local-pref values
| Session type | local-pref | Effect |
|---|---|---|
| iBGP | 50 | Lower priority than direct upstreams |
| Upstream | 80 | Standard external routes |
| Helios iBGP | 150 | Helios prefers routes from all external peers |
By giving iBGP routes local-pref 50, a router that has a direct upstream route for a prefix will prefer it over an iBGP-learned route. Traffic exits from the nearest router, not from wherever it entered.
Helios routing
Helios uses kernel: export: false in its BIRD config — BIRD does not install routes into the kernel routing table. Packet forwarding from helios uses WireGuard AllowedIPs instead. Fra peer uses AllowedIPs ::/0 so all traffic exits via fra.
Helios peers:
- fra: endpoint
fra.de.as207258.net:51820, AllowedIPs::/0 - man: endpoint
man.gb.as207258.net:51820, AllowedIPsfd00:2072:5820::2/128 - tor: endpoint
207.174.104.100:51820(IPv4 to avoid lossy IPv6 path), AllowedIPsfd00:2072:5820::4/128
Traffic Engineering
Because the anycast prefix 2a0f:85c1:d68::/48 is announced from three locations, the rest of the internet decides which node a given network reaches based on AS path length and BGP policy. We influence this distribution using BGP communities.
The problem
Without any intervention, BGP’s shortest-path selection would route most traffic to the nearest AS207258 exit geographically. In practice:
- GB users would mostly reach fra (Frankfurt) because iFog (fra’s transit) has strong IXP presence including LINX — closer than man’s colocation transit for many UK providers
- European networks routed through Xenyth would sometimes end up in Toronto at 100ms instead of Frankfurt at 10ms
Tools available
iFog communities (applied on fra)
iFog (AS34927) exposes BGP communities that let customers control where their routes are propagated. We use two types:
2× AS path prepend — makes our route look two hops longer via specific IXPs/transit, so those networks prefer another path:
- Applied to large transit providers (Cogent, Telia, HE, GTT, Liberty Global, RETN)
No-export — suppresses the anycast route entirely at specific IXPs, so members of those IXPs never see fra’s announcement at all:
- LINX LON:
34927:9350 - LoNAP:
34927:9430 - AMS-IX:
34927:9540
These are set via Pathvector prefix-communities on fra’s anycast prefix (2a0f:85c1:d68::/48). The per-location prefix (2a06:9801:1601::/48) is not suppressed — those communities only apply to the anycast route.
Xenyth prepend (applied on tor)
Tor exports the anycast prefix to Xenyth (AS835) with a 2× AS path prepend. This is implemented as a BIRD pre-export filter in Pathvector. Without it, some European networks (notably Deutsche Telekom via Xenyth) would route to Toronto at ~100ms rather than Frankfurt at ~10ms.
Current distribution (measured Aug 2026)
| Country | Preferred node | Approximate split | Mechanism |
|---|---|---|---|
| GB | man | ~86% man, ~12% fra | No-export to LINX LON, LoNAP, AMS-IX |
| DE | fra | ~83% fra | Natural topology + tor prepend |
| NL | fra | ~89% fra | Frankfurt closer to NL than Manchester |
| US/CA | man | majority | Xenyth (AS835) has weak NA peering, 0 IXP memberships |
Known limitation: tor NA coverage
Xenyth (AS835, tor’s transit) has limited global peering — no IXP memberships in PeeringDB. This means NA users don’t reliably reach tor even though Toronto is geographically closer to many US/CA users than Manchester.
The 2× tor prepend was added to prevent EU traffic from routing to Toronto, but it also further reduces tor’s attractiveness to NA traffic. Options to improve this:
- Get tor a second transit with better NA IXP presence (e.g., HE AS6939)
- Persuade Xenyth to join major NA IXPs (unlikely)
- Accept the current state — man at ~100ms from NA is functional
Iterating on distribution
To adjust distribution:
- Check current state via RIPE Atlas measurements or the dashboard’s Distribution tab
- Identify the target: which country/network should shift to which node
- Find the iFog community for the relevant IXP or transit
- Add/modify
prefix-communitiesin fra’s pathvector.yml for the anycast prefix - Apply via
pathvector generate --dry-run→ review →pathvector generate - Wait ~15 minutes for propagation, then re-measure
Communities used so far are in fra’s /etc/pathvector.yml under the 2a0f:85c1:d68::/48 prefix entry.
Routers
fra — Frankfurt
| Field | Value |
|---|---|
| SSH alias | fra |
| Hostname | fra.de.as207258.net (port 11988) |
| Login user | debian |
| Sudo | required — prefix BIRD/Pathvector/Caddy commands with sudo |
| OS | Debian 13 (trixie), kernel 6.12.101 |
| BIRD | 2.19.2 |
| Pathvector | 6.3.2 |
Prefixes originated
| Prefix | Interface |
|---|---|
2a06:9801:1601::/48 |
dummy1 |
2a0f:85c1:d68::/48 |
dummy1 |
dummy1 addresses
2a06:9801:1601::1/48
2a0f:85c1:d68::1/48
2a0f:85c1:d68::53/48 ← CoreDNS ns1
2a0f:85c1:d68::54/48 ← CoreDNS ns2
2a0f:85c1:d68::80/48 ← Caddy HTTPS
2a0f:85c1:d68::123/48 ← chrony NTP
Config: /etc/netplan/200-dummy.yaml
Network interfaces
| Interface | Address | Purpose |
|---|---|---|
| eth0 | 2a0c:9a40:1036:101::1975/64 |
iFog transit |
| eth0 | 118.91.186.64 (IPv4) |
WireGuard endpoint for tor/man tunnels |
| eth19 | 2001:7f8:ca:1:0:20:7258:1/64 |
FogIXP LAN |
| wg0 | fd00:2072:5820::3/48 |
iBGP tunnel interface |
| dummy1 | (see above) | Originated prefixes + services |
BGP sessions
| Session | ASN | Neighbor | State | Type |
|---|---|---|---|---|
| iFog | 34927 | 2a0c:9a40:1036:101::1 |
Established | upstream (local-pref 80) |
| FogIXP RS1 | 47498 | 2001:7f8:ca:1::111 |
Established | route server |
| FogIXP RS2 | 47498 | 2001:7f8:ca:1::222 |
Established | route server |
| IBGPMAN | 207258 | fd00:2072:5820::2 |
Established | iBGP |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP |
| IBGPTOR | 207258 | fd00:2072:5820::4 |
Established | iBGP |
WireGuard peers
| Peer | Endpoint | AllowedIPs |
|---|---|---|
| helios | fra.de.as207258.net:51820 → helios connects to fra |
fd00:2072:5820::1/128 |
| man | 103.146.103.241:51820 (man’s IPv4, DHCP) |
fd00:2072:5820::2/128 |
| tor | 207.174.104.100:51820 (tor’s IPv4) |
fd00:2072:5820::4/128 |
All WG tunnels use IPv4 endpoints to avoid the unreliable IPv6 path issues that caused hold timer expiry. Config: /etc/wireguard/wg0.conf
Services running on fra
- Caddy —
ip.as207258.netHTTPS anycast (::80) - CoreDNS — authoritative DNS on
::53and::54 - chrony — NTP on
::123 - RIPE Atlas probe — sourced from
2a06:9801:1601::1 - traceroute-proxy — port 9099
Notes
- fra is the main traffic engineering node. iFog communities for anycast distribution are applied here.
sudois required for all BIRD, Pathvector, and Caddy commands.- FogIXP gives access to dozens of peers via the route servers without bilateral sessions.
- BIRD config is at
/etc/bird/— generated by Pathvector, never hand-edit. - Pathvector config:
/etc/pathvector.yml
man — Manchester
| Field | Value |
|---|---|
| SSH alias | man |
| Hostname | man.gb.as207258.net (port 11988) |
| Login user | root |
| Sudo | not required |
| OS | Debian 13 (trixie), kernel 6.12.101 |
| BIRD | 2.19.2 |
| Pathvector | 6.3.2 |
Note: man SSH intermittently times out — retry if it fails.
Prefixes originated
| Prefix | Interface |
|---|---|
2a06:9801:1602::/48 |
dummy1 |
2a0f:85c1:d68::/48 |
dummy1 |
dummy1 addresses
2a06:9801:1602::1/48
2a0f:85c1:d68::2/48
2a0f:85c1:d68::53/48 ← CoreDNS ns1
2a0f:85c1:d68::54/48 ← CoreDNS ns2
2a0f:85c1:d68::80/48 ← Caddy HTTPS
2a0f:85c1:d68::123/48 ← chrony NTP
Config: /etc/netplan/100-dummy.yaml
Network interfaces
| Interface | Address | Purpose |
|---|---|---|
| ens3 | 2a0f:85c2:101::66/128 |
transit link (/128 on transit LAN) |
| ens3 | 103.146.103.241 (IPv4, DHCP) |
WireGuard endpoint for tor tunnel |
| wg0 | fd00:2072:5820::2/48 |
iBGP tunnel interface |
| dummy1 | (see above) | Originated prefixes + services |
BGP sessions
| Session | ASN | Neighbor | State | Type |
|---|---|---|---|---|
| colocation | 44355 | 2a0f:85c2:101::1 |
Established | upstream (local-pref 80) |
| lagrange | 209735 | 2a0f:85c2:101::ff |
Established | upstream (local-pref 80) |
| bgptools | 212232 | 2a0c:2f07:9459::b14 |
Established | downstream |
| IBGPFRA | 207258 | fd00:2072:5820::3 |
Established | iBGP |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP |
| IBGPTOR | 207258 | fd00:2072:5820::4 |
Established | iBGP |
WireGuard peers
| Peer | Endpoint | AllowedIPs |
|---|---|---|
| helios | man.gb.as207258.net:51820 → helios connects to man |
fd00:2072:5820::1/128 |
| fra | 118.91.186.64:51820 (fra’s IPv4) |
fd00:2072:5820::3/128 |
| tor | 207.174.104.100:51820 (tor’s IPv4) |
fd00:2072:5820::4/128 |
Config: /etc/wireguard/wg0.conf
Services running on man
- Caddy —
ip.as207258.netHTTPS anycast (::80) - CoreDNS — authoritative DNS on
::53and::54 - chrony — NTP on
::123 - RIPE Atlas probe — sourced from
2a06:9801:1602::1 - traceroute-proxy — port 9099
Known issues
DHCP IPv4 (man↔tor tunnel)
Man’s IPv4 address (103.146.103.241) is assigned via DHCP. If the lease renews with a different IP, the WireGuard tunnel to tor will break silently.
Symptoms: man↔tor iBGP session drops, WG handshake times out.
Fix: update the WG endpoint on tor:
wg set wg0 peer <man-pubkey> endpoint <new-ip>:51820
Persist to /etc/wireguard/wg0.conf on both man and tor.
kernel: learn: true in BIRD
Man’s BIRD config has kernel: learn: true because ens3 uses a /128 address on the transit LAN. This causes KRT startup noise (“File exists” errors in Netlink) and can wedge BIRD’s I/O loop under load — seen in August 2026 where sessions got stuck in flush state with 18s/cycle I/O. If BIRD on man looks wedged (all sessions in flush, high CPU), restart it:
systemctl restart bird
tor — Toronto
| Field | Value |
|---|---|
| SSH alias | tor |
| Hostname | tor.ca.as207258.net (port 11988) |
| Login user | root |
| Sudo | not required |
| OS | Debian 13 (trixie) |
| BIRD | 2.19.2 |
| Pathvector | 6.3.2 |
Prefixes originated
| Prefix | Interface |
|---|---|
2a06:9801:1603::/48 |
dummy1 |
2a0f:85c1:d68::/48 |
dummy1 |
dummy1 addresses
2a06:9801:1603::1/48
2a0f:85c1:d68::3/48
2a0f:85c1:d68::53/48 ← CoreDNS ns1
2a0f:85c1:d68::54/48 ← CoreDNS ns2
2a0f:85c1:d68::80/48 ← Caddy HTTPS
2a0f:85c1:d68::123/48 ← chrony NTP
Config: /etc/systemd/network/10-dummy1.network
Network interfaces
| Interface | Address | Purpose |
|---|---|---|
| eth0 | (IPv6 from Xenyth) | upstream transit |
| eth0 | 207.174.104.100 (IPv4) |
WireGuard endpoint |
| wg0 | fd00:2072:5820::4/48 |
iBGP tunnel interface |
| dummy1 | (see above) | Originated prefixes + services |
BGP sessions
| Session | ASN | Neighbor | State | Type |
|---|---|---|---|---|
| Xenyth | 835 | 2602:fd50:100::5 |
Established | upstream (local-pref 80) |
| IBGPFRA | 207258 | fd00:2072:5820::3 |
Established | iBGP |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP |
| IBGPMAN | 207258 | fd00:2072:5820::2 |
Established | iBGP |
WireGuard peers
| Peer | Endpoint | AllowedIPs |
|---|---|---|
| fra | 118.91.186.64:51820 (fra’s IPv4) |
fd00:2072:5820::3/128 |
| man | 103.146.103.241:51820 (man’s IPv4, DHCP) |
fd00:2072:5820::2/128 |
| helios | 37.114.53.96:51825 (helios’s IPv4) |
fd00:2072:5820::1/128 |
Config: /etc/wireguard/wg0.conf
All tunnels use IPv4 endpoints — the IPv6 path between Toronto and Europe caused repeated hold timer expiry.
Export to Xenyth
Tor exports 2a06:9801:1603::/48 and 2a0f:85c1:d68::/48 to Xenyth via an accept_local() filter that matches the LOCALv6 prefix set (no community required).
A pre-export BIRD filter applies a 2× AS path prepend for the anycast prefix 2a0f:85c1:d68::/48 only. This prevents European networks (particularly Deutsche Telekom via Xenyth) from routing to Toronto at ~100ms transatlantic latency instead of Frankfurt at ~10ms.
Services running on tor
- Caddy —
ip.as207258.netHTTPS anycast (::80) - CoreDNS — authoritative DNS on
::53and::54 - chrony — NTP on
::123 - RIPE Atlas probe — sourced from
2a06:9801:1603::1 - traceroute-proxy — port 9099
System notes
- Swapfile:
/swapfile(2GB) — added because tor is memory-constrained - dummy1 is configured via systemd-network (
/etc/systemd/network/10-dummy1.network), not netplan
helios — Colocation
| Field | Value |
|---|---|
| SSH alias | helios |
| Hostname | helios.cb.sb (port 11988) |
| Login user | root |
| Sudo | not required |
| OS | Arch Linux |
| BIRD | 3.2.1 |
| Pathvector | 6.3.2 |
Role
Helios is the colocation server. Unlike fra/man/tor, it has no external BGP sessions — it only speaks iBGP internally. It does not currently originate any unicast prefix (no Lagrange /48 assigned yet).
Network interfaces
| Interface | Address | Purpose |
|---|---|---|
| eth0 | (public IPv6) | colocation uplink |
| eth0 | 37.114.53.96 (IPv4) |
WireGuard endpoint |
| wg0 | fd00:2072:5820::1/48 |
iBGP tunnel interface |
iBGP sessions
| Session | ASN | Neighbor | State | local-pref |
|---|---|---|---|---|
| ibgp-fra | 207258 | fd00:2072:5820::3 |
Established | 150 |
| ibgp-man | 207258 | fd00:2072:5820::2 |
Established | 150 |
| ibgp-tor | 207258 | fd00:2072:5820::4 |
Established | 150 |
Helios uses local-pref 150 on iBGP (higher than the 80 used by edge upstreams) — this makes helios strongly prefer routes learned from iBGP peers since it has no direct external routes of its own.
WireGuard peers
| Peer | Endpoint | AllowedIPs |
|---|---|---|
| fra | fra.de.as207258.net:51820 |
::/0 |
| man | man.gb.as207258.net:51820 |
fd00:2072:5820::2/128 |
| tor | 207.174.104.100:51820 (IPv4) |
fd00:2072:5820::4/128 |
The fra peer uses AllowedIPs ::/0 (default route) — all traffic exits via fra. The tor peer uses IPv4 to avoid an unreliable IPv6 path between the colocation and Toronto.
Config: /etc/wireguard/wg0.conf, ListenPort 51825 (not 51820 — different to avoid conflicts).
Routing quirk
Helios uses kernel: export: false — BIRD does not write routes into the kernel routing table. WireGuard AllowedIPs handle reachability. This is necessary because helios has no external BGP upstreams.
BIRD version note
Helios runs BIRD 3.2.1 (Arch Linux packages are newer). Fra/man/tor run BIRD 2.19.2 (Debian 13). The configs are compatible — Pathvector 6.3.2 targets BIRD 2.x syntax which is accepted by BIRD 3.x.
Services
CoreDNS — Authoritative DNS
What it does
CoreDNS serves authoritative PTR (reverse DNS) records for AS207258’s prefixes. It runs on fra, man, and tor, bound to the anycast addresses 2a0f:85c1:d68::53 and 2a0f:85c1:d68::54 — so DNS queries are routed to the nearest node automatically.
This means ns1.as207258.net and ns2.as207258.net both resolve to anycast IPs, not to fixed per-node addresses. All three nodes answer identically; there is no primary/secondary relationship.
Why CoreDNS?
CoreDNS is a single statically linked Go binary with a plugin-based Corefile config (similar to Caddy). It’s well-suited for a flat-file zone setup with no dynamic updates, no zone transfers, and identical zone files on every node. No database, no replication protocol — just files.
Deployment
| Item | Value |
|---|---|
| Binary | /usr/local/bin/coredns |
| Config | /etc/coredns/Corefile |
| Zones | /etc/coredns/zones/ |
| Service | coredns.service |
| Listen addresses | 2a0f:85c1:d68::53, 2a0f:85c1:d68::54 |
| Systemd unit | /etc/systemd/system/coredns.service |
Corefile
8.6.d.0.1.c.5.8.f.0.a.2.ip6.arpa {
bind 2a0f:85c1:d68::53 2a0f:85c1:d68::54
file /etc/coredns/zones/d68.arpa
log
errors
}
6.1.1.0.8.9.6.0.a.2.ip6.arpa {
bind 2a0f:85c1:d68::53 2a0f:85c1:d68::54
file /etc/coredns/zones/lagrange.arpa
log
errors
}
The bind directive tells CoreDNS to listen only on the anycast addresses, not :: — this avoids conflicting with systemd-resolved which binds 127.0.0.53.
Both server blocks bind to the same two addresses. CoreDNS merges them internally into one listener per address, serving both zones from the same socket.
Zone files
d68.arpa — 2a0f:85c1:d68::/48
Zone name: 8.6.d.0.1.c.5.8.f.0.a.2.ip6.arpa
| Address | PTR record |
|---|---|
2a0f:85c1:d68::1 |
fra.de.as207258.net. |
2a0f:85c1:d68::2 |
man.gb.as207258.net. |
2a0f:85c1:d68::3 |
tor.ca.as207258.net. |
2a0f:85c1:d68::80 |
ip.as207258.net. |
2a0f:85c1:d68::123 |
ntp.as207258.net. |
2a0f:85c1:d68::53 |
ns1.as207258.net. |
2a0f:85c1:d68::54 |
ns2.as207258.net. |
lagrange.arpa — 2a06:9801:1600::/40
Zone name: 6.1.1.0.8.9.6.0.a.2.ip6.arpa
| Address | PTR record |
|---|---|
2a06:9801:1601::1 |
fra.de.as207258.net. |
2a06:9801:1602::1 |
man.gb.as207258.net. |
2a06:9801:1603::1 |
tor.ca.as207258.net. |
RIPE delegation
DNS delegation is configured via RIPE domain objects pointing ns1/ns2 at the anycast addresses.
| Zone | Status |
|---|---|
8.6.d.0.1.c.5.8.f.0.a.2.ip6.arpa (d68 /48) |
Live — RIPE domain object created, delegation active |
6.1.1.0.8.9.6.0.a.2.ip6.arpa (Lagrange /40) |
Pending — Lagrange self-service portal (portal may be IPv4-only; ns1/ns2.as207258.net are AAAA-only) |
Once the Lagrange /40 delegation is live, PTR lookups for per-location /48 addresses (fra, man, tor loopbacks) will resolve.
Systemd unit
[Unit]
Description=CoreDNS
After=network-online.target
Wants=network-online.target
[Service]
User=coredns
Group=coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
Restart=always
RestartSec=2
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
After=network-online.target ensures CoreDNS starts only after the dummy1 addresses (::53, ::54) are up. Without this, CoreDNS would start before the addresses exist and only bind one or neither of them.
Updating zone files
- Edit
/etc/coredns/zones/d68.arpaand/or/etc/coredns/zones/lagrange.arpa - Bump the SOA serial (format
YYYYMMDDnn) - Repeat on all three nodes (fra, man, tor)
- Reload:
systemctl reload coredns(orkill -HUPthe process — CoreDNS re-reads zones on SIGHUP) - Verify with:
dig @2a0f:85c1:d68::53 -x <address> PTR
There is no replication — zone files must be updated manually on each node. Since zones are static (rarely change), this is acceptable.
Debugging
# Check listeners
ss -lnp6 | grep coredns
# Test from a node
dig @2a0f:85c1:d68::53 -x 2a0f:85c1:d68::80 PTR
drill @2a0f:85c1:d68::53 -x 2a0f:85c1:d68::80 # helios (no dig)
# Check RIPE delegation
dig @pri.authdns.ripe.net -x 2a0f:85c1:d68::80
# Test full recursive resolution
dig -x 2a0f:85c1:d68::80 # should return ip.as207258.net.
Caddy — Anycast HTTPS
What it does
Caddy serves ip.as207258.net from the anycast address 2a0f:85c1:d68::80 on fra, man, and tor. The response shows the client’s IPv6 address and which node answered — useful for verifying anycast routing and seeing which node is closest.
Details
| Item | Value |
|---|---|
| Binary | /usr/local/bin/caddy v2.11.4 (Cloudflare DNS plugin) |
| Config | /etc/caddy/Caddyfile |
| Cert storage | /opt/caddy |
| Service | caddy.service (custom unit at /etc/systemd/system/caddy.service) |
| Anycast address | 2a0f:85c1:d68::80 |
| Hostname | ip.as207258.net → AAAA 2a0f:85c1:d68::80 |
| TLS | Let’s Encrypt DNS-01 via Cloudflare API |
Response format
client: <remote_ip> node: <hostname> connected to: <local_host>:<local_port>
The {system.hostname} placeholder resolves per-node at runtime, so the same Caddyfile config is deployed identically on all three nodes.
Caddyfile
ip.as207258.net {
bind 2a0f:85c1:d68::80
tls {
dns cloudflare {env.CF_API_TOKEN}
}
respond "{remote_host} node {system.hostname} {http.request.local.host}:{http.request.local.port}"
}
(Exact syntax may vary — check the live file on each router.)
TLS certificate
Caddy handles TLS automatically using DNS-01 challenge via the Cloudflare DNS plugin. The cert covers ip.as207258.net and is stored in /opt/caddy. Since all three nodes use the same Cloudflare API token and domain, they all obtain the same cert (or Caddy does its own per-node issuance — either way it’s transparent).
Service management
# fra (requires sudo)
sudo systemctl status caddy
sudo systemctl restart caddy
# man / tor (no sudo)
systemctl status caddy
systemctl restart caddy
Testing
# From anywhere with IPv6
curl -6 https://ip.as207258.net/
# With verbose output to see TLS cert
curl -6 -v https://ip.as207258.net/ 2>&1 | head -30
The response shows which node answered. If you’re in GB, you should typically see node man; from DE, node fra.
NTP — chrony (Anycast)
What it does
chrony runs on fra, man, and tor, serving NTP from the anycast address 2a0f:85c1:d68::123. Any IPv6 client that can query ntp.as207258.net reaches the nearest node automatically.
Details
| Item | Value |
|---|---|
| Software | chrony |
| Service | chronyd.service |
| Listen address | 2a0f:85c1:d68::123 |
| Hostname | ntp.as207258.net → AAAA 2a0f:85c1:d68::123 |
| Nodes | fra, man, tor |
Configuration
chrony is bound to 2a0f:85c1:d68::123 on dummy1 of each node. The config typically includes:
bindaddress 2a0f:85c1:d68::123
allow ::/0
This allows any client to query. Since it’s anycast, each client reaches the nearest node.
Testing
# Check NTP response
chronyc -h 2a0f:85c1:d68::123 tracking
# Or with ntpdate (if installed)
ntpdate -q 2a0f:85c1:d68::123
Notes
ntp.as207258.netDNS record was pending as of Aug 2026 — verify it’s been added to Cloudflare.- NTP over anycast is inherently stateless (UDP), so failover is transparent — if one node goes down, the next query reaches a different node.
RIPE Atlas Software Probes
What it does
RIPE Atlas software probes are installed on fra, man, and tor. They connect to the RIPE NCC infrastructure via a reverse SSH tunnel and participate in the global RIPE Atlas measurement platform — allowing the network to run ping/traceroute measurements from AS207258 nodes to anywhere.
Details
| Item | Value |
|---|---|
| Package | ripe-atlas-probe (RIPE NCC apt repo) |
| Service | ripe-atlas.service |
| Nodes | fra, man, tor |
Source address binding
Each probe sources its controller SSH tunnel from the per-location /48 address, not the default outbound address:
| Router | Source address |
|---|---|
| fra | 2a06:9801:1601::1 |
| man | 2a06:9801:1602::1 |
| tor | 2a06:9801:1603::1 |
This is configured via BindAddress in /run/ripe-atlas/.ssh/config, written at service start by the systemd drop-in at /etc/systemd/system/ripe-atlas.service.d/bind-source.conf:
[Service]
ExecStartPre=+mkdir -p /run/ripe-atlas/.ssh
ExecStartPre=+chmod 700 /run/ripe-atlas/.ssh
ExecStartPre=+bash -c 'printf "Host *.atlas.ripe.net *.atlas.prod.ripe.net\n AddressFamily inet6\n BindAddress 2a06:9801:160X::1\n" > /run/ripe-atlas/.ssh/config'
ExecStartPre=+chmod 600 /run/ripe-atlas/.ssh/config
ExecStartPre=+chown -R ripe-atlas:ripe-atlas /run/ripe-atlas/.ssh
(Where 160X is 1601 on fra, 1602 on man, 1603 on tor.)
AddressFamily inet6 forces the SSH controller tunnel to use IPv6. BindAddress selects the per-location /48 loopback rather than the default outbound address.
Security note
RIPE Atlas probes maintain a persistent reverse SSH tunnel to RIPE NCC infrastructure. This is an accepted risk given the RIPE NCC trust model, but it’s worth noting as an attack surface on routing infrastructure.
Testing
# Check probe status
systemctl status ripe-atlas
# Verify source IP of controller tunnel
ss -tp | grep ripe-atlas
# Check which source address is in use
ip -6 route get 2001:67c:2e8:: # RIPE NCC range
Running measurements
Log in to atlas.ripe.net with the AS207258 account. Measurements sourced from these probes will show fra.de.as207258.net, man.gb.as207258.net, or tor.ca.as207258.net as the probe host in results.
Traceroute Proxy
What it does
A small Go HTTP service runs on fra, man, tor, and helios. It exposes two endpoints that the dashboard uses to gather per-node network data without requiring SSH access from the dashboard itself.
Details
| Item | Value |
|---|---|
| Service | traceroute-proxy.service |
| Port | 9099 |
| User | nobody |
| Capability | CAP_NET_ADMIN (for traceroute raw sockets) |
| Nodes | fra, man, tor, helios |
Source address
Traceroutes and RTT measurements are sourced from the per-location /48 loopback address, not the anycast node-unique IP:
| Router | Source address |
|---|---|
| fra | 2a06:9801:1601::1 |
| man | 2a06:9801:1602::1 |
| tor | 2a06:9801:1603::1 |
This is set via SOURCE_ADDR in the systemd drop-in /etc/systemd/system/traceroute-proxy.service.d/source-addr.conf.
Endpoints
| Endpoint | Command | Description |
|---|---|---|
GET /traceroute?target=<ipv6> |
traceroute6 -n -s $SOURCE_ADDR <target> |
Runs a traceroute from this node to the target IPv6 address |
GET /wg |
wg show all dump |
Returns WireGuard peer status (last handshake, transfer bytes) |
How the dashboard uses it
The dashboard calls these proxies via TRACEROUTE_PROXY_FRA, TRACEROUTE_PROXY_MAN, etc. environment variables (set in the Cloudflare Worker). Requests are authenticated with Cloudflare Access service tokens (CF-Access-Client-Id / CF-Access-Client-Secret headers).
The looking glass traceroute page fires requests to all three edge nodes in parallel and overlays the results on the map.
The WireGuard data feeds the Topology page’s edge state calculation — lastHandshake within 180 seconds counts as an alive tunnel.
Access control
The proxy is behind Cloudflare Access. It should not be publicly reachable without a valid service token. The dashboard’s API routes pass the service token through server-side fetch calls, so the token never reaches the browser.
Operations
Upstreams & Peers
Terminology
- Upstream (transit provider) — a network you pay to carry your traffic everywhere else. An upstream accepts your prefixes and propagates them to the global internet, and sends you all routes it knows about. Think of it as an ISP for your ASN.
- Route server peer — an IXP-operated server that redistributes routes between IXP members. You announce your prefixes to it, and it announces all other members’ prefixes to you.
- Downstream — a network that accepts your routes for their own purposes (e.g., route visibility, research).
- iBGP — internal BGP between your own routers (same ASN).
Sessions by router
fra — Frankfurt
| Session | ASN | Neighbor | State | Type | local-pref |
|---|---|---|---|---|---|
| iFog | 34927 | 2a0c:9a40:1036:101::1 |
Established | upstream | 80 |
| FogIXP RS1 | 47498 | 2001:7f8:ca:1::111 |
Established | route server | 80 |
| FogIXP RS2 | 47498 | 2001:7f8:ca:1::222 |
Established | route server | 80 |
| IBGPMAN | 207258 | fd00:2072:5820::2 |
Established | iBGP | 50 |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP | 50 |
| IBGPTOR | 207258 | fd00:2072:5820::4 |
Established | iBGP | 50 |
iFog (AS34927) — fra’s primary upstream. Also provides access to FogIXP, a small Frankfurt IXP. iFog exposes a rich set of BGP communities for traffic engineering (prepend, no-export at specific IXPs and transit providers).
FogIXP (AS47498) — fra connects to two route servers at FogIXP, giving indirect peering with all other IXP members without bilateral sessions.
man — Manchester
| Session | ASN | Neighbor | State | Type | local-pref |
|---|---|---|---|---|---|
| colocation | 44355 | 2a0f:85c2:101::1 |
Established | upstream | 80 |
| lagrange | 209735 | 2a0f:85c2:101::ff |
Established | upstream | 80 |
| bgptools | 212232 | 2a0c:2f07:9459::b14 |
Established | downstream | — |
| IBGPFRA | 207258 | fd00:2072:5820::3 |
Established | iBGP | 50 |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP | 50 |
| IBGPTOR | 207258 | fd00:2072:5820::4 |
Established | iBGP | 50 |
Colocation MAN1 (AS44355) — man’s primary upstream, co-located in Manchester.
Lagrange Cloud (AS209735) — man’s secondary upstream.
bgp.tools (AS212232) — downstream session. Receives our routes for route visibility and BGP analytics at bgp.tools.
tor — Toronto
| Session | ASN | Neighbor | State | Type | local-pref |
|---|---|---|---|---|---|
| Xenyth | 835 | 2602:fd50:100::5 |
Established | upstream | 80 |
| IBGPFRA | 207258 | fd00:2072:5820::3 |
Established | iBGP | 50 |
| IBGPHELIOS | 207258 | fd00:2072:5820::1 |
Established | iBGP | 50 |
| IBGPMAN | 207258 | fd00:2072:5820::2 |
Established | iBGP | 50 |
Xenyth / GoCodeIT (AS835) — tor’s only upstream. Known limitation: AS835 has no IXP memberships in PeeringDB, which means NA traffic doesn’t always reach tor — many NA networks route to man instead via transatlantic transit.
helios — iBGP only
| Session | ASN | Neighbor | State | Type | local-pref |
|---|---|---|---|---|---|
| ibgp-fra | 207258 | fd00:2072:5820::3 |
Established | iBGP | 150 |
| ibgp-man | 207258 | fd00:2072:5820::2 |
Established | iBGP | 150 |
| ibgp-tor | 207258 | fd00:2072:5820::4 |
Established | iBGP | 150 |
Helios has no external sessions.
What we export
Each edge router exports two prefixes to its external peers:
| Router | Exported prefixes |
|---|---|
| fra | 2a06:9801:1601::/48, 2a0f:85c1:d68::/48 |
| man | 2a06:9801:1602::/48, 2a0f:85c1:d68::/48 |
| tor | 2a06:9801:1603::/48, 2a0f:85c1:d68::/48 |
Via iBGP, each router also learns the per-location prefixes of the others.
Outstanding / missing sessions
- GoCodeIT (AS207841) — declared in the RIPE aut-num but no active BGP session. Needs GoCodeIT’s BGP endpoint before the session can be set up.
BGP Communities
BGP communities are metadata tags attached to route announcements. Routers can read these tags and change how they handle the route. AS207258 uses large communities (RFC 8092) with three 32-bit fields: ASN:function:value.
Informational communities (set on import)
These are added automatically when a route is imported, recording where it came from.
| Community | Meaning |
|---|---|
207258:0:12 |
Learned from upstream (transit) |
207258:0:13 |
Learned from route server |
207258:0:14 |
Learned from peer |
207258:0:15 |
Learned from downstream / locally originated |
207258:0:16 |
Learned via iBGP |
207258:0:18 |
Learned from bgp.tools |
Action communities (set by route owners)
These can be attached to routes by peers or downstreams to influence how AS207258 re-announces them. The check runs in a pre-export filter before the outbound filter strips all communities.
| Community | Effect |
|---|---|
207258:666:0 |
Blackhole — do not re-announce to any external peer |
207258:65:1 |
Prepend AS207258 × 1 on re-announcement |
207258:65:2 |
Prepend AS207258 × 2 on re-announcement |
207258:65:3 |
Prepend AS207258 × 3 on re-announcement |
iFog transit communities (applied to our own prefixes on fra)
iFog (AS34927) exposes communities that customers can attach to their routes to control where iFog propagates them. We use these for anycast traffic engineering.
No-export (suppress entirely at an IXP)
| Community | Effect |
|---|---|
34927:9350 |
No-export to LINX LON members |
34927:9430 |
No-export to LoNAP members |
34927:9540 |
No-export to AMS-IX members |
These are applied only to 2a0f:85c1:d68::/48 (the anycast prefix). The per-location prefix 2a06:9801:1601::/48 is not suppressed.
AS path prepend (make path look less attractive)
Used for large transit providers to reduce anycast traffic reaching tor from Europe:
| Provider | iFog community |
|---|---|
| Cogent | (check pathvector.yml) |
| Telia | (check pathvector.yml) |
| Hurricane Electric | (check pathvector.yml) |
| GTT | (check pathvector.yml) |
| Liberty Global | (check pathvector.yml) |
| RETN | (check pathvector.yml) |
Check fra’s /etc/pathvector.yml under the prefix-communities key for the exact community values in use.
Tor Xenyth prepend
Tor applies a 2× AS path prepend for 2a0f:85c1:d68::/48 when exporting to Xenyth (AS835). This is implemented as a BIRD pre-export filter in tor’s Pathvector config — not via Xenyth communities. It prevents European networks (particularly Deutsche Telekom which peers via Xenyth) from routing to Toronto instead of Frankfurt.
RPKI — Route Origin Validation
What is RPKI?
Without verification, anyone could announce “I own these addresses” and hijack traffic. RPKI (Resource Public Key Infrastructure) is a cryptographic system where the real owner of an address block signs a certificate (called a ROA, or Route Origin Authorisation) that says which AS is allowed to announce it. Routers check these signatures and drop routes that don’t match.
How it works in AS207258
All four routers (fra, man, tor, helios) connect to Cloudflare’s RPKI-to-Router (RTR) server:
rtr.rpki.cloudflare.com:8282
The RTR session is named rpki1 and is Established on all nodes. BIRD uses this to build an in-memory table of valid ROAs (rpki6). On import, every route is checked:
- RPKI Valid — AS and prefix match a ROA, route is accepted
- RPKI Unknown — no ROA covers this prefix, route is accepted (not all operators have ROAs)
- RPKI Invalid — a ROA exists but this announcement doesn’t match it, route is dropped
AS207258 ROAs
Two ROAs cover all our address space, preventing any other AS from legitimately announcing these prefixes:
| Prefix | Max-length | Origin |
|---|---|---|
2a06:9801:1600::/40 |
/48 |
AS207258 |
2a0f:85c1:d68::/48 |
/48 |
AS207258 |
The first ROA covers all current and future per-location /48s. The max-length /48 means more-specific announcements (e.g., a /52 or /56) from AS207258 would be RPKI Invalid — we only announce /48s.
Checking RPKI status
# Check RTR session
birdc show protocols rpki1
# Look up a specific prefix in the RPKI table
birdc show route table rpki6 | grep <prefix-fragment>
# Check a route's RPKI status
birdc show route for 2a0f:85c1:d68::/48 all | grep rpki
If rpki1 is not Established
If the RTR session drops, BIRD stops receiving ROA updates but uses its last-known table. The session should reconnect automatically. If it stays down for an extended period and you want to force a reconnect:
birdc disable rpki1
birdc enable rpki1
Do not restart BIRD just because rpki1 is flapping — it will reconnect on its own.
Change Workflow
All config changes follow this sequence to avoid outages. Never skip steps; never work on two routers at once.
Golden rules
- Read-only until approved — only
show,status,cat,--dry-runcommands unless explicitly told to proceed - One router at a time — verify sessions and prefix counts on the first router before touching the second
- Back up before modifying —
./backups/<host>/<timestamp>-<filename> - Never hand-edit BIRD config —
/etc/bird/is generated by Pathvector; edit/etc/pathvector.ymlinstead - Never touch firewall rules, sshd config, sudoers, or network interfaces without user doing it manually — lockout risk
- Stop and report — if a session drops, RPKI goes invalid, or anything looks off, stop immediately
BGP config changes (Pathvector workflow)
- Edit
/etc/pathvector.ymlon the target router - Run
pathvector generate --dry-run(prefixsudoon fra) and review the BIRD config diff - Show the diff and get approval before proceeding
- On approval:
pathvector generate→birdc show protocolsto verify sessions - Check prefix counts:
birdc show route count - Repeat on the next router only after the first is confirmed stable
File backup convention
# Before modifying any file on a router:
# ./backups/<host>/<timestamp>-<filename>
# Example:
cp /etc/pathvector.yml ./backups/fra/$(date +%Y%m%dT%H%M%S)-pathvector.yml
Backups go into the local ./backups/ directory (next to the SSH config), not on the router itself.
SSH invocation
ssh -F ./config -i ./id_ed25519 -o ConnectTimeout=10 -o BatchMode=yes fra "sudo birdc show protocols all"
ssh -F ./config -i ./id_ed25519 -o ConnectTimeout=10 -o BatchMode=yes man "birdc show protocols all"
ssh -F ./config -i ./id_ed25519 -o ConnectTimeout=10 -o BatchMode=yes helios "birdc show protocols all"
ssh -F ./config -i ./id_ed25519 -o ConnectTimeout=10 -o BatchMode=yes tor "birdc show protocols all"
The config file is ./config (not ./ssh_config). fra requires sudo for BIRD commands.
Verifying after a change
# Session states — all should be Established (or expected state)
birdc show protocols all
# Route counts — should be consistent across routers
birdc show route count
# RPKI validator — rpki1 should be Established
birdc show protocols rpki1
# Specific prefix
birdc show route for 2a0f:85c1:d68::/48
WireGuard endpoint updates
If man’s IPv4 (DHCP) changes and breaks the man↔tor tunnel:
# On tor:
wg set wg0 peer <man-pubkey> endpoint <new-ip>:51820
# Persist (on tor):
# Edit /etc/wireguard/wg0.conf, update man's Endpoint line
# Persist (on man):
# Edit /etc/wireguard/wg0.conf, update tor's Endpoint line
# wg syncconf wg0 <(wg-quick strip wg0)
Service restarts
# CoreDNS (all three nodes)
systemctl restart coredns # man/tor
sudo systemctl restart coredns # fra
# BIRD (avoid unless necessary — sessions will flap)
systemctl restart bird # man/tor/helios
sudo systemctl restart bird # fra
# Caddy
systemctl restart caddy # man/tor
sudo systemctl restart caddy # fra
What NOT to do
- Do not run
birdc configuredirectly — use Pathvector - Do not run
pathvector generatewithout--dry-runfirst - Do not use
git add .orgit add -A— may accidentally commit the private key - Do not paste or echo the private key (
./id_ed25519) - Do not
git push --forceon main
Software Stack
BIRD
BGP routing daemon. Runs on all four routers. Manages BGP sessions, RPKI validation via RTR, and the kernel routing table.
| Router | BIRD version |
|---|---|
| fra | 2.19.2 |
| man | 2.19.2 |
| tor | 2.19.2 |
| helios | 3.2.1 (Arch Linux) |
BIRD config lives at /etc/bird/ and is generated by Pathvector — never hand-edit it. The Pathvector-generated files will be overwritten on the next pathvector generate run.
Key BIRD commands:
birdc show protocols all # all session states
birdc show route count # route counts per table
birdc show route for <prefix> # how we'd route this prefix
birdc show protocols rpki1 # RPKI validator session
birdc show route table rpki6 # RPKI ROA table
Pathvector
BIRD config generator. Takes a high-level YAML file (/etc/pathvector.yml) and generates complete BIRD config with sensible defaults, filters, and community handling.
Version: 6.3.2 on all routers.
# Preview changes (always do this first)
pathvector generate --dry-run # man/tor/helios
sudo pathvector generate --dry-run # fra
# Apply
pathvector generate # man/tor/helios
sudo pathvector generate # fra
The pathvector-gen wrapper at /usr/local/sbin/pathvector-gen exists on all routers but is not used — run pathvector generate directly.
WireGuard
Encrypted tunnel mesh. Creates point-to-point tunnels between all four routers. iBGP sessions run inside these tunnels over the fd00:2072:5820::/48 ULA range.
- Interface:
wg0on all routers - Port: 51820 (fra/man/tor), 51825 (helios)
- MTU: 1420
- Keepalive: 25s
- Config:
/etc/wireguard/wg0.conf
All tunnels involving tor use IPv4 endpoints (IPv6 path was unreliable).
Caddy
Anycast HTTPS server. Runs on fra, man, tor. Custom build with Cloudflare DNS plugin for Let’s Encrypt DNS-01 challenges.
- Version: 2.11.4
- Binary:
/usr/local/bin/caddy - Config:
/etc/caddy/Caddyfile - Certs:
/opt/caddy - Unit:
/etc/systemd/system/caddy.service(custom, not the apt package)
CoreDNS
Authoritative DNS server. Runs on fra, man, tor. Serves PTR zones for both prefixes from the anycast DNS addresses.
- Binary:
/usr/local/bin/coredns - Config:
/etc/coredns/Corefile - Zones:
/etc/coredns/zones/ - Unit:
/etc/systemd/system/coredns.service - Listens on:
2a0f:85c1:d68::53and::54
chrony
NTP server. Runs on fra, man, tor. Bound to the anycast address 2a0f:85c1:d68::123.
- Service:
chronyd.service - Hostname:
ntp.as207258.net
RIPE Atlas probe
Network measurement. Software probes on fra, man, tor. Connected to the RIPE Atlas platform, sourcing their controller tunnel from per-location /48 addresses.
- Package:
ripe-atlas-probe(RIPE NCC apt repo) - Service:
ripe-atlas.service - Source: fra=
2a06:9801:1601::1, man=2a06:9801:1602::1, tor=2a06:9801:1603::1
traceroute-proxy
Dashboard data proxy. Small Go HTTP service on fra, man, tor, helios. Exposes /traceroute and /wg endpoints for the dashboard.
- Port: 9099
- Service:
traceroute-proxy.service - User:
nobody+CAP_NET_ADMIN
Dashboard
Ops UI. Astro 5 app deployed to Cloudflare Workers via @astrojs/cloudflare.
- Public paths (
/,/lg,/peering,/anycast): looking glass, traceroute, peering form — no auth - Ops paths (
/ops/*): protected by Cloudflare Access - Repo:
/Users/conor/code/NET/site - Domain:
as207258.net