package multinet import ( "net/netip" "sync" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) type NetlinkWatcher struct { d Multidialer linkUpdates chan netlink.LinkUpdate addrUpdates chan netlink.AddrUpdate done chan struct{} wg sync.WaitGroup } func NewNetlinkWatcher(d Multidialer) *NetlinkWatcher { return &NetlinkWatcher{ d: d, addrUpdates: make(chan netlink.AddrUpdate, 1), linkUpdates: make(chan netlink.LinkUpdate, 1), done: make(chan struct{}), } } func (w *NetlinkWatcher) Start() error { ns, err := netns.Get() if err != nil { return err } if err := netlink.LinkSubscribe(w.linkUpdates, w.done); err != nil { return err } if err := netlink.AddrSubscribe(w.addrUpdates, w.done); err != nil { close(w.done) return err } w.wg.Add(1) go w.watch(ns) return nil } func (w *NetlinkWatcher) watch(ns netns.NsHandle) { defer w.wg.Done() for { select { case <-w.done: return case update := <-w.addrUpdates: // Wont work if an multiple interfaces share IP address. // Should not happen in practice. ip, ok := netip.AddrFromSlice(update.LinkAddress.IP) if !ok { continue } w.d.UpdateInterface("", ip, update.NewAddr) case update := <-w.linkUpdates: up := update.Flags&unix.IFF_UP != 0 w.d.UpdateInterface(update.Link.Attrs().Name, netip.Addr{}, up) } } } func (w *NetlinkWatcher) Stop() { close(w.done) w.wg.Wait() }