// Package hostif reads the machine's network interfaces: their IPv4 and // IPv6 addresses and whether they are up. Results are cached for a few // seconds, since validation and the status page ask often. package hostif import ( "net" "net/netip" "sync" "time" ) // Host reads this machine's interfaces. type Host struct { mu sync.Mutex at time.Time addrs map[string][]netip.Prefix up map[string]bool err error } func (h *Host) refresh() { if time.Since(h.at) < 5*time.Second && (h.addrs != nil || h.err != nil) { return } h.at = time.Now() ifs, err := net.Interfaces() if err != nil { h.addrs, h.up, h.err = nil, nil, err return } addrs, up := map[string][]netip.Prefix{}, map[string]bool{} for _, i := range ifs { var ps []netip.Prefix as, _ := i.Addrs() for _, a := range as { if n, ok := a.(*net.IPNet); ok { if ip, ok := netip.AddrFromSlice(n.IP); ok { bits, _ := n.Mask.Size() ps = append(ps, netip.PrefixFrom(ip.Unmap(), bits)) } } } addrs[i.Name] = ps up[i.Name] = i.Flags&net.FlagUp != 0 } h.addrs, h.up, h.err = addrs, up, nil } // All returns every interface with its addresses. func (h *Host) All() (map[string][]netip.Prefix, error) { h.mu.Lock() defer h.mu.Unlock() h.refresh() return h.addrs, h.err } // Up reports whether an interface is up. func (h *Host) Up(name string) bool { h.mu.Lock() defer h.mu.Unlock() h.refresh() return h.up[name] } // Forget drops what was read, so the next question reads the interfaces // again (after the kernel said one appeared or went). func (h *Host) Forget() { h.mu.Lock() h.at = time.Time{} h.mu.Unlock() } // Exists returns the set of interface names (nil if they can't be read). func (h *Host) Exists() map[string]bool { h.mu.Lock() defer h.mu.Unlock() h.refresh() if h.err != nil { return nil } out := make(map[string]bool, len(h.addrs)) for n := range h.addrs { out[n] = true } return out }