73 lines
1.5 KiB
Go
73 lines
1.5 KiB
Go
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()
|
|
}
|