check: support tcp check, use checktype & checkaddr instead of checkwebsite

This commit is contained in:
nadoo 2020-11-17 20:46:55 +08:00
parent e6e5c3d4b6
commit 1173f533ec
6 changed files with 80 additions and 29 deletions

View File

@ -44,7 +44,8 @@ func parseConfig() *Config {
flag.StringSliceUniqVar(&conf.Forwards, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]")
flag.StringVar(&conf.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "fowarder check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
flag.StringVar(&conf.StrategyConfig.CheckType, "checktype", "http", "fowarder check type, http/tcp, default: http")
flag.StringVar(&conf.StrategyConfig.CheckAddr, "checkaddr", "www.apple.com:80", "fowarder check addr, format: HOST[:PORT], default port: 80,")
flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "fowarder check interval(seconds)")
flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "fowarder check timeout(seconds)")
flag.IntVar(&conf.StrategyConfig.CheckTolerance, "checktolerance", 0, "fowarder check tolerance(ms), switch only when new_latency < old_latency - tolerance, only used in lha mode")

5
go.mod
View File

@ -14,8 +14,9 @@ require (
github.com/nadoo/ipset v0.3.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/tools v0.0.0-20201111224557-41a3a589386c // indirect
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582
golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 // indirect
golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
)

13
go.sum
View File

@ -87,8 +87,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 h1:0WDrJ1E7UolDk1KhTXxxw3Fc8qtk5x7dHP431KHEJls=
golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582/go.mod h1:tCqSYrHVcf3i63Co2FzBkTCo2gdF6Zak62921dSfraU=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -119,6 +119,7 @@ golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -127,6 +128,10 @@ golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 h1:AYCWBZhgIw6XobZ5CibNJr0Rc4ZofGGKvWa1vcx2IGk=
golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201113234701-d7a72108b828 h1:htWEtQEuEVJ4tU/Ngx7Cd/4Q7e3A5Up1owgyBtVsTwk=
golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -136,8 +141,8 @@ golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174 h1:0rx0F4EjJNbxTuzWe0KjKcIzs+3VEb/Mrs/d1ciNz1c=
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201111224557-41a3a589386c h1:o4Kpkl2kZqc7n22gkunVhOJ+Ys5u3NrnXKAr2OgO5s0=
golang.org/x/tools v0.0.0-20201111224557-41a3a589386c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10 h1:epqY6OjPdDktZ8Cbnv7rUhy89e44hYWhxmhdecJr4cg=
golang.org/x/tools v0.0.0-20201117021029-3c3a81204b10/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -18,7 +18,7 @@ import (
)
var (
version = "0.12.3"
version = "0.13.0"
config = parseConfig()
)

View File

@ -26,7 +26,8 @@ type Config struct {
// StrategyConfig is config of strategy.
type StrategyConfig struct {
Strategy string
CheckWebSite string
CheckType string
CheckAddr string
CheckInterval int
CheckTimeout int
CheckTolerance int
@ -44,7 +45,8 @@ func NewConfFromFile(ruleFile string) (*Config, error) {
f := conflag.NewFromFile("rule", ruleFile)
f.StringSliceUniqVar(&p.Forward, "forward", nil, "forward url, format: SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS[,SCHEME://[USER|METHOD:PASSWORD@][HOST]:PORT?PARAMS]")
f.StringVar(&p.StrategyConfig.Strategy, "strategy", "rr", "forward strategy, default: rr")
f.StringVar(&p.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "fowarder check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
f.StringVar(&p.StrategyConfig.CheckType, "checktype", "http", "fowarder check type, http/tcp, default: http")
f.StringVar(&p.StrategyConfig.CheckAddr, "checkaddr", "www.apple.com:80", "fowarder check addr, format: HOST[:PORT], default port: 80,")
f.IntVar(&p.StrategyConfig.CheckInterval, "checkinterval", 30, "fowarder check interval(seconds)")
f.IntVar(&p.StrategyConfig.CheckTimeout, "checktimeout", 10, "fowarder check timeout(seconds)")
f.IntVar(&p.StrategyConfig.CheckTolerance, "checktolerance", 0, "fowarder check tolerance(ms), switch only when new_latency < old_latency - tolerance, only used in lha mode")

View File

@ -63,8 +63,8 @@ func newFwdrGroup(name string, fwdrs []*Forwarder, c *StrategyConfig) *FwdrGroup
p.init()
if strings.IndexByte(p.config.CheckWebSite, ':') == -1 {
p.config.CheckWebSite += ":80"
if strings.IndexByte(p.config.CheckAddr, ':') == -1 {
p.config.CheckAddr += ":80"
}
// default scheduler
@ -180,15 +180,20 @@ func (p *FwdrGroup) onStatusChanged(fwdr *Forwarder) {
// Check implements the Checker interface.
func (p *FwdrGroup) Check() {
if p.config.CheckType != "http" && p.config.CheckType != "tcp" {
p.config.MaxFailures = 0
return
}
// no need to check when there's only 1 forwarder
if len(p.fwdrs) > 1 {
for i := 0; i < len(p.fwdrs); i++ {
go p.check(p.fwdrs[i])
go p.check(p.fwdrs[i], p.config.CheckType == "http")
}
}
}
func (p *FwdrGroup) check(f *Forwarder) {
func (p *FwdrGroup) check(f *Forwarder, http bool) {
wait := uint8(0)
buf := make([]byte, 4)
intval := time.Duration(p.config.CheckInterval) * time.Second
@ -205,9 +210,16 @@ func (p *FwdrGroup) check(f *Forwarder) {
continue
}
if checkWebSite(f, p.config.CheckWebSite, time.Duration(p.config.CheckTimeout)*time.Second, buf) {
wait = 1
continue
if http {
if checkHttp(f, p.config.CheckAddr, time.Duration(p.config.CheckTimeout)*time.Second, buf) {
wait = 1
continue
}
} else {
if checkTcp(f, p.config.CheckAddr, time.Duration(p.config.CheckTimeout)*time.Second) {
wait = 1
continue
}
}
if wait == 0 {
@ -221,12 +233,12 @@ func (p *FwdrGroup) check(f *Forwarder) {
}
}
func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []byte) bool {
func checkTcp(fwdr *Forwarder, addr string, timeout time.Duration) bool {
startTime := time.Now()
rc, err := fwdr.Dial("tcp", website)
rc, err := fwdr.Dial("tcp", addr)
if err != nil {
log.F("[check] %s(%d) -> %s, FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), website, err)
log.F("[check-tcp] %s(%d), FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), err)
fwdr.Disable()
return false
}
@ -236,36 +248,66 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
rc.SetDeadline(time.Now().Add(timeout))
}
_, err = io.WriteString(rc, "GET / HTTP/1.1\r\nHost:"+website+"\r\nConnection: close"+"\r\n\r\n")
elapsed := time.Since(startTime)
fwdr.SetLatency(int64(elapsed))
if elapsed > timeout {
log.F("[check-tcp] %s(%d), FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), elapsed)
fwdr.Disable()
return false
}
log.F("[check-tcp] %s(%d), SUCCESS. elapsed: %s", fwdr.Addr(), fwdr.Priority(), elapsed)
fwdr.Enable()
return true
}
func checkHttp(fwdr *Forwarder, addr string, timeout time.Duration, buf []byte) bool {
startTime := time.Now()
rc, err := fwdr.Dial("tcp", addr)
if err != nil {
log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), website, err)
log.F("[check] %s(%d) -> %s, FAILED. error in dial: %s", fwdr.Addr(), fwdr.Priority(), addr, err)
fwdr.Disable()
return false
}
defer rc.Close()
if timeout > 0 {
rc.SetDeadline(time.Now().Add(timeout))
}
_, err = io.WriteString(rc, "GET / HTTP/1.1\r\nHost:"+addr+"\r\nConnection: close"+"\r\n\r\n")
if err != nil {
log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), addr, err)
fwdr.Disable()
return false
}
_, err = io.ReadFull(rc, buf)
if err != nil {
log.F("[check] %s(%d) -> %s, FAILED. error in read: %s", fwdr.Addr(), fwdr.Priority(), website, err)
log.F("[check] %s(%d) -> %s, FAILED. error in read: %s", fwdr.Addr(), fwdr.Priority(), addr, err)
fwdr.Disable()
return false
}
if !bytes.Equal([]byte("HTTP"), buf) {
log.F("[check] %s(%d) -> %s, FAILED. server response: %s", fwdr.Addr(), fwdr.Priority(), website, buf)
log.F("[check] %s(%d) -> %s, FAILED. server response: %s", fwdr.Addr(), fwdr.Priority(), addr, buf)
fwdr.Disable()
return false
}
readTime := time.Since(startTime)
fwdr.SetLatency(int64(readTime))
elapsed := time.Since(startTime)
fwdr.SetLatency(int64(elapsed))
if readTime > timeout {
log.F("[check] %s(%d) -> %s, FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), website, readTime)
if elapsed > timeout {
log.F("[check] %s(%d) -> %s, FAILED. check timeout: %s", fwdr.Addr(), fwdr.Priority(), addr, elapsed)
fwdr.Disable()
return false
}
log.F("[check] %s(%d) -> %s, SUCCESS. elapsed time: %s", fwdr.Addr(), fwdr.Priority(), website, readTime)
log.F("[check] %s(%d) -> %s, SUCCESS. elapsed: %s", fwdr.Addr(), fwdr.Priority(), addr, elapsed)
fwdr.Enable()
return true