Fix: PROCESS-NAME on FreeBSD 11.x (#947)

This commit is contained in:
gVisor bot 2020-09-07 17:43:34 +08:00
parent 6b3f75e21b
commit a18ad955e7

View file

@ -8,6 +8,7 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"unsafe"
@ -17,7 +18,15 @@ import (
)
// store process name for when dealing with multiple PROCESS-NAME rules
var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
var (
processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
errNotFound = errors.New("process not found")
matchMeta = func(p *Process, m *C.Metadata) bool { return false }
defaultSearcher *searcher
once sync.Once
)
type Process struct {
adapter string
@ -28,7 +37,7 @@ func (ps *Process) RuleType() C.RuleType {
return C.Process
}
func (ps *Process) Match(metadata *C.Metadata) bool {
func match(ps *Process, metadata *C.Metadata) bool {
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
cached, hit := processCache.Get(key)
if !hit {
@ -45,6 +54,10 @@ func (ps *Process) Match(metadata *C.Metadata) bool {
return strings.EqualFold(cached.(string), ps.process)
}
func (ps *Process) Match(metadata *C.Metadata) bool {
return matchMeta(ps, metadata)
}
func (p *Process) Adapter() string {
return p.adapter
}
@ -58,6 +71,15 @@ func (p *Process) ShouldResolveIP() bool {
}
func NewProcess(process string, adapter string) (*Process, error) {
once.Do(func() {
err := initSearcher()
if err != nil {
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
log.Warnln("All PROCESS-NAME rules will be skipped")
return
}
matchMeta = match
})
return &Process{
adapter: adapter,
process: process,
@ -85,28 +107,6 @@ func getExecPathFromPID(pid uint32) (string, error) {
return filepath.Base(string(buf[:size-1])), nil
}
func searchSocketPid(socket uint64) (uint32, error) {
value, err := syscall.Sysctl("kern.file")
if err != nil {
return 0, err
}
buf := []byte(value)
// struct xfile
itemSize := 128
for i := 0; i+itemSize <= len(buf); i += itemSize {
// xfile.xf_data
data := binary.BigEndian.Uint64(buf[i+56 : i+64])
if data == socket {
// xfile.xf_pid
pid := readNativeUint32(buf[i+8 : i+12])
return pid, nil
}
}
return 0, errors.New("pid not found")
}
func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
ip := metadata.SrcIP
port, err := strconv.Atoi(metadata.SrcPort)
@ -115,25 +115,18 @@ func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
}
var spath string
var itemSize int
var inpOffset int
var isTCP bool
switch metadata.NetWork {
case C.TCP:
spath = "net.inet.tcp.pcblist"
// struct xtcpcb
itemSize = 744
inpOffset = 8
isTCP = true
case C.UDP:
spath = "net.inet.udp.pcblist"
// struct xinpcb
itemSize = 400
inpOffset = 0
isTCP = false
default:
return "", ErrInvalidNetwork
}
isIPv4 := ip.To4() != nil
value, err := syscall.Sysctl(spath)
if err != nil {
return "", err
@ -141,27 +134,73 @@ func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
buf := []byte(value)
// skip the first xinpgen(64 bytes) block
for i := 64; i+itemSize <= len(buf); i += itemSize {
pid, err := defaultSearcher.Search(buf, ip, uint16(port), isTCP)
if err != nil {
return "", err
}
return getExecPathFromPID(pid)
}
func readNativeUint32(b []byte) uint32 {
return *(*uint32)(unsafe.Pointer(&b[0]))
}
type searcher struct {
// sizeof(struct xinpgen)
headSize int
// sizeof(struct xtcpcb)
tcpItemSize int
// sizeof(struct xinpcb)
udpItemSize int
udpInpOffset int
port int
ip int
vflag int
socket int
// sizeof(struct xfile)
fileItemSize int
data int
pid int
}
func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint32, error) {
var itemSize int
var inpOffset int
if isTCP {
// struct xtcpcb
itemSize = s.tcpItemSize
inpOffset = 8
} else {
// struct xinpcb
itemSize = s.udpItemSize
inpOffset = s.udpInpOffset
}
isIPv4 := ip.To4() != nil
// skip the first xinpgen block
for i := s.headSize; i+itemSize <= len(buf); i += itemSize {
inp := i + inpOffset
srcPort := binary.BigEndian.Uint16(buf[inp+254 : inp+256])
srcPort := binary.BigEndian.Uint16(buf[inp+s.port : inp+s.port+2])
if uint16(port) != srcPort {
if port != srcPort {
continue
}
// xinpcb.inp_vflag
flag := buf[inp+392]
flag := buf[inp+s.vflag]
var srcIP net.IP
switch {
case flag&0x1 > 0 && isIPv4:
// ipv4
srcIP = net.IP(buf[inp+284 : inp+288])
srcIP = net.IP(buf[inp+s.ip : inp+s.ip+4])
case flag&0x2 > 0 && !isIPv4:
// ipv6
srcIP = net.IP(buf[inp+272 : inp+288])
srcIP = net.IP(buf[inp+s.ip-12 : inp+s.ip+4])
default:
continue
}
@ -171,17 +210,85 @@ func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
}
// xsocket.xso_so, interpreted as big endian anyway since it's only used for comparison
socket := binary.BigEndian.Uint64(buf[inp+16 : inp+24])
pid, err := searchSocketPid(socket)
socket := binary.BigEndian.Uint64(buf[inp+s.socket : inp+s.socket+8])
return s.searchSocketPid(socket)
}
return 0, errNotFound
}
func (s *searcher) searchSocketPid(socket uint64) (uint32, error) {
value, err := syscall.Sysctl("kern.file")
if err != nil {
return "", err
}
return getExecPathFromPID(pid)
return 0, err
}
return "", errors.New("process not found")
buf := []byte(value)
// struct xfile
itemSize := s.fileItemSize
for i := 0; i+itemSize <= len(buf); i += itemSize {
// xfile.xf_data
data := binary.BigEndian.Uint64(buf[i+s.data : i+s.data+8])
if data == socket {
// xfile.xf_pid
pid := readNativeUint32(buf[i+s.pid : i+s.pid+4])
return pid, nil
}
}
return 0, errNotFound
}
func readNativeUint32(b []byte) uint32 {
return *(*uint32)(unsafe.Pointer(&b[0]))
func newSearcher(major int) *searcher {
var s *searcher = nil
switch major {
case 11:
s = &searcher{
headSize: 32,
tcpItemSize: 1304,
udpItemSize: 632,
port: 198,
ip: 228,
vflag: 116,
socket: 88,
fileItemSize: 80,
data: 56,
pid: 8,
udpInpOffset: 8,
}
case 12:
s = &searcher{
headSize: 64,
tcpItemSize: 744,
udpItemSize: 400,
port: 254,
ip: 284,
vflag: 392,
socket: 16,
fileItemSize: 128,
data: 56,
pid: 8,
}
}
return s
}
func initSearcher() error {
osRelease, err := syscall.Sysctl("kern.osrelease")
if err != nil {
return err
}
dot := strings.Index(osRelease, ".")
if dot != -1 {
osRelease = osRelease[:dot]
}
major, err := strconv.Atoi(osRelease)
if err != nil {
return err
}
defaultSearcher = newSearcher(major)
if defaultSearcher == nil {
return fmt.Errorf("unsupported freebsd version %d", major)
}
return nil
}