mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Fri Mar 20 20:03:30 CET 2026
This commit is contained in:
@@ -1304,3 +1304,4 @@ Update On Mon Mar 16 20:17:53 CET 2026
|
||||
Update On Tue Mar 17 20:16:34 CET 2026
|
||||
Update On Wed Mar 18 20:13:21 CET 2026
|
||||
Update On Thu Mar 19 20:11:34 CET 2026
|
||||
Update On Fri Mar 20 20:03:22 CET 2026
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
- { goos: linux, goarch: mipsle, gomips: softfloat, output: mipsle-softfloat }
|
||||
- { goos: linux, goarch: mips64, output: mips64 }
|
||||
- { goos: linux, goarch: mips64le, output: mips64le, debian: mips64el, rpm: mips64el }
|
||||
- { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1', debian: loongarch64, rpm: loongarch64 }
|
||||
- { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1', debian: loongarch64, rpm: loongarch64, goversion: 'custom' }
|
||||
- { goos: linux, goarch: loong64, output: loong64-abi2, abi: '2', debian: loong64, rpm: loong64 }
|
||||
- { goos: linux, goarch: riscv64, output: riscv64, debian: riscv64, rpm: riscv64 }
|
||||
- { goos: linux, goarch: s390x, output: s390x, debian: s390x, rpm: s390x }
|
||||
@@ -158,14 +158,14 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go
|
||||
if: ${{ matrix.jobs.goversion == '' && matrix.jobs.abi != '1' }}
|
||||
if: ${{ matrix.jobs.goversion == '' }}
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.26'
|
||||
check-latest: true # Always check for the latest patch release
|
||||
|
||||
- name: Set up Go
|
||||
if: ${{ matrix.jobs.goversion != '' && matrix.jobs.abi != '1' }}
|
||||
if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goversion != 'custom' }}
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ matrix.jobs.goversion }}
|
||||
|
||||
@@ -54,7 +54,7 @@ type SudokuHTTPMaskOptions struct {
|
||||
Mode string `proxy:"mode,omitempty"`
|
||||
TLS bool `proxy:"tls,omitempty"`
|
||||
Host string `proxy:"host,omitempty"`
|
||||
PathRoot string `proxy:"path_root,omitempty"`
|
||||
PathRoot string `proxy:"path-root,omitempty"`
|
||||
Multiplex string `proxy:"multiplex,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ type Trojan struct {
|
||||
hexPassword [trojan.KeyLength]byte
|
||||
|
||||
// for gun mux
|
||||
gunConfig *gun.Config
|
||||
gunTransport *gun.TransportWrap
|
||||
gunTransport *gun.Transport
|
||||
|
||||
realityConfig *tlsC.RealityConfig
|
||||
echConfig *ech.Config
|
||||
@@ -178,7 +177,7 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Con
|
||||
var c net.Conn
|
||||
// gun transport
|
||||
if t.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(t.gunTransport, t.gunConfig)
|
||||
c, err = t.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = t.dialer.DialContext(ctx, "tcp", t.addr)
|
||||
}
|
||||
@@ -206,7 +205,7 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
||||
var c net.Conn
|
||||
// grpc transport
|
||||
if t.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(t.gunTransport, t.gunConfig)
|
||||
c, err = t.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = t.dialer.DialContext(ctx, "tcp", t.addr)
|
||||
}
|
||||
@@ -317,13 +316,14 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
|
||||
Reality: t.realityConfig,
|
||||
}
|
||||
|
||||
t.gunTransport = gun.NewHTTP2Client(dialFn, tlsConfig)
|
||||
|
||||
t.gunConfig = &gun.Config{
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.SNI,
|
||||
gunConfig := &gun.Config{
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.SNI,
|
||||
PingInterval: option.GrpcOpts.PingInterval,
|
||||
}
|
||||
|
||||
t.gunTransport = gun.NewTransport(dialFn, tlsConfig, gunConfig)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
|
||||
@@ -33,8 +33,7 @@ type Vless struct {
|
||||
encryption *encryption.ClientInstance
|
||||
|
||||
// for gun mux
|
||||
gunConfig *gun.Config
|
||||
gunTransport *gun.TransportWrap
|
||||
gunTransport *gun.Transport
|
||||
|
||||
realityConfig *tlsC.RealityConfig
|
||||
echConfig *ech.Config
|
||||
@@ -234,7 +233,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn
|
||||
var c net.Conn
|
||||
// gun transport
|
||||
if v.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(v.gunTransport, v.gunConfig)
|
||||
c, err = v.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = v.dialer.DialContext(ctx, "tcp", v.addr)
|
||||
}
|
||||
@@ -260,7 +259,7 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (
|
||||
var c net.Conn
|
||||
// gun transport
|
||||
if v.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(v.gunTransport, v.gunConfig)
|
||||
c, err = v.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = v.dialer.DialContext(ctx, "tcp", v.addr)
|
||||
}
|
||||
@@ -431,9 +430,10 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||
}
|
||||
|
||||
gunConfig := &gun.Config{
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.ServerName,
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.ServerName,
|
||||
PingInterval: option.GrpcOpts.PingInterval,
|
||||
}
|
||||
if option.ServerName == "" {
|
||||
gunConfig.Host = v.addr
|
||||
@@ -457,9 +457,7 @@ func NewVless(option VlessOption) (*Vless, error) {
|
||||
}
|
||||
}
|
||||
|
||||
v.gunConfig = gunConfig
|
||||
|
||||
v.gunTransport = gun.NewHTTP2Client(dialFn, tlsConfig)
|
||||
v.gunTransport = gun.NewTransport(dialFn, tlsConfig, gunConfig)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
|
||||
@@ -34,8 +34,7 @@ type Vmess struct {
|
||||
option *VmessOption
|
||||
|
||||
// for gun mux
|
||||
gunConfig *gun.Config
|
||||
gunTransport *gun.TransportWrap
|
||||
gunTransport *gun.Transport
|
||||
|
||||
realityConfig *tlsC.RealityConfig
|
||||
echConfig *ech.Config
|
||||
@@ -86,6 +85,7 @@ type HTTP2Options struct {
|
||||
type GrpcOptions struct {
|
||||
GrpcServiceName string `proxy:"grpc-service-name,omitempty"`
|
||||
GrpcUserAgent string `proxy:"grpc-user-agent,omitempty"`
|
||||
PingInterval int `proxy:"ping-interval,omitempty"`
|
||||
}
|
||||
|
||||
type WSOptions struct {
|
||||
@@ -295,7 +295,7 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn
|
||||
var c net.Conn
|
||||
// gun transport
|
||||
if v.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(v.gunTransport, v.gunConfig)
|
||||
c, err = v.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = v.dialer.DialContext(ctx, "tcp", v.addr)
|
||||
}
|
||||
@@ -318,7 +318,7 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (
|
||||
var c net.Conn
|
||||
// gun transport
|
||||
if v.gunTransport != nil {
|
||||
c, err = gun.StreamGunWithTransport(v.gunTransport, v.gunConfig)
|
||||
c, err = v.gunTransport.Dial()
|
||||
} else {
|
||||
c, err = v.dialer.DialContext(ctx, "tcp", v.addr)
|
||||
}
|
||||
@@ -437,9 +437,10 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
}
|
||||
|
||||
gunConfig := &gun.Config{
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.ServerName,
|
||||
ServiceName: option.GrpcOpts.GrpcServiceName,
|
||||
UserAgent: option.GrpcOpts.GrpcUserAgent,
|
||||
Host: option.ServerName,
|
||||
PingInterval: option.GrpcOpts.PingInterval,
|
||||
}
|
||||
if option.ServerName == "" {
|
||||
gunConfig.Host = v.addr
|
||||
@@ -463,9 +464,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
}
|
||||
}
|
||||
|
||||
v.gunConfig = gunConfig
|
||||
|
||||
v.gunTransport = gun.NewHTTP2Client(dialFn, tlsConfig)
|
||||
v.gunTransport = gun.NewTransport(dialFn, tlsConfig, gunConfig)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
|
||||
@@ -19,8 +19,6 @@ type Fallback struct {
|
||||
testUrl string
|
||||
selected string
|
||||
expectedStatus string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
func (f *Fallback) Now() string {
|
||||
@@ -90,8 +88,8 @@ func (f *Fallback) MarshalJSON() ([]byte, error) {
|
||||
"testUrl": f.testUrl,
|
||||
"expectedStatus": f.expectedStatus,
|
||||
"fixed": f.selected,
|
||||
"hidden": f.Hidden,
|
||||
"icon": f.Icon,
|
||||
"hidden": f.Hidden(),
|
||||
"icon": f.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,6 +161,8 @@ func NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallba
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.Fallback,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -173,7 +173,5 @@ func NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallba
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
|
||||
type GroupBase struct {
|
||||
*outbound.Base
|
||||
hidden bool
|
||||
icon string
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterRegs []*regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
@@ -30,7 +32,7 @@ type GroupBase struct {
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
TestTimeout int
|
||||
testTimeout int
|
||||
maxFailedTimes int
|
||||
|
||||
// for GetProxies
|
||||
@@ -42,6 +44,8 @@ type GroupBase struct {
|
||||
type GroupBaseOption struct {
|
||||
Name string
|
||||
Type C.AdapterType
|
||||
Hidden bool
|
||||
Icon string
|
||||
Filter string
|
||||
ExcludeFilter string
|
||||
ExcludeType string
|
||||
@@ -74,17 +78,19 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
|
||||
gb := &GroupBase{
|
||||
Base: outbound.NewBase(outbound.BaseOption{Name: opt.Name, Type: opt.Type}),
|
||||
hidden: opt.Hidden,
|
||||
icon: opt.Icon,
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterRegs: excludeFilterRegs,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.Providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
testTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.MaxFailedTimes,
|
||||
}
|
||||
|
||||
if gb.TestTimeout == 0 {
|
||||
gb.TestTimeout = 5000
|
||||
if gb.testTimeout == 0 {
|
||||
gb.testTimeout = 5000
|
||||
}
|
||||
if gb.maxFailedTimes == 0 {
|
||||
gb.maxFailedTimes = 5
|
||||
@@ -93,6 +99,14 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
return gb
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Hidden() bool {
|
||||
return gb.hidden
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Icon() string {
|
||||
return gb.icon
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Touch() {
|
||||
for _, pd := range gb.providers {
|
||||
pd.Touch()
|
||||
@@ -265,7 +279,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func(
|
||||
log.Debugln("ProxyGroup: %s first failed", gb.Name())
|
||||
gb.failedTime = time.Now()
|
||||
} else {
|
||||
if time.Since(gb.failedTime) > time.Duration(gb.TestTimeout)*time.Millisecond {
|
||||
if time.Since(gb.failedTime) > time.Duration(gb.testTimeout)*time.Millisecond {
|
||||
gb.failedTimes = 0
|
||||
return
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ type LoadBalance struct {
|
||||
strategyFn strategyFn
|
||||
testUrl string
|
||||
expectedStatus string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
var errStrategy = errors.New("unsupported strategy")
|
||||
@@ -234,8 +232,8 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
|
||||
"all": all,
|
||||
"testUrl": lb.testUrl,
|
||||
"expectedStatus": lb.expectedStatus,
|
||||
"hidden": lb.Hidden,
|
||||
"icon": lb.Icon,
|
||||
"hidden": lb.Hidden(),
|
||||
"icon": lb.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -267,6 +265,8 @@ func NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, stra
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.LoadBalance,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -278,7 +278,5 @@ func NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, stra
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ type Selector struct {
|
||||
disableUDP bool
|
||||
selected string
|
||||
testUrl string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
@@ -68,8 +66,8 @@ func (s *Selector) MarshalJSON() ([]byte, error) {
|
||||
"now": s.Now(),
|
||||
"all": all,
|
||||
"testUrl": url,
|
||||
"hidden": s.Hidden,
|
||||
"icon": s.Icon,
|
||||
"hidden": s.Hidden(),
|
||||
"icon": s.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -121,6 +119,8 @@ func NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Select
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.Selector,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -131,7 +131,5 @@ func NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Select
|
||||
selected: "COMPATIBLE",
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ type URLTest struct {
|
||||
expectedStatus string
|
||||
tolerance uint16
|
||||
disableUDP bool
|
||||
Hidden bool
|
||||
Icon string
|
||||
fastNode C.Proxy
|
||||
fastSingle *singledo.Single[C.Proxy]
|
||||
}
|
||||
@@ -180,8 +178,8 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||
"testUrl": u.testUrl,
|
||||
"expectedStatus": u.expectedStatus,
|
||||
"fixed": u.selected,
|
||||
"hidden": u.Hidden,
|
||||
"icon": u.Icon,
|
||||
"hidden": u.Hidden(),
|
||||
"icon": u.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,6 +213,8 @@ func NewURLTest(option *GroupCommonOption, providers []P.ProxyProvider, options
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.URLTest,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -226,8 +226,6 @@ func NewURLTest(option *GroupCommonOption, providers []P.ProxyProvider, options
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
|
||||
@@ -16,6 +16,9 @@ type ProxyGroup interface {
|
||||
Now() string
|
||||
Touch()
|
||||
|
||||
Hidden() bool
|
||||
Icon() string
|
||||
|
||||
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ var (
|
||||
|
||||
type healthCheckSchema struct {
|
||||
Enable bool `provider:"enable"`
|
||||
URL string `provider:"url"`
|
||||
Interval int `provider:"interval"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
TestTimeout int `provider:"timeout,omitempty"`
|
||||
Lazy bool `provider:"lazy,omitempty"`
|
||||
ExpectedStatus string `provider:"expected-status,omitempty"`
|
||||
|
||||
@@ -669,6 +669,7 @@ proxies: # socks5
|
||||
grpc-opts:
|
||||
grpc-service-name: "example"
|
||||
# grpc-user-agent: "grpc-go/1.36.0"
|
||||
# ping-interval: 0 # 默认关闭,单位为秒
|
||||
# ip-version: ipv4
|
||||
|
||||
# vless
|
||||
@@ -759,6 +760,7 @@ proxies: # socks5
|
||||
grpc-opts:
|
||||
grpc-service-name: "grpc"
|
||||
# grpc-user-agent: "grpc-go/1.36.0"
|
||||
# ping-interval: 0 # 默认关闭,单位为秒
|
||||
|
||||
reality-opts:
|
||||
public-key: CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE
|
||||
@@ -830,6 +832,7 @@ proxies: # socks5
|
||||
grpc-opts:
|
||||
grpc-service-name: "example"
|
||||
# grpc-user-agent: "grpc-go/1.36.0"
|
||||
# ping-interval: 0 # 默认关闭,单位为秒
|
||||
|
||||
- name: trojan-ws
|
||||
server: server
|
||||
@@ -1098,7 +1101,7 @@ proxies: # socks5
|
||||
mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll)、ws(WebSocket 隧道)
|
||||
# tls: true # 可选:仅在 mode 为 stream/poll/auto/ws 时生效;true 强制 https/wss;false 强制 http/ws(不会根据端口自动推断)
|
||||
# host: "" # 可选:覆盖 Host/SNI(支持 example.com 或 example.com:443);仅在 mode 为 stream/poll/auto/ws 时生效
|
||||
# path_root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
|
||||
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
|
||||
# multiplex: off # 可选:off(默认)、auto(复用底层 HTTP 连接,减少建链 RTT)、on(Sudoku mux 单隧道多目标;仅在 mode=stream/poll/auto 生效;ws 强制 off)
|
||||
#
|
||||
# 向后兼容旧写法:
|
||||
@@ -1677,7 +1680,7 @@ listeners:
|
||||
httpmask:
|
||||
disable: false # true 禁用所有 HTTP 伪装/隧道
|
||||
mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll)、ws(WebSocket 隧道)
|
||||
# path_root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
|
||||
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
|
||||
#
|
||||
# 可选:当启用 HTTPMask 且识别到“像 HTTP 但不符合 tunnel/auth”的请求时,将原始字节透传给 fallback(常用于与其他服务共端口):
|
||||
# fallback: "127.0.0.1:80"
|
||||
|
||||
@@ -23,6 +23,7 @@ require (
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/http v0.1.0
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604
|
||||
github.com/metacubex/mhurl v0.1.0
|
||||
github.com/metacubex/mlkem v0.1.0
|
||||
github.com/metacubex/quic-go v0.59.1-0.20260213014310-4df8f0de5b56
|
||||
github.com/metacubex/randv2 v0.2.0
|
||||
|
||||
@@ -107,6 +107,8 @@ github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o=
|
||||
github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
|
||||
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
|
||||
github.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=
|
||||
github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=
|
||||
github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
|
||||
@@ -35,7 +35,7 @@ type SudokuOption struct {
|
||||
type SudokuHTTPMaskOptions struct {
|
||||
Disable bool `inbound:"disable,omitempty"`
|
||||
Mode string `inbound:"mode,omitempty"`
|
||||
PathRoot string `inbound:"path_root,omitempty"`
|
||||
PathRoot string `inbound:"path-root,omitempty"`
|
||||
}
|
||||
|
||||
func (o SudokuOption) Equal(config C.InboundConfig) bool {
|
||||
|
||||
@@ -148,6 +148,7 @@ func handleConn(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions
|
||||
bufConn := N.NewBufferedConn(conn)
|
||||
head, err := bufConn.Peek(1)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
|
||||
"github.com/metacubex/mhurl"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
@@ -48,7 +49,7 @@ func (c *packet) InAddr() net.Addr {
|
||||
}
|
||||
|
||||
func ParseSSURL(s string) (addr, cipher, password string, err error) {
|
||||
u, err := url.Parse(s)
|
||||
u, err := mhurl.Parse(s) // we need multiple hosts url supports
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseSSURL(t *testing.T) {
|
||||
for _, test := range []struct{ method, passwd, hosts string }{
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: ":1000,:2000,:3000"},
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: "127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000"},
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: "[::1]:1000,[::1]:2000,[::1]:3000"},
|
||||
} {
|
||||
addr, cipher, password, err := ParseSSURL(fmt.Sprintf("ss://%s:%s@%s", test.method, test.passwd, test.hosts))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.hosts, addr)
|
||||
require.Equal(t, test.method, cipher)
|
||||
require.Equal(t, test.passwd, password)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||
|
||||
"github.com/metacubex/http"
|
||||
"github.com/metacubex/mhurl"
|
||||
vmess "github.com/metacubex/sing-vmess"
|
||||
"github.com/metacubex/sing/common"
|
||||
"github.com/metacubex/sing/common/metadata"
|
||||
@@ -230,7 +230,7 @@ func HandleVmess(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
}
|
||||
|
||||
func ParseVmessURL(s string) (addr, username, password string, err error) {
|
||||
u, err := url.Parse(s)
|
||||
u, err := mhurl.Parse(s) // we need multiple hosts url supports
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package sing_vmess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseVmessURL(t *testing.T) {
|
||||
for _, test := range []struct{ username, passwd, hosts string }{
|
||||
{username: "username", passwd: "password", hosts: ":1000,:2000,:3000"},
|
||||
{username: "username", passwd: "password", hosts: "127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000"},
|
||||
{username: "username", passwd: "password", hosts: "[::1]:1000,[::1]:2000,[::1]:3000"},
|
||||
} {
|
||||
addr, username, password, err := ParseVmessURL(fmt.Sprintf("vmess://%s:%s@%s", test.username, test.passwd, test.hosts))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.hosts, addr)
|
||||
require.Equal(t, test.username, username)
|
||||
require.Equal(t, test.passwd, password)
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,8 @@ var (
|
||||
)
|
||||
|
||||
var defaultHeader = http.Header{
|
||||
"content-type": []string{"application/grpc"},
|
||||
"user-agent": []string{"grpc-go/1.36.0"},
|
||||
"Content-Type": []string{"application/grpc"},
|
||||
"User-Agent": []string{"grpc-go/1.36.0"},
|
||||
}
|
||||
|
||||
type DialFn = func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
@@ -59,9 +59,10 @@ type Conn struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ServiceName string
|
||||
UserAgent string
|
||||
Host string
|
||||
ServiceName string
|
||||
UserAgent string
|
||||
Host string
|
||||
PingInterval int
|
||||
}
|
||||
|
||||
func (g *Conn) initReader() {
|
||||
@@ -246,7 +247,7 @@ func (g *Conn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHTTP2Client(dialFn DialFn, tlsConfig *vmess.TLSConfig) *TransportWrap {
|
||||
func NewTransport(dialFn DialFn, tlsConfig *vmess.TLSConfig, gunCfg *Config) *Transport {
|
||||
dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)
|
||||
defer cancel()
|
||||
@@ -288,14 +289,16 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *vmess.TLSConfig) *TransportWrap {
|
||||
DialTLSContext: dialFunc,
|
||||
AllowHTTP: false,
|
||||
DisableCompression: true,
|
||||
ReadIdleTimeout: time.Duration(gunCfg.PingInterval) * time.Second, // If zero, no health check is performed
|
||||
PingTimeout: 0,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
wrap := &TransportWrap{
|
||||
Http2Transport: transport,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
wrap := &Transport{
|
||||
transport: transport,
|
||||
cfg: gunCfg,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
return wrap
|
||||
}
|
||||
@@ -307,18 +310,18 @@ func ServiceNameToPath(serviceName string) string {
|
||||
return "/" + serviceName + "/Tun"
|
||||
}
|
||||
|
||||
func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, error) {
|
||||
func (t *Transport) Dial() (net.Conn, error) {
|
||||
serviceName := "GunService"
|
||||
if cfg.ServiceName != "" {
|
||||
serviceName = cfg.ServiceName
|
||||
if t.cfg.ServiceName != "" {
|
||||
serviceName = t.cfg.ServiceName
|
||||
}
|
||||
path := ServiceNameToPath(serviceName)
|
||||
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
header := defaultHeader.Clone()
|
||||
if cfg.UserAgent != "" {
|
||||
header.Set("User-Agent", cfg.UserAgent)
|
||||
if t.cfg.UserAgent != "" {
|
||||
header.Set("User-Agent", t.cfg.UserAgent)
|
||||
}
|
||||
|
||||
request := &http.Request{
|
||||
@@ -326,17 +329,17 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
|
||||
Body: reader,
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: cfg.Host,
|
||||
Host: t.cfg.Host,
|
||||
Path: path,
|
||||
// for unescape path
|
||||
Opaque: "//" + cfg.Host + path,
|
||||
Opaque: "//" + t.cfg.Host + path,
|
||||
},
|
||||
Proto: "HTTP/2",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Header: header,
|
||||
}
|
||||
request = request.WithContext(transport.ctx)
|
||||
request = request.WithContext(t.ctx)
|
||||
|
||||
conn := &Conn{
|
||||
initFn: func() (io.ReadCloser, NetAddr, error) {
|
||||
@@ -348,7 +351,7 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
|
||||
},
|
||||
}
|
||||
request = request.WithContext(httptrace.WithClientTrace(request.Context(), trace))
|
||||
response, err := transport.RoundTrip(request)
|
||||
response, err := t.transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
return nil, nAddr, err
|
||||
}
|
||||
@@ -361,13 +364,13 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func StreamGunWithConn(conn net.Conn, tlsConfig *vmess.TLSConfig, cfg *Config) (net.Conn, error) {
|
||||
func StreamGunWithConn(conn net.Conn, tlsConfig *vmess.TLSConfig, gunCfg *Config) (net.Conn, error) {
|
||||
dialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
transport := NewHTTP2Client(dialFn, tlsConfig)
|
||||
c, err := StreamGunWithTransport(transport, cfg)
|
||||
transport := NewTransport(dialFn, tlsConfig, gunCfg)
|
||||
c, err := transport.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,17 +10,18 @@ import (
|
||||
"github.com/metacubex/http"
|
||||
)
|
||||
|
||||
type TransportWrap struct {
|
||||
*http.Http2Transport
|
||||
type Transport struct {
|
||||
transport *http.Http2Transport
|
||||
cfg *Config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (tw *TransportWrap) Close() error {
|
||||
tw.closeOnce.Do(func() {
|
||||
tw.cancel()
|
||||
CloseTransport(tw.Http2Transport)
|
||||
func (t *Transport) Close() error {
|
||||
t.closeOnce.Do(func() {
|
||||
t.cancel()
|
||||
CloseHttp2Transport(t.transport)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func closeClientConn(cc *http.Http2ClientConn) { // like forceCloseConn() in htt
|
||||
_ = cc.Close()
|
||||
}
|
||||
|
||||
func CloseTransport(tr *http.Http2Transport) {
|
||||
func CloseHttp2Transport(tr *http.Http2Transport) {
|
||||
connPool := transportConnPool(tr)
|
||||
p := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data)
|
||||
p.mu.Lock()
|
||||
|
||||
+115
-37
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
@@ -55,13 +57,15 @@ type RecordConn struct {
|
||||
recvAEADEpoch uint32
|
||||
|
||||
// Send direction state.
|
||||
sendEpoch uint32
|
||||
sendSeq uint64
|
||||
sendBytes int64
|
||||
sendEpoch uint32
|
||||
sendSeq uint64
|
||||
sendBytes int64
|
||||
sendEpochUpdates uint32
|
||||
|
||||
// Receive direction state.
|
||||
recvEpoch uint32
|
||||
recvSeq uint64
|
||||
recvEpoch uint32
|
||||
recvSeq uint64
|
||||
recvInitialized bool
|
||||
|
||||
readBuf bytes.Buffer
|
||||
|
||||
@@ -105,6 +109,9 @@ func NewRecordConn(conn net.Conn, method string, baseSend, baseRecv []byte) (*Re
|
||||
}
|
||||
rc := &RecordConn{Conn: conn, method: method}
|
||||
rc.keys = recordKeys{baseSend: cloneBytes(baseSend), baseRecv: cloneBytes(baseRecv)}
|
||||
if err := rc.resetTrafficState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
@@ -127,11 +134,9 @@ func (c *RecordConn) Rekey(baseSend, baseRecv []byte) error {
|
||||
defer c.writeMu.Unlock()
|
||||
|
||||
c.keys = recordKeys{baseSend: cloneBytes(baseSend), baseRecv: cloneBytes(baseRecv)}
|
||||
c.sendEpoch = 0
|
||||
c.sendSeq = 0
|
||||
c.sendBytes = 0
|
||||
c.recvEpoch = 0
|
||||
c.recvSeq = 0
|
||||
if err := c.resetTrafficState(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.readBuf.Reset()
|
||||
|
||||
c.sendAEAD = nil
|
||||
@@ -141,6 +146,21 @@ func (c *RecordConn) Rekey(baseSend, baseRecv []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RecordConn) resetTrafficState() error {
|
||||
sendEpoch, sendSeq, err := randomRecordCounters()
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize record counters: %w", err)
|
||||
}
|
||||
c.sendEpoch = sendEpoch
|
||||
c.sendSeq = sendSeq
|
||||
c.sendBytes = 0
|
||||
c.sendEpochUpdates = 0
|
||||
c.recvEpoch = 0
|
||||
c.recvSeq = 0
|
||||
c.recvInitialized = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeAEADMethod(method string) string {
|
||||
switch method {
|
||||
case "", "chacha20-poly1305":
|
||||
@@ -166,6 +186,44 @@ func cloneBytes(b []byte) []byte {
|
||||
return append([]byte(nil), b...)
|
||||
}
|
||||
|
||||
func randomRecordCounters() (uint32, uint64, error) {
|
||||
epoch, err := randomNonZeroUint32()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
seq, err := randomNonZeroUint64()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return epoch, seq, nil
|
||||
}
|
||||
|
||||
func randomNonZeroUint32() (uint32, error) {
|
||||
var b [4]byte
|
||||
for {
|
||||
if _, err := io.ReadFull(rand.Reader, b[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v := binary.BigEndian.Uint32(b[:])
|
||||
if v != 0 && v != ^uint32(0) {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func randomNonZeroUint64() (uint64, error) {
|
||||
var b [8]byte
|
||||
for {
|
||||
if _, err := io.ReadFull(rand.Reader, b[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v := binary.BigEndian.Uint64(b[:])
|
||||
if v != 0 && v != ^uint64(0) {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RecordConn) newAEADFor(base []byte, epoch uint32) (cipher.AEAD, error) {
|
||||
if c.method == "none" {
|
||||
return nil, nil
|
||||
@@ -209,17 +267,49 @@ func deriveEpochKey(base []byte, epoch uint32, method string) []byte {
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func (c *RecordConn) maybeBumpSendEpochLocked(addedPlain int) {
|
||||
if KeyUpdateAfterBytes <= 0 || c.method == "none" {
|
||||
return
|
||||
func (c *RecordConn) maybeBumpSendEpochLocked(addedPlain int) error {
|
||||
ku := atomic.LoadInt64(&KeyUpdateAfterBytes)
|
||||
if ku <= 0 || c.method == "none" {
|
||||
return nil
|
||||
}
|
||||
c.sendBytes += int64(addedPlain)
|
||||
threshold := KeyUpdateAfterBytes * int64(c.sendEpoch+1)
|
||||
threshold := ku * int64(c.sendEpochUpdates+1)
|
||||
if c.sendBytes < threshold {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
c.sendEpoch++
|
||||
c.sendSeq = 0
|
||||
c.sendEpochUpdates++
|
||||
nextSeq, err := randomNonZeroUint64()
|
||||
if err != nil {
|
||||
return fmt.Errorf("rotate record seq: %w", err)
|
||||
}
|
||||
c.sendSeq = nextSeq
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RecordConn) validateRecvPosition(epoch uint32, seq uint64) error {
|
||||
if !c.recvInitialized {
|
||||
return nil
|
||||
}
|
||||
if epoch < c.recvEpoch {
|
||||
return fmt.Errorf("replayed epoch: got %d want >=%d", epoch, c.recvEpoch)
|
||||
}
|
||||
if epoch == c.recvEpoch && seq != c.recvSeq {
|
||||
return fmt.Errorf("out of order: epoch=%d got=%d want=%d", epoch, seq, c.recvSeq)
|
||||
}
|
||||
if epoch > c.recvEpoch {
|
||||
const maxJump = 8
|
||||
if epoch-c.recvEpoch > maxJump {
|
||||
return fmt.Errorf("epoch jump too large: got=%d want<=%d", epoch-c.recvEpoch, maxJump)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RecordConn) markRecvPosition(epoch uint32, seq uint64) {
|
||||
c.recvEpoch = epoch
|
||||
c.recvSeq = seq + 1
|
||||
c.recvInitialized = true
|
||||
}
|
||||
|
||||
func (c *RecordConn) Write(p []byte) (int, error) {
|
||||
@@ -282,7 +372,9 @@ func (c *RecordConn) Write(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
total += n
|
||||
c.maybeBumpSendEpochLocked(n)
|
||||
if err := c.maybeBumpSendEpochLocked(n); err != nil {
|
||||
return total, err
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
@@ -324,31 +416,17 @@ func (c *RecordConn) Read(p []byte) (int, error) {
|
||||
epoch := binary.BigEndian.Uint32(header[:4])
|
||||
seq := binary.BigEndian.Uint64(header[4:])
|
||||
|
||||
if epoch < c.recvEpoch {
|
||||
return 0, fmt.Errorf("replayed epoch: got %d want >=%d", epoch, c.recvEpoch)
|
||||
}
|
||||
if epoch == c.recvEpoch && seq != c.recvSeq {
|
||||
return 0, fmt.Errorf("out of order: epoch=%d got=%d want=%d", epoch, seq, c.recvSeq)
|
||||
}
|
||||
if epoch > c.recvEpoch {
|
||||
const maxJump = 8
|
||||
if epoch-c.recvEpoch > maxJump {
|
||||
return 0, fmt.Errorf("epoch jump too large: got=%d want<=%d", epoch-c.recvEpoch, maxJump)
|
||||
}
|
||||
c.recvEpoch = epoch
|
||||
c.recvSeq = 0
|
||||
if seq != 0 {
|
||||
return 0, fmt.Errorf("out of order: epoch advanced to %d but seq=%d", epoch, seq)
|
||||
}
|
||||
if err := c.validateRecvPosition(epoch, seq); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if c.recvAEAD == nil || c.recvAEADEpoch != c.recvEpoch {
|
||||
a, err := c.newAEADFor(c.keys.baseRecv, c.recvEpoch)
|
||||
if c.recvAEAD == nil || c.recvAEADEpoch != epoch {
|
||||
a, err := c.newAEADFor(c.keys.baseRecv, epoch)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.recvAEAD = a
|
||||
c.recvAEADEpoch = c.recvEpoch
|
||||
c.recvAEADEpoch = epoch
|
||||
}
|
||||
aead := c.recvAEAD
|
||||
|
||||
@@ -356,7 +434,7 @@ func (c *RecordConn) Read(p []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("decryption failed: epoch=%d seq=%d: %w", epoch, seq, err)
|
||||
}
|
||||
c.recvSeq++
|
||||
c.markRecvPosition(epoch, seq)
|
||||
|
||||
c.readBuf.Write(plaintext)
|
||||
return c.readBuf.Read(p)
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type captureConn struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *captureConn) Read(_ []byte) (int, error) { return 0, io.EOF }
|
||||
func (c *captureConn) Write(p []byte) (int, error) { return c.Buffer.Write(p) }
|
||||
func (c *captureConn) Close() error { return nil }
|
||||
func (c *captureConn) LocalAddr() net.Addr { return nil }
|
||||
func (c *captureConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *captureConn) SetDeadline(time.Time) error { return nil }
|
||||
func (c *captureConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (c *captureConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
type replayConn struct {
|
||||
reader *bytes.Reader
|
||||
}
|
||||
|
||||
func (c *replayConn) Read(p []byte) (int, error) { return c.reader.Read(p) }
|
||||
func (c *replayConn) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (c *replayConn) Close() error { return nil }
|
||||
func (c *replayConn) LocalAddr() net.Addr { return nil }
|
||||
func (c *replayConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *replayConn) SetDeadline(time.Time) error { return nil }
|
||||
func (c *replayConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (c *replayConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
func TestRecordConn_FirstFrameUsesRandomizedCounters(t *testing.T) {
|
||||
pskSend := sha256.Sum256([]byte("record-send"))
|
||||
pskRecv := sha256.Sum256([]byte("record-recv"))
|
||||
|
||||
raw := &captureConn{}
|
||||
writer, err := NewRecordConn(raw, "chacha20-poly1305", pskSend[:], pskRecv[:])
|
||||
if err != nil {
|
||||
t.Fatalf("new writer: %v", err)
|
||||
}
|
||||
|
||||
if writer.sendEpoch == 0 || writer.sendSeq == 0 {
|
||||
t.Fatalf("expected non-zero randomized counters, got epoch=%d seq=%d", writer.sendEpoch, writer.sendSeq)
|
||||
}
|
||||
|
||||
want := []byte("record prefix camouflage")
|
||||
if _, err := writer.Write(want); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
wire := raw.Bytes()
|
||||
if len(wire) < 2+recordHeaderSize {
|
||||
t.Fatalf("short frame: %d", len(wire))
|
||||
}
|
||||
|
||||
bodyLen := int(binary.BigEndian.Uint16(wire[:2]))
|
||||
if bodyLen != len(wire)-2 {
|
||||
t.Fatalf("body len mismatch: got %d want %d", bodyLen, len(wire)-2)
|
||||
}
|
||||
|
||||
epoch := binary.BigEndian.Uint32(wire[2:6])
|
||||
seq := binary.BigEndian.Uint64(wire[6:14])
|
||||
if epoch == 0 || seq == 0 {
|
||||
t.Fatalf("wire header still starts from zero: epoch=%d seq=%d", epoch, seq)
|
||||
}
|
||||
|
||||
reader, err := NewRecordConn(&replayConn{reader: bytes.NewReader(wire)}, "chacha20-poly1305", pskRecv[:], pskSend[:])
|
||||
if err != nil {
|
||||
t.Fatalf("new reader: %v", err)
|
||||
}
|
||||
|
||||
got := make([]byte, len(want))
|
||||
if _, err := io.ReadFull(reader, got); err != nil {
|
||||
t.Fatalf("read: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("plaintext mismatch: got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/transport/sudoku/crypto"
|
||||
httpmaskobfs "github.com/metacubex/mihomo/transport/sudoku/obfs/httpmask"
|
||||
sudokuobfs "github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku"
|
||||
)
|
||||
|
||||
const earlyKIPHandshakeTTL = 60 * time.Second
|
||||
|
||||
type EarlyCodecConfig struct {
|
||||
PSK string
|
||||
AEAD string
|
||||
EnablePureDownlink bool
|
||||
PaddingMin int
|
||||
PaddingMax int
|
||||
}
|
||||
|
||||
type EarlyClientState struct {
|
||||
RequestPayload []byte
|
||||
|
||||
cfg EarlyCodecConfig
|
||||
table *sudokuobfs.Table
|
||||
nonce [kipHelloNonceSize]byte
|
||||
ephemeral *ecdh.PrivateKey
|
||||
sessionC2S []byte
|
||||
sessionS2C []byte
|
||||
responseSet bool
|
||||
}
|
||||
|
||||
type EarlyServerState struct {
|
||||
ResponsePayload []byte
|
||||
UserHash string
|
||||
|
||||
cfg EarlyCodecConfig
|
||||
table *sudokuobfs.Table
|
||||
sessionC2S []byte
|
||||
sessionS2C []byte
|
||||
}
|
||||
|
||||
type ReplayAllowFunc func(userHash string, nonce [kipHelloNonceSize]byte, now time.Time) bool
|
||||
|
||||
type earlyMemoryConn struct {
|
||||
reader *bytes.Reader
|
||||
write bytes.Buffer
|
||||
}
|
||||
|
||||
func newEarlyMemoryConn(readBuf []byte) *earlyMemoryConn {
|
||||
return &earlyMemoryConn{reader: bytes.NewReader(readBuf)}
|
||||
}
|
||||
|
||||
func (c *earlyMemoryConn) Read(p []byte) (int, error) {
|
||||
if c == nil || c.reader == nil {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
return c.reader.Read(p)
|
||||
}
|
||||
|
||||
func (c *earlyMemoryConn) Write(p []byte) (int, error) {
|
||||
if c == nil {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
return c.write.Write(p)
|
||||
}
|
||||
|
||||
func (c *earlyMemoryConn) Close() error { return nil }
|
||||
func (c *earlyMemoryConn) LocalAddr() net.Addr { return earlyDummyAddr("local") }
|
||||
func (c *earlyMemoryConn) RemoteAddr() net.Addr { return earlyDummyAddr("remote") }
|
||||
func (c *earlyMemoryConn) SetDeadline(time.Time) error { return nil }
|
||||
func (c *earlyMemoryConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (c *earlyMemoryConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
func (c *earlyMemoryConn) Written() []byte { return append([]byte(nil), c.write.Bytes()...) }
|
||||
|
||||
type earlyDummyAddr string
|
||||
|
||||
func (a earlyDummyAddr) Network() string { return string(a) }
|
||||
func (a earlyDummyAddr) String() string { return string(a) }
|
||||
|
||||
func buildEarlyClientObfsConn(raw net.Conn, cfg EarlyCodecConfig, table *sudokuobfs.Table) net.Conn {
|
||||
base := sudokuobfs.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)
|
||||
if cfg.EnablePureDownlink {
|
||||
return base
|
||||
}
|
||||
packed := sudokuobfs.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
||||
return newDirectionalConn(raw, packed, base)
|
||||
}
|
||||
|
||||
func buildEarlyServerObfsConn(raw net.Conn, cfg EarlyCodecConfig, table *sudokuobfs.Table) net.Conn {
|
||||
uplink := sudokuobfs.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)
|
||||
if cfg.EnablePureDownlink {
|
||||
return uplink
|
||||
}
|
||||
packed := sudokuobfs.NewPackedConn(raw, table, cfg.PaddingMin, cfg.PaddingMax)
|
||||
return newDirectionalConn(raw, uplink, packed, packed.Flush)
|
||||
}
|
||||
|
||||
func NewEarlyClientState(cfg EarlyCodecConfig, table *sudokuobfs.Table, userHash [kipHelloUserHashSize]byte, feats uint32) (*EarlyClientState, error) {
|
||||
if table == nil {
|
||||
return nil, fmt.Errorf("nil table")
|
||||
}
|
||||
|
||||
curve := ecdh.X25519()
|
||||
ephemeral, err := curve.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ecdh generate failed: %w", err)
|
||||
}
|
||||
|
||||
var nonce [kipHelloNonceSize]byte
|
||||
if _, err := rand.Read(nonce[:]); err != nil {
|
||||
return nil, fmt.Errorf("nonce generate failed: %w", err)
|
||||
}
|
||||
|
||||
var clientPub [kipHelloPubSize]byte
|
||||
copy(clientPub[:], ephemeral.PublicKey().Bytes())
|
||||
hello := &KIPClientHello{
|
||||
Timestamp: time.Now(),
|
||||
UserHash: userHash,
|
||||
Nonce: nonce,
|
||||
ClientPub: clientPub,
|
||||
Features: feats,
|
||||
}
|
||||
|
||||
mem := newEarlyMemoryConn(nil)
|
||||
obfsConn := buildEarlyClientObfsConn(mem, cfg, table)
|
||||
pskC2S, pskS2C := derivePSKDirectionalBases(cfg.PSK)
|
||||
rc, err := crypto.NewRecordConn(obfsConn, cfg.AEAD, pskC2S, pskS2C)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client early crypto setup failed: %w", err)
|
||||
}
|
||||
if err := WriteKIPMessage(rc, KIPTypeClientHello, hello.EncodePayload()); err != nil {
|
||||
return nil, fmt.Errorf("write early client hello failed: %w", err)
|
||||
}
|
||||
|
||||
return &EarlyClientState{
|
||||
RequestPayload: mem.Written(),
|
||||
cfg: cfg,
|
||||
table: table,
|
||||
nonce: nonce,
|
||||
ephemeral: ephemeral,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *EarlyClientState) ProcessResponse(payload []byte) error {
|
||||
if s == nil {
|
||||
return fmt.Errorf("nil client state")
|
||||
}
|
||||
|
||||
mem := newEarlyMemoryConn(payload)
|
||||
obfsConn := buildEarlyClientObfsConn(mem, s.cfg, s.table)
|
||||
pskC2S, pskS2C := derivePSKDirectionalBases(s.cfg.PSK)
|
||||
rc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, pskC2S, pskS2C)
|
||||
if err != nil {
|
||||
return fmt.Errorf("client early crypto setup failed: %w", err)
|
||||
}
|
||||
|
||||
msg, err := ReadKIPMessage(rc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read early server hello failed: %w", err)
|
||||
}
|
||||
if msg.Type != KIPTypeServerHello {
|
||||
return fmt.Errorf("unexpected early handshake message: %d", msg.Type)
|
||||
}
|
||||
sh, err := DecodeKIPServerHelloPayload(msg.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode early server hello failed: %w", err)
|
||||
}
|
||||
if sh.Nonce != s.nonce {
|
||||
return fmt.Errorf("early handshake nonce mismatch")
|
||||
}
|
||||
|
||||
shared, err := x25519SharedSecret(s.ephemeral, sh.ServerPub[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("ecdh failed: %w", err)
|
||||
}
|
||||
s.sessionC2S, s.sessionS2C, err = deriveSessionDirectionalBases(s.cfg.PSK, shared, s.nonce)
|
||||
if err != nil {
|
||||
return fmt.Errorf("derive session keys failed: %w", err)
|
||||
}
|
||||
s.responseSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *EarlyClientState) WrapConn(raw net.Conn) (net.Conn, error) {
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("nil client state")
|
||||
}
|
||||
if !s.responseSet {
|
||||
return nil, fmt.Errorf("early handshake not completed")
|
||||
}
|
||||
|
||||
obfsConn := buildEarlyClientObfsConn(raw, s.cfg, s.table)
|
||||
rc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, s.sessionC2S, s.sessionS2C)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setup client session crypto failed: %w", err)
|
||||
}
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func (s *EarlyClientState) Ready() bool {
|
||||
return s != nil && s.responseSet
|
||||
}
|
||||
|
||||
func NewHTTPMaskClientEarlyHandshake(cfg EarlyCodecConfig, table *sudokuobfs.Table, userHash [kipHelloUserHashSize]byte, feats uint32) (*httpmaskobfs.ClientEarlyHandshake, error) {
|
||||
state, err := NewEarlyClientState(cfg, table, userHash, feats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &httpmaskobfs.ClientEarlyHandshake{
|
||||
RequestPayload: state.RequestPayload,
|
||||
HandleResponse: state.ProcessResponse,
|
||||
Ready: state.Ready,
|
||||
WrapConn: state.WrapConn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ProcessEarlyClientPayload(cfg EarlyCodecConfig, tables []*sudokuobfs.Table, payload []byte, allowReplay ReplayAllowFunc) (*EarlyServerState, error) {
|
||||
if len(payload) == 0 {
|
||||
return nil, fmt.Errorf("empty early payload")
|
||||
}
|
||||
if len(tables) == 0 {
|
||||
return nil, fmt.Errorf("no tables configured")
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for _, table := range tables {
|
||||
state, err := processEarlyClientPayloadForTable(cfg, table, payload, allowReplay)
|
||||
if err == nil {
|
||||
return state, nil
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = fmt.Errorf("early handshake probe failed")
|
||||
}
|
||||
return nil, firstErr
|
||||
}
|
||||
|
||||
func processEarlyClientPayloadForTable(cfg EarlyCodecConfig, table *sudokuobfs.Table, payload []byte, allowReplay ReplayAllowFunc) (*EarlyServerState, error) {
|
||||
mem := newEarlyMemoryConn(payload)
|
||||
obfsConn := buildEarlyServerObfsConn(mem, cfg, table)
|
||||
pskC2S, pskS2C := derivePSKDirectionalBases(cfg.PSK)
|
||||
rc, err := crypto.NewRecordConn(obfsConn, cfg.AEAD, pskS2C, pskC2S)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := ReadKIPMessage(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Type != KIPTypeClientHello {
|
||||
return nil, fmt.Errorf("unexpected handshake message: %d", msg.Type)
|
||||
}
|
||||
ch, err := DecodeKIPClientHelloPayload(msg.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if absInt64(time.Now().Unix()-ch.Timestamp.Unix()) > int64(earlyKIPHandshakeTTL.Seconds()) {
|
||||
return nil, fmt.Errorf("time skew/replay")
|
||||
}
|
||||
|
||||
userHash := hex.EncodeToString(ch.UserHash[:])
|
||||
if allowReplay != nil && !allowReplay(userHash, ch.Nonce, time.Now()) {
|
||||
return nil, fmt.Errorf("replay detected")
|
||||
}
|
||||
|
||||
curve := ecdh.X25519()
|
||||
serverEphemeral, err := curve.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ecdh generate failed: %w", err)
|
||||
}
|
||||
shared, err := x25519SharedSecret(serverEphemeral, ch.ClientPub[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ecdh failed: %w", err)
|
||||
}
|
||||
sessionC2S, sessionS2C, err := deriveSessionDirectionalBases(cfg.PSK, shared, ch.Nonce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("derive session keys failed: %w", err)
|
||||
}
|
||||
|
||||
var serverPub [kipHelloPubSize]byte
|
||||
copy(serverPub[:], serverEphemeral.PublicKey().Bytes())
|
||||
serverHello := &KIPServerHello{
|
||||
Nonce: ch.Nonce,
|
||||
ServerPub: serverPub,
|
||||
SelectedFeats: ch.Features & KIPFeatAll,
|
||||
}
|
||||
|
||||
respMem := newEarlyMemoryConn(nil)
|
||||
respObfs := buildEarlyServerObfsConn(respMem, cfg, table)
|
||||
respConn, err := crypto.NewRecordConn(respObfs, cfg.AEAD, pskS2C, pskC2S)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("server early crypto setup failed: %w", err)
|
||||
}
|
||||
if err := WriteKIPMessage(respConn, KIPTypeServerHello, serverHello.EncodePayload()); err != nil {
|
||||
return nil, fmt.Errorf("write early server hello failed: %w", err)
|
||||
}
|
||||
|
||||
return &EarlyServerState{
|
||||
ResponsePayload: respMem.Written(),
|
||||
UserHash: userHash,
|
||||
cfg: cfg,
|
||||
table: table,
|
||||
sessionC2S: sessionC2S,
|
||||
sessionS2C: sessionS2C,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *EarlyServerState) WrapConn(raw net.Conn) (net.Conn, error) {
|
||||
if s == nil {
|
||||
return nil, fmt.Errorf("nil server state")
|
||||
}
|
||||
obfsConn := buildEarlyServerObfsConn(raw, s.cfg, s.table)
|
||||
rc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, s.sessionS2C, s.sessionC2S)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("setup server session crypto failed: %w", err)
|
||||
}
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func NewHTTPMaskServerEarlyHandshake(cfg EarlyCodecConfig, tables []*sudokuobfs.Table, allowReplay ReplayAllowFunc) *httpmaskobfs.TunnelServerEarlyHandshake {
|
||||
return &httpmaskobfs.TunnelServerEarlyHandshake{
|
||||
Prepare: func(payload []byte) (*httpmaskobfs.PreparedServerEarlyHandshake, error) {
|
||||
state, err := ProcessEarlyClientPayload(cfg, tables, payload, allowReplay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &httpmaskobfs.PreparedServerEarlyHandshake{
|
||||
ResponsePayload: state.ResponsePayload,
|
||||
WrapConn: state.WrapConn,
|
||||
UserHash: state.UserHash,
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -337,6 +337,9 @@ func ServerHandshake(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, *Handshak
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid config: %w", err)
|
||||
}
|
||||
if userHash, ok := httpmask.EarlyHandshakeUserHash(rawConn); ok {
|
||||
return rawConn, &HandshakeMeta{UserHash: userHash}, nil
|
||||
}
|
||||
|
||||
handshakeTimeout := time.Duration(cfg.HandshakeTimeoutSeconds) * time.Second
|
||||
if handshakeTimeout <= 0 {
|
||||
|
||||
@@ -14,6 +14,30 @@ type HTTPMaskTunnelServer struct {
|
||||
ts *httpmask.TunnelServer
|
||||
}
|
||||
|
||||
func newHTTPMaskEarlyCodecConfig(cfg *ProtocolConfig, psk string) EarlyCodecConfig {
|
||||
return EarlyCodecConfig{
|
||||
PSK: psk,
|
||||
AEAD: cfg.AEADMethod,
|
||||
EnablePureDownlink: cfg.EnablePureDownlink,
|
||||
PaddingMin: cfg.PaddingMin,
|
||||
PaddingMax: cfg.PaddingMax,
|
||||
}
|
||||
}
|
||||
|
||||
func newClientHTTPMaskEarlyHandshake(cfg *ProtocolConfig) (*httpmask.ClientEarlyHandshake, error) {
|
||||
table, err := pickClientTable(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHTTPMaskClientEarlyHandshake(
|
||||
newHTTPMaskEarlyCodecConfig(cfg, ClientAEADSeed(cfg.Key)),
|
||||
table,
|
||||
kipUserHashFromKey(cfg.Key),
|
||||
KIPFeatAll,
|
||||
)
|
||||
}
|
||||
|
||||
func NewHTTPMaskTunnelServer(cfg *ProtocolConfig) *HTTPMaskTunnelServer {
|
||||
return newHTTPMaskTunnelServer(cfg, false)
|
||||
}
|
||||
@@ -35,6 +59,11 @@ func newHTTPMaskTunnelServer(cfg *ProtocolConfig, passThroughOnReject bool) *HTT
|
||||
Mode: cfg.HTTPMaskMode,
|
||||
PathRoot: cfg.HTTPMaskPathRoot,
|
||||
AuthKey: ServerAEADSeed(cfg.Key),
|
||||
EarlyHandshake: NewHTTPMaskServerEarlyHandshake(
|
||||
newHTTPMaskEarlyCodecConfig(cfg, ServerAEADSeed(cfg.Key)),
|
||||
cfg.tableCandidates(),
|
||||
globalHandshakeReplay.allow,
|
||||
),
|
||||
// When upstream fallback is enabled, preserve rejected HTTP requests for the caller.
|
||||
PassThroughOnReject: passThroughOnReject,
|
||||
})
|
||||
@@ -101,14 +130,25 @@ func DialHTTPMaskTunnel(ctx context.Context, serverAddress string, cfg *Protocol
|
||||
default:
|
||||
return nil, fmt.Errorf("http-mask-mode=%q does not use http tunnel", cfg.HTTPMaskMode)
|
||||
}
|
||||
var (
|
||||
earlyHandshake *httpmask.ClientEarlyHandshake
|
||||
err error
|
||||
)
|
||||
if upgrade != nil {
|
||||
earlyHandshake, err = newClientHTTPMaskEarlyHandshake(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return httpmask.DialTunnel(ctx, serverAddress, httpmask.TunnelDialOptions{
|
||||
Mode: cfg.HTTPMaskMode,
|
||||
TLSEnabled: cfg.HTTPMaskTLSEnabled,
|
||||
HostOverride: cfg.HTTPMaskHost,
|
||||
PathRoot: cfg.HTTPMaskPathRoot,
|
||||
AuthKey: ClientAEADSeed(cfg.Key),
|
||||
Upgrade: upgrade,
|
||||
Multiplex: cfg.HTTPMaskMultiplex,
|
||||
DialContext: dial,
|
||||
Mode: cfg.HTTPMaskMode,
|
||||
TLSEnabled: cfg.HTTPMaskTLSEnabled,
|
||||
HostOverride: cfg.HTTPMaskHost,
|
||||
PathRoot: cfg.HTTPMaskPathRoot,
|
||||
AuthKey: ClientAEADSeed(cfg.Key),
|
||||
EarlyHandshake: earlyHandshake,
|
||||
Upgrade: upgrade,
|
||||
Multiplex: cfg.HTTPMaskMultiplex,
|
||||
DialContext: dial,
|
||||
})
|
||||
}
|
||||
|
||||
+62
@@ -389,6 +389,68 @@ func TestHTTPMaskTunnel_WS_TCPRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPMaskTunnel_EarlyHandshake_TCPRoundTrip(t *testing.T) {
|
||||
modes := []string{"stream", "poll", "ws"}
|
||||
for _, mode := range modes {
|
||||
t.Run(mode, func(t *testing.T) {
|
||||
key := "tunnel-early-" + mode
|
||||
target := "1.1.1.1:80"
|
||||
|
||||
serverCfg := newTunnelTestTable(t, key)
|
||||
serverCfg.HTTPMaskMode = mode
|
||||
|
||||
addr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {
|
||||
if s.Type != SessionTypeTCP {
|
||||
return fmt.Errorf("unexpected session type: %v", s.Type)
|
||||
}
|
||||
if s.Target != target {
|
||||
return fmt.Errorf("target mismatch: %s", s.Target)
|
||||
}
|
||||
_, _ = s.Conn.Write([]byte("ok"))
|
||||
return nil
|
||||
})
|
||||
defer stop()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
clientCfg := *serverCfg
|
||||
clientCfg.ServerAddress = addr
|
||||
|
||||
handshakeCfg := clientCfg
|
||||
handshakeCfg.DisableHTTPMask = true
|
||||
tunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, func(raw net.Conn) (net.Conn, error) {
|
||||
return ClientHandshake(raw, &handshakeCfg)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("dial tunnel: %v", err)
|
||||
}
|
||||
defer tunnelConn.Close()
|
||||
|
||||
addrBuf, err := EncodeAddress(target)
|
||||
if err != nil {
|
||||
t.Fatalf("encode addr: %v", err)
|
||||
}
|
||||
if err := WriteKIPMessage(tunnelConn, KIPTypeOpenTCP, addrBuf); err != nil {
|
||||
t.Fatalf("write addr: %v", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(tunnelConn, buf); err != nil {
|
||||
t.Fatalf("read: %v", err)
|
||||
}
|
||||
if string(buf) != "ok" {
|
||||
t.Fatalf("unexpected payload: %q", buf)
|
||||
}
|
||||
|
||||
stop()
|
||||
for err := range errCh {
|
||||
t.Fatalf("server error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPMaskTunnel_Validation(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
cfg.Key = "k"
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
package httpmask
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tunnelEarlyDataQueryKey = "ed"
|
||||
tunnelEarlyDataHeader = "X-Sudoku-Early"
|
||||
)
|
||||
|
||||
type ClientEarlyHandshake struct {
|
||||
RequestPayload []byte
|
||||
HandleResponse func(payload []byte) error
|
||||
Ready func() bool
|
||||
WrapConn func(raw net.Conn) (net.Conn, error)
|
||||
}
|
||||
|
||||
type TunnelServerEarlyHandshake struct {
|
||||
Prepare func(payload []byte) (*PreparedServerEarlyHandshake, error)
|
||||
}
|
||||
|
||||
type PreparedServerEarlyHandshake struct {
|
||||
ResponsePayload []byte
|
||||
WrapConn func(raw net.Conn) (net.Conn, error)
|
||||
UserHash string
|
||||
}
|
||||
|
||||
type earlyHandshakeMeta interface {
|
||||
HTTPMaskEarlyHandshakeUserHash() string
|
||||
}
|
||||
|
||||
type earlyHandshakeConn struct {
|
||||
net.Conn
|
||||
userHash string
|
||||
}
|
||||
|
||||
func (c *earlyHandshakeConn) HTTPMaskEarlyHandshakeUserHash() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.userHash
|
||||
}
|
||||
|
||||
func wrapEarlyHandshakeConn(conn net.Conn, userHash string) net.Conn {
|
||||
if conn == nil {
|
||||
return nil
|
||||
}
|
||||
return &earlyHandshakeConn{Conn: conn, userHash: userHash}
|
||||
}
|
||||
|
||||
func EarlyHandshakeUserHash(conn net.Conn) (string, bool) {
|
||||
if conn == nil {
|
||||
return "", false
|
||||
}
|
||||
v, ok := conn.(earlyHandshakeMeta)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return v.HTTPMaskEarlyHandshakeUserHash(), true
|
||||
}
|
||||
|
||||
type authorizeResponse struct {
|
||||
token string
|
||||
earlyPayload []byte
|
||||
}
|
||||
|
||||
func isTunnelTokenByte(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' ||
|
||||
c == '_'
|
||||
}
|
||||
|
||||
func parseAuthorizeResponse(body []byte) (*authorizeResponse, error) {
|
||||
s := strings.TrimSpace(string(body))
|
||||
idx := strings.Index(s, "token=")
|
||||
if idx < 0 {
|
||||
return nil, errors.New("missing token")
|
||||
}
|
||||
s = s[idx+len("token="):]
|
||||
if s == "" {
|
||||
return nil, errors.New("empty token")
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if isTunnelTokenByte(c) {
|
||||
b.WriteByte(c)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
token := b.String()
|
||||
if token == "" {
|
||||
return nil, errors.New("empty token")
|
||||
}
|
||||
|
||||
out := &authorizeResponse{token: token}
|
||||
if earlyLine := findAuthorizeField(body, "ed="); earlyLine != "" {
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(earlyLine)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode early authorize payload failed: %w", err)
|
||||
}
|
||||
out.earlyPayload = decoded
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func findAuthorizeField(body []byte, prefix string) string {
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(body)), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, prefix) {
|
||||
return strings.TrimSpace(strings.TrimPrefix(line, prefix))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func setEarlyDataQuery(rawURL string, payload []byte) (string, error) {
|
||||
if len(payload) == 0 {
|
||||
return rawURL, nil
|
||||
}
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set(tunnelEarlyDataQueryKey, base64.RawURLEncoding.EncodeToString(payload))
|
||||
u.RawQuery = q.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func parseEarlyDataQuery(u *url.URL) ([]byte, error) {
|
||||
if u == nil {
|
||||
return nil, nil
|
||||
}
|
||||
val := strings.TrimSpace(u.Query().Get(tunnelEarlyDataQueryKey))
|
||||
if val == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return base64.RawURLEncoding.DecodeString(val)
|
||||
}
|
||||
|
||||
func applyEarlyHandshakeOrUpgrade(raw net.Conn, opts TunnelDialOptions) (net.Conn, error) {
|
||||
out := raw
|
||||
if opts.EarlyHandshake != nil && opts.EarlyHandshake.WrapConn != nil && (opts.EarlyHandshake.Ready == nil || opts.EarlyHandshake.Ready()) {
|
||||
wrapped, err := opts.EarlyHandshake.WrapConn(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrapped != nil {
|
||||
out = wrapped
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
if opts.Upgrade != nil {
|
||||
wrapped, err := opts.Upgrade(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrapped != nil {
|
||||
out = wrapped
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
+97
-73
@@ -72,6 +72,10 @@ type TunnelDialOptions struct {
|
||||
// AuthKey enables short-term HMAC auth for HTTP tunnel requests (anti-probing).
|
||||
// When set (non-empty), each HTTP request carries an Authorization bearer token derived from AuthKey.
|
||||
AuthKey string
|
||||
// EarlyHandshake folds the protocol handshake into the HTTP/WS setup round trip.
|
||||
// When the server accepts the early payload, DialTunnel returns a conn that is already post-handshake.
|
||||
// When the server does not echo early data, DialTunnel falls back to Upgrade.
|
||||
EarlyHandshake *ClientEarlyHandshake
|
||||
// Upgrade optionally wraps the raw tunnel conn and/or writes a small prelude before DialTunnel returns.
|
||||
// It is called with the raw tunnel conn; if it returns a non-nil conn, that conn is returned by DialTunnel.
|
||||
Upgrade func(raw net.Conn) (net.Conn, error)
|
||||
@@ -225,30 +229,11 @@ func canonicalHeaderHost(urlHost, scheme string) string {
|
||||
}
|
||||
|
||||
func parseTunnelToken(body []byte) (string, error) {
|
||||
s := strings.TrimSpace(string(body))
|
||||
idx := strings.Index(s, "token=")
|
||||
if idx < 0 {
|
||||
return "", errors.New("missing token")
|
||||
resp, err := parseAuthorizeResponse(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s = s[idx+len("token="):]
|
||||
if s == "" {
|
||||
return "", errors.New("empty token")
|
||||
}
|
||||
// Token is base64.RawURLEncoding (A-Z a-z 0-9 - _). Strip any trailing bytes (e.g. from CDN compression).
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
|
||||
b.WriteByte(c)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
token := b.String()
|
||||
if token == "" {
|
||||
return "", errors.New("empty token")
|
||||
}
|
||||
return token, nil
|
||||
return resp.token, nil
|
||||
}
|
||||
|
||||
type httpClientTarget struct {
|
||||
@@ -353,6 +338,13 @@ func dialSessionWithClient(ctx context.Context, client *http.Client, target http
|
||||
|
||||
auth := newTunnelAuth(opts.AuthKey, 0)
|
||||
authorizeURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, "/session")}).String()
|
||||
if opts.EarlyHandshake != nil && len(opts.EarlyHandshake.RequestPayload) > 0 {
|
||||
var err error
|
||||
authorizeURL, err = setEarlyDataQuery(authorizeURL, opts.EarlyHandshake.RequestPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var bodyBytes []byte
|
||||
for attempt := 0; ; attempt++ {
|
||||
@@ -410,13 +402,19 @@ func dialSessionWithClient(ctx context.Context, client *http.Client, target http
|
||||
break
|
||||
}
|
||||
|
||||
token, err := parseTunnelToken(bodyBytes)
|
||||
authResp, err := parseAuthorizeResponse(bodyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s authorize failed: %q", mode, strings.TrimSpace(string(bodyBytes)))
|
||||
}
|
||||
token := authResp.token
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("%s authorize empty token", mode)
|
||||
}
|
||||
if opts.EarlyHandshake != nil && len(authResp.earlyPayload) > 0 && opts.EarlyHandshake.HandleResponse != nil {
|
||||
if err := opts.EarlyHandshake.HandleResponse(authResp.earlyPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pushURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, "/api/v1/upload"), RawQuery: "token=" + url.QueryEscape(token)}).String()
|
||||
pullURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, "/stream"), RawQuery: "token=" + url.QueryEscape(token)}).String()
|
||||
@@ -671,16 +669,10 @@ func dialStreamSplitWithClient(ctx context.Context, client *http.Client, target
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("failed to build stream split conn")
|
||||
}
|
||||
outConn := net.Conn(c)
|
||||
if opts.Upgrade != nil {
|
||||
upgraded, err := opts.Upgrade(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
if upgraded != nil {
|
||||
outConn = upgraded
|
||||
}
|
||||
outConn, err := applyEarlyHandshakeOrUpgrade(c, opts)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return outConn, nil
|
||||
}
|
||||
@@ -694,16 +686,10 @@ func dialStreamSplit(ctx context.Context, serverAddress string, opts TunnelDialO
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("failed to build stream split conn")
|
||||
}
|
||||
outConn := net.Conn(c)
|
||||
if opts.Upgrade != nil {
|
||||
upgraded, err := opts.Upgrade(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
if upgraded != nil {
|
||||
outConn = upgraded
|
||||
}
|
||||
outConn, err := applyEarlyHandshakeOrUpgrade(c, opts)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return outConn, nil
|
||||
}
|
||||
@@ -1120,16 +1106,10 @@ func dialPollWithClient(ctx context.Context, client *http.Client, target httpCli
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("failed to build poll conn")
|
||||
}
|
||||
outConn := net.Conn(c)
|
||||
if opts.Upgrade != nil {
|
||||
upgraded, err := opts.Upgrade(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
if upgraded != nil {
|
||||
outConn = upgraded
|
||||
}
|
||||
outConn, err := applyEarlyHandshakeOrUpgrade(c, opts)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return outConn, nil
|
||||
}
|
||||
@@ -1143,16 +1123,10 @@ func dialPoll(ctx context.Context, serverAddress string, opts TunnelDialOptions)
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("failed to build poll conn")
|
||||
}
|
||||
outConn := net.Conn(c)
|
||||
if opts.Upgrade != nil {
|
||||
upgraded, err := opts.Upgrade(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
if upgraded != nil {
|
||||
outConn = upgraded
|
||||
}
|
||||
outConn, err := applyEarlyHandshakeOrUpgrade(c, opts)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return outConn, nil
|
||||
}
|
||||
@@ -1528,6 +1502,8 @@ type TunnelServerOptions struct {
|
||||
PullReadTimeout time.Duration
|
||||
// SessionTTL is a best-effort TTL to prevent leaked sessions. 0 uses a conservative default.
|
||||
SessionTTL time.Duration
|
||||
// EarlyHandshake optionally folds the protocol handshake into the initial HTTP/WS round trip.
|
||||
EarlyHandshake *TunnelServerEarlyHandshake
|
||||
}
|
||||
|
||||
type TunnelServer struct {
|
||||
@@ -1538,6 +1514,7 @@ type TunnelServer struct {
|
||||
|
||||
pullReadTimeout time.Duration
|
||||
sessionTTL time.Duration
|
||||
earlyHandshake *TunnelServerEarlyHandshake
|
||||
|
||||
mu sync.Mutex
|
||||
sessions map[string]*tunnelSession
|
||||
@@ -1570,6 +1547,7 @@ func NewTunnelServer(opts TunnelServerOptions) *TunnelServer {
|
||||
passThroughOnReject: opts.PassThroughOnReject,
|
||||
pullReadTimeout: timeout,
|
||||
sessionTTL: ttl,
|
||||
earlyHandshake: opts.EarlyHandshake,
|
||||
sessions: make(map[string]*tunnelSession),
|
||||
}
|
||||
}
|
||||
@@ -1925,9 +1903,12 @@ func (s *TunnelServer) handleStream(rawConn net.Conn, req *httpRequestHeader, he
|
||||
|
||||
switch strings.ToUpper(req.method) {
|
||||
case http.MethodGet:
|
||||
// Stream split-session: GET /session (no token) => token + start tunnel on a server-side pipe.
|
||||
if token == "" && path == "/session" {
|
||||
return s.sessionAuthorize(rawConn)
|
||||
earlyPayload, err := parseEarlyDataQuery(u)
|
||||
if err != nil {
|
||||
return rejectOrReply(http.StatusBadRequest, "bad request")
|
||||
}
|
||||
return s.sessionAuthorize(rawConn, earlyPayload)
|
||||
}
|
||||
// Stream split-session: GET /stream?token=... => downlink poll.
|
||||
if token != "" && path == "/stream" {
|
||||
@@ -2045,10 +2026,18 @@ func writeSimpleHTTPResponse(w io.Writer, code int, body string) error {
|
||||
|
||||
func writeTokenHTTPResponse(w io.Writer, token string) error {
|
||||
token = strings.TrimRight(token, "\r\n")
|
||||
// Use application/octet-stream to avoid CDN auto-compression (e.g. brotli) breaking clients that expect a plain token string.
|
||||
return writeTokenHTTPResponseWithEarlyData(w, token, nil)
|
||||
}
|
||||
|
||||
func writeTokenHTTPResponseWithEarlyData(w io.Writer, token string, earlyPayload []byte) error {
|
||||
token = strings.TrimRight(token, "\r\n")
|
||||
body := "token=" + token
|
||||
if len(earlyPayload) > 0 {
|
||||
body += "\ned=" + base64.RawURLEncoding.EncodeToString(earlyPayload)
|
||||
}
|
||||
_, err := io.WriteString(w,
|
||||
fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-Length: %d\r\nConnection: close\r\n\r\ntoken=%s",
|
||||
len("token=")+len(token), token))
|
||||
fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s",
|
||||
len(body), body))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2088,7 +2077,11 @@ func (s *TunnelServer) handlePoll(rawConn net.Conn, req *httpRequestHeader, head
|
||||
switch strings.ToUpper(req.method) {
|
||||
case http.MethodGet:
|
||||
if token == "" && path == "/session" {
|
||||
return s.sessionAuthorize(rawConn)
|
||||
earlyPayload, err := parseEarlyDataQuery(u)
|
||||
if err != nil {
|
||||
return rejectOrReply(http.StatusBadRequest, "bad request")
|
||||
}
|
||||
return s.sessionAuthorize(rawConn, earlyPayload)
|
||||
}
|
||||
if token != "" && path == "/stream" {
|
||||
if s.passThroughOnReject && !s.sessionHas(token) {
|
||||
@@ -2128,7 +2121,7 @@ func (s *TunnelServer) handlePoll(rawConn net.Conn, req *httpRequestHeader, head
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TunnelServer) sessionAuthorize(rawConn net.Conn) (HandleResult, net.Conn, error) {
|
||||
func (s *TunnelServer) sessionAuthorize(rawConn net.Conn, earlyPayload []byte) (HandleResult, net.Conn, error) {
|
||||
token, err := newSessionToken()
|
||||
if err != nil {
|
||||
_ = writeSimpleHTTPResponse(rawConn, http.StatusInternalServerError, "internal error")
|
||||
@@ -2137,6 +2130,37 @@ func (s *TunnelServer) sessionAuthorize(rawConn net.Conn) (HandleResult, net.Con
|
||||
}
|
||||
|
||||
c1, c2 := newHalfPipe()
|
||||
outConn := net.Conn(c1)
|
||||
var responsePayload []byte
|
||||
var userHash string
|
||||
if len(earlyPayload) > 0 && s.earlyHandshake != nil && s.earlyHandshake.Prepare != nil {
|
||||
prepared, err := s.earlyHandshake.Prepare(earlyPayload)
|
||||
if err != nil {
|
||||
_ = c1.Close()
|
||||
_ = c2.Close()
|
||||
if s.passThroughOnReject {
|
||||
return HandlePassThrough, newRejectedPreBufferedConn(rawConn, nil), nil
|
||||
}
|
||||
_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, "not found")
|
||||
_ = rawConn.Close()
|
||||
return HandleDone, nil, nil
|
||||
}
|
||||
responsePayload = prepared.ResponsePayload
|
||||
userHash = prepared.UserHash
|
||||
if prepared.WrapConn != nil {
|
||||
wrapped, err := prepared.WrapConn(c1)
|
||||
if err != nil {
|
||||
_ = c1.Close()
|
||||
_ = c2.Close()
|
||||
_ = writeSimpleHTTPResponse(rawConn, http.StatusInternalServerError, "internal error")
|
||||
_ = rawConn.Close()
|
||||
return HandleDone, nil, nil
|
||||
}
|
||||
if wrapped != nil {
|
||||
outConn = wrapEarlyHandshakeConn(wrapped, userHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.sessions[token] = &tunnelSession{conn: c2, lastActive: time.Now()}
|
||||
@@ -2144,9 +2168,9 @@ func (s *TunnelServer) sessionAuthorize(rawConn net.Conn) (HandleResult, net.Con
|
||||
|
||||
go s.reapLater(token)
|
||||
|
||||
_ = writeTokenHTTPResponse(rawConn, token)
|
||||
_ = writeTokenHTTPResponseWithEarlyData(rawConn, token, responsePayload)
|
||||
_ = rawConn.Close()
|
||||
return HandleStartTunnel, c1, nil
|
||||
return HandleStartTunnel, outConn, nil
|
||||
}
|
||||
|
||||
func newSessionToken() (string, error) {
|
||||
|
||||
+23
-8
@@ -2,6 +2,7 @@ package httpmask
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
@@ -115,6 +116,16 @@ func dialWS(ctx context.Context, serverAddress string, opts TunnelDialOptions) (
|
||||
Host: urlHost,
|
||||
Path: joinPathRoot(opts.PathRoot, "/ws"),
|
||||
}
|
||||
if opts.EarlyHandshake != nil && len(opts.EarlyHandshake.RequestPayload) > 0 {
|
||||
rawURL, err := setEarlyDataQuery(u.String(), opts.EarlyHandshake.RequestPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err = url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
header := make(stdhttp.Header)
|
||||
applyWSHeaders(header, headerHost)
|
||||
@@ -132,6 +143,16 @@ func dialWS(ctx context.Context, serverAddress string, opts TunnelDialOptions) (
|
||||
d := ws.Dialer{
|
||||
Host: headerHost,
|
||||
Header: ws.HandshakeHeaderHTTP(header),
|
||||
OnHeader: func(key, value []byte) error {
|
||||
if !strings.EqualFold(string(key), tunnelEarlyDataHeader) || opts.EarlyHandshake == nil || opts.EarlyHandshake.HandleResponse == nil {
|
||||
return nil
|
||||
}
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(strings.TrimSpace(string(value)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return opts.EarlyHandshake.HandleResponse(decoded)
|
||||
},
|
||||
NetDial: func(dialCtx context.Context, network, addr string) (net.Conn, error) {
|
||||
if addr == urlHost {
|
||||
addr = dialAddr
|
||||
@@ -161,16 +182,10 @@ func dialWS(ctx context.Context, serverAddress string, opts TunnelDialOptions) (
|
||||
}
|
||||
|
||||
wsConn := newWSStreamConn(conn, ws.StateClientSide)
|
||||
if opts.Upgrade == nil {
|
||||
return wsConn, nil
|
||||
}
|
||||
upgraded, err := opts.Upgrade(wsConn)
|
||||
upgraded, err := applyEarlyHandshakeOrUpgrade(wsConn, opts)
|
||||
if err != nil {
|
||||
_ = wsConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if upgraded != nil {
|
||||
return upgraded, nil
|
||||
}
|
||||
return wsConn, nil
|
||||
return upgraded, nil
|
||||
}
|
||||
|
||||
+34
-2
@@ -1,6 +1,7 @@
|
||||
package httpmask
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -63,15 +64,46 @@ func (s *TunnelServer) handleWS(rawConn net.Conn, req *httpRequestHeader, header
|
||||
return rejectOrReply(http.StatusNotFound, "not found")
|
||||
}
|
||||
|
||||
earlyPayload, err := parseEarlyDataQuery(u)
|
||||
if err != nil {
|
||||
return rejectOrReply(http.StatusBadRequest, "bad request")
|
||||
}
|
||||
var prepared *PreparedServerEarlyHandshake
|
||||
if len(earlyPayload) > 0 && s.earlyHandshake != nil && s.earlyHandshake.Prepare != nil {
|
||||
prepared, err = s.earlyHandshake.Prepare(earlyPayload)
|
||||
if err != nil {
|
||||
return rejectOrReply(http.StatusNotFound, "not found")
|
||||
}
|
||||
}
|
||||
|
||||
prefix := make([]byte, 0, len(headerBytes)+len(buffered))
|
||||
prefix = append(prefix, headerBytes...)
|
||||
prefix = append(prefix, buffered...)
|
||||
wsConnRaw := newPreBufferedConn(rawConn, prefix)
|
||||
|
||||
if _, err := ws.Upgrade(wsConnRaw); err != nil {
|
||||
upgrader := ws.Upgrader{}
|
||||
if prepared != nil && len(prepared.ResponsePayload) > 0 {
|
||||
upgrader.OnBeforeUpgrade = func() (ws.HandshakeHeader, error) {
|
||||
h := http.Header{}
|
||||
h.Set(tunnelEarlyDataHeader, base64.RawURLEncoding.EncodeToString(prepared.ResponsePayload))
|
||||
return ws.HandshakeHeaderHTTP(h), nil
|
||||
}
|
||||
}
|
||||
if _, err := upgrader.Upgrade(wsConnRaw); err != nil {
|
||||
_ = rawConn.Close()
|
||||
return HandleDone, nil, nil
|
||||
}
|
||||
|
||||
return HandleStartTunnel, newWSStreamConn(wsConnRaw, ws.StateServerSide), nil
|
||||
outConn := net.Conn(newWSStreamConn(wsConnRaw, ws.StateServerSide))
|
||||
if prepared != nil && prepared.WrapConn != nil {
|
||||
wrapped, err := prepared.WrapConn(outConn)
|
||||
if err != nil {
|
||||
_ = outConn.Close()
|
||||
return HandleDone, nil, nil
|
||||
}
|
||||
if wrapped != nil {
|
||||
outConn = wrapEarlyHandshakeConn(wrapped, prepared.UserHash)
|
||||
}
|
||||
}
|
||||
return HandleStartTunnel, outConn, nil
|
||||
}
|
||||
|
||||
+110
-82
@@ -11,35 +11,35 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// 每次从 RNG 获取批量随机数的缓存大小,减少 RNG 函数调用开销
|
||||
RngBatchSize = 128
|
||||
|
||||
packedProtectedPrefixBytes = 14
|
||||
)
|
||||
|
||||
// 1. 使用 12字节->16组 的块处理优化 Write (减少循环开销)
|
||||
// 2. 使用整数阈值随机概率判断 Padding,与纯 Sudoku 保持流量特征一致
|
||||
// 3. Read 使用 copy 移动避免底层数组泄漏
|
||||
// PackedConn encodes traffic with the packed Sudoku layout while preserving
|
||||
// the same padding model as the regular connection.
|
||||
type PackedConn struct {
|
||||
net.Conn
|
||||
table *Table
|
||||
reader *bufio.Reader
|
||||
|
||||
// 读缓冲
|
||||
// Read-side buffers.
|
||||
rawBuf []byte
|
||||
pendingData []byte // 解码后尚未被 Read 取走的字节
|
||||
pendingData []byte
|
||||
|
||||
// 写缓冲与状态
|
||||
// Write-side state.
|
||||
writeMu sync.Mutex
|
||||
writeBuf []byte
|
||||
bitBuf uint64 // 暂存的位数据
|
||||
bitCount int // 暂存的位数
|
||||
bitBuf uint64
|
||||
bitCount int
|
||||
|
||||
// 读状态
|
||||
// Read-side bit accumulator.
|
||||
readBitBuf uint64
|
||||
readBits int
|
||||
|
||||
// 随机数与填充控制 - 使用整数阈值随机,与 Conn 一致
|
||||
// Padding selection matches Conn's threshold-based model.
|
||||
rng *rand.Rand
|
||||
paddingThreshold uint64 // 与 Conn 保持一致的随机概率模型
|
||||
paddingThreshold uint64
|
||||
padMarker byte
|
||||
padPool []byte
|
||||
}
|
||||
@@ -95,7 +95,6 @@ func NewPackedConn(c net.Conn, table *Table, pMin, pMax int) *PackedConn {
|
||||
return pc
|
||||
}
|
||||
|
||||
// maybeAddPadding 内联辅助:根据概率阈值插入 padding
|
||||
func (pc *PackedConn) maybeAddPadding(out []byte) []byte {
|
||||
if shouldPad(pc.rng, pc.paddingThreshold) {
|
||||
out = append(out, pc.getPaddingByte())
|
||||
@@ -103,7 +102,73 @@ func (pc *PackedConn) maybeAddPadding(out []byte) []byte {
|
||||
return out
|
||||
}
|
||||
|
||||
// Write 极致优化版 - 批量处理 12 字节
|
||||
func (pc *PackedConn) appendGroup(out []byte, group byte) []byte {
|
||||
out = pc.maybeAddPadding(out)
|
||||
return append(out, pc.encodeGroup(group))
|
||||
}
|
||||
|
||||
func (pc *PackedConn) appendForcedPadding(out []byte) []byte {
|
||||
return append(out, pc.getPaddingByte())
|
||||
}
|
||||
|
||||
func (pc *PackedConn) nextProtectedPrefixGap() int {
|
||||
return 1 + pc.rng.Intn(2)
|
||||
}
|
||||
|
||||
func (pc *PackedConn) writeProtectedPrefix(out []byte, p []byte) ([]byte, int) {
|
||||
if len(p) == 0 {
|
||||
return out, 0
|
||||
}
|
||||
|
||||
limit := len(p)
|
||||
if limit > packedProtectedPrefixBytes {
|
||||
limit = packedProtectedPrefixBytes
|
||||
}
|
||||
|
||||
for padCount := 0; padCount < 1+pc.rng.Intn(2); padCount++ {
|
||||
out = pc.appendForcedPadding(out)
|
||||
}
|
||||
|
||||
gap := pc.nextProtectedPrefixGap()
|
||||
effective := 0
|
||||
for i := 0; i < limit; i++ {
|
||||
pc.bitBuf = (pc.bitBuf << 8) | uint64(p[i])
|
||||
pc.bitCount += 8
|
||||
for pc.bitCount >= 6 {
|
||||
pc.bitCount -= 6
|
||||
group := byte(pc.bitBuf >> pc.bitCount)
|
||||
if pc.bitCount == 0 {
|
||||
pc.bitBuf = 0
|
||||
} else {
|
||||
pc.bitBuf &= (1 << pc.bitCount) - 1
|
||||
}
|
||||
out = pc.appendGroup(out, group&0x3F)
|
||||
}
|
||||
|
||||
effective++
|
||||
if effective >= gap {
|
||||
out = pc.appendForcedPadding(out)
|
||||
effective = 0
|
||||
gap = pc.nextProtectedPrefixGap()
|
||||
}
|
||||
}
|
||||
|
||||
return out, limit
|
||||
}
|
||||
|
||||
func (pc *PackedConn) drainPendingData(dst []byte) int {
|
||||
n := copy(dst, pc.pendingData)
|
||||
if n == len(pc.pendingData) {
|
||||
pc.pendingData = pc.pendingData[:0]
|
||||
return n
|
||||
}
|
||||
|
||||
remaining := len(pc.pendingData) - n
|
||||
copy(pc.pendingData, pc.pendingData[n:])
|
||||
pc.pendingData = pc.pendingData[:remaining]
|
||||
return n
|
||||
}
|
||||
|
||||
func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
@@ -112,20 +177,19 @@ func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
pc.writeMu.Lock()
|
||||
defer pc.writeMu.Unlock()
|
||||
|
||||
// 1. 预分配内存,避免 append 导致的多次扩容
|
||||
// 预估:原数据 * 1.5 (4/3 + padding 余量)
|
||||
needed := len(p)*3/2 + 32
|
||||
if cap(pc.writeBuf) < needed {
|
||||
pc.writeBuf = make([]byte, 0, needed)
|
||||
}
|
||||
out := pc.writeBuf[:0]
|
||||
|
||||
i := 0
|
||||
var prefixN int
|
||||
out, prefixN = pc.writeProtectedPrefix(out, p)
|
||||
|
||||
i := prefixN
|
||||
n := len(p)
|
||||
|
||||
// 2. 头部对齐处理 (Slow Path)
|
||||
for pc.bitCount > 0 && i < n {
|
||||
out = pc.maybeAddPadding(out)
|
||||
b := p[i]
|
||||
i++
|
||||
pc.bitBuf = (pc.bitBuf << 8) | uint64(b)
|
||||
@@ -138,14 +202,11 @@ func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
} else {
|
||||
pc.bitBuf &= (1 << pc.bitCount) - 1
|
||||
}
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(group&0x3F))
|
||||
out = pc.appendGroup(out, group&0x3F)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 极速批量处理 (Fast Path) - 每次处理 12 字节 → 生成 16 个编码组
|
||||
for i+11 < n {
|
||||
// 处理 4 组,每组 3 字节
|
||||
for batch := 0; batch < 4; batch++ {
|
||||
b1, b2, b3 := p[i], p[i+1], p[i+2]
|
||||
i += 3
|
||||
@@ -155,19 +216,13 @@ func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
g3 := ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03)
|
||||
g4 := b3 & 0x3F
|
||||
|
||||
// 每个组之前都有概率插入 padding
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g1))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g2))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g3))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g4))
|
||||
out = pc.appendGroup(out, g1)
|
||||
out = pc.appendGroup(out, g2)
|
||||
out = pc.appendGroup(out, g3)
|
||||
out = pc.appendGroup(out, g4)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 处理剩余的 3 字节块
|
||||
for i+2 < n {
|
||||
b1, b2, b3 := p[i], p[i+1], p[i+2]
|
||||
i += 3
|
||||
@@ -177,17 +232,12 @@ func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
g3 := ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03)
|
||||
g4 := b3 & 0x3F
|
||||
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g1))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g2))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g3))
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(g4))
|
||||
out = pc.appendGroup(out, g1)
|
||||
out = pc.appendGroup(out, g2)
|
||||
out = pc.appendGroup(out, g3)
|
||||
out = pc.appendGroup(out, g4)
|
||||
}
|
||||
|
||||
// 5. 尾部处理 (Tail Path) - 处理剩余的 1 或 2 个字节
|
||||
for ; i < n; i++ {
|
||||
b := p[i]
|
||||
pc.bitBuf = (pc.bitBuf << 8) | uint64(b)
|
||||
@@ -200,35 +250,28 @@ func (pc *PackedConn) Write(p []byte) (int, error) {
|
||||
} else {
|
||||
pc.bitBuf &= (1 << pc.bitCount) - 1
|
||||
}
|
||||
out = pc.maybeAddPadding(out)
|
||||
out = append(out, pc.encodeGroup(group&0x3F))
|
||||
out = pc.appendGroup(out, group&0x3F)
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 处理残留位
|
||||
if pc.bitCount > 0 {
|
||||
out = pc.maybeAddPadding(out)
|
||||
group := byte(pc.bitBuf << (6 - pc.bitCount))
|
||||
pc.bitBuf = 0
|
||||
pc.bitCount = 0
|
||||
out = append(out, pc.encodeGroup(group&0x3F))
|
||||
out = pc.appendGroup(out, group&0x3F)
|
||||
out = append(out, pc.padMarker)
|
||||
}
|
||||
|
||||
// 尾部可能添加 padding
|
||||
out = pc.maybeAddPadding(out)
|
||||
|
||||
// 发送数据
|
||||
if len(out) > 0 {
|
||||
_, err := pc.Conn.Write(out)
|
||||
pc.writeBuf = out[:0]
|
||||
return len(p), err
|
||||
return len(p), writeFull(pc.Conn, out)
|
||||
}
|
||||
pc.writeBuf = out[:0]
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Flush 处理最后不足 6 bit 的情况
|
||||
func (pc *PackedConn) Flush() error {
|
||||
pc.writeMu.Lock()
|
||||
defer pc.writeMu.Unlock()
|
||||
@@ -243,38 +286,34 @@ func (pc *PackedConn) Flush() error {
|
||||
out = append(out, pc.padMarker)
|
||||
}
|
||||
|
||||
// 尾部随机添加 padding
|
||||
out = pc.maybeAddPadding(out)
|
||||
|
||||
if len(out) > 0 {
|
||||
_, err := pc.Conn.Write(out)
|
||||
pc.writeBuf = out[:0]
|
||||
return err
|
||||
return writeFull(pc.Conn, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read 优化版:减少切片操作,避免内存泄漏
|
||||
func (pc *PackedConn) Read(p []byte) (int, error) {
|
||||
// 1. 优先返回待处理区的数据
|
||||
if len(pc.pendingData) > 0 {
|
||||
n := copy(p, pc.pendingData)
|
||||
if n == len(pc.pendingData) {
|
||||
pc.pendingData = pc.pendingData[:0]
|
||||
} else {
|
||||
// 优化:移动剩余数据到数组头部,避免切片指向中间导致内存泄漏
|
||||
remaining := len(pc.pendingData) - n
|
||||
copy(pc.pendingData, pc.pendingData[n:])
|
||||
pc.pendingData = pc.pendingData[:remaining]
|
||||
func writeFull(w io.Writer, b []byte) error {
|
||||
for len(b) > 0 {
|
||||
n, err := w.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n, nil
|
||||
b = b[n:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PackedConn) Read(p []byte) (int, error) {
|
||||
if len(pc.pendingData) > 0 {
|
||||
return pc.drainPendingData(p), nil
|
||||
}
|
||||
|
||||
// 2. 循环读取直到解出数据或出错
|
||||
for {
|
||||
nr, rErr := pc.reader.Read(pc.rawBuf)
|
||||
if nr > 0 {
|
||||
// 缓存频繁访问的变量
|
||||
rBuf := pc.readBitBuf
|
||||
rBits := pc.readBits
|
||||
padMarker := pc.padMarker
|
||||
@@ -324,24 +363,13 @@ func (pc *PackedConn) Read(p []byte) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 返回解码后的数据 - 优化:避免底层数组泄漏
|
||||
n := copy(p, pc.pendingData)
|
||||
if n == len(pc.pendingData) {
|
||||
pc.pendingData = pc.pendingData[:0]
|
||||
} else {
|
||||
remaining := len(pc.pendingData) - n
|
||||
copy(pc.pendingData, pc.pendingData[n:])
|
||||
pc.pendingData = pc.pendingData[:remaining]
|
||||
}
|
||||
return n, nil
|
||||
return pc.drainPendingData(p), nil
|
||||
}
|
||||
|
||||
// getPaddingByte 从 Pool 中随机取 Padding 字节
|
||||
func (pc *PackedConn) getPaddingByte() byte {
|
||||
return pc.padPool[pc.rng.Intn(len(pc.padPool))]
|
||||
}
|
||||
|
||||
// encodeGroup 编码 6-bit 组
|
||||
func (pc *PackedConn) encodeGroup(group byte) byte {
|
||||
return pc.table.layout.encodeGroup(group)
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package sudoku
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type mockConn struct {
|
||||
readBuf []byte
|
||||
writeBuf []byte
|
||||
}
|
||||
|
||||
func (c *mockConn) Read(p []byte) (int, error) {
|
||||
if len(c.readBuf) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n := copy(p, c.readBuf)
|
||||
c.readBuf = c.readBuf[n:]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *mockConn) Write(p []byte) (int, error) {
|
||||
c.writeBuf = append(c.writeBuf, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *mockConn) Close() error { return nil }
|
||||
func (c *mockConn) LocalAddr() net.Addr { return nil }
|
||||
func (c *mockConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *mockConn) SetDeadline(time.Time) error { return nil }
|
||||
func (c *mockConn) SetReadDeadline(time.Time) error { return nil }
|
||||
func (c *mockConn) SetWriteDeadline(time.Time) error { return nil }
|
||||
|
||||
func TestPackedConn_ProtectedPrefixPadding(t *testing.T) {
|
||||
table := NewTable("packed-prefix-seed", "prefer_ascii")
|
||||
mock := &mockConn{}
|
||||
writer := NewPackedConn(mock, table, 0, 0)
|
||||
writer.rng = rand.New(rand.NewSource(1))
|
||||
|
||||
payload := bytes.Repeat([]byte{0}, 32)
|
||||
if _, err := writer.Write(payload); err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
wire := append([]byte(nil), mock.writeBuf...)
|
||||
if len(wire) < 20 {
|
||||
t.Fatalf("wire too short: %d", len(wire))
|
||||
}
|
||||
|
||||
firstHint := -1
|
||||
nonHintCount := 0
|
||||
maxHintRun := 0
|
||||
currentHintRun := 0
|
||||
for i, b := range wire[:20] {
|
||||
if table.layout.isHint(b) {
|
||||
if firstHint == -1 {
|
||||
firstHint = i
|
||||
}
|
||||
currentHintRun++
|
||||
if currentHintRun > maxHintRun {
|
||||
maxHintRun = currentHintRun
|
||||
}
|
||||
continue
|
||||
}
|
||||
nonHintCount++
|
||||
currentHintRun = 0
|
||||
}
|
||||
|
||||
if firstHint < 1 || firstHint > 2 {
|
||||
t.Fatalf("expected 1-2 leading padding bytes, first hint index=%d", firstHint)
|
||||
}
|
||||
if nonHintCount < 6 {
|
||||
t.Fatalf("expected dense prefix padding, got only %d non-hint bytes in first 20", nonHintCount)
|
||||
}
|
||||
if maxHintRun > 3 {
|
||||
t.Fatalf("prefix still exposes long hint run: %d", maxHintRun)
|
||||
}
|
||||
|
||||
reader := NewPackedConn(&mockConn{readBuf: wire}, table, 0, 0)
|
||||
decoded := make([]byte, len(payload))
|
||||
if _, err := io.ReadFull(reader, decoded); err != nil {
|
||||
t.Fatalf("read back: %v", err)
|
||||
}
|
||||
if !bytes.Equal(decoded, payload) {
|
||||
t.Fatalf("roundtrip mismatch")
|
||||
}
|
||||
}
|
||||
@@ -131,34 +131,45 @@ func (c *Client) resetHealthCheckTimer() {
|
||||
c.healthCheckTimer.Reset(DefaultHealthCheckTimeout)
|
||||
}
|
||||
|
||||
func (c *Client) dial(ctx context.Context, request *http.Request, conn *httpConn, pipeReader *io.PipeReader, pipeWriter *io.PipeWriter) {
|
||||
func (c *Client) roundTrip(request *http.Request, conn *httpConn) {
|
||||
c.startOnce.Do(c.start)
|
||||
trace := &httptrace.ClientTrace{
|
||||
GotConn: func(connInfo httptrace.GotConnInfo) {
|
||||
conn.SetLocalAddr(connInfo.Conn.LocalAddr())
|
||||
conn.SetRemoteAddr(connInfo.Conn.RemoteAddr())
|
||||
},
|
||||
}
|
||||
request = request.WithContext(httptrace.WithClientTrace(ctx, trace))
|
||||
response, err := c.roundTripper.RoundTrip(request)
|
||||
if err != nil {
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
_ = pipeReader.CloseWithError(err)
|
||||
conn.setUp(nil, err)
|
||||
} else if response.StatusCode != http.StatusOK {
|
||||
_ = response.Body.Close()
|
||||
err = fmt.Errorf("unexpected status code: %d", response.StatusCode)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
_ = pipeReader.CloseWithError(err)
|
||||
conn.setUp(nil, err)
|
||||
} else {
|
||||
c.resetHealthCheckTimer()
|
||||
conn.setUp(response.Body, nil)
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
request.Body = pipeReader
|
||||
*conn = httpConn{
|
||||
writer: pipeWriter,
|
||||
created: make(chan struct{}),
|
||||
}
|
||||
ctx, cancel := context.WithCancel(c.ctx) // requestCtx must alive during conn not closed
|
||||
conn.cancelFn = cancel // cancel ctx when conn closed
|
||||
go func() {
|
||||
timeout := time.AfterFunc(C.DefaultTCPTimeout, cancel) // only cancel when RoundTrip timeout
|
||||
defer timeout.Stop() // RoundTrip already returned, stop the timer
|
||||
trace := &httptrace.ClientTrace{
|
||||
GotConn: func(connInfo httptrace.GotConnInfo) {
|
||||
conn.SetLocalAddr(connInfo.Conn.LocalAddr())
|
||||
conn.SetRemoteAddr(connInfo.Conn.RemoteAddr())
|
||||
},
|
||||
}
|
||||
request = request.WithContext(httptrace.WithClientTrace(ctx, trace))
|
||||
response, err := c.roundTripper.RoundTrip(request)
|
||||
if err != nil {
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
_ = pipeReader.CloseWithError(err)
|
||||
conn.setUp(nil, err)
|
||||
} else if response.StatusCode != http.StatusOK {
|
||||
_ = response.Body.Close()
|
||||
err = fmt.Errorf("unexpected status code: %d", response.StatusCode)
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
_ = pipeReader.CloseWithError(err)
|
||||
conn.setUp(nil, err)
|
||||
} else {
|
||||
c.resetHealthCheckTimer()
|
||||
conn.setUp(response.Body, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) Dial(ctx context.Context, host string) (net.Conn, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{
|
||||
@@ -166,23 +177,16 @@ func (c *Client) Dial(ctx context.Context, host string) (net.Conn, error) {
|
||||
Host: host,
|
||||
},
|
||||
Header: make(http.Header),
|
||||
Body: pipeReader,
|
||||
Host: host,
|
||||
}
|
||||
request.Header.Add("User-Agent", TCPUserAgent)
|
||||
request.Header.Add("Proxy-Authorization", c.auth)
|
||||
conn := &tcpConn{
|
||||
httpConn: httpConn{
|
||||
writer: pipeWriter,
|
||||
created: make(chan struct{}),
|
||||
},
|
||||
}
|
||||
go c.dial(ctx, request, &conn.httpConn, pipeReader, pipeWriter)
|
||||
conn := &tcpConn{}
|
||||
c.roundTrip(request, &conn.httpConn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListenPacket(ctx context.Context) (net.PacketConn, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{
|
||||
@@ -190,25 +194,16 @@ func (c *Client) ListenPacket(ctx context.Context) (net.PacketConn, error) {
|
||||
Host: UDPMagicAddress,
|
||||
},
|
||||
Header: make(http.Header),
|
||||
Body: pipeReader,
|
||||
Host: UDPMagicAddress,
|
||||
}
|
||||
request.Header.Add("User-Agent", UDPUserAgent)
|
||||
request.Header.Add("Proxy-Authorization", c.auth)
|
||||
conn := &clientPacketConn{
|
||||
packetConn: packetConn{
|
||||
httpConn: httpConn{
|
||||
writer: pipeWriter,
|
||||
created: make(chan struct{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
go c.dial(ctx, request, &conn.httpConn, pipeReader, pipeWriter)
|
||||
conn := &clientPacketConn{}
|
||||
c.roundTrip(request, &conn.httpConn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListenICMP(ctx context.Context) (*IcmpConn, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
request := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{
|
||||
@@ -216,18 +211,12 @@ func (c *Client) ListenICMP(ctx context.Context) (*IcmpConn, error) {
|
||||
Host: ICMPMagicAddress,
|
||||
},
|
||||
Header: make(http.Header),
|
||||
Body: pipeReader,
|
||||
Host: ICMPMagicAddress,
|
||||
}
|
||||
request.Header.Add("User-Agent", ICMPUserAgent)
|
||||
request.Header.Add("Proxy-Authorization", c.auth)
|
||||
conn := &IcmpConn{
|
||||
httpConn{
|
||||
writer: pipeWriter,
|
||||
created: make(chan struct{}),
|
||||
},
|
||||
}
|
||||
go c.dial(ctx, request, &conn.httpConn, pipeReader, pipeWriter)
|
||||
conn := &IcmpConn{}
|
||||
c.roundTrip(request, &conn.httpConn)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ func forceCloseAllConnections(roundTripper RoundTripper) {
|
||||
roundTripper.CloseIdleConnections()
|
||||
switch tr := roundTripper.(type) {
|
||||
case *http.Http2Transport:
|
||||
gun.CloseTransport(tr)
|
||||
gun.CloseHttp2Transport(tr)
|
||||
case *http3.Transport:
|
||||
_ = tr.Close()
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ type httpConn struct {
|
||||
body io.ReadCloser
|
||||
created chan struct{}
|
||||
createErr error
|
||||
cancelFn func()
|
||||
gun.NetAddr
|
||||
|
||||
// deadlines
|
||||
@@ -125,6 +126,9 @@ func (h *httpConn) Close() error {
|
||||
if h.body != nil {
|
||||
errorArr = append(errorArr, h.body.Close())
|
||||
}
|
||||
if h.cancelFn != nil {
|
||||
h.cancelFn()
|
||||
}
|
||||
return errors.Join(errorArr...)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ require (
|
||||
github.com/metacubex/hpke v0.1.0 // indirect
|
||||
github.com/metacubex/http v0.1.0 // indirect
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 // indirect
|
||||
github.com/metacubex/mhurl v0.1.0 // indirect
|
||||
github.com/metacubex/mihomo v1.7.0 // indirect
|
||||
github.com/metacubex/mlkem v0.1.0 // indirect
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
|
||||
|
||||
@@ -102,6 +102,8 @@ github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o=
|
||||
github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
|
||||
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
|
||||
github.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=
|
||||
github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=
|
||||
github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
|
||||
@@ -58,6 +58,7 @@ require (
|
||||
github.com/metacubex/hpke v0.1.0 // indirect
|
||||
github.com/metacubex/http v0.1.0 // indirect
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 // indirect
|
||||
github.com/metacubex/mhurl v0.1.0 // indirect
|
||||
github.com/metacubex/mlkem v0.1.0 // indirect
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
|
||||
github.com/metacubex/qpack v0.6.0 // indirect
|
||||
|
||||
@@ -102,6 +102,8 @@ github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o=
|
||||
github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
|
||||
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
|
||||
github.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=
|
||||
github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=
|
||||
github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
|
||||
@@ -68,8 +68,11 @@ func QueryProxyGroupNames(excludeNotSelectable bool) []string {
|
||||
}
|
||||
|
||||
for _, p := range proxies {
|
||||
if _, ok := p.Adapter().(outboundgroup.ProxyGroup); ok {
|
||||
if g, ok := p.Adapter().(outboundgroup.ProxyGroup); ok {
|
||||
if !excludeNotSelectable || p.Type() == C.Selector {
|
||||
if g.Hidden() {
|
||||
continue
|
||||
}
|
||||
result = append(result, p.Name())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ type Fallback struct {
|
||||
testUrl string
|
||||
selected string
|
||||
expectedStatus string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
func (f *Fallback) Now() string {
|
||||
@@ -90,8 +88,8 @@ func (f *Fallback) MarshalJSON() ([]byte, error) {
|
||||
"testUrl": f.testUrl,
|
||||
"expectedStatus": f.expectedStatus,
|
||||
"fixed": f.selected,
|
||||
"hidden": f.Hidden,
|
||||
"icon": f.Icon,
|
||||
"hidden": f.Hidden(),
|
||||
"icon": f.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,6 +161,8 @@ func NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallba
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.Fallback,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -173,7 +173,5 @@ func NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallba
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
|
||||
type GroupBase struct {
|
||||
*outbound.Base
|
||||
hidden bool
|
||||
icon string
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterRegs []*regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
@@ -30,7 +32,7 @@ type GroupBase struct {
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
TestTimeout int
|
||||
testTimeout int
|
||||
maxFailedTimes int
|
||||
|
||||
// for GetProxies
|
||||
@@ -42,6 +44,8 @@ type GroupBase struct {
|
||||
type GroupBaseOption struct {
|
||||
Name string
|
||||
Type C.AdapterType
|
||||
Hidden bool
|
||||
Icon string
|
||||
Filter string
|
||||
ExcludeFilter string
|
||||
ExcludeType string
|
||||
@@ -74,17 +78,19 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
|
||||
gb := &GroupBase{
|
||||
Base: outbound.NewBase(outbound.BaseOption{Name: opt.Name, Type: opt.Type}),
|
||||
hidden: opt.Hidden,
|
||||
icon: opt.Icon,
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterRegs: excludeFilterRegs,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.Providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
testTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.MaxFailedTimes,
|
||||
}
|
||||
|
||||
if gb.TestTimeout == 0 {
|
||||
gb.TestTimeout = 5000
|
||||
if gb.testTimeout == 0 {
|
||||
gb.testTimeout = 5000
|
||||
}
|
||||
if gb.maxFailedTimes == 0 {
|
||||
gb.maxFailedTimes = 5
|
||||
@@ -93,6 +99,14 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
return gb
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Hidden() bool {
|
||||
return gb.hidden
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Icon() string {
|
||||
return gb.icon
|
||||
}
|
||||
|
||||
func (gb *GroupBase) Touch() {
|
||||
for _, pd := range gb.providers {
|
||||
pd.Touch()
|
||||
@@ -265,7 +279,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func(
|
||||
log.Debugln("ProxyGroup: %s first failed", gb.Name())
|
||||
gb.failedTime = time.Now()
|
||||
} else {
|
||||
if time.Since(gb.failedTime) > time.Duration(gb.TestTimeout)*time.Millisecond {
|
||||
if time.Since(gb.failedTime) > time.Duration(gb.testTimeout)*time.Millisecond {
|
||||
gb.failedTimes = 0
|
||||
return
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ type LoadBalance struct {
|
||||
strategyFn strategyFn
|
||||
testUrl string
|
||||
expectedStatus string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
var errStrategy = errors.New("unsupported strategy")
|
||||
@@ -234,8 +232,8 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
|
||||
"all": all,
|
||||
"testUrl": lb.testUrl,
|
||||
"expectedStatus": lb.expectedStatus,
|
||||
"hidden": lb.Hidden,
|
||||
"icon": lb.Icon,
|
||||
"hidden": lb.Hidden(),
|
||||
"icon": lb.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -267,6 +265,8 @@ func NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, stra
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.LoadBalance,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -278,7 +278,5 @@ func NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, stra
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ type Selector struct {
|
||||
disableUDP bool
|
||||
selected string
|
||||
testUrl string
|
||||
Hidden bool
|
||||
Icon string
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
@@ -68,8 +66,8 @@ func (s *Selector) MarshalJSON() ([]byte, error) {
|
||||
"now": s.Now(),
|
||||
"all": all,
|
||||
"testUrl": url,
|
||||
"hidden": s.Hidden,
|
||||
"icon": s.Icon,
|
||||
"hidden": s.Hidden(),
|
||||
"icon": s.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -121,6 +119,8 @@ func NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Select
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.Selector,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -131,7 +131,5 @@ func NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Select
|
||||
selected: "COMPATIBLE",
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ type URLTest struct {
|
||||
expectedStatus string
|
||||
tolerance uint16
|
||||
disableUDP bool
|
||||
Hidden bool
|
||||
Icon string
|
||||
fastNode C.Proxy
|
||||
fastSingle *singledo.Single[C.Proxy]
|
||||
}
|
||||
@@ -180,8 +178,8 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||
"testUrl": u.testUrl,
|
||||
"expectedStatus": u.expectedStatus,
|
||||
"fixed": u.selected,
|
||||
"hidden": u.Hidden,
|
||||
"icon": u.Icon,
|
||||
"hidden": u.Hidden(),
|
||||
"icon": u.Icon(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,6 +213,8 @@ func NewURLTest(option *GroupCommonOption, providers []P.ProxyProvider, options
|
||||
GroupBase: NewGroupBase(GroupBaseOption{
|
||||
Name: option.Name,
|
||||
Type: C.URLTest,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
Filter: option.Filter,
|
||||
ExcludeFilter: option.ExcludeFilter,
|
||||
ExcludeType: option.ExcludeType,
|
||||
@@ -226,8 +226,6 @@ func NewURLTest(option *GroupCommonOption, providers []P.ProxyProvider, options
|
||||
disableUDP: option.DisableUDP,
|
||||
testUrl: option.URL,
|
||||
expectedStatus: option.ExpectedStatus,
|
||||
Hidden: option.Hidden,
|
||||
Icon: option.Icon,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
|
||||
@@ -16,6 +16,9 @@ type ProxyGroup interface {
|
||||
Now() string
|
||||
Touch()
|
||||
|
||||
Hidden() bool
|
||||
Icon() string
|
||||
|
||||
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ require (
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/http v0.1.0
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604
|
||||
github.com/metacubex/mhurl v0.1.0
|
||||
github.com/metacubex/mlkem v0.1.0
|
||||
github.com/metacubex/quic-go v0.59.1-0.20260213014310-4df8f0de5b56
|
||||
github.com/metacubex/randv2 v0.2.0
|
||||
@@ -43,7 +44,6 @@ require (
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
|
||||
github.com/mroth/weightedrand/v2 v2.1.0
|
||||
github.com/openacid/low v0.1.21
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||
github.com/samber/lo v1.53.0
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/stretchr/testify v1.11.1
|
||||
@@ -104,6 +104,7 @@ require (
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
|
||||
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
|
||||
|
||||
@@ -107,6 +107,8 @@ github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o=
|
||||
github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
|
||||
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
|
||||
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
|
||||
github.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=
|
||||
github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=
|
||||
github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
|
||||
"github.com/metacubex/mhurl"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
@@ -48,7 +49,7 @@ func (c *packet) InAddr() net.Addr {
|
||||
}
|
||||
|
||||
func ParseSSURL(s string) (addr, cipher, password string, err error) {
|
||||
u, err := url.Parse(s)
|
||||
u, err := mhurl.Parse(s) // we need multiple hosts url supports
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseSSURL(t *testing.T) {
|
||||
for _, test := range []struct{ method, passwd, hosts string }{
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: ":1000,:2000,:3000"},
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: "127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000"},
|
||||
{method: "aes-256-gcm", passwd: "password", hosts: "[::1]:1000,[::1]:2000,[::1]:3000"},
|
||||
} {
|
||||
addr, cipher, password, err := ParseSSURL(fmt.Sprintf("ss://%s:%s@%s", test.method, test.passwd, test.hosts))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.hosts, addr)
|
||||
require.Equal(t, test.method, cipher)
|
||||
require.Equal(t, test.passwd, password)
|
||||
}
|
||||
}
|
||||
@@ -506,8 +506,6 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
||||
}
|
||||
}
|
||||
|
||||
//l.openAndroidHotspot(tunOptions)
|
||||
|
||||
if !l.options.AutoDetectInterface {
|
||||
resolver.ResetConnection()
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package sing_tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/metacubex/mihomo/component/process"
|
||||
@@ -13,8 +12,6 @@ import (
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
"github.com/metacubex/sing-tun"
|
||||
"github.com/sagernet/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type packageManagerCallback struct{}
|
||||
@@ -78,25 +75,3 @@ func init() {
|
||||
process.DefaultPackageNameResolver = findPackageName
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) openAndroidHotspot(tunOptions tun.Options) {
|
||||
if runtime.GOOS == "android" && tunOptions.AutoRoute {
|
||||
priority := 9000
|
||||
if len(tunOptions.ExcludedRanges()) > 0 {
|
||||
priority++
|
||||
}
|
||||
if tunOptions.InterfaceMonitor.AndroidVPNEnabled() {
|
||||
priority++
|
||||
}
|
||||
it := netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IifName = tunOptions.Name
|
||||
it.Table = 254 //main
|
||||
it.Family = unix.AF_INET
|
||||
it.SuppressPrefixlen = 0
|
||||
err := netlink.RuleAdd(it)
|
||||
if err != nil {
|
||||
log.Warnln("[TUN] add AndroidHotspot rule error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,3 @@ import (
|
||||
func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error {
|
||||
return nil
|
||||
}
|
||||
func (l *Listener) openAndroidHotspot(tunOptions tun.Options) {}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||
|
||||
"github.com/metacubex/http"
|
||||
"github.com/metacubex/mhurl"
|
||||
vmess "github.com/metacubex/sing-vmess"
|
||||
"github.com/metacubex/sing/common"
|
||||
"github.com/metacubex/sing/common/metadata"
|
||||
@@ -230,7 +230,7 @@ func HandleVmess(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
}
|
||||
|
||||
func ParseVmessURL(s string) (addr, username, password string, err error) {
|
||||
u, err := url.Parse(s)
|
||||
u, err := mhurl.Parse(s) // we need multiple hosts url supports
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package sing_vmess
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseVmessURL(t *testing.T) {
|
||||
for _, test := range []struct{ username, passwd, hosts string }{
|
||||
{username: "username", passwd: "password", hosts: ":1000,:2000,:3000"},
|
||||
{username: "username", passwd: "password", hosts: "127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000"},
|
||||
{username: "username", passwd: "password", hosts: "[::1]:1000,[::1]:2000,[::1]:3000"},
|
||||
} {
|
||||
addr, username, password, err := ParseVmessURL(fmt.Sprintf("vmess://%s:%s@%s", test.username, test.passwd, test.hosts))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.hosts, addr)
|
||||
require.Equal(t, test.username, username)
|
||||
require.Equal(t, test.passwd, password)
|
||||
}
|
||||
}
|
||||
Generated
+3
-3
@@ -1692,7 +1692,7 @@ dependencies = [
|
||||
"windows-registry 0.5.3",
|
||||
"windows-sys 0.60.2",
|
||||
"winreg 0.55.0",
|
||||
"zip 8.2.0",
|
||||
"zip 8.3.0",
|
||||
"zip-extensions",
|
||||
]
|
||||
|
||||
@@ -12802,9 +12802,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "8.2.0"
|
||||
version = "8.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004"
|
||||
checksum = "4a243cfad17427fc077f529da5a95abe4e94fd2bfdb601611870a6557cc67657"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bzip2",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "5.91.2",
|
||||
"@tanstack/react-query": "5.91.3",
|
||||
"@tauri-apps/api": "2.10.1",
|
||||
"ahooks": "3.9.6",
|
||||
"dayjs": "1.11.20",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"country-emoji": "1.5.6",
|
||||
"dayjs": "1.11.20",
|
||||
"framer-motion": "12.38.0",
|
||||
"i18next": "25.8.19",
|
||||
"i18next": "25.8.20",
|
||||
"jotai": "2.18.1",
|
||||
"json-schema": "0.4.0",
|
||||
"material-react-table": "3.2.1",
|
||||
@@ -71,7 +71,7 @@
|
||||
"@emotion/react": "11.14.0",
|
||||
"@iconify/json": "2.2.452",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@tanstack/react-query": "5.91.2",
|
||||
"@tanstack/react-query": "5.91.3",
|
||||
"@tanstack/react-router": "1.167.5",
|
||||
"@tanstack/react-router-devtools": "1.166.9",
|
||||
"@tanstack/router-plugin": "1.166.14",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"mihomo_alpha": "alpha-dd4eb63",
|
||||
"clash_rs": "v0.9.6",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.9.6-alpha+sha.489d5d9"
|
||||
"clash_rs_alpha": "0.9.6-alpha+sha.b17ba0a"
|
||||
},
|
||||
"arch_template": {
|
||||
"mihomo": {
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-rs-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2026-03-18T22:23:38.730Z"
|
||||
"updated_at": "2026-03-19T22:22:44.922Z"
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"prettier-plugin-ember-template-tag": "2.1.3",
|
||||
"prettier-plugin-tailwindcss": "0.7.2",
|
||||
"prettier-plugin-toml": "2.0.6",
|
||||
"stylelint": "17.4.0",
|
||||
"stylelint": "17.5.0",
|
||||
"stylelint-config-html": "1.1.0",
|
||||
"stylelint-config-recess-order": "7.7.0",
|
||||
"stylelint-config-standard": "40.0.0",
|
||||
|
||||
Generated
+107
-82
@@ -95,26 +95,26 @@ importers:
|
||||
specifier: 2.0.6
|
||||
version: 2.0.6(prettier@3.8.1)
|
||||
stylelint:
|
||||
specifier: 17.4.0
|
||||
version: 17.4.0(typescript@5.9.3)
|
||||
specifier: 17.5.0
|
||||
version: 17.5.0(typescript@5.9.3)
|
||||
stylelint-config-html:
|
||||
specifier: 1.1.0
|
||||
version: 1.1.0(postcss-html@1.8.1)(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 1.1.0(postcss-html@1.8.1)(stylelint@17.5.0(typescript@5.9.3))
|
||||
stylelint-config-recess-order:
|
||||
specifier: 7.7.0
|
||||
version: 7.7.0(stylelint-order@8.1.1(stylelint@17.4.0(typescript@5.9.3)))(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 7.7.0(stylelint-order@8.1.1(stylelint@17.5.0(typescript@5.9.3)))(stylelint@17.5.0(typescript@5.9.3))
|
||||
stylelint-config-standard:
|
||||
specifier: 40.0.0
|
||||
version: 40.0.0(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 40.0.0(stylelint@17.5.0(typescript@5.9.3))
|
||||
stylelint-declaration-block-no-ignored-properties:
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 3.0.0(stylelint@17.5.0(typescript@5.9.3))
|
||||
stylelint-order:
|
||||
specifier: 8.1.1
|
||||
version: 8.1.1(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 8.1.1(stylelint@17.5.0(typescript@5.9.3))
|
||||
stylelint-scss:
|
||||
specifier: 7.0.0
|
||||
version: 7.0.0(stylelint@17.4.0(typescript@5.9.3))
|
||||
version: 7.0.0(stylelint@17.5.0(typescript@5.9.3))
|
||||
tailwindcss:
|
||||
specifier: 4.2.2
|
||||
version: 4.2.2
|
||||
@@ -128,8 +128,8 @@ importers:
|
||||
frontend/interface:
|
||||
dependencies:
|
||||
'@tanstack/react-query':
|
||||
specifier: 5.91.2
|
||||
version: 5.91.2(react@19.2.4)
|
||||
specifier: 5.91.3
|
||||
version: 5.91.3(react@19.2.4)
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.10.1
|
||||
version: 2.10.1
|
||||
@@ -261,8 +261,8 @@ importers:
|
||||
specifier: 12.38.0
|
||||
version: 12.38.0(@emotion/is-prop-valid@1.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
i18next:
|
||||
specifier: 25.8.19
|
||||
version: 25.8.19(typescript@5.9.3)
|
||||
specifier: 25.8.20
|
||||
version: 25.8.20(typescript@5.9.3)
|
||||
jotai:
|
||||
specifier: 2.18.1
|
||||
version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.14)(react@19.2.4)
|
||||
@@ -301,7 +301,7 @@ importers:
|
||||
version: 8.2.0(e309558cb1df3652b39c2e76ceb9cee4)
|
||||
react-i18next:
|
||||
specifier: 15.7.4
|
||||
version: 15.7.4(i18next@25.8.19(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||
version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||
react-markdown:
|
||||
specifier: 10.1.0
|
||||
version: 10.1.0(@types/react@19.2.14)(react@19.2.4)
|
||||
@@ -340,8 +340,8 @@ importers:
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
'@tanstack/react-query':
|
||||
specifier: 5.91.2
|
||||
version: 5.91.2(react@19.2.4)
|
||||
specifier: 5.91.3
|
||||
version: 5.91.3(react@19.2.4)
|
||||
'@tanstack/react-router':
|
||||
specifier: 1.167.5
|
||||
version: 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -500,7 +500,7 @@ importers:
|
||||
version: 6.0.0(react@19.2.4)
|
||||
react-i18next:
|
||||
specifier: 15.7.4
|
||||
version: 15.7.4(i18next@25.8.19(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||
version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||
react-use:
|
||||
specifier: 17.6.0
|
||||
version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -594,14 +594,14 @@ importers:
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1
|
||||
tar:
|
||||
specifier: 7.5.11
|
||||
version: 7.5.11
|
||||
specifier: 7.5.12
|
||||
version: 7.5.12
|
||||
telegram:
|
||||
specifier: 2.26.22
|
||||
version: 2.26.22
|
||||
undici:
|
||||
specifier: 7.24.4
|
||||
version: 7.24.4
|
||||
specifier: 7.24.5
|
||||
version: 7.24.5
|
||||
yargs:
|
||||
specifier: 18.0.0
|
||||
version: 18.0.0
|
||||
@@ -3852,8 +3852,8 @@ packages:
|
||||
'@tanstack/query-core@5.91.2':
|
||||
resolution: {integrity: sha512-Uz2pTgPC1mhqrrSGg18RKCWT/pkduAYtxbcyIyKBhw7dTWjXZIzqmpzO2lBkyWr4hlImQgpu1m1pei3UnkFRWw==}
|
||||
|
||||
'@tanstack/react-query@5.91.2':
|
||||
resolution: {integrity: sha512-GClLPzbM57iFXv+FlvOUL56XVe00PxuTaVEyj1zAObhRiKF008J5vedmaq7O6ehs+VmPHe8+PUQhMuEyv8d9wQ==}
|
||||
'@tanstack/react-query@5.91.3':
|
||||
resolution: {integrity: sha512-D8jsCexxS5crZxAeiH6VlLHOUzmHOxeW5c11y8rZu0c34u/cy18hUKQXA/gn1Ila3ZIFzP+Pzv76YnliC0EtZQ==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
@@ -4575,6 +4575,10 @@ packages:
|
||||
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-regex@6.2.2:
|
||||
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4959,15 +4963,6 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
cosmiconfig@9.0.0:
|
||||
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
typescript: '>=4.9.5'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
cosmiconfig@9.0.1:
|
||||
resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -5010,6 +5005,10 @@ packages:
|
||||
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||
|
||||
css-tree@3.2.1:
|
||||
resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
|
||||
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
|
||||
|
||||
css-what@6.1.0:
|
||||
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -5528,6 +5527,10 @@ packages:
|
||||
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-east-asian-width@1.5.0:
|
||||
resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-nonce@1.0.1:
|
||||
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -5568,8 +5571,8 @@ packages:
|
||||
resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
globby@16.1.0:
|
||||
resolution: {integrity: sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==}
|
||||
globby@16.1.1:
|
||||
resolution: {integrity: sha512-dW7vl+yiAJSp6aCekaVnVJxurRv7DCOLyXqEG3RYMYUg7AuJ2jCqPkZTA8ooqC2vtnkaMcV5WfFBMuEnTu1OQg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
globjoin@0.1.4:
|
||||
@@ -5661,8 +5664,8 @@ packages:
|
||||
hyphenate-style-name@1.1.0:
|
||||
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
|
||||
|
||||
i18next@25.8.19:
|
||||
resolution: {integrity: sha512-Szzho7sXdv9Do6TvZIyYu+H+94xCrh60cq01DKcYH/gDjpcHimrQApUEQycxw37U7guwbTEJ07CWScGGbgs96w==}
|
||||
i18next@25.8.20:
|
||||
resolution: {integrity: sha512-xjo9+lbX/P1tQt3xpO2rfJiBppNfUnNIPKgCvNsTKsvTOCro1Qr/geXVg1N47j5ScOSaXAPq8ET93raK3Rr06A==}
|
||||
peerDependencies:
|
||||
typescript: ^5
|
||||
peerDependenciesMeta:
|
||||
@@ -6169,6 +6172,9 @@ packages:
|
||||
mdn-data@2.25.0:
|
||||
resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==}
|
||||
|
||||
mdn-data@2.27.1:
|
||||
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
|
||||
|
||||
memorystream@0.3.1:
|
||||
resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
@@ -6177,8 +6183,8 @@ packages:
|
||||
resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
meow@14.0.0:
|
||||
resolution: {integrity: sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA==}
|
||||
meow@14.1.0:
|
||||
resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
merge2@1.4.1:
|
||||
@@ -7339,6 +7345,10 @@ packages:
|
||||
resolution: {integrity: sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
string-width@8.2.0:
|
||||
resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
|
||||
|
||||
@@ -7350,6 +7360,10 @@ packages:
|
||||
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-bom@3.0.0:
|
||||
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -7411,8 +7425,8 @@ packages:
|
||||
peerDependencies:
|
||||
stylelint: ^16.8.2 || ^17.0.0
|
||||
|
||||
stylelint@17.4.0:
|
||||
resolution: {integrity: sha512-3kQ2/cHv3Zt8OBg+h2B8XCx9evEABQIrv4hh3uXahGz/ZEHrTR80zxBiK2NfXNaSoyBzxO1pjsz1Vhdzwn5XSw==}
|
||||
stylelint@17.5.0:
|
||||
resolution: {integrity: sha512-o/NS6zhsPZFmgUm5tXX4pVNg1XDOZSlucLdf2qow/lVn4JIyzZIQ5b3kad1ugqUj3GSIgr2u5lQw7X8rjqw33g==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -7482,8 +7496,8 @@ packages:
|
||||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tar@7.5.11:
|
||||
resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==}
|
||||
tar@7.5.12:
|
||||
resolution: {integrity: sha512-9TsuLcdhOn4XztcQqhNyq1KOwOOED/3k58JAvtULiYqbO8B/0IBAAIE1hj0Svmm58k27TmcigyDI0deMlgG3uw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
telegram@2.26.22:
|
||||
@@ -7612,8 +7626,8 @@ packages:
|
||||
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
undici@7.24.4:
|
||||
resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==}
|
||||
undici@7.24.5:
|
||||
resolution: {integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||
@@ -11362,7 +11376,7 @@ snapshots:
|
||||
|
||||
'@tanstack/query-core@5.91.2': {}
|
||||
|
||||
'@tanstack/react-query@5.91.2(react@19.2.4)':
|
||||
'@tanstack/react-query@5.91.3(react@19.2.4)':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.91.2
|
||||
react: 19.2.4
|
||||
@@ -12201,6 +12215,8 @@ snapshots:
|
||||
|
||||
ansi-regex@6.0.1: {}
|
||||
|
||||
ansi-regex@6.2.2: {}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
@@ -12565,15 +12581,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
cosmiconfig@9.0.0(typescript@5.9.3):
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.1
|
||||
parse-json: 5.2.0
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
cosmiconfig@9.0.1(typescript@5.9.3):
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
@@ -12622,6 +12629,11 @@ snapshots:
|
||||
mdn-data: 2.12.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
css-tree@3.2.1:
|
||||
dependencies:
|
||||
mdn-data: 2.27.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
css-what@6.1.0: {}
|
||||
|
||||
cssesc@3.0.0: {}
|
||||
@@ -13135,6 +13147,8 @@ snapshots:
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
|
||||
get-east-asian-width@1.5.0: {}
|
||||
|
||||
get-nonce@1.0.1: {}
|
||||
|
||||
get-tsconfig@4.10.1:
|
||||
@@ -13182,7 +13196,7 @@ snapshots:
|
||||
|
||||
globals@17.4.0: {}
|
||||
|
||||
globby@16.1.0:
|
||||
globby@16.1.1:
|
||||
dependencies:
|
||||
'@sindresorhus/merge-streams': 4.0.0
|
||||
fast-glob: 3.3.3
|
||||
@@ -13301,7 +13315,7 @@ snapshots:
|
||||
|
||||
hyphenate-style-name@1.1.0: {}
|
||||
|
||||
i18next@25.8.19(typescript@5.9.3):
|
||||
i18next@25.8.20(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
optionalDependencies:
|
||||
@@ -13787,11 +13801,13 @@ snapshots:
|
||||
|
||||
mdn-data@2.25.0: {}
|
||||
|
||||
mdn-data@2.27.1: {}
|
||||
|
||||
memorystream@0.3.1: {}
|
||||
|
||||
meow@13.2.0: {}
|
||||
|
||||
meow@14.0.0: {}
|
||||
meow@14.1.0: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
@@ -14530,11 +14546,11 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
react-i18next@15.7.4(i18next@25.8.19(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
||||
react-i18next@15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.3
|
||||
html-parse-stringify: 3.0.1
|
||||
i18next: 25.8.19(typescript@5.9.3)
|
||||
i18next: 25.8.20(typescript@5.9.3)
|
||||
react: 19.2.4
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
@@ -15060,6 +15076,11 @@ snapshots:
|
||||
get-east-asian-width: 1.4.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string-width@8.2.0:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.5.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
dependencies:
|
||||
character-entities-html4: 2.1.0
|
||||
@@ -15073,6 +15094,10 @@ snapshots:
|
||||
dependencies:
|
||||
ansi-regex: 6.0.1
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
dependencies:
|
||||
ansi-regex: 6.2.2
|
||||
|
||||
strip-bom@3.0.0: {}
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
@@ -15087,36 +15112,36 @@ snapshots:
|
||||
dependencies:
|
||||
inline-style-parser: 0.2.3
|
||||
|
||||
stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
postcss-html: 1.8.1
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
|
||||
stylelint-config-recess-order@7.7.0(stylelint-order@8.1.1(stylelint@17.4.0(typescript@5.9.3)))(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-config-recess-order@7.7.0(stylelint-order@8.1.1(stylelint@17.5.0(typescript@5.9.3)))(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint-order: 8.1.1(stylelint@17.4.0(typescript@5.9.3))
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
stylelint-order: 8.1.1(stylelint@17.5.0(typescript@5.9.3))
|
||||
|
||||
stylelint-config-recommended@18.0.0(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-config-recommended@18.0.0(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
|
||||
stylelint-config-standard@40.0.0(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-config-standard@40.0.0(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint-config-recommended: 18.0.0(stylelint@17.4.0(typescript@5.9.3))
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
stylelint-config-recommended: 18.0.0(stylelint@17.5.0(typescript@5.9.3))
|
||||
|
||||
stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
|
||||
stylelint-order@8.1.1(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-order@8.1.1(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
postcss: 8.5.8
|
||||
postcss-sorting: 10.0.0(postcss@8.5.8)
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
|
||||
stylelint-scss@7.0.0(stylelint@17.4.0(typescript@5.9.3)):
|
||||
stylelint-scss@7.0.0(stylelint@17.5.0(typescript@5.9.3)):
|
||||
dependencies:
|
||||
css-tree: 3.1.0
|
||||
is-plain-object: 5.0.0
|
||||
@@ -15126,9 +15151,9 @@ snapshots:
|
||||
postcss-resolve-nested-selector: 0.1.6
|
||||
postcss-selector-parser: 7.1.1
|
||||
postcss-value-parser: 4.2.0
|
||||
stylelint: 17.4.0(typescript@5.9.3)
|
||||
stylelint: 17.5.0(typescript@5.9.3)
|
||||
|
||||
stylelint@17.4.0(typescript@5.9.3):
|
||||
stylelint@17.5.0(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
|
||||
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
|
||||
@@ -15138,15 +15163,15 @@ snapshots:
|
||||
'@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1)
|
||||
'@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1)
|
||||
colord: 2.9.3
|
||||
cosmiconfig: 9.0.0(typescript@5.9.3)
|
||||
cosmiconfig: 9.0.1(typescript@5.9.3)
|
||||
css-functions-list: 3.3.3
|
||||
css-tree: 3.1.0
|
||||
css-tree: 3.2.1
|
||||
debug: 4.4.3
|
||||
fast-glob: 3.3.3
|
||||
fastest-levenshtein: 1.0.16
|
||||
file-entry-cache: 11.1.2
|
||||
global-modules: 2.0.0
|
||||
globby: 16.1.0
|
||||
globby: 16.1.1
|
||||
globjoin: 0.1.4
|
||||
html-tags: 5.1.0
|
||||
ignore: 7.0.5
|
||||
@@ -15154,7 +15179,7 @@ snapshots:
|
||||
imurmurhash: 0.1.4
|
||||
is-plain-object: 5.0.0
|
||||
mathml-tag-names: 4.0.0
|
||||
meow: 14.0.0
|
||||
meow: 14.1.0
|
||||
micromatch: 4.0.8
|
||||
normalize-path: 3.0.0
|
||||
picocolors: 1.1.1
|
||||
@@ -15162,7 +15187,7 @@ snapshots:
|
||||
postcss-safe-parser: 7.0.1(postcss@8.5.8)
|
||||
postcss-selector-parser: 7.1.1
|
||||
postcss-value-parser: 4.2.0
|
||||
string-width: 8.1.1
|
||||
string-width: 8.2.0
|
||||
supports-hyperlinks: 4.4.0
|
||||
svg-tags: 1.0.0
|
||||
table: 6.9.0
|
||||
@@ -15235,7 +15260,7 @@ snapshots:
|
||||
|
||||
tapable@2.3.0: {}
|
||||
|
||||
tar@7.5.11:
|
||||
tar@7.5.12:
|
||||
dependencies:
|
||||
'@isaacs/fs-minipass': 4.0.1
|
||||
chownr: 3.0.0
|
||||
@@ -15375,7 +15400,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.1
|
||||
|
||||
undici@7.24.4: {}
|
||||
undici@7.24.5: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
"fs-extra": "11.3.4",
|
||||
"octokit": "5.0.5",
|
||||
"picocolors": "1.1.1",
|
||||
"tar": "7.5.11",
|
||||
"tar": "7.5.12",
|
||||
"telegram": "2.26.22",
|
||||
"undici": "7.24.4",
|
||||
"undici": "7.24.5",
|
||||
"yargs": "18.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,7 @@ type serverConfigMasqueradeFile struct {
|
||||
type serverConfigMasqueradeProxy struct {
|
||||
URL string `mapstructure:"url"`
|
||||
RewriteHost bool `mapstructure:"rewriteHost"`
|
||||
XForwarded bool `mapstructure:"xForwarded"`
|
||||
Insecure bool `mapstructure:"insecure"`
|
||||
}
|
||||
|
||||
@@ -852,6 +853,9 @@ func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error {
|
||||
if !c.Masquerade.Proxy.RewriteHost {
|
||||
r.Out.Host = r.In.Host
|
||||
}
|
||||
if c.Masquerade.Proxy.XForwarded {
|
||||
r.SetXForwarded()
|
||||
}
|
||||
},
|
||||
Transport: transport,
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
|
||||
@@ -172,6 +172,7 @@ func TestServerConfig(t *testing.T) {
|
||||
Proxy: serverConfigMasqueradeProxy{
|
||||
URL: "https://some.site.net",
|
||||
RewriteHost: true,
|
||||
XForwarded: true,
|
||||
Insecure: true,
|
||||
},
|
||||
String: serverConfigMasqueradeString{
|
||||
|
||||
@@ -133,6 +133,7 @@ masquerade:
|
||||
proxy:
|
||||
url: https://some.site.net
|
||||
rewriteHost: true
|
||||
xForwarded: true
|
||||
insecure: true
|
||||
string:
|
||||
content: aint nothin here
|
||||
|
||||
@@ -10,15 +10,24 @@ include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=mac80211
|
||||
|
||||
ifdef CONFIG_LINUX_6_12
|
||||
PKG_VERSION:=6.18.7
|
||||
PKG_HASH:=623e5cf46ca8e81fd413f4f465e2580a0143e24929f9c22ce1ba7c34f2872989
|
||||
PKG_SOURCE_URL:=https://github.com/openwrt/backports/releases/download/backports-v$(PKG_VERSION)
|
||||
PKG_SOURCE:=backports-$(PKG_VERSION).tar.zst
|
||||
PATCH_DIR:=./patches-6.18
|
||||
else
|
||||
PKG_VERSION:=6.6.15
|
||||
PKG_HASH:=3bbc461121134fda9089c084a5eed577d05e7837a157edf9a3797937172a3ece
|
||||
PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/
|
||||
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
|
||||
PATCH_DIR:=./patches
|
||||
endif
|
||||
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/
|
||||
PKG_HASH:=3bbc461121134fda9089c084a5eed577d05e7837a157edf9a3797937172a3ece
|
||||
|
||||
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
|
||||
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(if $(BUILD_VARIANT),$(PKG_NAME)-$(BUILD_VARIANT)/)backports-$(PKG_VERSION)
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
@@ -333,9 +342,7 @@ endif
|
||||
ifeq ($(strip $(CONFIG_EXTERNAL_KERNEL_TREE)),"")
|
||||
ifeq ($(strip $(CONFIG_KERNEL_GIT_CLONE_URI)),"")
|
||||
define Build/Configure
|
||||
cmp $(PKG_BUILD_DIR)/include/linux/ath9k_platform.h $(LINUX_DIR)/include/linux/ath9k_platform.h
|
||||
cmp $(PKG_BUILD_DIR)/include/linux/ath5k_platform.h $(LINUX_DIR)/include/linux/ath5k_platform.h
|
||||
cmp $(PKG_BUILD_DIR)/include/linux/rt2x00_platform.h $(LINUX_DIR)/include/linux/rt2x00_platform.h
|
||||
endef
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/drivers/net/wireless/ath/Kconfig
|
||||
+++ b/drivers/net/wireless/ath/Kconfig
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: ISC
|
||||
config ATH_COMMON
|
||||
- tristate
|
||||
+ tristate "ath.ko"
|
||||
depends on m
|
||||
|
||||
config WLAN_VENDOR_ATH
|
||||
@@ -0,0 +1,31 @@
|
||||
--- a/drivers/net/wireless/ath/Makefile
|
||||
+++ b/drivers/net/wireless/ath/Makefile
|
||||
@@ -16,10 +16,10 @@ ath-objs := main.o \
|
||||
regd.o \
|
||||
hw.o \
|
||||
key.o \
|
||||
+ debug.o \
|
||||
dfs_pattern_detector.o \
|
||||
dfs_pri_detector.o
|
||||
|
||||
-ath-$(CPTCFG_ATH_DEBUG) += debug.o
|
||||
ath-$(CPTCFG_ATH_TRACEPOINTS) += trace.o
|
||||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
--- a/drivers/net/wireless/ath/ath.h
|
||||
+++ b/drivers/net/wireless/ath/ath.h
|
||||
@@ -321,14 +321,7 @@ void _ath_dbg(struct ath_common *common,
|
||||
#endif /* CPTCFG_ATH_DEBUG */
|
||||
|
||||
/** Returns string describing opmode, or NULL if unknown mode. */
|
||||
-#ifdef CPTCFG_ATH_DEBUG
|
||||
const char *ath_opmode_to_string(enum nl80211_iftype opmode);
|
||||
-#else
|
||||
-static inline const char *ath_opmode_to_string(enum nl80211_iftype opmode)
|
||||
-{
|
||||
- return "UNKNOWN";
|
||||
-}
|
||||
-#endif
|
||||
|
||||
extern const char *ath_bus_type_strings[];
|
||||
static inline const char *ath_bus_type_to_string(enum ath_bus_type bustype)
|
||||
@@ -0,0 +1,92 @@
|
||||
--- a/drivers/net/wireless/ath/regd.c
|
||||
+++ b/drivers/net/wireless/ath/regd.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "regd_common.h"
|
||||
|
||||
static int __ath_regd_init(struct ath_regulatory *reg);
|
||||
+static struct reg_dmn_pair_mapping *ath_get_regpair(int regdmn);
|
||||
|
||||
/*
|
||||
* This is a set of common rules used by our world regulatory domains.
|
||||
@@ -116,6 +117,9 @@ static const struct ieee80211_regdomain
|
||||
|
||||
static bool dynamic_country_user_possible(struct ath_regulatory *reg)
|
||||
{
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return true;
|
||||
+
|
||||
if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
|
||||
return true;
|
||||
|
||||
@@ -188,6 +192,8 @@ static bool dynamic_country_user_possibl
|
||||
|
||||
static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
|
||||
{
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return true;
|
||||
if (!IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_REG_HINTS))
|
||||
return false;
|
||||
if (!dynamic_country_user_possible(reg))
|
||||
@@ -345,6 +351,9 @@ ath_reg_apply_beaconing_flags(struct wip
|
||||
struct ieee80211_channel *ch;
|
||||
unsigned int i;
|
||||
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return;
|
||||
+
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
if (!wiphy->bands[band])
|
||||
continue;
|
||||
@@ -379,6 +388,9 @@ ath_reg_apply_ir_flags(struct wiphy *wip
|
||||
{
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return;
|
||||
+
|
||||
sband = wiphy->bands[NL80211_BAND_2GHZ];
|
||||
if (!sband)
|
||||
return;
|
||||
@@ -408,6 +420,9 @@ static void ath_reg_apply_radar_flags(st
|
||||
struct ieee80211_channel *ch;
|
||||
unsigned int i;
|
||||
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return;
|
||||
+
|
||||
if (!wiphy->bands[NL80211_BAND_5GHZ])
|
||||
return;
|
||||
|
||||
@@ -640,6 +655,10 @@ ath_regd_init_wiphy(struct ath_regulator
|
||||
const struct ieee80211_regdomain *regd;
|
||||
|
||||
wiphy->reg_notifier = reg_notifier;
|
||||
+
|
||||
+ if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
+ return 0;
|
||||
+
|
||||
wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
|
||||
REGULATORY_CUSTOM_REG;
|
||||
|
||||
--- a/drivers/net/wireless/ath/Kconfig
|
||||
+++ b/drivers/net/wireless/ath/Kconfig
|
||||
@@ -24,6 +24,9 @@ config WLAN_VENDOR_ATH
|
||||
|
||||
if WLAN_VENDOR_ATH
|
||||
|
||||
+config ATH_USER_REGD
|
||||
+ bool "Do not enforce EEPROM regulatory restrictions"
|
||||
+
|
||||
config ATH_DEBUG
|
||||
bool "Atheros wireless debugging"
|
||||
help
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -91,6 +91,7 @@ ADM8211=
|
||||
ATH_COMMON=
|
||||
WLAN_VENDOR_ATH=
|
||||
ATH_DEBUG=
|
||||
+ATH_USER_REGD=
|
||||
ATH_TRACEPOINTS=
|
||||
ATH_REG_DYNAMIC_USER_REG_HINTS=
|
||||
ATH_REG_DYNAMIC_USER_CERT_TESTING=
|
||||
@@ -0,0 +1,84 @@
|
||||
--- a/drivers/net/wireless/ath/regd.c
|
||||
+++ b/drivers/net/wireless/ath/regd.c
|
||||
@@ -44,7 +44,8 @@ static struct reg_dmn_pair_mapping *ath_
|
||||
NL80211_RRF_NO_OFDM)
|
||||
|
||||
/* We allow IBSS on these on a case by case basis by regulatory domain */
|
||||
-#define ATH_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\
|
||||
+#define ATH_5GHZ_5150_5350 REG_RULE(5150-10, 5240+10, 80, 0, 30, 0),\
|
||||
+ REG_RULE(5260-10, 5350+10, 80, 0, 30,\
|
||||
NL80211_RRF_NO_IR)
|
||||
#define ATH_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\
|
||||
NL80211_RRF_NO_IR)
|
||||
@@ -62,57 +63,56 @@ static struct reg_dmn_pair_mapping *ath_
|
||||
#define ATH_5GHZ_NO_MIDBAND ATH_5GHZ_5150_5350, \
|
||||
ATH_5GHZ_5725_5850
|
||||
|
||||
+#define REGD_RULES(...) \
|
||||
+ .reg_rules = { __VA_ARGS__ }, \
|
||||
+ .n_reg_rules = ARRAY_SIZE(((struct ieee80211_reg_rule[]) { __VA_ARGS__ }))
|
||||
+
|
||||
/* Can be used for:
|
||||
* 0x60, 0x61, 0x62 */
|
||||
static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
|
||||
- .n_reg_rules = 5,
|
||||
.alpha2 = "99",
|
||||
- .reg_rules = {
|
||||
+ REGD_RULES(
|
||||
ATH_2GHZ_ALL,
|
||||
ATH_5GHZ_ALL,
|
||||
- }
|
||||
+ )
|
||||
};
|
||||
|
||||
/* Can be used by 0x63 and 0x65 */
|
||||
static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
|
||||
- .n_reg_rules = 4,
|
||||
.alpha2 = "99",
|
||||
- .reg_rules = {
|
||||
+ REGD_RULES(
|
||||
ATH_2GHZ_CH01_11,
|
||||
ATH_2GHZ_CH12_13,
|
||||
ATH_5GHZ_NO_MIDBAND,
|
||||
- }
|
||||
+ )
|
||||
};
|
||||
|
||||
/* Can be used by 0x64 only */
|
||||
static const struct ieee80211_regdomain ath_world_regdom_64 = {
|
||||
- .n_reg_rules = 3,
|
||||
.alpha2 = "99",
|
||||
- .reg_rules = {
|
||||
+ REGD_RULES(
|
||||
ATH_2GHZ_CH01_11,
|
||||
ATH_5GHZ_NO_MIDBAND,
|
||||
- }
|
||||
+ )
|
||||
};
|
||||
|
||||
/* Can be used by 0x66 and 0x69 */
|
||||
static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
|
||||
- .n_reg_rules = 3,
|
||||
.alpha2 = "99",
|
||||
- .reg_rules = {
|
||||
+ REGD_RULES(
|
||||
ATH_2GHZ_CH01_11,
|
||||
ATH_5GHZ_ALL,
|
||||
- }
|
||||
+ )
|
||||
};
|
||||
|
||||
/* Can be used by 0x67, 0x68, 0x6A and 0x6C */
|
||||
static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
|
||||
- .n_reg_rules = 4,
|
||||
.alpha2 = "99",
|
||||
- .reg_rules = {
|
||||
+ REGD_RULES(
|
||||
ATH_2GHZ_CH01_11,
|
||||
ATH_2GHZ_CH12_13,
|
||||
ATH_5GHZ_ALL,
|
||||
- }
|
||||
+ )
|
||||
};
|
||||
|
||||
static bool dynamic_country_user_possible(struct ath_regulatory *reg)
|
||||
@@ -0,0 +1,19 @@
|
||||
--- a/net/wireless/reg.c
|
||||
+++ b/net/wireless/reg.c
|
||||
@@ -3335,6 +3335,8 @@ void regulatory_hint_country_ie(struct w
|
||||
enum environment_cap env = ENVIRON_ANY;
|
||||
struct regulatory_request *request = NULL, *lr;
|
||||
|
||||
+ return;
|
||||
+
|
||||
/* IE len must be evenly divisible by 2 */
|
||||
if (country_ie_len & 0x01)
|
||||
return;
|
||||
@@ -3584,6 +3586,7 @@ static bool is_wiphy_all_set_reg_flag(en
|
||||
|
||||
void regulatory_hint_disconnect(void)
|
||||
{
|
||||
+ return;
|
||||
/* Restore of regulatory settings is not required when wiphy(s)
|
||||
* ignore IE from connected access point but clearance of beacon hints
|
||||
* is required when wiphy(s) supports beacon hints.
|
||||
@@ -0,0 +1,26 @@
|
||||
--- a/drivers/net/wireless/ath/regd_common.h
|
||||
+++ b/drivers/net/wireless/ath/regd_common.h
|
||||
@@ -32,6 +32,7 @@ enum EnumRd {
|
||||
FCC2_WORLD = 0x21,
|
||||
FCC2_ETSIC = 0x22,
|
||||
FCC6_WORLD = 0x23,
|
||||
+ FCC3_FCCA_2 = 0x2A,
|
||||
FRANCE_RES = 0x31,
|
||||
FCC3_FCCA = 0x3A,
|
||||
FCC3_WORLD = 0x3B,
|
||||
@@ -173,6 +174,7 @@ static struct reg_dmn_pair_mapping regDo
|
||||
{FCC2_WORLD, CTL_FCC, CTL_ETSI},
|
||||
{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
|
||||
{FCC3_FCCA, CTL_FCC, CTL_FCC},
|
||||
+ {FCC3_FCCA_2, CTL_FCC, CTL_FCC},
|
||||
{FCC3_WORLD, CTL_FCC, CTL_ETSI},
|
||||
{FCC3_ETSIC, CTL_FCC, CTL_ETSI},
|
||||
{FCC4_FCCA, CTL_FCC, CTL_FCC},
|
||||
@@ -486,6 +488,7 @@ static struct country_code_to_enum_rd al
|
||||
{CTRY_UAE, NULL1_WORLD, "AE"},
|
||||
{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
|
||||
{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
|
||||
+ {CTRY_UNITED_STATES, FCC3_FCCA_2, "US"},
|
||||
{CTRY_UNITED_STATES2, FCC3_FCCA, "US"},
|
||||
{CTRY_UNITED_STATES3, FCC3_FCCA, "US"},
|
||||
/* This "PS" is for US public safety actually... to support this we
|
||||
@@ -0,0 +1,51 @@
|
||||
--- a/drivers/net/wireless/ath/regd.c
|
||||
+++ b/drivers/net/wireless/ath/regd.c
|
||||
@@ -115,6 +115,16 @@ static const struct ieee80211_regdomain
|
||||
)
|
||||
};
|
||||
|
||||
+static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
|
||||
+{
|
||||
+ return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
|
||||
+}
|
||||
+
|
||||
+static bool is_default_regd(struct ath_regulatory *reg)
|
||||
+{
|
||||
+ return ath_regd_get_eepromRD(reg) == CTRY_DEFAULT;
|
||||
+}
|
||||
+
|
||||
static bool dynamic_country_user_possible(struct ath_regulatory *reg)
|
||||
{
|
||||
if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
@@ -123,6 +133,9 @@ static bool dynamic_country_user_possibl
|
||||
if (IS_ENABLED(CPTCFG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
|
||||
return true;
|
||||
|
||||
+ if (is_default_regd(reg))
|
||||
+ return true;
|
||||
+
|
||||
switch (reg->country_code) {
|
||||
case CTRY_UNITED_STATES:
|
||||
case CTRY_JAPAN1:
|
||||
@@ -208,11 +221,6 @@ static inline bool is_wwr_sku(u16 regd)
|
||||
(regd == WORLD));
|
||||
}
|
||||
|
||||
-static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
|
||||
-{
|
||||
- return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
|
||||
-}
|
||||
-
|
||||
bool ath_is_world_regd(struct ath_regulatory *reg)
|
||||
{
|
||||
return is_wwr_sku(ath_regd_get_eepromRD(reg));
|
||||
@@ -659,6 +667,9 @@ ath_regd_init_wiphy(struct ath_regulator
|
||||
if (IS_ENABLED(CPTCFG_ATH_USER_REGD))
|
||||
return 0;
|
||||
|
||||
+ if (is_default_regd(reg))
|
||||
+ return 0;
|
||||
+
|
||||
wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
|
||||
REGULATORY_CUSTOM_REG;
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
--- a/drivers/net/wireless/ath/ath5k/pci.c
|
||||
+++ b/drivers/net/wireless/ath/ath5k/pci.c
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/ath5k_platform.h>
|
||||
#include "../ath.h"
|
||||
#include "ath5k.h"
|
||||
#include "debug.h"
|
||||
@@ -73,7 +74,7 @@ static void ath5k_pci_read_cachesize(str
|
||||
}
|
||||
|
||||
/*
|
||||
- * Read from eeprom
|
||||
+ * Read from eeprom or platform_data
|
||||
*/
|
||||
static bool
|
||||
ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
|
||||
@@ -81,6 +82,19 @@ ath5k_pci_eeprom_read(struct ath_common
|
||||
struct ath5k_hw *ah = common->ah;
|
||||
u32 status, timeout;
|
||||
|
||||
+ struct ath5k_platform_data *pdata = NULL;
|
||||
+
|
||||
+ if (ah->pdev)
|
||||
+ pdata = ah->pdev->dev.platform_data;
|
||||
+
|
||||
+ if (pdata && pdata->eeprom_data && pdata->eeprom_data[61] == AR5K_EEPROM_MAGIC_VALUE) {
|
||||
+ if (offset >= ATH5K_PLAT_EEP_MAX_WORDS)
|
||||
+ return false;
|
||||
+
|
||||
+ *data = pdata->eeprom_data[offset];
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Initialize EEPROM access
|
||||
*/
|
||||
@@ -124,6 +138,16 @@ static int ath5k_pci_eeprom_read_mac(str
|
||||
u16 data;
|
||||
int octet;
|
||||
|
||||
+ struct ath5k_platform_data *pdata = NULL;
|
||||
+
|
||||
+ if (ah->pdev)
|
||||
+ pdata = ah->pdev->dev.platform_data;
|
||||
+
|
||||
+ if (pdata && pdata->macaddr) {
|
||||
+ memcpy(mac, pdata->macaddr, ETH_ALEN);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
AR5K_EEPROM_READ(0x20, data);
|
||||
|
||||
for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
|
||||
@@ -0,0 +1,47 @@
|
||||
--- a/drivers/net/wireless/ath/ath10k/Kconfig
|
||||
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
|
||||
@@ -94,6 +94,12 @@ config ATH10K_TRACING
|
||||
help
|
||||
Select this to ath10k use tracing infrastructure.
|
||||
|
||||
+config ATH10K_THERMAL
|
||||
+ bool "Atheros ath10k thermal monitoring support"
|
||||
+ depends on THERMAL
|
||||
+ ---help---
|
||||
+ Select this to ath10k use hwmon for thermal measurement.
|
||||
+
|
||||
config ATH10K_DFS_CERTIFIED
|
||||
bool "Atheros DFS support for certified platforms"
|
||||
depends on ATH10K && CFG80211_CERTIFICATION_ONUS
|
||||
--- a/drivers/net/wireless/ath/ath10k/Makefile
|
||||
+++ b/drivers/net/wireless/ath/ath10k/Makefile
|
||||
@@ -18,7 +18,7 @@ ath10k_core-y += mac.o \
|
||||
ath10k_core-$(CPTCFG_ATH10K_SPECTRAL) += spectral.o
|
||||
ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o
|
||||
ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o
|
||||
-ath10k_core-$(CONFIG_THERMAL) += thermal.o
|
||||
+ath10k_core-$(CPTCFG_ATH10K_THERMAL) += thermal.o
|
||||
ath10k_core-$(CPTCFG_ATH10K_LEDS) += leds.o
|
||||
ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o
|
||||
ath10k_core-$(CONFIG_PM) += wow.o
|
||||
--- a/drivers/net/wireless/ath/ath10k/thermal.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/thermal.h
|
||||
@@ -25,7 +25,7 @@ struct ath10k_thermal {
|
||||
int temperature;
|
||||
};
|
||||
|
||||
-#if IS_REACHABLE(CONFIG_THERMAL)
|
||||
+#if IS_REACHABLE(CPTCFG_ATH10K_THERMAL)
|
||||
int ath10k_thermal_register(struct ath10k *ar);
|
||||
void ath10k_thermal_unregister(struct ath10k *ar);
|
||||
void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -151,6 +151,7 @@ ATH10K_DEBUG=
|
||||
ATH10K_DEBUGFS=
|
||||
ATH10K_LEDS=
|
||||
ATH10K_SPECTRAL=
|
||||
+ATH10K_THERMAL=
|
||||
ATH10K_TRACING=
|
||||
ATH10K_DFS_CERTIFIED=
|
||||
WCN36XX=
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
From: Sven Eckelmann <sven@open-mesh.com>
|
||||
Date: Tue, 18 Nov 2014 12:29:28 +0100
|
||||
Subject: [PATCH] ath10k: Don't initialize devices asynchronously
|
||||
|
||||
OpenWrt requires all PHYs to be initialized to create the configuration files
|
||||
during bootup. ath10k violates this because it delays the creation of the PHY
|
||||
to a not well defined point in the future.
|
||||
|
||||
Forcing the work to be done immediately works around this problem but may also
|
||||
delay the boot when firmware images cannot be found.
|
||||
|
||||
Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/core.c
|
||||
@@ -3583,6 +3583,16 @@ int ath10k_core_register(struct ath10k *
|
||||
|
||||
queue_work(ar->workqueue, &ar->register_work);
|
||||
|
||||
+ /* OpenWrt requires all PHYs to be initialized to create the
|
||||
+ * configuration files during bootup. ath10k violates this
|
||||
+ * because it delays the creation of the PHY to a not well defined
|
||||
+ * point in the future.
|
||||
+ *
|
||||
+ * Forcing the work to be done immediately works around this problem
|
||||
+ * but may also delay the boot when firmware images cannot be found.
|
||||
+ */
|
||||
+ flush_workqueue(ar->workqueue);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ath10k_core_register);
|
||||
@@ -0,0 +1,37 @@
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -9970,6 +9970,21 @@ static int ath10k_mac_init_rd(struct ath
|
||||
return 0;
|
||||
}
|
||||
|
||||
+#ifdef CPTCFG_MAC80211_LEDS
|
||||
+static const struct ieee80211_tpt_blink ath10k_tpt_blink[] = {
|
||||
+ { .throughput = 0 * 1024, .blink_time = 334 },
|
||||
+ { .throughput = 1 * 1024, .blink_time = 260 },
|
||||
+ { .throughput = 2 * 1024, .blink_time = 220 },
|
||||
+ { .throughput = 5 * 1024, .blink_time = 190 },
|
||||
+ { .throughput = 10 * 1024, .blink_time = 170 },
|
||||
+ { .throughput = 25 * 1024, .blink_time = 150 },
|
||||
+ { .throughput = 54 * 1024, .blink_time = 130 },
|
||||
+ { .throughput = 120 * 1024, .blink_time = 110 },
|
||||
+ { .throughput = 265 * 1024, .blink_time = 80 },
|
||||
+ { .throughput = 586 * 1024, .blink_time = 50 },
|
||||
+};
|
||||
+#endif
|
||||
+
|
||||
int ath10k_mac_register(struct ath10k *ar)
|
||||
{
|
||||
static const u32 cipher_suites[] = {
|
||||
@@ -10332,6 +10347,12 @@ int ath10k_mac_register(struct ath10k *a
|
||||
|
||||
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
|
||||
|
||||
+#ifdef CPTCFG_MAC80211_LEDS
|
||||
+ ieee80211_create_tpt_led_trigger(ar->hw,
|
||||
+ IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
|
||||
+ ARRAY_SIZE(ath10k_tpt_blink));
|
||||
+#endif
|
||||
+
|
||||
ret = ieee80211_register_hw(ar->hw);
|
||||
if (ret) {
|
||||
ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
From 79c9d7aabae1d1da9eea97d83b61e1517a8a2221 Mon Sep 17 00:00:00 2001
|
||||
From: Mathias Kresin <dev@kresin.me>
|
||||
Date: Fri, 22 Jun 2018 18:59:44 +0200
|
||||
Subject: [PATCH] ath10k: use tpt LED trigger by default
|
||||
|
||||
Use the tpt LED trigger for each created phy led. Ths way LEDs attached
|
||||
to the ath10k GPIO pins are indicating the phy status and blink on
|
||||
traffic.
|
||||
|
||||
Signed-off-by: Mathias Kresin <dev@kresin.me>
|
||||
---
|
||||
drivers/net/wireless/ath/ath10k/core.h | 4 ++++
|
||||
drivers/net/wireless/ath/ath10k/leds.c | 4 +---
|
||||
drivers/net/wireless/ath/ath10k/mac.c | 2 +-
|
||||
3 files changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/core.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/core.h
|
||||
@@ -1320,6 +1320,10 @@ struct ath10k {
|
||||
s32 tx_power_2g_limit;
|
||||
s32 tx_power_5g_limit;
|
||||
|
||||
+#ifdef CPTCFG_MAC80211_LEDS
|
||||
+ const char *led_default_trigger;
|
||||
+#endif
|
||||
+
|
||||
/* must be last */
|
||||
u8 drv_priv[] __aligned(sizeof(void *));
|
||||
};
|
||||
--- a/drivers/net/wireless/ath/ath10k/leds.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/leds.c
|
||||
@@ -69,7 +69,7 @@ int ath10k_leds_register(struct ath10k *
|
||||
|
||||
ar->leds.cdev.name = ar->leds.label;
|
||||
ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
|
||||
- ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger;
|
||||
+ ar->leds.cdev.default_trigger = ar->led_default_trigger;
|
||||
|
||||
ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
|
||||
if (ret)
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -10348,7 +10348,7 @@ int ath10k_mac_register(struct ath10k *a
|
||||
ar->hw->weight_multiplier = ATH10K_AIRTIME_WEIGHT_MULTIPLIER;
|
||||
|
||||
#ifdef CPTCFG_MAC80211_LEDS
|
||||
- ieee80211_create_tpt_led_trigger(ar->hw,
|
||||
+ ar->led_default_trigger = ieee80211_create_tpt_led_trigger(ar->hw,
|
||||
IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
|
||||
ARRAY_SIZE(ath10k_tpt_blink));
|
||||
#endif
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
From: Sven Eckelmann <seckelmann@datto.com>
|
||||
Date: Wed, 28 Nov 2018 16:16:27 +0100
|
||||
Subject: ath10k: adjust tx power reduction for US regulatory domain
|
||||
|
||||
FCC allows maximum antenna gain of 6 dBi. 15.247(b)(4):
|
||||
|
||||
> (4) The conducted output power limit
|
||||
> specified in paragraph (b) of this section
|
||||
> is based on the use of antennas
|
||||
> with directional gains that do not exceed
|
||||
> 6 dBi. Except as shown in paragraph
|
||||
> (c) of this section, if transmitting
|
||||
> antennas of directional gain greater
|
||||
> than 6 dBi are used, the conducted
|
||||
> output power from the intentional radiator
|
||||
> shall be reduced below the stated
|
||||
> values in paragraphs (b)(1), (b)(2),
|
||||
> and (b)(3) of this section, as appropriate,
|
||||
> by the amount in dB that the
|
||||
> directional gain of the antenna exceeds
|
||||
> 6 dBi.
|
||||
|
||||
https://www.gpo.gov/fdsys/pkg/CFR-2013-title47-vol1/pdf/CFR-2013-title47-vol1-sec15-247.pdf
|
||||
|
||||
Signed-off-by: Sven Eckelmann <seckelmann@datto.com>
|
||||
|
||||
Forwarded: no
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -1051,6 +1051,40 @@ static inline int ath10k_vdev_delete_syn
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static u32 ath10k_get_max_antenna_gain(struct ath10k *ar,
|
||||
+ u32 ch_max_antenna_gain)
|
||||
+{
|
||||
+ u32 max_antenna_gain;
|
||||
+
|
||||
+ if (ar->dfs_detector && ar->dfs_detector->region == NL80211_DFS_FCC) {
|
||||
+ /* FCC allows maximum antenna gain of 6 dBi. 15.247(b)(4):
|
||||
+ *
|
||||
+ * > (4) The conducted output power limit
|
||||
+ * > specified in paragraph (b) of this section
|
||||
+ * > is based on the use of antennas
|
||||
+ * > with directional gains that do not exceed
|
||||
+ * > 6 dBi. Except as shown in paragraph
|
||||
+ * > (c) of this section, if transmitting
|
||||
+ * > antennas of directional gain greater
|
||||
+ * > than 6 dBi are used, the conducted
|
||||
+ * > output power from the intentional radiator
|
||||
+ * > shall be reduced below the stated
|
||||
+ * > values in paragraphs (b)(1), (b)(2),
|
||||
+ * > and (b)(3) of this section, as appropriate,
|
||||
+ * > by the amount in dB that the
|
||||
+ * > directional gain of the antenna exceeds
|
||||
+ * > 6 dBi.
|
||||
+ *
|
||||
+ * https://www.gpo.gov/fdsys/pkg/CFR-2013-title47-vol1/pdf/CFR-2013-title47-vol1-sec15-247.pdf
|
||||
+ */
|
||||
+ max_antenna_gain = 6;
|
||||
+ } else {
|
||||
+ max_antenna_gain = 0;
|
||||
+ }
|
||||
+
|
||||
+ return max(ch_max_antenna_gain, max_antenna_gain);
|
||||
+}
|
||||
+
|
||||
static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
|
||||
{
|
||||
struct cfg80211_chan_def *chandef = NULL;
|
||||
@@ -1083,7 +1117,8 @@ static int ath10k_monitor_vdev_start(str
|
||||
arg.channel.min_power = 0;
|
||||
arg.channel.max_power = channel->max_power * 2;
|
||||
arg.channel.max_reg_power = channel->max_reg_power * 2;
|
||||
- arg.channel.max_antenna_gain = channel->max_antenna_gain;
|
||||
+ arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
|
||||
+ channel->max_antenna_gain);
|
||||
|
||||
reinit_completion(&ar->vdev_setup_done);
|
||||
reinit_completion(&ar->vdev_delete_done);
|
||||
@@ -1529,7 +1564,8 @@ static int ath10k_vdev_start_restart(str
|
||||
arg.channel.min_power = 0;
|
||||
arg.channel.max_power = chandef->chan->max_power * 2;
|
||||
arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
|
||||
- arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain;
|
||||
+ arg.channel.max_antenna_gain = ath10k_get_max_antenna_gain(ar,
|
||||
+ chandef->chan->max_antenna_gain);
|
||||
|
||||
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
|
||||
arg.ssid = arvif->u.ap.ssid;
|
||||
@@ -3460,7 +3496,8 @@ static int ath10k_update_channel_list(st
|
||||
ch->min_power = 0;
|
||||
ch->max_power = channel->max_power * 2;
|
||||
ch->max_reg_power = channel->max_reg_power * 2;
|
||||
- ch->max_antenna_gain = channel->max_antenna_gain;
|
||||
+ ch->max_antenna_gain = ath10k_get_max_antenna_gain(ar,
|
||||
+ channel->max_antenna_gain);
|
||||
ch->reg_class_id = 0; /* FIXME */
|
||||
|
||||
/* FIXME: why use only legacy modes, why not any
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
From 22fb5991a44c78ff18ec0082dc90c809356eb893 Mon Sep 17 00:00:00 2001
|
||||
From: Ansuel Smith <ansuelsmth@gmail.com>
|
||||
Date: Sun, 27 Sep 2020 19:23:35 +0200
|
||||
Subject: [PATCH 1/2] ath10k: Try to get mac-address from dts
|
||||
|
||||
Most of embedded device that have the ath10k wifi integrated store the
|
||||
mac-address in nvmem partitions. Try to fetch the mac-address using the
|
||||
standard 'of_get_mac_address' than in all the check also try to fetch the
|
||||
address using the nvmem api searching for a defined 'mac-address' cell.
|
||||
Mac-address defined in the dts have priority than any other address found.
|
||||
|
||||
Tested-on: QCA9984 hw1.0 PCI 10.4
|
||||
|
||||
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath10k/core.c | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/core.c
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/of.h>
|
||||
+#include <linux/of_net.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/ctype.h>
|
||||
@@ -3455,6 +3456,8 @@ static int ath10k_core_probe_fw(struct a
|
||||
|
||||
device_get_mac_address(ar->dev, ar->mac_addr);
|
||||
|
||||
+ of_get_mac_address(ar->dev->of_node, ar->mac_addr);
|
||||
+
|
||||
ret = ath10k_core_init_firmware_features(ar);
|
||||
if (ret) {
|
||||
ath10k_err(ar, "fatal problem with firmware features: %d\n",
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
From f7d6edafe4358e3880a26775cfde4cd5c71ba063 Mon Sep 17 00:00:00 2001
|
||||
From: David Bauer <mail@david-bauer.net>
|
||||
Date: Wed, 5 Jul 2023 01:30:29 +0200
|
||||
Subject: [PATCH] ath10k: always use mac80211 loss detection
|
||||
|
||||
ath10k does not report excessive loss in case of broken block-ack
|
||||
sessions. The loss is communicated to the host-os, but ath10k does not
|
||||
trigger a low-ack events by itself.
|
||||
|
||||
The mac80211 framework for loss detection however detects this
|
||||
circumstance well in case of ath10k. So use it regardless of ath10k's
|
||||
own loss detection mechanism.
|
||||
|
||||
Signed-off-by: David Bauer <mail@david-bauer.net>
|
||||
---
|
||||
drivers/net/wireless/ath/ath10k/mac.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -10140,7 +10140,6 @@ int ath10k_mac_register(struct ath10k *a
|
||||
ieee80211_hw_set(ar->hw, CHANCTX_STA_CSA);
|
||||
ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
|
||||
ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
|
||||
- ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
|
||||
|
||||
if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
|
||||
ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
|
||||
@@ -0,0 +1,64 @@
|
||||
--- a/drivers/net/wireless/ath/ath10k/htt.h
|
||||
+++ b/drivers/net/wireless/ath/ath10k/htt.h
|
||||
@@ -236,7 +236,11 @@ enum htt_rx_ring_flags {
|
||||
};
|
||||
|
||||
#define HTT_RX_RING_SIZE_MIN 128
|
||||
+#ifndef CONFIG_ATH10K_SMALLBUFFERS
|
||||
#define HTT_RX_RING_SIZE_MAX 2048
|
||||
+#else
|
||||
+#define HTT_RX_RING_SIZE_MAX 512
|
||||
+#endif
|
||||
#define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
|
||||
#define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
|
||||
#define HTT_RX_RING_FILL_LEVEL_DUAL_MAC (HTT_RX_RING_SIZE - 1)
|
||||
--- a/drivers/net/wireless/ath/ath10k/pci.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/pci.c
|
||||
@@ -133,7 +133,11 @@ static const struct ce_attr pci_host_ce_
|
||||
.flags = CE_ATTR_FLAGS,
|
||||
.src_nentries = 0,
|
||||
.src_sz_max = 2048,
|
||||
+#ifndef CONFIG_ATH10K_SMALLBUFFERS
|
||||
.dest_nentries = 512,
|
||||
+#else
|
||||
+ .dest_nentries = 128,
|
||||
+#endif
|
||||
.recv_cb = ath10k_pci_htt_htc_rx_cb,
|
||||
},
|
||||
|
||||
@@ -142,7 +146,11 @@ static const struct ce_attr pci_host_ce_
|
||||
.flags = CE_ATTR_FLAGS,
|
||||
.src_nentries = 0,
|
||||
.src_sz_max = 2048,
|
||||
+#ifndef CONFIG_ATH10K_SMALLBUFFERS
|
||||
.dest_nentries = 128,
|
||||
+#else
|
||||
+ .dest_nentries = 64,
|
||||
+#endif
|
||||
.recv_cb = ath10k_pci_htc_rx_cb,
|
||||
},
|
||||
|
||||
@@ -169,7 +177,11 @@ static const struct ce_attr pci_host_ce_
|
||||
.flags = CE_ATTR_FLAGS,
|
||||
.src_nentries = 0,
|
||||
.src_sz_max = 512,
|
||||
+#ifndef CONFIG_ATH10K_SMALLBUFFERS
|
||||
.dest_nentries = 512,
|
||||
+#else
|
||||
+ .dest_nentries = 128,
|
||||
+#endif
|
||||
.recv_cb = ath10k_pci_htt_rx_cb,
|
||||
},
|
||||
|
||||
@@ -194,7 +206,11 @@ static const struct ce_attr pci_host_ce_
|
||||
.flags = CE_ATTR_FLAGS,
|
||||
.src_nentries = 0,
|
||||
.src_sz_max = 2048,
|
||||
+#ifndef CONFIG_ATH10K_SMALLBUFFERS
|
||||
.dest_nentries = 128,
|
||||
+#else
|
||||
+ .dest_nentries = 96,
|
||||
+#endif
|
||||
.recv_cb = ath10k_pci_pktlog_rx_cb,
|
||||
},
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
From: Zhi-Jun You <hujy652@gmail.com>
|
||||
Date: Wed, 6 Aug 2025 15:00:05 +0800
|
||||
Subject: [RFC PATCH] wifi: ath10k: support flush_sta method
|
||||
|
||||
|
||||
When a STA is marked as no longer authorized, if the driver doesn't
|
||||
implement flush_sta(), mac80211 calls ieee80211_flush_queues() to
|
||||
flush hardware queues to avoid sending unencrypted frames.
|
||||
|
||||
This has became a problem for ath10k because ieee80211_flush_queues()
|
||||
will stop all traffic and call ath10k_flush, which waits until the
|
||||
whole HW queue is empty. In a busy environment this will trigger a
|
||||
timeout warning and stalls other STAs.
|
||||
|
||||
Fix this by implementing flush_sta method using WMI command to flush
|
||||
frames of a specific STA.
|
||||
Flushed frames will be marked as discard in tx complete indication.
|
||||
|
||||
ops->flush_sta will be set to NULL if htt.disable_tx_comp is set to
|
||||
true.
|
||||
|
||||
Tested-on: QCA9984 hw1.0 PCI 10.4-3.9.0.2-00157
|
||||
|
||||
Signed-off-by: Zhi-Jun You <hujy652@gmail.com>
|
||||
Tested-by: Florian Maurer <maurer@fh-aachen.de>
|
||||
---
|
||||
drivers/net/wireless/ath/ath10k/mac.c | 18 ++++++++++++++++++
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath10k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath10k/mac.c
|
||||
@@ -8180,6 +8180,20 @@ static void ath10k_flush(struct ieee8021
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
}
|
||||
|
||||
+static void ath10k_mac_op_flush_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
+ struct ieee80211_sta *sta)
|
||||
+{
|
||||
+ struct ath10k_vif *arvif = (void *)vif->drv_priv;
|
||||
+ struct ath10k *ar = hw->priv;
|
||||
+ u32 bitmap = 0xFFFFFFFF;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ ret = ath10k_wmi_peer_flush(ar, arvif->vdev_id, sta->addr, bitmap);
|
||||
+ if (ret)
|
||||
+ ath10k_warn(ar, "failed to flush sta (sta %pM)\n",
|
||||
+ sta->addr);
|
||||
+}
|
||||
+
|
||||
/* TODO: Implement this function properly
|
||||
* For now it is needed to reply to Probe Requests in IBSS mode.
|
||||
* Probably we need this information from FW.
|
||||
@@ -9532,6 +9546,7 @@ static const struct ieee80211_ops ath10k
|
||||
.set_rts_threshold = ath10k_set_rts_threshold,
|
||||
.set_frag_threshold = ath10k_mac_op_set_frag_threshold,
|
||||
.flush = ath10k_flush,
|
||||
+ .flush_sta = ath10k_mac_op_flush_sta,
|
||||
.tx_last_beacon = ath10k_tx_last_beacon,
|
||||
.set_antenna = ath10k_set_antenna,
|
||||
.get_antenna = ath10k_get_antenna,
|
||||
@@ -10353,6 +10368,9 @@ int ath10k_mac_register(struct ath10k *a
|
||||
if (!ar->hw_params.hw_ops->set_coverage_class)
|
||||
ar->ops->set_coverage_class = NULL;
|
||||
|
||||
+ if (ar->htt.disable_tx_comp)
|
||||
+ ar->ops->flush_sta = NULL;
|
||||
+
|
||||
ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
|
||||
ath10k_reg_notifier);
|
||||
if (ret) {
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
From 534a5f99d589cfa6b244b4433c192b6a278a67ff Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Sat, 5 Nov 2022 20:15:40 +0100
|
||||
Subject: [PATCH] wifi: ath11k: use unique QRTR instance ID
|
||||
|
||||
Currently, trying to use AHB + PCI/MHI cards or multiple PCI/MHI cards
|
||||
will cause a clash in the QRTR instance node ID and prevent the driver
|
||||
from talking via QMI to the card and thus initializing it with:
|
||||
[ 9.836329] ath11k c000000.wifi: host capability request failed: 1 90
|
||||
[ 9.842047] ath11k c000000.wifi: failed to send qmi host cap: -22
|
||||
|
||||
So, in order to allow for this combination of cards, especially AHB + PCI
|
||||
cards like IPQ8074 + QCN9074 (Used by me and tested on) set the desired
|
||||
QRTR instance ID offset by calculating a unique one based on PCI domain
|
||||
and bus ID-s and writing it to bits 7-0 of BHI_ERRDBG2 MHI register by
|
||||
using the SBL state callback that is added as part of the series.
|
||||
We also have to make sure that new QRTR offset is added on top of the
|
||||
default QRTR instance ID-s that are currently used in the driver.
|
||||
|
||||
This finally allows using AHB + PCI or multiple PCI cards on the same
|
||||
system.
|
||||
|
||||
Since this is not supported on QCA6390 and like, its limited to QCN9074
|
||||
which is known to support changing QRTR instance ID.
|
||||
|
||||
Before:
|
||||
root@OpenWrt:/# qrtr-lookup
|
||||
Service Version Instance Node Port
|
||||
1054 1 0 7 1 <unknown>
|
||||
69 1 2 7 3 ATH10k WLAN firmware service
|
||||
|
||||
After:
|
||||
root@OpenWrt:/# qrtr-lookup
|
||||
Service Version Instance Node Port
|
||||
1054 1 0 7 1 <unknown>
|
||||
69 1 2 7 3 ATH10k WLAN firmware service
|
||||
15 1 0 8 1 Test service
|
||||
69 1 8 8 2 ATH10k WLAN firmware service
|
||||
|
||||
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
|
||||
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
|
||||
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/mhi.c | 49 ++++++++++++++++++---------
|
||||
drivers/net/wireless/ath/ath11k/mhi.h | 3 ++
|
||||
drivers/net/wireless/ath/ath11k/pci.c | 9 ++++-
|
||||
3 files changed, 44 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/mhi.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
|
||||
@@ -239,6 +239,34 @@ static void ath11k_mhi_op_runtime_put(st
|
||||
{
|
||||
}
|
||||
|
||||
+static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
|
||||
+ void __iomem *addr,
|
||||
+ u32 *out)
|
||||
+{
|
||||
+ *out = readl(addr);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
|
||||
+ void __iomem *addr,
|
||||
+ u32 val)
|
||||
+{
|
||||
+ writel(val, addr);
|
||||
+}
|
||||
+
|
||||
+static void ath11k_mhi_qrtr_instance_set(struct mhi_controller *mhi_cntrl)
|
||||
+{
|
||||
+ struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
|
||||
+
|
||||
+ if (ab->hw_rev == ATH11K_HW_QCN9074_HW10) {
|
||||
+ ath11k_mhi_op_write_reg(mhi_cntrl,
|
||||
+ mhi_cntrl->bhi + BHI_ERRDBG2,
|
||||
+ FIELD_PREP(QRTR_INSTANCE_MASK,
|
||||
+ ab->qmi.service_ins_id - ab->hw_params.qmi_service_ins_id));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)
|
||||
{
|
||||
switch (reason) {
|
||||
@@ -260,6 +288,8 @@ static char *ath11k_mhi_op_callback_to_s
|
||||
return "MHI_CB_FATAL_ERROR";
|
||||
case MHI_CB_BW_REQ:
|
||||
return "MHI_CB_BW_REQ";
|
||||
+ case MHI_CB_EE_SBL_MODE:
|
||||
+ return "MHI_CB_EE_SBL_MODE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -290,6 +320,9 @@ static void ath11k_mhi_op_status_cb(stru
|
||||
queue_work(ab->workqueue_aux, &ab->reset_work);
|
||||
|
||||
break;
|
||||
+ case MHI_CB_EE_SBL_MODE:
|
||||
+ ath11k_mhi_qrtr_instance_set(mhi_cntrl);
|
||||
+ break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -297,22 +330,6 @@ static void ath11k_mhi_op_status_cb(stru
|
||||
ab_pci->mhi_pre_cb = cb;
|
||||
}
|
||||
|
||||
-static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
|
||||
- void __iomem *addr,
|
||||
- u32 *out)
|
||||
-{
|
||||
- *out = readl(addr);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
|
||||
- void __iomem *addr,
|
||||
- u32 val)
|
||||
-{
|
||||
- writel(val, addr);
|
||||
-}
|
||||
-
|
||||
static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl)
|
||||
{
|
||||
struct device_node *np;
|
||||
--- a/drivers/net/wireless/ath/ath11k/mhi.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/mhi.h
|
||||
@@ -17,6 +17,9 @@
|
||||
#define MHICTRL 0x38
|
||||
#define MHICTRL_RESET_MASK 0x2
|
||||
|
||||
+#define BHI_ERRDBG2 0x38
|
||||
+#define QRTR_INSTANCE_MASK GENMASK(7, 0)
|
||||
+
|
||||
int ath11k_mhi_start(struct ath11k_pci *ar_pci);
|
||||
void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend);
|
||||
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
|
||||
--- a/drivers/net/wireless/ath/ath11k/pci.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/pci.c
|
||||
@@ -394,13 +394,20 @@ static void ath11k_pci_sw_reset(struct a
|
||||
static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab)
|
||||
{
|
||||
struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg;
|
||||
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
||||
+ struct pci_bus *bus = ab_pci->pdev->bus;
|
||||
|
||||
cfg->tgt_ce = ab->hw_params.target_ce_config;
|
||||
cfg->tgt_ce_len = ab->hw_params.target_ce_count;
|
||||
|
||||
cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map;
|
||||
cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len;
|
||||
- ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
|
||||
+
|
||||
+ if (ab->hw_rev == ATH11K_HW_QCN9074_HW10) {
|
||||
+ ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id +
|
||||
+ (((pci_domain_nr(bus) & 0xF) << 4) | (bus->number & 0xF));
|
||||
+ } else
|
||||
+ ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id;
|
||||
|
||||
ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2,
|
||||
&cfg->shadow_reg_v2_len);
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
From 88f17c87ddc9ee7467acdc322d383e5a443a55ab Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 8 Dec 2025 20:50:47 +0100
|
||||
Subject: [PATCH 1/2] wifi: ath11k: fix wrong usage of resource_size() causing
|
||||
firmware panic
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On converting to the of_reserved_mem_region_to_resource() helper with
|
||||
commit 900730dc4705 ("wifi: ath: Use
|
||||
of_reserved_mem_region_to_resource() for "memory-region"") a logic error
|
||||
was introduced in the ath11k_core_coldboot_cal_support() if condition.
|
||||
|
||||
The original code checked for hremote_node presence and skipped
|
||||
ath11k_core_coldboot_cal_support() in the other switch case but now
|
||||
everything is driven entirely on the values of the resource struct.
|
||||
|
||||
resource_size() (in this case) is wrongly assumed to return a size of
|
||||
zero if the passed resource struct is init to zero. This is not the case
|
||||
as a resource struct should be always init with correct values (or at
|
||||
best set the end value to -1 to signal it's not configured)
|
||||
(the return value of resource_size() for a resource struct with start
|
||||
and end set to zero is 1)
|
||||
|
||||
On top of this, using resource_size() to check if a resource struct is
|
||||
initialized or not is generally wrong and other measure should be used
|
||||
instead.
|
||||
|
||||
To better handle this, use the DEFINE_RES macro to initialize the
|
||||
resource struct and set the IORESOURCE_UNSET flag by default.
|
||||
|
||||
Replace the resource_size() check with checking for the resource struct
|
||||
flags and check if it's IORESOURCE_UNSET.
|
||||
|
||||
This change effectively restore the original logic and restore correct
|
||||
loading of the ath11k firmware (restoring correct functionality of
|
||||
Wi-Fi)
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Fixes: 900730dc4705 ("wifi: ath: Use of_reserved_mem_region_to_resource() for "memory-region"")
|
||||
Link: https://lore.kernel.org/all/20251207215359.28895-1-ansuelsmth@gmail.com/T/#m990492684913c5a158ff0e5fc90697d8ad95351b
|
||||
Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/qmi.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/qmi.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
|
||||
@@ -2039,8 +2039,8 @@ static int ath11k_qmi_alloc_target_mem_c
|
||||
|
||||
static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab)
|
||||
{
|
||||
+ struct resource res = DEFINE_RES_NAMED(0, 0, NULL, IORESOURCE_UNSET);
|
||||
struct device *dev = ab->dev;
|
||||
- struct resource res = {};
|
||||
u32 host_ddr_sz;
|
||||
int i, idx, ret;
|
||||
|
||||
@@ -2086,7 +2086,7 @@ static int ath11k_qmi_assign_target_mem_
|
||||
}
|
||||
|
||||
if (ath11k_core_coldboot_cal_support(ab)) {
|
||||
- if (resource_size(&res)) {
|
||||
+ if (res.flags != IORESOURCE_UNSET) {
|
||||
ab->qmi.target_mem[idx].paddr =
|
||||
res.start + host_ddr_sz;
|
||||
ab->qmi.target_mem[idx].iaddr =
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
From 824dde8652815aa67b4e2bf2d8a9455a8ef82b8f Mon Sep 17 00:00:00 2001
|
||||
From: Ziyang Huang <hzyitc@outlook.com>
|
||||
Date: Thu, 29 Jun 2023 06:12:45 +0000
|
||||
Subject: [PATCH] wifi: ath11k: Support setting bdf-addr and caldb-addr via DT
|
||||
|
||||
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/qmi.c | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/qmi.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
|
||||
@@ -2042,6 +2042,7 @@ static int ath11k_qmi_assign_target_mem_
|
||||
struct resource res = DEFINE_RES_NAMED(0, 0, NULL, IORESOURCE_UNSET);
|
||||
struct device *dev = ab->dev;
|
||||
u32 host_ddr_sz;
|
||||
+ u32 addr;
|
||||
int i, idx, ret;
|
||||
|
||||
for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
|
||||
@@ -2073,7 +2074,9 @@ static int ath11k_qmi_assign_target_mem_
|
||||
idx++;
|
||||
break;
|
||||
case BDF_MEM_REGION_TYPE:
|
||||
- ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr;
|
||||
+ if (of_property_read_u32(dev->of_node, "qcom,bdf-addr", &addr))
|
||||
+ addr = ab->hw_params.bdf_addr;
|
||||
+ ab->qmi.target_mem[idx].paddr = addr;
|
||||
ab->qmi.target_mem[idx].iaddr = NULL;
|
||||
ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
|
||||
ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
|
||||
@@ -2095,8 +2098,9 @@ static int ath11k_qmi_assign_target_mem_
|
||||
if (!ab->qmi.target_mem[idx].iaddr)
|
||||
return -EIO;
|
||||
} else {
|
||||
- ab->qmi.target_mem[idx].paddr =
|
||||
- ATH11K_QMI_CALDB_ADDRESS;
|
||||
+ if (of_property_read_u32(dev->of_node, "qcom,caldb-addr", &addr))
|
||||
+ addr = ATH11K_QMI_CALDB_ADDRESS;
|
||||
+ ab->qmi.target_mem[idx].paddr = addr;
|
||||
ab->qmi.target_mem[idx].iaddr = NULL;
|
||||
}
|
||||
} else {
|
||||
@@ -2302,6 +2306,7 @@ static int ath11k_qmi_load_file_target_m
|
||||
struct qmi_wlanfw_bdf_download_resp_msg_v01 resp;
|
||||
struct qmi_txn txn;
|
||||
const u8 *temp = data;
|
||||
+ u32 addr;
|
||||
void __iomem *bdf_addr = NULL;
|
||||
int ret = 0;
|
||||
u32 remaining = len;
|
||||
@@ -2313,7 +2318,9 @@ static int ath11k_qmi_load_file_target_m
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
|
||||
if (ab->hw_params.fixed_bdf_addr) {
|
||||
- bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size);
|
||||
+ if(of_property_read_u32(ab->dev->of_node, "qcom,bdf-addr", &addr))
|
||||
+ addr = ab->hw_params.bdf_addr;
|
||||
+ bdf_addr = ioremap(addr, ab->hw_params.fw.board_size);
|
||||
if (!bdf_addr) {
|
||||
ath11k_warn(ab, "qmi ioremap error for bdf_addr\n");
|
||||
ret = -EIO;
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
From b5ade0e0e1c1622a85fbfd2c93b41caff479f305 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Maurer <f.maurer@outlook.de>
|
||||
Date: Fri, 3 Oct 2025 12:56:13 +0200
|
||||
Subject: [PATCH] ath11k: add ath11k_mac_op_flush_sta to properly flush pending
|
||||
packets
|
||||
|
||||
When a STA is marked as no longer authorized, if the driver doesn't
|
||||
implement flush_sta(), mac80211 calls ieee80211_flush_queues() to
|
||||
flush hardware queues to avoid sending unencrypted frames.
|
||||
|
||||
This has became a problem for ath11k because ieee80211_flush_queues()
|
||||
will stop all traffic and call ath11k_flush, which waits until the
|
||||
whole HW queue is empty. In a busy environment this will trigger a
|
||||
timeout warning and stalls other STAs.
|
||||
|
||||
Fix this by implementing flush_sta method using WMI command to flush
|
||||
frames of a specific STA.
|
||||
Flushed frames will be marked as discard in tx complete indication.
|
||||
|
||||
warning print "ath11k c000000.wifi: failed to flush transmit queue 0"
|
||||
was observed on various openwrt devices, and is fixed through this patch.
|
||||
|
||||
Tested-by: Florian Maurer <f.maurer@outlook.de>
|
||||
Tested-by: Flole <flole@flole.de>
|
||||
Co-developed-by: Benjamin Berg <benjamin@sipsolutions.net>
|
||||
Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net>
|
||||
Signed-off-by: Florian Maurer <f.maurer@outlook.de>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/mac.c | 18 ++++++++++++++++++
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
||||
@@ -8330,6 +8330,23 @@ static void ath11k_mac_op_flush(struct i
|
||||
ath11k_mac_flush_tx_complete(ar);
|
||||
}
|
||||
|
||||
+static void ath11k_mac_op_flush_sta(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_vif *vif,
|
||||
+ struct ieee80211_sta *sta)
|
||||
+{
|
||||
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
|
||||
+ struct ath11k *ar = hw->priv;
|
||||
+ struct peer_flush_params params = {
|
||||
+ .peer_tid_bitmap = 0xFF,
|
||||
+ .vdev_id = arvif->vdev_id,
|
||||
+ };
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = ath11k_wmi_send_peer_flush_tids_cmd(ar, sta->addr, ¶ms);
|
||||
+ if (ret)
|
||||
+ ath11k_warn(ar->ab, "failed to flush sta %pM: %d\n", sta->addr, ret);
|
||||
+}
|
||||
+
|
||||
static bool
|
||||
ath11k_mac_has_single_legacy_rate(struct ath11k *ar,
|
||||
enum nl80211_band band,
|
||||
@@ -9920,6 +9937,7 @@ static const struct ieee80211_ops ath11k
|
||||
.set_bitrate_mask = ath11k_mac_op_set_bitrate_mask,
|
||||
.get_survey = ath11k_mac_op_get_survey,
|
||||
.flush = ath11k_mac_op_flush,
|
||||
+ .flush_sta = ath11k_mac_op_flush_sta,
|
||||
.sta_statistics = ath11k_mac_op_sta_statistics,
|
||||
CFG80211_TESTMODE_CMD(ath11k_tm_cmd)
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
From 703d6551f71e7290619d6effe2a25a64e10538b7 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Thu, 15 Dec 2022 12:20:52 +0100
|
||||
Subject: [PATCH] ath11k: control thermal support via symbol
|
||||
|
||||
Currently, thermal support will get built if CONFIG_THERMAL is reachable,
|
||||
however this is not suitable for OpenWrt as with ALL_KMODS being set to y
|
||||
ATH11K_THERMAL wont get selected and so hwmon and thermal kmods wont get
|
||||
pulled in resulting in a build-failure.
|
||||
|
||||
So, to avoid that, lets do what is already done for ath10k and add a
|
||||
config symbol into backports for enabling thermal support.
|
||||
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/Kconfig | 7 +++++++
|
||||
drivers/net/wireless/ath/ath11k/Makefile | 2 +-
|
||||
drivers/net/wireless/ath/ath11k/thermal.h | 2 +-
|
||||
local-symbols | 1 +
|
||||
4 files changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/Kconfig
|
||||
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
|
||||
@@ -62,3 +62,10 @@ config ATH11K_SPECTRAL
|
||||
Enable ath11k spectral scan support
|
||||
|
||||
Say Y to enable access to the FFT/spectral data via debugfs.
|
||||
+
|
||||
+config ATH11K_THERMAL
|
||||
+ bool "ath11k thermal sensors and throttling support"
|
||||
+ depends on ATH11K
|
||||
+ depends on THERMAL
|
||||
+ help
|
||||
+ Enable ath11k thermal sensors and throttling support.
|
||||
--- a/drivers/net/wireless/ath/ath11k/Makefile
|
||||
+++ b/drivers/net/wireless/ath/ath11k/Makefile
|
||||
@@ -24,7 +24,7 @@ ath11k-y += core.o \
|
||||
ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
|
||||
ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o
|
||||
ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o
|
||||
-ath11k-$(CONFIG_THERMAL) += thermal.o
|
||||
+ath11k-$(CPTCFG_ATH11K_THERMAL) += thermal.o
|
||||
ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o
|
||||
ath11k-$(CONFIG_PM) += wow.o
|
||||
ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o
|
||||
--- a/drivers/net/wireless/ath/ath11k/thermal.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/thermal.h
|
||||
@@ -26,7 +26,7 @@ struct ath11k_thermal {
|
||||
int temperature;
|
||||
};
|
||||
|
||||
-#if IS_REACHABLE(CONFIG_THERMAL)
|
||||
+#if IS_REACHABLE(CPTCFG_ATH11K_THERMAL)
|
||||
int ath11k_thermal_register(struct ath11k_base *ab);
|
||||
void ath11k_thermal_unregister(struct ath11k_base *ab);
|
||||
int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state);
|
||||
--- a/local-symbols
|
||||
+++ b/local-symbols
|
||||
@@ -163,6 +163,7 @@ ATH11K_DEBUG=
|
||||
ATH11K_DEBUGFS=
|
||||
ATH11K_TRACING=
|
||||
ATH11K_SPECTRAL=
|
||||
+ATH11K_THERMAL=
|
||||
ATH12K=
|
||||
ATH12K_AHB=
|
||||
ATH12K_DEBUG=
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
From fb1c40c225cbc413d82c872dd8c8af3469b2b921 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Fri, 16 Dec 2022 17:17:52 +0100
|
||||
Subject: [PATCH] ath11k: support setting FW memory mode via DT
|
||||
|
||||
ath11k is really memory intensive for devices with less that 1GB of RAM,
|
||||
so lets allow saving a significant amount of memory by setting the FW to
|
||||
Mode-1 via DTS for devices that need it.
|
||||
|
||||
However the drawback is reduced number of VDEV-s and peers which is a
|
||||
reasonable tradeoff.
|
||||
|
||||
Mode-2 allows for further reduction, but it has further restrictions.
|
||||
|
||||
While we are here, lets add a print to be able to easily determine what
|
||||
FW memory mode is being used.
|
||||
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/core.c | 28 ++++++++++++++++++++++++--
|
||||
1 file changed, 26 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||||
@@ -39,7 +39,7 @@ bool ath11k_ftm_mode;
|
||||
module_param_named(ftm_mode, ath11k_ftm_mode, bool, 0444);
|
||||
MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
|
||||
|
||||
-static const struct ath11k_hw_params ath11k_hw_params[] = {
|
||||
+static struct ath11k_hw_params ath11k_hw_params[] = {
|
||||
{
|
||||
.hw_rev = ATH11K_HW_IPQ8074,
|
||||
.name = "ipq8074 hw2.0",
|
||||
@@ -2521,7 +2521,8 @@ static void ath11k_core_reset(struct wor
|
||||
static int ath11k_init_hw_params(struct ath11k_base *ab)
|
||||
{
|
||||
const struct ath11k_hw_params *hw_params = NULL;
|
||||
- int i;
|
||||
+ u32 fw_mem_mode;
|
||||
+ int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ath11k_hw_params); i++) {
|
||||
hw_params = &ath11k_hw_params[i];
|
||||
@@ -2537,7 +2538,31 @@ static int ath11k_init_hw_params(struct
|
||||
|
||||
ab->hw_params = *hw_params;
|
||||
|
||||
+ ret = of_property_read_u32(ab->dev->of_node,
|
||||
+ "qcom,ath11k-fw-memory-mode",
|
||||
+ &fw_mem_mode);
|
||||
+ if (!ret) {
|
||||
+ if (fw_mem_mode == 0) {
|
||||
+ ab->hw_params.fw_mem_mode = 0;
|
||||
+ ab->hw_params.num_vdevs = 16 + 1;
|
||||
+ ab->hw_params.num_peers = 512;
|
||||
+ }
|
||||
+ else if (fw_mem_mode == 1) {
|
||||
+ ab->hw_params.fw_mem_mode = 1;
|
||||
+ ab->hw_params.num_vdevs = 8;
|
||||
+ ab->hw_params.num_peers = 128;
|
||||
+ } else if (fw_mem_mode == 2) {
|
||||
+ ab->hw_params.fw_mem_mode = 2;
|
||||
+ ab->hw_params.num_vdevs = 8;
|
||||
+ ab->hw_params.num_peers = 128;
|
||||
+ ab->hw_params.coldboot_cal_mm = false;
|
||||
+ ab->hw_params.coldboot_cal_ftm = false;
|
||||
+ } else
|
||||
+ ath11k_info(ab, "Unsupported FW memory mode: %u\n", fw_mem_mode);
|
||||
+ }
|
||||
+
|
||||
ath11k_info(ab, "%s\n", ab->hw_params.name);
|
||||
+ ath11k_info(ab, "FW memory mode: %d\n", ab->hw_params.fw_mem_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
From abdd0985a36189ef2cc0e393b027276e86137ace Mon Sep 17 00:00:00 2001
|
||||
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
||||
Date: Tue, 11 Apr 2023 20:08:49 +0200
|
||||
Subject: [PATCH] ath11k: remove intersection support for regulatory rules
|
||||
|
||||
Currently, regulatory rules from new country settings is intersected with
|
||||
rules from default country settings(during initialisation) in order to prevent
|
||||
users to bypass their default country settings such as power limits, channel
|
||||
flags, etc.
|
||||
|
||||
However, the country setting in the BDF will take higher higher precendence
|
||||
and FW will protect it. Therefore, there is no need to handle intersection
|
||||
on the driver side now.
|
||||
|
||||
Remove regulatory rules intersection logic support.
|
||||
|
||||
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/reg.c | 168 +++-----------------------
|
||||
drivers/net/wireless/ath/ath11k/reg.h | 2 +-
|
||||
drivers/net/wireless/ath/ath11k/wmi.c | 24 +---
|
||||
3 files changed, 16 insertions(+), 178 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/reg.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/reg.c
|
||||
@@ -353,134 +353,6 @@ static u32 ath11k_map_fw_phy_flags(u32 p
|
||||
return flags;
|
||||
}
|
||||
|
||||
-static bool
|
||||
-ath11k_reg_can_intersect(struct ieee80211_reg_rule *rule1,
|
||||
- struct ieee80211_reg_rule *rule2)
|
||||
-{
|
||||
- u32 start_freq1, end_freq1;
|
||||
- u32 start_freq2, end_freq2;
|
||||
-
|
||||
- start_freq1 = rule1->freq_range.start_freq_khz;
|
||||
- start_freq2 = rule2->freq_range.start_freq_khz;
|
||||
-
|
||||
- end_freq1 = rule1->freq_range.end_freq_khz;
|
||||
- end_freq2 = rule2->freq_range.end_freq_khz;
|
||||
-
|
||||
- if ((start_freq1 >= start_freq2 &&
|
||||
- start_freq1 < end_freq2) ||
|
||||
- (start_freq2 > start_freq1 &&
|
||||
- start_freq2 < end_freq1))
|
||||
- return true;
|
||||
-
|
||||
- /* TODO: Should we restrict intersection feasibility
|
||||
- * based on min bandwidth of the intersected region also,
|
||||
- * say the intersected rule should have a min bandwidth
|
||||
- * of 20MHz?
|
||||
- */
|
||||
-
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
-static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
|
||||
- struct ieee80211_reg_rule *rule2,
|
||||
- struct ieee80211_reg_rule *new_rule)
|
||||
-{
|
||||
- u32 start_freq1, end_freq1;
|
||||
- u32 start_freq2, end_freq2;
|
||||
- u32 freq_diff, max_bw;
|
||||
-
|
||||
- start_freq1 = rule1->freq_range.start_freq_khz;
|
||||
- start_freq2 = rule2->freq_range.start_freq_khz;
|
||||
-
|
||||
- end_freq1 = rule1->freq_range.end_freq_khz;
|
||||
- end_freq2 = rule2->freq_range.end_freq_khz;
|
||||
-
|
||||
- new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1,
|
||||
- start_freq2);
|
||||
- new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2);
|
||||
-
|
||||
- freq_diff = new_rule->freq_range.end_freq_khz -
|
||||
- new_rule->freq_range.start_freq_khz;
|
||||
- max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz,
|
||||
- rule2->freq_range.max_bandwidth_khz);
|
||||
- new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff);
|
||||
-
|
||||
- new_rule->power_rule.max_antenna_gain =
|
||||
- min_t(u32, rule1->power_rule.max_antenna_gain,
|
||||
- rule2->power_rule.max_antenna_gain);
|
||||
-
|
||||
- new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp,
|
||||
- rule2->power_rule.max_eirp);
|
||||
-
|
||||
- /* Use the flags of both the rules */
|
||||
- new_rule->flags = rule1->flags | rule2->flags;
|
||||
-
|
||||
- if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD))
|
||||
- new_rule->psd = min_t(s8, rule1->psd, rule2->psd);
|
||||
- else
|
||||
- new_rule->flags &= ~NL80211_RRF_PSD;
|
||||
-
|
||||
- /* To be safe, lts use the max cac timeout of both rules */
|
||||
- new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
|
||||
- rule2->dfs_cac_ms);
|
||||
-}
|
||||
-
|
||||
-static struct ieee80211_regdomain *
|
||||
-ath11k_regd_intersect(struct ieee80211_regdomain *default_regd,
|
||||
- struct ieee80211_regdomain *curr_regd)
|
||||
-{
|
||||
- u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules;
|
||||
- struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule;
|
||||
- struct ieee80211_regdomain *new_regd = NULL;
|
||||
- u8 i, j, k;
|
||||
-
|
||||
- num_old_regd_rules = default_regd->n_reg_rules;
|
||||
- num_curr_regd_rules = curr_regd->n_reg_rules;
|
||||
- num_new_regd_rules = 0;
|
||||
-
|
||||
- /* Find the number of intersecting rules to allocate new regd memory */
|
||||
- for (i = 0; i < num_old_regd_rules; i++) {
|
||||
- old_rule = default_regd->reg_rules + i;
|
||||
- for (j = 0; j < num_curr_regd_rules; j++) {
|
||||
- curr_rule = curr_regd->reg_rules + j;
|
||||
-
|
||||
- if (ath11k_reg_can_intersect(old_rule, curr_rule))
|
||||
- num_new_regd_rules++;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!num_new_regd_rules)
|
||||
- return NULL;
|
||||
-
|
||||
- new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules *
|
||||
- sizeof(struct ieee80211_reg_rule)),
|
||||
- GFP_ATOMIC);
|
||||
-
|
||||
- if (!new_regd)
|
||||
- return NULL;
|
||||
-
|
||||
- /* We set the new country and dfs region directly and only trim
|
||||
- * the freq, power, antenna gain by intersecting with the
|
||||
- * default regdomain. Also MAX of the dfs cac timeout is selected.
|
||||
- */
|
||||
- new_regd->n_reg_rules = num_new_regd_rules;
|
||||
- memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2));
|
||||
- new_regd->dfs_region = curr_regd->dfs_region;
|
||||
- new_rule = new_regd->reg_rules;
|
||||
-
|
||||
- for (i = 0, k = 0; i < num_old_regd_rules; i++) {
|
||||
- old_rule = default_regd->reg_rules + i;
|
||||
- for (j = 0; j < num_curr_regd_rules; j++) {
|
||||
- curr_rule = curr_regd->reg_rules + j;
|
||||
-
|
||||
- if (ath11k_reg_can_intersect(old_rule, curr_rule))
|
||||
- ath11k_reg_intersect_rules(old_rule, curr_rule,
|
||||
- (new_rule + k++));
|
||||
- }
|
||||
- }
|
||||
- return new_regd;
|
||||
-}
|
||||
-
|
||||
static const char *
|
||||
ath11k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region)
|
||||
{
|
||||
@@ -631,11 +503,11 @@ ath11k_reg_ap_pwr_convert(enum ieee80211
|
||||
|
||||
struct ieee80211_regdomain *
|
||||
ath11k_reg_build_regd(struct ath11k_base *ab,
|
||||
- struct cur_regulatory_info *reg_info, bool intersect,
|
||||
+ struct cur_regulatory_info *reg_info,
|
||||
enum wmi_vdev_type vdev_type,
|
||||
enum ieee80211_ap_reg_power power_type)
|
||||
{
|
||||
- struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
|
||||
+ struct ieee80211_regdomain *new_regd = NULL;
|
||||
struct cur_reg_rule *reg_rule, *reg_rule_6ghz;
|
||||
u8 i = 0, j = 0, k = 0;
|
||||
u8 num_rules;
|
||||
@@ -678,26 +550,26 @@ ath11k_reg_build_regd(struct ath11k_base
|
||||
}
|
||||
|
||||
if (!num_rules)
|
||||
- goto ret;
|
||||
+ return new_regd;
|
||||
|
||||
/* Add max additional rules to accommodate weather radar band */
|
||||
if (reg_info->dfs_region == ATH11K_DFS_REG_ETSI)
|
||||
num_rules += 2;
|
||||
|
||||
- tmp_regd = kzalloc(sizeof(*tmp_regd) +
|
||||
+ new_regd = kzalloc(sizeof(*new_regd) +
|
||||
(num_rules * sizeof(struct ieee80211_reg_rule)),
|
||||
GFP_ATOMIC);
|
||||
- if (!tmp_regd)
|
||||
- goto ret;
|
||||
+ if (!new_regd)
|
||||
+ return new_regd;
|
||||
|
||||
- memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
|
||||
+ memcpy(new_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
|
||||
memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
|
||||
alpha2[2] = '\0';
|
||||
- tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region);
|
||||
+ new_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region);
|
||||
|
||||
ath11k_dbg(ab, ATH11K_DBG_REG,
|
||||
"Country %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
|
||||
- alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region),
|
||||
+ alpha2, ath11k_reg_get_regdom_str(new_regd->dfs_region),
|
||||
reg_info->dfs_region, num_rules);
|
||||
/* Update reg_rules[] below. Firmware is expected to
|
||||
* send these rules in order(2 GHz rules first and then 5 GHz)
|
||||
@@ -736,7 +608,7 @@ ath11k_reg_build_regd(struct ath11k_base
|
||||
flags |= ath11k_map_fw_reg_flags(reg_rule->flags);
|
||||
flags |= ath11k_map_fw_phy_flags(reg_info->phybitmap);
|
||||
|
||||
- ath11k_reg_update_rule(tmp_regd->reg_rules + i,
|
||||
+ ath11k_reg_update_rule(new_regd->reg_rules + i,
|
||||
reg_rule->start_freq,
|
||||
reg_rule->end_freq, max_bw,
|
||||
reg_rule->ant_gain, reg_rule->reg_power,
|
||||
@@ -751,7 +623,7 @@ ath11k_reg_build_regd(struct ath11k_base
|
||||
reg_info->dfs_region == ATH11K_DFS_REG_ETSI &&
|
||||
(reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW &&
|
||||
reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){
|
||||
- ath11k_reg_update_weather_radar_band(ab, tmp_regd,
|
||||
+ ath11k_reg_update_weather_radar_band(ab, new_regd,
|
||||
reg_rule, &i,
|
||||
flags, max_bw);
|
||||
continue;
|
||||
@@ -762,37 +634,20 @@ ath11k_reg_build_regd(struct ath11k_base
|
||||
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n",
|
||||
i + 1, reg_rule->start_freq, reg_rule->end_freq,
|
||||
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
|
||||
- tmp_regd->reg_rules[i].dfs_cac_ms, flags,
|
||||
+ new_regd->reg_rules[i].dfs_cac_ms, flags,
|
||||
reg_rule->psd_flag, reg_rule->psd_eirp);
|
||||
} else {
|
||||
ath11k_dbg(ab, ATH11K_DBG_REG,
|
||||
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
|
||||
i + 1, reg_rule->start_freq, reg_rule->end_freq,
|
||||
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
|
||||
- tmp_regd->reg_rules[i].dfs_cac_ms,
|
||||
+ new_regd->reg_rules[i].dfs_cac_ms,
|
||||
flags);
|
||||
}
|
||||
}
|
||||
|
||||
- tmp_regd->n_reg_rules = i;
|
||||
+ new_regd->n_reg_rules = i;
|
||||
|
||||
- if (intersect) {
|
||||
- default_regd = ab->default_regd[reg_info->phy_id];
|
||||
-
|
||||
- /* Get a new regd by intersecting the received regd with
|
||||
- * our default regd.
|
||||
- */
|
||||
- new_regd = ath11k_regd_intersect(default_regd, tmp_regd);
|
||||
- kfree(tmp_regd);
|
||||
- if (!new_regd) {
|
||||
- ath11k_warn(ab, "Unable to create intersected regdomain\n");
|
||||
- goto ret;
|
||||
- }
|
||||
- } else {
|
||||
- new_regd = tmp_regd;
|
||||
- }
|
||||
-
|
||||
-ret:
|
||||
return new_regd;
|
||||
}
|
||||
|
||||
@@ -844,17 +699,6 @@ void ath11k_regd_update_chan_list_work(s
|
||||
}
|
||||
}
|
||||
|
||||
-static bool ath11k_reg_is_world_alpha(char *alpha)
|
||||
-{
|
||||
- if (alpha[0] == '0' && alpha[1] == '0')
|
||||
- return true;
|
||||
-
|
||||
- if (alpha[0] == 'n' && alpha[1] == 'a')
|
||||
- return true;
|
||||
-
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
static enum wmi_vdev_type ath11k_reg_get_ar_vdev_type(struct ath11k *ar)
|
||||
{
|
||||
struct ath11k_vif *arvif;
|
||||
@@ -877,7 +721,6 @@ int ath11k_reg_handle_chan_list(struct a
|
||||
enum ieee80211_ap_reg_power power_type)
|
||||
{
|
||||
struct ieee80211_regdomain *regd;
|
||||
- bool intersect = false;
|
||||
int pdev_idx;
|
||||
struct ath11k *ar;
|
||||
enum wmi_vdev_type vdev_type;
|
||||
@@ -929,24 +772,14 @@ int ath11k_reg_handle_chan_list(struct a
|
||||
(char *)reg_info->alpha2, 2))
|
||||
goto retfail;
|
||||
|
||||
- /* Intersect new rules with default regd if a new country setting was
|
||||
- * requested, i.e a default regd was already set during initialization
|
||||
- * and the regd coming from this event has a valid country info.
|
||||
- */
|
||||
- if (ab->default_regd[pdev_idx] &&
|
||||
- !ath11k_reg_is_world_alpha((char *)
|
||||
- ab->default_regd[pdev_idx]->alpha2) &&
|
||||
- !ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
|
||||
- intersect = true;
|
||||
-
|
||||
ar = ab->pdevs[pdev_idx].ar;
|
||||
vdev_type = ath11k_reg_get_ar_vdev_type(ar);
|
||||
|
||||
ath11k_dbg(ab, ATH11K_DBG_WMI,
|
||||
- "wmi handle chan list power type %d vdev type %d intersect %d\n",
|
||||
- power_type, vdev_type, intersect);
|
||||
+ "wmi handle chan list power type %d vdev type %d\n",
|
||||
+ power_type, vdev_type);
|
||||
|
||||
- regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type);
|
||||
+ regd = ath11k_reg_build_regd(ab, reg_info, vdev_type, power_type);
|
||||
if (!regd) {
|
||||
ath11k_warn(ab, "failed to build regd from reg_info\n");
|
||||
goto fallback;
|
||||
--- a/drivers/net/wireless/ath/ath11k/reg.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/reg.h
|
||||
@@ -36,7 +36,7 @@ void ath11k_regd_update_work(struct work
|
||||
void ath11k_regd_update_chan_list_work(struct work_struct *work);
|
||||
struct ieee80211_regdomain *
|
||||
ath11k_reg_build_regd(struct ath11k_base *ab,
|
||||
- struct cur_regulatory_info *reg_info, bool intersect,
|
||||
+ struct cur_regulatory_info *reg_info,
|
||||
enum wmi_vdev_type vdev_type,
|
||||
enum ieee80211_ap_reg_power power_type);
|
||||
int ath11k_regd_update(struct ath11k *ar);
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
From a3be23672b4a81256d275af31afc6edcce5c5a26 Mon Sep 17 00:00:00 2001
|
||||
From: Mantas Pucka <mantas@8devices.com>
|
||||
Date: Mon, 22 Jan 2024 11:38:28 +0200
|
||||
Subject: [PATCH] wifi: ath11k: disable coldboot for ipq6018
|
||||
|
||||
Coldboot calibration does not work at the moment and causes failure during
|
||||
wifi startup.
|
||||
|
||||
Signed-off-by: Mantas Pucka <mantas@8devices.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/core.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||||
@@ -173,8 +173,8 @@ static struct ath11k_hw_params ath11k_hw
|
||||
.supports_shadow_regs = false,
|
||||
.idle_ps = false,
|
||||
.supports_sta_ps = false,
|
||||
- .coldboot_cal_mm = true,
|
||||
- .coldboot_cal_ftm = true,
|
||||
+ .coldboot_cal_mm = false,
|
||||
+ .coldboot_cal_ftm = false,
|
||||
.cbcal_restart_fw = true,
|
||||
.fw_mem_mode = 0,
|
||||
.num_vdevs = 16 + 1,
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
From b2d16b688ce04b67f2033f90f49f4add7ebd3fe8 Mon Sep 17 00:00:00 2001
|
||||
From: George Moussalem <george.moussalem@outlook.com>
|
||||
Date: Tue, 10 Jun 2025 14:34:37 +0400
|
||||
Subject: [PATCH] wifi: ath11k: disable coldboot calibration for ipq5018
|
||||
|
||||
Coldboot calibration does not work causes the firmware to crash during
|
||||
wifi startup. So let's disable coldboot calibration until a solution is
|
||||
found.
|
||||
|
||||
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
|
||||
---
|
||||
drivers/net/wireless/ath/ath11k/core.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||||
@@ -700,8 +700,8 @@ static struct ath11k_hw_params ath11k_hw
|
||||
.supports_suspend = false,
|
||||
.hal_params = &ath11k_hw_hal_params_ipq8074,
|
||||
.single_pdev_only = false,
|
||||
- .coldboot_cal_mm = true,
|
||||
- .coldboot_cal_ftm = true,
|
||||
+ .coldboot_cal_mm = false,
|
||||
+ .coldboot_cal_ftm = false,
|
||||
.cbcal_restart_fw = true,
|
||||
.fix_l1ss = true,
|
||||
.supports_dynamic_smps_6ghz = false,
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
From: Ziyang Huang <hzyitc@outlook.com>
|
||||
Date: Thu, 2 May 2024 00:14:31 +0800
|
||||
Subject: [PATCH] wifi: ath11k: fix remapped ce accessing issue on 64bit OS
|
||||
|
||||
On 64bit OS, when ab->mem_ce is lower than or 4G far away from ab->mem,
|
||||
u32 is not enough to store the offsets, which makes ath11k_ahb_read32()
|
||||
and ath11k_ahb_write32() access incorrect address and causes Data Abort
|
||||
Exception.
|
||||
|
||||
Let's use the high bits of offsets to decide where to access, which is
|
||||
similar as ath11k_pci_get_window_start() done. In the future, we can merge
|
||||
these functions for unified regs accessing.
|
||||
|
||||
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/ahb.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
|
||||
@@ -198,12 +198,18 @@ static const struct ath11k_pci_ops ath11
|
||||
|
||||
static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset)
|
||||
{
|
||||
- return ioread32(ab->mem + offset);
|
||||
+ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE)
|
||||
+ return ioread32(ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset));
|
||||
+ else
|
||||
+ return ioread32(ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset));
|
||||
}
|
||||
|
||||
static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
||||
{
|
||||
- iowrite32(value, ab->mem + offset);
|
||||
+ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE)
|
||||
+ iowrite32(value, ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset));
|
||||
+ else
|
||||
+ iowrite32(value, ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset));
|
||||
}
|
||||
|
||||
static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab)
|
||||
@@ -275,9 +281,9 @@ static void ath11k_ahb_ce_irq_enable(str
|
||||
const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr;
|
||||
u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;
|
||||
|
||||
- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
+ ie1_reg_addr = ce_ie_addr->ie1_reg_addr;
|
||||
+ ie2_reg_addr = ce_ie_addr->ie2_reg_addr;
|
||||
+ ie3_reg_addr = ce_ie_addr->ie3_reg_addr;
|
||||
|
||||
ce_attr = &ab->hw_params.host_ce_config[ce_id];
|
||||
if (ce_attr->src_nentries)
|
||||
@@ -296,9 +302,9 @@ static void ath11k_ahb_ce_irq_disable(st
|
||||
const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr;
|
||||
u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr;
|
||||
|
||||
- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab);
|
||||
+ ie1_reg_addr = ce_ie_addr->ie1_reg_addr;
|
||||
+ ie2_reg_addr = ce_ie_addr->ie2_reg_addr;
|
||||
+ ie3_reg_addr = ce_ie_addr->ie3_reg_addr;
|
||||
|
||||
ce_attr = &ab->hw_params.host_ce_config[ce_id];
|
||||
if (ce_attr->src_nentries)
|
||||
--- a/drivers/net/wireless/ath/ath11k/hal.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/hal.c
|
||||
@@ -1274,20 +1274,16 @@ static int ath11k_hal_srng_create_config
|
||||
s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP;
|
||||
|
||||
s = &hal->srng_config[HAL_CE_SRC];
|
||||
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB +
|
||||
- ATH11K_CE_OFFSET(ab);
|
||||
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP +
|
||||
- ATH11K_CE_OFFSET(ab);
|
||||
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
|
||||
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP;
|
||||
s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
|
||||
HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
|
||||
s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) -
|
||||
HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab);
|
||||
|
||||
s = &hal->srng_config[HAL_CE_DST];
|
||||
- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB +
|
||||
- ATH11K_CE_OFFSET(ab);
|
||||
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP +
|
||||
- ATH11K_CE_OFFSET(ab);
|
||||
+ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB;
|
||||
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP;
|
||||
s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
|
||||
HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
|
||||
s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
|
||||
@@ -1295,9 +1291,8 @@ static int ath11k_hal_srng_create_config
|
||||
|
||||
s = &hal->srng_config[HAL_CE_DST_STATUS];
|
||||
s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) +
|
||||
- HAL_CE_DST_STATUS_RING_BASE_LSB + ATH11K_CE_OFFSET(ab);
|
||||
- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP +
|
||||
- ATH11K_CE_OFFSET(ab);
|
||||
+ HAL_CE_DST_STATUS_RING_BASE_LSB;
|
||||
+ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP;
|
||||
s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
|
||||
HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab);
|
||||
s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) -
|
||||
--- a/drivers/net/wireless/ath/ath11k/hw.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/hw.c
|
||||
@@ -2268,9 +2268,9 @@ const struct ce_ie_addr ath11k_ce_ie_add
|
||||
};
|
||||
|
||||
const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018 = {
|
||||
- .ie1_reg_addr = CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
- .ie2_reg_addr = CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
- .ie3_reg_addr = CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
+ .ie1_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
+ .ie2_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
+ .ie3_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
};
|
||||
|
||||
const struct ce_remap ath11k_ce_remap_ipq5018 = {
|
||||
@@ -2801,13 +2801,13 @@ const struct ath11k_hw_regs ipq5018_regs
|
||||
.hal_reo_status_hp = 0x00003070,
|
||||
|
||||
/* WCSS relative address */
|
||||
- .hal_seq_wcss_umac_ce0_src_reg = 0x08400000
|
||||
+ .hal_seq_wcss_umac_ce0_src_reg = ATH11K_REG_TYPE_CE + 0x08400000
|
||||
- HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
- .hal_seq_wcss_umac_ce0_dst_reg = 0x08401000
|
||||
+ .hal_seq_wcss_umac_ce0_dst_reg = ATH11K_REG_TYPE_CE + 0x08401000
|
||||
- HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
- .hal_seq_wcss_umac_ce1_src_reg = 0x08402000
|
||||
+ .hal_seq_wcss_umac_ce1_src_reg = ATH11K_REG_TYPE_CE + 0x08402000
|
||||
- HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
- .hal_seq_wcss_umac_ce1_dst_reg = 0x08403000
|
||||
+ .hal_seq_wcss_umac_ce1_dst_reg = ATH11K_REG_TYPE_CE + 0x08403000
|
||||
- HAL_IPQ5018_CE_WFSS_REG_BASE,
|
||||
|
||||
/* WBM Idle address */
|
||||
--- a/drivers/net/wireless/ath/ath11k/hw.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/hw.h
|
||||
@@ -81,7 +81,12 @@
|
||||
#define ATH11K_M3_FILE "m3.bin"
|
||||
#define ATH11K_REGDB_FILE_NAME "regdb.bin"
|
||||
|
||||
-#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem)
|
||||
+#define ATH11K_REG_TYPE_MASK GENMASK(31, 28)
|
||||
+#define ATH11K_REG_TYPE(x) FIELD_PREP_CONST(ATH11K_REG_TYPE_MASK, x)
|
||||
+#define ATH11K_REG_TYPE_NORMAL ATH11K_REG_TYPE(0)
|
||||
+#define ATH11K_REG_TYPE_DP ATH11K_REG_TYPE(1)
|
||||
+#define ATH11K_REG_TYPE_CE ATH11K_REG_TYPE(2)
|
||||
+#define ATH11K_REG_OFFSET_MASK GENMASK(27, 0)
|
||||
|
||||
enum ath11k_hw_rate_cck {
|
||||
ATH11K_HW_RATE_CCK_LP_11M = 0,
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
From: George Moussalem <george.moussalem@outlook.com>
|
||||
Date: Wed, 27 Oct 2024 16:34:11 +0400
|
||||
Subject: [PATCH] wifi: ath11k: add hw params for QCN6122
|
||||
|
||||
Add QCN6122 platform support.
|
||||
|
||||
QCN6122 is a PCIe based solution that is attached to and enumerated
|
||||
by the WPSS (Wireless Processor SubSystem) Q6 processor.
|
||||
|
||||
Though it is a PCIe device, since it is not attached to APSS processor
|
||||
(Application Processor SubSystem), APSS will be unaware of such a decice
|
||||
and hence it is registered to the APSS processor as a platform device(AHB).
|
||||
Because of this hybrid nature, it is called as a hybrid bus device.
|
||||
|
||||
As such, QCN6122 is a hybrid bus type device and follows the same codepath
|
||||
as for WCN6750.
|
||||
|
||||
This is a heavily simplified version of below downstream patch:
|
||||
Download from https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch
|
||||
|
||||
Co-developed-by: George Moussalem <george.moussalem@outlook.com>
|
||||
Signed-off-by: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
|
||||
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||||
@@ -907,6 +907,67 @@ static struct ath11k_hw_params ath11k_hw
|
||||
.support_dual_stations = true,
|
||||
.pdev_suspend = false,
|
||||
},
|
||||
+ {
|
||||
+ .hw_rev = ATH11K_HW_QCN6122_HW10,
|
||||
+ .name = "qcn6122 hw1.0",
|
||||
+ .fw = {
|
||||
+ .dir = "QCN6122/hw1.0",
|
||||
+ .board_size = 256 * 1024,
|
||||
+ .cal_offset = 128 * 1024,
|
||||
+ },
|
||||
+ .hal_params = &ath11k_hw_hal_params_ipq8074,
|
||||
+ .max_radios = MAX_RADIOS_5018,
|
||||
+ .bdf_addr = 0x4D200000,
|
||||
+ .hw_ops = &ipq5018_ops,
|
||||
+ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
|
||||
+ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,
|
||||
+ .interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
+ BIT(NL80211_IFTYPE_AP) |
|
||||
+ BIT(NL80211_IFTYPE_MESH_POINT),
|
||||
+ .spectral = {
|
||||
+ .fft_sz = 2,
|
||||
+ .fft_pad_sz = 0,
|
||||
+ .summary_pad_sz = 16,
|
||||
+ .fft_hdr_len = 24,
|
||||
+ .max_fft_bins = 1024,
|
||||
+ },
|
||||
+ .credit_flow = false,
|
||||
+ .max_tx_ring = 1,
|
||||
+ .supports_monitor = true,
|
||||
+ .supports_shadow_regs = false,
|
||||
+ .idle_ps = false,
|
||||
+ .supports_suspend = false,
|
||||
+ .host_ce_config = ath11k_host_ce_config_qcn9074,
|
||||
+ .ce_count = CE_CNT_5018,
|
||||
+ .target_ce_config = ath11k_target_ce_config_wlan_ipq5018,
|
||||
+ .target_ce_count = TARGET_CE_CNT_5018,
|
||||
+ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018,
|
||||
+ .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018,
|
||||
+ .single_pdev_only = false,
|
||||
+ .rxdma1_enable = true,
|
||||
+ .num_rxdma_per_pdev = RXDMA_PER_PDEV_5018,
|
||||
+ .rx_mac_buf_ring = false,
|
||||
+ .vdev_start_delay = false,
|
||||
+ .htt_peer_map_v2 = true,
|
||||
+ .coldboot_cal_mm = false,
|
||||
+ .coldboot_cal_ftm = false,
|
||||
+ .cbcal_restart_fw = true,
|
||||
+ .fix_l1ss = true,
|
||||
+ .alloc_cacheable_memory = true,
|
||||
+ .m3_fw_support = false,
|
||||
+ .fixed_bdf_addr = true,
|
||||
+ .fixed_mem_region = true,
|
||||
+ .static_window_map = true,
|
||||
+ .hybrid_bus_type = true,
|
||||
+ .fw_mem_mode = 1,
|
||||
+ .supports_sta_ps = false,
|
||||
+ .dbr_debug_support = true,
|
||||
+ .bios_sar_capa = NULL,
|
||||
+ .fixed_fw_mem = false,
|
||||
+ .support_off_channel_tx = false,
|
||||
+ .tcl_ring_retry = true,
|
||||
+ .tx_ring_size = DP_TCL_DATA_RING_SIZE,
|
||||
+ },
|
||||
};
|
||||
|
||||
static const struct dmi_system_id ath11k_pm_quirk_table[] = {
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
||||
@@ -152,6 +152,7 @@ enum ath11k_hw_rev {
|
||||
ATH11K_HW_IPQ5018_HW10,
|
||||
ATH11K_HW_QCA2066_HW21,
|
||||
ATH11K_HW_QCA6698AQ_HW21,
|
||||
+ ATH11K_HW_QCN6122_HW10,
|
||||
};
|
||||
|
||||
enum ath11k_firmware_mode {
|
||||
--- a/drivers/net/wireless/ath/ath11k/qmi.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/qmi.h
|
||||
@@ -22,10 +22,11 @@
|
||||
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02
|
||||
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07
|
||||
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03
|
||||
+#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122 0x40
|
||||
#define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32
|
||||
#define ATH11K_QMI_RESP_LEN_MAX 8192
|
||||
#define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
|
||||
-#define ATH11K_QMI_CALDB_SIZE 0x480000
|
||||
+#define ATH11K_QMI_CALDB_SIZE 0x500000
|
||||
#define ATH11K_QMI_BDF_EXT_STR_LENGTH 0x20
|
||||
#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 5
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
From: George Moussalem <george.moussalem@outlook.com>
|
||||
Date: Wed, 27 Oct 2024 16:34:11 +0400
|
||||
Subject: [PATCH] wifi: ath11k: add hal regs for QCN6122
|
||||
|
||||
Add HAL changes required to support QCN6122. Offsets are similar to those of
|
||||
WCN6750 but QCN6122 does not use the hal_shadow_base_addr, so add platform
|
||||
specific ath11k_hw_regs and register them in hw params.
|
||||
|
||||
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
|
||||
---
|
||||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||||
@@ -920,6 +920,7 @@ static struct ath11k_hw_params ath11k_hw
|
||||
.bdf_addr = 0x4D200000,
|
||||
.hw_ops = &ipq5018_ops,
|
||||
.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
|
||||
+ .regs = &qcn6122_regs,
|
||||
.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,
|
||||
.interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
--- a/drivers/net/wireless/ath/ath11k/hw.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/hw.c
|
||||
@@ -2822,6 +2822,81 @@ const struct ath11k_hw_regs ipq5018_regs
|
||||
.hal_wbm1_release_ring_base_lsb = 0x0000097c,
|
||||
};
|
||||
|
||||
+const struct ath11k_hw_regs qcn6122_regs = {
|
||||
+ /* SW2TCL(x) R0 ring configuration address */
|
||||
+ .hal_tcl1_ring_base_lsb = 0x00000694,
|
||||
+ .hal_tcl1_ring_base_msb = 0x00000698,
|
||||
+ .hal_tcl1_ring_id = 0x0000069c,
|
||||
+ .hal_tcl1_ring_misc = 0x000006a4,
|
||||
+ .hal_tcl1_ring_tp_addr_lsb = 0x000006b0,
|
||||
+ .hal_tcl1_ring_tp_addr_msb = 0x000006b4,
|
||||
+ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4,
|
||||
+ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8,
|
||||
+ .hal_tcl1_ring_msi1_base_lsb = 0x000006dc,
|
||||
+ .hal_tcl1_ring_msi1_base_msb = 0x000006e0,
|
||||
+ .hal_tcl1_ring_msi1_data = 0x000006e4,
|
||||
+ .hal_tcl2_ring_base_lsb = 0x000006ec,
|
||||
+ .hal_tcl_ring_base_lsb = 0x0000079c,
|
||||
+
|
||||
+ /* TCL STATUS ring address */
|
||||
+ .hal_tcl_status_ring_base_lsb = 0x000008a4,
|
||||
+
|
||||
+ /* REO2SW(x) R0 ring configuration address */
|
||||
+ .hal_reo1_ring_base_lsb = 0x000001ec,
|
||||
+ .hal_reo1_ring_base_msb = 0x000001f0,
|
||||
+ .hal_reo1_ring_id = 0x000001f4,
|
||||
+ .hal_reo1_ring_misc = 0x000001fc,
|
||||
+ .hal_reo1_ring_hp_addr_lsb = 0x00000200,
|
||||
+ .hal_reo1_ring_hp_addr_msb = 0x00000204,
|
||||
+ .hal_reo1_ring_producer_int_setup = 0x00000210,
|
||||
+ .hal_reo1_ring_msi1_base_lsb = 0x00000234,
|
||||
+ .hal_reo1_ring_msi1_base_msb = 0x00000238,
|
||||
+ .hal_reo1_ring_msi1_data = 0x0000023c,
|
||||
+ .hal_reo2_ring_base_lsb = 0x00000244,
|
||||
+ .hal_reo1_aging_thresh_ix_0 = 0x00000564,
|
||||
+ .hal_reo1_aging_thresh_ix_1 = 0x00000568,
|
||||
+ .hal_reo1_aging_thresh_ix_2 = 0x0000056c,
|
||||
+ .hal_reo1_aging_thresh_ix_3 = 0x00000570,
|
||||
+
|
||||
+ /* REO2SW(x) R2 ring pointers (head/tail) address */
|
||||
+ .hal_reo1_ring_hp = 0x00003028,
|
||||
+ .hal_reo1_ring_tp = 0x0000302c,
|
||||
+ .hal_reo2_ring_hp = 0x00003030,
|
||||
+
|
||||
+ /* REO2TCL R0 ring configuration address */
|
||||
+ .hal_reo_tcl_ring_base_lsb = 0x000003fc,
|
||||
+ .hal_reo_tcl_ring_hp = 0x00003058,
|
||||
+
|
||||
+ /* SW2REO ring address */
|
||||
+ .hal_sw2reo_ring_base_lsb = 0x0000013c,
|
||||
+ .hal_sw2reo_ring_hp = 0x00003018,
|
||||
+
|
||||
+ /* REO CMD ring address */
|
||||
+ .hal_reo_cmd_ring_base_lsb = 0x000000e4,
|
||||
+ .hal_reo_cmd_ring_hp = 0x00003010,
|
||||
+
|
||||
+ /* REO status address */
|
||||
+ .hal_reo_status_ring_base_lsb = 0x00000504,
|
||||
+ .hal_reo_status_hp = 0x00003070,
|
||||
+
|
||||
+ /* WCSS relative address */
|
||||
+ .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000,
|
||||
+ .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000,
|
||||
+ .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000,
|
||||
+ .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000,
|
||||
+
|
||||
+ /* WBM Idle address */
|
||||
+ .hal_wbm_idle_link_ring_base_lsb = 0x00000874,
|
||||
+ .hal_wbm_idle_link_ring_misc = 0x00000884,
|
||||
+
|
||||
+ /* SW2WBM release address */
|
||||
+ .hal_wbm_release_ring_base_lsb = 0x000001ec,
|
||||
+
|
||||
+ /* WBM2SW release address */
|
||||
+ .hal_wbm0_release_ring_base_lsb = 0x00000924,
|
||||
+ .hal_wbm1_release_ring_base_lsb = 0x0000097c,
|
||||
+};
|
||||
+
|
||||
const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = {
|
||||
.rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM,
|
||||
.tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074,
|
||||
--- a/drivers/net/wireless/ath/ath11k/hw.h
|
||||
+++ b/drivers/net/wireless/ath/ath11k/hw.h
|
||||
@@ -426,6 +426,7 @@ extern const struct ath11k_hw_regs qcn90
|
||||
extern const struct ath11k_hw_regs wcn6855_regs;
|
||||
extern const struct ath11k_hw_regs wcn6750_regs;
|
||||
extern const struct ath11k_hw_regs ipq5018_regs;
|
||||
+extern const struct ath11k_hw_regs qcn6122_regs;
|
||||
|
||||
static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
|
||||
{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user