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 {
|
if item != nil && item.Pid == pid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.RLock()
|
c.Lock()
|
||||||
defer c.RUnlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
sort.Slice(c.items, func(i, j int) bool {
|
sort.Slice(c.items, func(i, j int) bool {
|
||||||
t := c.items[i].LastSeen
|
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).
|
// and Data is the configuration of each task (a map[string]string, converted by the json package).
|
||||||
type TaskNotification struct {
|
type TaskNotification struct {
|
||||||
// Data of the task.
|
// Data of the task.
|
||||||
Data map[string]string
|
Data interface{}
|
||||||
|
|
||||||
// Name of the task.
|
// Name of the task.
|
||||||
Name string
|
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"
|
||||||
"github.com/evilsocket/opensnitch/daemon/tasks/nodemonitor"
|
"github.com/evilsocket/opensnitch/daemon/tasks/nodemonitor"
|
||||||
"github.com/evilsocket/opensnitch/daemon/tasks/pidmonitor"
|
"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/config"
|
||||||
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
|
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
|
||||||
"golang.org/x/net/context"
|
"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) {
|
func (c *Client) monitorNode(node, interval string, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
|
||||||
taskName, nodeMonTask := nodemonitor.New(node, interval, true)
|
taskName, nodeMonTask := nodemonitor.New(node, interval, true)
|
||||||
ctxNode, err := TaskMgr.AddTask(taskName, nodeMonTask)
|
ctxNode, err := TaskMgr.AddTask(taskName, nodeMonTask)
|
||||||
|
@ -206,15 +238,28 @@ func (c *Client) handleActionTaskStart(stream protocol.UI_NotificationsClient, n
|
||||||
}
|
}
|
||||||
switch taskConf.Name {
|
switch taskConf.Name {
|
||||||
case pidmonitor.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 {
|
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)
|
c.sendNotificationReply(stream, notification.Id, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.monitorProcessDetails(pid, taskConf.Data["interval"], stream, notification)
|
interval, _ := conf["interval"].(string)
|
||||||
|
c.monitorProcessDetails(pid, interval, stream, notification)
|
||||||
case nodemonitor.Name:
|
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:
|
default:
|
||||||
log.Debug("TaskStart, unknown task: %v", taskConf)
|
log.Debug("TaskStart, unknown task: %v", taskConf)
|
||||||
//c.sendNotificationReply(stream, notification.Id, "", err)
|
//c.sendNotificationReply(stream, notification.Id, "", err)
|
||||||
|
@ -231,7 +276,12 @@ func (c *Client) handleActionTaskStop(stream protocol.UI_NotificationsClient, no
|
||||||
}
|
}
|
||||||
switch taskConf.Name {
|
switch taskConf.Name {
|
||||||
case pidmonitor.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 {
|
if err != nil {
|
||||||
log.Error("TaskStop.Data, err: %s, %s, %v+, %q", err, notification.Data, taskConf.Data, taskConf.Data)
|
log.Error("TaskStop.Data, err: %s, %s, %v+, %q", err, notification.Data, taskConf.Data, taskConf.Data)
|
||||||
c.sendNotificationReply(stream, notification.Id, "", err)
|
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))
|
TaskMgr.RemoveTask(fmt.Sprint(taskConf.Name, "-", pid))
|
||||||
case nodemonitor.Name:
|
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:
|
default:
|
||||||
log.Debug("TaskStop, unknown task: %v", taskConf)
|
log.Debug("TaskStop, unknown task: %v", taskConf)
|
||||||
//c.sendNotificationReply(stream, notification.Id, "", err)
|
//c.sendNotificationReply(stream, notification.Id, "", err)
|
||||||
|
|
|
@ -9,7 +9,8 @@ from opensnitch.utils.xdg import xdg_config_home
|
||||||
from opensnitch.actions.default_configs import (
|
from opensnitch.actions.default_configs import (
|
||||||
commonDelegateConfig,
|
commonDelegateConfig,
|
||||||
rulesDelegateConfig,
|
rulesDelegateConfig,
|
||||||
fwDelegateConfig
|
fwDelegateConfig,
|
||||||
|
netstatDelegateConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
from opensnitch.plugins import PluginsList
|
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[commonDelegateConfig[Actions.KEY_NAME]] = self.compile(commonDelegateConfig)
|
||||||
self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig)
|
self._actions_list[rulesDelegateConfig[Actions.KEY_NAME]] = self.compile(rulesDelegateConfig)
|
||||||
self._actions_list[fwDelegateConfig[Actions.KEY_NAME]] = self.compile(fwDelegateConfig)
|
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):
|
def load(self, action_file):
|
||||||
"""read a json file from disk and create the action."""
|
"""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_RULES_COL_STATE = "statsDialog/rules_columns_state"
|
||||||
STATS_FW_COL_STATE = "statsDialog/firewall_columns_state"
|
STATS_FW_COL_STATE = "statsDialog/firewall_columns_state"
|
||||||
STATS_ALERTS_COL_STATE = "statsDialog/alerts_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_0 = "statsDialog/rules_tree_0_expanded"
|
||||||
STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded"
|
STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded"
|
||||||
STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos"
|
STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos"
|
||||||
STATS_NODES_SPLITTER_POS = "statsDialog/nodes_splitter_pos"
|
STATS_NODES_SPLITTER_POS = "statsDialog/nodes_splitter_pos"
|
||||||
STATS_VIEW_COL_STATE = "statsDialog/view_columns_state"
|
STATS_VIEW_COL_STATE = "statsDialog/view_columns_state"
|
||||||
STATS_VIEW_DETAILS_COL_STATE = "statsDialog/view_details_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_AUTO_SCREEN_SCALE_FACTOR = "global/screen_scale_factor_auto"
|
||||||
QT_SCREEN_SCALE_FACTOR = "global/screen_scale_factor"
|
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 " \
|
"status int " \
|
||||||
")", self.db)
|
")", self.db)
|
||||||
q.exec_()
|
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 = QSqlQuery("create index rules_index on rules (time)", self.db)
|
||||||
q.exec_()
|
q.exec_()
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
POLICY_ACCEPT = 0
|
POLICY_ACCEPT = 0
|
||||||
POLICY_DROP = 1
|
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):
|
def __init__(self, parent=None, appicon=None, node=None):
|
||||||
QtWidgets.QDialog.__init__(self, parent)
|
QtWidgets.QDialog.__init__(self, parent)
|
||||||
|
@ -87,8 +87,8 @@ class FirewallDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
self.cmdNewRule.setIcon(newIcon)
|
self.cmdNewRule.setIcon(newIcon)
|
||||||
self.cmdHelp.setIcon(helpIcon)
|
self.cmdHelp.setIcon(helpIcon)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, addr, reply):
|
||||||
self.comboInput.setEnabled(True)
|
self.comboInput.setEnabled(True)
|
||||||
if reply.id in self._notifications_sent:
|
if reply.id in self._notifications_sent:
|
||||||
if reply.code == ui_pb2.OK:
|
if reply.code == ui_pb2.OK:
|
||||||
|
|
|
@ -53,7 +53,7 @@ class FwRuleDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
STATM_COUNTER = 15
|
STATM_COUNTER = 15
|
||||||
STATM_LIMIT = 16
|
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):
|
def __init__(self, parent=None, appicon=None):
|
||||||
QtWidgets.QDialog.__init__(self, parent)
|
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.comboDirection.currentIndexChanged.disconnect(self._cb_direction_changed)
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, addr, reply):
|
||||||
self._enable_buttons()
|
self._enable_buttons()
|
||||||
|
|
||||||
try:
|
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]):
|
class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
|
|
||||||
LOG_TAG = "[Preferences] "
|
LOG_TAG = "[Preferences] "
|
||||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||||
saved = QtCore.pyqtSignal()
|
saved = QtCore.pyqtSignal()
|
||||||
|
|
||||||
TAB_POPUPS = 0
|
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)
|
self._themes.change_theme(self, self.comboUITheme.currentText(), extra_opts)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, addr, reply):
|
||||||
#print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code)
|
#print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code)
|
||||||
if reply.id in self._notifications_sent:
|
if reply.id in self._notifications_sent:
|
||||||
if reply.code == ui_pb2.OK:
|
if reply.code == ui_pb2.OK:
|
||||||
|
|
|
@ -18,7 +18,7 @@ class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0])
|
||||||
|
|
||||||
LOG_TAG = "[ProcessDetails]: "
|
LOG_TAG = "[ProcessDetails]: "
|
||||||
|
|
||||||
_notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
|
_notification_callback = QtCore.pyqtSignal(str, ui_pb2.NotificationReply)
|
||||||
|
|
||||||
TAB_STATUS = 0
|
TAB_STATUS = 0
|
||||||
TAB_DESCRIPTORS = 1
|
TAB_DESCRIPTORS = 1
|
||||||
|
@ -111,8 +111,8 @@ class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("procdialog._configure_plugins() exception:", name, " you may want to enable this plugin -", e)
|
print("procdialog._configure_plugins() exception:", name, " you may want to enable this plugin -", e)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, addr, reply):
|
||||||
if reply.id not in self._notifications_sent:
|
if reply.id not in self._notifications_sent:
|
||||||
print("[stats] unknown notification received: ", reply.id)
|
print("[stats] unknown notification received: ", reply.id)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -48,7 +48,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
PW_USER = 0
|
PW_USER = 0
|
||||||
PW_UID = 2
|
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):
|
def __init__(self, parent=None, _rule=None, appicon=None):
|
||||||
super(RulesEditorDialog, self).__init__(parent)
|
super(RulesEditorDialog, self).__init__(parent)
|
||||||
|
@ -302,8 +302,8 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
|
|
||||||
self._rules.updated.emit(0)
|
self._rules.updated.emit(0)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, addr, reply):
|
||||||
#print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code)
|
#print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code)
|
||||||
if reply.id in self._notifications_sent:
|
if reply.id in self._notifications_sent:
|
||||||
if reply.code == ui_pb2.OK:
|
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.firewalltableview import FirewallTableModel
|
||||||
from opensnitch.customwidgets.generictableview import GenericTableModel
|
from opensnitch.customwidgets.generictableview import GenericTableModel
|
||||||
from opensnitch.customwidgets.addresstablemodel import AddressTableModel
|
from opensnitch.customwidgets.addresstablemodel import AddressTableModel
|
||||||
|
from opensnitch.customwidgets.netstattablemodel import NetstatTableModel
|
||||||
from opensnitch.utils import Message, QuickHelp, AsnDB, Icons
|
from opensnitch.utils import Message, QuickHelp, AsnDB, Icons
|
||||||
from opensnitch.utils.infowindow import InfoWindow
|
from opensnitch.utils.infowindow import InfoWindow
|
||||||
from opensnitch.utils.xdg import xdg_current_desktop
|
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)
|
_status_changed_trigger = QtCore.pyqtSignal(bool)
|
||||||
_shown_trigger = QtCore.pyqtSignal()
|
_shown_trigger = QtCore.pyqtSignal()
|
||||||
_notification_trigger = QtCore.pyqtSignal(ui_pb2.Notification)
|
_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"]
|
SORT_ORDER = ["ASC", "DESC"]
|
||||||
LIMITS = ["LIMIT 50", "LIMIT 100", "LIMIT 200", "LIMIT 300", ""]
|
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_ADDRS = 5
|
||||||
TAB_PORTS = 6
|
TAB_PORTS = 6
|
||||||
TAB_USERS = 7
|
TAB_USERS = 7
|
||||||
TAB_FIREWALL = 8 # in rules tab
|
TAB_NETSTAT = 8
|
||||||
TAB_ALERTS = 9 # in rules tab
|
# 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
|
# tree's top level items
|
||||||
RULES_TREE_APPS = 0
|
RULES_TREE_APPS = 0
|
||||||
|
@ -138,6 +141,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
TAB_ADDRS: False,
|
TAB_ADDRS: False,
|
||||||
TAB_PORTS: False,
|
TAB_PORTS: False,
|
||||||
TAB_USERS: False,
|
TAB_USERS: False,
|
||||||
|
TAB_NETSTAT: False,
|
||||||
TAB_FIREWALL: False,
|
TAB_FIREWALL: False,
|
||||||
TAB_ALERTS: False
|
TAB_ALERTS: False
|
||||||
}
|
}
|
||||||
|
@ -214,40 +218,6 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
"last_order_to": 0,
|
"last_order_to": 0,
|
||||||
"tracking_column:": COL_R_NAME
|
"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: {
|
TAB_HOSTS: {
|
||||||
"name": "hosts",
|
"name": "hosts",
|
||||||
"label": None,
|
"label": None,
|
||||||
|
@ -322,6 +292,65 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
"last_order_by": "2",
|
"last_order_by": "2",
|
||||||
"last_order_to": 1,
|
"last_order_to": 1,
|
||||||
"tracking_column:": COL_TIME
|
"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._fw = Firewall().instance()
|
||||||
self._rules = Rules.instance()
|
self._rules = Rules.instance()
|
||||||
self._fw.rules.rulesUpdated.connect(self._cb_fw_rules_updated)
|
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._rules.updated.connect(self._cb_app_rules_updated)
|
||||||
self._actions = Actions().instance()
|
self._actions = Actions().instance()
|
||||||
self._action_list = self._actions.getByType(PluginBase.TYPE_MAIN_DIALOG)
|
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.nextButton.clicked.connect(self._cb_next_button_clicked)
|
||||||
self.prevButton.clicked.connect(self._cb_prev_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.enableRuleCheck.setVisible(False)
|
||||||
self.delRuleButton.setVisible(False)
|
self.delRuleButton.setVisible(False)
|
||||||
self.editRuleButton.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_PORTS]['header_labels'] = stats_headers
|
||||||
self.TABLES[self.TAB_USERS]['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]['view'] = self._setup_table(QtWidgets.QTableView, self.eventsTable, "connections",
|
||||||
self.TABLES[self.TAB_MAIN]['display_fields'],
|
self.TABLES[self.TAB_MAIN]['display_fields'],
|
||||||
order_by="1",
|
order_by="1",
|
||||||
|
@ -607,6 +689,16 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
order_by="2",
|
order_by="2",
|
||||||
limit=self._get_limit()
|
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_NODES]['label'] = self.nodesLabel
|
||||||
self.TABLES[self.TAB_RULES]['label'] = self.ruleLabel
|
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_ADDRS]['label'] = self.addrsLabel
|
||||||
self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel
|
self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel
|
||||||
self.TABLES[self.TAB_USERS]['label'] = self.usersLabel
|
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_NODES]['cmd'] = self.cmdNodesBack
|
||||||
self.TABLES[self.TAB_RULES]['cmd'] = self.cmdRulesBack
|
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_ADDRS]['cmd'] = self.cmdAddrsBack
|
||||||
self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack
|
self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack
|
||||||
self.TABLES[self.TAB_USERS]['cmd'] = self.cmdUsersBack
|
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_MAIN]['cmdCleanStats'] = self.cmdCleanSql
|
||||||
self.TABLES[self.TAB_NODES]['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_ADDRS]['cmdCleanStats'] = self.cmdCleanSql
|
||||||
self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql
|
self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql
|
||||||
self.TABLES[self.TAB_USERS]['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.
|
# 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))
|
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_PROCS]['view'],
|
||||||
self.TABLES[self.TAB_ADDRS]['view'],
|
self.TABLES[self.TAB_ADDRS]['view'],
|
||||||
self.TABLES[self.TAB_PORTS]['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 = ( \
|
self._file_names = ( \
|
||||||
'events.csv',
|
'events.csv',
|
||||||
|
@ -685,7 +781,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
'procs.csv',
|
'procs.csv',
|
||||||
'addrs.csv',
|
'addrs.csv',
|
||||||
'ports.csv',
|
'ports.csv',
|
||||||
'users.csv'
|
'users.csv',
|
||||||
|
'netstat.csv'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.iconStart = Icons.new(self, "media-playback-start")
|
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()
|
w = self.nodesSplitter.width()
|
||||||
self.nodesSplitter.setSizes([int(w/2), int(w/3)])
|
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.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.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.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.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.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)
|
rulesTreeNodes_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_1)
|
||||||
if rulesTreeNodes_expanded != None:
|
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())
|
self._cfg.setSettings(Config.STATS_FW_COL_STATE, fwHeader.saveState())
|
||||||
alertsHeader = self.alertsTable.horizontalHeader()
|
alertsHeader = self.alertsTable.horizontalHeader()
|
||||||
self._cfg.setSettings(Config.STATS_ALERTS_COL_STATE, alertsHeader.saveState())
|
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)
|
rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS)
|
||||||
if rules_tree_apps != None:
|
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)
|
csv.writer(stream, delimiter=',').writerows(selection)
|
||||||
QtWidgets.qApp.clipboard().setText(stream.getvalue())
|
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):
|
def _configure_events_contextual_menu(self, pos):
|
||||||
try:
|
try:
|
||||||
cur_idx = self.tabWidget.currentIndex()
|
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):
|
def _cb_app_rules_updated(self, what):
|
||||||
self._refresh_active_table()
|
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)
|
@QtCore.pyqtSlot(str)
|
||||||
def _cb_fw_table_rows_reordered(self, node_addr):
|
def _cb_fw_table_rows_reordered(self, node_addr):
|
||||||
node = self._nodes.get_node(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)
|
self._proc_details_dialog.monitor(pids)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _cb_notification_callback(self, reply):
|
def _cb_notification_callback(self, node_addr, reply):
|
||||||
if reply.id in self._notifications_sent:
|
if reply.id in self._notifications_sent:
|
||||||
noti = self._notifications_sent[reply.id]
|
noti = self._notifications_sent[reply.id]
|
||||||
if noti.type == ui_pb2.TASK_START and reply.code != ui_pb2.ERROR:
|
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
|
return
|
||||||
|
elif noti.type == ui_pb2.TASK_START and reply.code == ui_pb2.ERROR:
|
||||||
if reply.code == ui_pb2.ERROR:
|
self.netstatLabel.setText("error starting netstat table: {0}".format(reply.data))
|
||||||
|
elif reply.code == ui_pb2.ERROR:
|
||||||
Message.ok(
|
Message.ok(
|
||||||
QC.translate("stats", "Error:"),
|
QC.translate("stats", "Error:"),
|
||||||
"{0}".format(reply.data),
|
"{0}".format(reply.data),
|
||||||
QtWidgets.QMessageBox.Warning)
|
QtWidgets.QMessageBox.Warning)
|
||||||
|
else:
|
||||||
|
print("_cb_notification_callback, unknown reply:", reply)
|
||||||
|
|
||||||
del self._notifications_sent[reply.id]
|
del self._notifications_sent[reply.id]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
print("_cb_notification_callback, reply not in the list:", reply)
|
||||||
Message.ok(
|
Message.ok(
|
||||||
QC.translate("stats", "Warning:"),
|
QC.translate("stats", "Warning:"),
|
||||||
"{0}".format(reply.data),
|
"{0}".format(reply.data),
|
||||||
|
@ -1522,12 +1666,19 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
def _cb_tab_changed(self, index):
|
def _cb_tab_changed(self, index):
|
||||||
self.comboAction.setVisible(index == self.TAB_MAIN)
|
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 != "":
|
if self.LAST_TAB == self.TAB_NODES and self.LAST_SELECTED_ITEM != "":
|
||||||
self._unmonitor_deselected_node(self.LAST_SELECTED_ITEM)
|
self._unmonitor_deselected_node(self.LAST_SELECTED_ITEM)
|
||||||
|
|
||||||
self.TABLES[index]['cmdCleanStats'].setVisible(True)
|
self.TABLES[index]['cmdCleanStats'].setVisible(True)
|
||||||
if index == self.TAB_MAIN:
|
if index == self.TAB_MAIN:
|
||||||
self._set_events_query()
|
self._set_events_query()
|
||||||
|
elif index == self.TAB_NETSTAT:
|
||||||
|
self.IN_DETAIL_VIEW[index] = True
|
||||||
|
self._monitor_node_netstat()
|
||||||
else:
|
else:
|
||||||
if index == self.TAB_RULES:
|
if index == self.TAB_RULES:
|
||||||
# display the clean buton only if not in detail view
|
# 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:
|
if qstr != None:
|
||||||
self.setQuery(model, qstr)
|
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):
|
def _cb_limit_combo_changed(self, idx):
|
||||||
if self.tabWidget.currentIndex() == self.TAB_MAIN:
|
if self.tabWidget.currentIndex() == self.TAB_MAIN:
|
||||||
self._set_events_query()
|
self._set_events_query()
|
||||||
|
@ -2484,6 +2650,142 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
if nid != None:
|
if nid != None:
|
||||||
self._notifications_sent[nid] = noti
|
self._notifications_sent[nid] = noti
|
||||||
self.labelNodeDetails.setText("")
|
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):
|
def _update_node_info(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -3068,7 +3370,20 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
|
||||||
values.append(table.model().index(row, col).data())
|
values.append(table.model().index(row, col).data())
|
||||||
w.writerow(values)
|
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)
|
tableWidget.setSortingEnabled(True)
|
||||||
if model == None:
|
if model == None:
|
||||||
model = self._db.get_new_qsql_model()
|
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
|
# FIXME: the reply is sent before we return the notification id
|
||||||
if callback_signal != None:
|
if callback_signal != None:
|
||||||
callback_signal.emit(
|
callback_signal.emit(
|
||||||
|
addr,
|
||||||
ui_pb2.NotificationReply(
|
ui_pb2.NotificationReply(
|
||||||
id=notification.id,
|
id=notification.id,
|
||||||
code=ui_pb2.ERROR,
|
code=ui_pb2.ERROR,
|
||||||
|
@ -294,6 +295,7 @@ class Nodes(QObject):
|
||||||
print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
|
print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
|
||||||
if callback_signal != None:
|
if callback_signal != None:
|
||||||
callback_signal.emit(
|
callback_signal.emit(
|
||||||
|
addr,
|
||||||
ui_pb2.NotificationReply(
|
ui_pb2.NotificationReply(
|
||||||
id=notification.id,
|
id=notification.id,
|
||||||
code=ui_pb2.ERROR,
|
code=ui_pb2.ERROR,
|
||||||
|
@ -336,7 +338,7 @@ class Nodes(QObject):
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._notifications_sent[reply.id]['callback'] != None:
|
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
|
# delete only one-time notifications
|
||||||
# we need the ID of streaming notifications from the server
|
# we need the ID of streaming notifications from the server
|
||||||
|
|
|
@ -560,7 +560,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>336</width>
|
<width>314</width>
|
||||||
<height>404</height>
|
<height>404</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1518,6 +1518,313 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<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)
|
_add_alert_trigger = QtCore.pyqtSignal(str, str, ui_pb2.Alert)
|
||||||
_version_warning_trigger = QtCore.pyqtSignal(str, str)
|
_version_warning_trigger = QtCore.pyqtSignal(str, str)
|
||||||
_status_change_trigger = QtCore.pyqtSignal(bool)
|
_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)
|
_show_message_trigger = QtCore.pyqtSignal(str, str, int, int)
|
||||||
|
|
||||||
# .desktop filename located under /usr/share/applications/
|
# .desktop filename located under /usr/share/applications/
|
||||||
|
@ -401,8 +401,8 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
|
||||||
else:
|
else:
|
||||||
self._tray.setIcon(self.off_icon)
|
self._tray.setIcon(self.off_icon)
|
||||||
|
|
||||||
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
|
@QtCore.pyqtSlot(str, ui_pb2.NotificationReply)
|
||||||
def _on_notification_reply(self, reply):
|
def _on_notification_reply(self, addr, reply):
|
||||||
if reply.code == ui_pb2.ERROR:
|
if reply.code == ui_pb2.ERROR:
|
||||||
self._tray.showMessage("Error",
|
self._tray.showMessage("Error",
|
||||||
reply.data,
|
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