Restructure modules (#1572)
* Move portbase into monorepo * Add new simple module mgr * [WIP] Switch to new simple module mgr * Add StateMgr and more worker variants * [WIP] Switch more modules * [WIP] Switch more modules * [WIP] swtich more modules * [WIP] switch all SPN modules * [WIP] switch all service modules * [WIP] Convert all workers to the new module system * [WIP] add new task system to module manager * [WIP] Add second take for scheduling workers * [WIP] Add FIXME for bugs in new scheduler * [WIP] Add minor improvements to scheduler * [WIP] Add new worker scheduler * [WIP] Fix more bug related to new module system * [WIP] Fix start handing of the new module system * [WIP] Improve startup process * [WIP] Fix minor issues * [WIP] Fix missing subsystem in settings * [WIP] Initialize managers in constructor * [WIP] Move module event initialization to constrictors * [WIP] Fix setting for enabling and disabling the SPN module * [WIP] Move API registeration into module construction * [WIP] Update states mgr for all modules * [WIP] Add CmdLine operation support * Add state helper methods to module group and instance * Add notification and module status handling to status package * Fix starting issues * Remove pilot widget and update security lock to new status data * Remove debug logs * Improve http server shutdown * Add workaround for cleanly shutting down firewall+netquery * Improve logging * Add syncing states with notifications for new module system * Improve starting, stopping, shutdown; resolve FIXMEs/TODOs * [WIP] Fix most unit tests * Review new module system and fix minor issues * Push shutdown and restart events again via API * Set sleep mode via interface * Update example/template module * [WIP] Fix spn/cabin unit test * Remove deprecated UI elements * Make log output more similar for the logging transition phase * Switch spn hub and observer cmds to new module system * Fix log sources * Make worker mgr less error prone * Fix tests and minor issues * Fix observation hub * Improve shutdown and restart handling * Split up big connection.go source file * Move varint and dsd packages to structures repo * Improve expansion test * Fix linter warnings * Fix interception module on windows * Fix linter errors --------- Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
This commit is contained in:
@@ -8,10 +8,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/utils/debug"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/utils/debug"
|
||||
"github.com/safing/portmaster/service/network/state"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
@@ -23,7 +23,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "debug/network",
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
DataFunc: debugInfo,
|
||||
Name: "Get Network Debug Information",
|
||||
Description: "Returns network debugging information, similar to debug/core, but with connection data.",
|
||||
@@ -52,9 +51,8 @@ func registerAPIEndpoints() error {
|
||||
}
|
||||
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "debug/network/state",
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
Path: "debug/network/state",
|
||||
Read: api.PermitUser,
|
||||
StructFunc: func(ar *api.Request) (i interface{}, err error) {
|
||||
return state.GetInfo(), nil
|
||||
},
|
||||
@@ -79,8 +77,7 @@ func debugInfo(ar *api.Request) (data []byte, err error) {
|
||||
di.AddVersionInfo()
|
||||
di.AddPlatformInfo(ar.Context())
|
||||
|
||||
// Errors and unexpected logs.
|
||||
di.AddLastReportedModuleError()
|
||||
// Unexpected logs.
|
||||
di.AddLastUnexpectedLogs()
|
||||
|
||||
// Network Connections.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/network/state"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
@@ -31,21 +31,21 @@ const (
|
||||
cleanerTickDuration = 5 * time.Second
|
||||
)
|
||||
|
||||
func connectionCleaner(ctx context.Context) error {
|
||||
ticker := module.NewSleepyTicker(cleanerTickDuration, 0)
|
||||
func connectionCleaner(ctx *mgr.WorkerCtx) error {
|
||||
module.connectionCleanerTicker = mgr.NewSleepyTicker(cleanerTickDuration, 0)
|
||||
defer module.connectionCleanerTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return nil
|
||||
case <-ticker.Wait():
|
||||
case <-module.connectionCleanerTicker.Wait():
|
||||
// clean connections and processes
|
||||
activePIDs := cleanConnections()
|
||||
process.CleanProcessStorage(activePIDs)
|
||||
|
||||
// clean udp connection states
|
||||
state.CleanUDPStates(ctx)
|
||||
state.CleanUDPStates(ctx.Ctx())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func connectionCleaner(ctx context.Context) error {
|
||||
func cleanConnections() (activePIDs map[int]struct{}) {
|
||||
activePIDs = make(map[int]struct{})
|
||||
|
||||
_ = module.RunMicroTask("clean connections", 0, func(ctx context.Context) error {
|
||||
_ = module.mgr.Do("clean connections", func(ctx *mgr.WorkerCtx) error {
|
||||
now := time.Now().UTC()
|
||||
nowUnix := now.Unix()
|
||||
ignoreNewer := nowUnix - 2
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
@@ -814,221 +814,6 @@ func (conn *Connection) delete() {
|
||||
}
|
||||
}
|
||||
|
||||
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
||||
// worker to handle the packets.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
// Cannot be called with "nil" handler. Call StopFirewallHandler() instead.
|
||||
func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize packet queue, if needed.
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
if !conn.pktQueueActive {
|
||||
conn.pktQueue = make(chan packet.Packet, 100)
|
||||
conn.pktQueueActive = true
|
||||
}
|
||||
|
||||
// Start packet handler worker when new handler is set.
|
||||
if conn.firewallHandler == nil {
|
||||
module.StartWorker("packet handler", conn.packetHandlerWorker)
|
||||
}
|
||||
|
||||
// Set new handler.
|
||||
conn.firewallHandler = handler
|
||||
}
|
||||
|
||||
// UpdateFirewallHandler sets the firewall handler if it already set and the
|
||||
// given handler is not nil.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) UpdateFirewallHandler(handler FirewallHandler) {
|
||||
if handler != nil && conn.firewallHandler != nil {
|
||||
conn.firewallHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// StopFirewallHandler unsets the firewall handler and stops the handler worker.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) StopFirewallHandler() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// Unset the firewall handler to revert to the default handler.
|
||||
conn.firewallHandler = nil
|
||||
|
||||
// Signal the packet handler worker that it can stop.
|
||||
if conn.pktQueueActive {
|
||||
close(conn.pktQueue)
|
||||
conn.pktQueueActive = false
|
||||
}
|
||||
|
||||
// Unset the packet queue so that it can be freed.
|
||||
conn.pktQueue = nil
|
||||
}
|
||||
|
||||
// HandlePacket queues packet of Link for handling.
|
||||
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
||||
// Update last seen timestamp.
|
||||
conn.lastSeen.Store(time.Now().Unix())
|
||||
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// execute handler or verdict
|
||||
if conn.pktQueueActive {
|
||||
select {
|
||||
case conn.pktQueue <- pkt:
|
||||
default:
|
||||
log.Debugf(
|
||||
"filter: dropping packet %s, as there is no space in the connection's handling queue",
|
||||
pkt,
|
||||
)
|
||||
_ = pkt.Drop()
|
||||
}
|
||||
} else {
|
||||
// Run default handler.
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
}
|
||||
}
|
||||
|
||||
var infoOnlyPacketsActive = abool.New()
|
||||
|
||||
// packetHandlerWorker sequentially handles queued packets.
|
||||
func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
||||
// Copy packet queue, so we can remove the reference from the connection
|
||||
// when we stop the firewall handler.
|
||||
var pktQueue chan packet.Packet
|
||||
func() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
pktQueue = conn.pktQueue
|
||||
}()
|
||||
|
||||
// pktSeq counts the seen packets.
|
||||
var pktSeq int
|
||||
|
||||
for {
|
||||
select {
|
||||
case pkt := <-pktQueue:
|
||||
if pkt == nil {
|
||||
return nil
|
||||
}
|
||||
pktSeq++
|
||||
|
||||
// Attempt to optimize packet handling order by handling info-only packets first.
|
||||
switch {
|
||||
case pktSeq > 1:
|
||||
// Order correction is only for first packet.
|
||||
|
||||
case pkt.InfoOnly():
|
||||
// Correct order only if first packet is not info-only.
|
||||
|
||||
// We have observed a first packet that is info-only.
|
||||
// Info-only packets seem to be active and working.
|
||||
infoOnlyPacketsActive.Set()
|
||||
|
||||
case pkt.ExpectInfo():
|
||||
// Packet itself tells us that we should expect an info-only packet.
|
||||
fallthrough
|
||||
|
||||
case infoOnlyPacketsActive.IsSet() && pkt.IsOutbound():
|
||||
// Info-only packets are active and the packet is outbound.
|
||||
// The probability is high that we will also get an info-only packet for this connection.
|
||||
// TODO: Do not do this for forwarded packets in the future.
|
||||
|
||||
// DEBUG:
|
||||
// log.Debugf("filter: waiting for info only packet in order to pull forward: %s", pkt)
|
||||
select {
|
||||
case infoPkt := <-pktQueue:
|
||||
if infoPkt != nil {
|
||||
// DEBUG:
|
||||
// log.Debugf("filter: packet #%d [pulled forward] info=%v PID=%d packet: %s", pktSeq, infoPkt.InfoOnly(), infoPkt.Info().PID, pkt)
|
||||
packetHandlerHandleConn(ctx, conn, infoPkt)
|
||||
pktSeq++
|
||||
}
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG:
|
||||
// switch {
|
||||
// case pkt.Info().Inbound:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq == 1 && !pkt.InfoOnly():
|
||||
// log.Warningf("filter: packet #%d [should be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq >= 2 && pkt.InfoOnly():
|
||||
// log.Errorf("filter: packet #%d [should not be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// default:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// }
|
||||
|
||||
packetHandlerHandleConn(ctx, conn, pkt)
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.Packet) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
|
||||
// Check if we should use the default handler.
|
||||
// The default handler is only for fully decided
|
||||
// connections and just applying the verdict.
|
||||
// There is no logging for these packets.
|
||||
if conn.firewallHandler == nil {
|
||||
// Run default handler.
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Create tracing context.
|
||||
// Add context tracer and set context on packet.
|
||||
traceCtx, tracer := log.AddTracer(ctx)
|
||||
if tracer != nil {
|
||||
// The trace is submitted in `network.Connection.packetHandler()`.
|
||||
tracer.Tracef("filter: handling packet: %s", pkt)
|
||||
}
|
||||
pkt.SetCtx(traceCtx)
|
||||
|
||||
// Handle packet with set handler.
|
||||
conn.firewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
|
||||
// Log result and submit trace, when there are any changes.
|
||||
if conn.saveWhenFinished {
|
||||
switch {
|
||||
case conn.DataIsComplete():
|
||||
tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
||||
case conn.Verdict != VerdictUndecided:
|
||||
tracer.Debugf("filter: connection %s fast-tracked", pkt)
|
||||
default:
|
||||
tracer.Debugf("filter: gathered data on connection %s", conn)
|
||||
}
|
||||
// Submit trace logs.
|
||||
tracer.Submit()
|
||||
}
|
||||
|
||||
// Push changes, if there are any.
|
||||
if conn.saveWhenFinished {
|
||||
conn.saveWhenFinished = false
|
||||
conn.Save()
|
||||
}
|
||||
}
|
||||
|
||||
// GetActiveInspectors returns the list of active inspectors.
|
||||
func (conn *Connection) GetActiveInspectors() []bool {
|
||||
return conn.activeInspectors
|
||||
|
||||
227
service/network/connection_handler.go
Normal file
227
service/network/connection_handler.go
Normal file
@@ -0,0 +1,227 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
)
|
||||
|
||||
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
||||
// worker to handle the packets.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
// Cannot be called with "nil" handler. Call StopFirewallHandler() instead.
|
||||
func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize packet queue, if needed.
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
if !conn.pktQueueActive {
|
||||
conn.pktQueue = make(chan packet.Packet, 100)
|
||||
conn.pktQueueActive = true
|
||||
}
|
||||
|
||||
// Start packet handler worker when new handler is set.
|
||||
if conn.firewallHandler == nil {
|
||||
module.mgr.Go("packet handler", conn.packetHandlerWorker)
|
||||
}
|
||||
|
||||
// Set new handler.
|
||||
conn.firewallHandler = handler
|
||||
}
|
||||
|
||||
// UpdateFirewallHandler sets the firewall handler if it already set and the
|
||||
// given handler is not nil.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) UpdateFirewallHandler(handler FirewallHandler) {
|
||||
if handler != nil && conn.firewallHandler != nil {
|
||||
conn.firewallHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// StopFirewallHandler unsets the firewall handler and stops the handler worker.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) StopFirewallHandler() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// Unset the firewall handler to revert to the default handler.
|
||||
conn.firewallHandler = nil
|
||||
|
||||
// Signal the packet handler worker that it can stop.
|
||||
if conn.pktQueueActive {
|
||||
close(conn.pktQueue)
|
||||
conn.pktQueueActive = false
|
||||
}
|
||||
|
||||
// Unset the packet queue so that it can be freed.
|
||||
conn.pktQueue = nil
|
||||
}
|
||||
|
||||
// HandlePacket queues packet of Link for handling.
|
||||
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
||||
// Update last seen timestamp.
|
||||
conn.lastSeen.Store(time.Now().Unix())
|
||||
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// execute handler or verdict
|
||||
if conn.pktQueueActive {
|
||||
select {
|
||||
case conn.pktQueue <- pkt:
|
||||
default:
|
||||
log.Debugf(
|
||||
"filter: dropping packet %s, as there is no space in the connection's handling queue",
|
||||
pkt,
|
||||
)
|
||||
_ = pkt.Drop()
|
||||
}
|
||||
} else {
|
||||
// Run default handler.
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
}
|
||||
}
|
||||
|
||||
var infoOnlyPacketsActive = abool.New()
|
||||
|
||||
// packetHandlerWorker sequentially handles queued packets.
|
||||
func (conn *Connection) packetHandlerWorker(ctx *mgr.WorkerCtx) error {
|
||||
// Copy packet queue, so we can remove the reference from the connection
|
||||
// when we stop the firewall handler.
|
||||
var pktQueue chan packet.Packet
|
||||
func() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
pktQueue = conn.pktQueue
|
||||
}()
|
||||
|
||||
// pktSeq counts the seen packets.
|
||||
var pktSeq int
|
||||
|
||||
for {
|
||||
select {
|
||||
case pkt := <-pktQueue:
|
||||
if pkt == nil {
|
||||
return nil
|
||||
}
|
||||
pktSeq++
|
||||
|
||||
// Attempt to optimize packet handling order by handling info-only packets first.
|
||||
switch {
|
||||
case pktSeq > 1:
|
||||
// Order correction is only for first packet.
|
||||
|
||||
case pkt.InfoOnly():
|
||||
// Correct order only if first packet is not info-only.
|
||||
|
||||
// We have observed a first packet that is info-only.
|
||||
// Info-only packets seem to be active and working.
|
||||
infoOnlyPacketsActive.Set()
|
||||
|
||||
case pkt.ExpectInfo():
|
||||
// Packet itself tells us that we should expect an info-only packet.
|
||||
fallthrough
|
||||
|
||||
case infoOnlyPacketsActive.IsSet() && pkt.IsOutbound():
|
||||
// Info-only packets are active and the packet is outbound.
|
||||
// The probability is high that we will also get an info-only packet for this connection.
|
||||
// TODO: Do not do this for forwarded packets in the future.
|
||||
|
||||
// DEBUG:
|
||||
// log.Debugf("filter: waiting for info only packet in order to pull forward: %s", pkt)
|
||||
select {
|
||||
case infoPkt := <-pktQueue:
|
||||
if infoPkt != nil {
|
||||
// DEBUG:
|
||||
// log.Debugf("filter: packet #%d [pulled forward] info=%v PID=%d packet: %s", pktSeq, infoPkt.InfoOnly(), infoPkt.Info().PID, pkt)
|
||||
packetHandlerHandleConn(ctx.Ctx(), conn, infoPkt)
|
||||
pktSeq++
|
||||
}
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG:
|
||||
// switch {
|
||||
// case pkt.Info().Inbound:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq == 1 && !pkt.InfoOnly():
|
||||
// log.Warningf("filter: packet #%d [should be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq >= 2 && pkt.InfoOnly():
|
||||
// log.Errorf("filter: packet #%d [should not be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// default:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// }
|
||||
|
||||
packetHandlerHandleConn(ctx.Ctx(), conn, pkt)
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.Packet) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
|
||||
// Check if we should use the default handler.
|
||||
// The default handler is only for fully decided
|
||||
// connections and just applying the verdict.
|
||||
// There is no logging for these packets.
|
||||
if conn.firewallHandler == nil {
|
||||
// Run default handler.
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Create tracing context.
|
||||
// Add context tracer and set context on packet.
|
||||
traceCtx, tracer := log.AddTracer(ctx)
|
||||
if tracer != nil {
|
||||
// The trace is submitted in `network.Connection.packetHandler()`.
|
||||
tracer.Tracef("filter: handling packet: %s", pkt)
|
||||
}
|
||||
pkt.SetCtx(traceCtx)
|
||||
|
||||
// Handle packet with set handler.
|
||||
conn.firewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
|
||||
// Log result and submit trace, when there are any changes.
|
||||
if conn.saveWhenFinished {
|
||||
switch {
|
||||
case conn.DataIsComplete():
|
||||
tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
||||
case conn.Verdict != VerdictUndecided:
|
||||
tracer.Debugf("filter: connection %s fast-tracked", pkt)
|
||||
default:
|
||||
tracer.Debugf("filter: gathered data on connection %s", conn)
|
||||
}
|
||||
// Submit trace logs.
|
||||
tracer.Submit()
|
||||
}
|
||||
|
||||
// Push changes, if there are any.
|
||||
if conn.saveWhenFinished {
|
||||
conn.saveWhenFinished = false
|
||||
conn.Save()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/iterator"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/database/storage"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/iterator"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/database/storage"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
)
|
||||
|
||||
@@ -110,7 +110,7 @@ func (s *StorageInterface) Get(key string) (record.Record, error) {
|
||||
func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) {
|
||||
it := iterator.New()
|
||||
|
||||
module.StartWorker("connection query", func(_ context.Context) error {
|
||||
module.mgr.Go("connection query", func(_ *mgr.WorkerCtx) error {
|
||||
s.processQuery(q, it)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/nameserver/nsutil"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
@@ -173,15 +174,15 @@ func SaveOpenDNSRequest(q *resolver.Query, rrCache *resolver.RRCache, conn *Conn
|
||||
openDNSRequests[key] = conn
|
||||
}
|
||||
|
||||
func openDNSRequestWriter(ctx context.Context) error {
|
||||
ticker := module.NewSleepyTicker(writeOpenDNSRequestsTickDuration, 0)
|
||||
defer ticker.Stop()
|
||||
func openDNSRequestWriter(ctx *mgr.WorkerCtx) error {
|
||||
module.dnsRequestTicker = mgr.NewSleepyTicker(writeOpenDNSRequestsTickDuration, 0)
|
||||
defer module.dnsRequestTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.Wait():
|
||||
case <-module.dnsRequestTicker.Wait():
|
||||
writeOpenDNSRequestsToDB()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/metrics"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,33 +2,57 @@ package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/state"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
|
||||
defaultFirewallHandler FirewallHandler
|
||||
)
|
||||
|
||||
// Events.
|
||||
var (
|
||||
const (
|
||||
ConnectionReattributedEvent = "connection re-attributed"
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("network", prep, start, nil, "base", "netenv", "processes")
|
||||
module.RegisterEvent(ConnectionReattributedEvent, false)
|
||||
type Network struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
|
||||
dnsRequestTicker *mgr.SleepyTicker
|
||||
connectionCleanerTicker *mgr.SleepyTicker
|
||||
|
||||
EventConnectionReattributed *mgr.EventMgr[string]
|
||||
}
|
||||
|
||||
func (n *Network) Manager() *mgr.Manager {
|
||||
return n.mgr
|
||||
}
|
||||
|
||||
func (n *Network) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
func (n *Network) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Network) SetSleep(enabled bool) {
|
||||
if n.dnsRequestTicker != nil {
|
||||
n.dnsRequestTicker.SetSleep(enabled)
|
||||
}
|
||||
if n.connectionCleanerTicker != nil {
|
||||
n.connectionCleanerTicker.SetSleep(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
var defaultFirewallHandler FirewallHandler
|
||||
|
||||
// SetDefaultFirewallHandler sets the default firewall handler.
|
||||
func SetDefaultFirewallHandler(handler FirewallHandler) {
|
||||
if defaultFirewallHandler == nil {
|
||||
@@ -55,17 +79,9 @@ func start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
module.StartServiceWorker("clean connections", 0, connectionCleaner)
|
||||
module.StartServiceWorker("write open dns requests", 0, openDNSRequestWriter)
|
||||
|
||||
if err := module.RegisterEventHook(
|
||||
"profiles",
|
||||
profile.DeletedEvent,
|
||||
"re-attribute connections from deleted profile",
|
||||
reAttributeConnections,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
module.mgr.Go("clean connections", connectionCleaner)
|
||||
module.mgr.Go("write open dns requests", openDNSRequestWriter)
|
||||
module.instance.Profile().EventDelete.AddCallback("re-attribute connections from deleted profile", reAttributeConnections)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -74,14 +90,10 @@ var reAttributionLock sync.Mutex
|
||||
|
||||
// reAttributeConnections finds all connections of a deleted profile and re-attributes them.
|
||||
// Expected event data: scoped profile ID.
|
||||
func reAttributeConnections(_ context.Context, eventData any) error {
|
||||
profileID, ok := eventData.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("event data is not a string: %v", eventData)
|
||||
}
|
||||
func reAttributeConnections(_ *mgr.WorkerCtx, profileID string) (bool, error) {
|
||||
profileSource, profileID, ok := strings.Cut(profileID, "/")
|
||||
if !ok {
|
||||
return fmt.Errorf("event data does not seem to be a scoped profile ID: %v", eventData)
|
||||
return false, fmt.Errorf("event data does not seem to be a scoped profile ID: %v", profileID)
|
||||
}
|
||||
|
||||
// Hold a lock for re-attribution, to prevent simultaneous processing of the
|
||||
@@ -114,7 +126,7 @@ func reAttributeConnections(_ context.Context, eventData any) error {
|
||||
}
|
||||
|
||||
tracer.Infof("filter: re-attributed %d connections", reAttributed)
|
||||
return nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func reAttributeConnection(ctx context.Context, conn *Connection, profileID, profileSource string) (reAttributed bool) {
|
||||
@@ -144,8 +156,36 @@ func reAttributeConnection(ctx context.Context, conn *Connection, profileID, pro
|
||||
conn.Save()
|
||||
|
||||
// Trigger event for re-attribution.
|
||||
module.TriggerEvent(ConnectionReattributedEvent, conn.ID)
|
||||
module.EventConnectionReattributed.Submit(conn.ID)
|
||||
|
||||
log.Tracer(ctx).Debugf("filter: re-attributed %s to %s", conn, conn.process.PrimaryProfileID)
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
module *Network
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
// New returns a new Network module.
|
||||
func New(instance instance) (*Network, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Network")
|
||||
module = &Network{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
EventConnectionReattributed: mgr.NewEventMgr[string](ConnectionReattributedEvent, m),
|
||||
}
|
||||
|
||||
if err := prep(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
Profile() *profile.ProfileModule
|
||||
}
|
||||
|
||||
@@ -84,6 +84,10 @@ func GetIPScope(ip net.IP) IPScope { //nolint:gocognit
|
||||
}
|
||||
} else if len(ip) == net.IPv6len {
|
||||
// IPv6
|
||||
|
||||
// TODO: Add IPv6 RFC5771 test / doc networks
|
||||
// 2001:db8::/32
|
||||
// 3fff::/20
|
||||
switch {
|
||||
case ip.Equal(net.IPv6zero):
|
||||
return Invalid
|
||||
@@ -153,7 +157,7 @@ func GetBroadcastAddress(ip net.IP, netMask net.IPMask) net.IP {
|
||||
// Merge to broadcast address
|
||||
n := len(ip)
|
||||
broadcastAddress := make(net.IP, n)
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
broadcastAddress[i] = ip[i] | ^mask[i]
|
||||
}
|
||||
return broadcastAddress
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
)
|
||||
|
||||
// GetUnusedLocalPort returns a local port of the specified protocol that is
|
||||
@@ -13,7 +13,7 @@ func GetUnusedLocalPort(protocol uint8) (port uint16, ok bool) {
|
||||
|
||||
// Try up to 1000 times to find an unused port.
|
||||
nextPort:
|
||||
for i := 0; i < tries; i++ {
|
||||
for i := range tries {
|
||||
// Generate random port between 10000 and 65535
|
||||
rN, err := rng.Number(55535)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package state
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ package state
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/network/socket"
|
||||
|
||||
Reference in New Issue
Block a user