diff --git a/daemon/netstat/entry.go b/daemon/netstat/entry.go index 8ef132d0..3e7b2b1c 100644 --- a/daemon/netstat/entry.go +++ b/daemon/netstat/entry.go @@ -14,6 +14,7 @@ type Entry struct { DstIP net.IP SrcPort uint DstPort uint + Iface int UserId int INode int } diff --git a/daemon/netstat/parse_packet.go b/daemon/netstat/parse_packet.go new file mode 100644 index 00000000..f6e601cc --- /dev/null +++ b/daemon/netstat/parse_packet.go @@ -0,0 +1,61 @@ +package netstat + +import ( + "bufio" + "os" + "regexp" + + "github.com/evilsocket/opensnitch/daemon/core" + "github.com/evilsocket/opensnitch/daemon/log" +) + +var ( + // sk RefCnt Type Proto Iface R Rmem User Inode + // ffff90b72f893800 3 3 0003 3 1 0 0 257944535 + packetParser = regexp.MustCompile(`(?i)` + + `[a-z0-9]+\s+` + // sk + `[0-9]\s+` + // refCnt + `([0-9])\s+` + // Type + `([0-9a-z]+)\s+` + // proto + `([0-9])\s+` + // iface + `[0-9]\s+` + // r + `[0-9]+\s+` + // rmem + `([0-9]+)\s+` + // user + `([0-9]+)`, // inode + ) +) + +// ParsePacket scans and retrieves the opened sockets from /proc/net/packet +func ParsePacket() ([]Entry, error) { + filename := core.ConcatStrings("/proc/net/packet") + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + + entries := make([]Entry, 0) + scanner := bufio.NewScanner(fd) + for lineno := 0; scanner.Scan(); lineno++ { + // skip column names + if lineno == 0 { + continue + } + + line := core.Trim(scanner.Text()) + m := packetParser.FindStringSubmatch(line) + if m == nil { + log.Warning("Could not parse netstat line from %s: %s", filename, line) + continue + } + // TODO: get proto, type, etc. + en := Entry{} + en.Iface = decToInt(m[3]) + en.UserId = decToInt(m[4]) + en.INode = decToInt(m[5]) + + entries = append(entries, en) + } + + return entries, nil +} diff --git a/daemon/tasks/socketsmonitor/dump.go b/daemon/tasks/socketsmonitor/dump.go index c47b0acc..632b7af6 100644 --- a/daemon/tasks/socketsmonitor/dump.go +++ b/daemon/tasks/socketsmonitor/dump.go @@ -5,10 +5,13 @@ import ( "fmt" "net" "sync" + "syscall" "github.com/evilsocket/opensnitch/daemon/log" daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink" + "github.com/evilsocket/opensnitch/daemon/netstat" "github.com/evilsocket/opensnitch/daemon/procmon" + "golang.org/x/sys/unix" ) const ( @@ -70,6 +73,35 @@ func (pm *SocketsMonitor) dumpSockets() *SocketsTable { wg.Wait() } + if exclude(pm.Config.Family, unix.AF_PACKET) { + return socketList + } + entries, err := netstat.ParsePacket() + if err != nil { + return socketList + } + var wg sync.WaitGroup + + pktList := make(map[int]struct{}, len(entries)) + for n, e := range entries { + if _, isDup := pktList[n]; isDup { + continue + } + pktList[n] = struct{}{} + + wg.Add(1) + s := daemonNetlink.Socket{} + s.Family = unix.AF_PACKET + s.INode = uint32(e.INode) + s.UID = uint32(e.UserId) + s.ID = daemonNetlink.SocketID{ + Interface: uint32(e.Iface), + } + // TODO: report the protocol and type + go addSocketToTable(pm.Ctx, &wg, syscall.IPPROTO_RAW, socketList, s) + } + wg.Wait() + return socketList } diff --git a/daemon/tasks/socketsmonitor/options.go b/daemon/tasks/socketsmonitor/options.go index d683ba3b..46e0e3a9 100644 --- a/daemon/tasks/socketsmonitor/options.go +++ b/daemon/tasks/socketsmonitor/options.go @@ -1,8 +1,9 @@ package socketsmonitor import ( - //"golang.org/x/sys/unix" "syscall" + + "golang.org/x/sys/unix" ) // Protos holds valid combinations of protocols, families and socket types that can be created. @@ -28,4 +29,11 @@ var options = []Protos{ {syscall.IPPROTO_UDP, syscall.AF_INET6}, {syscall.IPPROTO_UDPLITE, syscall.AF_INET}, {syscall.IPPROTO_UDPLITE, syscall.AF_INET6}, + + // for AF_PACKET, Type is the "Protocol" (SOCK_DGRAM, SOCK_RAW) + {syscall.IPPROTO_RAW, unix.AF_PACKET}, + // here UDP is SOCK_DGRAM. Does not imply UDP protocol. + {syscall.IPPROTO_UDP, unix.AF_PACKET}, + //{syscall.IPPROTO_IP, unix.AF_PACKET}, + //{unix.ETH_P_ALL, syscall.AF_PACKET}, } diff --git a/ui/opensnitch/dialogs/stats.py b/ui/opensnitch/dialogs/stats.py index 54821667..71b3d180 100644 --- a/ui/opensnitch/dialogs/stats.py +++ b/ui/opensnitch/dialogs/stats.py @@ -539,6 +539,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): self.comboNetstatFamily.addItem(QC.translate("stats", "ALL"), 0) self.comboNetstatFamily.addItem("AF_INET", 2) self.comboNetstatFamily.addItem("AF_INET6", 10) + self.comboNetstatFamily.addItem("AF_PACKET", 17) # 0x11 self.comboNetstatInterval.currentIndexChanged.connect(lambda index: self._cb_combo_netstat_changed(0, index)) self.comboNetstatNodes.activated.connect(lambda index: self._cb_combo_netstat_changed(1, index)) self.comboNetstatProto.currentIndexChanged.connect(lambda index: self._cb_combo_netstat_changed(2, index)) diff --git a/ui/opensnitch/utils/sockets.py b/ui/opensnitch/utils/sockets.py index 73d1fa89..fbfd1428 100644 --- a/ui/opensnitch/utils/sockets.py +++ b/ui/opensnitch/utils/sockets.py @@ -5,7 +5,7 @@ Family = { '0': 'AF_UNSPEC', '2': 'AF_INET', '10': 'AF_INET6', - '11': 'AF_PACKET', + '17': 'AF_PACKET', '40': 'AF_VSOCK', '44': 'AF_XDP', '45': 'AF_MCTP', @@ -27,6 +27,8 @@ Proto = { } State = { +# special case for protos that don't report state (AF_PACKET) + '0': 'LISTEN', '1': 'Established', '2': 'TCP_SYN_SENT', '3': 'TCP_SYN_RECV',