Files
sing-box-extended/transport/trusttunnel/quic.go
2026-06-04 07:54:26 +03:00

144 lines
4.1 KiB
Go

package trusttunnel
import (
"context"
"errors"
"net"
"time"
"github.com/sagernet/quic-go"
"github.com/sagernet/quic-go/congestion"
"github.com/sagernet/quic-go/http3"
"github.com/sagernet/sing-box/common/tls"
E "github.com/sagernet/sing/common/exceptions"
qtls "github.com/sagernet/sing-quic"
"github.com/sagernet/sing-quic/congestion_bbr1"
"github.com/sagernet/sing-quic/congestion_bbr2"
congestion_meta1 "github.com/sagernet/sing-quic/congestion_meta1"
congestion_meta2 "github.com/sagernet/sing-quic/congestion_meta2"
"github.com/sagernet/sing/common/ntp"
)
func NewCongestionControl(name string, cwnd int, bbrProfile string, timeFunc func() time.Time) (func(conn *quic.Conn) congestion.CongestionControl, error) {
if timeFunc == nil {
timeFunc = time.Now
}
if cwnd == 0 {
cwnd = 32
}
switch name {
case "", "bbr":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta2.NewBbrSender(
congestion_meta2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
congestion.ByteCount(cwnd)*congestion.ByteCount(conn.Config().InitialPacketSize),
)
}, nil
case "bbr_standard":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr1.NewBbrSender(
congestion_bbr1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
congestion_bbr1.InitialCongestionWindowPackets,
congestion_bbr1.MaxCongestionWindowPackets,
)
}, nil
case "bbr2":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr2.NewBBR2Sender(
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
0,
false,
)
}, nil
case "bbr2_variant":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_bbr2.NewBBR2Sender(
congestion_bbr2.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
32*congestion.ByteCount(conn.Config().InitialPacketSize),
true,
)
}, nil
case "cubic":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta1.NewCubicSender(
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
false,
)
}, nil
case "reno":
return func(conn *quic.Conn) congestion.CongestionControl {
return congestion_meta1.NewCubicSender(
congestion_meta1.DefaultClock{TimeFunc: timeFunc},
congestion.ByteCount(conn.Config().InitialPacketSize),
true,
)
}, nil
default:
return nil, E.New("unknown congestion control: ", name)
}
}
type QUICService struct {
service *Service
h3Server *http3.Server
udpConn net.PacketConn
congestionControl string
cwnd int
bbrProfile string
}
func NewQUICService(service *Service, congestionControl string, cwnd int, bbrProfile string) *QUICService {
return &QUICService{
service: service,
congestionControl: congestionControl,
cwnd: cwnd,
bbrProfile: bbrProfile,
}
}
func (s *QUICService) Start(ctx context.Context, udpConn net.PacketConn, tlsConfig tls.ServerConfig) error {
s.udpConn = udpConn
congestionControlFactory, err := NewCongestionControl(s.congestionControl, s.cwnd, s.bbrProfile, ntp.TimeFuncFromContext(ctx))
if err != nil {
return err
}
s.h3Server = &http3.Server{
Handler: s.service,
ConnContext: func(ctx context.Context, conn *quic.Conn) context.Context {
conn.SetCongestionControl(congestionControlFactory(conn))
return ctx
},
}
if err := qtls.ConfigureHTTP3(tlsConfig); err != nil {
return err
}
quicListener, err := qtls.ListenEarly(udpConn, tlsConfig, &quic.Config{
MaxIdleTimeout: DefaultSessionTimeout * 2,
MaxIncomingStreams: 1 << 60,
Allow0RTT: true,
})
if err != nil {
return err
}
go func() {
_ = s.h3Server.ServeListener(quicListener)
}()
return nil
}
func (s *QUICService) Close() error {
var errs []error
if s.h3Server != nil {
errs = append(errs, s.h3Server.Close())
}
if s.udpConn != nil {
errs = append(errs, s.udpConn.Close())
}
return errors.Join(errs...)
}