multinet/health.go

74 lines
1.5 KiB
Go
Raw Permalink Normal View History

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()
}