mirror of https://github.com/vishvananda/netlink
feat: add proc events support
This commit is contained in:
parent
76d8bfe0aa
commit
dd687eb2f2
|
@ -27,7 +27,8 @@ const (
|
||||||
// tc rules or filters, or other more memory requiring data.
|
// tc rules or filters, or other more memory requiring data.
|
||||||
RECEIVE_BUFFER_SIZE = 65536
|
RECEIVE_BUFFER_SIZE = 65536
|
||||||
// Kernel netlink pid
|
// Kernel netlink pid
|
||||||
PidKernel uint32 = 0
|
PidKernel uint32 = 0
|
||||||
|
SizeofCnMsgOp = 0x18
|
||||||
)
|
)
|
||||||
|
|
||||||
// SupportedNlFamilies contains the list of netlink families this netlink package supports
|
// SupportedNlFamilies contains the list of netlink families this netlink package supports
|
||||||
|
@ -85,6 +86,56 @@ type NetlinkRequestData interface {
|
||||||
Serialize() []byte
|
Serialize() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROC_CN_MCAST_LISTEN = 1
|
||||||
|
PROC_CN_MCAST_IGNORE
|
||||||
|
)
|
||||||
|
|
||||||
|
type CbID struct {
|
||||||
|
Idx uint32
|
||||||
|
Val uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CnMsg struct {
|
||||||
|
ID CbID
|
||||||
|
Seq uint32
|
||||||
|
Ack uint32
|
||||||
|
Length uint16
|
||||||
|
Flags uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type CnMsgOp struct {
|
||||||
|
CnMsg
|
||||||
|
// here we differ from the C header
|
||||||
|
Op uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCnMsg(idx, val, op uint32) *CnMsgOp {
|
||||||
|
var cm CnMsgOp
|
||||||
|
|
||||||
|
cm.ID.Idx = idx
|
||||||
|
cm.ID.Val = val
|
||||||
|
|
||||||
|
cm.Ack = 0
|
||||||
|
cm.Seq = 1
|
||||||
|
cm.Length = uint16(binary.Size(op))
|
||||||
|
cm.Op = op
|
||||||
|
|
||||||
|
return &cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *CnMsgOp) Serialize() []byte {
|
||||||
|
return (*(*[SizeofCnMsgOp]byte)(unsafe.Pointer(msg)))[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeserializeCnMsgOp(b []byte) *CnMsgOp {
|
||||||
|
return (*CnMsgOp)(unsafe.Pointer(&b[0:SizeofCnMsgOp][0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *CnMsgOp) Len() int {
|
||||||
|
return SizeofCnMsgOp
|
||||||
|
}
|
||||||
|
|
||||||
// IfInfomsg is related to links, but it is used for list requests as well
|
// IfInfomsg is related to links, but it is used for list requests as well
|
||||||
type IfInfomsg struct {
|
type IfInfomsg struct {
|
||||||
unix.IfInfomsg
|
unix.IfInfomsg
|
||||||
|
|
|
@ -98,3 +98,35 @@ func TestIfSocketCloses(t *testing.T) {
|
||||||
t.Fatalf("Expected error instead received nil")
|
t.Fatalf("Expected error instead received nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *CnMsgOp) write(b []byte) {
|
||||||
|
native := NativeEndian()
|
||||||
|
native.PutUint32(b[0:4], msg.ID.Idx)
|
||||||
|
native.PutUint32(b[4:8], msg.ID.Val)
|
||||||
|
native.PutUint32(b[8:12], msg.Seq)
|
||||||
|
native.PutUint32(b[12:16], msg.Ack)
|
||||||
|
native.PutUint16(b[16:18], msg.Length)
|
||||||
|
native.PutUint16(b[18:20], msg.Flags)
|
||||||
|
native.PutUint32(b[20:24], msg.Op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *CnMsgOp) serializeSafe() []byte {
|
||||||
|
length := msg.Len()
|
||||||
|
b := make([]byte, length)
|
||||||
|
msg.write(b)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func deserializeCnMsgOpSafe(b []byte) *CnMsgOp {
|
||||||
|
var msg = CnMsgOp{}
|
||||||
|
binary.Read(bytes.NewReader(b[0:SizeofCnMsgOp]), NativeEndian(), &msg)
|
||||||
|
return &msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCnMsgOpDeserializeSerialize(t *testing.T) {
|
||||||
|
var orig = make([]byte, SizeofCnMsgOp)
|
||||||
|
rand.Read(orig)
|
||||||
|
safemsg := deserializeCnMsgOpSafe(orig)
|
||||||
|
msg := DeserializeCnMsgOp(orig)
|
||||||
|
testDeserializeSerialize(t, orig, safemsg, msg)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
"github.com/vishvananda/netns"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CN_IDX_PROC = 0x1
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROC_EVENT_NONE = 0x00000000
|
||||||
|
PROC_EVENT_FORK = 0x00000001
|
||||||
|
PROC_EVENT_EXEC = 0x00000002
|
||||||
|
PROC_EVENT_UID = 0x00000004
|
||||||
|
PROC_EVENT_GID = 0x00000040
|
||||||
|
PROC_EVENT_SID = 0x00000080
|
||||||
|
PROC_EVENT_PTRACE = 0x00000100
|
||||||
|
PROC_EVENT_COMM = 0x00000200
|
||||||
|
PROC_EVENT_COREDUMP = 0x40000000
|
||||||
|
PROC_EVENT_EXIT = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CN_VAL_PROC = 0x1
|
||||||
|
PROC_CN_MCAST_LISTEN = 0x1
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProcEventMsg interface {
|
||||||
|
Pid() uint32
|
||||||
|
Tgid() uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcEventHeader struct {
|
||||||
|
What uint32
|
||||||
|
CPU uint32
|
||||||
|
Timestamp uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcEvent struct {
|
||||||
|
ProcEventHeader
|
||||||
|
Msg ProcEventMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *ProcEvent) setHeader(h ProcEventHeader) {
|
||||||
|
pe.What = h.What
|
||||||
|
pe.CPU = h.CPU
|
||||||
|
pe.Timestamp = h.Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExitProcEvent struct {
|
||||||
|
ProcessPid uint32
|
||||||
|
ProcessTgid uint32
|
||||||
|
ExitCode uint32
|
||||||
|
ExitSignal uint32
|
||||||
|
ParentPid uint32
|
||||||
|
ParentTgid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExitProcEvent2 struct {
|
||||||
|
ProcessPid uint32
|
||||||
|
ProcessTgid uint32
|
||||||
|
ExitCode uint32
|
||||||
|
ExitSignal uint32
|
||||||
|
ParentPid uint32
|
||||||
|
ParentTgid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExitProcEvent) Pid() uint32 {
|
||||||
|
return e.ProcessPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExitProcEvent) Tgid() uint32 {
|
||||||
|
return e.ProcessTgid
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecProcEvent struct {
|
||||||
|
ProcessPid uint32
|
||||||
|
ProcessTgid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExecProcEvent) Pid() uint32 {
|
||||||
|
return e.ProcessPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExecProcEvent) Tgid() uint32 {
|
||||||
|
return e.ProcessTgid
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForkProcEvent struct {
|
||||||
|
ParentPid uint32
|
||||||
|
ParentTgid uint32
|
||||||
|
ChildPid uint32
|
||||||
|
ChildTgid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ForkProcEvent) Pid() uint32 {
|
||||||
|
return e.ParentPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ForkProcEvent) Tgid() uint32 {
|
||||||
|
return e.ParentTgid
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommProcEvent struct {
|
||||||
|
ProcessPid uint32
|
||||||
|
ProcessTgid uint32
|
||||||
|
Comm [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommProcEvent) Pid() uint32 {
|
||||||
|
return e.ProcessPid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CommProcEvent) Tgid() uint32 {
|
||||||
|
return e.ProcessTgid
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcEventMonitor(ch chan<- ProcEvent, done <-chan struct{}, errorChan chan<- error) error {
|
||||||
|
h, err := NewHandle()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer h.Delete()
|
||||||
|
|
||||||
|
s, err := nl.SubscribeAt(netns.None(), netns.None(), unix.NETLINK_CONNECTOR, CN_IDX_PROC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nlmsg nl.NetlinkRequest
|
||||||
|
|
||||||
|
nlmsg.Pid = uint32(os.Getpid())
|
||||||
|
nlmsg.Type = unix.NLMSG_DONE
|
||||||
|
nlmsg.Len = uint32(unix.SizeofNlMsghdr)
|
||||||
|
|
||||||
|
cm := nl.NewCnMsg(CN_IDX_PROC, CN_VAL_PROC, PROC_CN_MCAST_LISTEN)
|
||||||
|
nlmsg.AddData(cm)
|
||||||
|
|
||||||
|
s.Send(&nlmsg)
|
||||||
|
|
||||||
|
if done != nil {
|
||||||
|
go func() {
|
||||||
|
<-done
|
||||||
|
s.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
for {
|
||||||
|
msgs, from, err := s.Receive()
|
||||||
|
if err != nil {
|
||||||
|
errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if from.Pid != nl.PidKernel {
|
||||||
|
errorChan <- fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range msgs {
|
||||||
|
e := parseNetlinkMessage(m)
|
||||||
|
if e != nil {
|
||||||
|
ch <- *e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNetlinkMessage(m syscall.NetlinkMessage) *ProcEvent {
|
||||||
|
if m.Header.Type == unix.NLMSG_DONE {
|
||||||
|
buf := bytes.NewBuffer(m.Data)
|
||||||
|
msg := &nl.CnMsg{}
|
||||||
|
hdr := &ProcEventHeader{}
|
||||||
|
binary.Read(buf, nl.NativeEndian(), msg)
|
||||||
|
binary.Read(buf, nl.NativeEndian(), hdr)
|
||||||
|
|
||||||
|
pe := &ProcEvent{}
|
||||||
|
pe.setHeader(*hdr)
|
||||||
|
switch hdr.What {
|
||||||
|
case PROC_EVENT_EXIT:
|
||||||
|
event := &ExitProcEvent{}
|
||||||
|
binary.Read(buf, nl.NativeEndian(), event)
|
||||||
|
pe.Msg = event
|
||||||
|
return pe
|
||||||
|
case PROC_EVENT_FORK:
|
||||||
|
event := &ForkProcEvent{}
|
||||||
|
binary.Read(buf, nl.NativeEndian(), event)
|
||||||
|
pe.Msg = event
|
||||||
|
return pe
|
||||||
|
case PROC_EVENT_EXEC:
|
||||||
|
event := &ExecProcEvent{}
|
||||||
|
binary.Read(buf, nl.NativeEndian(), event)
|
||||||
|
pe.Msg = event
|
||||||
|
return pe
|
||||||
|
case PROC_EVENT_COMM:
|
||||||
|
event := &CommProcEvent{}
|
||||||
|
binary.Read(buf, nl.NativeEndian(), event)
|
||||||
|
pe.Msg = event
|
||||||
|
return pe
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vishvananda/netns"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSubscribeProcEvent(t *testing.T) {
|
||||||
|
skipUnlessRoot(t)
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
pid1ns, err := netns.GetFromPid(1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = netns.Set(pid1ns)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan ProcEvent)
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
errChan := make(chan error)
|
||||||
|
|
||||||
|
if err := ProcEventMonitor(ch, done, errChan); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("false")
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first we wait for proc - i.e. childTgid is cmd.Process.Pid
|
||||||
|
for {
|
||||||
|
e := <-ch
|
||||||
|
t.Logf("pid: %+v e: %+v", os.Getpid(), e)
|
||||||
|
if e.Msg.Tgid() == uint32(os.Getpid()) {
|
||||||
|
if forkEvent, ok := e.Msg.(*ForkProcEvent); ok {
|
||||||
|
if forkEvent.ChildTgid == uint32(cmd.Process.Pid) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for exec event
|
||||||
|
for {
|
||||||
|
e := <-ch
|
||||||
|
if e.Msg.Tgid() == uint32(cmd.Process.Pid) {
|
||||||
|
if _, ok := e.Msg.(*ExecProcEvent); ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Wait()
|
||||||
|
for {
|
||||||
|
e := <-ch
|
||||||
|
if e.Msg.Tgid() == uint32(cmd.Process.Pid) {
|
||||||
|
if exitEvent, ok := e.Msg.(*ExitProcEvent); ok {
|
||||||
|
if exitEvent.ExitCode != 256 {
|
||||||
|
t.Errorf("Expected error code 256 (-1), but got %+v", exitEvent)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done <- struct{}{}
|
||||||
|
}
|
Loading…
Reference in New Issue