Adopt the predecessor's tunnel in place: its range, its address, its peers
On an adopted hub the private network takes over the tunnel it finds rather than running beside it (hq ADR 0105): two tunnels leave the mesh's unreachable through the provider's filter, so no machine can ever join. The node presents the found tunnel when it enrols, under the key it took as its own; the inventory records it (node.tunnel, tunnel_peer — migration 0031) and the mesh composes from it: the overlay's range is the adopted tunnel's, the hub is placed at the tunnel's address on the tunnel's port, and every peer the tunnel had is carried in the hub's peer list as a peer of the tunnel, not a node of the mesh, until a node enrols with that key — which then keeps the address the tunnel had for it. A fresh node never gets an address the tunnel holds. The hub's declaration tells the host which unit to take over; the host's account of carrying it is recorded and shown. Every reader of the range follows the setting; nothing stores it. A found tunnel under another key is recorded and not adopted, so ADR 0100's non-overlap rule keeps applying where a tunnel is left running beside the mesh's. A lab bed and test skeleton for "How it is checked" are under lab/.
This commit is contained in:
@@ -16,25 +16,64 @@ import (
|
||||
// node's peer list to chase it, and an address that moves is the thing declaring the hub was
|
||||
// meant to stop.
|
||||
//
|
||||
// Allocated in order from the range, taking the lowest free one. Not random: a person reading a
|
||||
// peer list should be able to guess which node an address belongs to, and reuse of a released
|
||||
// address is a smaller problem than a list nobody can hold in their head.
|
||||
// **The adopted tunnel's addresses come first** (novox/hq ADR 0105). The hub that took over a
|
||||
// tunnel is at the tunnel's own address. A node enrolling with a key the tunnel already routed
|
||||
// to keeps the address the tunnel had for it — nothing a peer knows changes. And an address the
|
||||
// tunnel holds for a peer that has not enrolled is never handed to anyone else: that peer is
|
||||
// still reaching the hub at it.
|
||||
//
|
||||
// The rest is allocated in order from the range, taking the lowest free one. Not random: a person
|
||||
// reading a peer list should be able to guess which node an address belongs to, and reuse of a
|
||||
// released address is a smaller problem than a list nobody can hold in their head.
|
||||
func (i *Inventory) AssignAddress(ctx context.Context, node, cidr string) (string, error) {
|
||||
prefix, err := netip.ParsePrefix(cidr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%q is not a network the mesh can allocate from: %w", cidr, err)
|
||||
}
|
||||
|
||||
var existing *string
|
||||
var existing, key *string
|
||||
var name string
|
||||
var hub bool
|
||||
if err := i.store.Pool().QueryRow(ctx,
|
||||
`select host(overlay_address) from node where id = $1`, node).Scan(&existing); err != nil {
|
||||
`select name, host(overlay_address), overlay_key, is_hub from node where id = $1`, node).
|
||||
Scan(&name, &existing, &key, &hub); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if existing != nil && *existing != "" {
|
||||
return *existing, nil
|
||||
}
|
||||
|
||||
tunnel, hubName, adopted, err := i.AdoptedTunnel(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if adopted && hub && hubName == name {
|
||||
address, err := netip.ParsePrefix(tunnel.Address)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the adopted tunnel's address %q: %w", tunnel.Address, err)
|
||||
}
|
||||
return i.place(ctx, node, address.Addr().String())
|
||||
}
|
||||
carried, err := i.CarriedPeers(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
taken := map[string]bool{}
|
||||
if adopted {
|
||||
// The tunnel's own address is the hub's whether or not the hub has been placed yet.
|
||||
if address, err := netip.ParsePrefix(tunnel.Address); err == nil {
|
||||
taken[address.Addr().String()] = true
|
||||
}
|
||||
}
|
||||
for _, p := range carried {
|
||||
if key != nil && p.PublicKey == *key {
|
||||
// The tunnel already routes to this key: the node keeps that address, and the peer
|
||||
// notices nothing when its machine enrols.
|
||||
return i.place(ctx, node, p.Address)
|
||||
}
|
||||
taken[p.Address] = true
|
||||
}
|
||||
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select host(overlay_address) from node where overlay_address is not null`)
|
||||
if err != nil {
|
||||
@@ -58,12 +97,7 @@ func (i *Inventory) AssignAddress(ctx context.Context, node, cidr string) (strin
|
||||
candidate := prefix.Masked().Addr().Next()
|
||||
for prefix.Contains(candidate) {
|
||||
if !taken[candidate.String()] {
|
||||
if _, err := i.store.Pool().Exec(ctx,
|
||||
`update node set overlay_address = $2::inet where id = $1`,
|
||||
node, candidate.String()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return candidate.String(), nil
|
||||
return i.place(ctx, node, candidate.String())
|
||||
}
|
||||
candidate = candidate.Next()
|
||||
}
|
||||
@@ -72,5 +106,13 @@ func (i *Inventory) AssignAddress(ctx context.Context, node, cidr string) (strin
|
||||
// halfway through assigning one node.
|
||||
return "", fmt.Errorf(
|
||||
"every address in %s is taken, so %s cannot be given one. The mesh has outgrown its "+
|
||||
"range and renumbering it is a deliberate act", cidr, node)
|
||||
"range and renumbering it is a deliberate act", cidr, name)
|
||||
}
|
||||
|
||||
func (i *Inventory) place(ctx context.Context, node, address string) (string, error) {
|
||||
if _, err := i.store.Pool().Exec(ctx,
|
||||
`update node set overlay_address = $2::inet where id = $1`, node, address); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user