Revamp connection handling flow to fix race condition and support info-only packets
This commit is contained in:
@@ -141,7 +141,11 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo
|
|||||||
authenticatedPath += string(filepath.Separator)
|
authenticatedPath += string(filepath.Separator)
|
||||||
|
|
||||||
// Get process of request.
|
// Get process of request.
|
||||||
proc, _, err := process.GetProcessByConnection(ctx, pktInfo)
|
pid, _, _ := process.GetPidOfConnection(ctx, pktInfo)
|
||||||
|
if pid < 0 {
|
||||||
|
return false, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
|
||||||
|
}
|
||||||
|
proc, err := process.GetOrFindProcess(ctx, pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracer(ctx).Debugf("filter: failed to get process of api request: %s", err)
|
log.Tracer(ctx).Debugf("filter: failed to get process of api request: %s", err)
|
||||||
originalPid = process.UnidentifiedProcessID
|
originalPid = process.UnidentifiedProcessID
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
"golang.org/x/sync/singleflight"
|
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
@@ -54,7 +53,7 @@ func init() {
|
|||||||
// TODO: Move interception module to own package (dir).
|
// TODO: Move interception module to own package (dir).
|
||||||
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications", "profiles")
|
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications", "profiles")
|
||||||
|
|
||||||
network.SetDefaultFirewallHandler(defaultHandler)
|
network.SetDefaultFirewallHandler(verdictHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func interceptionPrep() error {
|
func interceptionPrep() error {
|
||||||
@@ -120,6 +119,11 @@ func resetAllConnectionVerdicts() {
|
|||||||
// Re-evaluate all connections.
|
// Re-evaluate all connections.
|
||||||
var changedVerdicts int
|
var changedVerdicts int
|
||||||
for _, conn := range network.GetAllConnections() {
|
for _, conn := range network.GetAllConnections() {
|
||||||
|
// Skip incomplete connections.
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
conn.Lock()
|
conn.Lock()
|
||||||
defer conn.Unlock()
|
defer conn.Unlock()
|
||||||
@@ -167,15 +171,10 @@ func resetAllConnectionVerdicts() {
|
|||||||
|
|
||||||
func interceptionStart() error {
|
func interceptionStart() error {
|
||||||
getConfig()
|
getConfig()
|
||||||
|
|
||||||
if err := registerMetrics(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
startAPIAuth()
|
startAPIAuth()
|
||||||
|
|
||||||
interceptionModule.StartWorker("stat logger", statLogger)
|
interceptionModule.StartServiceWorker("stat logger", 0, statLogger)
|
||||||
interceptionModule.StartWorker("packet handler", packetHandler)
|
interceptionModule.StartServiceWorker("packet handler", 0, packetHandler)
|
||||||
|
|
||||||
return interception.Start()
|
return interception.Start()
|
||||||
}
|
}
|
||||||
@@ -196,92 +195,38 @@ func SetNameserverIPMatcher(fn func(ip net.IP) bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePacket(ctx context.Context, pkt packet.Packet) {
|
func handlePacket(pkt packet.Packet) {
|
||||||
// Record metrics.
|
// First, check for an existing connection.
|
||||||
startTime := time.Now()
|
conn, ok := network.GetConnection(pkt.GetConnectionID())
|
||||||
defer packetHandlingHistogram.UpdateDuration(startTime)
|
if ok {
|
||||||
|
// Add packet to connection handler queue or apply verdict directly.
|
||||||
if fastTrackedPermit(pkt) {
|
conn.HandlePacket(pkt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add context tracer and set context on packet.
|
// Else create new incomplete connection from the packet and start the new handler.
|
||||||
traceCtx, tracer := log.AddTracer(ctx)
|
conn = network.NewIncompleteConnection(pkt)
|
||||||
if tracer != nil {
|
conn.Lock()
|
||||||
// The trace is submitted in `network.Connection.packetHandler()`.
|
defer conn.Unlock()
|
||||||
tracer.Tracef("filter: handling packet: %s", pkt)
|
conn.SetFirewallHandler(fastTrackHandler)
|
||||||
}
|
|
||||||
pkt.SetCtx(traceCtx)
|
|
||||||
|
|
||||||
// Get connection of packet.
|
// Let the new connection handler worker handle the packet.
|
||||||
conn, err := getConnection(pkt)
|
|
||||||
if err != nil {
|
|
||||||
tracer.Errorf("filter: packet %s dropped: %s", pkt, err)
|
|
||||||
_ = pkt.Drop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle packet
|
|
||||||
conn.HandlePacket(pkt)
|
conn.HandlePacket(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
var getConnectionSingleInflight singleflight.Group
|
|
||||||
|
|
||||||
func getConnection(pkt packet.Packet) (*network.Connection, error) {
|
|
||||||
created := false
|
|
||||||
|
|
||||||
// Create or get connection in single inflight lock in order to prevent duplicates.
|
|
||||||
newConn, err, shared := getConnectionSingleInflight.Do(pkt.GetConnectionID(), func() (interface{}, error) {
|
|
||||||
// First, check for an existing connection.
|
|
||||||
conn, ok := network.GetConnection(pkt.GetConnectionID())
|
|
||||||
if ok {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else create new one from the packet.
|
|
||||||
conn = network.NewConnectionFromFirstPacket(pkt)
|
|
||||||
conn.Lock()
|
|
||||||
defer conn.Unlock()
|
|
||||||
conn.SetFirewallHandler(initialHandler)
|
|
||||||
created = true
|
|
||||||
return conn, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get connection: %w", err)
|
|
||||||
}
|
|
||||||
if newConn == nil {
|
|
||||||
return nil, errors.New("connection getter returned nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform and log result.
|
|
||||||
conn := newConn.(*network.Connection) //nolint:forcetypeassert // Can only be a *network.Connection.
|
|
||||||
sharedIndicator := ""
|
|
||||||
if shared {
|
|
||||||
sharedIndicator = " (shared)"
|
|
||||||
}
|
|
||||||
if created {
|
|
||||||
log.Tracer(pkt.Ctx()).Tracef("filter: created new connection %s%s", conn.ID, sharedIndicator)
|
|
||||||
} else {
|
|
||||||
log.Tracer(pkt.Ctx()).Tracef("filter: assigned connection %s%s", conn.ID, sharedIndicator)
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fastTrackedPermit quickly permits certain network critical or internal connections.
|
// fastTrackedPermit quickly permits certain network critical or internal connections.
|
||||||
func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
func fastTrackedPermit(pkt packet.Packet) (verdict network.Verdict, permanent bool) {
|
||||||
meta := pkt.Info()
|
meta := pkt.Info()
|
||||||
|
|
||||||
// Check if packed was already fast-tracked by the OS integration.
|
// Check if packed was already fast-tracked by the OS integration.
|
||||||
if pkt.FastTrackedByIntegration() {
|
if pkt.FastTrackedByIntegration() {
|
||||||
log.Debugf("filter: fast-tracked by OS integration: %s", pkt)
|
log.Debugf("filter: fast-tracked by OS integration: %s", pkt)
|
||||||
return true
|
return network.VerdictAccept, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if connection was already blocked.
|
// Check if connection was already blocked.
|
||||||
if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) {
|
if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) {
|
||||||
_ = pkt.PermanentBlock()
|
return network.VerdictBlock, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some programs do a network self-check where they connects to the same
|
// Some programs do a network self-check where they connects to the same
|
||||||
@@ -290,8 +235,8 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
if meta.SrcPort == meta.DstPort &&
|
if meta.SrcPort == meta.DstPort &&
|
||||||
meta.Src.Equal(meta.Dst) {
|
meta.Src.Equal(meta.Dst) {
|
||||||
log.Debugf("filter: fast-track network self-check: %s", pkt)
|
log.Debugf("filter: fast-track network self-check: %s", pkt)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch meta.Protocol { //nolint:exhaustive // Checking for specific values only.
|
switch meta.Protocol { //nolint:exhaustive // Checking for specific values only.
|
||||||
@@ -300,8 +245,7 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
err := pkt.LoadPacketData()
|
err := pkt.LoadPacketData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("filter: failed to load ICMP packet data: %s", err)
|
log.Debugf("filter: failed to load ICMP packet data: %s", err)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit to ICMP listener.
|
// Submit to ICMP listener.
|
||||||
@@ -311,8 +255,7 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
// permanent accept, because then we won't see any future packets of that
|
// permanent accept, because then we won't see any future packets of that
|
||||||
// connection and thus cannot continue to submit them.
|
// connection and thus cannot continue to submit them.
|
||||||
log.Debugf("filter: fast-track tracing ICMP/v6: %s", pkt)
|
log.Debugf("filter: fast-track tracing ICMP/v6: %s", pkt)
|
||||||
_ = pkt.Accept()
|
return network.VerdictAccept, false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle echo request and replies regularly.
|
// Handle echo request and replies regularly.
|
||||||
@@ -323,20 +266,19 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
switch icmpLayer.TypeCode.Type() {
|
switch icmpLayer.TypeCode.Type() {
|
||||||
case layers.ICMPv4TypeEchoRequest,
|
case layers.ICMPv4TypeEchoRequest,
|
||||||
layers.ICMPv4TypeEchoReply:
|
layers.ICMPv4TypeEchoReply:
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
case *layers.ICMPv6:
|
case *layers.ICMPv6:
|
||||||
switch icmpLayer.TypeCode.Type() {
|
switch icmpLayer.TypeCode.Type() {
|
||||||
case layers.ICMPv6TypeEchoRequest,
|
case layers.ICMPv6TypeEchoRequest,
|
||||||
layers.ICMPv6TypeEchoReply:
|
layers.ICMPv6TypeEchoReply:
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Permit all ICMP/v6 packets that are not echo requests or replies.
|
// Permit all ICMP/v6 packets that are not echo requests or replies.
|
||||||
log.Debugf("filter: fast-track accepting ICMP/v6: %s", pkt)
|
log.Debugf("filter: fast-track accepting ICMP/v6: %s", pkt)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
|
|
||||||
case packet.UDP, packet.TCP:
|
case packet.UDP, packet.TCP:
|
||||||
switch meta.DstPort {
|
switch meta.DstPort {
|
||||||
@@ -346,37 +288,36 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
|
|
||||||
// DHCP and DHCPv6 must be UDP.
|
// DHCP and DHCPv6 must be UDP.
|
||||||
if meta.Protocol != packet.UDP {
|
if meta.Protocol != packet.UDP {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// DHCP is only valid in local network scopes.
|
// DHCP is only valid in local network scopes.
|
||||||
switch netutils.ClassifyIP(meta.Dst) { //nolint:exhaustive // Checking for specific values only.
|
switch netutils.ClassifyIP(meta.Dst) { //nolint:exhaustive // Checking for specific values only.
|
||||||
case netutils.HostLocal, netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
case netutils.HostLocal, netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||||
default:
|
default:
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log and permit.
|
// Log and permit.
|
||||||
log.Debugf("filter: fast-track accepting DHCP: %s", pkt)
|
log.Debugf("filter: fast-track accepting DHCP: %s", pkt)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
|
|
||||||
case apiPort:
|
case apiPort:
|
||||||
// Always allow direct access to the Portmaster API.
|
// Always allow direct access to the Portmaster API.
|
||||||
|
|
||||||
// Portmaster API is TCP only.
|
// Portmaster API is TCP only.
|
||||||
if meta.Protocol != packet.TCP {
|
if meta.Protocol != packet.TCP {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the api port is even set.
|
// Check if the api port is even set.
|
||||||
if !apiPortSet {
|
if !apiPortSet {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be destined for the API IP.
|
// Must be destined for the API IP.
|
||||||
if !meta.Dst.Equal(apiIP) {
|
if !meta.Dst.Equal(apiIP) {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only fast-track local requests.
|
// Only fast-track local requests.
|
||||||
@@ -384,15 +325,14 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
case !isMe:
|
case !isMe:
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log and permit.
|
// Log and permit.
|
||||||
log.Debugf("filter: fast-track accepting api connection: %s", pkt)
|
log.Debugf("filter: fast-track accepting api connection: %s", pkt)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
|
|
||||||
case 53:
|
case 53:
|
||||||
// Always allow direct access to the Portmaster Nameserver.
|
// Always allow direct access to the Portmaster Nameserver.
|
||||||
@@ -400,12 +340,12 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
|
|
||||||
// Check if a nameserver IP matcher is set.
|
// Check if a nameserver IP matcher is set.
|
||||||
if !nameserverIPMatcherReady.IsSet() {
|
if !nameserverIPMatcherReady.IsSet() {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if packet is destined for a nameserver IP.
|
// Check if packet is destined for a nameserver IP.
|
||||||
if !nameserverIPMatcher(meta.Dst) {
|
if !nameserverIPMatcher(meta.Dst) {
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only fast-track local requests.
|
// Only fast-track local requests.
|
||||||
@@ -413,32 +353,76 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
case !isMe:
|
case !isMe:
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log and permit.
|
// Log and permit.
|
||||||
log.Debugf("filter: fast-track accepting local dns: %s", pkt)
|
log.Debugf("filter: fast-track accepting local dns: %s", pkt)
|
||||||
_ = pkt.PermanentAccept()
|
return network.VerdictAccept, true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case compat.SystemIntegrationCheckProtocol:
|
case compat.SystemIntegrationCheckProtocol:
|
||||||
if pkt.Info().Dst.Equal(compat.SystemIntegrationCheckDstIP) {
|
if pkt.Info().Dst.Equal(compat.SystemIntegrationCheckDstIP) {
|
||||||
compat.SubmitSystemIntegrationCheckPacket(pkt)
|
compat.SubmitSystemIntegrationCheckPacket(pkt)
|
||||||
_ = pkt.Drop()
|
return network.VerdictDrop, false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return network.VerdictUndecided, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
func fastTrackHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
|
fastTrackedVerdict, permanent := fastTrackedPermit(pkt)
|
||||||
|
if fastTrackedVerdict != network.VerdictUndecided {
|
||||||
|
// Set verdict on connection.
|
||||||
|
conn.Verdict.Active = fastTrackedVerdict
|
||||||
|
conn.Verdict.Firewall = fastTrackedVerdict
|
||||||
|
// Apply verdict to (real) packet.
|
||||||
|
if !pkt.InfoOnly() {
|
||||||
|
issueVerdict(conn, pkt, fastTrackedVerdict, permanent)
|
||||||
|
}
|
||||||
|
// Stop handler if permanent.
|
||||||
|
if permanent {
|
||||||
|
conn.SetVerdict(fastTrackedVerdict, "fast-tracked", "", nil)
|
||||||
|
conn.Verdict.Worst = fastTrackedVerdict
|
||||||
|
// Do not finalize verdict, as we are missing necessary data.
|
||||||
|
conn.StopFirewallHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not continue to next handler.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If packet is not fast-tracked, continue with gathering more information.
|
||||||
|
conn.UpdateFirewallHandler(gatherDataHandler)
|
||||||
|
gatherDataHandler(conn, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gatherDataHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
|
// Get process info
|
||||||
|
_ = conn.GatherConnectionInfo(pkt)
|
||||||
|
// Errors are informational and are logged to the context.
|
||||||
|
|
||||||
|
// Run this handler again if data is not yet complete.
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to filter handler, when connection data is complete.
|
||||||
|
conn.UpdateFirewallHandler(filterHandler)
|
||||||
|
filterHandler(conn, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
|
// Skip if data is not complete.
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
filterConnection := true
|
filterConnection := true
|
||||||
|
|
||||||
log.Tracer(pkt.Ctx()).Trace("filter: handing over to connection-based handler")
|
|
||||||
// Check for special (internal) connection cases.
|
// Check for special (internal) connection cases.
|
||||||
switch {
|
switch {
|
||||||
case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort):
|
case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort):
|
||||||
@@ -480,8 +464,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||||||
switch {
|
switch {
|
||||||
case conn.Inspecting:
|
case conn.Inspecting:
|
||||||
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
|
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
|
||||||
conn.SetFirewallHandler(inspectThenVerdict)
|
conn.SetFirewallHandler(inspectAndVerdictHandler)
|
||||||
inspectThenVerdict(conn, pkt)
|
inspectAndVerdictHandler(conn, pkt)
|
||||||
default:
|
default:
|
||||||
conn.StopFirewallHandler()
|
conn.StopFirewallHandler()
|
||||||
issueVerdict(conn, pkt, 0, true)
|
issueVerdict(conn, pkt, 0, true)
|
||||||
@@ -490,6 +474,11 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||||||
|
|
||||||
// FilterConnection runs all the filtering (and tunneling) procedures.
|
// FilterConnection runs all the filtering (and tunneling) procedures.
|
||||||
func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet, checkFilter, checkTunnel bool) {
|
func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet, checkFilter, checkTunnel bool) {
|
||||||
|
// Skip if data is not complete.
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if checkFilter {
|
if checkFilter {
|
||||||
if filterEnabled() {
|
if filterEnabled() {
|
||||||
log.Tracer(ctx).Trace("filter: starting decision process")
|
log.Tracer(ctx).Trace("filter: starting decision process")
|
||||||
@@ -537,12 +526,11 @@ func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultHandler(conn *network.Connection, pkt packet.Packet) {
|
func verdictHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
// TODO: `pkt` has an active trace log, which we currently don't submit.
|
|
||||||
issueVerdict(conn, pkt, 0, true)
|
issueVerdict(conn, pkt, 0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func inspectThenVerdict(conn *network.Connection, pkt packet.Packet) {
|
func inspectAndVerdictHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
pktVerdict, continueInspection := inspection.RunInspectors(conn, pkt)
|
pktVerdict, continueInspection := inspection.RunInspectors(conn, pkt)
|
||||||
if continueInspection {
|
if continueInspection {
|
||||||
issueVerdict(conn, pkt, pktVerdict, false)
|
issueVerdict(conn, pkt, pktVerdict, false)
|
||||||
@@ -689,10 +677,11 @@ func packetHandler(ctx context.Context) error {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
case pkt := <-interception.Packets:
|
case pkt := <-interception.Packets:
|
||||||
interceptionModule.StartWorker("initial packet handler", func(workerCtx context.Context) error {
|
if pkt != nil {
|
||||||
handlePacket(workerCtx, pkt)
|
handlePacket(pkt)
|
||||||
return nil
|
} else {
|
||||||
})
|
return errors.New("received nil packet from interception")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ type infoPacket struct {
|
|||||||
pmpacket.Base
|
pmpacket.Base
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InfoOnly returns whether the packet is informational only and does not
|
||||||
|
// represent an actual packet.
|
||||||
|
func (pkt *infoPacket) InfoOnly() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// LoadPacketData does nothing on Linux, as data is always fully parsed.
|
// LoadPacketData does nothing on Linux, as data is always fully parsed.
|
||||||
func (pkt *infoPacket) LoadPacketData() error {
|
func (pkt *infoPacket) LoadPacketData() error {
|
||||||
return fmt.Errorf("can't load data in info only packet")
|
return fmt.Errorf("can't load data in info only packet")
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/cilium/ebpf/link"
|
"github.com/cilium/ebpf/link"
|
||||||
"github.com/cilium/ebpf/ringbuf"
|
"github.com/cilium/ebpf/ringbuf"
|
||||||
"github.com/cilium/ebpf/rlimit"
|
"github.com/cilium/ebpf/rlimit"
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
"github.com/safing/portmaster/network/packet"
|
"github.com/safing/portmaster/network/packet"
|
||||||
)
|
)
|
||||||
@@ -17,6 +19,7 @@ import (
|
|||||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf program/monitor.c
|
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf program/monitor.c
|
||||||
var stopper chan struct{}
|
var stopper chan struct{}
|
||||||
|
|
||||||
|
// StartEBPFWorker starts the ebpf worker.
|
||||||
func StartEBPFWorker(ch chan packet.Packet) {
|
func StartEBPFWorker(ch chan packet.Packet) {
|
||||||
stopper = make(chan struct{})
|
stopper = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
@@ -32,7 +35,7 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
log.Errorf("ebpf: failed to load ebpf object: %s", err)
|
log.Errorf("ebpf: failed to load ebpf object: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer objs.Close()
|
defer objs.Close() //nolint:errcheck
|
||||||
|
|
||||||
// Create a link to the tcp_connect program.
|
// Create a link to the tcp_connect program.
|
||||||
linkTCPConnect, err := link.AttachTracing(link.TracingOptions{
|
linkTCPConnect, err := link.AttachTracing(link.TracingOptions{
|
||||||
@@ -42,7 +45,7 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
log.Errorf("ebpf: failed to attach to tcp_v4_connect: %s ", err)
|
log.Errorf("ebpf: failed to attach to tcp_v4_connect: %s ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer linkTCPConnect.Close()
|
defer linkTCPConnect.Close() //nolint:errcheck
|
||||||
|
|
||||||
// Create a link to the udp_v4_connect program.
|
// Create a link to the udp_v4_connect program.
|
||||||
linkUDPV4, err := link.AttachTracing(link.TracingOptions{
|
linkUDPV4, err := link.AttachTracing(link.TracingOptions{
|
||||||
@@ -52,7 +55,7 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
log.Errorf("ebpf: failed to attach to udp_v4_connect: %s ", err)
|
log.Errorf("ebpf: failed to attach to udp_v4_connect: %s ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer linkUDPV4.Close()
|
defer linkUDPV4.Close() //nolint:errcheck
|
||||||
|
|
||||||
// Create a link to the udp_v6_connect program.
|
// Create a link to the udp_v6_connect program.
|
||||||
linkUDPV6, err := link.AttachTracing(link.TracingOptions{
|
linkUDPV6, err := link.AttachTracing(link.TracingOptions{
|
||||||
@@ -62,14 +65,14 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
log.Errorf("ebpf: failed to attach to udp_v6_connect: %s ", err)
|
log.Errorf("ebpf: failed to attach to udp_v6_connect: %s ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer linkUDPV6.Close()
|
defer linkUDPV6.Close() //nolint:errcheck
|
||||||
|
|
||||||
rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
|
rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("ebpf: failed to open ring buffer: %s", err)
|
log.Errorf("ebpf: failed to open ring buffer: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer rd.Close()
|
defer rd.Close() //nolint:errcheck
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-stopper
|
<-stopper
|
||||||
@@ -107,7 +110,8 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
DstPort: event.Dport,
|
DstPort: event.Dport,
|
||||||
Src: arrayToIP(event.Saddr, packet.IPVersion(event.IpVersion)),
|
Src: arrayToIP(event.Saddr, packet.IPVersion(event.IpVersion)),
|
||||||
Dst: arrayToIP(event.Daddr, packet.IPVersion(event.IpVersion)),
|
Dst: arrayToIP(event.Daddr, packet.IPVersion(event.IpVersion)),
|
||||||
PID: event.Pid,
|
PID: int(event.Pid),
|
||||||
|
SeenAt: time.Now(),
|
||||||
}
|
}
|
||||||
if isEventValid(event) {
|
if isEventValid(event) {
|
||||||
log.Debugf("ebpf: PID: %d conn: %s:%d -> %s:%d %s %s", info.PID, info.LocalIP(), info.LocalPort(), info.RemoteIP(), info.RemotePort(), info.Version.String(), info.Protocol.String())
|
log.Debugf("ebpf: PID: %d conn: %s:%d -> %s:%d %s %s", info.PID, info.LocalIP(), info.LocalPort(), info.RemoteIP(), info.RemotePort(), info.Version.String(), info.Protocol.String())
|
||||||
@@ -123,6 +127,7 @@ func StartEBPFWorker(ch chan packet.Packet) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopEBPFWorker stops the ebpf worker.
|
||||||
func StopEBPFWorker() {
|
func StopEBPFWorker() {
|
||||||
close(stopper)
|
close(stopper)
|
||||||
}
|
}
|
||||||
@@ -148,11 +153,12 @@ func isEventValid(event bpfEvent) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// arrayToIP converts IP number array to net.IP
|
// arrayToIP converts IP number array to net.IP.
|
||||||
func arrayToIP(ipNum [4]uint32, ipVersion packet.IPVersion) net.IP {
|
func arrayToIP(ipNum [4]uint32, ipVersion packet.IPVersion) net.IP {
|
||||||
if ipVersion == packet.IPv4 {
|
if ipVersion == packet.IPv4 {
|
||||||
|
// FIXME: maybe convertIPv4 from windowskext package
|
||||||
return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4)
|
return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 4)
|
||||||
} else {
|
|
||||||
return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 16)
|
|
||||||
}
|
}
|
||||||
|
// FIXME: maybe use convertIPv6 from windowskext package
|
||||||
|
return unsafe.Slice((*byte)(unsafe.Pointer(&ipNum)), 16)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
pmpacket "github.com/safing/portmaster/network/packet"
|
pmpacket "github.com/safing/portmaster/network/packet"
|
||||||
|
"github.com/safing/portmaster/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Queue wraps a nfqueue.
|
// Queue wraps a nfqueue.
|
||||||
@@ -175,10 +176,11 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
|||||||
pkt := &packet{
|
pkt := &packet{
|
||||||
pktID: *attrs.PacketID,
|
pktID: *attrs.PacketID,
|
||||||
queue: q,
|
queue: q,
|
||||||
received: time.Now(),
|
|
||||||
verdictSet: make(chan struct{}),
|
verdictSet: make(chan struct{}),
|
||||||
verdictPending: abool.New(),
|
verdictPending: abool.New(),
|
||||||
}
|
}
|
||||||
|
pkt.Info().PID = process.UndefinedProcessID
|
||||||
|
pkt.Info().SeenAt = time.Now()
|
||||||
|
|
||||||
if attrs.Payload == nil {
|
if attrs.Payload == nil {
|
||||||
// There is not payload.
|
// There is not payload.
|
||||||
@@ -194,11 +196,11 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case q.packets <- pkt:
|
case q.packets <- pkt:
|
||||||
log.Tracef("nfqueue: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received))
|
log.Tracef("nfqueue: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt))
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return 0
|
return 0
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
log.Warningf("nfqueue: failed to queue packet (%s since it was handed over by the kernel)", time.Since(pkt.received))
|
log.Warningf("nfqueue: failed to queue packet (%s since it was handed over by the kernel)", time.Since(pkt.Info().SeenAt))
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -206,7 +208,7 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
|||||||
case <-pkt.verdictSet:
|
case <-pkt.verdictSet:
|
||||||
|
|
||||||
case <-time.After(20 * time.Second):
|
case <-time.After(20 * time.Second):
|
||||||
log.Warningf("nfqueue: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received))
|
log.Warningf("nfqueue: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt))
|
||||||
if err := pkt.Drop(); err != nil {
|
if err := pkt.Drop(); err != nil {
|
||||||
log.Warningf("nfqueue: failed to apply default-drop to unveridcted packet %s (%s -> %s)", pkt.ID(), pkt.Info().Src, pkt.Info().Dst)
|
log.Warningf("nfqueue: failed to apply default-drop to unveridcted packet %s (%s -> %s)", pkt.ID(), pkt.Info().Src, pkt.Info().Dst)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ func markToString(mark int) string {
|
|||||||
type packet struct {
|
type packet struct {
|
||||||
pmpacket.Base
|
pmpacket.Base
|
||||||
pktID uint32
|
pktID uint32
|
||||||
received time.Time
|
|
||||||
queue *Queue
|
queue *Queue
|
||||||
verdictSet chan struct{}
|
verdictSet chan struct{}
|
||||||
verdictPending *abool.AtomicBool
|
verdictPending *abool.AtomicBool
|
||||||
@@ -118,7 +117,7 @@ func (pkt *packet) setMark(mark int) error {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Tracer(pkt.Ctx()).Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.received))
|
log.Tracer(pkt.Ctx()).Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.Info().SeenAt))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/process"
|
||||||
|
|
||||||
"github.com/tevino/abool"
|
"github.com/tevino/abool"
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
@@ -103,21 +106,28 @@ func Handler(packets chan packet.Packet) {
|
|||||||
verdictRequest: packetInfo,
|
verdictRequest: packetInfo,
|
||||||
verdictSet: abool.NewBool(false),
|
verdictSet: abool.NewBool(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
info := new.Info()
|
info := new.Info()
|
||||||
info.Inbound = packetInfo.direction > 0
|
info.Inbound = packetInfo.direction > 0
|
||||||
info.InTunnel = false
|
info.InTunnel = false
|
||||||
info.Protocol = packet.IPProtocol(packetInfo.protocol)
|
info.Protocol = packet.IPProtocol(packetInfo.protocol)
|
||||||
info.PID = packetInfo.pid
|
info.PID = int(packetInfo.pid)
|
||||||
|
info.SeenAt = time.Now()
|
||||||
|
|
||||||
// IP version
|
// Check PID
|
||||||
|
if info.PID == 0 {
|
||||||
|
// Windows does not have zero PIDs.
|
||||||
|
// Set to UndefinedProcessID.
|
||||||
|
info.PID = process.UndefinedProcessID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set IP version
|
||||||
if packetInfo.ipV6 == 1 {
|
if packetInfo.ipV6 == 1 {
|
||||||
info.Version = packet.IPv6
|
info.Version = packet.IPv6
|
||||||
} else {
|
} else {
|
||||||
info.Version = packet.IPv4
|
info.Version = packet.IPv4
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPs
|
// Set IPs
|
||||||
if info.Version == packet.IPv4 {
|
if info.Version == packet.IPv4 {
|
||||||
// IPv4
|
// IPv4
|
||||||
if info.Inbound {
|
if info.Inbound {
|
||||||
@@ -142,7 +152,7 @@ func Handler(packets chan packet.Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ports
|
// Set Ports
|
||||||
if info.Inbound {
|
if info.Inbound {
|
||||||
// Inbound
|
// Inbound
|
||||||
info.SrcPort = packetInfo.remotePort
|
info.SrcPort = packetInfo.remotePort
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package windowskext
|
package windowskext
|
||||||
@@ -23,6 +24,12 @@ type Packet struct {
|
|||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InfoOnly returns whether the packet is informational only and does not
|
||||||
|
// represent an actual packet.
|
||||||
|
func (pkt *Packet) InfoOnly() bool {
|
||||||
|
return pkt.verdictRequest.flags&VerdictRequestFlagSocketAuth > 0
|
||||||
|
}
|
||||||
|
|
||||||
// FastTrackedByIntegration returns whether the packet has been fast-track
|
// FastTrackedByIntegration returns whether the packet has been fast-track
|
||||||
// accepted by the OS integration.
|
// accepted by the OS integration.
|
||||||
func (pkt *Packet) FastTrackedByIntegration() bool {
|
func (pkt *Packet) FastTrackedByIntegration() bool {
|
||||||
|
|||||||
@@ -25,19 +25,6 @@ import (
|
|||||||
"github.com/safing/portmaster/profile/endpoints"
|
"github.com/safing/portmaster/profile/endpoints"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call order:
|
|
||||||
//
|
|
||||||
// DNS Query:
|
|
||||||
// 1. DecideOnConnection
|
|
||||||
// is called when a DNS query is made, may set verdict to Undeterminable to permit a DNS reply.
|
|
||||||
// is called with a nil packet.
|
|
||||||
// 2. DecideOnResolvedDNS
|
|
||||||
// is called to (possibly) filter out A/AAAA records that the filter would deny later.
|
|
||||||
//
|
|
||||||
// Network Connection:
|
|
||||||
// 3. DecideOnConnection
|
|
||||||
// is called with the first packet of a network connection.
|
|
||||||
|
|
||||||
const noReasonOptionKey = ""
|
const noReasonOptionKey = ""
|
||||||
|
|
||||||
type deciderFn func(context.Context, *network.Connection, *profile.LayeredProfile, packet.Packet) bool
|
type deciderFn func(context.Context, *network.Connection, *profile.LayeredProfile, packet.Packet) bool
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package firewall
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/safing/portbase/api"
|
|
||||||
"github.com/safing/portbase/config"
|
|
||||||
"github.com/safing/portbase/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
var packetHandlingHistogram *metrics.Histogram
|
|
||||||
|
|
||||||
func registerMetrics() (err error) {
|
|
||||||
packetHandlingHistogram, err = metrics.NewHistogram(
|
|
||||||
"firewall/handling/duration/seconds",
|
|
||||||
nil,
|
|
||||||
&metrics.Options{
|
|
||||||
Permission: api.PermitUser,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -87,6 +87,9 @@ func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connect
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
model, err := convertConnection(conn)
|
model, err := convertConnection(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
|||||||
|
|
||||||
// delete inactive connections
|
// delete inactive connections
|
||||||
switch {
|
switch {
|
||||||
|
case !conn.DataIsComplete():
|
||||||
|
// Step 0: delete old incomplete connections
|
||||||
|
if conn.Started < deleteOlderThan {
|
||||||
|
conn.delete()
|
||||||
|
}
|
||||||
case conn.Ended == 0:
|
case conn.Ended == 0:
|
||||||
// Step 1: check if still active
|
// Step 1: check if still active
|
||||||
exists := state.Exists(&packet.Info{
|
exists := state.Exists(&packet.Info{
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
|
||||||
"github.com/safing/portbase/database/record"
|
"github.com/safing/portbase/database/record"
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
"github.com/safing/portmaster/intel"
|
"github.com/safing/portmaster/intel"
|
||||||
@@ -102,6 +104,8 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
|||||||
// set for connections created from DNS requests. LocalPort is
|
// set for connections created from DNS requests. LocalPort is
|
||||||
// considered immutable once a connection object has been created.
|
// considered immutable once a connection object has been created.
|
||||||
LocalPort uint16
|
LocalPort uint16
|
||||||
|
// PID holds the PID of the owning process.
|
||||||
|
PID int
|
||||||
// Entity describes the remote entity that the connection has been
|
// Entity describes the remote entity that the connection has been
|
||||||
// established to. The entity might be changed or information might
|
// established to. The entity might be changed or information might
|
||||||
// be added to it during the livetime of a connection. Access to
|
// be added to it during the livetime of a connection. Access to
|
||||||
@@ -168,6 +172,19 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
|||||||
StopTunnel() error
|
StopTunnel() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pkgQueue is used to serialize packet handling for a single
|
||||||
|
// connection and is served by the connections packetHandler.
|
||||||
|
pktQueue chan packet.Packet
|
||||||
|
// pktQueueActive signifies whether the packet queue is active and may be written to.
|
||||||
|
pktQueueActive bool
|
||||||
|
// pktQueueLock locks access to pktQueueActive and writing to pktQueue.
|
||||||
|
pktQueueLock sync.Mutex
|
||||||
|
|
||||||
|
// dataComplete signifies that all information about the connection is
|
||||||
|
// available and an actual packet has been seen.
|
||||||
|
// As long as this flag is not set, the connection may not be evaluated for
|
||||||
|
// a verdict and may not be sent to the UI.
|
||||||
|
dataComplete *abool.AtomicBool
|
||||||
// Internal is set to true if the connection is attributed as an
|
// Internal is set to true if the connection is attributed as an
|
||||||
// Portmaster internal connection. Internal may be set at different
|
// Portmaster internal connection. Internal may be set at different
|
||||||
// points and access to it must be guarded by the connection lock.
|
// points and access to it must be guarded by the connection lock.
|
||||||
@@ -175,9 +192,6 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
|||||||
// process holds a reference to the actor process. That is, the
|
// process holds a reference to the actor process. That is, the
|
||||||
// process instance that initiated the connection.
|
// process instance that initiated the connection.
|
||||||
process *process.Process
|
process *process.Process
|
||||||
// pkgQueue is used to serialize packet handling for a single
|
|
||||||
// connection and is served by the connections packetHandler.
|
|
||||||
pktQueue chan packet.Packet
|
|
||||||
// firewallHandler is the firewall handler that is called for
|
// firewallHandler is the firewall handler that is called for
|
||||||
// each packet sent to pktQueue.
|
// each packet sent to pktQueue.
|
||||||
firewallHandler FirewallHandler
|
firewallHandler FirewallHandler
|
||||||
@@ -250,8 +264,11 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
|||||||
ipVersion = packet.IPv4
|
ipVersion = packet.IPv4
|
||||||
}
|
}
|
||||||
|
|
||||||
// get Process
|
// Get Process.
|
||||||
proc, _, err := process.GetProcessByConnection(
|
// FIXME: Find direct or redirected connection and grab the PID from there.
|
||||||
|
|
||||||
|
// Find process by remote IP/Port.
|
||||||
|
pid, _, _ := process.GetPidOfConnection(
|
||||||
ctx,
|
ctx,
|
||||||
&packet.Info{
|
&packet.Info{
|
||||||
Inbound: false, // outbound as we are looking for the process of the source address
|
Inbound: false, // outbound as we are looking for the process of the source address
|
||||||
@@ -261,18 +278,17 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
|||||||
SrcPort: localPort, // source as in the process we are looking for
|
SrcPort: localPort, // source as in the process we are looking for
|
||||||
Dst: nil, // do not record direction
|
Dst: nil, // do not record direction
|
||||||
DstPort: 0, // do not record direction
|
DstPort: 0, // do not record direction
|
||||||
|
PID: process.UndefinedProcessID,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
proc, _ := process.GetProcessWithProfile(ctx, pid)
|
||||||
log.Tracer(ctx).Debugf("network: failed to find process of dns request for %s: %s", fqdn, err)
|
|
||||||
proc = process.GetUnidentifiedProcess(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp := time.Now().Unix()
|
timestamp := time.Now().Unix()
|
||||||
dnsConn := &Connection{
|
dnsConn := &Connection{
|
||||||
ID: connID,
|
ID: connID,
|
||||||
Type: DNSRequest,
|
Type: DNSRequest,
|
||||||
Scope: fqdn,
|
Scope: fqdn,
|
||||||
|
PID: proc.Pid,
|
||||||
Entity: &intel.Entity{
|
Entity: &intel.Entity{
|
||||||
Domain: fqdn,
|
Domain: fqdn,
|
||||||
CNAME: cnames,
|
CNAME: cnames,
|
||||||
@@ -281,6 +297,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
|||||||
ProcessContext: getProcessContext(ctx, proc),
|
ProcessContext: getProcessContext(ctx, proc),
|
||||||
Started: timestamp,
|
Started: timestamp,
|
||||||
Ended: timestamp,
|
Ended: timestamp,
|
||||||
|
dataComplete: abool.NewBool(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherit internal status of profile.
|
// Inherit internal status of profile.
|
||||||
@@ -292,6 +309,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
|||||||
// query. Blocked requests are saved immediately, accepted ones are only
|
// query. Blocked requests are saved immediately, accepted ones are only
|
||||||
// saved if they are not "used" by a connection.
|
// saved if they are not "used" by a connection.
|
||||||
|
|
||||||
|
dnsConn.UpdateMeta()
|
||||||
return dnsConn
|
return dnsConn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +326,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
|||||||
Type: DNSRequest,
|
Type: DNSRequest,
|
||||||
External: true,
|
External: true,
|
||||||
Scope: fqdn,
|
Scope: fqdn,
|
||||||
|
PID: process.NetworkHostProcessID,
|
||||||
Entity: &intel.Entity{
|
Entity: &intel.Entity{
|
||||||
Domain: fqdn,
|
Domain: fqdn,
|
||||||
CNAME: cnames,
|
CNAME: cnames,
|
||||||
@@ -316,6 +335,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
|||||||
ProcessContext: getProcessContext(ctx, remoteHost),
|
ProcessContext: getProcessContext(ctx, remoteHost),
|
||||||
Started: timestamp,
|
Started: timestamp,
|
||||||
Ended: timestamp,
|
Ended: timestamp,
|
||||||
|
dataComplete: abool.NewBool(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherit internal status of profile.
|
// Inherit internal status of profile.
|
||||||
@@ -327,131 +347,152 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
|||||||
// query. Blocked requests are saved immediately, accepted ones are only
|
// query. Blocked requests are saved immediately, accepted ones are only
|
||||||
// saved if they are not "used" by a connection.
|
// saved if they are not "used" by a connection.
|
||||||
|
|
||||||
|
dnsConn.UpdateMeta()
|
||||||
return dnsConn, nil
|
return dnsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConnectionFromFirstPacket returns a new connection based on the given packet.
|
// NewIncompleteConnection creates a new incomplete connection with only minimal information.
|
||||||
func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
func NewIncompleteConnection(pkt packet.Packet) *Connection {
|
||||||
// get Process
|
info := pkt.Info()
|
||||||
proc, inbound, err := process.GetProcessByConnection(pkt.Ctx(), pkt.Info())
|
|
||||||
if err != nil {
|
// Create new connection object.
|
||||||
log.Tracer(pkt.Ctx()).Debugf("network: failed to find process of packet %s: %s", pkt, err)
|
// We do not yet know the direction of the connection for sure, so we can only set minimal information.
|
||||||
if inbound && !netutils.ClassifyIP(pkt.Info().Dst).IsLocalhost() {
|
conn := &Connection{
|
||||||
proc = process.GetUnsolicitedProcess(pkt.Ctx())
|
ID: pkt.GetConnectionID(),
|
||||||
} else {
|
Type: IPConnection,
|
||||||
proc = process.GetUnidentifiedProcess(pkt.Ctx())
|
IPVersion: info.Version,
|
||||||
}
|
IPProtocol: info.Protocol,
|
||||||
|
Started: info.SeenAt.Unix(),
|
||||||
|
PID: info.PID,
|
||||||
|
dataComplete: abool.NewBool(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the (remote) entity.
|
// Save connection to internal state in order to mitigate creation of
|
||||||
entity := &intel.Entity{
|
// duplicates. Do not propagate yet, as data is not yet complete.
|
||||||
Protocol: uint8(pkt.Info().Protocol),
|
conn.UpdateMeta()
|
||||||
Port: pkt.Info().RemotePort(),
|
conns.add(conn)
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatherConnectionInfo gathers information on the process and remote entity.
|
||||||
|
func (conn *Connection) GatherConnectionInfo(pkt packet.Packet) (err error) {
|
||||||
|
// Get PID if not yet available.
|
||||||
|
// FIXME: Only match for UndefinedProcessID when integrations have been updated.
|
||||||
|
if conn.PID <= 0 {
|
||||||
|
// Get process by looking at the system state tables.
|
||||||
|
// Apply direction as reported from the state tables.
|
||||||
|
conn.PID, conn.Inbound, _ = process.GetPidOfConnection(pkt.Ctx(), pkt.Info())
|
||||||
|
// Errors are informational and are logged to the context.
|
||||||
}
|
}
|
||||||
entity.SetIP(pkt.Info().RemoteIP())
|
|
||||||
entity.SetDstPort(pkt.Info().DstPort)
|
|
||||||
|
|
||||||
var scope string
|
// Get Process and Profile.
|
||||||
var resolverInfo *resolver.ResolverInfo
|
if conn.process == nil {
|
||||||
var dnsContext *resolver.DNSRequestContext
|
// We got connection from the system.
|
||||||
|
conn.process, err = process.GetProcessWithProfile(pkt.Ctx(), conn.PID)
|
||||||
if inbound {
|
|
||||||
switch entity.IPScope {
|
|
||||||
case netutils.HostLocal:
|
|
||||||
scope = IncomingHost
|
|
||||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
|
||||||
scope = IncomingLAN
|
|
||||||
case netutils.Global, netutils.GlobalMulticast:
|
|
||||||
scope = IncomingInternet
|
|
||||||
|
|
||||||
case netutils.Undefined, netutils.Invalid:
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
scope = IncomingInvalid
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// check if we can find a domain for that IP
|
|
||||||
ipinfo, err := resolver.GetIPInfo(proc.Profile().LocalProfile().ID, pkt.Info().RemoteIP().String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Try again with the global scope, in case DNS went through the system resolver.
|
conn.process = nil
|
||||||
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
|
err = fmt.Errorf("failed to get process and profile of PID %d: %w", conn.PID, err)
|
||||||
}
|
log.Tracer(pkt.Ctx()).Debugf("network: %s", err)
|
||||||
if err == nil {
|
return err
|
||||||
lastResolvedDomain := ipinfo.MostRecentDomain()
|
|
||||||
if lastResolvedDomain != nil {
|
|
||||||
scope = lastResolvedDomain.Domain
|
|
||||||
entity.Domain = lastResolvedDomain.Domain
|
|
||||||
entity.CNAME = lastResolvedDomain.CNAMEs
|
|
||||||
dnsContext = lastResolvedDomain.DNSRequestContext
|
|
||||||
resolverInfo = lastResolvedDomain.Resolver
|
|
||||||
removeOpenDNSRequest(proc.Pid, lastResolvedDomain.Domain)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if destination IP is the captive portal's IP
|
// Add process/profile metadata for connection.
|
||||||
portal := netenv.GetCaptivePortal()
|
conn.ProcessContext = getProcessContext(pkt.Ctx(), conn.process)
|
||||||
if pkt.Info().RemoteIP().Equal(portal.IP) {
|
conn.ProfileRevisionCounter = conn.process.Profile().RevisionCnt()
|
||||||
scope = portal.Domain
|
|
||||||
entity.Domain = portal.Domain
|
|
||||||
}
|
|
||||||
|
|
||||||
if scope == "" {
|
// Inherit internal status of profile.
|
||||||
// outbound direct (possibly P2P) connection
|
if localProfile := conn.process.Profile().LocalProfile(); localProfile != nil {
|
||||||
switch entity.IPScope {
|
conn.Internal = localProfile.Internal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create remote entity.
|
||||||
|
if conn.Entity == nil {
|
||||||
|
// Remote
|
||||||
|
conn.Entity = &intel.Entity{
|
||||||
|
Protocol: uint8(pkt.Info().Protocol),
|
||||||
|
Port: pkt.Info().RemotePort(),
|
||||||
|
}
|
||||||
|
conn.Entity.SetIP(pkt.Info().RemoteIP())
|
||||||
|
conn.Entity.SetDstPort(pkt.Info().DstPort)
|
||||||
|
// Local
|
||||||
|
conn.SetLocalIP(pkt.Info().LocalIP())
|
||||||
|
conn.LocalPort = pkt.Info().LocalPort()
|
||||||
|
|
||||||
|
if conn.Inbound {
|
||||||
|
switch conn.Entity.IPScope {
|
||||||
case netutils.HostLocal:
|
case netutils.HostLocal:
|
||||||
scope = PeerHost
|
conn.Scope = IncomingHost
|
||||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||||
scope = PeerLAN
|
conn.Scope = IncomingLAN
|
||||||
case netutils.Global, netutils.GlobalMulticast:
|
case netutils.Global, netutils.GlobalMulticast:
|
||||||
scope = PeerInternet
|
conn.Scope = IncomingInternet
|
||||||
|
|
||||||
case netutils.Undefined, netutils.Invalid:
|
case netutils.Undefined, netutils.Invalid:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
scope = PeerInvalid
|
conn.Scope = IncomingInvalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// check if we can find a domain for that IP
|
||||||
|
ipinfo, err := resolver.GetIPInfo(conn.process.Profile().LocalProfile().ID, pkt.Info().RemoteIP().String())
|
||||||
|
if err != nil {
|
||||||
|
// Try again with the global scope, in case DNS went through the system resolver.
|
||||||
|
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
lastResolvedDomain := ipinfo.MostRecentDomain()
|
||||||
|
if lastResolvedDomain != nil {
|
||||||
|
conn.Scope = lastResolvedDomain.Domain
|
||||||
|
conn.Entity.Domain = lastResolvedDomain.Domain
|
||||||
|
conn.Entity.CNAME = lastResolvedDomain.CNAMEs
|
||||||
|
conn.DNSContext = lastResolvedDomain.DNSRequestContext
|
||||||
|
conn.Resolver = lastResolvedDomain.Resolver
|
||||||
|
removeOpenDNSRequest(conn.process.Pid, lastResolvedDomain.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if destination IP is the captive portal's IP
|
||||||
|
portal := netenv.GetCaptivePortal()
|
||||||
|
if pkt.Info().RemoteIP().Equal(portal.IP) {
|
||||||
|
conn.Scope = portal.Domain
|
||||||
|
conn.Entity.Domain = portal.Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.Scope == "" {
|
||||||
|
// outbound direct (possibly P2P) connection
|
||||||
|
switch conn.Entity.IPScope {
|
||||||
|
case netutils.HostLocal:
|
||||||
|
conn.Scope = PeerHost
|
||||||
|
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||||
|
conn.Scope = PeerLAN
|
||||||
|
case netutils.Global, netutils.GlobalMulticast:
|
||||||
|
conn.Scope = PeerInternet
|
||||||
|
|
||||||
|
case netutils.Undefined, netutils.Invalid:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
conn.Scope = PeerInvalid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new connection object.
|
// Data collection is only complete with a packet.
|
||||||
newConn := &Connection{
|
if pkt.InfoOnly() {
|
||||||
ID: pkt.GetConnectionID(),
|
return nil
|
||||||
Type: IPConnection,
|
|
||||||
Scope: scope,
|
|
||||||
IPVersion: pkt.Info().Version,
|
|
||||||
Inbound: inbound,
|
|
||||||
// local endpoint
|
|
||||||
IPProtocol: pkt.Info().Protocol,
|
|
||||||
LocalPort: pkt.Info().LocalPort(),
|
|
||||||
ProcessContext: getProcessContext(pkt.Ctx(), proc),
|
|
||||||
DNSContext: dnsContext,
|
|
||||||
process: proc,
|
|
||||||
// remote endpoint
|
|
||||||
Entity: entity,
|
|
||||||
// resolver used to resolve dns request
|
|
||||||
Resolver: resolverInfo,
|
|
||||||
// meta
|
|
||||||
Started: time.Now().Unix(),
|
|
||||||
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
|
||||||
}
|
|
||||||
newConn.SetLocalIP(pkt.Info().LocalIP())
|
|
||||||
|
|
||||||
// Inherit internal status of profile.
|
|
||||||
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
|
|
||||||
newConn.Internal = localProfile.Internal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save connection to internal state in order to mitigate creation of
|
// If we have all data and have seen an actual packet, the connection data is complete.
|
||||||
// duplicates. Do not propagate yet, as there is no verdict yet.
|
conn.dataComplete.Set()
|
||||||
conns.add(newConn)
|
return nil
|
||||||
|
|
||||||
return newConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConnection fetches a Connection from the database.
|
// GetConnection fetches a Connection from the database.
|
||||||
func GetConnection(id string) (*Connection, bool) {
|
func GetConnection(connID string) (*Connection, bool) {
|
||||||
return conns.get(id)
|
return conns.get(connID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllConnections Gets all connection.
|
// GetAllConnections Gets all connection.
|
||||||
@@ -563,6 +604,14 @@ func (conn *Connection) VerdictVerb() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataIsComplete returns whether all information about the connection is
|
||||||
|
// available and an actual packet has been seen.
|
||||||
|
// As long as this flag is not set, the connection may not be evaluated for
|
||||||
|
// a verdict and may not be sent to the UI.
|
||||||
|
func (conn *Connection) DataIsComplete() bool {
|
||||||
|
return conn.dataComplete.IsSet()
|
||||||
|
}
|
||||||
|
|
||||||
// Process returns the connection's process.
|
// Process returns the connection's process.
|
||||||
func (conn *Connection) Process() *process.Process {
|
func (conn *Connection) Process() *process.Process {
|
||||||
return conn.process
|
return conn.process
|
||||||
@@ -579,9 +628,13 @@ func (conn *Connection) SaveWhenFinished() {
|
|||||||
// Callers must make sure to lock the connection itself before calling
|
// Callers must make sure to lock the connection itself before calling
|
||||||
// Save().
|
// Save().
|
||||||
func (conn *Connection) Save() {
|
func (conn *Connection) Save() {
|
||||||
conn.addToMetrics()
|
|
||||||
conn.UpdateMeta()
|
conn.UpdateMeta()
|
||||||
|
|
||||||
|
// Do not save/update until data is complete.
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !conn.KeyIsSet() {
|
if !conn.KeyIsSet() {
|
||||||
if conn.Type == DNSRequest {
|
if conn.Type == DNSRequest {
|
||||||
conn.SetKey(makeKey(conn.process.Pid, dbScopeDNS, conn.ID))
|
conn.SetKey(makeKey(conn.process.Pid, dbScopeDNS, conn.ID))
|
||||||
@@ -592,6 +645,8 @@ func (conn *Connection) Save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.addToMetrics()
|
||||||
|
|
||||||
// notify database controller
|
// notify database controller
|
||||||
dbController.PushUpdate(conn)
|
dbController.PushUpdate(conn)
|
||||||
}
|
}
|
||||||
@@ -610,29 +665,61 @@ func (conn *Connection) delete() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn.Meta().Delete()
|
conn.Meta().Delete()
|
||||||
dbController.PushUpdate(conn)
|
|
||||||
|
// Notify database controller if data is complete and thus connection was previously exposed.
|
||||||
|
if conn.DataIsComplete() {
|
||||||
|
dbController.PushUpdate(conn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
||||||
// worker to handle the packets.
|
// worker to handle the packets.
|
||||||
// The caller needs to hold a lock on the connection.
|
// 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) {
|
func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
|
||||||
if conn.firewallHandler == nil {
|
if handler == nil {
|
||||||
conn.pktQueue = make(chan packet.Packet, 100)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start packet handler worker when first handler is set.
|
||||||
|
if conn.firewallHandler == nil {
|
||||||
// start handling
|
// start handling
|
||||||
module.StartWorker("packet handler", conn.packetHandlerWorker)
|
module.StartWorker("packet handler", conn.packetHandlerWorker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set new handler.
|
||||||
conn.firewallHandler = handler
|
conn.firewallHandler = handler
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
// StopFirewallHandler unsets the firewall handler and stops the handler worker.
|
||||||
// The caller needs to hold a lock on the connection.
|
// The caller needs to hold a lock on the connection.
|
||||||
func (conn *Connection) StopFirewallHandler() {
|
func (conn *Connection) StopFirewallHandler() {
|
||||||
|
conn.pktQueueLock.Lock()
|
||||||
|
defer conn.pktQueueLock.Unlock()
|
||||||
|
|
||||||
|
// Unset the firewall handler to revert to the default handler.
|
||||||
conn.firewallHandler = nil
|
conn.firewallHandler = nil
|
||||||
|
|
||||||
// Signal the packet handler worker that it can stop.
|
// Signal the packet handler worker that it can stop.
|
||||||
close(conn.pktQueue)
|
close(conn.pktQueue)
|
||||||
|
conn.pktQueueActive = false
|
||||||
|
|
||||||
// Unset the packet queue so that it can be freed.
|
// Unset the packet queue so that it can be freed.
|
||||||
conn.pktQueue = nil
|
conn.pktQueue = nil
|
||||||
@@ -640,15 +727,25 @@ func (conn *Connection) StopFirewallHandler() {
|
|||||||
|
|
||||||
// HandlePacket queues packet of Link for handling.
|
// HandlePacket queues packet of Link for handling.
|
||||||
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
||||||
conn.Lock()
|
conn.pktQueueLock.Lock()
|
||||||
defer conn.Unlock()
|
defer conn.pktQueueLock.Unlock()
|
||||||
|
|
||||||
// execute handler or verdict
|
// execute handler or verdict
|
||||||
if conn.firewallHandler != nil {
|
if conn.pktQueueActive {
|
||||||
conn.pktQueue <- pkt
|
select {
|
||||||
// TODO: drop if overflowing?
|
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 {
|
} else {
|
||||||
defaultFirewallHandler(conn, pkt)
|
defaultFirewallHandler(conn, pkt)
|
||||||
|
|
||||||
|
// Record metrics.
|
||||||
|
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +753,12 @@ func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
|||||||
func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
||||||
// Copy packet queue, so we can remove the reference from the connection
|
// Copy packet queue, so we can remove the reference from the connection
|
||||||
// when we stop the firewall handler.
|
// when we stop the firewall handler.
|
||||||
pktQueue := conn.pktQueue
|
var pktQueue chan packet.Packet
|
||||||
|
func() {
|
||||||
|
conn.pktQueueLock.Lock()
|
||||||
|
defer conn.pktQueueLock.Unlock()
|
||||||
|
pktQueue = conn.pktQueue
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -664,21 +766,27 @@ func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
|||||||
if pkt == nil {
|
if pkt == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
packetHandlerHandleConn(conn, pkt)
|
packetHandlerHandleConn(ctx, conn, pkt)
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
conn.Lock()
|
|
||||||
defer conn.Unlock()
|
|
||||||
conn.firewallHandler = nil
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) {
|
func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.Packet) {
|
||||||
conn.Lock()
|
conn.Lock()
|
||||||
defer conn.Unlock()
|
defer conn.Unlock()
|
||||||
|
|
||||||
|
// 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 appropriate handler.
|
// Handle packet with appropriate handler.
|
||||||
if conn.firewallHandler != nil {
|
if conn.firewallHandler != nil {
|
||||||
conn.firewallHandler(conn, pkt)
|
conn.firewallHandler(conn, pkt)
|
||||||
@@ -686,13 +794,22 @@ func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) {
|
|||||||
defaultFirewallHandler(conn, pkt)
|
defaultFirewallHandler(conn, pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log verdict.
|
// Record metrics.
|
||||||
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||||
// Submit trace logs.
|
|
||||||
log.Tracer(pkt.Ctx()).Submit()
|
|
||||||
|
|
||||||
// Save() itself does not touch any changing data.
|
// Log result and submit trace.
|
||||||
// Must not be locked - would deadlock with cleaner functions.
|
switch {
|
||||||
|
case conn.DataIsComplete():
|
||||||
|
tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
||||||
|
case conn.Verdict.Firewall != VerdictUndecided:
|
||||||
|
tracer.Debugf("filter: connection %s fast-tracked", conn)
|
||||||
|
default:
|
||||||
|
tracer.Infof("filter: gathered data on connection %s", conn)
|
||||||
|
}
|
||||||
|
// Submit trace logs.
|
||||||
|
tracer.Submit()
|
||||||
|
|
||||||
|
// Push changes, if there are any.
|
||||||
if conn.saveWhenFinished {
|
if conn.saveWhenFinished {
|
||||||
conn.saveWhenFinished = false
|
conn.saveWhenFinished = false
|
||||||
conn.Save()
|
conn.Save()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/safing/portmaster/network/packet"
|
"github.com/safing/portmaster/network/packet"
|
||||||
"github.com/safing/portmaster/process"
|
"github.com/safing/portmaster/process"
|
||||||
"github.com/safing/spn/navigator"
|
"github.com/safing/spn/navigator"
|
||||||
|
"github.com/tevino/abool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDefaultConnection creates a new connection with default values except local and remote IPs and protocols.
|
// NewDefaultConnection creates a new connection with default values except local and remote IPs and protocols.
|
||||||
@@ -25,6 +26,7 @@ func NewDefaultConnection(localIP net.IP, localPort uint16, remoteIP net.IP, rem
|
|||||||
LocalIP: localIP,
|
LocalIP: localIP,
|
||||||
LocalIPScope: netutils.Global,
|
LocalIPScope: netutils.Global,
|
||||||
LocalPort: localPort,
|
LocalPort: localPort,
|
||||||
|
PID: process.UnidentifiedProcessID,
|
||||||
Entity: &intel.Entity{
|
Entity: &intel.Entity{
|
||||||
Protocol: uint8(protocol),
|
Protocol: uint8(protocol),
|
||||||
IP: remoteIP,
|
IP: remoteIP,
|
||||||
@@ -35,6 +37,7 @@ func NewDefaultConnection(localIP net.IP, localPort uint16, remoteIP net.IP, rem
|
|||||||
VerdictPermanent: false,
|
VerdictPermanent: false,
|
||||||
Tunneled: true,
|
Tunneled: true,
|
||||||
Encrypted: false,
|
Encrypted: false,
|
||||||
|
DataComplete: abool.NewBool(true),
|
||||||
Internal: false,
|
Internal: false,
|
||||||
addedToMetrics: true, // Metrics are not needed for now. This will mark the Connection to be ignored.
|
addedToMetrics: true, // Metrics are not needed for now. This will mark the Connection to be ignored.
|
||||||
process: process.GetUnidentifiedProcess(context.Background()),
|
process: process.GetUnidentifiedProcess(context.Background()),
|
||||||
|
|||||||
@@ -90,12 +90,12 @@ func (s *StorageInterface) Get(key string) (record.Record, error) {
|
|||||||
|
|
||||||
switch scope {
|
switch scope {
|
||||||
case dbScopeDNS:
|
case dbScopeDNS:
|
||||||
if r, ok := dnsConns.get(id); ok {
|
if c, ok := dnsConns.get(id); ok && c.DataIsComplete() {
|
||||||
return r, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
case dbScopeIP:
|
case dbScopeIP:
|
||||||
if r, ok := conns.get(id); ok {
|
if c, ok := conns.get(id); ok && c.DataIsComplete() {
|
||||||
return r, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
case dbScopeNone:
|
case dbScopeNone:
|
||||||
if proc, ok := process.GetProcessFromStorage(pid); ok {
|
if proc, ok := process.GetProcessFromStorage(pid); ok {
|
||||||
@@ -143,11 +143,16 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|||||||
if scope == dbScopeNone || scope == dbScopeDNS {
|
if scope == dbScopeNone || scope == dbScopeDNS {
|
||||||
// dns scopes only
|
// dns scopes only
|
||||||
for _, dnsConn := range dnsConns.clone() {
|
for _, dnsConn := range dnsConns.clone() {
|
||||||
|
if !dnsConn.DataIsComplete() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
dnsConn.Lock()
|
dnsConn.Lock()
|
||||||
defer dnsConn.Unlock()
|
defer dnsConn.Unlock()
|
||||||
matches = q.Matches(dnsConn)
|
matches = q.Matches(dnsConn)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if matches {
|
if matches {
|
||||||
it.Next <- dnsConn
|
it.Next <- dnsConn
|
||||||
}
|
}
|
||||||
@@ -157,11 +162,16 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|||||||
if scope == dbScopeNone || scope == dbScopeIP {
|
if scope == dbScopeNone || scope == dbScopeIP {
|
||||||
// connections
|
// connections
|
||||||
for _, conn := range conns.clone() {
|
for _, conn := range conns.clone() {
|
||||||
|
if !conn.DataIsComplete() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
conn.Lock()
|
conn.Lock()
|
||||||
defer conn.Unlock()
|
defer conn.Unlock()
|
||||||
matches = q.Matches(conn)
|
matches = q.Matches(conn)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if matches {
|
if matches {
|
||||||
it.Next <- conn
|
it.Next <- conn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
packetHandlingHistogram *metrics.Histogram
|
||||||
blockedOutConnCounter *metrics.Counter
|
blockedOutConnCounter *metrics.Counter
|
||||||
encryptedAndTunneledOutConnCounter *metrics.Counter
|
encryptedAndTunneledOutConnCounter *metrics.Counter
|
||||||
encryptedOutConnCounter *metrics.Counter
|
encryptedOutConnCounter *metrics.Counter
|
||||||
@@ -15,8 +16,21 @@ var (
|
|||||||
outConnCounter *metrics.Counter
|
outConnCounter *metrics.Counter
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerMetrics() error {
|
func registerMetrics() (err error) {
|
||||||
_, err := metrics.NewGauge(
|
// This needed to be moved here, because every packet is now handled by the
|
||||||
|
// connection handler worker.
|
||||||
|
packetHandlingHistogram, err = metrics.NewHistogram(
|
||||||
|
"firewall/handling/duration/seconds",
|
||||||
|
nil,
|
||||||
|
&metrics.Options{
|
||||||
|
Permission: api.PermitUser,
|
||||||
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = metrics.NewGauge(
|
||||||
"network/connections/active/total",
|
"network/connections/active/total",
|
||||||
nil,
|
nil,
|
||||||
func() float64 {
|
func() float64 {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ func GetMulticastRequestConn(responseConn *Connection, responseFromNet *net.IPNe
|
|||||||
// Find requesting multicast/broadcast connection.
|
// Find requesting multicast/broadcast connection.
|
||||||
for _, conn := range conns.clone() {
|
for _, conn := range conns.clone() {
|
||||||
switch {
|
switch {
|
||||||
|
case !conn.DataIsComplete():
|
||||||
|
// Ignore connection with incomplete data.
|
||||||
case conn.Inbound:
|
case conn.Inbound:
|
||||||
// Ignore incoming connections.
|
// Ignore incoming connections.
|
||||||
case conn.Ended != 0:
|
case conn.Ended != 0:
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ func (pkt *Base) FastTrackedByIntegration() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InfoOnly returns whether the packet is informational only and does not
|
||||||
|
// represent an actual packet.
|
||||||
|
func (pkt *Base) InfoOnly() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// SetCtx sets the packet context.
|
// SetCtx sets the packet context.
|
||||||
func (pkt *Base) SetCtx(ctx context.Context) {
|
func (pkt *Base) SetCtx(ctx context.Context) {
|
||||||
pkt.ctx = ctx
|
pkt.ctx = ctx
|
||||||
@@ -107,6 +113,7 @@ func (pkt *Base) GetConnectionID() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pkt *Base) createConnectionID() {
|
func (pkt *Base) createConnectionID() {
|
||||||
|
// TODO: make this ID not depend on the packet direction for better support for forwarded packets.
|
||||||
if pkt.info.Protocol == TCP || pkt.info.Protocol == UDP {
|
if pkt.info.Protocol == TCP || pkt.info.Protocol == UDP {
|
||||||
if pkt.info.Inbound {
|
if pkt.info.Inbound {
|
||||||
pkt.connID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.info.Protocol, pkt.info.Dst, pkt.info.DstPort, pkt.info.Src, pkt.info.SrcPort)
|
pkt.connID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.info.Protocol, pkt.info.Dst, pkt.info.DstPort, pkt.info.Src, pkt.info.SrcPort)
|
||||||
@@ -236,6 +243,7 @@ type Packet interface {
|
|||||||
RerouteToNameserver() error
|
RerouteToNameserver() error
|
||||||
RerouteToTunnel() error
|
RerouteToTunnel() error
|
||||||
FastTrackedByIntegration() bool
|
FastTrackedByIntegration() bool
|
||||||
|
InfoOnly() bool
|
||||||
|
|
||||||
// Info.
|
// Info.
|
||||||
SetCtx(context.Context)
|
SetCtx(context.Context)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package packet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Info holds IP and TCP/UDP header information.
|
// Info holds IP and TCP/UDP header information.
|
||||||
@@ -14,7 +15,8 @@ type Info struct {
|
|||||||
SrcPort, DstPort uint16
|
SrcPort, DstPort uint16
|
||||||
Src, Dst net.IP
|
Src, Dst net.IP
|
||||||
|
|
||||||
PID uint32
|
PID int
|
||||||
|
SeenAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalIP returns the local IP of the packet.
|
// LocalIP returns the local IP of the packet.
|
||||||
|
|||||||
@@ -28,13 +28,18 @@ nextPort:
|
|||||||
// Check if the generated port is unused.
|
// Check if the generated port is unused.
|
||||||
nextConnection:
|
nextConnection:
|
||||||
for _, conn := range allConns {
|
for _, conn := range allConns {
|
||||||
// Skip connection if the protocol does not match the protocol of interest.
|
switch {
|
||||||
if conn.Entity.Protocol != protocol {
|
case !conn.DataIsComplete():
|
||||||
|
// Skip connection if the data is not complete.
|
||||||
continue nextConnection
|
continue nextConnection
|
||||||
}
|
|
||||||
// Skip port if the local port is in dangerous proximity.
|
case conn.Entity.Protocol != protocol:
|
||||||
// Consecutive port numbers are very common.
|
// Skip connection if the protocol does not match the protocol of interest.
|
||||||
if conn.LocalPort <= port && conn.LocalPort >= portRangeStart {
|
continue nextConnection
|
||||||
|
|
||||||
|
case conn.LocalPort <= port && conn.LocalPort >= portRangeStart:
|
||||||
|
// Skip port if the local port is in dangerous proximity.
|
||||||
|
// Consecutive port numbers are very common.
|
||||||
continue nextPort
|
continue nextPort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,43 +14,19 @@ import (
|
|||||||
"github.com/safing/portmaster/profile"
|
"github.com/safing/portmaster/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetProcessByConnection returns the process that owns the described connection.
|
// GetProcessWithProfile returns the process, including the profile.
|
||||||
func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process *Process, connInbound bool, err error) {
|
// Always returns valid data.
|
||||||
|
// Errors are logged and returned for information or special handling purposes.
|
||||||
|
func GetProcessWithProfile(ctx context.Context, pid int) (process *Process, err error) {
|
||||||
if !enableProcessDetection() {
|
if !enableProcessDetection() {
|
||||||
log.Tracer(ctx).Tracef("process: process detection disabled")
|
log.Tracer(ctx).Tracef("process: process detection disabled")
|
||||||
return GetUnidentifiedProcess(ctx), pktInfo.Inbound, nil
|
return GetUnidentifiedProcess(ctx), nil
|
||||||
}
|
|
||||||
|
|
||||||
// Use fast search for inbound packets, as the listening socket should
|
|
||||||
// already be there for a while now.
|
|
||||||
fastSearch := pktInfo.Inbound
|
|
||||||
|
|
||||||
var pid int
|
|
||||||
if pktInfo.PID == 0 {
|
|
||||||
log.Tracer(ctx).Tracef("process: getting pid from system network state")
|
|
||||||
pid, connInbound, err = state.Lookup(pktInfo, fastSearch)
|
|
||||||
if err != nil {
|
|
||||||
log.Tracer(ctx).Tracef("process: failed to find PID of connection: %s", err)
|
|
||||||
return nil, pktInfo.Inbound, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Tracer(ctx).Tracef("process: pid already set in packet (by ebpf or kext)")
|
|
||||||
pid = int(pktInfo.PID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to special profiles if PID could not be found.
|
|
||||||
if pid == UndefinedProcessID {
|
|
||||||
if connInbound {
|
|
||||||
pid = UnsolicitedProcessID
|
|
||||||
} else {
|
|
||||||
pid = UnidentifiedProcessID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process, err = GetOrFindProcess(ctx, pid)
|
process, err = GetOrFindProcess(ctx, pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracer(ctx).Debugf("process: failed to find (primary) process with PID: %s", err)
|
log.Tracer(ctx).Warningf("process: failed to find process with PID: %s", err)
|
||||||
return nil, connInbound, err
|
return GetUnidentifiedProcess(ctx), err
|
||||||
}
|
}
|
||||||
|
|
||||||
changed, err := process.GetProfile(ctx)
|
changed, err := process.GetProfile(ctx)
|
||||||
@@ -62,7 +38,46 @@ func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process
|
|||||||
process.Save()
|
process.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
return process, connInbound, nil
|
return process, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPidOfConnection returns the PID of the process that owns the described connection.
|
||||||
|
// Always returns valid data.
|
||||||
|
// Errors are logged and returned for information or special handling purposes.
|
||||||
|
func GetPidOfConnection(ctx context.Context, pktInfo *packet.Info) (pid int, connInbound bool, err error) {
|
||||||
|
if !enableProcessDetection() {
|
||||||
|
return UnidentifiedProcessID, pktInfo.Inbound, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use fast search for inbound packets, as the listening socket should
|
||||||
|
// already be there for a while now.
|
||||||
|
fastSearch := pktInfo.Inbound
|
||||||
|
connInbound = pktInfo.Inbound
|
||||||
|
|
||||||
|
// FIXME: Only match for UndefinedProcessID when integrations have been updated.
|
||||||
|
if pktInfo.PID <= 0 {
|
||||||
|
log.Tracer(ctx).Tracef("process: getting pid from system network state")
|
||||||
|
pid, connInbound, err = state.Lookup(pktInfo, fastSearch)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to find PID of connection: %w", err)
|
||||||
|
log.Tracer(ctx).Tracef("process: %s", err)
|
||||||
|
pid = UndefinedProcessID
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Tracer(ctx).Tracef("process: pid already set in packet (by ebpf or kext)")
|
||||||
|
pid = pktInfo.PID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to special profiles if PID could not be found.
|
||||||
|
if pid == UndefinedProcessID {
|
||||||
|
if connInbound && !netutils.ClassifyIP(pktInfo.Dst).IsLocalhost() {
|
||||||
|
pid = UnsolicitedProcessID
|
||||||
|
} else {
|
||||||
|
pid = UnidentifiedProcessID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid, connInbound, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNetworkHost returns a *Process that represents a host on the network.
|
// GetNetworkHost returns a *Process that represents a host on the network.
|
||||||
@@ -113,7 +128,12 @@ func GetProcessByRequestOrigin(ar *api.Request) (*Process, error) {
|
|||||||
SrcPort: remotePort, // source as in the process we are looking for
|
SrcPort: remotePort, // source as in the process we are looking for
|
||||||
}
|
}
|
||||||
|
|
||||||
proc, _, err := GetProcessByConnection(ar.Context(), pkt)
|
pid, _, err := GetPidOfConnection(ar.Context(), pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, err := GetProcessWithProfile(ar.Context(), pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UID
|
// UID
|
||||||
// net yet implemented for windows
|
// TODO: implemented for windows
|
||||||
if onLinux {
|
if onLinux {
|
||||||
var uids []int32
|
var uids []int32
|
||||||
uids, err = pInfo.UidsWithContext(ctx)
|
uids, err = pInfo.UidsWithContext(ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user