Skip to content

Commit

Permalink
Reduce PID finding retries and simply proc interface on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
dhaavi committed Dec 18, 2023
1 parent 6822b7a commit d67b1e8
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 72 deletions.
65 changes: 13 additions & 52 deletions network/proc/findpid.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,11 @@ import (
"io/fs"
"os"
"strconv"
"time"

"github.com/safing/portbase/log"
"github.com/safing/portmaster/network/socket"
)

var (
baseWaitTime = 3 * time.Millisecond
lookupRetries = 3
)

// GetPID returns the already existing pid of the given socket info or searches for it.
// This also acts as a getter for socket.Info.PID, as locking for that occurs here.
func GetPID(socketInfo socket.Info) (pid int) {
Expand All @@ -43,60 +37,27 @@ func GetPID(socketInfo socket.Info) (pid int) {
func findPID(uid, inode int) (pid int) {
socketName := "socket:[" + strconv.Itoa(inode) + "]"

for i := 0; i <= lookupRetries; i++ {
var pidsUpdated bool

// Get all pids for the given uid.
pids, ok := getPidsByUser(uid)
if !ok {
// If we cannot find the uid in the map, update it.
updatePids()
pidsUpdated = true
pids, ok = getPidsByUser(uid)
}
// Always update pid table (it has a call limiter anyway)
updatePids()

// If we have found PIDs, search them.
if ok {
// Look through the PIDs in reverse order, because higher/newer PIDs will be more likely to
// be searched for.
for i := len(pids) - 1; i >= 0; i-- {
if findSocketFromPid(pids[i], socketName) {
return pids[i]
}
}
}

// If we still cannot find our socket, and haven't yet updated the PID map,
// do this and then check again immediately.
if !pidsUpdated {
updatePids()
pids, ok = getPidsByUser(uid)
if ok {
// Look through the PIDs in reverse order, because higher/newer PIDs will be more likely to
// be searched for.
for i := len(pids) - 1; i >= 0; i-- {
if findSocketFromPid(pids[i], socketName) {
return pids[i]
}
}
}
}

// We have updated the PID map, but still cannot find anything.
// So, there is nothing we can do other than to wait a little for the kernel to
// populate the information.
// Get all pids for the given uid.
pids, ok := getPidsByUser(uid)
if !ok {
return socket.UndefinedProcessID
}

// Wait after each try, except for the last iteration
if i < lookupRetries {
// Wait in back-off fashion - with 3ms baseWaitTime: 3, 6, 9 - 18ms in total.
time.Sleep(time.Duration(i+1) * baseWaitTime)
// Look through the PIDs in reverse order, because higher/newer PIDs will be more likely to
// be searched for.
for j := len(pids) - 1; j >= 0; j-- {
if pidHasSocket(pids[j], socketName) {
return pids[j]
}
}

return socket.UndefinedProcessID
}

func findSocketFromPid(pid int, socketName string) bool {
func pidHasSocket(pid int, socketName string) bool {
socketBase := "/proc/" + strconv.Itoa(pid) + "/fd"
entries := readDirNames(socketBase)
if len(entries) == 0 {
Expand Down
5 changes: 5 additions & 0 deletions network/state/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ var (
ErrPIDNotFound = errors.New("could not find pid for socket inode")
)

const (
lookupTries = 5
fastLookupTries = 2
)

// Lookup looks for the given connection in the system state tables and returns the PID of the associated process and whether the connection is inbound.
func Lookup(pktInfo *packet.Info, fast bool) (pid int, inbound bool, err error) {
// auto-detect version
Expand Down
5 changes: 0 additions & 5 deletions network/state/system_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import (
"github.com/safing/portmaster/network/socket"
)

var (
lookupTries = 20 // With a max wait of 5ms, this amounts to up to 100ms.
fastLookupTries = 2
)

func init() {
// This increases performance on unsupported system.
// It's not critical at all and does not break anything if it fails.
Expand Down
14 changes: 6 additions & 8 deletions network/state/system_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ var (
getUDP4Table = proc.GetUDP4Table
getUDP6Table = proc.GetUDP6Table

lookupTries = 20 // With a max wait of 5ms, this amounts to up to 100ms.
fastLookupTries = 2

baseWaitTime = 3 * time.Millisecond
checkPIDTries = 5
checkPIDBaseWaitTime = 5 * time.Millisecond
)

// CheckPID checks the if socket info already has a PID and if not, tries to find it.
// Depending on the OS, this might be a no-op.
func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool, err error) {
for i := 1; i <= lookupTries; i++ {
for i := 1; i <= checkPIDTries; i++ {
// look for PID
pid = proc.GetPID(socketInfo)
if pid != socket.UndefinedProcessID {
Expand All @@ -31,10 +29,10 @@ func CheckPID(socketInfo socket.Info, connInbound bool) (pid int, inbound bool,
}

// every time, except for the last iteration
if i < lookupTries {
if i < checkPIDTries {
// we found no PID, we could have been too fast, give the kernel some time to think
// back off timer: with 3ms baseWaitTime: 3, 6, 9, 12, 15, 18, 21ms - 84ms in total
time.Sleep(time.Duration(i+1) * baseWaitTime)
// back off timer: with 5ms baseWaitTime: 5, 10, 15, 20, 25 - 75ms in total
time.Sleep(time.Duration(i) * checkPIDBaseWaitTime)
}
}

Expand Down
7 changes: 0 additions & 7 deletions network/state/system_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@ var (
getTCP6Table = iphelper.GetTCP6Table
getUDP4Table = iphelper.GetUDP4Table
getUDP6Table = iphelper.GetUDP6Table

// With a max wait of 5ms, this amounts to up to 25ms,
// excluding potential data fetching time.
// Measured on Windows: ~150ms
lookupTries = 5

fastLookupTries = 2
)

// CheckPID checks the if socket info already has a PID and if not, tries to find it.
Expand Down

0 comments on commit d67b1e8

Please sign in to comment.