Files
vpp-maglev/internal/prober/tcp.go

79 lines
2.3 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package prober
import (
"context"
"crypto/tls"
"net"
"strconv"
"time"
"git.ipng.ch/ipng/vpp-maglev/internal/health"
)
// TCPProbe performs a TCP connect to cfg.Target:cfg.Port inside the healthcheck
// netns. If cfg.TCP.SSL is true a TLS handshake is performed after connect,
// making this an L6 check (useful for smtps, imaps, etc.).
// Plain connect returns L4OK/L4TOUT/L4CON.
// TLS handshake returns L6OK/L6TOUT/L6RSP (on top of an L4OK connect).
func TCPProbe(ctx context.Context, cfg ProbeConfig) health.ProbeResult {
port := cfg.Port
if port == 0 {
port = 80
}
addr := net.JoinHostPort(cfg.Target.String(), strconv.Itoa(int(port)))
doTLS := cfg.TCP != nil && cfg.TCP.SSL
var serverName string
var insecureSkipVerify bool
if cfg.TCP != nil {
serverName = cfg.TCP.ServerName
insecureSkipVerify = cfg.TCP.InsecureSkipVerify
}
var result health.ProbeResult
err := inNetns(cfg.HealthCheckNetns, func() error {
dialer := &net.Dialer{Timeout: cfg.Timeout}
if cfg.ProbeSrc != nil {
dialer.LocalAddr = &net.TCPAddr{IP: cfg.ProbeSrc}
}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
if isTimeout(err) {
result = health.ProbeResult{OK: false, Layer: health.LayerL4, Code: "L4TOUT", Detail: err.Error()}
} else {
result = health.ProbeResult{OK: false, Layer: health.LayerL4, Code: "L4CON", Detail: err.Error()}
}
return nil
}
if !doTLS {
_ = conn.Close()
result = health.ProbeResult{OK: true, Layer: health.LayerL4, Code: "L4OK"}
return nil
}
// TLS handshake.
tlsConn := tls.Client(conn, tlsConfig(serverName, insecureSkipVerify))
_ = tlsConn.SetDeadline(time.Now().Add(cfg.Timeout))
if err := tlsConn.HandshakeContext(ctx); err != nil {
_ = tlsConn.Close()
if isTimeout(err) {
result = health.ProbeResult{OK: false, Layer: health.LayerL6, Code: "L6TOUT", Detail: err.Error()}
} else {
result = health.ProbeResult{OK: false, Layer: health.LayerL6, Code: "L6RSP", Detail: err.Error()}
}
return nil
}
_ = tlsConn.Close()
result = health.ProbeResult{OK: true, Layer: health.LayerL6, Code: "L6OK"}
return nil
})
if err != nil {
return health.ProbeResult{OK: false, Layer: health.LayerL4, Code: "L4CON", Detail: err.Error()}
}
return result
}