mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
package inspect
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
)
|
|
|
|
// PROXY protocol v2 constants (RFC 7239 / HAProxy spec)
|
|
var proxyV2Signature = [12]byte{
|
|
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51,
|
|
0x55, 0x49, 0x54, 0x0A,
|
|
}
|
|
|
|
const (
|
|
proxyV2VersionCommand = 0x21 // version 2, PROXY command
|
|
proxyV2FamilyTCP4 = 0x11 // AF_INET, STREAM
|
|
proxyV2FamilyTCP6 = 0x21 // AF_INET6, STREAM
|
|
)
|
|
|
|
// forwardToEnvoy forwards a connection to the given envoy sidecar via PROXY protocol v2.
|
|
// The caller provides the envoy manager snapshot to avoid accessing p.envoy without lock.
|
|
func (p *Proxy) forwardToEnvoy(ctx context.Context, pconn *peekConn, dst netip.AddrPort, src SourceInfo, em *envoyManager) error {
|
|
envoyAddr := em.ListenAddr()
|
|
|
|
conn, err := (&net.Dialer{}).DialContext(ctx, "tcp", envoyAddr.String())
|
|
if err != nil {
|
|
return fmt.Errorf("dial envoy at %s: %w", envoyAddr, err)
|
|
}
|
|
defer func() {
|
|
if err := conn.Close(); err != nil {
|
|
p.log.Debugf("close envoy conn: %v", err)
|
|
}
|
|
}()
|
|
|
|
if err := writeProxyV2Header(conn, src.IP, dst); err != nil {
|
|
return fmt.Errorf("write PROXY v2 header: %w", err)
|
|
}
|
|
|
|
p.log.Tracef("envoy: forwarded %s -> %s via PROXY v2", src.IP, dst)
|
|
|
|
return relay(ctx, pconn, conn)
|
|
}
|
|
|
|
// writeProxyV2Header writes a PROXY protocol v2 header to w.
|
|
// The header encodes the original source IP and the destination address:port.
|
|
func writeProxyV2Header(w net.Conn, srcIP netip.Addr, dst netip.AddrPort) error {
|
|
srcIP = srcIP.Unmap()
|
|
dstIP := dst.Addr().Unmap()
|
|
|
|
var (
|
|
family byte
|
|
addrs []byte
|
|
)
|
|
|
|
if srcIP.Is4() && dstIP.Is4() {
|
|
family = proxyV2FamilyTCP4
|
|
s4 := srcIP.As4()
|
|
d4 := dstIP.As4()
|
|
addrs = make([]byte, 12) // 4+4+2+2
|
|
copy(addrs[0:4], s4[:])
|
|
copy(addrs[4:8], d4[:])
|
|
binary.BigEndian.PutUint16(addrs[8:10], 0) // src port unknown
|
|
binary.BigEndian.PutUint16(addrs[10:12], dst.Port())
|
|
} else {
|
|
family = proxyV2FamilyTCP6
|
|
s16 := srcIP.As16()
|
|
d16 := dstIP.As16()
|
|
addrs = make([]byte, 36) // 16+16+2+2
|
|
copy(addrs[0:16], s16[:])
|
|
copy(addrs[16:32], d16[:])
|
|
binary.BigEndian.PutUint16(addrs[32:34], 0) // src port unknown
|
|
binary.BigEndian.PutUint16(addrs[34:36], dst.Port())
|
|
}
|
|
|
|
// Header: signature(12) + ver_cmd(1) + family(1) + len(2) + addrs
|
|
header := make([]byte, 16+len(addrs))
|
|
copy(header[0:12], proxyV2Signature[:])
|
|
header[12] = proxyV2VersionCommand
|
|
header[13] = family
|
|
binary.BigEndian.PutUint16(header[14:16], uint16(len(addrs)))
|
|
copy(header[16:], addrs)
|
|
|
|
_, err := w.Write(header)
|
|
return err
|
|
}
|