feat: use binary codec for SUDP packets (#5461)

This commit is contained in:
fatedier
2026-07-31 16:53:16 +08:00
committed by GitHub
parent 1ab59e763c
commit 2291e8835f
17 changed files with 900 additions and 54 deletions
+62 -6
View File
@@ -328,10 +328,18 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
func (pxy *BaseProxy) joinUserConnection(local io.ReadWriteCloser, userConn net.Conn, proxyType string, xl *xlog.Logger) (int64, int64, []error) {
visitorWireProtocol := wireProtocolFromConn(userConn)
if proxyType == string(v1.ProxyTypeSUDP) && isMixedWireProtocol(pxy.wireProtocol, visitorWireProtocol) {
xl.Infof("bridge mixed SUDP payload codecs, proxy wireProtocol [%s], visitor wireProtocol [%s]",
normalizeWireProtocol(pxy.wireProtocol), normalizeWireProtocol(visitorWireProtocol))
return joinSUDPMessageBridge(local, userConn, pxy.wireProtocol, visitorWireProtocol, xl)
visitorUDPPacketCodec := udpPacketCodecFromConn(userConn)
if proxyType == string(v1.ProxyTypeSUDP) {
mixed, err := isMixedSUDPPacketEncoding(pxy.wireProtocol, pxy.udpPacketCodec, visitorWireProtocol, visitorUDPPacketCodec)
if err != nil {
return 0, 0, []error{err}
}
if mixed {
xl.Infof("bridge mixed SUDP payload codecs, proxy [%s/%s], visitor [%s/%s]",
normalizeWireProtocol(pxy.wireProtocol), pxy.udpPacketCodec,
normalizeWireProtocol(visitorWireProtocol), visitorUDPPacketCodec)
return joinSUDPMessageBridge(local, userConn, pxy.wireProtocol, pxy.udpPacketCodec, visitorWireProtocol, visitorUDPPacketCodec, xl)
}
}
return libio.Join(local, userConn)
}
@@ -340,6 +348,10 @@ type wireProtocolGetter interface {
WireProtocol() string
}
type udpPacketCodecGetter interface {
UDPPacketCodec() string
}
func wireProtocolFromConn(conn net.Conn) string {
if getter, ok := conn.(wireProtocolGetter); ok {
return getter.WireProtocol()
@@ -347,10 +359,46 @@ func wireProtocolFromConn(conn net.Conn) string {
return ""
}
func udpPacketCodecFromConn(conn net.Conn) string {
if getter, ok := conn.(udpPacketCodecGetter); ok {
return getter.UDPPacketCodec()
}
return ""
}
func isMixedWireProtocol(left, right string) bool {
return normalizeWireProtocol(left) != normalizeWireProtocol(right)
}
func isMixedSUDPPacketEncoding(leftWire, leftCodec, rightWire, rightCodec string) (bool, error) {
leftCodec, err := normalizeUDPPacketCodec(leftWire, leftCodec)
if err != nil {
return false, fmt.Errorf("invalid left SUDP packet encoding: %w", err)
}
rightCodec, err = normalizeUDPPacketCodec(rightWire, rightCodec)
if err != nil {
return false, fmt.Errorf("invalid right SUDP packet encoding: %w", err)
}
return normalizeWireProtocol(leftWire) != normalizeWireProtocol(rightWire) || leftCodec != rightCodec, nil
}
func normalizeUDPPacketCodec(wireProtocol, codec string) (string, error) {
switch wireProtocol {
case "", wire.ProtocolV1:
if codec != "" {
return "", fmt.Errorf("UDP packet codec %q requires wire protocol v2", codec)
}
return "", nil
case wire.ProtocolV2:
if codec == "" || codec == wire.UDPPacketCodecBinary {
return codec, nil
}
return "", fmt.Errorf("unsupported UDP packet codec %q", codec)
default:
return "", fmt.Errorf("unsupported wire protocol %q", wireProtocol)
}
}
func normalizeWireProtocol(wireProtocol string) string {
if wireProtocol == wire.ProtocolV2 {
return wire.ProtocolV2
@@ -362,13 +410,21 @@ func joinSUDPMessageBridge(
proxyConn io.ReadWriteCloser,
visitorConn io.ReadWriteCloser,
proxyWireProtocol string,
proxyUDPPacketCodec string,
visitorWireProtocol string,
visitorUDPPacketCodec string,
xl *xlog.Logger,
) (inCount int64, outCount int64, errs []error) {
// The mixed bridge decodes and re-encodes messages, so raw framed byte counts
// are not available. Count UDP payload bytes and ignore heartbeat traffic.
proxyRW := msg.NewReadWriter(proxyConn, proxyWireProtocol)
visitorRW := msg.NewReadWriter(visitorConn, visitorWireProtocol)
proxyRW, err := msg.NewUDPPacketReadWriter(proxyConn, proxyWireProtocol, proxyUDPPacketCodec)
if err != nil {
return 0, 0, []error{err}
}
visitorRW, err := msg.NewUDPPacketReadWriter(visitorConn, visitorWireProtocol, visitorUDPPacketCodec)
if err != nil {
return 0, 0, []error{err}
}
var (
once sync.Once