diff --git a/README.md b/README.md index 6f9df1a..6a2a434 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,7 @@ https://t.me/shadowrocket_unofficial ## 免责 -MIT协议!作者不负任何责任。本项目只是个代理项目,适合内网测试使用,以及适合阅读代码了解原理。 +MIT协议!作者不负任何责任。本项目只是个poc项目,适合内网测试使用,以及适合阅读代码了解原理。 你如果用于任何其它目的,我们不会帮助你。 diff --git a/go.sum b/go.sum index 3e5b46e..5bacc15 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/netLayer/addr.go b/netLayer/addr.go index 069f3ac..420a0d1 100644 --- a/netLayer/addr.go +++ b/netLayer/addr.go @@ -125,6 +125,16 @@ func (a *Addr) UrlString() string { return a.Network + "://" + url.PathEscape(str) } +func (a *Addr) GetNetIPAddr() (na netip.Addr) { + if len(a.IP) < 1 { + return + } + + na, _ = netip.AddrFromSlice(a.IP) + + return +} + func (a *Addr) ToUDPAddr() *net.UDPAddr { switch a.Network { case "udp", "udp4", "udp6": diff --git a/netLayer/cidr_test.go b/netLayer/cidr_test.go new file mode 100644 index 0000000..5403381 --- /dev/null +++ b/netLayer/cidr_test.go @@ -0,0 +1,152 @@ +package netLayer + +import ( + "net" + "net/netip" + "strconv" + "testing" + + "github.com/yl2chen/cidranger" +) + +/* + +go1.18 +goos: darwin +goarch: arm64 +Benchmark_CIDR_ranger-8 30880119 38.38 ns/op +Benchmark_CIDR200_ranger-8 10963292 107.9 ns/op +Benchmark_CIDR_netIPList-8 341961285 3.507 ns/op +Benchmark_CIDR60_netIPList-8 11371914 105.4 ns/op +Benchmark_CIDR200_netIPList-8 3625110 331.4 ns/op + +60个以内时,直接用 netip.Prefix 列表进行遍历更快;其他情况 cidranger 更快; + +考虑到我们国别分流直接用的mmdb,而不是自己给ip段, 而如果要自定义ip段的话,很少有能自定义好几十个点,所以这个确实可以直接用列表优化一下。 不过依然是纳秒级,意义不大 +*/ + +func Benchmark_CIDR_ranger(b *testing.B) { + b.StopTimer() + b.ResetTimer() + + theRange := "192.168.1.0/24" + theIPStr := "192.168.1.23" + theIP := net.ParseIP(theIPStr) + netRanger := cidranger.NewPCTrieRanger() + if _, net, err := net.ParseCIDR(theRange); err == nil { + netRanger.Insert(cidranger.NewBasicRangerEntry(*net)) + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + netRanger.Contains(theIP) + } + +} + +func Benchmark_CIDR200_ranger(b *testing.B) { + b.StopTimer() + b.ResetTimer() + netRanger := cidranger.NewPCTrieRanger() + + theIPStr := "192.168.1.23" + theIP := net.ParseIP(theIPStr) + + for i := 0; i < 200; i++ { + theRange := "192.168." + strconv.Itoa(i) + ".0/24" + + if _, net, err := net.ParseCIDR(theRange); err == nil { + netRanger.Insert(cidranger.NewBasicRangerEntry(*net)) + } + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + netRanger.Contains(theIP) + } + +} + +func Benchmark_CIDR_netIPList(b *testing.B) { + + b.StopTimer() + b.ResetTimer() + + ipStr := "192.168.1.0/24" + ipStr2 := "192.168.1.23" + theIP, err := netip.ParseAddr(ipStr2) + if err != nil { + b.Log(err) + b.FailNow() + } + + thelist := make([]netip.Prefix, 0, 10) + + ipnet, err := netip.ParsePrefix(ipStr) + if err != nil { + b.Log(err) + b.FailNow() + } + + thelist = append(thelist, ipnet) + + b.StartTimer() + + for i := 0; i < b.N; i++ { + for _, n := range thelist { + if n.Contains(theIP) { + break + } + } + } + +} + +func Benchmark_CIDR60_netIPList(b *testing.B) { + benchmark_CIDR_netIPList(b, 60) + +} +func Benchmark_CIDR200_netIPList(b *testing.B) { + benchmark_CIDR_netIPList(b, 200) + +} + +func benchmark_CIDR_netIPList(b *testing.B, num int) { + + b.StopTimer() + b.ResetTimer() + + theIPStr := "192.168." + strconv.Itoa(num/2) + ".23" + theIP, err := netip.ParseAddr(theIPStr) + if err != nil { + b.Log(err) + b.FailNow() + } + + thelist := make([]netip.Prefix, num) + + for i := 0; i < num; i++ { + theRange := "192.168." + strconv.Itoa(i) + ".0/24" + + ipnet, err := netip.ParsePrefix(theRange) + if err != nil { + b.Log(err) + b.FailNow() + } + + thelist[i] = ipnet + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + for _, n := range thelist { + if n.Contains(theIP) { + break + } + } + } + +} diff --git a/netLayer/const.go b/netLayer/const.go index d32b315..7741817 100644 --- a/netLayer/const.go +++ b/netLayer/const.go @@ -1,7 +1,7 @@ package netLayer const ( - // transport Layer, 使用uint16 mask,所以最多支持16种 + // Transport Layer Protocols, 使用uint16 mask,所以最多支持16种 TCP uint16 = 1 << iota UDP @@ -16,9 +16,9 @@ const ( func StrToTransportProtocol(s string) uint16 { switch s { - case "tcp": + case "tcp", "tcp4", "tcp6": return TCP - case "udp": + case "udp", "udp4", "udp6": return UDP case "unix": return UNIX @@ -28,7 +28,6 @@ func StrToTransportProtocol(s string) uint16 { return KCP case "quic": return Quic - } return 0 } diff --git a/netLayer/geoip.go b/netLayer/geoip.go index ef50a52..c514ead 100644 --- a/netLayer/geoip.go +++ b/netLayer/geoip.go @@ -29,7 +29,7 @@ func HasEmbedGeoip() bool { func loadMaxmindGeoipBytes(bs []byte) { db, err := maxminddb.FromBytes(bs) if err != nil { - log.Fatalln("loadMaxmindGeoipBytes", err) + log.Fatalln("err when loadMaxmindGeoipBytes", err) } the_geoipdb = db } diff --git a/netLayer/mmdb_test.go b/netLayer/mmdb_test.go new file mode 100644 index 0000000..88a8372 --- /dev/null +++ b/netLayer/mmdb_test.go @@ -0,0 +1,35 @@ +package netLayer + +import ( + "log" + "net" + "testing" + + "github.com/hahahrfool/v2ray_simple/utils" +) + +/* go test -bench "CheckMMDB_country" . -v +BenchmarkCheckMMDB_country-8 3631854 315.3 ns/op + +总之一次mmdb查询比map查询慢了十倍多 (见 utils/container_test.go.bak) + +有必要设置一个 国别-ip 的map缓存; 不过这种纳秒级别的优化就无所谓了 +*/ + +func BenchmarkCheckMMDB_country(b *testing.B) { + b.StopTimer() + b.ResetTimer() + LoadMaxmindGeoipFile(utils.GetFilePath("../" + GeoipFileName)) + + if the_geoipdb == nil { + log.Fatalln("err load") + } + + theIP := net.ParseIP("1.22.233.44") + + b.StartTimer() + + for i := 0; i < b.N; i++ { + GetIP_ISO(theIP) + } +} diff --git a/netLayer/readv_test.go b/netLayer/readv_test.go index 3d9175e..cf9ed33 100644 --- a/netLayer/readv_test.go +++ b/netLayer/readv_test.go @@ -357,11 +357,11 @@ func BenchmarkClassicCopy_SimulateRealWorld_ReadV(b *testing.B) { b.Log(err) b.FailNow() } + unit := bigBytesLen / 10 b.StartTimer() for i := 0; i < transmitCount; i++ { - unit := bigBytesLen / 10 for cursor := 0; cursor < bigBytesLen; cursor += unit { _, e := tcpConn.Write(bigBytes[cursor : cursor+unit]) diff --git a/netLayer/route.go b/netLayer/route.go index bb70adb..a7bdef7 100644 --- a/netLayer/route.go +++ b/netLayer/route.go @@ -1,7 +1,7 @@ package netLayer import ( - "net" + "net/netip" "strings" "github.com/yl2chen/cidranger" @@ -18,18 +18,17 @@ type TargetDescription struct { // 这里的相同点,就是它们同属于 将发往一个方向, 即同属一个路由策略 // 任意一个参数匹配后,都将发往相同的方向,由该方向OutTag 指定 // RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起 -// -// 目前先使用map,以后优化时再考虑使用 能加速查找 的数据结构 type RouteSet struct { //网络层 - NetRanger cidranger.Ranger //一个范围 - IPs map[string]net.IP //一个确定值 - Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key + NetRanger cidranger.Ranger //一个范围 + IPs map[netip.Addr]bool //一个确定值 + Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key //传输层 AllowedTransportLayerProtocols uint16 OutTag string //目标 + } func NewRouteSetForMyCountry(iso string) *RouteSet { @@ -51,7 +50,7 @@ func NewRouteSetForMyCountry(iso string) *RouteSet { func NewFullRouteSet() *RouteSet { return &RouteSet{ NetRanger: cidranger.NewPCTrieRanger(), - IPs: make(map[string]net.IP), + IPs: make(map[netip.Addr]bool), Domains: make(map[string]bool), InTags: make(map[string]bool), Countries: make(map[string]bool), @@ -60,7 +59,7 @@ func NewFullRouteSet() *RouteSet { } func (sg *RouteSet) IsIn(td *TargetDescription) bool { - if td.Tag != "" { + if td.Tag != "" && sg.InTags != nil { if _, found := sg.InTags[td.Tag]; found { return true } @@ -89,16 +88,15 @@ func (sg *RouteSet) IsTCPAllowed() bool { func (sg *RouteSet) IsAddrIn(a *Addr) bool { //我们先过滤传输层,再过滤网络层 - //目前我们仅支持udp和tcp这两种传输层协议,所以可以如此。以后如果加了更多的话,还需改动 if !sg.IsAddrNetworkAllowed(a) { return false - } else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil { + } else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.Countries == nil { //如果仅限制了一个传输层协议,且本集合里没有任何其它内容,那就直接通过 return true } //开始网络层判断 - if a.IP != nil { + if len(a.IP) > 0 { if sg.NetRanger != nil { if has, _ := sg.NetRanger.Contains(a.IP); has { return true @@ -114,7 +112,7 @@ func (sg *RouteSet) IsAddrIn(a *Addr) bool { } if sg.IPs != nil { - if _, found := sg.IPs[a.IP.To16().String()]; found { + if _, found := sg.IPs[a.GetNetIPAddr()]; found { return true } } diff --git a/proxy/config_standard.go b/proxy/config_standard.go index 310fd07..6a2ece8 100644 --- a/proxy/config_standard.go +++ b/proxy/config_standard.go @@ -3,6 +3,7 @@ package proxy import ( "io/ioutil" "net" + "net/netip" "os" "strconv" "strings" @@ -154,9 +155,10 @@ func LoadRuleForRouteSet(rule *RuleConf) (rs *netLayer.RouteSet) { } continue } - if ip := net.ParseIP(ipStr); ip != nil { - rs.IPs[ipStr] = ip - continue + + na, e := netip.ParseAddr(ipStr) + if e == nil { + rs.IPs[na] = true } } diff --git a/utils/container_test.go.bak b/utils/container_test.go.bak new file mode 100644 index 0000000..23cd5cd --- /dev/null +++ b/utils/container_test.go.bak @@ -0,0 +1,194 @@ +package utils_test + +import ( + "math/rand" + "testing" + "unsafe" + + "github.com/bits-and-blooms/bloom/v3" +) + +/* +go1.18 +goos: darwin +goarch: arm64 +pkg: github.com/hahahrfool/v2ray_simple/utils +BenchmarkSliceFind_10-8 152484100 7.447 ns/op +BenchmarkSliceFind_20-8 100000000 10.43 ns/op +BenchmarkSliceFind_40-8 73516086 16.45 ns/op +BenchmarkSliceFind_80-8 40491806 30.28 ns/op +BenchmarkMapFind_10-8 131343604 8.255 ns/op +BenchmarkMapFind_20-8 77772673 13.84 ns/op +BenchmarkMapFind_40-8 78834778 14.75 ns/op +BenchmarkMapFind_80-8 77047785 15.59 ns/op +BenchmarkMapFind_800-8 78424113 14.88 ns/op +BenchmarkMapFind_8000-8 78607766 15.09 ns/op +BenchmarkMapFind_800000-8 51825218 21.70 ns/op +BenchmarkMapFind_80000000-8 21130418 57.20 ns/op +BenchmarkBloomFind_10-8 134930332 8.418 ns/op +BenchmarkBloomFind_20-8 78121606 16.51 ns/op +BenchmarkBloomFind_40-8 73593855 19.28 ns/op +BenchmarkBloomFind_80-8 81744565 14.12 ns/op +BenchmarkBloomFind_800-8 79790108 15.19 ns/op +BenchmarkBloomFind_8000-8 78823557 15.27 ns/op +BenchmarkBloomFind_800000-8 53090004 21.57 ns/op +BenchmarkBloomFind_80000000-8 21073072 56.83 ns/op +PASS +ok github.com/hahahrfool/v2ray_simple/utils 147.920s + + +总之数量小于四十时, 直接用数组循环查找是更快的,但是几纳秒的节约没有实际意义; + +布隆过滤器没测出比map强在哪里,而且还可能假阳性,暂不考虑; 结论是直接用map匹配即可,无需任何其它机制。 + +*/ + +func BenchmarkSliceFind_10(b *testing.B) { + benchmarkSliceFind(b, 10) +} +func BenchmarkSliceFind_20(b *testing.B) { + benchmarkSliceFind(b, 20) +} +func BenchmarkSliceFind_40(b *testing.B) { + benchmarkSliceFind(b, 40) +} +func BenchmarkSliceFind_80(b *testing.B) { + benchmarkSliceFind(b, 80) +} + +func BenchmarkMapFind_10(b *testing.B) { + benchmarkMapFind(b, 10) +} +func BenchmarkMapFind_20(b *testing.B) { + benchmarkMapFind(b, 20) +} +func BenchmarkMapFind_40(b *testing.B) { + benchmarkMapFind(b, 40) +} +func BenchmarkMapFind_80(b *testing.B) { + benchmarkMapFind(b, 80) +} + +func BenchmarkMapFind_800(b *testing.B) { + benchmarkMapFind(b, 800) +} +func BenchmarkMapFind_8000(b *testing.B) { + benchmarkMapFind(b, 8000) +} + +func BenchmarkMapFind_800000(b *testing.B) { + benchmarkMapFind(b, 800000) +} +func BenchmarkMapFind_80000000(b *testing.B) { + benchmarkMapFind(b, 80000000) +} + +func benchmarkSliceFind(b *testing.B, size int) { + b.StopTimer() + b.ResetTimer() + s := make([]int, size) + for i := range s { + s[i] = rand.Intn(size) + } + + randBuf := make([]int, b.N) + + for i := 0; i < b.N; i++ { + randBuf[i] = rand.Intn(size) + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + for j := range s { + if s[j] == randBuf[i] { + break + } + } + } +} + +func BenchmarkBloomFind_10(b *testing.B) { + benchmarkMapFind(b, 10) +} + +func BenchmarkBloomFind_20(b *testing.B) { + benchmarkMapFind(b, 20) +} + +func BenchmarkBloomFind_40(b *testing.B) { + benchmarkMapFind(b, 40) +} + +func BenchmarkBloomFind_80(b *testing.B) { + benchmarkMapFind(b, 80) +} + +func BenchmarkBloomFind_800(b *testing.B) { + benchmarkMapFind(b, 800) +} +func BenchmarkBloomFind_8000(b *testing.B) { + benchmarkMapFind(b, 8000) +} + +func BenchmarkBloomFind_800000(b *testing.B) { + benchmarkMapFind(b, 800000) +} +func BenchmarkBloomFind_80000000(b *testing.B) { + benchmarkMapFind(b, 80000000) +} + +func benchmarkMapFind(b *testing.B, size int) { + b.StopTimer() + b.ResetTimer() + s := make(map[int]bool) + for i := 0; i < size; i++ { + s[rand.Intn(size)] = true + } + + randBuf := make([]int, b.N) + + for i := 0; i < b.N; i++ { + randBuf[i] = rand.Intn(size) + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + + if s[randBuf[i]] { + + } + + } +} + +func benchmarkBloomFind(b *testing.B, size uint32) { + b.StopTimer() + b.ResetTimer() + s := bloom.NewWithEstimates(uint(b.N), 0.01) + + var curRand uint32 + + for i := 0; i < int(size); i++ { + curRand = uint32(rand.Intn(int(size))) + + s.Add((*(*[4]byte)(unsafe.Pointer(&curRand)))[:]) + } + + randBuf := make([]int, b.N) + + for i := 0; i < b.N; i++ { + randBuf[i] = rand.Intn(int(size)) + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + + if s.Test((*(*[4]byte)(unsafe.Pointer(&randBuf[i])))[:]) { + + } + + } +}