mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 16:44:46 +01:00

- Fixed multiple race conditions when using the cache of PIDs. - Improved the chances to hit the cache of inodes, which helps to keep down the times to get the PID of a connection to <= 30us. These caches are mainly used when not using "ebpf" proc monitor method.
108 lines
2.7 KiB
Go
108 lines
2.7 KiB
Go
package procmon
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
func sortPidsByTime(fdList []os.FileInfo) []os.FileInfo {
|
|
sort.Slice(fdList, func(i, j int) bool {
|
|
t := fdList[i].ModTime().UnixNano()
|
|
u := fdList[j].ModTime().UnixNano()
|
|
return t > u
|
|
})
|
|
return fdList
|
|
}
|
|
|
|
// inodeFound searches for the given inode in /proc/<pid>/fd/ or
|
|
// /proc/<pid>/task/<tid>/fd/ and gets the symbolink link it points to,
|
|
// in order to compare it against the given inode.
|
|
//
|
|
// If the inode is found, the cache is updated ans sorted.
|
|
func inodeFound(pidsPath, expect, inodeKey string, inode, pid int) bool {
|
|
fdPath := fmt.Sprint(pidsPath, pid, "/fd/")
|
|
fdList := lookupPidDescriptors(fdPath, pid)
|
|
if fdList == nil {
|
|
return false
|
|
}
|
|
|
|
for idx := 0; idx < len(fdList); idx++ {
|
|
descLink := fmt.Sprint(fdPath, fdList[idx])
|
|
if link, err := os.Readlink(descLink); err == nil && link == expect {
|
|
inodesCache.add(inodeKey, descLink, pid)
|
|
pidsCache.add(fdPath, fdList, pid)
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// lookupPidInProc searches for an inode in /proc.
|
|
// First it gets the running PIDs and obtains the opened sockets.
|
|
// TODO: If the inode is not found, search again in the task/threads
|
|
// of every PID (costly).
|
|
func lookupPidInProc(pidsPath, expect, inodeKey string, inode int) int {
|
|
pidList := getProcPids(pidsPath)
|
|
for _, pid := range pidList {
|
|
if inodeFound(pidsPath, expect, inodeKey, inode, pid) {
|
|
return pid
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// lookupPidDescriptors returns the list of descriptors inside
|
|
// /proc/<pid>/fd/
|
|
// TODO: search in /proc/<pid>/task/<tid>/fd/ .
|
|
func lookupPidDescriptors(fdPath string, pid int) []string {
|
|
f, err := os.Open(fdPath)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
// This is where most of the time is wasted when looking for PIDs.
|
|
// long running processes like firefox/chrome tend to have a lot of descriptor
|
|
// references that points to non existent files on disk, but that remains in
|
|
// memory (those with " (deleted)").
|
|
// This causes to have to iterate over 300 to 700 items, that are not sockets.
|
|
fdList, err := f.Readdir(-1)
|
|
f.Close()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
fdList = sortPidsByTime(fdList)
|
|
|
|
s := make([]string, len(fdList))
|
|
for n, f := range fdList {
|
|
s[n] = f.Name()
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// getProcPids returns the list of running PIDs, /proc or /proc/<pid>/task/ .
|
|
func getProcPids(pidsPath string) (pidList []int) {
|
|
f, err := os.Open(pidsPath)
|
|
if err != nil {
|
|
return pidList
|
|
}
|
|
ls, err := f.Readdir(-1)
|
|
f.Close()
|
|
if err != nil {
|
|
return pidList
|
|
}
|
|
ls = sortPidsByTime(ls)
|
|
|
|
for _, f := range ls {
|
|
if f.IsDir() == false {
|
|
continue
|
|
}
|
|
if pid, err := strconv.Atoi(f.Name()); err == nil {
|
|
pidList = append(pidList, []int{pid}...)
|
|
}
|
|
}
|
|
|
|
return pidList
|
|
}
|