opensnitch/daemon/netlink/socket_packet.go
Gustavo Iñiguez Goia 1a39122c1d
netstat: dump AF_PACKET sockets from the kernel
We'll try to dump the AF_PACKET sockets from the kernel. If it's not
possible, we'll fallback to read /proc/net/packet.
2025-02-06 01:49:40 +01:00

172 lines
5.3 KiB
Go

package netlink
import (
"encoding/binary"
"syscall"
"unsafe"
"github.com/evilsocket/opensnitch/daemon/log"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// request:
// {nlmsg_len=36, nlmsg_type=SOCK_DIAG_BY_FAMILY, nlmsg_flags=NLM_F_REQUEST|NLM_F_DUMP, nlmsg_seq=123456, nlmsg_pid=0},
// {sdiag_family=AF_PACKET, sdiag_protocol=0, pdiag_ino=0, pdiag_show=PACKET_SHOW_INFO, pdiag_cookie=[0, 0]}
// responses (depends on what filters are passed in the request):
// {pdiag_family=AF_PACKET, pdiag_type=SOCK_RAW, pdiag_num=ETH_P_ALL, pdiag_ino=257944535, pdiag_cookie=[1291434, 0]},
// {nla_len=28, nla_type=PACKET_DIAG_INFO},
// {pdi_index=if_nametoindex("wifi0"), pdi_version=TPACKET_V3, pdi_reserve=4, pdi_copy_thresh=0, pdi_tstamp=0, pdi_flags=PDI_RUNNING|PDI_AUXDATA}
// {nla_len=8, nla_type=PACKET_DIAG_UID}, 0}
// {nla_len=32, nla_type=PACKET_DIAG_RX_RING},
// {pdr_block_size=262144, pdr_block_nr=8, pdr_frame_size=262144, pdr_frame_nr=8, pdr_retire_tmo=10, pdr_sizeof_priv=0, pdr_features=0}
// {nla_len=40, nla_type=PACKET_DIAG_MEMINFO},
// [[SK_MEMINFO_RMEM_ALLOC]=0, [SK_MEMINFO_RCVBUF]=212992, [SK_MEMINFO_WMEM_ALLOC]=0, [SK_MEMINFO_SNDBUF]=212992, [SK_MEMINFO_FWD_ALLOC]=0, [SK_MEMINFO_WMEM_QUEUED]=0, [SK_MEMINFO_OPTMEM]=128, [SK_MEMINFO_BACKLOG]=0, [SK_MEMINFO_DROPS]=0]],
// {nla_len=12, nla_type=PACKET_DIAG_FILTER}, 0x512e76dfc140}
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/packet_diag.h#L16
// list of possible information to request
const (
PACKET_SHOW_INFO = 0x00000001 /* Basic packet_sk information */
PACKET_SHOW_MCLIST = 0x00000002 /* A set of packet_diag_mclist-s */
PACKET_SHOW_RING_CFG = 0x00000004 /* Rings configuration parameters */
PACKET_SHOW_FANOUT = 0x00000008
PACKET_SHOW_MEMINFO = 0x00000010
PACKET_SHOW_FILTER = 0x00000020
)
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/packet_diag.h#L32
// types of messages retrieved from kernel
const (
PACKET_DIAG_INFO = iota
PACKET_DIAG_MCLIST
PACKET_DIAG_RX_RING
PACKET_DIAG_TX_RING
PACKET_DIAG_FANOUT
PACKET_DIAG_UID
PACKET_DIAG_MEMINFO
PACKET_DIAG_FILTER
)
const (
sizePktDiagReq = 20
sizePktDiagMclist = 28
)
// PacketDiagMsg holds the message(s) sent by the kernel
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/packet_diag.h#L23
type PacketDiagMsg struct {
Mclist PacketDiagMclist
Cookie [2]uint32
Inode uint32
UID uint32
Num uint16 // ETH_P_ALL, etc
Family uint8
Type uint8
}
// PacketDiagMclist struct
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/packet_diag.h#L63
type PacketDiagMclist struct {
Index uint32
Count uint32
Type uint16
Alen uint16
Addr [32]uint8 /* MAX_ADDR_LEN */
}
func (pm *PacketDiagMsg) deserialize(b []byte) error {
rb := readBuffer{Bytes: b}
// 1st message: PacketDiagMsg
pm.Family = rb.Read()
pm.Type = rb.Read()
pm.Num = native.Uint16(rb.Next(2))
pm.Inode = native.Uint32(rb.Next(4))
pm.Cookie[0] = native.Uint32(rb.Next(4))
pm.Cookie[1] = native.Uint32(rb.Next(4))
nextMsg := rb.Read() // next msg size
if nextMsg == sizePktDiagMclist {
pm.Mclist = PacketDiagMclist{
// XXX: wrong values with native.Uint32()
Index: binary.BigEndian.Uint32(rb.Next(4)),
Count: binary.BigEndian.Uint32(rb.Next(4)),
Type: binary.BigEndian.Uint16(rb.Next(2)),
Alen: binary.BigEndian.Uint16(rb.Next(2)),
}
copy(pm.Mclist.Addr[:], rb.Next(16))
}
// {nla_len=8, nla_type=PACKET_DIAG_UID}, 1000}
nextMsg = rb.Read() // 8, size of next msg
nextMsg = rb.Read() // pad?
if nextMsg == PACKET_DIAG_UID {
rb.Read() // pad?
pm.UID = native.Uint32(rb.Next(4))
}
log.Trace("PktDiagMsg.deserialize (size: %d, sizeOf(PacketDiagMsg): %d): %+v", len(b), unsafe.Sizeof(b), pm)
return nil
}
// PacketDiagReq struct to request data from the kernel
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/packet_diag.h#L7
type PacketDiagReq struct {
Family uint8
Protocol uint8
Pad uint16
Inode uint32
Show uint32
Cookie [2]uint32
}
// Serialize ...
func (p *PacketDiagReq) Serialize() []byte {
b := writeBuffer{Bytes: make([]byte, sizePktDiagReq)}
b.Write(p.Family)
b.Write(p.Protocol)
native.PutUint16(b.Next(2), p.Pad)
native.PutUint32(b.Next(4), p.Inode)
native.PutUint32(b.Next(4), p.Show)
native.PutUint32(b.Next(4), p.Cookie[0])
native.PutUint32(b.Next(4), p.Cookie[1])
return b.Bytes
}
// Len ...
func (p *PacketDiagReq) Len() int { return sizePktDiagReq }
// SocketDiagPacket dumps AF_PACKET sockets from kernel
func SocketDiagPacket(proto uint8) ([]*PacketDiagMsg, error) {
req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, syscall.NLM_F_DUMP)
req.AddData(&PacketDiagReq{
Family: unix.AF_PACKET,
Protocol: proto,
// TODO: dump bpf filters | PACKET_SHOW_FILTER
Show: PACKET_SHOW_INFO | PACKET_SHOW_MCLIST,
})
msgs, err := req.Execute(syscall.NETLINK_INET_DIAG, 0)
if err != nil {
log.Debug("[netlink] socket.packetRequest: %s", err)
return nil, err
}
if len(msgs) == 0 {
log.Debug("[netlink] socket.packetRequest: 0 msgs")
return []*PacketDiagMsg{}, nil
}
pkts := make([]*PacketDiagMsg, len(msgs))
for n, m := range msgs {
log.Trace("[netlink] AF_PACKET, size: %d, %+v", len(m), m)
p := &PacketDiagMsg{}
if err = p.deserialize(m); err != nil {
log.Trace("[%d] netlink socket.packet error: %s", n, err)
continue
}
pkts[n] = p
}
return pkts, nil
}