mirror of
https://github.com/sigcn/pg.git
synced 2025-09-27 01:05:51 +08:00
netlink: add func AddrSubscribe on darwin
This commit is contained in:
9
netlink/addr.go
Normal file
9
netlink/addr.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package netlink
|
||||
|
||||
import "net"
|
||||
|
||||
type AddrUpdate struct {
|
||||
New bool
|
||||
Addr net.IPNet
|
||||
LinkIndex int
|
||||
}
|
83
netlink/addr_darwin.go
Normal file
83
netlink/addr_darwin.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"slices"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/route"
|
||||
)
|
||||
|
||||
func AddrSubscribe(ctx context.Context, ch chan<- AddrUpdate) error {
|
||||
fd, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
|
||||
if err != nil {
|
||||
return fmt.Errorf("syscall socket: %w", err)
|
||||
}
|
||||
go func() {
|
||||
err := runAddrMsgReadLoop(fd, ch)
|
||||
if err != nil {
|
||||
slog.Error("AddrSubscribe", "err", fmt.Errorf("msg read loop exited: %w", err))
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
syscall.Close(fd)
|
||||
close(ch)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddrMsgReadLoop(fd int, ch chan<- AddrUpdate) error {
|
||||
buf := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
n, err := syscall.Read(fd, buf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("syscall read: %w", err)
|
||||
}
|
||||
buf[0] = 254
|
||||
msgs, err := route.ParseRIB(route.RIBTypeRoute, buf[:254])
|
||||
if err != nil {
|
||||
slog.Warn("RouteParseRIB", "err", err, "msglen", n, "msg", hex.EncodeToString(buf[:n]))
|
||||
continue
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
m, ok := msg.(*route.InterfaceAddrMessage)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains([]int{syscall.RTM_NEWADDR, syscall.RTM_DELADDR}, m.Type) {
|
||||
continue
|
||||
}
|
||||
ipnet := net.IPNet{}
|
||||
for _, addr := range m.Addrs {
|
||||
var ip []byte
|
||||
switch v := addr.(type) {
|
||||
case *route.Inet4Addr:
|
||||
ip = v.IP[:]
|
||||
case *route.Inet6Addr:
|
||||
ip = v.IP[:]
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if ipnet.Mask == nil {
|
||||
ipnet.Mask = ip
|
||||
continue
|
||||
}
|
||||
if ipnet.IP == nil {
|
||||
ipnet.IP = ip
|
||||
break
|
||||
}
|
||||
}
|
||||
ch <- AddrUpdate{
|
||||
New: m.Type == syscall.RTM_NEWADDR,
|
||||
Addr: ipnet,
|
||||
LinkIndex: m.Index,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user