2024-10-07 23:40:40 +02:00
|
|
|
package socketsmonitor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"sync"
|
2025-02-03 21:05:25 +01:00
|
|
|
"syscall"
|
2024-10-07 23:40:40 +02:00
|
|
|
|
|
|
|
"github.com/evilsocket/opensnitch/daemon/log"
|
2025-02-05 00:00:47 +01:00
|
|
|
"github.com/evilsocket/opensnitch/daemon/netlink"
|
2025-02-03 21:05:25 +01:00
|
|
|
"github.com/evilsocket/opensnitch/daemon/netstat"
|
2024-10-07 23:40:40 +02:00
|
|
|
"github.com/evilsocket/opensnitch/daemon/procmon"
|
2025-02-03 21:05:25 +01:00
|
|
|
"golang.org/x/sys/unix"
|
2024-10-07 23:40:40 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// AnySocket constant indicates that we should return all sockets found.
|
|
|
|
// If the user selected a socket type, family or protocol, the value will be > 0
|
|
|
|
AnySocket = 0
|
|
|
|
)
|
|
|
|
|
|
|
|
// Socket represents every socket dumped from the kernel for the given filter.
|
2025-02-06 01:49:40 +01:00
|
|
|
// Internal to this package, and sent to the GUI as JSON.
|
2024-10-07 23:40:40 +02:00
|
|
|
type Socket struct {
|
2025-02-05 00:00:47 +01:00
|
|
|
Socket *netlink.Socket
|
2024-10-07 23:40:40 +02:00
|
|
|
Iface string
|
|
|
|
PID int
|
|
|
|
Mark uint32
|
2025-02-06 01:49:40 +01:00
|
|
|
Proto uint16
|
2024-10-07 23:40:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SocketsTable holds all the dumped sockets, after applying the filters, if any.
|
|
|
|
type SocketsTable struct {
|
|
|
|
sync.RWMutex `json:"-"`
|
|
|
|
Table []*Socket
|
|
|
|
Processes map[int]*procmon.Process
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pm *SocketsMonitor) dumpSockets() *SocketsTable {
|
|
|
|
socketList := &SocketsTable{}
|
|
|
|
socketList.Table = make([]*Socket, 0)
|
|
|
|
socketList.Processes = make(map[int]*procmon.Process, 0)
|
2025-02-05 00:00:47 +01:00
|
|
|
|
2024-10-07 23:40:40 +02:00
|
|
|
for n, opt := range options {
|
|
|
|
if exclude(pm.Config.Family, opt.Fam) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if exclude(pm.Config.Proto, opt.Proto) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2025-02-05 00:00:47 +01:00
|
|
|
sockList, err := netlink.SocketsDump(opt.Fam, opt.Proto)
|
2024-10-07 23:40:40 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Debug("[sockmon][%d] fam: %d, proto: %d, error: %s", n, opt.Fam, opt.Proto, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(sockList) == 0 {
|
|
|
|
log.Debug("[sockmon][%d] fam: %d, proto: %d, no sockets: %d", n, opt.Fam, opt.Proto, opt.Proto)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for _, sock := range sockList {
|
|
|
|
if sock == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if exclude(pm.Config.State, sock.State) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
// XXX: firing a goroutine per socket may be too much on some scenarios
|
2025-02-06 01:49:40 +01:00
|
|
|
go addSocketToTable(pm.Ctx, &wg, uint16(opt.Proto), socketList, *sock)
|
2024-10-07 23:40:40 +02:00
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2025-02-06 01:49:40 +01:00
|
|
|
dumpXDPSockets(pm.Ctx, pm.Config, socketList)
|
|
|
|
dumpPacketSockets(pm.Ctx, pm.Config, socketList)
|
|
|
|
|
|
|
|
return socketList
|
|
|
|
}
|
|
|
|
|
|
|
|
func dumpXDPSockets(ctx context.Context, conf *monConfig, socketList *SocketsTable) {
|
|
|
|
if exclude(conf.Family, unix.AF_XDP) && exclude(conf.Proto, syscall.IPPROTO_RAW) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
xdpList, err := netlink.SocketGetXDP()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for _, xdp := range xdpList {
|
|
|
|
s := netlink.Socket{}
|
|
|
|
s.Family = unix.AF_XDP
|
|
|
|
s.INode = uint32(xdp.XDPDiagMsg.Ino)
|
|
|
|
s.UID = uint32(xdp.XDPInfo.UID)
|
|
|
|
s.ID = netlink.SocketID{
|
|
|
|
Interface: xdp.XDPInfo.Ifindex,
|
|
|
|
Cookie: xdp.XDPDiagMsg.Cookie,
|
|
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
go addSocketToTable(ctx, &wg, syscall.IPPROTO_RAW, socketList, s)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
func dumpPacketSockets(ctx context.Context, conf *monConfig, socketList *SocketsTable) {
|
|
|
|
if exclude(conf.Family, unix.AF_PACKET) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
pktList, err := netlink.SocketDiagPacket(0)
|
|
|
|
for _, pkt := range pktList {
|
|
|
|
/*if excludePacket(pm.Config.Proto, pkt.Num) {
|
|
|
|
continue
|
|
|
|
}*/
|
|
|
|
|
|
|
|
s := netlink.Socket{}
|
|
|
|
s.Family = unix.AF_PACKET
|
|
|
|
s.INode = uint32(pkt.Inode)
|
|
|
|
s.UID = uint32(pkt.UID)
|
|
|
|
s.ID = netlink.SocketID{
|
|
|
|
Interface: uint32(pkt.Mclist.Index),
|
|
|
|
Cookie: pkt.Cookie,
|
2025-02-05 00:00:47 +01:00
|
|
|
}
|
2025-02-06 01:49:40 +01:00
|
|
|
wg.Add(1)
|
|
|
|
go addSocketToTable(ctx, &wg, pkt.Num /* proto */, socketList, s)
|
2025-02-05 00:00:47 +01:00
|
|
|
}
|
2025-02-06 01:49:40 +01:00
|
|
|
wg.Wait()
|
2025-02-05 00:00:47 +01:00
|
|
|
|
2025-02-06 01:49:40 +01:00
|
|
|
if err == nil {
|
|
|
|
return
|
2025-02-03 21:05:25 +01:00
|
|
|
}
|
2025-02-06 01:49:40 +01:00
|
|
|
|
|
|
|
// dumping AF_PACKET from kernel failed. Try it with /proc
|
2025-02-03 21:05:25 +01:00
|
|
|
entries, err := netstat.ParsePacket()
|
|
|
|
if err != nil {
|
2025-02-06 01:49:40 +01:00
|
|
|
return
|
2025-02-03 21:05:25 +01:00
|
|
|
}
|
|
|
|
|
2025-02-06 01:49:40 +01:00
|
|
|
pktEntr := make(map[int]struct{}, len(entries))
|
2025-02-03 21:05:25 +01:00
|
|
|
for n, e := range entries {
|
2025-02-06 01:49:40 +01:00
|
|
|
if _, isDup := pktEntr[n]; isDup {
|
2025-02-03 21:05:25 +01:00
|
|
|
continue
|
|
|
|
}
|
2025-02-06 01:49:40 +01:00
|
|
|
pktEntr[n] = struct{}{}
|
2025-02-03 21:05:25 +01:00
|
|
|
|
2025-02-06 01:49:40 +01:00
|
|
|
/*if excludePacket(conf.Proto, opt.Proto) {
|
|
|
|
continue
|
|
|
|
}*/
|
2025-02-05 00:00:47 +01:00
|
|
|
s := netlink.Socket{}
|
2025-02-03 21:05:25 +01:00
|
|
|
s.Family = unix.AF_PACKET
|
|
|
|
s.INode = uint32(e.INode)
|
|
|
|
s.UID = uint32(e.UserId)
|
2025-02-05 00:00:47 +01:00
|
|
|
s.ID = netlink.SocketID{
|
2025-02-03 21:05:25 +01:00
|
|
|
Interface: uint32(e.Iface),
|
|
|
|
}
|
2025-02-06 01:49:40 +01:00
|
|
|
// TODO: report sock type
|
|
|
|
wg.Add(1)
|
|
|
|
go addSocketToTable(ctx, &wg, syscall.IPPROTO_RAW, socketList, s)
|
2025-02-03 21:05:25 +01:00
|
|
|
}
|
|
|
|
wg.Wait()
|
2024-10-07 23:40:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func exclude(expected, what uint8) bool {
|
|
|
|
return expected > AnySocket && expected != what
|
|
|
|
}
|
|
|
|
|
2025-02-06 01:49:40 +01:00
|
|
|
func addSocketToTable(ctx context.Context, wg *sync.WaitGroup, proto uint16, st *SocketsTable, s netlink.Socket) {
|
2024-10-07 23:40:40 +02:00
|
|
|
inode := int(s.INode)
|
|
|
|
pid := procmon.GetPIDFromINode(inode, fmt.Sprint(inode,
|
|
|
|
s.ID.Source, s.ID.SourcePort, s.ID.Destination, s.ID.DestinationPort),
|
|
|
|
)
|
|
|
|
// pid can be -1 in some scenarios (tor socket in FIN_WAIT1 state).
|
|
|
|
// we could lookup the connection in the ebpfCache of connections.
|
|
|
|
st.Lock()
|
|
|
|
var p *procmon.Process
|
|
|
|
if pid == -1 {
|
|
|
|
p = &procmon.Process{}
|
|
|
|
} else {
|
|
|
|
if pp, found := st.Processes[pid]; !found {
|
|
|
|
p = procmon.FindProcess(pid, false)
|
|
|
|
} else {
|
|
|
|
p = pp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// XXX: should we assume that if the PID is in cache, it has already been sent to the GUI (server)?
|
|
|
|
ss := &Socket{}
|
|
|
|
ss.Socket = &s
|
|
|
|
ss.PID = pid
|
|
|
|
ss.Proto = proto
|
|
|
|
if iface, err := net.InterfaceByIndex(int(s.ID.Interface)); err == nil {
|
|
|
|
ss.Iface = iface.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
st.Table = append(st.Table, ss)
|
|
|
|
st.Processes[pid] = p
|
|
|
|
st.Unlock()
|
|
|
|
|
|
|
|
wg.Done()
|
|
|
|
}
|