2017-04-17 10:33:09 +05:30

358 lines
8.5 KiB
Go
Executable File

// +build linux
package nlgo
import (
"fmt"
"log"
"sync"
"syscall"
"unsafe"
)
type GenlMessage struct {
syscall.NetlinkMessage
Family GenlFamily
}
func (self GenlMessage) Genl() GenlMsghdr {
return *(*GenlMsghdr)(unsafe.Pointer(&self.Data[0]))
}
func (self GenlMessage) FamilyHeader() []byte {
return self.Data[GENL_HDRLEN : GENL_HDRLEN+NLMSG_ALIGN(int(self.Family.Hdrsize))]
}
func (self GenlMessage) Body() []byte {
return self.Data[GENL_HDRLEN+NLMSG_ALIGN(int(self.Family.Hdrsize)):]
}
type GenlListener interface {
GenlListen(GenlMessage)
}
type groupKey struct {
Family string
Name string
}
// GenlHub is generic netlink version of RtHub.
type GenlHub struct {
sock *NlSock
lock *sync.Mutex
familyIds map[uint16]GenlFamily
groupIds map[uint32]GenlGroup
membership []uint32
unilock *sync.Mutex
uniseq uint32
unicast GenlListener
multicast map[groupKey][]GenlListener
}
// sync group registeration
func (self *GenlHub) sync() error {
var active []uint32
for gkey, _ := range self.multicast {
for _, ginfo := range self.groupIds {
if ginfo.Family == gkey.Family && ginfo.Name == gkey.Name {
active = append(active, ginfo.Id)
}
}
}
for _, old := range self.membership {
// should not happen because CTRL_CMD_DELMCAST_GRP event have already cleaned up.
drop := true
for _, new := range active {
if new == old {
drop = false
}
}
if drop {
if err := NlSocketDropMembership(self.sock, int(old)); err != nil {
return err
}
}
}
for _, new := range active {
join := true
for _, old := range self.membership {
if new == old {
join = false
}
}
if join {
if err := NlSocketAddMembership(self.sock, int(new)); err != nil {
return err
}
}
}
self.membership = active
return nil
}
func NewGenlHub() (*GenlHub, error) {
self := &GenlHub{
sock: NlSocketAlloc(),
lock: &sync.Mutex{},
familyIds: map[uint16]GenlFamily{
GENL_ID_CTRL: GenlFamilyCtrl,
},
groupIds: map[uint32]GenlGroup{
GENL_ID_CTRL: GenlGroup{
Id: GENL_ID_CTRL,
Family: "nlctrl",
Name: "notify",
},
},
unilock: &sync.Mutex{},
multicast: make(map[groupKey][]GenlListener),
}
if err := NlConnect(self.sock, syscall.NETLINK_GENERIC); err != nil {
self.Close()
return nil, err
}
if err := self.Add("nlctrl", "notify", self); err != nil {
self.Close()
return nil, err
}
if err := self.Async(GenlFamilyCtrl.DumpRequest(CTRL_CMD_GETFAMILY), self); err != nil {
self.Close()
return nil, err
}
go func() {
for {
buf := make([]byte, syscall.Getpagesize())
if bufN, _, 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[:bufN]); err != nil {
break
} else {
for _, msg := range msgs {
self.lock.Lock()
family := self.familyIds[msg.Header.Type]
var multi []GenlListener
for gkey, s := range self.multicast {
if family.Name == gkey.Family {
for _, n := range s {
if uniq := func() bool {
for _, m := range multi {
if m == n {
return false
}
}
return true
}(); uniq {
multi = append(multi, n)
}
}
}
}
self.lock.Unlock()
gmsg := GenlMessage{
NetlinkMessage: msg,
Family: family,
}
if msg.Header.Seq == self.uniseq {
if self.unicast != nil {
self.unicast.GenlListen(gmsg)
}
switch msg.Header.Type {
case syscall.NLMSG_DONE, syscall.NLMSG_ERROR:
self.unilock.Unlock()
}
}
if msg.Header.Seq == 0 {
for _, proc := range multi {
proc.GenlListen(gmsg)
}
}
}
}
}
log.Print("genl hub loop exit")
}()
return self, nil
}
func (self GenlHub) Close() {
NlSocketFree(self.sock)
}
// Async() submits request with callback. Note that this locks sending request.
// Calling Async() in GenlListen() may create dead lock.
func (self *GenlHub) Async(msg GenlMessage, listener GenlListener) error {
self.unilock.Lock()
self.lock.Lock()
defer self.lock.Unlock()
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 genlHubCapture struct {
Msgs []GenlMessage
}
func (self *genlHubCapture) GenlListen(msg GenlMessage) {
self.Msgs = append(self.Msgs, msg)
}
// Sync() is synchronous version of Async().
// Calling Sync() in GenlListen() may create dead lock.
func (self *GenlHub) Sync(msg GenlMessage) ([]GenlMessage, error) {
cap := &genlHubCapture{}
if err := self.Async(msg, cap); err != nil {
return nil, err
} else {
self.unilock.Lock() // wait for unicast processing
defer self.unilock.Unlock()
return cap.Msgs, nil
}
}
func (self GenlHub) GenlListen(msg GenlMessage) {
if msg.Header.Type != GENL_ID_CTRL {
return
}
if family, groups, err := GenlCtrl(GenlFamilyCtrl).Parse(msg); err != nil {
log.Printf("genl ctrl msg parse err %v", err)
} else {
self.lock.Lock()
defer self.lock.Unlock()
switch msg.Genl().Cmd {
case CTRL_CMD_NEWFAMILY:
self.familyIds[family.Id] = family
fallthrough
case CTRL_CMD_NEWMCAST_GRP:
for _, grp := range groups {
if _, exists := self.groupIds[grp.Id]; !exists {
self.groupIds[grp.Id] = grp
}
}
case CTRL_CMD_DELFAMILY:
delete(self.familyIds, family.Id)
fallthrough
case CTRL_CMD_DELMCAST_GRP:
for _, grp := range groups {
if _, exists := self.groupIds[grp.Id]; exists {
delete(self.groupIds, grp.Id)
}
}
}
self.sync()
}
}
// Add adds a GenlListener to GenlHub.
// listeners will recieve all of the same family events, regardless of their group registration.
// If you want to limited group multicast, create separate GenlHub for each.
func (self GenlHub) Add(family, group string, listener GenlListener) error {
self.lock.Lock()
defer self.lock.Unlock()
key := groupKey{Family: family, Name: group}
self.multicast[key] = append(self.multicast[key], listener)
return self.sync()
}
func (self GenlHub) Remove(family, group string, listener GenlListener) error {
self.lock.Lock()
defer self.lock.Unlock()
key := groupKey{Family: family, Name: group}
var active []GenlListener
for _, li := range self.multicast[key] {
if li != listener {
active = append(active, li)
}
}
self.multicast[key] = active
return self.sync()
}
type familyListener struct {
name string
done GenlFamily
}
func (self *familyListener) GenlListen(msg GenlMessage) {
switch msg.Header.Type {
case GENL_ID_CTRL:
if family, _, err := GenlCtrl(GenlFamilyCtrl).Parse(msg); err != nil {
log.Printf("genl ctrl %v", err)
} else if family.Name == self.name {
self.done = family
}
}
}
// Family waits for genl family loading. Make sure loading that module by modprobe or by other means.
func (self *GenlHub) Family(name string) GenlFamily {
cap := &familyListener{name: name}
self.Add("nlctrl", "notify", cap)
defer self.Remove("nlctrl", "notify", cap)
for _, f := range self.familyIds {
if f.Name == name {
return f
}
}
if err := self.Async(GenlFamilyCtrl.DumpRequest(CTRL_CMD_GETFAMILY), cap); err != nil {
log.Print(err)
} else {
self.unilock.Lock() // wait for unicast processing done.
defer self.unilock.Unlock()
}
return cap.done
}
type GenlCtrl GenlFamily
func (self GenlCtrl) Parse(msg GenlMessage) (GenlFamily, []GenlGroup, error) {
var ret GenlFamily
if attrs, err := CtrlPolicy.Parse(msg.Body()); err != nil {
return ret, nil, err
} else if amap, ok := attrs.(AttrMap); !ok {
return ret, nil, fmt.Errorf("genl ctrl policy parse error")
} else {
if t := amap.Get(CTRL_ATTR_FAMILY_ID); t != nil {
ret.Id = uint16(t.(U16))
}
if t := amap.Get(CTRL_ATTR_FAMILY_NAME); t != nil {
ret.Name = string(t.(NulString))
}
if t := amap.Get(CTRL_ATTR_VERSION); t != nil {
ret.Version = uint8(uint32(t.(U32)))
}
if t := amap.Get(CTRL_ATTR_HDRSIZE); t != nil {
ret.Hdrsize = uint32(t.(U32))
}
var groups []GenlGroup
if grps := amap.Get(CTRL_ATTR_MCAST_GROUPS); grps != nil {
for _, grp := range grps.(AttrSlice).Slice() {
gattr := grp.Value.(AttrMap)
key := uint32(gattr.Get(CTRL_ATTR_MCAST_GRP_ID).(U32))
groups = append(groups, GenlGroup{
Id: key,
Family: ret.Name,
Name: string(gattr.Get(CTRL_ATTR_MCAST_GRP_NAME).(NulString)),
})
}
}
return ret, groups, nil
}
}