mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34:40 +01:00
tasks: added sockets monitor task (netstat)
Added new task to monitor local sockets of nodes, similar to ss or netstat. More info: #1112
This commit is contained in:
parent
2fc1977d32
commit
83fad69316
21 changed files with 1235 additions and 76 deletions
|
@ -140,8 +140,8 @@ func (c *CacheProcs) sort(pid int) {
|
|||
if item != nil && item.Pid == pid {
|
||||
return
|
||||
}
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
sort.Slice(c.items, func(i, j int) bool {
|
||||
t := c.items[i].LastSeen
|
||||
|
|
|
@ -48,7 +48,7 @@ type Task interface {
|
|||
// and Data is the configuration of each task (a map[string]string, converted by the json package).
|
||||
type TaskNotification struct {
|
||||
// Data of the task.
|
||||
Data map[string]string
|
||||
Data interface{}
|
||||
|
||||
// Name of the task.
|
||||
Name string
|
||||
|
|
112
daemon/tasks/socketsmonitor/dump.go
Normal file
112
daemon/tasks/socketsmonitor/dump.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package socketsmonitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/evilsocket/opensnitch/daemon/log"
|
||||
daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink"
|
||||
"github.com/evilsocket/opensnitch/daemon/procmon"
|
||||
)
|
||||
|
||||
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.
|
||||
type Socket struct {
|
||||
Socket *daemonNetlink.Socket
|
||||
Iface string
|
||||
PID int
|
||||
Mark uint32
|
||||
Proto uint8
|
||||
}
|
||||
|
||||
// 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)
|
||||
for n, opt := range options {
|
||||
if exclude(pm.Config.Family, opt.Fam) {
|
||||
continue
|
||||
}
|
||||
if exclude(pm.Config.Proto, opt.Proto) {
|
||||
continue
|
||||
}
|
||||
|
||||
sockList, err := daemonNetlink.SocketsDump(opt.Fam, opt.Proto)
|
||||
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
|
||||
go addSocketToTable(pm.Ctx, &wg, opt.Proto, socketList, *sock)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
return socketList
|
||||
}
|
||||
|
||||
func exclude(expected, what uint8) bool {
|
||||
return expected > AnySocket && expected != what
|
||||
}
|
||||
|
||||
func addSocketToTable(ctx context.Context, wg *sync.WaitGroup, proto uint8, st *SocketsTable, s daemonNetlink.Socket) {
|
||||
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()
|
||||
}
|
166
daemon/tasks/socketsmonitor/main.go
Normal file
166
daemon/tasks/socketsmonitor/main.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package socketsmonitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/evilsocket/opensnitch/daemon/log"
|
||||
"github.com/evilsocket/opensnitch/daemon/tasks"
|
||||
)
|
||||
|
||||
// Name of this task
|
||||
var Name = "sockets-monitor"
|
||||
|
||||
// Config of this task
|
||||
// {"interval": "5s", "states": "0,1,2,3", "family": 2, "proto": 17}
|
||||
type monConfig struct {
|
||||
Interval string
|
||||
State uint8
|
||||
Proto uint8
|
||||
Family uint8
|
||||
}
|
||||
|
||||
// SocketsMonitor monitors a process ID.
|
||||
type SocketsMonitor struct {
|
||||
tasks.TaskBase
|
||||
mu *sync.RWMutex
|
||||
Ticker *time.Ticker
|
||||
|
||||
Config *monConfig
|
||||
states uint8
|
||||
|
||||
// stop the task if the daemon is disconnected from the GUI (server)
|
||||
StopOnDisconnect bool
|
||||
|
||||
// flag to indicate that the task has been stopped, so any running task should
|
||||
// exit on finish, to avoid sending data to closed channels.
|
||||
isStopped bool
|
||||
}
|
||||
|
||||
// initConfig parses the received configuration, and initializes it if
|
||||
// it's not complete.
|
||||
func initConfig(config interface{}) (*monConfig, error) {
|
||||
// https://pkg.go.dev/encoding/json#Unmarshal
|
||||
// JSON objects (are converted) to map[string]interface{}
|
||||
cfg, ok := config.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("[sockmon] invalid config received: %v", config)
|
||||
}
|
||||
var newCfg monConfig
|
||||
newCfg.Interval = cfg["interval"].(string)
|
||||
newCfg.State = uint8(cfg["state"].(float64))
|
||||
newCfg.Proto = uint8(cfg["proto"].(float64))
|
||||
newCfg.Family = uint8(cfg["family"].(float64))
|
||||
|
||||
if newCfg.Interval == "" {
|
||||
newCfg.Interval = "5s"
|
||||
}
|
||||
|
||||
return &newCfg, nil
|
||||
}
|
||||
|
||||
// New returns a new SocketsMonitor
|
||||
func New(config interface{}, stopOnDisconnect bool) (*SocketsMonitor, error) {
|
||||
cfg, err := initConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SocketsMonitor{
|
||||
TaskBase: tasks.TaskBase{
|
||||
Results: make(chan interface{}),
|
||||
Errors: make(chan error),
|
||||
StopChan: make(chan struct{}),
|
||||
},
|
||||
mu: &sync.RWMutex{},
|
||||
StopOnDisconnect: stopOnDisconnect,
|
||||
Config: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start ...
|
||||
func (pm *SocketsMonitor) Start(ctx context.Context, cancel context.CancelFunc) error {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
|
||||
pm.Ctx = ctx
|
||||
pm.Cancel = cancel
|
||||
|
||||
interval, err := time.ParseDuration(pm.Config.Interval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pm.Ticker = time.NewTicker(interval)
|
||||
go func(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-pm.TaskBase.StopChan:
|
||||
goto Exit
|
||||
case <-ctx.Done():
|
||||
goto Exit
|
||||
case <-pm.Ticker.C:
|
||||
// FIXME: ensure that dumpSockets() are not overlapped
|
||||
socketList := pm.dumpSockets()
|
||||
sockJSON, err := json.Marshal(socketList)
|
||||
if err != nil {
|
||||
if !pm.isStopped {
|
||||
pm.TaskBase.Errors <- err
|
||||
}
|
||||
goto Exit
|
||||
}
|
||||
if pm.isStopped {
|
||||
goto Exit
|
||||
}
|
||||
|
||||
pm.TaskBase.Results <- unsafe.String(unsafe.SliceData(sockJSON), len(sockJSON))
|
||||
}
|
||||
}
|
||||
Exit:
|
||||
log.Debug("[tasks.SocketsMonitor] stopped")
|
||||
}(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// Pause stops temporarily the task. For example it might be paused when the
|
||||
// connection with the GUI (server) is closed.
|
||||
func (pm *SocketsMonitor) Pause() error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resume stopped tasks.
|
||||
func (pm *SocketsMonitor) Resume() error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop ...
|
||||
func (pm *SocketsMonitor) Stop() error {
|
||||
pm.mu.RLock()
|
||||
defer pm.mu.RUnlock()
|
||||
|
||||
if !pm.StopOnDisconnect {
|
||||
return nil
|
||||
}
|
||||
log.Debug("[task.SocketsMonitor] Stop()")
|
||||
pm.isStopped = true
|
||||
pm.Ticker.Stop()
|
||||
pm.Cancel()
|
||||
close(pm.TaskBase.Results)
|
||||
close(pm.TaskBase.Errors)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Results ...
|
||||
func (pm *SocketsMonitor) Results() <-chan interface{} {
|
||||
return pm.TaskBase.Results
|
||||
}
|
||||
|
||||
// Errors ...
|
||||
func (pm *SocketsMonitor) Errors() <-chan error {
|
||||
return pm.TaskBase.Errors
|
||||
}
|
31
daemon/tasks/socketsmonitor/options.go
Normal file
31
daemon/tasks/socketsmonitor/options.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package socketsmonitor
|
||||
|
||||
import (
|
||||
//"golang.org/x/sys/unix"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Protos holds valid combinations of protocols, families and socket types that can be created.
|
||||
type Protos struct {
|
||||
Proto uint8
|
||||
Fam uint8
|
||||
}
|
||||
|
||||
var options = []Protos{
|
||||
{syscall.IPPROTO_DCCP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_DCCP, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_ICMPV6, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_ICMP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_IGMP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_IGMP, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_RAW, syscall.AF_INET},
|
||||
{syscall.IPPROTO_RAW, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_SCTP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_SCTP, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_TCP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_TCP, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_UDP, syscall.AF_INET},
|
||||
{syscall.IPPROTO_UDP, syscall.AF_INET6},
|
||||
{syscall.IPPROTO_UDPLITE, syscall.AF_INET},
|
||||
{syscall.IPPROTO_UDPLITE, syscall.AF_INET6},
|
||||
}
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/evilsocket/opensnitch/daemon/tasks"
|
||||
"github.com/evilsocket/opensnitch/daemon/tasks/nodemonitor"
|
||||
"github.com/evilsocket/opensnitch/daemon/tasks/pidmonitor"
|
||||
"github.com/evilsocket/opensnitch/daemon/tasks/socketsmonitor"
|
||||
"github.com/evilsocket/opensnitch/daemon/ui/config"
|
||||
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -59,6 +60,37 @@ func (c *Client) getClientConfig() *protocol.ClientConfig {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Client) monitorSockets(config interface{}, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
|
||||
sockMonTask, err := socketsmonitor.New(config, true)
|
||||
if err != nil {
|
||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
return
|
||||
}
|
||||
ctxSock, err := TaskMgr.AddTask(socketsmonitor.Name, sockMonTask)
|
||||
if err != nil {
|
||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
return
|
||||
}
|
||||
go func(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
goto Exit
|
||||
case err := <-sockMonTask.Errors():
|
||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
case temp := <-sockMonTask.Results():
|
||||
data, ok := temp.(string)
|
||||
if !ok {
|
||||
goto Exit
|
||||
}
|
||||
c.sendNotificationReply(stream, notification.Id, data, nil)
|
||||
}
|
||||
}
|
||||
Exit:
|
||||
// task should have already been removed via TASK_STOP
|
||||
}(ctxSock)
|
||||
}
|
||||
|
||||
func (c *Client) monitorNode(node, interval string, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
|
||||
taskName, nodeMonTask := nodemonitor.New(node, interval, true)
|
||||
ctxNode, err := TaskMgr.AddTask(taskName, nodeMonTask)
|
||||
|
@ -206,15 +238,28 @@ func (c *Client) handleActionTaskStart(stream protocol.UI_NotificationsClient, n
|
|||
}
|
||||
switch taskConf.Name {
|
||||
case pidmonitor.Name:
|
||||
pid, err := strconv.Atoi(taskConf.Data["pid"])
|
||||
conf, ok := taskConf.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Error("[pidmon] TaskStart.Data, PID err (string expected): %v", taskConf)
|
||||
return
|
||||
}
|
||||
pid, err := strconv.Atoi(conf["pid"].(string))
|
||||
if err != nil {
|
||||
log.Error("TaskStart.Data, PID err: %s, %v", err, taskConf)
|
||||
log.Error("[pidmon] TaskStart.Data, PID err: %s, %v", err, taskConf)
|
||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
return
|
||||
}
|
||||
c.monitorProcessDetails(pid, taskConf.Data["interval"], stream, notification)
|
||||
interval, _ := conf["interval"].(string)
|
||||
c.monitorProcessDetails(pid, interval, stream, notification)
|
||||
case nodemonitor.Name:
|
||||
c.monitorNode(taskConf.Data["node"], taskConf.Data["interval"], stream, notification)
|
||||
conf, ok := taskConf.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Error("[nodemon] TaskStart.Data, \"node\" err (string expected): %v", taskConf)
|
||||
return
|
||||
}
|
||||
c.monitorNode(conf["node"].(string), conf["interval"].(string), stream, notification)
|
||||
case socketsmonitor.Name:
|
||||
c.monitorSockets(taskConf.Data, stream, notification)
|
||||
default:
|
||||
log.Debug("TaskStart, unknown task: %v", taskConf)
|
||||
//c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
|
@ -231,7 +276,12 @@ func (c *Client) handleActionTaskStop(stream protocol.UI_NotificationsClient, no
|
|||
}
|
||||
switch taskConf.Name {
|
||||
case pidmonitor.Name:
|
||||
pid, err := strconv.Atoi(taskConf.Data["pid"])
|
||||
conf, ok := taskConf.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Error("[pidmon] TaskStop.Data, PID err (string expected): %v", taskConf)
|
||||
return
|
||||
}
|
||||
pid, err := strconv.Atoi(conf["pid"].(string))
|
||||
if err != nil {
|
||||
log.Error("TaskStop.Data, err: %s, %s, %v+, %q", err, notification.Data, taskConf.Data, taskConf.Data)
|
||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
|
@ -239,7 +289,14 @@ func (c *Client) handleActionTaskStop(stream protocol.UI_NotificationsClient, no
|
|||
}
|
||||
TaskMgr.RemoveTask(fmt.Sprint(taskConf.Name, "-", pid))
|
||||
case nodemonitor.Name:
|
||||
TaskMgr.RemoveTask(fmt.Sprint(nodemonitor.Name, "-", taskConf.Data["node"]))
|
||||
conf, ok := taskConf.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
log.Error("[pidmon] TaskStop.Data, PID err (string expected): %v", taskConf)
|
||||
return
|
||||
}
|
||||
TaskMgr.RemoveTask(fmt.Sprint(nodemonitor.Name, "-", conf["node"].(string)))
|
||||
case socketsmonitor.Name:
|
||||
TaskMgr.RemoveTask(socketsmonitor.Name)
|
||||
default:
|
||||
log.Debug("TaskStop, unknown task: %v", taskConf)
|
||||
//c.sendNotificationReply(stream, notification.Id, "", err)
|
||||
|
|
|
@ -9,7 +9,8 @@ from opensnitch.utils.xdg import xdg_config_home
|
|||
from opensnitch.actions.default_configs import (
|
||||
commonDelegateConfig,
|
||||
rulesDelegateConfig,
|
||||
fwDelegateConfig
|
||||
fwDelegateConfig,
|
||||
netstatDelegateConfig
|
||||
)
|
||||
|
||||
from opensnitch.plugins import PluginsList
|
||||
|
@ -129,6 +130,7 @@ class Actions(QObject):
|
|||
self._actions_list[commonDelegateConfig[Actions.KEY_NAME]] = self.compile(commonDelegateConfig)
|
||||
self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig)
|
||||
self._actions_list[fwDelegateConfig[Actions.KEY_NAME]] = self.compile(fwDelegateConfig)
|
||||
self._actions_list[netstatDelegateConfig[Actions.KEY_NAME]] = self.compile(netstatDelegateConfig)
|
||||
|
||||
def load(self, action_file):
|
||||
"""read a json file from disk and create the action."""
|
||||
|
|
|
@ -131,3 +131,51 @@ rulesDelegateConfig = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
netstatDelegateConfig = {
|
||||
"name": "netstatDelegateConfig",
|
||||
"created": "",
|
||||
"updated": "",
|
||||
"actions": {
|
||||
"highlight": {
|
||||
"enabled": True,
|
||||
"cells": [
|
||||
{
|
||||
"text": ["LISTEN"],
|
||||
"cols": [1],
|
||||
"color": "green",
|
||||
"bgcolor": "",
|
||||
"alignment": ["center"]
|
||||
},
|
||||
{
|
||||
"text": ["CLOSE"],
|
||||
"cols": [1],
|
||||
"color": "red",
|
||||
"bgcolor": "",
|
||||
"alignment": ["center"]
|
||||
},
|
||||
{
|
||||
"text": ["Established"],
|
||||
"cols": [1],
|
||||
"color": "blue",
|
||||
"bgcolor": "",
|
||||
"alignment": ["center"]
|
||||
},
|
||||
{
|
||||
"text": [
|
||||
"TCP_SYN_SENT", "TCP_SYN_RECV",
|
||||
"TCP_FIN_WAIT1", "TCP_FIN_WAIT2",
|
||||
"TCP_TIME_WAIT", "TCP_CLOSE_WAIT",
|
||||
"TCP_LAST_ACK", "TCP_CLOSING",
|
||||
"TCP_NEW_SYNC_RECV"
|
||||
],
|
||||
"cols": [1],
|
||||
"color": "",
|
||||
"bgcolor": "",
|
||||
"alignment": ["center"]
|
||||
}
|
||||
],
|
||||
"rows": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,12 +137,16 @@ class Config:
|
|||
STATS_RULES_COL_STATE = "statsDialog/rules_columns_state"
|
||||
STATS_FW_COL_STATE = "statsDialog/firewall_columns_state"
|
||||
STATS_ALERTS_COL_STATE = "statsDialog/alerts_columns_state"
|
||||
STATS_NETSTAT_COL_STATE = "statsDialog/netstat_columns_state"
|
||||
STATS_RULES_TREE_EXPANDED_0 = "statsDialog/rules_tree_0_expanded"
|
||||
STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded"
|
||||
STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos"
|
||||
STATS_NODES_SPLITTER_POS = "statsDialog/nodes_splitter_pos"
|
||||
STATS_VIEW_COL_STATE = "statsDialog/view_columns_state"
|
||||
STATS_VIEW_DETAILS_COL_STATE = "statsDialog/view_details_columns_state"
|
||||
STATS_NETSTAT_FILTER_PROTO = "statsDialog/netstat_proto_filter"
|
||||
STATS_NETSTAT_FILTER_FAMILY = "statsDialog/netstat_family_filter"
|
||||
STATS_NETSTAT_FILTER_STATE = "statsDialog/netstat_state_filter"
|
||||
|
||||
QT_AUTO_SCREEN_SCALE_FACTOR = "global/screen_scale_factor_auto"
|
||||
QT_SCREEN_SCALE_FACTOR = "global/screen_scale_factor"
|
||||
|
|
31
ui/opensnitch/customwidgets/netstattablemodel.py
Normal file
31
ui/opensnitch/customwidgets/netstattablemodel.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QStandardItemModel
|
||||
from opensnitch.customwidgets.generictableview import GenericTableModel
|
||||
from opensnitch.utils import sockets
|
||||
|
||||
class NetstatTableModel(GenericTableModel):
|
||||
|
||||
def __init__(self, tableName, headerLabels):
|
||||
super().__init__(tableName, headerLabels)
|
||||
|
||||
self.COL_STATE = 1
|
||||
self.COL_PROTO = 6
|
||||
self.COL_FAMILY = 8
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
"""Paint rows with the data stored in self.items"""
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
items_count = len(self.items)
|
||||
if index.isValid() and items_count > 0 and index.row() < items_count:
|
||||
try:
|
||||
# FIXME: protocol UDP + state CLOSE == state LISTEN
|
||||
if index.column() == self.COL_STATE:
|
||||
return sockets.State[self.items[index.row()][index.column()]]
|
||||
elif index.column() == self.COL_PROTO:
|
||||
return sockets.Proto[self.items[index.row()][index.column()]]
|
||||
elif index.column() == self.COL_FAMILY:
|
||||
return sockets.Family[self.items[index.row()][index.column()]]
|
||||
return self.items[index.row()][index.column()]
|
||||
except Exception as e:
|
||||
print("[socketsmodel] exception:", e, index.row(), index.column())
|
||||
return QStandardItemModel.data(self, index, role)
|
|
@ -199,7 +199,48 @@ class Database:
|
|||
"status int " \
|
||||
")", self.db)
|
||||
q.exec_()
|
||||
|
||||
q = QSqlQuery("create table if not exists sockets (" \
|
||||
"id int primary key, " \
|
||||
"last_seen text, " \
|
||||
"node text, " \
|
||||
"src_port text, " \
|
||||
"src_ip text, " \
|
||||
"dst_ip text, " \
|
||||
"dst_port text, " \
|
||||
"proto text, " \
|
||||
"uid text, " \
|
||||
"inode text, " \
|
||||
"iface text, " \
|
||||
"family text, " \
|
||||
"state text, " \
|
||||
"cookies text, " \
|
||||
"rqueue text, " \
|
||||
"wqueue text, " \
|
||||
"expires text, " \
|
||||
"retrans text, " \
|
||||
"timer text, " \
|
||||
"mark text, " \
|
||||
"proc_pid text, " \
|
||||
"proc_comm text, " \
|
||||
"proc_path text, " \
|
||||
"UNIQUE(node, src_port, src_ip, dst_ip, dst_port, proto, family, inode)" \
|
||||
")", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_srcport_index on sockets (src_port)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_dstip_index on sockets (dst_ip)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_srcip_index on sockets (src_ip)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_dsthost_index on sockets (dst_host)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_state_index on sockets (state)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_comm_index on sockets (proc_comm)", self.db)
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index sck_path_index on sockets (proc_path)", self.db)
|
||||
q.exec_()
|
||||
q.exec_()
|
||||
q = QSqlQuery("create index rules_index on rules (time)", self.db)
|
||||
q.exec_()
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
POLICY_ACCEPT = 0
|
||||
POLICY_DROP = 1
|
||||
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
|
||||
def __init__(self, parent=None, appicon=None, node=None):
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
|
@ -87,8 +87,8 @@ class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.cmdNewRule.setIcon(newIcon)
|
||||
self.cmdHelp.setIcon(helpIcon)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, addr, reply):
|
||||
self.comboInput.setEnabled(True)
|
||||
if reply.id in self._notifications_sent:
|
||||
if reply.code == ui_pb2.OK:
|
||||
|
|
|
@ -53,7 +53,7 @@ class FwRuleDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
STATM_COUNTER = 15
|
||||
STATM_LIMIT = 16
|
||||
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
|
||||
def __init__(self, parent=None, appicon=None):
|
||||
QtWidgets.QDialog.__init__(self, parent)
|
||||
|
@ -389,8 +389,8 @@ The value must be in the format: VALUE/UNITS/TIME, for example:
|
|||
self.comboDirection.currentIndexChanged.disconnect(self._cb_direction_changed)
|
||||
self.hide()
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, addr, reply):
|
||||
self._enable_buttons()
|
||||
|
||||
try:
|
||||
|
|
|
@ -21,7 +21,7 @@ DIALOG_UI_PATH = "%s/../res/preferences.ui" % os.path.dirname(sys.modules[__name
|
|||
class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||
|
||||
LOG_TAG = "[Preferences] "
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
saved = QtCore.pyqtSignal()
|
||||
|
||||
TAB_POPUPS = 0
|
||||
|
@ -962,8 +962,8 @@ class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
}
|
||||
self._themes.change_theme(self, self.comboUITheme.currentText(), extra_opts)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, addr, reply):
|
||||
#print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code)
|
||||
if reply.id in self._notifications_sent:
|
||||
if reply.code == ui_pb2.OK:
|
||||
|
|
|
@ -18,7 +18,7 @@ class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0])
|
|||
|
||||
LOG_TAG = "[ProcessDetails]: "
|
||||
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
|
||||
TAB_STATUS = 0
|
||||
TAB_DESCRIPTORS = 1
|
||||
|
@ -111,8 +111,8 @@ class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0])
|
|||
except Exception as e:
|
||||
print("procdialog._configure_plugins() exception:", name, " you may want to enable this plugin -", e)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, addr, reply):
|
||||
if reply.id not in self._notifications_sent:
|
||||
print("[stats] unknown notification received: ", reply.id)
|
||||
else:
|
||||
|
|
|
@ -48,7 +48,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
PW_USER = 0
|
||||
PW_UID = 2
|
||||
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
|
||||
def __init__(self, parent=None, _rule=None, appicon=None):
|
||||
super(RulesEditorDialog, self).__init__(parent)
|
||||
|
@ -302,8 +302,8 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
|
||||
self._rules.updated.emit(0)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, addr, reply):
|
||||
#print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code)
|
||||
if reply.id in self._notifications_sent:
|
||||
if reply.code == ui_pb2.OK:
|
||||
|
|
|
@ -24,6 +24,7 @@ from opensnitch.customwidgets.colorizeddelegate import ColorizedDelegate
|
|||
from opensnitch.customwidgets.firewalltableview import FirewallTableModel
|
||||
from opensnitch.customwidgets.generictableview import GenericTableModel
|
||||
from opensnitch.customwidgets.addresstablemodel import AddressTableModel
|
||||
from opensnitch.customwidgets.netstattablemodel import NetstatTableModel
|
||||
from opensnitch.utils import Message, QuickHelp, AsnDB, Icons
|
||||
from opensnitch.utils.infowindow import InfoWindow
|
||||
from opensnitch.utils.xdg import xdg_current_desktop
|
||||
|
@ -40,7 +41,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
_status_changed_trigger = QtCore.pyqtSignal(bool)
|
||||
_shown_trigger = QtCore.pyqtSignal()
|
||||
_notification_trigger = QtCore.pyqtSignal(ui_pb2.Notification)
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
|
||||
SORT_ORDER = ["ASC", "DESC"]
|
||||
LIMITS = ["LIMIT 50", "LIMIT 100", "LIMIT 200", "LIMIT 300", ""]
|
||||
|
@ -97,8 +98,10 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
TAB_ADDRS = 5
|
||||
TAB_PORTS = 6
|
||||
TAB_USERS = 7
|
||||
TAB_FIREWALL = 8 # in rules tab
|
||||
TAB_ALERTS = 9 # in rules tab
|
||||
TAB_NETSTAT = 8
|
||||
# these "specials" tables must be placed after the "real" tabs
|
||||
TAB_FIREWALL = 9 # in rules tab
|
||||
TAB_ALERTS = 10 # in rules tab
|
||||
|
||||
# tree's top level items
|
||||
RULES_TREE_APPS = 0
|
||||
|
@ -138,6 +141,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
TAB_ADDRS: False,
|
||||
TAB_PORTS: False,
|
||||
TAB_USERS: False,
|
||||
TAB_NETSTAT: False,
|
||||
TAB_FIREWALL: False,
|
||||
TAB_ALERTS: False
|
||||
}
|
||||
|
@ -214,40 +218,6 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
"last_order_to": 0,
|
||||
"tracking_column:": COL_R_NAME
|
||||
},
|
||||
TAB_FIREWALL: {
|
||||
"name": "firewall",
|
||||
"label": None,
|
||||
"cmd": None,
|
||||
"cmdCleanStats": None,
|
||||
"view": None,
|
||||
"filterLine": None,
|
||||
"model": None,
|
||||
"delegate": "defaultFWDelegateConfig",
|
||||
"display_fields": "*",
|
||||
"header_labels": [],
|
||||
"last_order_by": "2",
|
||||
"last_order_to": 0,
|
||||
"tracking_column:": COL_TIME
|
||||
},
|
||||
TAB_ALERTS: {
|
||||
"name": "alerts",
|
||||
"label": None,
|
||||
"cmd": None,
|
||||
"cmdCleanStats": None,
|
||||
"view": None,
|
||||
"filterLine": None,
|
||||
"model": None,
|
||||
"delegate": "defaultRulesDelegateConfig",
|
||||
"display_fields": "time as Time, " \
|
||||
"node as Node, " \
|
||||
"type as Type, " \
|
||||
"substr(what, 0, 128) as What, " \
|
||||
"substr(body, 0, 128) as Description ",
|
||||
"header_labels": [],
|
||||
"last_order_by": "1",
|
||||
"last_order_to": 0,
|
||||
"tracking_column:": COL_TIME
|
||||
},
|
||||
TAB_HOSTS: {
|
||||
"name": "hosts",
|
||||
"label": None,
|
||||
|
@ -322,6 +292,65 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
"last_order_by": "2",
|
||||
"last_order_to": 1,
|
||||
"tracking_column:": COL_TIME
|
||||
},
|
||||
TAB_NETSTAT: {
|
||||
"name": "sockets",
|
||||
"label": None,
|
||||
"cmd": None,
|
||||
"cmdCleanStats": None,
|
||||
"view": None,
|
||||
"filterLine": None,
|
||||
"model": None,
|
||||
"delegate": "netstatDelegateConfig",
|
||||
"display_fields": "proc_comm as Comm," \
|
||||
"state as State, " \
|
||||
"src_port as SrcPort, " \
|
||||
"src_ip as SrcIP, " \
|
||||
"dst_ip as DstIP, " \
|
||||
"dst_port as DstPort, " \
|
||||
"proto as Protocol, " \
|
||||
"uid as UID, " \
|
||||
"family as Family, " \
|
||||
"iface as IFace, " \
|
||||
"'pid:' || proc_pid || ', inode: ' || inode || ', cookies: '|| cookies || ', rqueue: ' || rqueue || ', wqueue: ' || wqueue || ', expires: ' || expires || ', retrans: ' || retrans || ', timer: ' || timer as Metadata ",
|
||||
"header_labels": [],
|
||||
"last_order_by": "2",
|
||||
"last_order_to": 1,
|
||||
"tracking_column:": COL_TIME
|
||||
},
|
||||
TAB_FIREWALL: {
|
||||
"name": "firewall",
|
||||
"label": None,
|
||||
"cmd": None,
|
||||
"cmdCleanStats": None,
|
||||
"view": None,
|
||||
"filterLine": None,
|
||||
"model": None,
|
||||
"delegate": "defaultFWDelegateConfig",
|
||||
"display_fields": "*",
|
||||
"header_labels": [],
|
||||
"last_order_by": "2",
|
||||
"last_order_to": 0,
|
||||
"tracking_column:": COL_TIME
|
||||
},
|
||||
TAB_ALERTS: {
|
||||
"name": "alerts",
|
||||
"label": None,
|
||||
"cmd": None,
|
||||
"cmdCleanStats": None,
|
||||
"view": None,
|
||||
"filterLine": None,
|
||||
"model": None,
|
||||
"delegate": "defaultRulesDelegateConfig",
|
||||
"display_fields": "time as Time, " \
|
||||
"node as Node, " \
|
||||
"type as Type, " \
|
||||
"substr(what, 0, 128) as What, " \
|
||||
"substr(body, 0, 128) as Description ",
|
||||
"header_labels": [],
|
||||
"last_order_by": "1",
|
||||
"last_order_to": 0,
|
||||
"tracking_column:": COL_TIME
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,6 +410,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self._fw = Firewall().instance()
|
||||
self._rules = Rules.instance()
|
||||
self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated)
|
||||
self._nodes.nodesUpdated.connect(self._cb_nodes_updated)
|
||||
self._rules.updated.connect(self._cb_app_rules_updated)
|
||||
self._actions = Actions().instance()
|
||||
self._action_list = self._actions.getByType(PluginBase.TYPE_MAIN_DIALOG)
|
||||
|
@ -452,6 +482,44 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.nextButton.clicked.connect(self._cb_next_button_clicked)
|
||||
self.prevButton.clicked.connect(self._cb_prev_button_clicked)
|
||||
|
||||
|
||||
# TODO: move to utils/
|
||||
self.comboNetstatProto.clear()
|
||||
self.comboNetstatProto.addItem(QC.translate("stats", "ALL"), 0)
|
||||
self.comboNetstatProto.addItem("TCP", 6)
|
||||
self.comboNetstatProto.addItem("UDP", 17)
|
||||
self.comboNetstatProto.addItem("SCTP", 132)
|
||||
self.comboNetstatProto.addItem("DCCP", 33)
|
||||
self.comboNetstatProto.addItem("ICMP", 1)
|
||||
self.comboNetstatProto.addItem("ICMPv6", 58)
|
||||
self.comboNetstatProto.addItem("IGMP", 2)
|
||||
|
||||
# These are sockets states. Conntrack uses a different enum.
|
||||
self.comboNetstatStates.clear()
|
||||
self.comboNetstatStates.addItem(QC.translate("stats", "ALL"), 0)
|
||||
self.comboNetstatStates.addItem("Established", 1)
|
||||
self.comboNetstatStates.addItem("TCP_SYN_SENT", 2)
|
||||
self.comboNetstatStates.addItem("TCP_SYN_RECV", 3)
|
||||
self.comboNetstatStates.addItem("TCP_FIN_WAIT1", 4)
|
||||
self.comboNetstatStates.addItem("TCP_FIN_WAIT2", 5)
|
||||
self.comboNetstatStates.addItem("TCP_TIME_WAIT", 6)
|
||||
self.comboNetstatStates.addItem("CLOSE", 7)
|
||||
self.comboNetstatStates.addItem("TCP_CLOSE_WAIT", 8)
|
||||
self.comboNetstatStates.addItem("TCP_LAST_ACK", 9)
|
||||
self.comboNetstatStates.addItem("LISTEN", 10)
|
||||
self.comboNetstatStates.addItem("TCP_CLOSING", 11)
|
||||
self.comboNetstatStates.addItem("TCP_NEW_SYN_RECV", 12)
|
||||
|
||||
self.comboNetstatFamily.clear()
|
||||
self.comboNetstatFamily.addItem(QC.translate("stats", "ALL"), 0)
|
||||
self.comboNetstatFamily.addItem("AF_INET", 2)
|
||||
self.comboNetstatFamily.addItem("AF_INET6", 10)
|
||||
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))
|
||||
self.comboNetstatFamily.currentIndexChanged.connect(lambda index: self._cb_combo_netstat_changed(3, index))
|
||||
self.comboNetstatStates.currentIndexChanged.connect(lambda index: self._cb_combo_netstat_changed(4, index))
|
||||
|
||||
self.enableRuleCheck.setVisible(False)
|
||||
self.delRuleButton.setVisible(False)
|
||||
self.editRuleButton.setVisible(False)
|
||||
|
@ -515,6 +583,20 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.TABLES[self.TAB_PORTS]['header_labels'] = stats_headers
|
||||
self.TABLES[self.TAB_USERS]['header_labels'] = stats_headers
|
||||
|
||||
self.LAST_NETSTAT_NODE = None
|
||||
self.TABLES[self.TAB_NETSTAT]['header_labels'] = [
|
||||
"Comm",
|
||||
QC.translate("stats", "State", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "SrcPort", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "SrcIP", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "DstIP", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "DstPort", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "UID", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "Family", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "Iface", "This is a word, without spaces and symbols.").replace(" ", ""),
|
||||
QC.translate("stats", "Metadata", "This is a word, without spaces and symbols.").replace(" ", "")
|
||||
]
|
||||
|
||||
self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTableView, self.eventsTable, "connections",
|
||||
self.TABLES[self.TAB_MAIN]['display_fields'],
|
||||
order_by="1",
|
||||
|
@ -607,6 +689,16 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
order_by="2",
|
||||
limit=self._get_limit()
|
||||
)
|
||||
self.TABLES[self.TAB_NETSTAT]['view'] = self._setup_table(QtWidgets.QTableView,
|
||||
self.netstatTable, "sockets",
|
||||
self.TABLES[self.TAB_NETSTAT]['display_fields'],
|
||||
model=NetstatTableModel("sockets", self.TABLES[self.TAB_NETSTAT]['header_labels']),
|
||||
verticalScrollBar=self.netstatScrollBar,
|
||||
#resize_cols=(),
|
||||
delegate=self.TABLES[self.TAB_NETSTAT]['delegate'],
|
||||
order_by="2",
|
||||
limit=self._get_limit()
|
||||
)
|
||||
|
||||
self.TABLES[self.TAB_NODES]['label'] = self.nodesLabel
|
||||
self.TABLES[self.TAB_RULES]['label'] = self.ruleLabel
|
||||
|
@ -615,6 +707,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.TABLES[self.TAB_ADDRS]['label'] = self.addrsLabel
|
||||
self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel
|
||||
self.TABLES[self.TAB_USERS]['label'] = self.usersLabel
|
||||
self.TABLES[self.TAB_NETSTAT]['label'] = self.netstatLabel
|
||||
|
||||
self.TABLES[self.TAB_NODES]['cmd'] = self.cmdNodesBack
|
||||
self.TABLES[self.TAB_RULES]['cmd'] = self.cmdRulesBack
|
||||
|
@ -623,6 +716,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.TABLES[self.TAB_ADDRS]['cmd'] = self.cmdAddrsBack
|
||||
self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack
|
||||
self.TABLES[self.TAB_USERS]['cmd'] = self.cmdUsersBack
|
||||
self.TABLES[self.TAB_NETSTAT]['cmd'] = self.cmdNetstatBack
|
||||
|
||||
self.TABLES[self.TAB_MAIN]['cmdCleanStats'] = self.cmdCleanSql
|
||||
self.TABLES[self.TAB_NODES]['cmdCleanStats'] = self.cmdCleanSql
|
||||
|
@ -632,6 +726,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.TABLES[self.TAB_ADDRS]['cmdCleanStats'] = self.cmdCleanSql
|
||||
self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql
|
||||
self.TABLES[self.TAB_USERS]['cmdCleanStats'] = self.cmdCleanSql
|
||||
self.TABLES[self.TAB_NETSTAT]['cmdCleanStats'] = self.cmdCleanSql
|
||||
# the rules clean button is only for a particular rule, not all.
|
||||
self.TABLES[self.TAB_MAIN]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(self.TAB_MAIN))
|
||||
|
||||
|
@ -675,7 +770,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self.TABLES[self.TAB_PROCS]['view'],
|
||||
self.TABLES[self.TAB_ADDRS]['view'],
|
||||
self.TABLES[self.TAB_PORTS]['view'],
|
||||
self.TABLES[self.TAB_USERS]['view']
|
||||
self.TABLES[self.TAB_USERS]['view'],
|
||||
self.TABLES[self.TAB_NETSTAT]['view']
|
||||
)
|
||||
self._file_names = ( \
|
||||
'events.csv',
|
||||
|
@ -685,7 +781,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
'procs.csv',
|
||||
'addrs.csv',
|
||||
'ports.csv',
|
||||
'users.csv'
|
||||
'users.csv',
|
||||
'netstat.csv'
|
||||
)
|
||||
|
||||
self.iconStart = Icons.new(self, "media-playback-start")
|
||||
|
@ -841,11 +938,14 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
w = self.nodesSplitter.width()
|
||||
self.nodesSplitter.setSizes([int(w/2), int(w/3)])
|
||||
|
||||
self._configure_netstat_combos()
|
||||
|
||||
self._restore_details_view_columns(self.eventsTable.horizontalHeader(), Config.STATS_GENERAL_COL_STATE)
|
||||
self._restore_details_view_columns(self.nodesTable.horizontalHeader(), Config.STATS_NODES_COL_STATE)
|
||||
self._restore_details_view_columns(self.rulesTable.horizontalHeader(), Config.STATS_RULES_COL_STATE)
|
||||
self._restore_details_view_columns(self.fwTable.horizontalHeader(), Config.STATS_FW_COL_STATE)
|
||||
self._restore_details_view_columns(self.alertsTable.horizontalHeader(), Config.STATS_ALERTS_COL_STATE)
|
||||
self._restore_details_view_columns(self.netstatTable.horizontalHeader(), Config.STATS_NETSTAT_COL_STATE)
|
||||
|
||||
rulesTreeNodes_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_1)
|
||||
if rulesTreeNodes_expanded != None:
|
||||
|
@ -877,6 +977,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
self._cfg.setSettings(Config.STATS_FW_COL_STATE, fwHeader.saveState())
|
||||
alertsHeader = self.alertsTable.horizontalHeader()
|
||||
self._cfg.setSettings(Config.STATS_ALERTS_COL_STATE, alertsHeader.saveState())
|
||||
netstatHeader = self.netstatTable.horizontalHeader()
|
||||
self._cfg.setSettings(Config.STATS_NETSTAT_COL_STATE, netstatHeader.saveState())
|
||||
|
||||
rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS)
|
||||
if rules_tree_apps != None:
|
||||
|
@ -932,6 +1034,23 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
csv.writer(stream, delimiter=',').writerows(selection)
|
||||
QtWidgets.qApp.clipboard().setText(stream.getvalue())
|
||||
|
||||
def _configure_netstat_combos(self):
|
||||
self.comboNetstatStates.blockSignals(True);
|
||||
self.comboNetstatStates.setCurrentIndex(
|
||||
self._cfg.getInt(Config.STATS_NETSTAT_FILTER_STATE, 0)
|
||||
)
|
||||
self.comboNetstatStates.blockSignals(False);
|
||||
self.comboNetstatFamily.blockSignals(True);
|
||||
self.comboNetstatFamily.setCurrentIndex(
|
||||
self._cfg.getInt(Config.STATS_NETSTAT_FILTER_FAMILY, 0)
|
||||
)
|
||||
self.comboNetstatFamily.blockSignals(False);
|
||||
self.comboNetstatProto.blockSignals(True);
|
||||
self.comboNetstatProto.setCurrentIndex(
|
||||
self._cfg.getInt(Config.STATS_NETSTAT_FILTER_PROTO, 0)
|
||||
)
|
||||
self.comboNetstatProto.blockSignals(False);
|
||||
|
||||
def _configure_events_contextual_menu(self, pos):
|
||||
try:
|
||||
cur_idx = self.tabWidget.currentIndex()
|
||||
|
@ -1469,6 +1588,21 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
def _cb_app_rules_updated(self, what):
|
||||
self._refresh_active_table()
|
||||
|
||||
def _cb_nodes_updated(self, count):
|
||||
prevNode = self.comboNetstatNodes.currentIndex()
|
||||
self.comboNetstatNodes.blockSignals(True);
|
||||
self.comboNetstatNodes.clear()
|
||||
for node in self._nodes.get_nodes():
|
||||
self.comboNetstatNodes.addItem(node)
|
||||
|
||||
if prevNode == -1:
|
||||
prevNode = 0
|
||||
self.comboNetstatNodes.setCurrentIndex(prevNode)
|
||||
if count == 0:
|
||||
self.netstatLabel.setText("")
|
||||
self.comboNetstatInterval.setCurrentIndex(0)
|
||||
self.comboNetstatNodes.blockSignals(False);
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
def _cb_fw_table_rows_reordered(self, node_addr):
|
||||
node = self._nodes.get_node(node_addr)
|
||||
|
@ -1497,23 +1631,33 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
|
||||
self._proc_details_dialog.monitor(pids)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _cb_notification_callback(self, node_addr, reply):
|
||||
if reply.id in self._notifications_sent:
|
||||
noti = self._notifications_sent[reply.id]
|
||||
if noti.type == ui_pb2.TASK_START and reply.code != ui_pb2.ERROR:
|
||||
self._update_node_info(reply.data)
|
||||
noti_data = json.loads(noti.data)
|
||||
if noti_data['name'] == "node-monitor":
|
||||
self._update_node_info(reply.data)
|
||||
elif noti_data['name'] == "sockets-monitor":
|
||||
self._update_netstat_table(node_addr, reply.data)
|
||||
else:
|
||||
print("_cb_notification_callback, unknown task reply?", noti_data)
|
||||
return
|
||||
|
||||
if reply.code == ui_pb2.ERROR:
|
||||
elif noti.type == ui_pb2.TASK_START and reply.code == ui_pb2.ERROR:
|
||||
self.netstatLabel.setText("error starting netstat table: {0}".format(reply.data))
|
||||
elif reply.code == ui_pb2.ERROR:
|
||||
Message.ok(
|
||||
QC.translate("stats", "Error:"),
|
||||
"{0}".format(reply.data),
|
||||
QtWidgets.QMessageBox.Warning)
|
||||
else:
|
||||
print("_cb_notification_callback, unknown reply:", reply)
|
||||
|
||||
del self._notifications_sent[reply.id]
|
||||
|
||||
else:
|
||||
print("_cb_notification_callback, reply not in the list:", reply)
|
||||
Message.ok(
|
||||
QC.translate("stats", "Warning:"),
|
||||
"{0}".format(reply.data),
|
||||
|
@ -1522,12 +1666,19 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
def _cb_tab_changed(self, index):
|
||||
self.comboAction.setVisible(index == self.TAB_MAIN)
|
||||
|
||||
if index != self.TAB_NETSTAT and self.LAST_TAB == self.TAB_NETSTAT:
|
||||
self._unmonitor_node_netstat(self.LAST_SELECTED_ITEM)
|
||||
self.comboNetstatNodes.setCurrentIndex(0)
|
||||
|
||||
if self.LAST_TAB == self.TAB_NODES and self.LAST_SELECTED_ITEM != "":
|
||||
self._unmonitor_deselected_node(self.LAST_SELECTED_ITEM)
|
||||
|
||||
self.TABLES[index]['cmdCleanStats'].setVisible(True)
|
||||
if index == self.TAB_MAIN:
|
||||
self._set_events_query()
|
||||
elif index == self.TAB_NETSTAT:
|
||||
self.IN_DETAIL_VIEW[index] = True
|
||||
self._monitor_node_netstat()
|
||||
else:
|
||||
if index == self.TAB_RULES:
|
||||
# display the clean buton only if not in detail view
|
||||
|
@ -1617,6 +1768,21 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
if qstr != None:
|
||||
self.setQuery(model, qstr)
|
||||
|
||||
def _cb_combo_netstat_changed(self, combo, idx):
|
||||
refreshIndex = self.comboNetstatInterval.currentIndex()
|
||||
self._unmonitor_node_netstat(self.LAST_NETSTAT_NODE)
|
||||
if refreshIndex > 0:
|
||||
self._monitor_node_netstat()
|
||||
|
||||
if combo == 2:
|
||||
self._cfg.setSettings(Config.STATS_NETSTAT_FILTER_PROTO, self.comboNetstatProto.currentIndex())
|
||||
elif combo == 3:
|
||||
self._cfg.setSettings(Config.STATS_NETSTAT_FILTER_FAMILY, self.comboNetstatFamily.currentIndex())
|
||||
elif combo == 4:
|
||||
self._cfg.setSettings(Config.STATS_NETSTAT_FILTER_STATE, self.comboNetstatStates.currentIndex())
|
||||
|
||||
self.LAST_NETSTAT_NODE = self.comboNetstatNodes.currentText()
|
||||
|
||||
def _cb_limit_combo_changed(self, idx):
|
||||
if self.tabWidget.currentIndex() == self.TAB_MAIN:
|
||||
self._set_events_query()
|
||||
|
@ -2484,6 +2650,142 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
if nid != None:
|
||||
self._notifications_sent[nid] = noti
|
||||
self.labelNodeDetails.setText("")
|
||||
print("taskStop, prev node:", last_addr, "nid:", nid)
|
||||
|
||||
# XXX: would be useful to leave latest data?
|
||||
#self._reset_node_info()
|
||||
|
||||
def _monitor_node_netstat(self):
|
||||
# TODO:
|
||||
# - create a tasks package, to centralize/normalize tasks' names and
|
||||
# config
|
||||
self.netstatLabel.show()
|
||||
|
||||
node_addr = self.comboNetstatNodes.currentText()
|
||||
if node_addr == "":
|
||||
print("monitor_netstat_node: no nodes")
|
||||
self.netstatLabel.setText("")
|
||||
return
|
||||
if not self._nodes.is_connected(node_addr):
|
||||
print("monitor_node_netstat, node not connected:", node_addr)
|
||||
self.netstatLabel.setText("{0} node is not connected".format(node_addr))
|
||||
return
|
||||
|
||||
refreshIndex = self.comboNetstatInterval.currentIndex()
|
||||
if refreshIndex == 0:
|
||||
self._unmonitor_node_netstat(node_addr)
|
||||
return
|
||||
|
||||
refreshInterval = self.comboNetstatInterval.currentText()
|
||||
proto = self.comboNetstatProto.currentIndex()
|
||||
family = self.comboNetstatFamily.currentIndex()
|
||||
state = self.comboNetstatStates.currentIndex()
|
||||
config = '{"name": "sockets-monitor", "data": {"interval": "%s", "state": %d, "proto": %d, "family": %d}}' % (
|
||||
refreshInterval,
|
||||
int(self.comboNetstatStates.itemData(state)),
|
||||
int(self.comboNetstatProto.itemData(proto)),
|
||||
int(self.comboNetstatFamily.itemData(family))
|
||||
)
|
||||
|
||||
self.netstatLabel.setText(QC.translate("stats", "loading in {0}...".format(refreshInterval)))
|
||||
|
||||
noti = ui_pb2.Notification(
|
||||
clientName="",
|
||||
serverName="",
|
||||
type=ui_pb2.TASK_START,
|
||||
data=config,
|
||||
rules=[])
|
||||
nid = self._nodes.send_notification(
|
||||
node_addr, noti, self._notification_callback
|
||||
)
|
||||
if nid != None:
|
||||
self._notifications_sent[nid] = noti
|
||||
|
||||
self.LAST_SELECTED_ITEM = node_addr
|
||||
|
||||
def _unmonitor_node_netstat(self, node_addr):
|
||||
self.netstatLabel.hide()
|
||||
self.netstatLabel.setText("")
|
||||
if node_addr == "":
|
||||
print("unmonitor_netstat_node: no nodes")
|
||||
return
|
||||
|
||||
if not self._nodes.is_connected(node_addr):
|
||||
print("unmonitor_node_netstat, node not connected:", node_addr)
|
||||
else:
|
||||
noti = ui_pb2.Notification(
|
||||
clientName="",
|
||||
serverName="",
|
||||
type=ui_pb2.TASK_STOP,
|
||||
data='{"name": "sockets-monitor", "data": {}}',
|
||||
rules=[])
|
||||
nid = self._nodes.send_notification(
|
||||
node_addr, noti, self._notification_callback
|
||||
)
|
||||
if nid != None:
|
||||
self._notifications_sent[nid] = noti
|
||||
|
||||
def _update_netstat_table(self, node_addr, data):
|
||||
netstat = json.loads(data)
|
||||
fields = []
|
||||
values = []
|
||||
cols = "(last_seen, node, src_port, src_ip, dst_ip, dst_port, proto, uid, inode, iface, family, state, cookies, rqueue, wqueue, expires, retrans, timer, proc_path, proc_comm, proc_pid)"
|
||||
try:
|
||||
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
# TODO: make this optional
|
||||
self._db.clean(self.TABLES[self.TAB_NETSTAT]['name'])
|
||||
self._db.transaction()
|
||||
for k in netstat['Table']:
|
||||
if k == None:
|
||||
continue
|
||||
sck = k['Socket']
|
||||
iface = k['Socket']['ID']['Interface']
|
||||
if k['Iface'] != "":
|
||||
iface = k['Iface']
|
||||
proc_comm = ""
|
||||
proc_path = ""
|
||||
proc_pid = ""
|
||||
if k['PID'] != -1 and str(k['PID']) in netstat['Processes'].keys():
|
||||
proc_pid = str(k['PID'])
|
||||
proc_path = netstat['Processes'][proc_pid]['Path']
|
||||
proc_comm = netstat['Processes'][proc_pid]['Comm']
|
||||
self._db.insert(
|
||||
self.TABLES[self.TAB_NETSTAT]['name'],
|
||||
cols,
|
||||
(
|
||||
now,
|
||||
node_addr,
|
||||
k['Socket']['ID']['SourcePort'],
|
||||
k['Socket']['ID']['Source'],
|
||||
k['Socket']['ID']['Destination'],
|
||||
k['Socket']['ID']['DestinationPort'],
|
||||
k['Proto'],
|
||||
k['Socket']['UID'],
|
||||
k['Socket']['INode'],
|
||||
iface,
|
||||
k['Socket']['Family'],
|
||||
k['Socket']['State'],
|
||||
str(k['Socket']['ID']['Cookie']),
|
||||
k['Socket']['RQueue'],
|
||||
k['Socket']['WQueue'],
|
||||
k['Socket']['Expires'],
|
||||
k['Socket']['Retrans'],
|
||||
k['Socket']['Timer'],
|
||||
proc_path,
|
||||
proc_comm,
|
||||
proc_pid
|
||||
)
|
||||
)
|
||||
self._db.commit()
|
||||
self.netstatLabel.setText(QC.translate("stats", "refreshing..."))
|
||||
self._refresh_active_table()
|
||||
except Exception as e:
|
||||
print("_update_netstat_table exception:", e)
|
||||
print(data)
|
||||
self.netstatLabel.setText("error loading netstat table")
|
||||
self.netstatLabel.setText(QC.translate("stats", "error loading: {0}".format(repr(e))))
|
||||
|
||||
# create plugins and actions before dialogs
|
||||
|
||||
def _update_node_info(self, data):
|
||||
try:
|
||||
|
@ -3068,7 +3370,20 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
|||
values.append(table.model().index(row, col).data())
|
||||
w.writerow(values)
|
||||
|
||||
def _setup_table(self, widget, tableWidget, table_name, fields="*", group_by="", order_by="2", sort_direction=SORT_ORDER[1], limit="", resize_cols=(), model=None, delegate=None, verticalScrollBar=None, tracking_column=COL_TIME):
|
||||
def _setup_table(self,
|
||||
widget,
|
||||
tableWidget,
|
||||
table_name,
|
||||
fields="*",
|
||||
group_by="",
|
||||
order_by="2",
|
||||
sort_direction=SORT_ORDER[1],
|
||||
limit="",
|
||||
resize_cols=(),
|
||||
model=None,
|
||||
delegate=None,
|
||||
verticalScrollBar=None,
|
||||
tracking_column=COL_TIME):
|
||||
tableWidget.setSortingEnabled(True)
|
||||
if model == None:
|
||||
model = self._db.get_new_qsql_model()
|
||||
|
|
|
@ -276,6 +276,7 @@ class Nodes(QObject):
|
|||
# FIXME: the reply is sent before we return the notification id
|
||||
if callback_signal != None:
|
||||
callback_signal.emit(
|
||||
addr,
|
||||
ui_pb2.NotificationReply(
|
||||
id=notification.id,
|
||||
code=ui_pb2.ERROR,
|
||||
|
@ -294,6 +295,7 @@ class Nodes(QObject):
|
|||
print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
|
||||
if callback_signal != None:
|
||||
callback_signal.emit(
|
||||
addr,
|
||||
ui_pb2.NotificationReply(
|
||||
id=notification.id,
|
||||
code=ui_pb2.ERROR,
|
||||
|
@ -336,7 +338,7 @@ class Nodes(QObject):
|
|||
return
|
||||
|
||||
if self._notifications_sent[reply.id]['callback'] != None:
|
||||
self._notifications_sent[reply.id]['callback'].emit(reply)
|
||||
self._notifications_sent[reply.id]['callback'].emit(addr, reply)
|
||||
|
||||
# delete only one-time notifications
|
||||
# we need the ID of streaming notifications from the server
|
||||
|
|
|
@ -560,7 +560,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>336</width>
|
||||
<width>314</width>
|
||||
<height>404</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -1518,6 +1518,313 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_9">
|
||||
<attribute name="icon">
|
||||
<iconset theme="network"/>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Netstat</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="GenericTableView" name="netstatTable">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollBar" name="netstatScrollBar">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_20">
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdNetstatBack">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-previous">
|
||||
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboNetstatInterval">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="stop"/>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>10s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>15s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>20s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>30s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>45s</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1m</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5m</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>10m</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboNetstatNodes">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All nodes</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboNetstatProto">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ALL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">UDP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">SCTP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">DCCP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">ICMP</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">RAW</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Family</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboNetstatFamily">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ALL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">AF_INET</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">AF_INET6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">AF_PACKET</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>State</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboNetstatStates">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ALL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Established</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_SYN_SENT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_SYN_RECV</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_FIN_WAIT1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_FIN_WAIT2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_TIME_WAIT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_CLOSE</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_CLOSE_WAIT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_LAST_ACK</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_LISTEN</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">TCP_CLOSING</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="netstatLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
|
|
|
@ -37,7 +37,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
|
|||
_add_alert_trigger = QtCore.pyqtSignal(str, str, ui_pb2.Alert)
|
||||
_version_warning_trigger = QtCore.pyqtSignal(str, str)
|
||||
_status_change_trigger = QtCore.pyqtSignal(bool)
|
||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
||||
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||
_show_message_trigger = QtCore.pyqtSignal(str, str, int, int)
|
||||
|
||||
# .desktop filename located under /usr/share/applications/
|
||||
|
@ -401,8 +401,8 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
|
|||
else:
|
||||
self._tray.setIcon(self.off_icon)
|
||||
|
||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
||||
def _on_notification_reply(self, reply):
|
||||
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||
def _on_notification_reply(self, addr, reply):
|
||||
if reply.code == ui_pb2.ERROR:
|
||||
self._tray.showMessage("Error",
|
||||
reply.data,
|
||||
|
|
43
ui/opensnitch/utils/sockets.py
Normal file
43
ui/opensnitch/utils/sockets.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
|
||||
# https://pkg.go.dev/syscall#pkg-constants
|
||||
Family = {
|
||||
'0': 'AF_UNSPEC',
|
||||
'2': 'AF_INET',
|
||||
'10': 'AF_INET6',
|
||||
'11': 'AF_PACKET',
|
||||
'40': 'AF_VSOCK',
|
||||
'44': 'AF_XDP',
|
||||
'45': 'AF_MCTP',
|
||||
}
|
||||
|
||||
Proto = {
|
||||
'0': 'IP',
|
||||
'1': 'ICMP',
|
||||
'2': 'IGMP',
|
||||
'3': 'ETH_P_ALL',
|
||||
'6': 'TCP',
|
||||
'17': 'UDP',
|
||||
'33': 'DCCP',
|
||||
'41': 'IPv6',
|
||||
'58': 'ICMPv6',
|
||||
'132': 'SCTP',
|
||||
'136': 'UDPLITE',
|
||||
'255': 'RAW'
|
||||
}
|
||||
|
||||
State = {
|
||||
'1': 'Established',
|
||||
'2': 'TCP_SYN_SENT',
|
||||
'3': 'TCP_SYN_RECV',
|
||||
'4': 'TCP_FIN_WAIT1',
|
||||
'5': 'TCP_FIN_WAIT2',
|
||||
'6': 'TCP_TIME_WAIT',
|
||||
'7': 'CLOSE',
|
||||
'8': 'TCP_CLOSE_WAIT',
|
||||
'9': 'TCP_LAST_ACK',
|
||||
'10': 'LISTEN',
|
||||
'11': 'TCP_CLOSING',
|
||||
'12': 'TCP_NEW_SYNC_RECV',
|
||||
'13': 'TCP_MAX_STATES'
|
||||
}
|
Loading…
Add table
Reference in a new issue