mirror of
https://github.com/fosrl/newt.git
synced 2026-10-02 18:59:08 +02:00
69 lines
1.6 KiB
Go
69 lines
1.6 KiB
Go
package network
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"golang.org/x/net/route"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// watchRouteEvents calls notify for routing socket messages that can change
|
|
// the physical path: routes added/removed/changed, interface addresses
|
|
// added/removed, and interface state changes. ARP/NDP neighbor entries
|
|
// (cloned host routes) and RTM_GET replies - which include those to our own
|
|
// `route get` calls - are ignored, as they would otherwise trigger constantly.
|
|
func watchRouteEvents(ctx context.Context, notify func()) error {
|
|
fd, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Non-blocking so os.NewFile registers it with the runtime poller and
|
|
// Close interrupts a pending Read.
|
|
if err := unix.SetNonblock(fd, true); err != nil {
|
|
unix.Close(fd)
|
|
return err
|
|
}
|
|
f := os.NewFile(uintptr(fd), "route")
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
f.Close()
|
|
}()
|
|
|
|
go func() {
|
|
buf := make([]byte, 4096)
|
|
for {
|
|
n, err := f.Read(buf)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if isRelevantRouteMessage(buf[:n]) {
|
|
notify()
|
|
}
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
func isRelevantRouteMessage(b []byte) bool {
|
|
if len(b) < 4 {
|
|
return false
|
|
}
|
|
switch int(b[3]) { // rtm_type, common to every routing message header
|
|
case unix.RTM_NEWADDR, unix.RTM_DELADDR, unix.RTM_IFINFO:
|
|
return true
|
|
case unix.RTM_ADD, unix.RTM_DELETE, unix.RTM_CHANGE:
|
|
msgs, err := route.ParseRIB(route.RIBTypeRoute, b)
|
|
if err != nil || len(msgs) == 0 {
|
|
return true
|
|
}
|
|
if m, ok := msgs[0].(*route.RouteMessage); ok && m.Flags&(unix.RTF_LLINFO|unix.RTF_WASCLONED) != 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|