summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2024-08-01 10:15:09 +0800
committerWe-unite <3205135446@qq.com>2024-08-01 10:15:09 +0800
commit88f080953a205c325ec80a19438236f3bb9e6d2b (patch)
tree1ede6e404f65b9383c0678fe2c5e7e17a4850064
parentca2d881a749d3fc9f737c391ebf8b84f7d551c0d (diff)
downloadgodo-88f080953a205c325ec80a19438236f3bb9e6d2b.tar.gz
godo-88f080953a205c325ec80a19438236f3bb9e6d2b.zip
Fuck\!
-rw-r--r--src/connector.go176
-rw-r--r--src/deal.go12
-rw-r--r--src/global.go3
-rw-r--r--src/godo.go29
-rw-r--r--src/organize.go63
-rw-r--r--src/procWatch.go86
-rw-r--r--src/request.go57
-rw-r--r--src/socket.go79
8 files changed, 457 insertions, 48 deletions
diff --git a/src/connector.go b/src/connector.go
new file mode 100644
index 0000000..278dfe9
--- /dev/null
+++ b/src/connector.go
@@ -0,0 +1,176 @@
1package main
2
3import "unsafe"
4
5const (
6 SizeofCnMsg = 0x14
7 SizeofProcCnMcastOp = 0x04
8 SizeofProcEventHeader = 0x10
9 SizeofProcEventData = 0x18
10)
11
12const (
13 CN_IDX_PROC = 0x1
14 CN_VAL_PROC = 0x1
15)
16
17// linux/connector.h
18type CbId struct {
19 idx uint32
20 val uint32
21}
22
23type CnMsg struct {
24 id CbId
25 seq uint32
26 ack uint32
27 len uint16
28 flags uint16
29 //data []uint8
30}
31
32func (cm *CnMsg) Serialize() []byte {
33 return (*(*[SizeofCnMsg]byte)(unsafe.Pointer(cm)))[:]
34}
35
36func (cm *CnMsg) Len() int {
37 return SizeofCnMsg
38}
39
40func NewCnMsg() *CnMsg {
41 return &CnMsg{
42 id: CbId{idx: CN_IDX_PROC, val: CN_VAL_PROC},
43 seq: 0,
44 ack: 0,
45 len: SizeofProcCnMcastOp,
46 }
47}
48
49type ProcCnMcastOp int
50
51const (
52 _ ProcCnMcastOp = iota
53 PROC_CN_MCAST_LISTEN
54 PROC_CN_MCAST_IGNORE
55)
56
57func (op *ProcCnMcastOp) Serialize() []byte {
58 return (*(*[SizeofProcCnMcastOp]byte)(unsafe.Pointer(op)))[:]
59}
60
61func (op *ProcCnMcastOp) Len() int {
62 return SizeofProcCnMcastOp
63}
64
65type ProcEventType uint32
66
67const (
68 PROC_EVENT_NONE = 0x00000000
69 PROC_EVENT_FORK = 0x00000001
70 PROC_EVENT_EXEC = 0x00000002
71 PROC_EVENT_UID = 0x00000004
72 PROC_EVENT_GID = 0x00000040
73 PROC_EVENT_SID = 0x00000080
74 PROC_EVENT_PTRACE = 0x00000100
75 PROC_EVENT_COMM = 0x00000200
76 PROC_EVENT_COREDUMP = 0x40000000
77 PROC_EVENT_EXIT = 0x80000000
78)
79
80type ProcEventData interface {
81}
82
83// linux/cn_proc.h
84type ProcEvent struct {
85 What ProcEventType
86 Cpu uint32
87 Timestamp uint64
88 Data ProcEventData
89}
90
91type ProcEventNone struct{}
92
93type ProcEventFork struct {
94 ParentPid int32
95 ParentTgid int32
96 ChildPid int32
97 ChildTgid int32
98}
99
100type ProcEventExec struct {
101 ProcessPid int32
102 ProcessTgid int32
103}
104
105type ProcEventUid struct {
106 ProcessPid int32
107 ProcessTgid int32
108 RUid uint32
109 EUid uint32
110}
111
112type ProcEventGid struct {
113 ProcessPid int32
114 ProcessTgid int32
115 RGid uint32
116 EGid uint32
117}
118
119type ProcEventExit struct {
120 ProcessPid int32
121 ProcessTgid int32
122 ExitCode uint32
123 ExitSignal uint32
124}
125
126type ProcEventCoreDump struct {
127 ProcessPid int32
128 ProcessTgid int32
129}
130
131type ProcEventComm struct {
132 ProcessPid int32
133 ProcessTgid int32
134 Comm [16]byte
135}
136
137type ProcEventPtrace struct {
138 ProcessPid int32
139 ProcessTgid int32
140 TracerPid int32
141 TracerTgid int32
142}
143
144type ProcEventSid struct {
145 ProcessPid int32
146 ProcessTgid int32
147}
148
149func ParseProcEvent(data []byte) *ProcEvent {
150 pe := (*ProcEvent)(unsafe.Pointer(&data[SizeofCnMsg]))
151 switch pe.What {
152 case PROC_EVENT_NONE:
153 pe.Data = nil
154 case PROC_EVENT_FORK:
155 pe.Data = *(*ProcEventFork)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
156 case PROC_EVENT_EXEC:
157 pe.Data = *(*ProcEventExec)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
158 case PROC_EVENT_UID:
159 pe.Data = *(*ProcEventUid)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
160 case PROC_EVENT_GID:
161 pe.Data = *(*ProcEventGid)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
162 case PROC_EVENT_SID:
163 pe.Data = *(*ProcEventSid)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
164 case PROC_EVENT_PTRACE:
165 pe.Data = *(*ProcEventPtrace)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
166 case PROC_EVENT_COMM:
167 pe.Data = *(*ProcEventComm)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
168 case PROC_EVENT_COREDUMP:
169 pe.Data = *(*ProcEventCoreDump)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
170 case PROC_EVENT_EXIT:
171 pe.Data = *(*ProcEventExit)(unsafe.Pointer(&data[SizeofCnMsg+SizeofProcEventHeader]))
172 default:
173 pe.Data = nil
174 }
175 return pe
176}
diff --git a/src/deal.go b/src/deal.go
index 717344c..c50174a 100644
--- a/src/deal.go
+++ b/src/deal.go
@@ -2,7 +2,6 @@ package main
2 2
3import ( 3import (
4 "fmt" 4 "fmt"
5 "sync"
6 "syscall" 5 "syscall"
7 6
8 "go.mongodb.org/mongo-driver/bson" 7 "go.mongodb.org/mongo-driver/bson"
@@ -14,9 +13,6 @@ const (
14 fdColName string = "fds" 13 fdColName string = "fds"
15) 14)
16 15
17var mongoMutex sync.Mutex
18var pidCol, fdCol mongoClient
19
20var docRes []bson.M 16var docRes []bson.M
21var err error 17var err error
22 18
@@ -55,15 +51,17 @@ func deal() {
55 break 51 break
56 } 52 }
57 53
58 // fmt.Printf("%v\n", cooked) 54 fmt.Printf("recv: %v\n", cooked.tag)
59 55
60 switch cooked.tag { 56 switch cooked.tag {
61 case NEWPID: 57 case NEWPID:
58 fmt.Printf("Fork: ppid=%6d, pid=%6d\n", cooked.ppid, cooked.pid)
62 dealNewPid(cooked) 59 dealNewPid(cooked)
63 case EXECVE: 60 case EXECVE:
64 check(cooked) 61 check(cooked)
65 dealExecve(cooked) 62 dealExecve(cooked)
66 case PIDEXIT: 63 case PIDEXIT:
64 fmt.Printf("Exit: pid=%6d\n", cooked.pid)
67 check(cooked) 65 check(cooked)
68 deletePid(cooked) 66 deletePid(cooked)
69 case FILEOPEN: 67 case FILEOPEN:
@@ -104,11 +102,13 @@ func deletePid(cooked Event) {
104} 102}
105 103
106func dealNewPid(cooked Event) { 104func dealNewPid(cooked Event) {
105 fmt.Printf("tag0 fork: %d %d\n", cooked.ppid, cooked.pid)
107 // 有无父进程在观察中 106 // 有无父进程在观察中
108 docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid}) 107 docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.ppid})
109 if err != nil || len(docRes) != 1 { 108 if err != nil || len(docRes) != 1 {
110 return 109 return
111 } 110 }
111 fmt.Printf("tag1 fork: %d %d\n", cooked.ppid, cooked.pid)
112 112
113 // 自身是否已经记录 113 // 自身是否已经记录
114 docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid}) 114 docRes, err = pidCol.Finddoc(bson.M{"pid": cooked.pid})
@@ -117,6 +117,7 @@ func dealNewPid(cooked Event) {
117 return 117 return
118 } 118 }
119 mongoMutex.Lock() 119 mongoMutex.Lock()
120 fmt.Printf("tag2 fork: %d %d\n", cooked.ppid, cooked.pid)
120 if len(docRes) != 0 { 121 if len(docRes) != 0 {
121 // 进程原本就存在,换言之别的消息先到了 122 // 进程原本就存在,换言之别的消息先到了
122 // 所有先行抵达的消息必须保留execve/children字段 123 // 所有先行抵达的消息必须保留execve/children字段
@@ -193,6 +194,7 @@ func dealExecve(cooked Event) {
193} 194}
194 195
195func fileOpen(cooked Event) { 196func fileOpen(cooked Event) {
197 fmt.Printf("FILEOPEN: %d %d %s\n", cooked.ppid, cooked.pid, cooked.pathName)
196 // 查看是否记录了该进程 198 // 查看是否记录了该进程
197 res, err := pidCol.Finddoc(bson.M{"pid": cooked.pid}) 199 res, err := pidCol.Finddoc(bson.M{"pid": cooked.pid})
198 if err != nil { 200 if err != nil {
diff --git a/src/global.go b/src/global.go
index f0f909c..d9bf4d0 100644
--- a/src/global.go
+++ b/src/global.go
@@ -33,12 +33,13 @@ type Event struct {
33 timestamp time.Time 33 timestamp time.Time
34 pid, ppid int 34 pid, ppid int
35 syscall int 35 syscall int
36 exit_code uint64
37 argc int 36 argc int
38 argv []string 37 argv []string
39 cwd string 38 cwd string
40 syscallParam [4]uint64 39 syscallParam [4]uint64
41 pathName string 40 pathName string
41 exit_code uint64
42 exit_signal int
42} 43}
43 44
44func (event Event) String() string { 45func (event Event) String() string {
diff --git a/src/godo.go b/src/godo.go
index 0edcc9f..c4d92c1 100644
--- a/src/godo.go
+++ b/src/godo.go
@@ -6,11 +6,14 @@ import (
6 "log" 6 "log"
7 "os" 7 "os"
8 "os/exec" 8 "os/exec"
9 "time" 9 "sync"
10 10
11 "github.com/elastic/go-libaudit/v2" 11 "github.com/elastic/go-libaudit/v2"
12) 12)
13 13
14var mongoMutex sync.Mutex
15var pidCol, fdCol mongoClient
16
14var ( 17var (
15 fs = flag.NewFlagSet("audit", flag.ExitOnError) 18 fs = flag.NewFlagSet("audit", flag.ExitOnError)
16 diag = fs.String("diag", "", "dump raw information from kernel to file") 19 diag = fs.String("diag", "", "dump raw information from kernel to file")
@@ -37,7 +40,8 @@ func main() {
37 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则 40 auditCmd = exec.Command("auditctl", "-D") // 清空所有规则
38 auditCmd.Run() 41 auditCmd.Run()
39 42
40 pidSyscall := []string{"fork", "vfork", "clone", "execve", "exit", "exit_group"} 43 pidSyscall := []string{"execve"}
44 // pidSyscall := []string{"fork", "vfork", "clone", "execve", "exit", "exit_group"}
41 // 设置监听规则 45 // 设置监听规则
42 for i := 0; i < len(pidSyscall); i++ { 46 for i := 0; i < len(pidSyscall); i++ {
43 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i]) 47 auditCmd = exec.Command("auditctl", "-a", "exit,always", "-F", "arch=b64", "-S", pidSyscall[i])
@@ -76,14 +80,17 @@ func main() {
76func coroutine(client *libaudit.AuditClient) { 80func coroutine(client *libaudit.AuditClient) {
77 // 各协程至此开始 81 // 各协程至此开始
78 rawChan = make(chan interface{}) 82 rawChan = make(chan interface{})
79 cookedChan = make(chan Event) 83 cookedChan = make(chan Event, 1024)
80 wg.Add(1) 84 ProcWatch()
81 go receive(client) 85 // wg.Add(1)
82 wg.Add(1) 86 // go ProcWatch()
83 go orgnaze() 87 // wg.Add(1)
84 wg.Add(1) 88 // go receive(client)
85 go deal() 89 // wg.Add(1)
90 // go orgnaze()
91 // wg.Add(1)
92 // go deal()
86 93
87 wg.Wait() 94 // wg.Wait()
88 time.Sleep(2 * time.Second) 95 // time.Sleep(2 * time.Second)
89} 96}
diff --git a/src/organize.go b/src/organize.go
index d7a1df1..49796cc 100644
--- a/src/organize.go
+++ b/src/organize.go
@@ -47,7 +47,7 @@ func orgnaze() {
47 break 47 break
48 } 48 }
49 rawEvent = raw.(libaudit.RawAuditMessage) 49 rawEvent = raw.(libaudit.RawAuditMessage)
50 fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data) 50 // fmt.Printf("type=%v msg=%s\n", rawEvent.Type, rawEvent.Data)
51 51
52 switch rawEvent.Type { 52 switch rawEvent.Type {
53 case auparse.AUDIT_SYSCALL: 53 case auparse.AUDIT_SYSCALL:
@@ -100,24 +100,24 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) {
100 } 100 }
101 101
102 switch syscallTable[event.syscall] { 102 switch syscallTable[event.syscall] {
103 case "clone": 103 // case "clone":
104 if exit == 0 || event.pid > exit { 104 // if exit == 0 || event.pid > exit {
105 // exit=0是给新进程的返回,没用 105 // // exit=0是给新进程的返回,没用
106 // pid>exit,证明有问题,抛弃 106 // // pid>exit,证明有问题,抛弃
107 break 107 // break
108 } else { 108 // } else {
109 eventTable.Store(eventId, &Event{ 109 // eventTable.Store(eventId, &Event{
110 tag: NEWPID, 110 // tag: NEWPID,
111 timestamp: event.timestamp, 111 // timestamp: event.timestamp,
112 syscall: event.syscall, 112 // syscall: event.syscall,
113 exit_code: 0, 113 // exit_code: 0,
114 ppid: event.pid, 114 // ppid: event.pid,
115 pid: exit, 115 // pid: exit,
116 argc: 0, 116 // argc: 0,
117 argv: make([]string, 0), 117 // argv: make([]string, 0),
118 cwd: "", 118 // cwd: "",
119 }) 119 // })
120 } 120 // }
121 case "execve": 121 case "execve":
122 eventTable.Store(eventId, &Event{ 122 eventTable.Store(eventId, &Event{
123 tag: EXECVE, 123 tag: EXECVE,
@@ -130,18 +130,18 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) {
130 argv: make([]string, 0), 130 argv: make([]string, 0),
131 cwd: "", 131 cwd: "",
132 }) 132 })
133 case "exit", "exit_group": 133 // case "exit", "exit_group":
134 eventTable.Store(eventId, &Event{ 134 // eventTable.Store(eventId, &Event{
135 tag: PIDEXIT, 135 // tag: PIDEXIT,
136 timestamp: event.timestamp, 136 // timestamp: event.timestamp,
137 syscall: event.syscall, 137 // syscall: event.syscall,
138 exit_code: a[0], 138 // exit_code: a[0],
139 ppid: event.ppid, 139 // ppid: event.ppid,
140 pid: event.pid, 140 // pid: event.pid,
141 argc: 0, 141 // argc: 0,
142 argv: make([]string, 0), 142 // argv: make([]string, 0),
143 cwd: "", 143 // cwd: "",
144 }) 144 // })
145 case "open": 145 case "open":
146 // 检查打开的权限 146 // 检查打开的权限
147 if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 { 147 if a[1]&(syscall.O_APPEND|syscall.O_WRONLY|syscall.O_RDWR|syscall.O_TRUNC) == 0 {
@@ -190,6 +190,7 @@ func syscallRaw(rawEvent libaudit.RawAuditMessage) {
190 syscallParam: a, 190 syscallParam: a,
191 // pathName: "", 191 // pathName: "",
192 }) 192 })
193 default:
193 } 194 }
194} 195}
195 196
diff --git a/src/procWatch.go b/src/procWatch.go
new file mode 100644
index 0000000..34721cf
--- /dev/null
+++ b/src/procWatch.go
@@ -0,0 +1,86 @@
1package main
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "strings"
8 "syscall"
9 "time"
10)
11
12func ProcWatch() error {
13 ns, err := NewNetlinkSocket(syscall.NETLINK_CONNECTOR, 12345)
14 if err != nil {
15 return err
16 }
17 defer ns.Close()
18 fmt.Printf("Proc Watch!\n")
19 for {
20 res, err := ns.Receive()
21 if err != nil {
22 continue
23 }
24 for i := 0; i < len(res); i++ {
25 procEvent := ParseProcEvent(res[i].Data)
26 switch procEvent.What {
27 case PROC_EVENT_FORK:
28 data := procEvent.Data.(ProcEventFork)
29 // if data.ChildPid != data.ChildTgid {
30 // break
31 // }
32 fmt.Printf("fork\t%6d\t%6d\t%6d\t%6d\n", data.ParentPid, data.ParentTgid, data.ChildPid, data.ChildTgid)
33 cooked := Event{
34 tag: NEWPID,
35 timestamp: time.Now(),
36 ppid: int(data.ParentTgid),
37 pid: int(data.ChildTgid),
38 syscall: 57,
39 syscallParam: [4]uint64{},
40 }
41 checkProc(&cooked)
42 cookedChan <- cooked
43 fmt.Printf("send fork: %d-->%d\n", cooked.ppid, cooked.pid)
44 case PROC_EVENT_EXIT:
45 data := procEvent.Data.(ProcEventExit)
46 // TODO: Event是否增加退出信号字段, 酌定
47 if data.ProcessPid != data.ProcessTgid {
48 break
49 }
50 cooked := Event{
51 tag: PIDEXIT,
52 timestamp: time.Now(),
53 pid: int(data.ProcessTgid),
54 exit_code: uint64(data.ExitCode),
55 exit_signal: int(data.ExitSignal),
56 }
57 cookedChan <- cooked
58 default:
59 }
60 }
61 }
62}
63
64// 补充args与cwd
65func checkProc(pCooked *Event) {
66 fileName := fmt.Sprintf("/proc/%d/cmdline", pCooked.pid)
67 fd, err := os.Open(fileName)
68 if err != nil {
69 return
70 }
71 // defer fd.Close()
72
73 scanner := bufio.NewScanner(fd)
74 for scanner.Scan() {
75 line := scanner.Text()
76 pCooked.argv = append(pCooked.argv, strings.Split(line, "\x00")...)
77 }
78 pCooked.argc = len(pCooked.argv)
79 fd.Close()
80
81 fileName = fmt.Sprintf("/proc/%d/cwd", pCooked.pid)
82 pCooked.cwd, err = os.Readlink(fileName)
83 if err != nil {
84 pCooked.cwd = ""
85 }
86}
diff --git a/src/request.go b/src/request.go
new file mode 100644
index 0000000..40df8df
--- /dev/null
+++ b/src/request.go
@@ -0,0 +1,57 @@
1package main
2
3import (
4 "os"
5 "syscall"
6 "unsafe"
7)
8
9type NetlinkRequestData interface {
10 Len() int
11 Serialize() []byte
12}
13
14// linux/netlink.h
15type NetlinkRequest struct {
16 syscall.NlMsghdr
17 Data []NetlinkRequestData
18}
19
20func (req *NetlinkRequest) Serialize() []byte {
21 length := syscall.SizeofNlMsghdr
22 dataBytes := make([][]byte, len(req.Data))
23 for i, data := range req.Data {
24 dataBytes[i] = data.Serialize()
25 length = length + len(dataBytes[i])
26 }
27 req.Len = uint32(length)
28 b := make([]byte, length)
29 hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
30 next := syscall.SizeofNlMsghdr
31 copy(b[0:next], hdr)
32 for _, data := range dataBytes {
33 for _, dataByte := range data {
34 b[next] = dataByte
35 next = next + 1
36 }
37 }
38 return b
39}
40
41func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
42 if data != nil {
43 req.Data = append(req.Data, data)
44 }
45}
46
47func NewNetlinkRequest() *NetlinkRequest {
48 return &NetlinkRequest{
49 NlMsghdr: syscall.NlMsghdr{
50 Len: uint32(0),
51 Type: uint16(syscall.NLMSG_DONE),
52 Flags: uint16(0),
53 Seq: uint32(0),
54 Pid: uint32(os.Getpid()),
55 },
56 }
57}
diff --git a/src/socket.go b/src/socket.go
new file mode 100644
index 0000000..83c7333
--- /dev/null
+++ b/src/socket.go
@@ -0,0 +1,79 @@
1package main
2
3import (
4 "fmt"
5 "os"
6 "syscall"
7)
8
9type NetlinkProtocol int
10
11const (
12 CONNECTOR NetlinkProtocol = syscall.NETLINK_CONNECTOR
13 INET_DIAG NetlinkProtocol = syscall.NETLINK_INET_DIAG
14)
15
16type NetlinkSocket struct {
17 fd int
18 protocol NetlinkProtocol
19 groups uint32
20}
21
22func NewNetlinkSocket(protocol NetlinkProtocol, groups uint32) (*NetlinkSocket, error) {
23 fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, int(protocol))
24 if err != nil {
25 return nil, err
26 }
27 ns := &NetlinkSocket{
28 fd: fd,
29 protocol: protocol,
30 groups: groups,
31 }
32 if ns.protocol == CONNECTOR {
33 sockaddr := &syscall.SockaddrNetlink{
34 Family: syscall.AF_NETLINK,
35 Pid: uint32(os.Getpid()),
36 Groups: groups,
37 }
38 if err = syscall.Bind(fd, sockaddr); err != nil {
39 syscall.Close(fd)
40 return nil, err
41 }
42 }
43 return ns, nil
44}
45
46func (ns *NetlinkSocket) Close() {
47 syscall.Close(ns.fd)
48}
49
50func (ns *NetlinkSocket) Send(request *NetlinkRequest) error {
51 if ns.protocol == CONNECTOR {
52 if _, err := syscall.Write(ns.fd, request.Serialize()); err != nil {
53 return err
54 }
55 } else {
56 sockaddr := &syscall.SockaddrNetlink{
57 Family: syscall.AF_NETLINK,
58 Pid: 0,
59 Groups: ns.groups,
60 }
61 if err := syscall.Sendto(ns.fd, request.Serialize(), 0, sockaddr); err != nil {
62 return err
63 }
64 }
65 return nil
66}
67
68func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
69 rb := make([]byte, syscall.Getpagesize())
70 nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
71 if err != nil {
72 return nil, err
73 }
74 if nr < syscall.NLMSG_HDRLEN {
75 return nil, fmt.Errorf("Got short response from netlink")
76 }
77 rb = rb[:nr]
78 return syscall.ParseNetlinkMessage(rb)
79}