carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package instancectl
2
3import (
4 "bufio"
5 "bytes"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "io/fs"
10 "net/netip"
11 "os"
12 "strconv"
13 "strings"
14)
15
16// The box's own addresses, for the resolver allow (#214 item 3, #126).
17// An allowed resolver must not be one of them: the allow opens an address
18// on every port, and on the box's own address that is every listener
19// bound to all addresses.
20//
21// They are read from procfs, not asked of the kernel over netlink, which
22// is what net.InterfaceAddrs does: this service runs with
23// RestrictAddressFamilies=AF_UNIX, and the probe unit, which used to make
24// this check, with AF_INET AF_INET6 AF_UNIX, so netlink is refused in
25// both (the v0.25.0 roll: "netlinkrib: address family not supported by
26// protocol" on every box, and no box ever ready). A file read needs no
27// socket at all, and widening either unit's families for one listing was
28// the alternative.
29
30// Where the box's addresses are read; tests point them at fixtures.
31const (
32 fibTriePath = "/proc/net/fib_trie" // IPv4: the routing tables, LOCAL entries
33 ifInet6Path = "/proc/net/if_inet6" // IPv6: one line per interface address
34)
35
36// localPrefixes is everything that routes to this box: each IPv4 LOCAL
37// route (an interface address, or a whole local range such as
38// 127.0.0.0/8), and each IPv6 interface address as a /128.
39func localPrefixes(fibTrie, ifInet6 string) ([]netip.Prefix, error) {
40 b, err := os.ReadFile(fibTrie)
41 if err != nil {
42 return nil, err
43 }
44 out, err := parseFibTrie(b)
45 if err != nil {
46 return nil, fmt.Errorf("%s: %w", fibTrie, err)
47 }
48 b, err = os.ReadFile(ifInet6)
49 switch {
50 case errors.Is(err, fs.ErrNotExist):
51 // No IPv6 in this kernel: no v6 address can be the box's.
52 return out, nil
53 case err != nil:
54 return nil, err
55 }
56 v6, err := parseIfInet6(b)
57 if err != nil {
58 return nil, fmt.Errorf("%s: %w", ifInet6, err)
59 }
60 return append(out, v6...), nil
61}
62
63// parseFibTrie reads /proc/net/fib_trie: a leaf line "|-- <addr>" is
64// followed by one line per route at that address, "/<len> <scope>
65// <type>". A type of LOCAL is a destination this box answers for itself.
66// A file with no LOCAL entry at all is refused rather than read as "no
67// addresses": every box has at least 127.0.0.1, so it is not the file
68// this parser knows.
69func parseFibTrie(b []byte) ([]netip.Prefix, error) {
70 var out []netip.Prefix
71 var leaf netip.Addr
72 sc := bufio.NewScanner(bytes.NewReader(b))
73 for sc.Scan() {
74 line := strings.TrimSpace(sc.Text())
75 if a, ok := strings.CutPrefix(line, "|-- "); ok {
76 leaf, _ = netip.ParseAddr(strings.TrimSpace(a))
77 continue
78 }
79 f := strings.Fields(line)
80 if len(f) < 3 || !strings.HasPrefix(f[0], "/") || f[len(f)-1] != "LOCAL" || !leaf.IsValid() {
81 continue
82 }
83 bits, err := strconv.Atoi(f[0][1:])
84 if err != nil {
85 continue
86 }
87 if p, err := leaf.Prefix(bits); err == nil {
88 out = append(out, p)
89 }
90 }
91 if err := sc.Err(); err != nil {
92 return nil, err
93 }
94 if len(out) == 0 {
95 return nil, errors.New("no LOCAL route")
96 }
97 return out, nil
98}
99
100// parseIfInet6 reads /proc/net/if_inet6: each line starts with the
101// address as 32 hex digits.
102func parseIfInet6(b []byte) ([]netip.Prefix, error) {
103 var out []netip.Prefix
104 sc := bufio.NewScanner(bytes.NewReader(b))
105 for sc.Scan() {
106 f := strings.Fields(sc.Text())
107 if len(f) == 0 {
108 continue
109 }
110 raw, err := hex.DecodeString(f[0])
111 if err != nil || len(raw) != 16 {
112 return nil, fmt.Errorf("%q is not an address", f[0])
113 }
114 out = append(out, netip.PrefixFrom(netip.AddrFrom16([16]byte(raw)), 128))
115 }
116 return out, sc.Err()
117}
118
119// isLocal names the local prefix a holds, if any.
120func isLocal(a netip.Addr, local []netip.Prefix) (netip.Prefix, bool) {
121 for _, p := range local {
122 if p.Contains(a) {
123 return p, true
124 }
125 }
126 return netip.Prefix{}, false
127}
128