diff --git a/Makefile b/Makefile index 1ef01fba..26d6ab90 100644 --- a/Makefile +++ b/Makefile @@ -18,5 +18,5 @@ test: mkdir rules make clear - xterm -e "python ui/main.py ; read" & + python ui/main.py & sudo ./daemon/daemon diff --git a/daemon/main.go b/daemon/main.go index 36f039c9..9d05e159 100644 --- a/daemon/main.go +++ b/daemon/main.go @@ -7,7 +7,6 @@ import ( "os" "os/signal" "syscall" - "time" "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/core" @@ -85,15 +84,6 @@ func setupWorkers() { } } -func setupStats() { - go func() { - t := time.NewTicker(time.Second * 30) - for _ = range t.C { - stats.Log() - } - }() -} - func doCleanup() { log.Info("Cleaning up ...") firewall.QueueDNSResponses(false, queueNum) @@ -214,7 +204,6 @@ func main() { setupSignals() setupWorkers() - setupStats() // prepare the queue queue, err := netfilter.NewNFQueue(uint16(queueNum), 4096, netfilter.NF_DEFAULT_PACKET_SIZE) @@ -236,7 +225,7 @@ func main() { if err := rules.Load(rulesPath); err != nil { log.Fatal("%s", err) } - uiClient = ui.NewClient(uiSocketPath) + uiClient = ui.NewClient(uiSocketPath, stats) log.Info("Running on netfilter queue #%d ...", queueNum) for true { diff --git a/daemon/statistics/stats.go b/daemon/statistics/stats.go index cb7572b7..b2ad5d31 100644 --- a/daemon/statistics/stats.go +++ b/daemon/statistics/stats.go @@ -21,23 +21,23 @@ type Statistics struct { Dropped int RuleHits int RuleMisses int - ByProto map[string]int - ByAddress map[string]int - ByHost map[string]int - ByPort map[string]int - ByUID map[string]int - ByExecutable map[string]int + ByProto map[string]uint64 + ByAddress map[string]uint64 + ByHost map[string]uint64 + ByPort map[string]uint64 + ByUID map[string]uint64 + ByExecutable map[string]uint64 } func New() *Statistics { return &Statistics{ Started: time.Now(), - ByProto: make(map[string]int), - ByAddress: make(map[string]int), - ByHost: make(map[string]int), - ByPort: make(map[string]int), - ByUID: make(map[string]int), - ByExecutable: make(map[string]int), + ByProto: make(map[string]uint64), + ByAddress: make(map[string]uint64), + ByHost: make(map[string]uint64), + ByPort: make(map[string]uint64), + ByUID: make(map[string]uint64), + ByExecutable: make(map[string]uint64), } } @@ -55,7 +55,7 @@ func (s *Statistics) OnIgnored() { s.Accepted++ } -func (s *Statistics) incMap(m *map[string]int, key string) { +func (s *Statistics) incMap(m *map[string]uint64, key string) { if val, found := (*m)[key]; found == false { (*m)[key] = 1 } else { @@ -103,13 +103,13 @@ func (s *Statistics) OnDrop() { s.Dropped++ } -func (s *Statistics) logMap(m *map[string]int, name string) { +func (s *Statistics) logMap(m *map[string]uint64, name string) { log.Raw("%s\n", name) log.Raw("----\n") type kv struct { Key string - Value int + Value uint64 } var padLen int diff --git a/daemon/ui/client.go b/daemon/ui/client.go index 9558a72e..6a4f2cdc 100644 --- a/daemon/ui/client.go +++ b/daemon/ui/client.go @@ -9,6 +9,7 @@ import ( "github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/rule" + "github.com/evilsocket/opensnitch/daemon/statistics" protocol "github.com/evilsocket/opensnitch/proto" @@ -29,14 +30,16 @@ var clientErrorRule = rule.Create("ui.client.error", rule.Allow, rule.Once, rule type Client struct { sync.Mutex + stats *statistics.Statistics socketPath string con *grpc.ClientConn client protocol.UIClient } -func NewClient(path string) *Client { +func NewClient(path string, stats *statistics.Statistics) *Client { c := &Client{ socketPath: path, + stats: stats, } go c.poller() return c @@ -92,7 +95,30 @@ func (c *Client) ping(ts time.Time) (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() reqId := uint64(ts.UnixNano()) - pong, err := c.client.Ping(ctx, &protocol.PingRequest{Id: reqId}) + + c.stats.Lock() + defer c.stats.Unlock() + + pong, err := c.client.Ping(ctx, &protocol.PingRequest{ + Id: reqId, + Stats: &protocol.Statistics{ + Uptime: uint64(time.Since(c.stats.Started).Seconds()), + DnsResponses: uint64(c.stats.DNSResponses), + Connections: uint64(c.stats.Connections), + Ignored: uint64(c.stats.Ignored), + Accepted: uint64(c.stats.Accepted), + Dropped: uint64(c.stats.Dropped), + RuleHits: uint64(c.stats.RuleHits), + RuleMisses: uint64(c.stats.RuleMisses), + ByProto: c.stats.ByProto, + ByAddress: c.stats.ByAddress, + ByHost: c.stats.ByHost, + ByPort: c.stats.ByPort, + ByUid: c.stats.ByUID, + ByExecutable: c.stats.ByExecutable, + }, + }) + if err != nil { return err } diff --git a/proto/ui.pb.go b/proto/ui.pb.go index d320d2f3..f06a956b 100644 --- a/proto/ui.pb.go +++ b/proto/ui.pb.go @@ -8,6 +8,7 @@ It is generated from these files: ui.proto It has these top-level messages: + Statistics PingRequest PingReply RuleRequest @@ -35,14 +36,135 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +type Statistics struct { + Uptime uint64 `protobuf:"varint,1,opt,name=uptime" json:"uptime,omitempty"` + DnsResponses uint64 `protobuf:"varint,2,opt,name=dns_responses,json=dnsResponses" json:"dns_responses,omitempty"` + Connections uint64 `protobuf:"varint,3,opt,name=connections" json:"connections,omitempty"` + Ignored uint64 `protobuf:"varint,4,opt,name=ignored" json:"ignored,omitempty"` + Accepted uint64 `protobuf:"varint,5,opt,name=accepted" json:"accepted,omitempty"` + Dropped uint64 `protobuf:"varint,6,opt,name=dropped" json:"dropped,omitempty"` + RuleHits uint64 `protobuf:"varint,7,opt,name=rule_hits,json=ruleHits" json:"rule_hits,omitempty"` + RuleMisses uint64 `protobuf:"varint,8,opt,name=rule_misses,json=ruleMisses" json:"rule_misses,omitempty"` + ByProto map[string]uint64 `protobuf:"bytes,9,rep,name=by_proto,json=byProto" json:"by_proto,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + ByAddress map[string]uint64 `protobuf:"bytes,10,rep,name=by_address,json=byAddress" json:"by_address,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + ByHost map[string]uint64 `protobuf:"bytes,11,rep,name=by_host,json=byHost" json:"by_host,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + ByPort map[string]uint64 `protobuf:"bytes,12,rep,name=by_port,json=byPort" json:"by_port,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + ByUid map[string]uint64 `protobuf:"bytes,13,rep,name=by_uid,json=byUid" json:"by_uid,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + ByExecutable map[string]uint64 `protobuf:"bytes,14,rep,name=by_executable,json=byExecutable" json:"by_executable,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` +} + +func (m *Statistics) Reset() { *m = Statistics{} } +func (m *Statistics) String() string { return proto.CompactTextString(m) } +func (*Statistics) ProtoMessage() {} +func (*Statistics) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Statistics) GetUptime() uint64 { + if m != nil { + return m.Uptime + } + return 0 +} + +func (m *Statistics) GetDnsResponses() uint64 { + if m != nil { + return m.DnsResponses + } + return 0 +} + +func (m *Statistics) GetConnections() uint64 { + if m != nil { + return m.Connections + } + return 0 +} + +func (m *Statistics) GetIgnored() uint64 { + if m != nil { + return m.Ignored + } + return 0 +} + +func (m *Statistics) GetAccepted() uint64 { + if m != nil { + return m.Accepted + } + return 0 +} + +func (m *Statistics) GetDropped() uint64 { + if m != nil { + return m.Dropped + } + return 0 +} + +func (m *Statistics) GetRuleHits() uint64 { + if m != nil { + return m.RuleHits + } + return 0 +} + +func (m *Statistics) GetRuleMisses() uint64 { + if m != nil { + return m.RuleMisses + } + return 0 +} + +func (m *Statistics) GetByProto() map[string]uint64 { + if m != nil { + return m.ByProto + } + return nil +} + +func (m *Statistics) GetByAddress() map[string]uint64 { + if m != nil { + return m.ByAddress + } + return nil +} + +func (m *Statistics) GetByHost() map[string]uint64 { + if m != nil { + return m.ByHost + } + return nil +} + +func (m *Statistics) GetByPort() map[string]uint64 { + if m != nil { + return m.ByPort + } + return nil +} + +func (m *Statistics) GetByUid() map[string]uint64 { + if m != nil { + return m.ByUid + } + return nil +} + +func (m *Statistics) GetByExecutable() map[string]uint64 { + if m != nil { + return m.ByExecutable + } + return nil +} + type PingRequest struct { - Id uint64 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` + Id uint64 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` + Stats *Statistics `protobuf:"bytes,2,opt,name=stats" json:"stats,omitempty"` } func (m *PingRequest) Reset() { *m = PingRequest{} } func (m *PingRequest) String() string { return proto.CompactTextString(m) } func (*PingRequest) ProtoMessage() {} -func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } func (m *PingRequest) GetId() uint64 { if m != nil { @@ -51,6 +173,13 @@ func (m *PingRequest) GetId() uint64 { return 0 } +func (m *PingRequest) GetStats() *Statistics { + if m != nil { + return m.Stats + } + return nil +} + type PingReply struct { Id uint64 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` } @@ -58,7 +187,7 @@ type PingReply struct { func (m *PingReply) Reset() { *m = PingReply{} } func (m *PingReply) String() string { return proto.CompactTextString(m) } func (*PingReply) ProtoMessage() {} -func (*PingReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (*PingReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func (m *PingReply) GetId() uint64 { if m != nil { @@ -83,7 +212,7 @@ type RuleRequest struct { func (m *RuleRequest) Reset() { *m = RuleRequest{} } func (m *RuleRequest) String() string { return proto.CompactTextString(m) } func (*RuleRequest) ProtoMessage() {} -func (*RuleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*RuleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } func (m *RuleRequest) GetProtocol() string { if m != nil { @@ -166,7 +295,7 @@ type RuleReply struct { func (m *RuleReply) Reset() { *m = RuleReply{} } func (m *RuleReply) String() string { return proto.CompactTextString(m) } func (*RuleReply) ProtoMessage() {} -func (*RuleReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +func (*RuleReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } func (m *RuleReply) GetName() string { if m != nil { @@ -204,6 +333,7 @@ func (m *RuleReply) GetValue() string { } func init() { + proto.RegisterType((*Statistics)(nil), "ui.Statistics") proto.RegisterType((*PingRequest)(nil), "ui.PingRequest") proto.RegisterType((*PingReply)(nil), "ui.PingReply") proto.RegisterType((*RuleRequest)(nil), "ui.RuleRequest") @@ -318,28 +448,50 @@ var _UI_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("ui.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 362 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0x4f, 0x4f, 0xe3, 0x30, - 0x10, 0xc5, 0xdb, 0x34, 0xcd, 0x9f, 0xe9, 0x76, 0x57, 0xb2, 0x76, 0x97, 0x50, 0x54, 0xa9, 0xe4, - 0x54, 0x09, 0xa9, 0x07, 0xf8, 0x04, 0xbd, 0x91, 0x5b, 0x15, 0x89, 0x13, 0x87, 0xca, 0xc4, 0x51, - 0x63, 0x11, 0x6a, 0xe3, 0xb1, 0x41, 0x3d, 0xf0, 0x75, 0xf8, 0x9c, 0xc8, 0x76, 0x52, 0x22, 0x71, - 0xf3, 0xcc, 0xef, 0xbd, 0xd7, 0xe6, 0x0d, 0x24, 0x86, 0x6f, 0xa4, 0x12, 0x5a, 0x90, 0xc0, 0xf0, - 0x7c, 0x09, 0xb3, 0x1d, 0x3f, 0x1e, 0xca, 0xfa, 0xd5, 0xd4, 0xa8, 0xc9, 0x6f, 0x08, 0x38, 0xcb, - 0xc6, 0xab, 0xf1, 0x3a, 0x2c, 0x03, 0xce, 0xf2, 0x2b, 0x48, 0x3d, 0x96, 0xed, 0xe9, 0x07, 0xfc, - 0x0c, 0x60, 0x56, 0x9a, 0xb6, 0xee, 0xcd, 0x0b, 0x48, 0x5c, 0x70, 0x25, 0x5a, 0xa7, 0x4a, 0xcb, - 0xf3, 0x4c, 0xfe, 0x41, 0x84, 0xaa, 0xda, 0x73, 0x99, 0x05, 0x8e, 0x4c, 0x51, 0x55, 0x85, 0x24, - 0x97, 0x90, 0xd8, 0xb5, 0x14, 0x4a, 0x67, 0x93, 0xd5, 0x78, 0x3d, 0x2f, 0x63, 0x54, 0xd5, 0x4e, - 0x28, 0x6d, 0x1d, 0x0c, 0xb5, 0x75, 0x84, 0xde, 0xc1, 0x50, 0x7b, 0x87, 0x5d, 0x37, 0x02, 0x75, - 0x36, 0x75, 0x20, 0x66, 0xa8, 0xef, 0x05, 0xea, 0x1e, 0xb9, 0xb0, 0xc8, 0x87, 0x31, 0xd4, 0x2e, - 0xec, 0x02, 0x62, 0x83, 0xb5, 0xda, 0x73, 0x96, 0xc5, 0x8e, 0x44, 0x76, 0x2c, 0x18, 0x59, 0x02, - 0x48, 0x25, 0xaa, 0x1a, 0xd1, 0xb2, 0xc4, 0xb1, 0xb4, 0xdb, 0x14, 0x8c, 0x5c, 0xc3, 0xaf, 0x1e, - 0x4b, 0xaa, 0x9b, 0x2c, 0x75, 0xbf, 0x38, 0xeb, 0x76, 0x3b, 0xaa, 0x9b, 0xa1, 0x84, 0xaa, 0x03, - 0x66, 0xb0, 0x9a, 0x0c, 0x24, 0x5b, 0x75, 0xc0, 0xfc, 0x03, 0x52, 0xdf, 0x93, 0x6d, 0x91, 0x40, - 0x78, 0xa4, 0x2f, 0x75, 0xd7, 0x90, 0x7b, 0x93, 0xff, 0x10, 0xd1, 0x4a, 0x73, 0x71, 0xec, 0xda, - 0xe9, 0x26, 0xdb, 0x28, 0x33, 0x8a, 0x3a, 0x32, 0xf1, 0x8d, 0xf6, 0xb3, 0xcd, 0x79, 0x6f, 0xa8, - 0xee, 0xda, 0x71, 0x6f, 0xf2, 0x17, 0xa6, 0x6f, 0xb4, 0x35, 0x75, 0xd7, 0x8c, 0x1f, 0x6e, 0x1f, - 0x21, 0x78, 0x28, 0xc8, 0x1a, 0x42, 0x7b, 0x4a, 0xf2, 0x67, 0x63, 0xf8, 0x66, 0x70, 0xf3, 0xc5, - 0xfc, 0x7b, 0x21, 0xdb, 0x53, 0x3e, 0x22, 0x37, 0x10, 0x6f, 0xf1, 0xd9, 0xfe, 0x63, 0x2f, 0x1e, - 0xdc, 0xd8, 0x8b, 0xcf, 0x1f, 0x93, 0x8f, 0x9e, 0x22, 0x77, 0xe2, 0xbb, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x46, 0x06, 0x72, 0xda, 0x57, 0x02, 0x00, 0x00, + // 705 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0x86, 0xdb, 0x5c, 0xec, 0xf8, 0x38, 0x09, 0x30, 0xa2, 0xe0, 0xa6, 0xaa, 0x08, 0x81, 0x45, + 0x25, 0xa4, 0x08, 0xb5, 0x12, 0x2a, 0x55, 0x25, 0x54, 0x50, 0xa5, 0x66, 0x81, 0x14, 0x19, 0x75, + 0xc5, 0xc2, 0xb2, 0x3d, 0xa3, 0x64, 0xd4, 0xd4, 0x36, 0x73, 0xc6, 0x80, 0x17, 0xbc, 0x0e, 0x6f, + 0xc1, 0xbb, 0xa1, 0x39, 0xe3, 0x5c, 0x4a, 0xc4, 0x22, 0xbb, 0xf9, 0xcf, 0x3f, 0xdf, 0xf1, 0xf8, + 0x9f, 0x0b, 0x74, 0x4a, 0x39, 0x2e, 0x54, 0xae, 0x73, 0xd6, 0x28, 0xe5, 0xe8, 0x8f, 0x0b, 0xf0, + 0x45, 0xc7, 0x5a, 0xa2, 0x96, 0x29, 0xb2, 0x67, 0xe0, 0x94, 0x85, 0x96, 0xf7, 0x22, 0xd8, 0x1f, + 0xee, 0x9f, 0xb4, 0xc2, 0x5a, 0xb1, 0x57, 0xd0, 0xe3, 0x19, 0x46, 0x4a, 0x60, 0x91, 0x67, 0x28, + 0x30, 0x68, 0x90, 0xdd, 0xe5, 0x19, 0x86, 0xcb, 0x1a, 0x1b, 0x82, 0x9f, 0xe6, 0x59, 0x26, 0x52, + 0x2d, 0xf3, 0x0c, 0x83, 0x26, 0x4d, 0xd9, 0x2c, 0xb1, 0x00, 0x5c, 0x39, 0xcb, 0x72, 0x25, 0x78, + 0xd0, 0x22, 0x77, 0x29, 0xd9, 0x00, 0x3a, 0x71, 0x9a, 0x8a, 0x42, 0x0b, 0x1e, 0xb4, 0xc9, 0x5a, + 0x69, 0x43, 0x71, 0x95, 0x17, 0x85, 0xe0, 0x81, 0x63, 0xa9, 0x5a, 0xb2, 0x23, 0xf0, 0x54, 0xb9, + 0x10, 0xd1, 0x5c, 0x6a, 0x0c, 0x5c, 0x8b, 0x99, 0xc2, 0x8d, 0xd4, 0xc8, 0x5e, 0x80, 0x4f, 0xe6, + 0xbd, 0x44, 0xb3, 0xe2, 0x0e, 0xd9, 0x60, 0x4a, 0x9f, 0xa9, 0xc2, 0xde, 0x41, 0x27, 0xa9, 0x22, + 0xca, 0x22, 0xf0, 0x86, 0xcd, 0x13, 0xff, 0xf4, 0x68, 0x5c, 0xca, 0xf1, 0x3a, 0x8e, 0xf1, 0xc7, + 0x6a, 0x6a, 0xdc, 0xeb, 0x4c, 0xab, 0x2a, 0x74, 0x13, 0xab, 0xd8, 0x25, 0x40, 0x52, 0x45, 0x31, + 0xe7, 0x4a, 0x20, 0x06, 0x40, 0xe4, 0xf1, 0x16, 0x79, 0x65, 0x7d, 0xcb, 0x7a, 0xc9, 0x52, 0xb3, + 0x33, 0x70, 0x93, 0x2a, 0x9a, 0xe7, 0xa8, 0x03, 0x9f, 0xd0, 0xc1, 0x16, 0x7a, 0x93, 0xa3, 0xb6, + 0x9c, 0x93, 0x90, 0xa8, 0xa1, 0x22, 0x57, 0x3a, 0xe8, 0xfe, 0x07, 0x9a, 0xe6, 0x6a, 0x0d, 0x19, + 0xc1, 0xde, 0x82, 0x93, 0x54, 0x51, 0x29, 0x79, 0xd0, 0x23, 0xe6, 0x70, 0x8b, 0xb9, 0x95, 0xdc, + 0x22, 0xed, 0xc4, 0x8c, 0xd9, 0x35, 0xf4, 0x92, 0x2a, 0x12, 0x3f, 0x45, 0x5a, 0xea, 0x38, 0x59, + 0x88, 0xa0, 0x4f, 0xe0, 0x70, 0x0b, 0xbc, 0x5e, 0x4d, 0xb1, 0x7c, 0x37, 0xd9, 0x28, 0x0d, 0x2e, + 0xa0, 0xbb, 0x99, 0x1c, 0x7b, 0x0c, 0xcd, 0x3b, 0x51, 0xd1, 0x91, 0xf2, 0x42, 0x33, 0x64, 0x4f, + 0xa1, 0xfd, 0x3d, 0x5e, 0x94, 0xa2, 0x3e, 0x47, 0x56, 0x5c, 0x34, 0xce, 0xf7, 0x07, 0x97, 0xd0, + 0x7f, 0x98, 0xdd, 0x4e, 0xf4, 0x7b, 0xf0, 0x37, 0xe2, 0xdb, 0x1d, 0x5d, 0x85, 0xb8, 0x13, 0x7a, + 0x0e, 0xb0, 0xce, 0x72, 0x27, 0xf2, 0x03, 0x3c, 0xd9, 0x0a, 0x73, 0x97, 0x06, 0xa3, 0x4f, 0xe0, + 0x4f, 0x65, 0x36, 0x0b, 0xc5, 0xb7, 0x52, 0xa0, 0x66, 0x7d, 0x68, 0x48, 0x5e, 0xdf, 0xdd, 0x86, + 0xe4, 0xec, 0x35, 0xb4, 0x51, 0xc7, 0xda, 0xde, 0x57, 0xff, 0xb4, 0xff, 0x70, 0x23, 0x43, 0x6b, + 0x8e, 0x8e, 0xc0, 0xb3, 0x4d, 0x8a, 0x45, 0xf5, 0x6f, 0x8b, 0xd1, 0xef, 0x06, 0xf8, 0x61, 0xb9, + 0x10, 0xcb, 0x4f, 0x0c, 0xa0, 0x43, 0x57, 0x26, 0xcd, 0x17, 0xf5, 0x12, 0x57, 0x9a, 0x1d, 0x80, + 0x83, 0x2a, 0x8d, 0x64, 0x41, 0xdf, 0xf3, 0xc2, 0x36, 0xaa, 0x74, 0x52, 0xb0, 0x43, 0xe8, 0x98, + 0x32, 0x1d, 0x5f, 0xf3, 0x2a, 0xf4, 0x42, 0x17, 0x55, 0x4a, 0x67, 0xf4, 0x00, 0x1c, 0x8e, 0xda, + 0x10, 0x2d, 0x4b, 0x70, 0xd4, 0x96, 0x30, 0x65, 0xba, 0x25, 0x6d, 0x32, 0x5c, 0x8e, 0x9a, 0xae, + 0x42, 0x6d, 0x51, 0x33, 0xc7, 0x36, 0xe3, 0xa8, 0xa9, 0xd9, 0x73, 0x70, 0x4b, 0x14, 0x2a, 0x92, + 0x9c, 0x1e, 0x83, 0x5e, 0xe8, 0x18, 0x39, 0xe1, 0xec, 0x18, 0xa0, 0x50, 0x79, 0x2a, 0x10, 0x8d, + 0xd7, 0x21, 0xcf, 0xab, 0x2b, 0x13, 0xce, 0x5e, 0x42, 0x77, 0x69, 0x17, 0xb1, 0x9e, 0x07, 0x1e, + 0x7d, 0xd1, 0xaf, 0x6b, 0xd3, 0x58, 0xcf, 0x37, 0xa7, 0xc4, 0x6a, 0x66, 0x6f, 0xfd, 0x7a, 0xca, + 0x95, 0x9a, 0xe1, 0xe8, 0x17, 0x78, 0x36, 0x27, 0x93, 0x22, 0x83, 0x56, 0x16, 0xd7, 0xcf, 0xa8, + 0x17, 0xd2, 0xd8, 0x3c, 0xae, 0x31, 0x3d, 0x84, 0x75, 0x3a, 0xb5, 0x32, 0x89, 0xf2, 0x52, 0xc5, + 0xe4, 0x34, 0x6d, 0xa2, 0x4b, 0x6d, 0xfa, 0xfc, 0x98, 0xc7, 0xba, 0x4e, 0x87, 0xc6, 0xeb, 0xd3, + 0x60, 0x93, 0xb1, 0xe2, 0xf4, 0x2b, 0x34, 0x6e, 0x27, 0xec, 0x04, 0x5a, 0x66, 0x2b, 0xd9, 0x23, + 0xb3, 0xd3, 0x1b, 0x27, 0x63, 0xd0, 0x5b, 0x17, 0x8a, 0x45, 0x35, 0xda, 0x63, 0x6f, 0xc0, 0xbd, + 0xc2, 0x3b, 0xb3, 0x62, 0x3b, 0x79, 0x63, 0x8f, 0xed, 0xe4, 0xd5, 0xcf, 0x8c, 0xf6, 0x12, 0x87, + 0xb6, 0xf8, 0xec, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0xb1, 0xc4, 0x6d, 0x3d, 0x06, 0x00, + 0x00, } diff --git a/proto/ui.proto b/proto/ui.proto index aaf91a1d..94ffd216 100644 --- a/proto/ui.proto +++ b/proto/ui.proto @@ -7,8 +7,26 @@ service UI { rpc AskRule (RuleRequest) returns (RuleReply) {} } +message Statistics { + uint64 uptime = 1; + uint64 dns_responses = 2; + uint64 connections = 3; + uint64 ignored = 4; + uint64 accepted = 5; + uint64 dropped = 6; + uint64 rule_hits = 7; + uint64 rule_misses = 8; + map by_proto = 9; + map by_address = 10; + map by_host = 11; + map by_port = 12; + map by_uid = 13; + map by_executable = 14; +} + message PingRequest { uint64 id = 1; + Statistics stats = 2; } message PingReply { diff --git a/proto/ui_pb2.py b/proto/ui_pb2.py index 1fcbc1e5..354d8497 100644 --- a/proto/ui_pb2.py +++ b/proto/ui_pb2.py @@ -19,12 +19,356 @@ DESCRIPTOR = _descriptor.FileDescriptor( name='ui.proto', package='ui', syntax='proto3', - serialized_pb=_b('\n\x08ui.proto\x12\x02ui\"\x19\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc6\x01\n\x0bRuleRequest\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x14\n\x0cprocess_args\x18\n \x03(\t\"X\n\tRuleReply\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x10\n\x08\x64uration\x18\x03 \x01(\t\x12\x0c\n\x04what\x18\x04 \x01(\t\x12\r\n\x05value\x18\x05 \x01(\t2[\n\x02UI\x12(\n\x04Ping\x12\x0f.ui.PingRequest\x1a\r.ui.PingReply\"\x00\x12+\n\x07\x41skRule\x12\x0f.ui.RuleRequest\x1a\r.ui.RuleReply\"\x00\x62\x06proto3') + serialized_pb=_b('\n\x08ui.proto\x12\x02ui\"\xe7\x05\n\nStatistics\x12\x0e\n\x06uptime\x18\x01 \x01(\x04\x12\x15\n\rdns_responses\x18\x02 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x03 \x01(\x04\x12\x0f\n\x07ignored\x18\x04 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x05 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x06 \x01(\x04\x12\x11\n\trule_hits\x18\x07 \x01(\x04\x12\x13\n\x0brule_misses\x18\x08 \x01(\x04\x12-\n\x08\x62y_proto\x18\t \x03(\x0b\x32\x1b.ui.Statistics.ByProtoEntry\x12\x31\n\nby_address\x18\n \x03(\x0b\x32\x1d.ui.Statistics.ByAddressEntry\x12+\n\x07\x62y_host\x18\x0b \x03(\x0b\x32\x1a.ui.Statistics.ByHostEntry\x12+\n\x07\x62y_port\x18\x0c \x03(\x0b\x32\x1a.ui.Statistics.ByPortEntry\x12)\n\x06\x62y_uid\x18\r \x03(\x0b\x32\x19.ui.Statistics.ByUidEntry\x12\x37\n\rby_executable\x18\x0e \x03(\x0b\x32 .ui.Statistics.ByExecutableEntry\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\"8\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x1d\n\x05stats\x18\x02 \x01(\x0b\x32\x0e.ui.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc6\x01\n\x0bRuleRequest\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x14\n\x0cprocess_args\x18\n \x03(\t\"X\n\tRuleReply\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x10\n\x08\x64uration\x18\x03 \x01(\t\x12\x0c\n\x04what\x18\x04 \x01(\t\x12\r\n\x05value\x18\x05 \x01(\t2[\n\x02UI\x12(\n\x04Ping\x12\x0f.ui.PingRequest\x1a\r.ui.PingReply\"\x00\x12+\n\x07\x41skRule\x12\x0f.ui.RuleRequest\x1a\r.ui.RuleReply\"\x00\x62\x06proto3') ) +_STATISTICS_BYPROTOENTRY = _descriptor.Descriptor( + name='ByProtoEntry', + full_name='ui.Statistics.ByProtoEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByProtoEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByProtoEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=471, + serialized_end=517, +) + +_STATISTICS_BYADDRESSENTRY = _descriptor.Descriptor( + name='ByAddressEntry', + full_name='ui.Statistics.ByAddressEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByAddressEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByAddressEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=519, + serialized_end=567, +) + +_STATISTICS_BYHOSTENTRY = _descriptor.Descriptor( + name='ByHostEntry', + full_name='ui.Statistics.ByHostEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByHostEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByHostEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=569, + serialized_end=614, +) + +_STATISTICS_BYPORTENTRY = _descriptor.Descriptor( + name='ByPortEntry', + full_name='ui.Statistics.ByPortEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByPortEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByPortEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=616, + serialized_end=661, +) + +_STATISTICS_BYUIDENTRY = _descriptor.Descriptor( + name='ByUidEntry', + full_name='ui.Statistics.ByUidEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByUidEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByUidEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=663, + serialized_end=707, +) + +_STATISTICS_BYEXECUTABLEENTRY = _descriptor.Descriptor( + name='ByExecutableEntry', + full_name='ui.Statistics.ByExecutableEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='ui.Statistics.ByExecutableEntry.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='ui.Statistics.ByExecutableEntry.value', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=709, + serialized_end=760, +) + +_STATISTICS = _descriptor.Descriptor( + name='Statistics', + full_name='ui.Statistics', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='uptime', full_name='ui.Statistics.uptime', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dns_responses', full_name='ui.Statistics.dns_responses', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='connections', full_name='ui.Statistics.connections', index=2, + number=3, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ignored', full_name='ui.Statistics.ignored', index=3, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='accepted', full_name='ui.Statistics.accepted', index=4, + number=5, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dropped', full_name='ui.Statistics.dropped', index=5, + number=6, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule_hits', full_name='ui.Statistics.rule_hits', index=6, + number=7, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rule_misses', full_name='ui.Statistics.rule_misses', index=7, + number=8, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_proto', full_name='ui.Statistics.by_proto', index=8, + number=9, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_address', full_name='ui.Statistics.by_address', index=9, + number=10, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_host', full_name='ui.Statistics.by_host', index=10, + number=11, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_port', full_name='ui.Statistics.by_port', index=11, + number=12, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_uid', full_name='ui.Statistics.by_uid', index=12, + number=13, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='by_executable', full_name='ui.Statistics.by_executable', index=13, + number=14, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[_STATISTICS_BYPROTOENTRY, _STATISTICS_BYADDRESSENTRY, _STATISTICS_BYHOSTENTRY, _STATISTICS_BYPORTENTRY, _STATISTICS_BYUIDENTRY, _STATISTICS_BYEXECUTABLEENTRY, ], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=17, + serialized_end=760, +) + + _PINGREQUEST = _descriptor.Descriptor( name='PingRequest', full_name='ui.PingRequest', @@ -39,6 +383,13 @@ _PINGREQUEST = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='stats', full_name='ui.PingRequest.stats', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -51,8 +402,8 @@ _PINGREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=16, - serialized_end=41, + serialized_start=762, + serialized_end=818, ) @@ -82,8 +433,8 @@ _PINGREPLY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=43, - serialized_end=66, + serialized_start=820, + serialized_end=843, ) @@ -176,8 +527,8 @@ _RULEREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=69, - serialized_end=267, + serialized_start=846, + serialized_end=1044, ) @@ -235,16 +586,85 @@ _RULEREPLY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=269, - serialized_end=357, + serialized_start=1046, + serialized_end=1134, ) +_STATISTICS_BYPROTOENTRY.containing_type = _STATISTICS +_STATISTICS_BYADDRESSENTRY.containing_type = _STATISTICS +_STATISTICS_BYHOSTENTRY.containing_type = _STATISTICS +_STATISTICS_BYPORTENTRY.containing_type = _STATISTICS +_STATISTICS_BYUIDENTRY.containing_type = _STATISTICS +_STATISTICS_BYEXECUTABLEENTRY.containing_type = _STATISTICS +_STATISTICS.fields_by_name['by_proto'].message_type = _STATISTICS_BYPROTOENTRY +_STATISTICS.fields_by_name['by_address'].message_type = _STATISTICS_BYADDRESSENTRY +_STATISTICS.fields_by_name['by_host'].message_type = _STATISTICS_BYHOSTENTRY +_STATISTICS.fields_by_name['by_port'].message_type = _STATISTICS_BYPORTENTRY +_STATISTICS.fields_by_name['by_uid'].message_type = _STATISTICS_BYUIDENTRY +_STATISTICS.fields_by_name['by_executable'].message_type = _STATISTICS_BYEXECUTABLEENTRY +_PINGREQUEST.fields_by_name['stats'].message_type = _STATISTICS +DESCRIPTOR.message_types_by_name['Statistics'] = _STATISTICS DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST DESCRIPTOR.message_types_by_name['PingReply'] = _PINGREPLY DESCRIPTOR.message_types_by_name['RuleRequest'] = _RULEREQUEST DESCRIPTOR.message_types_by_name['RuleReply'] = _RULEREPLY _sym_db.RegisterFileDescriptor(DESCRIPTOR) +Statistics = _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), dict( + + ByProtoEntry = _reflection.GeneratedProtocolMessageType('ByProtoEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYPROTOENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByProtoEntry) + )) + , + + ByAddressEntry = _reflection.GeneratedProtocolMessageType('ByAddressEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYADDRESSENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByAddressEntry) + )) + , + + ByHostEntry = _reflection.GeneratedProtocolMessageType('ByHostEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYHOSTENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByHostEntry) + )) + , + + ByPortEntry = _reflection.GeneratedProtocolMessageType('ByPortEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYPORTENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByPortEntry) + )) + , + + ByUidEntry = _reflection.GeneratedProtocolMessageType('ByUidEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYUIDENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByUidEntry) + )) + , + + ByExecutableEntry = _reflection.GeneratedProtocolMessageType('ByExecutableEntry', (_message.Message,), dict( + DESCRIPTOR = _STATISTICS_BYEXECUTABLEENTRY, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics.ByExecutableEntry) + )) + , + DESCRIPTOR = _STATISTICS, + __module__ = 'ui_pb2' + # @@protoc_insertion_point(class_scope:ui.Statistics) + )) +_sym_db.RegisterMessage(Statistics) +_sym_db.RegisterMessage(Statistics.ByProtoEntry) +_sym_db.RegisterMessage(Statistics.ByAddressEntry) +_sym_db.RegisterMessage(Statistics.ByHostEntry) +_sym_db.RegisterMessage(Statistics.ByPortEntry) +_sym_db.RegisterMessage(Statistics.ByUidEntry) +_sym_db.RegisterMessage(Statistics.ByExecutableEntry) + PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict( DESCRIPTOR = _PINGREQUEST, __module__ = 'ui_pb2' @@ -274,6 +694,18 @@ RuleReply = _reflection.GeneratedProtocolMessageType('RuleReply', (_message.Mess _sym_db.RegisterMessage(RuleReply) +_STATISTICS_BYPROTOENTRY.has_options = True +_STATISTICS_BYPROTOENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_STATISTICS_BYADDRESSENTRY.has_options = True +_STATISTICS_BYADDRESSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_STATISTICS_BYHOSTENTRY.has_options = True +_STATISTICS_BYHOSTENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_STATISTICS_BYPORTENTRY.has_options = True +_STATISTICS_BYPORTENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_STATISTICS_BYUIDENTRY.has_options = True +_STATISTICS_BYUIDENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) +_STATISTICS_BYEXECUTABLEENTRY.has_options = True +_STATISTICS_BYEXECUTABLEENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')) _UI = _descriptor.ServiceDescriptor( name='UI', @@ -281,8 +713,8 @@ _UI = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=0, options=None, - serialized_start=359, - serialized_end=450, + serialized_start=1136, + serialized_end=1227, methods=[ _descriptor.MethodDescriptor( name='Ping', diff --git a/screenshot.png b/screenshot.png index 7d7d8911..8f573b8e 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/ui/dialog.py b/ui/dialog.py index cbe341ce..e1c374bf 100644 --- a/ui/dialog.py +++ b/ui/dialog.py @@ -6,7 +6,6 @@ import os import pwd from PyQt5 import QtCore, QtGui, uic, QtWidgets -from PyQt5 import QtDBus from slugify import slugify diff --git a/ui/main.py b/ui/main.py index b0334743..ee5e400f 100644 --- a/ui/main.py +++ b/ui/main.py @@ -1,4 +1,5 @@ -from PyQt5 import QtWidgets +from PyQt5 import QtWidgets, QtGui + import sys import os import time @@ -14,30 +15,51 @@ from concurrent import futures import ui_pb2 import ui_pb2_grpc -from dialog import Dialog +from service import UIService +from stats_dialog import StatsDialog +from version import version -class UIServicer(ui_pb2_grpc.UIServicer): - def __init__(self): - self.dialog = Dialog() +def on_exit(): + # print "Closing UI" + app.quit() + server.stop(0) + sys.exit(0) - def Ping(self, request, context): - return ui_pb2.PingReply(id=request.id) - - def AskRule(self, request, context): - rule = self.dialog.promptUser(request) - # print "%s -> %s" % ( request, rule ) - return rule +def on_stats(): + stats_dialog.show() if __name__ == '__main__': parser = argparse.ArgumentParser(description='OpenSnitch UI service.') parser.add_argument("--socket", dest="socket", default="opensnitch-ui.sock", help="Path of the unix socket for the gRPC service.", metavar="FILE") args = parser.parse_args() - app = QtWidgets.QApplication(sys.argv) + + white_image = QtGui.QPixmap(os.path.join(path, "res/icon-white.png")) + white_icon = QtGui.QIcon() + white_icon.addPixmap(white_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + + red_image = QtGui.QPixmap(os.path.join(path, "res/icon-red.png")) + red_icon = QtGui.QIcon() + red_icon.addPixmap(red_image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + + menu = QtWidgets.QMenu() + menu.addAction("OpenSnitch v" + version) + menu.addSeparator() + + stats_dialog = StatsDialog() + statsAction = menu.addAction("Statistics") + statsAction.triggered.connect(on_stats) + exitAction = menu.addAction("Close") + exitAction.triggered.connect(on_exit) + + tray = QtWidgets.QSystemTrayIcon(white_icon) + tray.setContextMenu(menu) + tray.show() + server = grpc.server(futures.ThreadPoolExecutor(max_workers=4)) - ui_pb2_grpc.add_UIServicer_to_server(UIServicer(), server) + ui_pb2_grpc.add_UIServicer_to_server(UIService(stats_dialog), server) socket = os.path.abspath(args.socket) server.add_insecure_port("unix:%s" % socket) @@ -46,11 +68,9 @@ if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) try: - print "OpenSnitch UI service running on %s ..." % socket - + # print "OpenSnitch UI service running on %s ..." % socket server.start() app.exec_() except KeyboardInterrupt: - app.quit() - server.stop(0) + on_exit() diff --git a/ui/res/icon-red.png b/ui/res/icon-red.png new file mode 100644 index 00000000..d495251a Binary files /dev/null and b/ui/res/icon-red.png differ diff --git a/ui/res/icon-white.png b/ui/res/icon-white.png new file mode 100644 index 00000000..0e2b578a Binary files /dev/null and b/ui/res/icon-white.png differ diff --git a/ui/res/icon.png b/ui/res/icon.png new file mode 100644 index 00000000..786e9a07 Binary files /dev/null and b/ui/res/icon.png differ diff --git a/ui/res/stats.ui b/ui/res/stats.ui new file mode 100644 index 00000000..27f842fb --- /dev/null +++ b/ui/res/stats.ui @@ -0,0 +1,479 @@ + + + StatsDialog + + + Qt::NonModal + + + + 0 + 0 + 587 + 287 + + + + + 0 + 0 + + + + + 490 + 220 + + + + + true + + + + opensnitch-qt + + + false + + + false + + + + + 0 + 10 + 781 + 281 + + + + 0 + + + + General + + + + + 10 + 0 + 471 + 245 + + + + + QLayout::SetMinimumSize + + + 0 + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Rule hits + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + DNS Responses + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Dropped + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Uptime + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Ignored + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Accepted + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Connections + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + 0 + 0 + + + + + Ubuntu + 11 + 75 + false + true + + + + Rule misses + + + 5 + + + + + + + + 0 + 0 + + + + + 11 + + + + TextLabel + + + 5 + + + + + + + + + Protocols + + + + + Addresses + + + + + Hosts + + + + + Ports + + + + + Users + + + + + Processes + + + + + + + diff --git a/ui/service.py b/ui/service.py new file mode 100644 index 00000000..64a206d8 --- /dev/null +++ b/ui/service.py @@ -0,0 +1,18 @@ +import ui_pb2 +import ui_pb2_grpc + +from dialog import Dialog + +class UIService(ui_pb2_grpc.UIServicer): + def __init__(self, stats_dialog): + self.stats_dialog = stats_dialog + self.dialog = Dialog() + + def Ping(self, request, context): + self.stats_dialog.update(request.stats) + return ui_pb2.PingReply(id=request.id) + + def AskRule(self, request, context): + rule = self.dialog.promptUser(request) + # print "%s -> %s" % ( request, rule ) + return rule diff --git a/ui/stats_dialog.py b/ui/stats_dialog.py new file mode 100644 index 00000000..f752d678 --- /dev/null +++ b/ui/stats_dialog.py @@ -0,0 +1,61 @@ +import threading +import logging +import queue +import datetime +import sys +import os +import pwd + +from PyQt5 import QtCore, QtGui, uic, QtWidgets + +import ui_pb2 + +DIALOG_UI_PATH = "%s/res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__) + +class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]): + _trigger = QtCore.pyqtSignal() + + def __init__(self, parent=None): + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint) + + self.setupUi(self) + + self.setWindowTitle("Statistics") + + self._stats = None + self._trigger.connect(self._on_update_triggered) + + self._uptime_label = self.findChild(QtWidgets.QLabel, "uptimeLabel") + self._dns_label = self.findChild(QtWidgets.QLabel, "dnsLabel") + self._cons_label = self.findChild(QtWidgets.QLabel, "consLabel") + self._ignored_label = self.findChild(QtWidgets.QLabel, "ignoredLabel") + self._accepted_label = self.findChild(QtWidgets.QLabel, "acceptedLabel") + self._dropped_label = self.findChild(QtWidgets.QLabel, "droppedLabel") + self._hits_label = self.findChild(QtWidgets.QLabel, "hitsLabel") + self._misses_label = self.findChild(QtWidgets.QLabel, "missesLabel") + + def update(self, stats): + self._stats = stats + self._trigger.emit() + + @QtCore.pyqtSlot() + def _on_update_triggered(self): + self._uptime_label.setText(str(datetime.timedelta(seconds=self._stats.uptime))) + self._dns_label.setText("%s" % self._stats.dns_responses) + self._cons_label.setText("%s" % self._stats.connections) + self._ignored_label.setText("%s" % self._stats.ignored) + self._accepted_label.setText("%s" % self._stats.accepted) + self._dropped_label.setText("%s" % self._stats.dropped) + self._hits_label.setText("%s" % self._stats.rule_hits) + self._misses_label.setText("%s" % self._stats.rule_misses) + + # prevent a click on the window's x + # from quitting the whole application + def closeEvent(self, e): + e.ignore() + self.hide() + + # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog + def keyPressEvent(self, event): + if not event.key() == QtCore.Qt.Key_Escape: + super(StatsDialog, self).keyPressEvent(event)