Files
kubeshark/tap/tlstapper/golang_offsets.go
2022-06-02 03:23:11 +03:00

153 lines
3.5 KiB
Go

package tlstapper
import (
"bufio"
"debug/elf"
"fmt"
"os"
"github.com/Masterminds/semver"
"github.com/cilium/ebpf/link"
)
type golangOffsets struct {
GolangDialOffset uint64
GolangSocketOffset uint64
GolangWriteOffset uint64
GolangReadOffset uint64
}
const (
minimumSupportedGoVersion = "1.17.0"
golangVersionSymbol = "runtime.buildVersion.str"
golangWriteSymbol = "crypto/tls.(*Conn).Write"
golangReadSymbol = "crypto/tls.(*Conn).Read"
golangSocketSymbol = "net.socket"
golangDialSymbol = "net/http.(*Transport).dialConn"
)
func findGolangOffsets(filePath string) (golangOffsets, error) {
offsets, err := getOffsets(filePath)
if err != nil {
return golangOffsets{}, err
}
goVersionOffset, err := getOffset(offsets, golangVersionSymbol)
if err != nil {
return golangOffsets{}, err
}
passed, goVersion, err := checkGoVersion(filePath, goVersionOffset)
if err != nil {
return golangOffsets{}, fmt.Errorf("Checking Go version: %s", err)
}
if !passed {
return golangOffsets{}, fmt.Errorf("Unsupported Go version: %s", goVersion)
}
dialOffset, err := getOffset(offsets, golangDialSymbol)
if err != nil {
return golangOffsets{}, fmt.Errorf("reading offset [%s]: %s", golangDialSymbol, err)
}
socketOffset, err := getOffset(offsets, golangSocketSymbol)
if err != nil {
return golangOffsets{}, fmt.Errorf("reading offset [%s]: %s", golangSocketSymbol, err)
}
writeOffset, err := getOffset(offsets, golangWriteSymbol)
if err != nil {
return golangOffsets{}, fmt.Errorf("reading offset [%s]: %s", golangWriteSymbol, err)
}
readOffset, err := getOffset(offsets, golangReadSymbol)
if err != nil {
return golangOffsets{}, fmt.Errorf("reading offset [%s]: %s", golangReadSymbol, err)
}
return golangOffsets{
GolangDialOffset: dialOffset,
GolangSocketOffset: socketOffset,
GolangWriteOffset: writeOffset,
GolangReadOffset: readOffset,
}, nil
}
func getOffsets(filePath string) (offsets map[string]uint64, err error) {
offsets = make(map[string]uint64)
var fd *os.File
fd, err = os.Open(filePath)
if err != nil {
return
}
defer fd.Close()
var se *elf.File
se, err = elf.NewFile(fd)
if err != nil {
return nil, err
}
syms, err := se.Symbols()
for _, sym := range syms {
offset := sym.Value
for _, prog := range se.Progs {
if prog.Vaddr <= sym.Value && sym.Value < (prog.Vaddr+prog.Memsz) {
offset = sym.Value - prog.Vaddr + prog.Off
break
}
}
offsets[sym.Name] = offset
}
return
}
func getOffset(offsets map[string]uint64, symbol string) (uint64, error) {
if offset, ok := offsets[symbol]; ok {
return offset, nil
}
return 0, fmt.Errorf("symbol %s: %w", symbol, link.ErrNoSymbol)
}
func checkGoVersion(filePath string, offset uint64) (bool, string, error) {
fd, err := os.Open(filePath)
if err != nil {
return false, "", err
}
defer fd.Close()
reader := bufio.NewReader(fd)
_, err = reader.Discard(int(offset))
if err != nil {
return false, "", err
}
line, err := reader.ReadString(0)
if err != nil {
return false, "", err
}
if len(line) < 3 {
return false, "", fmt.Errorf("ELF data segment read error (corrupted result)")
}
goVersionStr := line[2 : len(line)-1]
goVersion, err := semver.NewVersion(goVersionStr)
if err != nil {
return false, goVersionStr, err
}
goVersionConstraint, err := semver.NewConstraint(fmt.Sprintf(">= %s", minimumSupportedGoVersion))
if err != nil {
return false, goVersionStr, err
}
return goVersionConstraint.Check(goVersion), goVersionStr, nil
}