mesh/merge-gate head cb7b71dcc568 merged into trunk 12bc6a56a38a — pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes wi…
mesh/repo-check head cb7b71dcc568 merged into trunk 12bc6a56a38a — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/154-local-network delivered: every member is delivered
A port reached from local-network is admitted from the private networks the machine is attached to, and only the machine can say which those are. Every network on a link facing outside is said among its facts, and a change is said at the next reconcile.
93 lines
2.8 KiB
Go
93 lines
2.8 KiB
Go
package outward
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"sort"
|
|
)
|
|
|
|
// Network is one network this machine is directly attached to, and the link it is on (novox/hq ADR
|
|
// 0340 §7): the address and prefix of the network, not of the machine's own address on it.
|
|
//
|
|
// **Every one is said, and the machine judges none of them.** Which count as the machine's local
|
|
// network — private ranges only, never a public subnet, never a link-local one — is the controller's
|
|
// judgement (ADR 0340 §2), made where the filter is composed and shown with why; a machine that left a
|
|
// network out would make that judgement silently.
|
|
type Network struct {
|
|
Link string `json:"link"`
|
|
Network string `json:"network"`
|
|
}
|
|
|
|
// AddressesOf is the addresses on one link, as address and prefix. A parameter so a test can hold a
|
|
// machine's links without one.
|
|
type AddressesOf func(link string) ([]netip.Prefix, error)
|
|
|
|
// RealAddresses reads one link's addresses from the kernel.
|
|
func RealAddresses(link string) ([]netip.Prefix, error) {
|
|
iface, err := net.InterfaceByName(link)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []netip.Prefix
|
|
for _, a := range addrs {
|
|
ipnet, ok := a.(*net.IPNet)
|
|
if !ok {
|
|
continue
|
|
}
|
|
addr, ok := netip.AddrFromSlice(ipnet.IP)
|
|
if !ok {
|
|
continue
|
|
}
|
|
bits, _ := ipnet.Mask.Size()
|
|
out = append(out, netip.PrefixFrom(addr.Unmap(), bits))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Networks is the networks on each of these links — the links that face outside (Links) — sorted by link
|
|
// and network, without repeats. A link that has gone since Links read it has no networks rather than
|
|
// failing the rest; a link whose addresses cannot be read fails, so the machine says the fact is unknown
|
|
// rather than reporting a partial account as the whole.
|
|
func Networks(links []string, addressesOf AddressesOf) ([]Network, error) {
|
|
if addressesOf == nil {
|
|
addressesOf = RealAddresses
|
|
}
|
|
seen := map[Network]bool{}
|
|
var out []Network
|
|
for _, link := range links {
|
|
prefixes, err := addressesOf(link)
|
|
if err != nil {
|
|
if isNoSuchInterface(err) {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("cannot read the addresses on %s: %w", link, err)
|
|
}
|
|
for _, p := range prefixes {
|
|
n := Network{Link: link, Network: p.Masked().String()}
|
|
if !seen[n] {
|
|
seen[n] = true
|
|
out = append(out, n)
|
|
}
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool {
|
|
if out[i].Link != out[j].Link {
|
|
return out[i].Link < out[j].Link
|
|
}
|
|
return out[i].Network < out[j].Network
|
|
})
|
|
return out, nil
|
|
}
|
|
|
|
// isNoSuchInterface is the error net.InterfaceByName gives for a link that is not there.
|
|
func isNoSuchInterface(err error) bool {
|
|
var op *net.OpError
|
|
return errors.As(err, &op) && op.Err != nil && op.Err.Error() == "no such network interface"
|
|
}
|