mirror of https://github.com/vishvananda/netlink
Implement unix socket diagnostics
This commit is contained in:
parent
a008cbde48
commit
ec7bcb248e
10
socket.go
10
socket.go
|
@ -25,3 +25,13 @@ type Socket struct {
|
||||||
UID uint32
|
UID uint32
|
||||||
INode uint32
|
INode uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnixSocket represents a netlink unix socket.
|
||||||
|
type UnixSocket struct {
|
||||||
|
Type uint8
|
||||||
|
Family uint8
|
||||||
|
State uint8
|
||||||
|
pad uint8
|
||||||
|
INode uint32
|
||||||
|
Cookie [2]uint32
|
||||||
|
}
|
||||||
|
|
142
socket_linux.go
142
socket_linux.go
|
@ -14,6 +14,8 @@ const (
|
||||||
sizeofSocketID = 0x30
|
sizeofSocketID = 0x30
|
||||||
sizeofSocketRequest = sizeofSocketID + 0x8
|
sizeofSocketRequest = sizeofSocketID + 0x8
|
||||||
sizeofSocket = sizeofSocketID + 0x18
|
sizeofSocket = sizeofSocketID + 0x18
|
||||||
|
sizeofUnixSocketRequest = 0x18 // 24 byte
|
||||||
|
sizeofUnixSocket = 0x10 // 16 byte
|
||||||
)
|
)
|
||||||
|
|
||||||
type socketRequest struct {
|
type socketRequest struct {
|
||||||
|
@ -67,6 +69,32 @@ func (r *socketRequest) Serialize() []byte {
|
||||||
|
|
||||||
func (r *socketRequest) Len() int { return sizeofSocketRequest }
|
func (r *socketRequest) Len() int { return sizeofSocketRequest }
|
||||||
|
|
||||||
|
// According to linux/include/uapi/linux/unix_diag.h
|
||||||
|
type unixSocketRequest struct {
|
||||||
|
Family uint8
|
||||||
|
Protocol uint8
|
||||||
|
pad uint16
|
||||||
|
States uint32
|
||||||
|
INode uint32
|
||||||
|
Show uint32
|
||||||
|
Cookie [2]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *unixSocketRequest) Serialize() []byte {
|
||||||
|
b := writeBuffer{Bytes: make([]byte, sizeofUnixSocketRequest)}
|
||||||
|
b.Write(r.Family)
|
||||||
|
b.Write(r.Protocol)
|
||||||
|
native.PutUint16(b.Next(2), r.pad)
|
||||||
|
native.PutUint32(b.Next(4), r.States)
|
||||||
|
native.PutUint32(b.Next(4), r.INode)
|
||||||
|
native.PutUint32(b.Next(4), r.Show)
|
||||||
|
native.PutUint32(b.Next(4), r.Cookie[0])
|
||||||
|
native.PutUint32(b.Next(4), r.Cookie[1])
|
||||||
|
return b.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *unixSocketRequest) Len() int { return sizeofUnixSocketRequest }
|
||||||
|
|
||||||
type readBuffer struct {
|
type readBuffer struct {
|
||||||
Bytes []byte
|
Bytes []byte
|
||||||
pos int
|
pos int
|
||||||
|
@ -115,6 +143,21 @@ func (s *Socket) deserialize(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UnixSocket) deserialize(b []byte) error {
|
||||||
|
if len(b) < sizeofUnixSocket {
|
||||||
|
return fmt.Errorf("unix diag data short read (%d); want %d", len(b), sizeofUnixSocket)
|
||||||
|
}
|
||||||
|
rb := readBuffer{Bytes: b}
|
||||||
|
u.Type = rb.Read()
|
||||||
|
u.Family = rb.Read()
|
||||||
|
u.State = rb.Read()
|
||||||
|
u.pad = rb.Read()
|
||||||
|
u.INode = native.Uint32(rb.Next(4))
|
||||||
|
u.Cookie[0] = native.Uint32(rb.Next(4))
|
||||||
|
u.Cookie[1] = native.Uint32(rb.Next(4))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SocketGet returns the Socket identified by its local and remote addresses.
|
// SocketGet returns the Socket identified by its local and remote addresses.
|
||||||
func SocketGet(local, remote net.Addr) (*Socket, error) {
|
func SocketGet(local, remote net.Addr) (*Socket, error) {
|
||||||
localTCP, ok := local.(*net.TCPAddr)
|
localTCP, ok := local.(*net.TCPAddr)
|
||||||
|
@ -157,7 +200,7 @@ func SocketGet(local, remote net.Addr) (*Socket, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if from.Pid != nl.PidKernel {
|
if from.Pid != nl.PidKernel {
|
||||||
return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
|
return nil, fmt.Errorf("wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
|
||||||
}
|
}
|
||||||
if len(msgs) == 0 {
|
if len(msgs) == 0 {
|
||||||
return nil, errors.New("no message nor error from netlink")
|
return nil, errors.New("no message nor error from netlink")
|
||||||
|
@ -305,6 +348,78 @@ func SocketDiagUDP(family uint8) ([]*Socket, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnixSocketDiagInfo requests UNIX_DIAG_INFO for unix sockets and return with extension info.
|
||||||
|
func UnixSocketDiagInfo() ([]*UnixDiagInfoResp, error) {
|
||||||
|
// Construct the request
|
||||||
|
var extensions uint8
|
||||||
|
extensions = 1 << UNIX_DIAG_NAME
|
||||||
|
extensions |= 1 << UNIX_DIAG_PEER
|
||||||
|
extensions |= 1 << UNIX_DIAG_RQLEN
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&unixSocketRequest{
|
||||||
|
Family: unix.AF_UNIX,
|
||||||
|
States: ^uint32(0), // all states
|
||||||
|
Show: uint32(extensions),
|
||||||
|
})
|
||||||
|
|
||||||
|
var result []*UnixDiagInfoResp
|
||||||
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
|
sockInfo := &UnixSocket{}
|
||||||
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnosis also delivers sockets with AF_INET family, filter those
|
||||||
|
if sockInfo.Family != unix.AF_UNIX {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs, err := nl.ParseRouteAttr(m.Data[sizeofUnixSocket:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := attrsToUnixDiagInfoResp(attrs, sockInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result = append(result, res)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixSocketDiag requests UNIX_DIAG_INFO for unix sockets.
|
||||||
|
func UnixSocketDiag() ([]*UnixSocket, error) {
|
||||||
|
// Construct the request
|
||||||
|
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
|
||||||
|
req.AddData(&unixSocketRequest{
|
||||||
|
Family: unix.AF_UNIX,
|
||||||
|
States: ^uint32(0), // all states
|
||||||
|
})
|
||||||
|
|
||||||
|
var result []*UnixSocket
|
||||||
|
err := socketDiagExecutor(req, func(m syscall.NetlinkMessage) error {
|
||||||
|
sockInfo := &UnixSocket{}
|
||||||
|
if err := sockInfo.deserialize(m.Data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnosis also delivers sockets with AF_INET family, filter those
|
||||||
|
if sockInfo.Family == unix.AF_UNIX {
|
||||||
|
result = append(result, sockInfo)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// socketDiagExecutor requests diagnoses info from the NETLINK_INET_DIAG socket for the specified request.
|
// socketDiagExecutor requests diagnoses info from the NETLINK_INET_DIAG socket for the specified request.
|
||||||
func socketDiagExecutor(req *nl.NetlinkRequest, receiver func(syscall.NetlinkMessage) error) error {
|
func socketDiagExecutor(req *nl.NetlinkRequest, receiver func(syscall.NetlinkMessage) error) error {
|
||||||
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
|
s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
|
||||||
|
@ -381,3 +496,28 @@ func attrsToInetDiagUDPInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *Sock
|
||||||
|
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func attrsToUnixDiagInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *UnixSocket) (*UnixDiagInfoResp, error) {
|
||||||
|
info := &UnixDiagInfoResp{
|
||||||
|
DiagMsg: sockInfo,
|
||||||
|
}
|
||||||
|
for _, a := range attrs {
|
||||||
|
switch a.Attr.Type {
|
||||||
|
case UNIX_DIAG_NAME:
|
||||||
|
name := string(a.Value[:a.Attr.Len])
|
||||||
|
info.Name = &name
|
||||||
|
case UNIX_DIAG_PEER:
|
||||||
|
peer := native.Uint32(a.Value)
|
||||||
|
info.Peer = &peer
|
||||||
|
case UNIX_DIAG_RQLEN:
|
||||||
|
info.Queue = &QueueInfo{
|
||||||
|
RQueue: native.Uint32(a.Value[:4]),
|
||||||
|
WQueue: native.Uint32(a.Value[4:]),
|
||||||
|
}
|
||||||
|
// default:
|
||||||
|
// fmt.Println("unknown unix attribute type", a.Attr.Type, "with data", a.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package netlink
|
package netlink
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
@ -91,3 +92,18 @@ func TestSocketDiagUDPnfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnixSocketDiagInfo(t *testing.T) {
|
||||||
|
want := syscall.AF_UNIX
|
||||||
|
result, err := UnixSocketDiagInfo()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, r := range result {
|
||||||
|
fmt.Println(r.DiagMsg)
|
||||||
|
if got := r.DiagMsg.Family; got != uint8(want) {
|
||||||
|
t.Fatalf("%d: protocol family = %v, want %v", i, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package netlink
|
||||||
|
|
||||||
|
// According to linux/include/uapi/linux/unix_diag.h
|
||||||
|
const (
|
||||||
|
UNIX_DIAG_NAME = iota
|
||||||
|
UNIX_DIAG_VFS
|
||||||
|
UNIX_DIAG_PEER
|
||||||
|
UNIX_DIAG_ICONS
|
||||||
|
UNIX_DIAG_RQLEN
|
||||||
|
UNIX_DIAG_MEMINFO
|
||||||
|
UNIX_DIAG_SHUTDOWN
|
||||||
|
UNIX_DIAG_UID
|
||||||
|
UNIX_DIAG_MAX
|
||||||
|
)
|
||||||
|
|
||||||
|
type UnixDiagInfoResp struct {
|
||||||
|
DiagMsg *UnixSocket
|
||||||
|
Name *string
|
||||||
|
Peer *uint32
|
||||||
|
Queue *QueueInfo
|
||||||
|
Shutdown *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueueInfo struct {
|
||||||
|
RQueue uint32
|
||||||
|
WQueue uint32
|
||||||
|
}
|
Loading…
Reference in New Issue