mirror of
https://github.com/cloudnativelabs/kube-router.git
synced 2025-10-09 00:41:05 +02:00
158 lines
3.8 KiB
Go
Executable File
158 lines
3.8 KiB
Go
Executable File
// +build linux
|
|
|
|
package nlgo
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
"syscall"
|
|
)
|
|
|
|
type NetlinkListener interface {
|
|
NetlinkListen(syscall.NetlinkMessage)
|
|
}
|
|
|
|
// RtHub is a high layer thread-safe API, which is not present in libnl.
|
|
// RtHub is useful for listening to kernel event notification.
|
|
type RtHub struct {
|
|
sock *NlSock
|
|
lock *sync.Mutex
|
|
unilock *sync.Mutex
|
|
uniseq uint32
|
|
unicast NetlinkListener
|
|
multicast map[uint32][]NetlinkListener
|
|
}
|
|
|
|
func NewRtHub() (*RtHub, error) {
|
|
self := &RtHub{
|
|
sock: NlSocketAlloc(),
|
|
lock: &sync.Mutex{},
|
|
unilock: &sync.Mutex{},
|
|
multicast: make(map[uint32][]NetlinkListener),
|
|
}
|
|
if err := NlConnect(self.sock, syscall.NETLINK_ROUTE); err != nil {
|
|
NlSocketFree(self.sock)
|
|
return nil, err
|
|
}
|
|
go func() {
|
|
for {
|
|
buf := make([]byte, syscall.Getpagesize())
|
|
if n, _, err := syscall.Recvfrom(self.sock.Fd, buf, syscall.MSG_TRUNC); err != nil {
|
|
if e, ok := err.(syscall.Errno); ok && e.Temporary() {
|
|
continue
|
|
}
|
|
break
|
|
} else if msgs, err := syscall.ParseNetlinkMessage(buf[:n]); err != nil {
|
|
break
|
|
} else {
|
|
for _, msg := range msgs {
|
|
multi := func() []NetlinkListener {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
|
|
var ret []NetlinkListener
|
|
for _, s := range self.multicast {
|
|
ret = append(ret, s...)
|
|
}
|
|
return ret
|
|
}()
|
|
if msg.Header.Seq == self.uniseq {
|
|
if self.unicast != nil {
|
|
self.unicast.NetlinkListen(msg)
|
|
}
|
|
switch msg.Header.Type {
|
|
case syscall.NLMSG_DONE, syscall.NLMSG_ERROR:
|
|
self.unilock.Unlock()
|
|
}
|
|
}
|
|
if msg.Header.Seq == 0 {
|
|
for _, proc := range multi {
|
|
proc.NetlinkListen(msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
log.Print("rt hub loop exit")
|
|
}()
|
|
return self, nil
|
|
}
|
|
|
|
func (self RtHub) Close() {
|
|
NlSocketFree(self.sock)
|
|
}
|
|
|
|
// Async() submits request with callback. Note that this locks sending request.
|
|
// Calling Async() in GenlListen() may create dead lock.
|
|
// netlink message header will be reparsed.
|
|
func (self *RtHub) Async(msg syscall.NetlinkMessage, listener NetlinkListener) error {
|
|
self.unilock.Lock()
|
|
self.unicast = listener
|
|
self.uniseq = self.sock.SeqNext
|
|
|
|
hdr := msg.Header
|
|
if err := NlSendSimple(self.sock, hdr.Type, hdr.Flags, msg.Data); err != nil {
|
|
self.unilock.Unlock()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type hubCapture struct {
|
|
Msgs []syscall.NetlinkMessage
|
|
}
|
|
|
|
func (self *hubCapture) NetlinkListen(msg syscall.NetlinkMessage) {
|
|
self.Msgs = append(self.Msgs, msg)
|
|
}
|
|
|
|
// Sync() is synchronous version of Async().
|
|
// Calling Sync() in GenlListen() may create dead lock.
|
|
func (self *RtHub) Sync(msg syscall.NetlinkMessage) ([]syscall.NetlinkMessage, error) {
|
|
cap := &hubCapture{}
|
|
if err := self.Async(msg, cap); err != nil {
|
|
return nil, err
|
|
} else {
|
|
self.unilock.Lock() // waits for unlock, which means response arrival.
|
|
defer self.unilock.Unlock()
|
|
|
|
return cap.Msgs, nil
|
|
}
|
|
}
|
|
|
|
// Add adds a listener to the hub.
|
|
// listener will recieve all of the rtnetlink events, regardless of their group registration.
|
|
// If you want to split it, then use separate RtHub.
|
|
func (self RtHub) Add(group uint32, listener NetlinkListener) error {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
|
|
if len(self.multicast[group]) == 0 {
|
|
if err := NlSocketAddMembership(self.sock, int(group)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
self.multicast[group] = append(self.multicast[group], listener)
|
|
return nil
|
|
}
|
|
|
|
func (self RtHub) Remove(group uint32, listener NetlinkListener) error {
|
|
self.lock.Lock()
|
|
defer self.lock.Unlock()
|
|
|
|
var active []NetlinkListener
|
|
for _, li := range self.multicast[group] {
|
|
if li != listener {
|
|
active = append(active, li)
|
|
}
|
|
}
|
|
self.multicast[group] = active
|
|
|
|
if len(active) == 0 {
|
|
if err := NlSocketDropMembership(self.sock, int(group)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|