Skip to content

Commit ec7bcb2

Browse files
srebhanaboch
authored andcommitted
Implement unix socket diagnostics
1 parent a008cbd commit ec7bcb2

File tree

4 files changed

+197
-4
lines changed

4 files changed

+197
-4
lines changed

socket.go

+10
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,13 @@ type Socket struct {
2525
UID uint32
2626
INode uint32
2727
}
28+
29+
// UnixSocket represents a netlink unix socket.
30+
type UnixSocket struct {
31+
Type uint8
32+
Family uint8
33+
State uint8
34+
pad uint8
35+
INode uint32
36+
Cookie [2]uint32
37+
}

socket_linux.go

+144-4
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import (
1111
)
1212

1313
const (
14-
sizeofSocketID = 0x30
15-
sizeofSocketRequest = sizeofSocketID + 0x8
16-
sizeofSocket = sizeofSocketID + 0x18
14+
sizeofSocketID = 0x30
15+
sizeofSocketRequest = sizeofSocketID + 0x8
16+
sizeofSocket = sizeofSocketID + 0x18
17+
sizeofUnixSocketRequest = 0x18 // 24 byte
18+
sizeofUnixSocket = 0x10 // 16 byte
1719
)
1820

1921
type socketRequest struct {
@@ -67,6 +69,32 @@ func (r *socketRequest) Serialize() []byte {
6769

6870
func (r *socketRequest) Len() int { return sizeofSocketRequest }
6971

72+
// According to linux/include/uapi/linux/unix_diag.h
73+
type unixSocketRequest struct {
74+
Family uint8
75+
Protocol uint8
76+
pad uint16
77+
States uint32
78+
INode uint32
79+
Show uint32
80+
Cookie [2]uint32
81+
}
82+
83+
func (r *unixSocketRequest) Serialize() []byte {
84+
b := writeBuffer{Bytes: make([]byte, sizeofUnixSocketRequest)}
85+
b.Write(r.Family)
86+
b.Write(r.Protocol)
87+
native.PutUint16(b.Next(2), r.pad)
88+
native.PutUint32(b.Next(4), r.States)
89+
native.PutUint32(b.Next(4), r.INode)
90+
native.PutUint32(b.Next(4), r.Show)
91+
native.PutUint32(b.Next(4), r.Cookie[0])
92+
native.PutUint32(b.Next(4), r.Cookie[1])
93+
return b.Bytes
94+
}
95+
96+
func (r *unixSocketRequest) Len() int { return sizeofUnixSocketRequest }
97+
7098
type readBuffer struct {
7199
Bytes []byte
72100
pos int
@@ -115,6 +143,21 @@ func (s *Socket) deserialize(b []byte) error {
115143
return nil
116144
}
117145

146+
func (u *UnixSocket) deserialize(b []byte) error {
147+
if len(b) < sizeofUnixSocket {
148+
return fmt.Errorf("unix diag data short read (%d); want %d", len(b), sizeofUnixSocket)
149+
}
150+
rb := readBuffer{Bytes: b}
151+
u.Type = rb.Read()
152+
u.Family = rb.Read()
153+
u.State = rb.Read()
154+
u.pad = rb.Read()
155+
u.INode = native.Uint32(rb.Next(4))
156+
u.Cookie[0] = native.Uint32(rb.Next(4))
157+
u.Cookie[1] = native.Uint32(rb.Next(4))
158+
return nil
159+
}
160+
118161
// SocketGet returns the Socket identified by its local and remote addresses.
119162
func SocketGet(local, remote net.Addr) (*Socket, error) {
120163
localTCP, ok := local.(*net.TCPAddr)
@@ -157,7 +200,7 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
157200
return nil, err
158201
}
159202
if from.Pid != nl.PidKernel {
160-
return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
203+
return nil, fmt.Errorf("wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
161204
}
162205
if len(msgs) == 0 {
163206
return nil, errors.New("no message nor error from netlink")
@@ -305,6 +348,78 @@ func SocketDiagUDP(family uint8) ([]*Socket, error) {
305348
return result, nil
306349
}
307350

351+
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info.
352+
func UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
353+
// Construct the request
354+
var extensions uint8
355+
extensions = 1 << UNIX_DIAG_NAME
356+
extensions |= 1 << UNIX_DIAG_PEER
357+
extensions |= 1 << UNIX_DIAG_RQLEN
358+
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
359+
req.AddData(&unixSocketRequest{
360+
Family: unix.AF_UNIX,
361+
States: ^uint32(0), // all states
362+
Show: uint32(extensions),
363+
})
364+
365+
var result []*UnixDiagInfoResp
366+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
367+
sockInfo := &UnixSocket{}
368+
if err := sockInfo.deserialize(m.Data); err != nil {
369+
return err
370+
}
371+
372+
// Diagnosis also delivers sockets with AF_INET family, filter those
373+
if sockInfo.Family != unix.AF_UNIX {
374+
return nil
375+
}
376+
377+
attrs, err := nl.ParseRouteAttr(m.Data[sizeofUnixSocket:])
378+
if err != nil {
379+
return err
380+
}
381+
382+
res, err := attrsToUnixDiagInfoResp(attrs, sockInfo)
383+
if err != nil {
384+
return err
385+
}
386+
result = append(result, res)
387+
return nil
388+
})
389+
if err != nil {
390+
return nil, err
391+
}
392+
return result, nil
393+
}
394+
395+
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets.
396+
func UnixSocketDiag() ([]*UnixSocket, error) {
397+
// Construct the request
398+
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
399+
req.AddData(&unixSocketRequest{
400+
Family: unix.AF_UNIX,
401+
States: ^uint32(0), // all states
402+
})
403+
404+
var result []*UnixSocket
405+
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
406+
sockInfo := &UnixSocket{}
407+
if err := sockInfo.deserialize(m.Data); err != nil {
408+
return err
409+
}
410+
411+
// Diagnosis also delivers sockets with AF_INET family, filter those
412+
if sockInfo.Family == unix.AF_UNIX {
413+
result = append(result, sockInfo)
414+
}
415+
return nil
416+
})
417+
if err != nil {
418+
return nil, err
419+
}
420+
return result, nil
421+
}
422+
308423
// socketDiagExecutor requests diagnoses info from the NETLINK_INET_DIAG socket for the specified request.
309424
func socketDiagExecutor(req *nl.NetlinkRequest, receiver func(syscall.NetlinkMessage) error) error {
310425
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
@@ -381,3 +496,28 @@ func attrsToInetDiagUDPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Sock
381496

382497
return info, nil
383498
}
499+
500+
func attrsToUnixDiagInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *UnixSocket) (*UnixDiagInfoResp, error) {
501+
info := &UnixDiagInfoResp{
502+
DiagMsg: sockInfo,
503+
}
504+
for _, a := range attrs {
505+
switch a.Attr.Type {
506+
case UNIX_DIAG_NAME:
507+
name := string(a.Value[:a.Attr.Len])
508+
info.Name = &name
509+
case UNIX_DIAG_PEER:
510+
peer := native.Uint32(a.Value)
511+
info.Peer = &peer
512+
case UNIX_DIAG_RQLEN:
513+
info.Queue = &QueueInfo{
514+
RQueue: native.Uint32(a.Value[:4]),
515+
WQueue: native.Uint32(a.Value[4:]),
516+
}
517+
// default:
518+
// fmt.Println("unknown unix attribute type", a.Attr.Type, "with data", a.Value)
519+
}
520+
}
521+
522+
return info, nil
523+
}

socket_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package netlink
55

66
import (
7+
"fmt"
78
"log"
89
"net"
910
"os/user"
@@ -91,3 +92,18 @@ func TestSocketDiagUDPnfo(t *testing.T) {
9192
}
9293
}
9394
}
95+
96+
func TestUnixSocketDiagInfo(t *testing.T) {
97+
want := syscall.AF_UNIX
98+
result, err := UnixSocketDiagInfo()
99+
if err != nil {
100+
t.Fatal(err)
101+
}
102+
103+
for i, r := range result {
104+
fmt.Println(r.DiagMsg)
105+
if got := r.DiagMsg.Family; got != uint8(want) {
106+
t.Fatalf("%d: protocol family = %v, want %v", i, got, want)
107+
}
108+
}
109+
}

unix_diag.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package netlink
2+
3+
// According to linux/include/uapi/linux/unix_diag.h
4+
const (
5+
UNIX_DIAG_NAME = iota
6+
UNIX_DIAG_VFS
7+
UNIX_DIAG_PEER
8+
UNIX_DIAG_ICONS
9+
UNIX_DIAG_RQLEN
10+
UNIX_DIAG_MEMINFO
11+
UNIX_DIAG_SHUTDOWN
12+
UNIX_DIAG_UID
13+
UNIX_DIAG_MAX
14+
)
15+
16+
type UnixDiagInfoResp struct {
17+
DiagMsg *UnixSocket
18+
Name *string
19+
Peer *uint32
20+
Queue *QueueInfo
21+
Shutdown *uint8
22+
}
23+
24+
type QueueInfo struct {
25+
RQueue uint32
26+
WQueue uint32
27+
}

0 commit comments

Comments
 (0)