在route中使用netip.Addr;修订代码;添加一些go test

This commit is contained in:
hahahrfool
2022-03-23 17:37:23 +08:00
parent 2c21d62f02
commit a40a343510
11 changed files with 412 additions and 24 deletions

View File

@@ -400,7 +400,7 @@ https://t.me/shadowrocket_unofficial
## 免责 ## 免责
MIT协议作者不负任何责任。本项目只是个代理项目,适合内网测试使用,以及适合阅读代码了解原理。 MIT协议作者不负任何责任。本项目只是个poc项目,适合内网测试使用,以及适合阅读代码了解原理。
你如果用于任何其它目的,我们不会帮助你。 你如果用于任何其它目的,我们不会帮助你。

2
go.sum
View File

@@ -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/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 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= 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 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= 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= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -125,6 +125,16 @@ func (a *Addr) UrlString() string {
return a.Network + "://" + url.PathEscape(str) 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 { func (a *Addr) ToUDPAddr() *net.UDPAddr {
switch a.Network { switch a.Network {
case "udp", "udp4", "udp6": case "udp", "udp4", "udp6":

152
netLayer/cidr_test.go Normal file
View File

@@ -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
}
}
}
}

View File

@@ -1,7 +1,7 @@
package netLayer package netLayer
const ( const (
// transport Layer, 使用uint16 mask所以最多支持16种 // Transport Layer Protocols, 使用uint16 mask所以最多支持16种
TCP uint16 = 1 << iota TCP uint16 = 1 << iota
UDP UDP
@@ -16,9 +16,9 @@ const (
func StrToTransportProtocol(s string) uint16 { func StrToTransportProtocol(s string) uint16 {
switch s { switch s {
case "tcp": case "tcp", "tcp4", "tcp6":
return TCP return TCP
case "udp": case "udp", "udp4", "udp6":
return UDP return UDP
case "unix": case "unix":
return UNIX return UNIX
@@ -28,7 +28,6 @@ func StrToTransportProtocol(s string) uint16 {
return KCP return KCP
case "quic": case "quic":
return Quic return Quic
} }
return 0 return 0
} }

View File

@@ -29,7 +29,7 @@ func HasEmbedGeoip() bool {
func loadMaxmindGeoipBytes(bs []byte) { func loadMaxmindGeoipBytes(bs []byte) {
db, err := maxminddb.FromBytes(bs) db, err := maxminddb.FromBytes(bs)
if err != nil { if err != nil {
log.Fatalln("loadMaxmindGeoipBytes", err) log.Fatalln("err when loadMaxmindGeoipBytes", err)
} }
the_geoipdb = db the_geoipdb = db
} }

35
netLayer/mmdb_test.go Normal file
View File

@@ -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)
}
}

View File

@@ -357,11 +357,11 @@ func BenchmarkClassicCopy_SimulateRealWorld_ReadV(b *testing.B) {
b.Log(err) b.Log(err)
b.FailNow() b.FailNow()
} }
unit := bigBytesLen / 10
b.StartTimer() b.StartTimer()
for i := 0; i < transmitCount; i++ { for i := 0; i < transmitCount; i++ {
unit := bigBytesLen / 10
for cursor := 0; cursor < bigBytesLen; cursor += unit { for cursor := 0; cursor < bigBytesLen; cursor += unit {
_, e := tcpConn.Write(bigBytes[cursor : cursor+unit]) _, e := tcpConn.Write(bigBytes[cursor : cursor+unit])

View File

@@ -1,7 +1,7 @@
package netLayer package netLayer
import ( import (
"net" "net/netip"
"strings" "strings"
"github.com/yl2chen/cidranger" "github.com/yl2chen/cidranger"
@@ -18,18 +18,17 @@ type TargetDescription struct {
// 这里的相同点,就是它们同属于 将发往一个方向, 即同属一个路由策略 // 这里的相同点,就是它们同属于 将发往一个方向, 即同属一个路由策略
// 任意一个参数匹配后都将发往相同的方向由该方向OutTag 指定 // 任意一个参数匹配后都将发往相同的方向由该方向OutTag 指定
// RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起 // RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起
//
// 目前先使用map以后优化时再考虑使用 能加速查找 的数据结构
type RouteSet struct { type RouteSet struct {
//网络层 //网络层
NetRanger cidranger.Ranger //一个范围 NetRanger cidranger.Ranger //一个范围
IPs map[string]net.IP //一个确定值 IPs map[netip.Addr]bool //一个确定值
Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key
//传输层 //传输层
AllowedTransportLayerProtocols uint16 AllowedTransportLayerProtocols uint16
OutTag string //目标 OutTag string //目标
} }
func NewRouteSetForMyCountry(iso string) *RouteSet { func NewRouteSetForMyCountry(iso string) *RouteSet {
@@ -51,7 +50,7 @@ func NewRouteSetForMyCountry(iso string) *RouteSet {
func NewFullRouteSet() *RouteSet { func NewFullRouteSet() *RouteSet {
return &RouteSet{ return &RouteSet{
NetRanger: cidranger.NewPCTrieRanger(), NetRanger: cidranger.NewPCTrieRanger(),
IPs: make(map[string]net.IP), IPs: make(map[netip.Addr]bool),
Domains: make(map[string]bool), Domains: make(map[string]bool),
InTags: make(map[string]bool), InTags: make(map[string]bool),
Countries: make(map[string]bool), Countries: make(map[string]bool),
@@ -60,7 +59,7 @@ func NewFullRouteSet() *RouteSet {
} }
func (sg *RouteSet) IsIn(td *TargetDescription) bool { func (sg *RouteSet) IsIn(td *TargetDescription) bool {
if td.Tag != "" { if td.Tag != "" && sg.InTags != nil {
if _, found := sg.InTags[td.Tag]; found { if _, found := sg.InTags[td.Tag]; found {
return true return true
} }
@@ -89,16 +88,15 @@ func (sg *RouteSet) IsTCPAllowed() bool {
func (sg *RouteSet) IsAddrIn(a *Addr) bool { func (sg *RouteSet) IsAddrIn(a *Addr) bool {
//我们先过滤传输层,再过滤网络层 //我们先过滤传输层,再过滤网络层
//目前我们仅支持udp和tcp这两种传输层协议所以可以如此。以后如果加了更多的话还需改动
if !sg.IsAddrNetworkAllowed(a) { if !sg.IsAddrNetworkAllowed(a) {
return false 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 return true
} }
//开始网络层判断 //开始网络层判断
if a.IP != nil { if len(a.IP) > 0 {
if sg.NetRanger != nil { if sg.NetRanger != nil {
if has, _ := sg.NetRanger.Contains(a.IP); has { if has, _ := sg.NetRanger.Contains(a.IP); has {
return true return true
@@ -114,7 +112,7 @@ func (sg *RouteSet) IsAddrIn(a *Addr) bool {
} }
if sg.IPs != nil { if sg.IPs != nil {
if _, found := sg.IPs[a.IP.To16().String()]; found { if _, found := sg.IPs[a.GetNetIPAddr()]; found {
return true return true
} }
} }

View File

@@ -3,6 +3,7 @@ package proxy
import ( import (
"io/ioutil" "io/ioutil"
"net" "net"
"net/netip"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@@ -154,9 +155,10 @@ func LoadRuleForRouteSet(rule *RuleConf) (rs *netLayer.RouteSet) {
} }
continue continue
} }
if ip := net.ParseIP(ipStr); ip != nil {
rs.IPs[ipStr] = ip na, e := netip.ParseAddr(ipStr)
continue if e == nil {
rs.IPs[na] = true
} }
} }

194
utils/container_test.go.bak Normal file
View File

@@ -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])))[:]) {
}
}
}