add vtun listener and handler

This commit is contained in:
ginuerzh
2025-07-23 21:30:21 +08:00
parent afcf4a5252
commit acae52ab89
20 changed files with 1157 additions and 85 deletions
+20 -14
View File
@@ -1,14 +1,14 @@
module github.com/go-gost/x
go 1.23.0
go 1.23.1
toolchain go1.24.4
toolchain go1.24.5
require (
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/gin-gonic/gin v1.10.1
github.com/go-gost/core v0.3.2
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.2
@@ -30,29 +30,32 @@ require (
github.com/quic-go/webtransport-go v0.9.0
github.com/rs/xid v1.3.0
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740
github.com/sirupsen/logrus v1.8.1
github.com/sirupsen/logrus v1.9.3
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/vishvananda/netlink v1.1.0
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
github.com/vishvananda/netns v0.0.4
github.com/vulcand/predicate v1.2.0
github.com/xjasonlyu/tun2socks/v2 v2.6.0
github.com/xtaci/kcp-go/v5 v5.6.5
github.com/xtaci/smux v1.5.31
github.com/xtaci/tcpraw v1.2.25
github.com/yl2chen/cidranger v1.0.2
github.com/zalando/go-keyring v0.2.4
golang.org/x/crypto v0.39.0
go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.40.0
golang.org/x/exp v0.0.0-20241210194714-1829a127f884
golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.26.0
golang.org/x/time v0.5.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
golang.org/x/net v0.42.0
golang.org/x/sys v0.34.0
golang.org/x/text v0.27.0
golang.org/x/time v0.11.0
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20
)
require (
@@ -75,6 +78,7 @@ require (
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -116,10 +120,12 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/tools v0.33.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v1.2.12
+36 -30
View File
@@ -47,8 +47,8 @@ github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQ
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-gost/core v0.3.2 h1:AjmIYWVa5fc172zIupT3e1pdnKx76LxppGiw00uRsSA=
github.com/go-gost/core v0.3.2/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
@@ -90,8 +90,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -193,8 +193,8 @@ github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWR
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740 h1:XdDrN8rtxdgW3TLn7pAuobI9PhPMbf6Geu9nvFzXn2E=
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740/go.mod h1:Oqfn/ykzqjeX00+7IuPyR7wGYgOzld0Tni6djgElacI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -235,13 +235,15 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50=
github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA=
github.com/xjasonlyu/tun2socks/v2 v2.6.0 h1:gI9saJT3XgH4e6v9jBuHRLwK7l3aN9YFWec/SsDTDx4=
github.com/xjasonlyu/tun2socks/v2 v2.6.0/go.mod h1:35AwqxIxnMkfBfT0UJ1Lku7PZm2ZiZJ8sxHyp0gt1yw=
github.com/xtaci/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k=
github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
@@ -255,6 +257,8 @@ github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.4 h1:wi2xxTqdiwMKbM6TWwi+uJCG/Tum2UV0jqaQhCa9/68=
github.com/zalando/go-keyring v0.2.4/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -269,8 +273,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
@@ -294,45 +298,47 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
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/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -341,15 +347,15 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
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=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -384,8 +390,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20 h1:0DxLu8hxI1OGp1qVRPqNd+2k1a7hMNUNqbZG0IrtKlM=
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+161
View File
@@ -0,0 +1,161 @@
// Package iobased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package tun
import (
"context"
"errors"
"io"
"sync"
"github.com/go-gost/core/logger"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// Queue length for outbound packet, arriving for read. Overflow
// causes packet drops.
defaultOutQueueLen = 1 << 10
)
// Endpoint implements the interface of stack.LinkEndpoint from io.ReadWriter.
type Endpoint struct {
*channel.Endpoint
// rw is the io.ReadWriter for reading and writing packets.
rw io.ReadWriter
// mtu (maximum transmission unit) is the maximum size of a packet.
mtu uint32
// offset can be useful when perform TUN device I/O with TUN_PI enabled.
offset int
// once is used to perform the init action once when attaching.
once sync.Once
// wg keeps track of running goroutines.
wg sync.WaitGroup
log logger.Logger
}
// New returns stack.LinkEndpoint(.*Endpoint) and error.
func newEndpoint(rw io.ReadWriter, mtu uint32, offset int, log logger.Logger) (*Endpoint, error) {
if mtu == 0 {
return nil, errors.New("MTU size is zero")
}
if rw == nil {
return nil, errors.New("RW interface is nil")
}
if offset < 0 {
return nil, errors.New("offset must be non-negative")
}
return &Endpoint{
Endpoint: channel.New(defaultOutQueueLen, mtu, ""),
rw: rw,
mtu: mtu,
offset: offset,
log: log,
}, nil
}
// Attach launches the goroutine that reads packets from io.Reader and
// dispatches them via the provided dispatcher.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.Endpoint.Attach(dispatcher)
e.once.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
e.wg.Add(2)
go func() {
e.outboundLoop(ctx)
e.wg.Done()
}()
go func() {
e.dispatchLoop(cancel)
e.wg.Done()
}()
})
}
func (e *Endpoint) Wait() {
e.wg.Wait()
}
// dispatchLoop dispatches packets to upper layer.
func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
// Call cancel() to ensure (*Endpoint).outboundLoop(context.Context) exits
// gracefully after (*Endpoint).dispatchLoop(context.CancelFunc) returns.
defer cancel()
offset, mtu := e.offset, int(e.mtu)
for {
data := make([]byte, offset+mtu)
n, err := e.rw.Read(data)
if err != nil {
e.log.Error(err)
break
}
e.log.Debugf("read tun: (%d) % x", n, data[:n])
if n == 0 || n > mtu {
continue
}
if !e.IsAttached() {
continue /* unattached, drop packet */
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(data[offset : offset+n]),
})
switch header.IPVersion(data[offset:]) {
case header.IPv4Version:
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
case header.IPv6Version:
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
}
pkt.DecRef()
}
}
// outboundLoop reads outbound packets from channel, and then it calls
// writePacket to send those packets back to lower layer.
func (e *Endpoint) outboundLoop(ctx context.Context) {
for {
pkt := e.ReadContext(ctx)
if pkt == nil {
break
}
e.writePacket(pkt)
}
}
// writePacket writes outbound packets to the io.Writer.
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
defer pkt.DecRef()
buf := pkt.ToBuffer()
defer buf.Release()
if e.offset != 0 {
v := buffer.NewViewWithData(make([]byte, e.offset))
_ = buf.Prepend(v)
}
if _, err := e.rw.Write(buf.Flatten()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil
}
+84
View File
@@ -0,0 +1,84 @@
package tun
import (
"context"
"net"
"time"
"github.com/go-gost/core/handler"
md "github.com/go-gost/core/metadata"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
"github.com/xjasonlyu/tun2socks/v2/core"
)
func init() {
registry.HandlerRegistry().Register("vtun", NewHandler)
}
type tunHandler struct {
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &tunHandler{
options: options,
}
}
func (h *tunHandler) Init(md md.Metadata) (err error) {
if err = h.parseMetadata(md); err != nil {
return
}
return
}
func (h *tunHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
log := h.options.Logger
start := time.Now()
log = log.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
ep, err := newEndpoint(conn, 1420, 0, log)
if err != nil {
return err
}
th := newTransportHandler(log)
th.ProcessAsync()
defer th.Close()
stack, err := core.CreateStack(&core.Config{
LinkEndpoint: ep,
TransportHandler: th,
})
if err != nil {
return err
}
defer stack.Close()
log.Debugf("is attached: %v", ep.IsAttached())
<-ctx.Done()
return nil
}
+12
View File
@@ -0,0 +1,12 @@
package tun
import (
mdata "github.com/go-gost/core/metadata"
)
type metadata struct {
}
func (h *tunHandler) parseMetadata(md mdata.Metadata) (err error) {
return
}
+105
View File
@@ -0,0 +1,105 @@
package tun
import (
"context"
"net/netip"
"sync"
"time"
"github.com/go-gost/core/logger"
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"go.uber.org/atomic"
)
const (
// tcpConnectTimeout is the default timeout for TCP handshakes.
tcpConnectTimeout = 5 * time.Second
// tcpWaitTimeout implements a TCP half-close timeout.
tcpWaitTimeout = 60 * time.Second
// udpSessionTimeout is the default timeout for UDP sessions.
udpSessionTimeout = 60 * time.Second
)
var _ adapter.TransportHandler = (*transportHandler)(nil)
type transportHandler struct {
// Unbuffered TCP/UDP queues.
tcpQueue chan adapter.TCPConn
udpQueue chan adapter.UDPConn
// UDP session timeout.
udpTimeout *atomic.Duration
procOnce sync.Once
procCancel context.CancelFunc
log logger.Logger
}
func newTransportHandler(log logger.Logger) *transportHandler {
return &transportHandler{
tcpQueue: make(chan adapter.TCPConn),
udpQueue: make(chan adapter.UDPConn),
udpTimeout: atomic.NewDuration(udpSessionTimeout),
procCancel: func() { /* nop */ },
log: log,
}
}
func (t *transportHandler) HandleTCP(conn adapter.TCPConn) {
t.tcpQueue <- conn
}
func (t *transportHandler) HandleUDP(conn adapter.UDPConn) {
t.udpQueue <- conn
}
func (t *transportHandler) process(ctx context.Context) {
for {
select {
case conn := <-t.tcpQueue:
go t.handleTCPConn(conn)
case conn := <-t.udpQueue:
go t.handleUDPConn(conn)
case <-ctx.Done():
return
}
}
}
// ProcessAsync can be safely called multiple times, but will only be effective once.
func (t *transportHandler) ProcessAsync() {
t.procOnce.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
t.procCancel = cancel
go t.process(ctx)
})
}
// Close closes the Tunnel and releases its resources.
func (t *transportHandler) Close() {
t.procCancel()
}
func (t *transportHandler) SetUDPTimeout(timeout time.Duration) {
t.udpTimeout.Store(timeout)
}
func (t *transportHandler) handleTCPConn(originConn adapter.TCPConn) {
defer originConn.Close()
id := originConn.ID()
srcIP, _ := netip.AddrFromSlice(id.RemoteAddress.AsSlice())
dstIP, _ := netip.AddrFromSlice(id.LocalAddress.AsSlice())
raddr := netip.AddrPortFrom(srcIP, id.RemotePort)
laddr := netip.AddrPortFrom(dstIP, id.LocalPort)
t.log.Debugf("[TCP] %s <-> %s", raddr.String(), laddr.String())
}
// TODO: Port Restricted NAT support.
func (t *transportHandler) handleUDPConn(uc adapter.UDPConn) {
defer uc.Close()
}
+4 -4
View File
@@ -27,7 +27,7 @@ type tunListener struct {
addr net.Addr
cqueue chan net.Conn
closed chan struct{}
logger logger.Logger
log logger.Logger
md metadata
options listener.Options
routes []*router.Route
@@ -39,7 +39,7 @@ func NewListener(opts ...listener.Option) listener.Listener {
opt(&options)
}
return &tunListener{
logger: options.Logger,
log: options.Logger,
options: options,
}
}
@@ -83,7 +83,7 @@ func (l *tunListener) listenLoop() {
}
addrs, _ := itf.Addrs()
l.logger.Infof("name: %s, net: %s, mtu: %d, addrs: %s",
l.log.Infof("name: %s, net: %s, mtu: %d, addrs: %s",
itf.Name, ip, itf.MTU, addrs)
var c net.Conn
@@ -112,7 +112,7 @@ func (l *tunListener) listenLoop() {
return nil
}()
if err != nil {
l.logger.Error(err)
l.log.Error(err)
cancel()
}
+43 -35
View File
@@ -7,53 +7,51 @@ import (
"golang.zx2c4.com/wireguard/tun"
)
const (
tunOffsetBytes = 16
MaxMessageSize = math.MaxUint16
)
type tunDevice struct {
dev tun.Device
elems [][MaxMessageSize]byte
rbufs [][]byte
wbuf [tunOffsetBytes + MaxMessageSize]byte
dev tun.Device
packets int
sizes []int
rbufs [][]byte
wbufs [][]byte
wbuf []byte
}
func (d *tunDevice) Read(p []byte) (n int, err error) {
if len(d.rbufs) > 0 {
n = copy(p, d.rbufs[0])
d.rbufs = d.rbufs[1:]
return
}
if d.packets > 0 {
for i, size := range d.sizes {
if size > 0 {
n = copy(p, d.rbufs[i][:size])
sizes := make([]int, len(d.elems))
bufs := make([][]byte, len(d.elems))
for i := range d.elems {
bufs[i] = d.elems[i][:]
}
nn, err := d.dev.Read(bufs, sizes, 0)
if err != nil {
return
}
for i := 0; i < nn; i++ {
if sizes[i] <= 0 {
continue
d.sizes[i] = 0
d.packets--
return
}
}
d.rbufs = append(d.rbufs, bufs[i][:sizes[i]])
}
n = copy(p, d.rbufs[0])
d.rbufs = d.rbufs[1:]
d.rbufs[0] = p
packets, err := d.dev.Read(d.rbufs, d.sizes, readOffset)
if err != nil && err != tun.ErrTooManySegments {
return
}
n = d.sizes[0]
d.sizes[0] = 0
d.packets = packets - 1
return
}
func (d *tunDevice) Write(p []byte) (n int, err error) {
buf := d.wbuf[:]
n = copy(buf[tunOffsetBytes:], p)
_, err = d.dev.Write([][]byte{buf[:tunOffsetBytes+n]}, tunOffsetBytes)
if writeOffset > 0 {
copy(d.wbuf[writeOffset:], p)
d.wbufs[0] = d.wbuf[:writeOffset+len(p)]
} else {
d.wbufs[0] = p
}
_, err = d.dev.Write(d.wbufs, writeOffset)
n = len(p)
return
}
@@ -67,9 +65,19 @@ func (l *tunListener) createTunDevice() (dev io.ReadWriteCloser, name string, er
return
}
batchSize := 16
rbufs := make([][]byte, batchSize)
for i := 1; i < len(rbufs); i++ {
rbufs[i] = make([]byte, math.MaxUint16)
}
dev = &tunDevice{
dev: ifce,
elems: make([][MaxMessageSize]byte, ifce.BatchSize()),
sizes: make([]int, batchSize),
rbufs: rbufs,
wbufs: make([][]byte, 1),
wbuf: make([]byte, math.MaxUint16+writeOffset),
}
name, err = ifce.Name()
+2
View File
@@ -10,6 +10,8 @@ import (
const (
defaultTunName = "utun"
readOffset = 4
writeOffset = 4
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
+7 -2
View File
@@ -10,6 +10,11 @@ import (
"github.com/vishvananda/netlink"
)
const (
readOffset = 0
writeOffset = 16
)
func (l *tunListener) createTun() (dev io.ReadWriteCloser, name string, ip net.IP, err error) {
dev, name, err = l.createTunDevice()
if err != nil {
@@ -30,7 +35,7 @@ func (l *tunListener) createTun() (dev io.ReadWriteCloser, name string, ip net.I
if err = netlink.AddrAdd(link, &netlink.Addr{
IPNet: &net,
}); err != nil {
l.logger.Error(err)
l.log.Error(err)
continue
}
}
@@ -52,7 +57,7 @@ func (l *tunListener) createTun() (dev io.ReadWriteCloser, name string, ip net.I
}
if len(dnsServers) > 0 {
cmd := fmt.Sprintf("resolvectl dns %s %s", name, strings.Join(dnsServers, " "))
l.logger.Debug(cmd)
l.log.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
+2
View File
@@ -12,6 +12,8 @@ import (
const (
defaultTunName = "tun0"
readOffset = 4
writeOffset = 4
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
+2
View File
@@ -10,6 +10,8 @@ import (
const (
defaultTunName = "wintun"
readOffset = 0
writeOffset = 0
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
+51
View File
@@ -0,0 +1,51 @@
package tun
import (
"context"
"errors"
"io"
"net"
"time"
)
type conn struct {
ifce io.ReadWriteCloser
laddr net.Addr
raddr net.Addr
cancel context.CancelFunc
}
func (c *conn) Read(b []byte) (n int, err error) {
return c.ifce.Read(b)
}
func (c *conn) Write(b []byte) (n int, err error) {
return c.ifce.Write(b)
}
func (c *conn) LocalAddr() net.Addr {
return c.laddr
}
func (c *conn) RemoteAddr() net.Addr {
return c.raddr
}
func (c *conn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *conn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "tun", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *conn) Close() (err error) {
if c.cancel != nil {
c.cancel()
}
return c.ifce.Close()
}
+151
View File
@@ -0,0 +1,151 @@
package tun
import (
"context"
"net"
"time"
"github.com/go-gost/core/limiter"
"github.com/go-gost/core/listener"
"github.com/go-gost/core/logger"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/core/router"
traffic_limiter "github.com/go-gost/x/limiter/traffic"
limiter_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
metrics "github.com/go-gost/x/metrics/wrapper"
stats "github.com/go-gost/x/observer/stats/wrapper"
"github.com/go-gost/x/registry"
)
func init() {
registry.ListenerRegistry().Register("vtun", NewListener)
}
type tunListener struct {
addr net.Addr
cqueue chan net.Conn
closed chan struct{}
log logger.Logger
md metadata
options listener.Options
routes []*router.Route
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &tunListener{
log: options.Logger,
options: options,
}
}
func (l *tunListener) Init(md mdata.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
l.addr = &Addr{Name: l.options.Addr}
l.cqueue = make(chan net.Conn)
l.closed = make(chan struct{})
go l.listenLoop()
return
}
func (l *tunListener) listenLoop() {
for {
ctx, cancel := context.WithCancel(context.Background())
err := func() error {
ifce, name, ip, err := l.createTun()
if err != nil {
if ifce != nil {
ifce.Close()
}
return err
}
itf, err := net.InterfaceByName(name)
if err != nil {
return err
}
addrs, _ := itf.Addrs()
l.log.Infof("name: %s, net: %s, mtu: %d, addrs: %s",
itf.Name, ip, itf.MTU, addrs)
var c net.Conn
c = &conn{
ifce: ifce,
laddr: l.addr,
raddr: &net.IPAddr{IP: ip},
cancel: cancel,
}
c = metrics.WrapConn(l.options.Service, c)
c = stats.WrapConn(c, l.options.Stats)
c = limiter_wrapper.WrapConn(
c,
l.options.TrafficLimiter,
traffic_limiter.ServiceLimitKey,
limiter.ScopeOption(limiter.ScopeService),
limiter.ServiceOption(l.options.Service),
limiter.NetworkOption(c.LocalAddr().Network()),
)
l.cqueue <- c
return nil
}()
if err != nil {
l.log.Error(err)
cancel()
}
select {
case <-ctx.Done():
case <-l.closed:
return
}
time.Sleep(time.Second)
}
}
func (l *tunListener) Accept() (net.Conn, error) {
select {
case conn := <-l.cqueue:
return conn, nil
case <-l.closed:
}
return nil, listener.ErrClosed
}
func (l *tunListener) Addr() net.Addr {
return l.addr
}
func (l *tunListener) Close() error {
select {
case <-l.closed:
return net.ErrClosed
default:
close(l.closed)
}
return nil
}
type Addr struct {
Name string
}
func (a *Addr) Network() string {
return "tun"
}
func (a *Addr) String() string {
return a.Name
}
+93
View File
@@ -0,0 +1,93 @@
package tun
import (
"net"
"strings"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/core/router"
tun_util "github.com/go-gost/x/internal/util/tun"
mdutil "github.com/go-gost/x/metadata/util"
xrouter "github.com/go-gost/x/router"
)
const (
defaultMTU = 1420
)
type metadata struct {
config *tun_util.Config
}
func (l *tunListener) parseMetadata(md mdata.Metadata) (err error) {
config := &tun_util.Config{
Name: mdutil.GetString(md, "name", "tun.name"),
Peer: mdutil.GetString(md, "peer", "tun.peer"),
MTU: mdutil.GetInt(md, "mtu", "tun.mtu"),
}
if config.MTU <= 0 {
config.MTU = defaultMTU
}
if gw := mdutil.GetString(md, "gw", "tun.gw"); gw != "" {
config.Gateway = net.ParseIP(gw)
}
for _, s := range strings.Split(mdutil.GetString(md, "net", "tun.net"), ",") {
if s = strings.TrimSpace(s); s == "" {
continue
}
ip, ipNet, err := net.ParseCIDR(s)
if err != nil {
continue
}
config.Net = append(config.Net, net.IPNet{
IP: ip,
Mask: ipNet.Mask,
})
}
for _, s := range strings.Split(mdutil.GetString(md, "route", "tun.route"), ",") {
gw := ""
if config.Gateway != nil {
gw = config.Gateway.String()
}
if route := xrouter.ParseRoute(strings.TrimSpace(s), gw); route != nil {
l.routes = append(l.routes, route)
}
}
for _, s := range mdutil.GetStrings(md, "routes", "tun.routes") {
ss := strings.SplitN(s, " ", 2)
if len(ss) == 2 {
_, ipNet, _ := net.ParseCIDR(strings.TrimSpace(ss[0]))
if ipNet == nil {
continue
}
gw := net.ParseIP(ss[1])
if gw == nil {
gw = config.Gateway
}
gateway := ""
if gw != nil {
gateway = gw.String()
}
l.routes = append(l.routes, &router.Route{
Net: ipNet,
Dst: ipNet.String(),
Gateway: gateway,
})
}
}
for _, v := range strings.Split(mdutil.GetString(md, "dns", "tun.dns"), ",") {
if ip := net.ParseIP(strings.TrimSpace(v)); ip != nil {
config.DNS = append(config.DNS, ip)
}
}
l.md.config = config
return
}
+91
View File
@@ -0,0 +1,91 @@
package tun
import (
"io"
"math"
"github.com/go-gost/core/logger"
"golang.zx2c4.com/wireguard/tun"
)
type tunDevice struct {
dev tun.Device
packets int
sizes []int
rbufs [][]byte
wbufs [][]byte
wbuf []byte
log logger.Logger
}
func (d *tunDevice) Read(p []byte) (n int, err error) {
if d.packets > 0 {
for i, size := range d.sizes {
if size > 0 {
n = copy(p, d.rbufs[i][:size])
d.sizes[i] = 0
d.packets--
return
}
}
}
d.rbufs[0] = p
packets, err := d.dev.Read(d.rbufs, d.sizes, readOffset)
if err != nil && err != tun.ErrTooManySegments {
return
}
n = d.sizes[0]
d.sizes[0] = 0
d.packets = packets - 1
// d.log.Debugf("read tun: (%d), % x", n, p[:n])
return
}
func (d *tunDevice) Write(p []byte) (n int, err error) {
if writeOffset > 0 {
copy(d.wbuf[writeOffset:], p)
d.wbufs[0] = d.wbuf[:writeOffset+len(p)]
} else {
d.wbufs[0] = p
}
_, err = d.dev.Write(d.wbufs, writeOffset)
n = len(p)
return
}
func (d *tunDevice) Close() error {
return d.dev.Close()
}
func (l *tunListener) createTunDevice() (dev io.ReadWriteCloser, name string, err error) {
ifce, err := tun.CreateTUN(l.md.config.Name, l.md.config.MTU)
if err != nil {
return
}
batchSize := 16
rbufs := make([][]byte, batchSize)
for i := 1; i < len(rbufs); i++ {
rbufs[i] = make([]byte, math.MaxUint16)
}
dev = &tunDevice{
dev: ifce,
sizes: make([]int, batchSize),
rbufs: rbufs,
wbufs: make([][]byte, 1),
wbuf: make([]byte, math.MaxUint16+writeOffset),
log: l.log,
}
name, err = ifce.Name()
return
}
+58
View File
@@ -0,0 +1,58 @@
package tun
import (
"fmt"
"io"
"net"
"os/exec"
"strings"
)
const (
defaultTunName = "utun"
readOffset = 4
writeOffset = 4
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
if l.md.config.Name == "" {
l.md.config.Name = defaultTunName
}
ifce, name, err = l.createTunDevice()
if err != nil {
return
}
peer := l.md.config.Peer
if peer == "" {
peer = ip.String()
}
if len(l.md.config.Net) > 0 {
cmd := fmt.Sprintf("ifconfig %s inet %s %s mtu %d up",
name, l.md.config.Net[0].String(), l.md.config.Peer, l.md.config.MTU)
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if err = exec.Command(args[0], args[1:]...).Run(); err != nil {
return
}
ip = l.md.config.Net[0].IP
}
if err = l.addRoutes(name); err != nil {
return
}
return
}
func (l *tunListener) addRoutes(ifName string) error {
for _, route := range l.routes {
cmd := fmt.Sprintf("route add -net %s -interface %s", route.Net.String(), ifName)
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if err := exec.Command(args[0], args[1:]...).Run(); err != nil {
return err
}
}
return nil
}
+90
View File
@@ -0,0 +1,90 @@
package tun
import (
"fmt"
"io"
"net"
"os/exec"
"strings"
"github.com/vishvananda/netlink"
)
const (
readOffset = 0
writeOffset = 16
)
func (l *tunListener) createTun() (dev io.ReadWriteCloser, name string, ip net.IP, err error) {
dev, name, err = l.createTunDevice()
if err != nil {
return
}
ifce, err := net.InterfaceByName(name)
if err != nil {
return
}
link, err := netlink.LinkByName(name)
if err != nil {
return
}
for _, net := range l.md.config.Net {
if err = netlink.AddrAdd(link, &netlink.Addr{
IPNet: &net,
}); err != nil {
l.log.Error(err)
continue
}
}
if len(l.md.config.Net) > 0 {
ip = l.md.config.Net[0].IP
}
if err = netlink.LinkSetUp(link); err != nil {
return
}
if err = l.addRoutes(ifce); err != nil {
return
}
var dnsServers []string
for _, dns := range l.md.config.DNS {
dnsServers = append(dnsServers, dns.String())
}
if len(dnsServers) > 0 {
cmd := fmt.Sprintf("resolvectl dns %s %s", name, strings.Join(dnsServers, " "))
l.log.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
}
return
}
func (l *tunListener) addRoutes(ifce *net.Interface) error {
for _, route := range l.routes {
if route.Net == nil {
continue
}
r := netlink.Route{
Dst: route.Net,
Gw: net.ParseIP(route.Gateway),
}
if r.Gw == nil {
r.LinkIndex = ifce.Index
}
if err := netlink.RouteReplace(&r); err != nil {
return fmt.Errorf("add route %v %v: %v", r.Dst, r.Gw, err)
}
}
return nil
}
+58
View File
@@ -0,0 +1,58 @@
//go:build !linux && !windows && !darwin
package tun
import (
"fmt"
"io"
"net"
"os/exec"
"strings"
)
const (
defaultTunName = "tun0"
readOffset = 4
writeOffset = 4
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
if l.md.config.Name == "" {
l.md.config.Name = defaultTunName
}
ifce, name, err = l.createTunDevice()
if err != nil {
return
}
if len(l.md.config.Net) > 0 {
cmd := fmt.Sprintf("ifconfig %s inet %s mtu %d up",
name, l.md.config.Net[0].String(), l.md.config.MTU)
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
ip = l.md.config.Net[0].IP
}
if err = l.addRoutes(name); err != nil {
return
}
return
}
func (l *tunListener) addRoutes(ifName string) error {
for _, route := range l.routes {
cmd := fmt.Sprintf("route add -net %s -interface %s", route.Net.String(), ifName)
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
return fmt.Errorf("%s: %v", cmd, er)
}
}
return nil
}
+87
View File
@@ -0,0 +1,87 @@
package tun
import (
"fmt"
"io"
"net"
"os/exec"
"strings"
)
const (
defaultTunName = "wintun"
readOffset = 0
writeOffset = 0
)
func (l *tunListener) createTun() (ifce io.ReadWriteCloser, name string, ip net.IP, err error) {
if l.md.config.Name == "" {
l.md.config.Name = defaultTunName
}
ifce, name, err = l.createTunDevice()
if err != nil {
return
}
if len(l.md.config.Net) > 0 {
ipNet := l.md.config.Net[0]
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+
"source=static addr=%s mask=%s gateway=none",
name, ipNet.IP.String(), ipMask(ipNet.Mask))
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
ip = ipNet.IP
}
if err = l.addRoutes(name, l.md.config.Gateway); err != nil {
return
}
for _, dns := range l.md.config.DNS {
cmd := fmt.Sprintf("netsh interface ip add dnsservers name=%s address=%s validate=no", name, dns.String())
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
}
}
return
}
func (l *tunListener) addRoutes(ifName string, gw net.IP) error {
for _, route := range l.routes {
l.deleteRoute(ifName, route.Net.String())
cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active",
route.Net.String(), ifName)
if gw != nil {
cmd += " nexthop=" + gw.String()
}
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
return fmt.Errorf("%s: %v", cmd, er)
}
}
return nil
}
func (l *tunListener) deleteRoute(ifName string, route string) error {
cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=%s store=active",
route, ifName)
l.logger.Debug(cmd)
args := strings.Split(cmd, " ")
return exec.Command(args[0], args[1:]...).Run()
}
func ipMask(mask net.IPMask) string {
return fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3])
}