|
|
@@ -1,6 +1,8 @@
|
|
|
package ebpftracer
|
|
|
|
|
|
import (
|
|
|
+ "bufio"
|
|
|
+ "bytes"
|
|
|
"debug/buildinfo"
|
|
|
"debug/elf"
|
|
|
"errors"
|
|
|
@@ -12,15 +14,99 @@ import (
|
|
|
"golang.org/x/mod/semver"
|
|
|
"k8s.io/klog/v2"
|
|
|
"os"
|
|
|
+ "regexp"
|
|
|
"strings"
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
minSupportedGoVersion = "v1.17.0"
|
|
|
- writeSymbol = "crypto/tls.(*Conn).Write"
|
|
|
- readSymbol = "crypto/tls.(*Conn).Read"
|
|
|
+ goTlsWriteSymbol = "crypto/tls.(*Conn).Write"
|
|
|
+ goTlsReadSymbol = "crypto/tls.(*Conn).Read"
|
|
|
)
|
|
|
|
|
|
+var (
|
|
|
+ opensslVersionRe = regexp.MustCompile(`OpenSSL\s(\d\.\d+\.\d+)`)
|
|
|
+)
|
|
|
+
|
|
|
+func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link {
|
|
|
+ if t.disableL7Tracing {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ libPath, version := getSslLibPathAndVersion(pid)
|
|
|
+ if libPath == "" || version == "" {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ log := func(msg string, err error) {
|
|
|
+ if err != nil {
|
|
|
+ for _, s := range []string{"no such file or directory", "no such process", "permission denied"} {
|
|
|
+ if strings.HasSuffix(err.Error(), s) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ klog.ErrorfDepth(1, "pid=%d libssl_version=%s: %s: %s", pid, version, msg, err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ klog.InfofDepth(1, "pid=%d libssl_version=%s: %s", pid, version, msg)
|
|
|
+ }
|
|
|
+
|
|
|
+ exe, err := link.OpenExecutable(libPath)
|
|
|
+ if err != nil {
|
|
|
+ log("failed to open executable", err)
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var links []link.Link
|
|
|
+ writeEnter := "openssl_SSL_write_enter"
|
|
|
+ readEnter := "openssl_SSL_read_enter"
|
|
|
+ readExEnter := "openssl_SSL_read_ex_enter"
|
|
|
+ readExit := "openssl_SSL_read_exit"
|
|
|
+ switch {
|
|
|
+ case semver.Compare(version, "v3.0.0") >= 0:
|
|
|
+ writeEnter = "openssl_SSL_write_enter_v3_0"
|
|
|
+ readEnter = "openssl_SSL_read_enter_v3_0"
|
|
|
+ readExEnter = "openssl_SSL_read_ex_enter_v3_0"
|
|
|
+ case semver.Compare(version, "v1.1.1") >= 0:
|
|
|
+ writeEnter = "openssl_SSL_write_enter_v1_1_1"
|
|
|
+ readEnter = "openssl_SSL_read_enter_v1_1_1"
|
|
|
+ readExEnter = "openssl_SSL_read_ex_enter_v1_1_1"
|
|
|
+ }
|
|
|
+
|
|
|
+ type prog struct {
|
|
|
+ symbol string
|
|
|
+ uprobe string
|
|
|
+ uretprobe string
|
|
|
+ }
|
|
|
+ progs := []prog{
|
|
|
+ {symbol: "SSL_write", uprobe: writeEnter},
|
|
|
+ {symbol: "SSL_write_ex", uprobe: writeEnter},
|
|
|
+ {symbol: "SSL_read", uprobe: readEnter},
|
|
|
+ {symbol: "SSL_read_ex", uprobe: readExEnter},
|
|
|
+ {symbol: "SSL_read", uretprobe: readExit},
|
|
|
+ {symbol: "SSL_read_ex", uretprobe: readExit},
|
|
|
+ }
|
|
|
+ for _, p := range progs {
|
|
|
+ if p.uprobe != "" {
|
|
|
+ l, err := exe.Uprobe(p.symbol, t.uprobes[p.uprobe], nil)
|
|
|
+ if err != nil {
|
|
|
+ log("failed to attach uprobe", err)
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ links = append(links, l)
|
|
|
+ }
|
|
|
+ if p.uretprobe != "" {
|
|
|
+ l, err := exe.Uretprobe(p.symbol, t.uprobes[p.uretprobe], nil)
|
|
|
+ if err != nil {
|
|
|
+ log("failed to attach uretprobe", err)
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ links = append(links, l)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log("libssl uprobes attached", nil)
|
|
|
+ return links
|
|
|
+}
|
|
|
+
|
|
|
func (t *Tracer) AttachGoTlsUprobes(pid uint32) []link.Link {
|
|
|
if t.disableL7Tracing {
|
|
|
return nil
|
|
|
@@ -101,7 +187,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32) []link.Link {
|
|
|
continue
|
|
|
}
|
|
|
switch s.Name {
|
|
|
- case writeSymbol, readSymbol:
|
|
|
+ case goTlsWriteSymbol, goTlsReadSymbol:
|
|
|
default:
|
|
|
continue
|
|
|
}
|
|
|
@@ -117,14 +203,14 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32) []link.Link {
|
|
|
}
|
|
|
}
|
|
|
switch s.Name {
|
|
|
- case writeSymbol:
|
|
|
+ case goTlsWriteSymbol:
|
|
|
l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
|
|
|
if err != nil {
|
|
|
log("failed to attach write_enter uprobe", err)
|
|
|
return nil
|
|
|
}
|
|
|
links = append(links, l)
|
|
|
- case readSymbol:
|
|
|
+ case goTlsReadSymbol:
|
|
|
l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
|
|
|
if err != nil {
|
|
|
log("failed to attach read_enter uprobe", err)
|
|
|
@@ -159,6 +245,72 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32) []link.Link {
|
|
|
return links
|
|
|
}
|
|
|
|
|
|
+func getSslLibPathAndVersion(pid uint32) (string, string) {
|
|
|
+ f, err := os.Open(proc.Path(pid, "maps"))
|
|
|
+ if err != nil {
|
|
|
+ return "", ""
|
|
|
+ }
|
|
|
+ defer f.Close()
|
|
|
+ scanner := bufio.NewScanner(f)
|
|
|
+ scanner.Split(bufio.ScanLines)
|
|
|
+ var libsslPath, libcryptoPath string
|
|
|
+ for scanner.Scan() {
|
|
|
+ parts := strings.Fields(scanner.Text())
|
|
|
+ if len(parts) <= 5 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ libPath := parts[5]
|
|
|
+ switch {
|
|
|
+ case libsslPath == "" && strings.Contains(libPath, "libssl.so"):
|
|
|
+ fullPath := proc.Path(pid, "root", libPath)
|
|
|
+ if _, err = os.Stat(fullPath); err == nil {
|
|
|
+ libsslPath = fullPath
|
|
|
+ }
|
|
|
+ case libcryptoPath == "" && strings.Contains(libPath, "libcrypto.so"):
|
|
|
+ fullPath := proc.Path(pid, "root", libPath)
|
|
|
+ if _, err = os.Stat(fullPath); err == nil {
|
|
|
+ libcryptoPath = fullPath
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if libsslPath != "" && libcryptoPath != "" {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if libsslPath == "" || libcryptoPath == "" {
|
|
|
+ return "", ""
|
|
|
+ }
|
|
|
+
|
|
|
+ ef, err := elf.Open(libcryptoPath)
|
|
|
+ if err != nil {
|
|
|
+ return "", ""
|
|
|
+ }
|
|
|
+ defer ef.Close()
|
|
|
+ rodataSection := ef.Section(".rodata")
|
|
|
+ if rodataSection == nil {
|
|
|
+ return "", ""
|
|
|
+ }
|
|
|
+ rodataSectionData, err := rodataSection.Data()
|
|
|
+ if err != nil {
|
|
|
+ return "", ""
|
|
|
+ }
|
|
|
+ var version string
|
|
|
+ for _, b := range bytes.Split(rodataSectionData, []byte("\x00")) {
|
|
|
+ if len(b) == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ s := string(b)
|
|
|
+ if !strings.HasPrefix(s, "OpenSSL") {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if m := opensslVersionRe.FindStringSubmatch(s); len(m) > 1 {
|
|
|
+ version = m[1]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return libsslPath, "v" + version
|
|
|
+}
|
|
|
+
|
|
|
func getReturnOffsets(machine elf.Machine, instructions []byte) []int {
|
|
|
var res []int
|
|
|
switch machine {
|