mirror of
https://github.com/ICKelin/gtun.git
synced 2025-09-26 19:11:15 +08:00
refactor code
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,4 +12,5 @@ research
|
||||
*.tar.gz
|
||||
|
||||
docker-build/gtun/gtun*
|
||||
docker-build/gtund/gtund
|
||||
docker-build/gtund/gtund
|
||||
release
|
20
build.sh
Executable file
20
build.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
rm -r release
|
||||
mkdir -p release
|
||||
mkdir -p release/gtun/etc
|
||||
mkdir -p release/gtund/etc
|
||||
|
||||
DIR=`pwd`
|
||||
|
||||
cd src/gtun
|
||||
GOOS=linux go build -o gtun
|
||||
mv gtun $DIR/release/gtun/
|
||||
cd $DIR
|
||||
cp scripts/install_gtun.sh release/gtun/install.sh
|
||||
cp -r etc/gtun/* release/gtun/etc
|
||||
|
||||
cd src/gtund
|
||||
GOOS=linux go build -o gtund
|
||||
mv gtund $DIR/release/gtund/
|
||||
cd $DIR
|
||||
cp scripts/install_gtund.sh release/gtund/install.sh
|
||||
cp -r etc/gtund/* release/gtund/etc
|
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "building gtund...."
|
||||
GOOS=linux go build -o bin/gtund/gtund cmd/gtund/*.go
|
||||
echo "builded gtund...."
|
||||
|
||||
echo "building gtun...."
|
||||
GOOS=linux go build -o bin/gtun/gtun-linux_amd64 cmd/gtun/*.go
|
||||
GOARCH=arm GOOS=linux go build -o bin/gtun/gtun-linux_arm cmd/gtun/*.go
|
||||
echo "builded gtun...."
|
||||
|
||||
cp -r etc/gtun.yaml bin/gtun/
|
||||
cp -r etc/gtund.yaml bin/gtund/
|
||||
cp install.sh bin/gtun/
|
@@ -1,14 +0,0 @@
|
||||
WORKSPACE=`pwd`
|
||||
./build_exec.sh
|
||||
|
||||
echo "building gtund docker image"
|
||||
cd docker-build/gtund
|
||||
cp $WORKSPACE/bin/gtund/gtund .
|
||||
docker build . -t gtund
|
||||
echo "builded gtund docker image"
|
||||
|
||||
echo "building gtun docker image"
|
||||
cd $WORKSPACE/docker-build/gtun
|
||||
cp $WORKSPACE/bin/gtun/gtun-linux_amd64 .
|
||||
docker build . -t gtun
|
||||
echo "builded gtun docker image"
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 ICKelin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
DESCRIPTION:
|
||||
This program is a gtun client for game/ip accelator.
|
||||
|
||||
Author: ICKelin
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ICKelin/gtun/gtun"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gtun.Main()
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 ICKelin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
DESCRIPTION:
|
||||
This program is a gtun server for game/ip accelator.
|
||||
|
||||
Author: ICKelin
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/ICKelin/gtun/gtund"
|
||||
|
||||
func main() {
|
||||
gtund.Main()
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
version: '3'
|
||||
services:
|
||||
gtun:
|
||||
build: ./gtun
|
||||
container_name: gtun
|
||||
restart: always
|
||||
network_mode: host
|
||||
privileged: true
|
||||
volumes:
|
||||
- /opt/apps/logs:/opt/logs
|
||||
environment:
|
||||
TIME_ZONE: Asia/Shanghai
|
||||
settings: |
|
||||
settings:
|
||||
CN:
|
||||
proxy_file: "https://www.ipdeny.com/ipblocks/data/countries/us.zone"
|
||||
route:
|
||||
- trace_addr: ${CN_SERVER_IP}:${CN_SERVER_TRACE_PORT}
|
||||
scheme: "kcp"
|
||||
addr: ${CN_SERVER_IP}:${CN_SERVER_PORT}
|
||||
auth_key: ""
|
||||
proxy:
|
||||
"tproxy_tcp": |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"listen_addr": ":8524",
|
||||
"rate_limit": 50,
|
||||
"region": "CN"
|
||||
}
|
||||
"tproxy_udp": |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"session_timeout": 30,
|
||||
"listen_addr": ":8524",
|
||||
"rate_limit": 50,
|
||||
"region": "CN"
|
||||
}
|
||||
log:
|
||||
days: 5
|
||||
level: Debug
|
||||
path: gtun.log
|
||||
|
||||
http_server:
|
||||
listen_addr: ":9001"
|
||||
gtund:
|
||||
build: ./gtund
|
||||
container_name: gtund
|
||||
restart: always
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /opt/apps/logs:/logs
|
||||
environment:
|
||||
TIME_ZONE: Asia/Shanghai
|
||||
settings: |
|
||||
server:
|
||||
- listen: ":3002"
|
||||
authKey: "rewrite with your auth key"
|
||||
scheme: "kcp"
|
||||
trace: ":3003"
|
||||
log:
|
||||
days: 5
|
||||
level: "debug"
|
||||
path: "gtund.log"
|
@@ -1,6 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
COPY gtun-linux_amd64 /gtun
|
||||
COPY start.sh /
|
||||
RUN chmod +x start.sh && chmod +x gtun
|
||||
RUN mkdir /opt/logs
|
||||
CMD /start.sh
|
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
if [ "$TIME_ZONE" != "" ]; then
|
||||
ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone
|
||||
fi
|
||||
|
||||
#项目的配置文件
|
||||
if [ "$settings" != "" ]; then
|
||||
echo "$settings" > /gtun.yaml
|
||||
fi
|
||||
|
||||
/gtun -c /gtun.yaml
|
@@ -1,6 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
COPY gtund /
|
||||
COPY start.sh /
|
||||
RUN chmod +x start.sh && chmod +x gtund
|
||||
RUN mkdir /opt/logs
|
||||
CMD /start.sh
|
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
if [ "$TIME_ZONE" != "" ]; then
|
||||
ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone
|
||||
fi
|
||||
|
||||
#项目的配置文件
|
||||
if [ "$settings" != "" ]; then
|
||||
echo "$settings" > /gtund.yaml
|
||||
fi
|
||||
|
||||
/gtund -c /gtund.yaml
|
@@ -1,33 +0,0 @@
|
||||
settings:
|
||||
CN:
|
||||
proxy_file: "https://www.ipdeny.com/ipblocks/data/countries/us.zone"
|
||||
route:
|
||||
- trace_addr: ${CN_SERVER_IP}:${CN_SERVER_TRACE_PORT}
|
||||
scheme: "kcp"
|
||||
addr: ${CN_SERVER_IP}:${CN_SERVER_PORT}
|
||||
auth_key: ""
|
||||
proxy:
|
||||
"tproxy_tcp": |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"listen_addr": ":8524",
|
||||
"rate_limit": 50,
|
||||
"region": "CN"
|
||||
}
|
||||
"tproxy_udp": |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"session_timeout": 30,
|
||||
"listen_addr": ":8524",
|
||||
"rate_limit": 50,
|
||||
"region": "CN"
|
||||
}
|
||||
log:
|
||||
days: 5
|
||||
level: Debug
|
||||
path: gtun.log
|
||||
|
||||
http_server:
|
||||
listen_addr: ":9001""
|
16
etc/gtun/gtun.service
Normal file
16
etc/gtun/gtun.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=gtun - ip accelerator base on tproxy
|
||||
After=network.target auditd.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/apps/gtun/gtun -c /opt/apps/gtun/etc/gtun.yaml
|
||||
KillMode=process
|
||||
Restart=always
|
||||
RestartPreventExitStatus=255
|
||||
Type=simple
|
||||
LimitNOFILE=1000000
|
||||
LimitNPROC=1000000
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=gtun.service
|
7
etc/gtun/gtun.yaml
Normal file
7
etc/gtun/gtun.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
route_file: "/opt/apps/gtun/etc/route.json"
|
||||
proxy_file: "/opt/apps/gtun/etc/proxy.yaml"
|
||||
|
||||
log:
|
||||
days: 5
|
||||
level: debug
|
||||
path: /opt/apps/gtun/logs/gtun.log
|
28
etc/gtun/proxy.yaml
Normal file
28
etc/gtun/proxy.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
"HK":
|
||||
tproxy_tcp: |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"listen_addr": ":8524"
|
||||
}
|
||||
tproxy_udp: |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"session_timeout": 30,
|
||||
"listen_addr": ":8524"
|
||||
}
|
||||
"US":
|
||||
tproxy_tcp: |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"listen_addr": ":8525"
|
||||
}
|
||||
tproxy_udp: |
|
||||
{
|
||||
"read_timeout": 30,
|
||||
"write_timeout": 30,
|
||||
"session_timeout": 30,
|
||||
"listen_addr": ":8525"
|
||||
}
|
16
etc/gtund/gtund.service
Normal file
16
etc/gtund/gtund.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=gtund - ip accelerator base on tproxy
|
||||
After=network.target auditd.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/apps/gtund/gtund -c /opt/apps/gtund/etc/gtund.yaml
|
||||
KillMode=process
|
||||
Restart=always
|
||||
RestartPreventExitStatus=255
|
||||
Type=simple
|
||||
LimitNOFILE=1000000
|
||||
LimitNPROC=1000000
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=gtund.service
|
@@ -11,4 +11,4 @@ server:
|
||||
log:
|
||||
days: 5
|
||||
level: "debug"
|
||||
path: "gtund.log"
|
||||
path: "/opt/apps/gtund/logs/gtund.log"
|
3
go.mod
3
go.mod
@@ -7,9 +7,12 @@ require (
|
||||
github.com/agiledragon/gomonkey/v2 v2.10.1
|
||||
github.com/astaxie/beego v1.12.3
|
||||
github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff
|
||||
github.com/beyond-net/golib v0.0.0-20230917030559-01515040c020
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/radovskyb/watcher v1.0.7
|
||||
github.com/smartystreets/goconvey v1.8.1
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
5
go.sum
5
go.sum
@@ -64,6 +64,8 @@ github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHK
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/beyond-net/golib v0.0.0-20230917030559-01515040c020 h1:oPvnpNkZiI7glSP5JZ/9UaGn1dx1jmDTRahh76+x0D0=
|
||||
github.com/beyond-net/golib v0.0.0-20230917030559-01515040c020/go.mod h1:a6+2fw9h26TRFMwTV5UiEfu7ktXHMY/M7nlAqNUdmi8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
@@ -288,6 +290,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -744,6 +748,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@@ -1,61 +0,0 @@
|
||||
package gtun
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
)
|
||||
|
||||
var gConfig *Config
|
||||
|
||||
type Config struct {
|
||||
Settings map[string]RegionConfig `yaml:"settings"`
|
||||
HTTPServer HTTPConfig `yaml:"http_server"`
|
||||
Log Log `yaml:"log"`
|
||||
}
|
||||
|
||||
type HTTPConfig struct {
|
||||
ListenAddr string `yaml:"listen_addr"`
|
||||
}
|
||||
|
||||
type RegionConfig struct {
|
||||
Route []RouteConfig `yaml:"route"`
|
||||
ProxyFile string `yaml:"proxy_file"`
|
||||
Proxy map[string]string `yaml:"proxy"`
|
||||
}
|
||||
|
||||
type RouteConfig struct {
|
||||
Region string `yaml:"region"`
|
||||
TraceAddr string `yaml:"trace_addr"`
|
||||
Scheme string `yaml:"scheme"`
|
||||
Addr string `yaml:"addr"`
|
||||
AuthKey string `yaml:"auth_key"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Days int64 `yaml:"days"`
|
||||
Level string `yaml:"level"`
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
func ParseConfig(path string) (*Config, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseBuffer(content)
|
||||
}
|
||||
|
||||
func ParseBuffer(content []byte) (*Config, error) {
|
||||
conf := Config{}
|
||||
err := yaml.Unmarshal(content, &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gConfig = &conf
|
||||
return &conf, err
|
||||
}
|
||||
|
||||
func GetConfig() *Config {
|
||||
return gConfig
|
||||
}
|
@@ -1,115 +0,0 @@
|
||||
package gtun
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/ICKelin/gtun/gtun/proxy"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HTTPServer struct {
|
||||
listenAddr string
|
||||
}
|
||||
|
||||
func NewHTTPServer(listenAddr string) *HTTPServer {
|
||||
return &HTTPServer{listenAddr: listenAddr}
|
||||
}
|
||||
|
||||
func (s *HTTPServer) ListenAndServe() error {
|
||||
http.HandleFunc("/meta", loadMeta)
|
||||
http.HandleFunc("/ip/add", addIP)
|
||||
http.HandleFunc("/ip/delete", delIP)
|
||||
return http.ListenAndServe(s.listenAddr, nil)
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func loadMeta(w http.ResponseWriter, r *http.Request) {
|
||||
regionList := make([]string, 0)
|
||||
regions := GetConfig().Settings
|
||||
for region, _ := range regions {
|
||||
regionList = append(regionList, region)
|
||||
}
|
||||
|
||||
type replyBody struct {
|
||||
Regions []string `json:"regions"`
|
||||
Cfg *Config
|
||||
}
|
||||
|
||||
body := &replyBody{
|
||||
Regions: regionList,
|
||||
Cfg: GetConfig(),
|
||||
}
|
||||
reply(w, body)
|
||||
}
|
||||
|
||||
func addIP(w http.ResponseWriter, r *http.Request) {
|
||||
type req struct {
|
||||
Region string `json:"region"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
var form = req{}
|
||||
err := bindForm(r, &form)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// add to ipset
|
||||
err = proxy.AddIP(form.Region, form.IP)
|
||||
if err != nil {
|
||||
reply(w, &response{
|
||||
Code: -1,
|
||||
Message: err.Error(),
|
||||
Data: nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
reply(w, &response{Code: 0, Message: "success"})
|
||||
}
|
||||
|
||||
func delIP(w http.ResponseWriter, r *http.Request) {
|
||||
type req struct {
|
||||
Region string `json:"region"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
var form = req{}
|
||||
err := bindForm(r, &form)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// delete from ipset
|
||||
err = proxy.DelIP(form.Region, form.IP)
|
||||
if err != nil {
|
||||
reply(w, &response{
|
||||
Code: -1,
|
||||
Message: err.Error(),
|
||||
Data: nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
reply(w, &response{Code: 0, Message: "success"})
|
||||
}
|
||||
|
||||
func bindForm(r *http.Request, obj interface{}) error {
|
||||
cnt, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(cnt, obj)
|
||||
}
|
||||
|
||||
func reply(w http.ResponseWriter, obj interface{}) {
|
||||
buf, _ := json.Marshal(obj)
|
||||
_, _ = w.Write(buf)
|
||||
}
|
52
gtun/main.go
52
gtun/main.go
@@ -1,52 +0,0 @@
|
||||
package gtun
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/gtun/proxy"
|
||||
"github.com/ICKelin/gtun/gtun/route"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
flgConf := flag.String("c", "", "config file")
|
||||
flag.Parse()
|
||||
|
||||
conf, err := ParseConfig(*flgConf)
|
||||
if err != nil {
|
||||
fmt.Printf("load config fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
|
||||
|
||||
// run proxy
|
||||
for region, cfg := range conf.Settings {
|
||||
// init plugins
|
||||
err = proxy.Setup(region, cfg.ProxyFile, cfg.Proxy)
|
||||
if err != nil {
|
||||
fmt.Printf("set proxy fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// run route and race
|
||||
raceManager := route.GetTraceManager()
|
||||
for region, cfg := range conf.Settings {
|
||||
raceTargets := make([]string, 0)
|
||||
for _, r := range cfg.Route {
|
||||
raceTargets = append(raceTargets, r.TraceAddr)
|
||||
hopConn, err := route.CreateConnection(region, r.Scheme, r.Addr, r.AuthKey)
|
||||
if err != nil {
|
||||
fmt.Printf("connect to %s://%s fail: %v\n", r.Scheme, r.Addr, err)
|
||||
return
|
||||
}
|
||||
go hopConn.ConnectNextHop()
|
||||
}
|
||||
|
||||
regionRace := route.NewTrace(region, raceTargets)
|
||||
raceManager.AddRegionTrace(region, regionRace)
|
||||
}
|
||||
raceManager.RunRace()
|
||||
|
||||
panic(NewHTTPServer(conf.HTTPServer.ListenAddr).ListenAndServe())
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
package gtun
|
||||
|
||||
import (
|
||||
_ "github.com/ICKelin/gtun/gtun/proxy"
|
||||
)
|
@@ -1,83 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
markID = int32(1)
|
||||
routeTableID = int32(101)
|
||||
)
|
||||
|
||||
func allocateMarkID() int32 {
|
||||
return atomic.AddInt32(&markID, 1)
|
||||
}
|
||||
|
||||
func allocateRouteTableID() int32 {
|
||||
return atomic.AddInt32(&routeTableID, 1)
|
||||
}
|
||||
|
||||
func initRedirect(proto, region, redirectPort string) {
|
||||
setName := ipsetNamePrefix + region
|
||||
|
||||
out, err := utils.ExecCmd("ipset", []string{"create", setName, "hash:net"})
|
||||
if err != nil {
|
||||
logs.Warn("create ipset fail: %v %s", err, out)
|
||||
}
|
||||
|
||||
out, err = utils.ExecCmd("ipset", []string{"-F", setName})
|
||||
if err != nil {
|
||||
logs.Warn("flush ipset fail: %v %s", err, out)
|
||||
}
|
||||
|
||||
markID := allocateMarkID()
|
||||
routeTable := allocateRouteTableID()
|
||||
|
||||
args := fmt.Sprintf("-t mangle -D PREROUTING -p %s -m set --match-set %s dst -j TPROXY --tproxy-mark %d/%d --on-port %s", proto, setName, markID, markID, redirectPort)
|
||||
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
args = fmt.Sprintf("-t mangle -A PREROUTING -p %s -m set --match-set %s dst -j TPROXY --tproxy-mark %d/%d --on-port %s", proto, setName, markID, markID, redirectPort)
|
||||
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
|
||||
args = fmt.Sprintf("-t mangle -D OUTPUT -p %s -m set --match-set %s dst -j MARK --set-mark %d", proto, setName, markID)
|
||||
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
args = fmt.Sprintf("-t mangle -A OUTPUT -p %s -m set --match-set %s dst -j MARK --set-mark %d", proto, setName, markID)
|
||||
out, err = utils.ExecCmd("iptables", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
|
||||
args = fmt.Sprintf("rule del fwmark %d lookup %d", markID, routeTable)
|
||||
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
args = fmt.Sprintf("rule add fwmark %d lookup %d", markID, routeTable)
|
||||
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
|
||||
args = fmt.Sprintf("ro del local default dev lo table %d", routeTable)
|
||||
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
args = fmt.Sprintf("ro add local default dev lo table %d", routeTable)
|
||||
out, err = utils.ExecCmd("ip", strings.Split(args, " "))
|
||||
if err != nil {
|
||||
logs.Warn("%s %s %s", args, err, out)
|
||||
}
|
||||
}
|
@@ -1,61 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dummyConn struct {
|
||||
readBuf bytes.Buffer
|
||||
writeBuf bytes.Buffer
|
||||
}
|
||||
|
||||
func (d *dummyConn) Read(b []byte) (n int, err error) {
|
||||
n = copy(b, d.readBuf.Bytes())
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) Write(b []byte) (n int, err error) {
|
||||
return d.writeBuf.Write(b)
|
||||
}
|
||||
|
||||
func (d *dummyConn) Close() error {
|
||||
d.readBuf.Reset()
|
||||
d.writeBuf.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) LocalAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) RemoteAddr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dummyConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTProxyTCPDoProxy(t *testing.T) {
|
||||
p := NewTProxyTCP()
|
||||
cfg := `{}`
|
||||
err := p.Setup([]byte(cfg))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
conn := &dummyConn{}
|
||||
p.(*TProxyTCP).doProxy(conn)
|
||||
}
|
@@ -1,146 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/gtun/route"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/proto"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"github.com/ICKelin/optw/transport"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("tun_proxy", NewTunProxy)
|
||||
}
|
||||
|
||||
type TunProxyConfig struct {
|
||||
Region string `json:"region"`
|
||||
MTU int `json:"mtu"`
|
||||
WriteTimeout int `json:"write_timeout"`
|
||||
ReadTimeout int `json:"read_timeout"`
|
||||
}
|
||||
|
||||
type TunProxy struct {
|
||||
config TunProxyConfig
|
||||
dev *utils.Interface
|
||||
}
|
||||
|
||||
func NewTunProxy() Proxy {
|
||||
return &TunProxy{}
|
||||
}
|
||||
|
||||
func (p *TunProxy) Name() string {
|
||||
return "tun_proxy"
|
||||
}
|
||||
|
||||
func (p *TunProxy) Setup(cfg json.RawMessage) error {
|
||||
var config = TunProxyConfig{}
|
||||
err := json.Unmarshal(cfg, &config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.MTU <= 0 {
|
||||
return fmt.Errorf("%s invalid mtu", p.Name())
|
||||
}
|
||||
|
||||
dev, err := utils.NewInterface()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dev.SetMTU(config.MTU)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dev.Up()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.config = config
|
||||
p.dev = dev
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TunProxy) ListenAndServe() error {
|
||||
// tun proxy use only one stream for long live connection
|
||||
var nextHopStream transport.Stream
|
||||
var nextHopConn *route.HopInfo
|
||||
for {
|
||||
buf, err := p.dev.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nextHopConn == nil || nextHopConn.IsClosed() {
|
||||
nextHopConn = route.GetRouteManager().Route(p.config.Region, "")
|
||||
if nextHopConn == nil {
|
||||
logs.Warn("route to next hop fail")
|
||||
continue
|
||||
}
|
||||
|
||||
nextHopStream, err = nextHopConn.OpenStream()
|
||||
if err != nil {
|
||||
logs.Warn("open stream fail: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// encode proxy protocol
|
||||
bytes := proto.EncodeProxyProtocol("tun_proxy", "", "0", "", "0")
|
||||
_ = nextHopStream.SetWriteDeadline(time.Now().Add(time.Duration(p.config.WriteTimeout)))
|
||||
_, err = nextHopStream.Write(bytes)
|
||||
_ = nextHopStream.SetWriteDeadline(time.Time{})
|
||||
|
||||
go p.readFromRemote(nextHopStream)
|
||||
}
|
||||
|
||||
bytes := proto.EncodeData(buf)
|
||||
nextHopStream.SetWriteDeadline(time.Now().Add(time.Duration(p.config.WriteTimeout)))
|
||||
_, err = nextHopStream.Write(bytes)
|
||||
nextHopStream.SetWriteDeadline(time.Time{})
|
||||
if err != nil {
|
||||
nextHopStream.Close()
|
||||
nextHopConn.Close()
|
||||
logs.Error("stream write fail: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TunProxy) readFromRemote(stream transport.Stream) {
|
||||
defer stream.Close()
|
||||
hdr := make([]byte, 2)
|
||||
for {
|
||||
nr, err := stream.Read(hdr)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
logs.Error("read stream fail %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if nr != 2 {
|
||||
logs.Error("invalid bodylen: %d", nr)
|
||||
continue
|
||||
}
|
||||
|
||||
nlen := binary.BigEndian.Uint16(hdr)
|
||||
buf := make([]byte, nlen)
|
||||
stream.SetReadDeadline(time.Now().Add(time.Duration(p.config.ReadTimeout)))
|
||||
_, err = io.ReadFull(stream, buf)
|
||||
stream.SetReadDeadline(time.Time{})
|
||||
if err != nil {
|
||||
logs.Error("read stream body fail: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
_, err = p.dev.Write(buf)
|
||||
if err != nil {
|
||||
logs.Warn("write to dev fail: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,130 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errRegistered = fmt.Errorf("already registered")
|
||||
var errNotRegister = fmt.Errorf("proxy not register")
|
||||
var ipsetNamePrefix = "GTUN-"
|
||||
|
||||
// Proxy defines Proxies, such as tproxy_tcp, tproxy_udp,ip_tun, ip_wireguard
|
||||
type Proxy interface {
|
||||
Name() string
|
||||
Setup(cfg json.RawMessage) error
|
||||
ListenAndServe() error
|
||||
}
|
||||
|
||||
var registerProxy = make(map[string]func() Proxy)
|
||||
|
||||
func Register(name string, constructor func() Proxy) error {
|
||||
if _, ok := registerProxy[name]; ok {
|
||||
return errRegistered
|
||||
}
|
||||
registerProxy[name] = constructor
|
||||
return nil
|
||||
}
|
||||
|
||||
func Setup(region, ruleFile string, proxyConfigs map[string]string) error {
|
||||
for name, config := range proxyConfigs {
|
||||
constructor := registerProxy[name]
|
||||
if constructor == nil {
|
||||
return errNotRegister
|
||||
}
|
||||
p := constructor()
|
||||
err := p.Setup([]byte(config))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
AddFromFile(region, ruleFile)
|
||||
go p.ListenAndServe()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddIP(region string, ip string) error {
|
||||
out, err := utils.ExecCmd("ipset", []string{"add", ipsetNamePrefix + region, ip})
|
||||
if err != nil {
|
||||
return fmt.Errorf("add to ipset fail: %v %s", err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DelIP(region, ip string) error {
|
||||
out, err := utils.ExecCmd("ipset", []string{"del", ipsetNamePrefix + region, ip})
|
||||
if err != nil {
|
||||
return fmt.Errorf("del from ipset fail: %v %s", err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddApp(region, appName string) error {
|
||||
AddFromFile(region, appName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddFromFile(region, file string) {
|
||||
ips := loadIPs(file)
|
||||
|
||||
for _, ip := range ips {
|
||||
AddIP(region, ip)
|
||||
}
|
||||
}
|
||||
|
||||
func loadIPs(file string) []string {
|
||||
if len(file) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ips := make([]string, 0)
|
||||
var br *bufio.Reader
|
||||
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
|
||||
// load from url
|
||||
req, err := http.NewRequest("GET", file, nil)
|
||||
if err != nil {
|
||||
logs.Warn("load file fail: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cli := http.Client{
|
||||
Timeout: time.Second * 120,
|
||||
}
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
logs.Warn("load file fail: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
br = bufio.NewReader(resp.Body)
|
||||
} else {
|
||||
// load from file
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
logs.Warn("open rule file fail: %v", err)
|
||||
return nil
|
||||
}
|
||||
defer fp.Close()
|
||||
br = bufio.NewReader(fp)
|
||||
}
|
||||
|
||||
for {
|
||||
line, _, err := br.ReadLine()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ips = append(ips, string(line))
|
||||
}
|
||||
|
||||
return ips
|
||||
}
|
@@ -1,105 +0,0 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ICKelin/optw/transport"
|
||||
)
|
||||
|
||||
var routeManager = &Manager{
|
||||
regionHops: make(map[string][]*HopInfo),
|
||||
raceManager: GetTraceManager(),
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
raceManager *TraceManager
|
||||
regionHopsMu sync.RWMutex
|
||||
regionHops map[string][]*HopInfo
|
||||
}
|
||||
|
||||
type HopInfo struct {
|
||||
transport.Conn
|
||||
}
|
||||
|
||||
func GetRouteManager() *Manager {
|
||||
return routeManager
|
||||
}
|
||||
|
||||
func (routeManager *Manager) Route(region, dip string) *HopInfo {
|
||||
routeManager.regionHopsMu.RLock()
|
||||
defer routeManager.regionHopsMu.RUnlock()
|
||||
|
||||
regionHops, ok := routeManager.regionHops[region]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(regionHops) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bestNode := routeManager.raceManager.GetBestNode(region)
|
||||
bestIP := strings.Split(bestNode, ":")[0]
|
||||
for i := 0; i < len(regionHops); i++ {
|
||||
hop := regionHops[i]
|
||||
if hop.IsClosed() {
|
||||
logs.Warn("%s %s is closed", region, hop.RemoteAddr())
|
||||
continue
|
||||
}
|
||||
|
||||
if len(bestIP) != 0 {
|
||||
// use only ip address for the same node
|
||||
// TODO: use scheme://ip:port
|
||||
hopIP := strings.Split(hop.RemoteAddr().String(), ":")[0]
|
||||
if bestIP == hopIP {
|
||||
logs.Debug("best ip match %s", bestIP)
|
||||
return hop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logs.Warn("use random hop")
|
||||
hash := 0
|
||||
for _, c := range dip {
|
||||
hash += int(c)
|
||||
}
|
||||
|
||||
hop := regionHops[hash%len(regionHops)]
|
||||
if hop == nil || hop.IsClosed() {
|
||||
return nil
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
func (routeManager *Manager) AddRoute(region string, hop *HopInfo) {
|
||||
routeManager.regionHopsMu.Lock()
|
||||
defer routeManager.regionHopsMu.Unlock()
|
||||
regionHops := routeManager.regionHops[region]
|
||||
if regionHops == nil {
|
||||
regionHops = make([]*HopInfo, 0)
|
||||
}
|
||||
regionHops = append(regionHops, hop)
|
||||
routeManager.regionHops[region] = regionHops
|
||||
}
|
||||
|
||||
func (routeManager *Manager) DeleteRoute(region string, hop *HopInfo) {
|
||||
routeManager.regionHopsMu.Lock()
|
||||
defer routeManager.regionHopsMu.Unlock()
|
||||
regionHops := routeManager.regionHops[region]
|
||||
if regionHops == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hops := make([]*HopInfo, 0, len(regionHops))
|
||||
for _, s := range regionHops {
|
||||
if s.RemoteAddr().String() == hop.RemoteAddr().String() {
|
||||
continue
|
||||
}
|
||||
|
||||
hops = append(hops, s)
|
||||
}
|
||||
|
||||
routeManager.regionHops[region] = hops
|
||||
}
|
@@ -1,191 +0,0 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"github.com/ICKelin/optw/transport"
|
||||
"github.com/agiledragon/gomonkey/v2"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestManager_AddRoute(t *testing.T) {
|
||||
Convey("Test add route", t, func() {
|
||||
Convey("add first hop for region", func() {
|
||||
GetRouteManager().AddRoute("test_region1", &HopInfo{})
|
||||
So(len(GetRouteManager().regionHops), ShouldEqual, 1)
|
||||
So(len(GetRouteManager().regionHops["test_region1"]), ShouldEqual, 1)
|
||||
|
||||
})
|
||||
|
||||
Convey("add second hop", func() {
|
||||
GetRouteManager().AddRoute("test_region1", &HopInfo{})
|
||||
So(len(GetRouteManager().regionHops), ShouldEqual, 1)
|
||||
So(len(GetRouteManager().regionHops["test_region1"]), ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestManager_DeleteRoute(t *testing.T) {
|
||||
Convey("Test delete route", t, func() {
|
||||
Convey("delete un exist region", func() {
|
||||
GetRouteManager().DeleteRoute("not-found", nil)
|
||||
})
|
||||
|
||||
Convey("delete exist region with empty hops", func() {
|
||||
GetRouteManager().regionHops["region1"] = make([]*HopInfo, 0)
|
||||
GetRouteManager().DeleteRoute("region1", nil)
|
||||
})
|
||||
|
||||
Convey("delete exist region with hops", func() {
|
||||
newHopInfo := &HopInfo{Conn: &mockConn{}}
|
||||
gomonkey.ApplyMethod(newHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
GetRouteManager().AddRoute("region1", newHopInfo)
|
||||
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
|
||||
GetRouteManager().DeleteRoute("region1", newHopInfo)
|
||||
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("delete not exist hops", func() {
|
||||
newHopInfo := &HopInfo{Conn: &mockConn{}}
|
||||
i := 0
|
||||
gomonkey.ApplyMethod(newHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
i += 1
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, byte(i), 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
|
||||
deleteHopInfo := &HopInfo{Conn: &mockConn{}}
|
||||
gomonkey.ApplyMethod(deleteHopInfo.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
i += 1
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, byte(i), 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
|
||||
GetRouteManager().AddRoute("region1", newHopInfo)
|
||||
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
|
||||
|
||||
GetRouteManager().DeleteRoute("region1", deleteHopInfo)
|
||||
So(len(GetRouteManager().regionHops["region1"]), ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestManager_Route(t *testing.T) {
|
||||
Convey("Test route", t, func() {
|
||||
Convey("nil next hop region", func() {
|
||||
hop := GetRouteManager().Route("not-found", "")
|
||||
So(hop, ShouldBeNil)
|
||||
|
||||
GetRouteManager().regionHops["region1"] = make([]*HopInfo, 0)
|
||||
hop = GetRouteManager().Route("region1", "")
|
||||
So(hop, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("next hop is closed", func() {
|
||||
hop := &HopInfo{Conn: &mockConn{}}
|
||||
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
|
||||
return true
|
||||
})
|
||||
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, byte(0), 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
|
||||
GetRouteManager().AddRoute("region1", hop)
|
||||
routeHop := GetRouteManager().Route("region1", "")
|
||||
So(routeHop, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("best ip not match", func() {
|
||||
hop := &HopInfo{Conn: &mockConn{}}
|
||||
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
|
||||
return false
|
||||
})
|
||||
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, byte(0), 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
|
||||
gomonkey.ApplyMethod(GetRouteManager().raceManager, "GetBestNode", func(manager *TraceManager) string {
|
||||
return "192.168.1.1:9000"
|
||||
})
|
||||
GetRouteManager().AddRoute("region2", hop)
|
||||
routeHop := GetRouteManager().Route("region2", "")
|
||||
// use random
|
||||
So(routeHop, ShouldNotBeNil)
|
||||
So(routeHop.RemoteAddr().String(), ShouldEqual, "127.0.0.1:10000")
|
||||
})
|
||||
|
||||
Convey("best ip match", func() {
|
||||
hop := &HopInfo{Conn: &mockConn{}}
|
||||
gomonkey.ApplyMethod(hop.Conn, "IsClosed", func(conn transport.Conn) bool {
|
||||
return false
|
||||
})
|
||||
gomonkey.ApplyMethod(hop.Conn, "RemoteAddr", func(hopInfo transport.Conn) net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, byte(0), 1),
|
||||
Port: 10000,
|
||||
Zone: "",
|
||||
}
|
||||
})
|
||||
|
||||
gomonkey.ApplyMethod(GetRouteManager().raceManager, "GetBestNode", func(manager *TraceManager) string {
|
||||
return "127.0.0.1:10000"
|
||||
})
|
||||
|
||||
GetRouteManager().AddRoute("region3", hop)
|
||||
routeHop := GetRouteManager().Route("region3", "")
|
||||
So(routeHop, ShouldNotBeNil)
|
||||
So(routeHop.RemoteAddr().String(), ShouldEqual, "127.0.0.1:10000")
|
||||
})
|
||||
|
||||
Convey("random next hop", func() {})
|
||||
})
|
||||
}
|
||||
|
||||
type mockConn struct{}
|
||||
|
||||
func (m *mockConn) OpenStream() (transport.Stream, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockConn) AcceptStream() (transport.Stream, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockConn) Close() {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockConn) IsClosed() bool {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m *mockConn) RemoteAddr() net.Addr {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
var _ transport.Conn = &mockConn{}
|
@@ -1,9 +0,0 @@
|
||||
echo "add no proxy address"
|
||||
ipset create GTUN-NOPROXY hash:net
|
||||
iptables -t mangle -I PREROUTING -m set --match-set GTUN-NOPROXY dst -j ACCEPT
|
||||
iptables -t mangle -I OUTPUT -m set --match-set GTUN-NOPROXY dst -j ACCEPT
|
||||
|
||||
echo "start gtun"
|
||||
nohup ./gtun-linux-amd64 -c gtun.yaml &
|
||||
|
||||
echo "start success."
|
7
scripts/install_gtun.sh
Executable file
7
scripts/install_gtun.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
systemctl stop gtun
|
||||
GTUN_DIR="/opt/apps/gtun"
|
||||
mkdir -p $GTUN_DIR/logs
|
||||
cp -r . $GTUN_DIR
|
||||
cp etc/gtun.service /lib/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl start gtun
|
7
scripts/install_gtund.sh
Executable file
7
scripts/install_gtund.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
systemctl stop gtun
|
||||
GTUND_DIR="/opt/apps/gtund"
|
||||
mkdir -p $GTUND_DIR/logs
|
||||
cp -r . $GTUND_DIR
|
||||
cp etc/gtund.service /lib/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl start gtun
|
77
src/gtun/config/config.go
Normal file
77
src/gtun/config/config.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
)
|
||||
|
||||
var gConfig *Config
|
||||
|
||||
type Config struct {
|
||||
RouteFile string `yaml:"route_file"`
|
||||
ProxyFile string `yaml:"proxy_file"`
|
||||
Log Log `yaml:"log"`
|
||||
}
|
||||
|
||||
type RouteConfig struct {
|
||||
Region string `yaml:"region" json:"region"`
|
||||
Scheme string `yaml:"scheme" json:"scheme"`
|
||||
Server string `yaml:"server" json:"server"`
|
||||
Trace string `yaml:"trace" json:"trace"`
|
||||
AuthKey string `yaml:"auth_key" json:"auth_key"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Days int64 `yaml:"days"`
|
||||
Level string `yaml:"level"`
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
func Parse(path string) (*Config, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseBuffer(content)
|
||||
}
|
||||
|
||||
func ParseBuffer(content []byte) (*Config, error) {
|
||||
conf := Config{}
|
||||
err := yaml.Unmarshal(content, &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gConfig = &conf
|
||||
return &conf, err
|
||||
}
|
||||
|
||||
func ParseProxy(proxyFile string) (map[string]map[string]string, error) {
|
||||
content, err := os.ReadFile(proxyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxies := make(map[string]map[string]string)
|
||||
err = yaml.Unmarshal(content, &proxies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
func ParseRoute(routeFile string) ([]*RouteConfig, error) {
|
||||
content, err := os.ReadFile(routeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var routeConfig = make([]*RouteConfig, 0)
|
||||
err = json.Unmarshal(content, &routeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return routeConfig, nil
|
||||
}
|
50
src/gtun/main.go
Normal file
50
src/gtun/main.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/src/gtun/config"
|
||||
"github.com/ICKelin/gtun/src/gtun/proxy"
|
||||
"github.com/ICKelin/gtun/src/gtun/route"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flgConf := flag.String("c", "", "config file")
|
||||
flag.Parse()
|
||||
|
||||
conf, err := config.Parse(*flgConf)
|
||||
if err != nil {
|
||||
fmt.Printf("load config fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
logs.Init(conf.Log.Path, conf.Log.Level, conf.Log.Days)
|
||||
|
||||
routeConfig, err := config.ParseRoute(conf.RouteFile)
|
||||
if err != nil {
|
||||
fmt.Printf("parse node config fail: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
proxyConfig, err := config.ParseProxy(conf.ProxyFile)
|
||||
if err != nil {
|
||||
fmt.Printf("parse proxy config fail: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// run route
|
||||
err = route.Setup(routeConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("route setup fail: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// run proxy
|
||||
err = proxy.Serve(proxyConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("proxy setup fail: %v", err)
|
||||
return
|
||||
}
|
||||
// TODO: watch for config file changes
|
||||
select {}
|
||||
}
|
56
src/gtun/proxy/proxy.go
Normal file
56
src/gtun/proxy/proxy.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
)
|
||||
|
||||
var errRegistered = fmt.Errorf("already registered")
|
||||
var errNotRegister = fmt.Errorf("proxy not register")
|
||||
|
||||
// Proxy defines Proxies, such as tproxy_tcp, tproxy_udp,ip_tun, ip_wireguard
|
||||
type Proxy interface {
|
||||
Name() string
|
||||
Setup(region string, cfg json.RawMessage) error
|
||||
ListenAndServe() error
|
||||
}
|
||||
|
||||
var registerProxy = make(map[string]func() Proxy)
|
||||
|
||||
func Register(name string, constructor func() Proxy) error {
|
||||
if _, ok := registerProxy[name]; ok {
|
||||
return errRegistered
|
||||
}
|
||||
registerProxy[name] = constructor
|
||||
return nil
|
||||
}
|
||||
|
||||
func Serve(proxyConfig map[string]map[string]string) error {
|
||||
for region, p := range proxyConfig {
|
||||
logs.Debug("region %s proxy config %s", region, p)
|
||||
err := setup(region, p)
|
||||
if err != nil {
|
||||
fmt.Printf("region[%s] setup proxy fail: %v\n", region, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setup(region string, proxyConfigs map[string]string) error {
|
||||
for name, config := range proxyConfigs {
|
||||
constructor := registerProxy[name]
|
||||
if constructor == nil {
|
||||
return errNotRegister
|
||||
}
|
||||
p := constructor()
|
||||
err := p.Setup(region, []byte(config))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go p.ListenAndServe()
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -2,14 +2,12 @@ package proxy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/gtun/route"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/proto"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"github.com/ICKelin/gtun/src/gtun/route"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"github.com/ICKelin/gtun/src/internal/proto"
|
||||
"github.com/ICKelin/gtun/src/internal/utils"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -29,7 +27,6 @@ type TProxyTCPConfig struct {
|
||||
WriteTimeout int `json:"write_timeout"`
|
||||
ListenAddr string `json:"listen_addr"`
|
||||
RateLimit int `json:"rate_limit"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type TProxyTCP struct {
|
||||
@@ -59,22 +56,23 @@ func (p *TProxyTCP) Name() string {
|
||||
return "tproxy_tcp"
|
||||
}
|
||||
|
||||
func (p *TProxyTCP) Setup(cfgContent json.RawMessage) error {
|
||||
func (p *TProxyTCP) Setup(region string, cfgContent json.RawMessage) error {
|
||||
var cfg = TProxyTCPConfig{}
|
||||
err := json.Unmarshal(cfgContent, &cfg)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = p.initConfig(cfg)
|
||||
logs.Debug("region[%s] proxy %s config %s", region, p.Name(), string(cfgContent))
|
||||
err = p.initConfig(region, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.initRedirect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
|
||||
func (p *TProxyTCP) initConfig(region string, cfg TProxyTCPConfig) error {
|
||||
tcpReadTimeout := cfg.ReadTimeout
|
||||
if tcpReadTimeout <= 0 {
|
||||
tcpReadTimeout = defaultTCPTimeout
|
||||
@@ -88,7 +86,7 @@ func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
|
||||
rateLimit := utils.NewRateLimit()
|
||||
rateLimit.SetRateLimit(int64(cfg.RateLimit * 1024 * 1024))
|
||||
|
||||
p.region = cfg.Region
|
||||
p.region = region
|
||||
p.listenAddr = cfg.ListenAddr
|
||||
p.writeTimeout = time.Duration(tcpWriteTimeout) * time.Second
|
||||
p.readTimeout = time.Duration(tcpReadTimeout) * time.Second
|
||||
@@ -102,34 +100,28 @@ func (p *TProxyTCP) initConfig(cfg TProxyTCPConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TProxyTCP) initRedirect() error {
|
||||
ipPort := strings.Split(p.listenAddr, ":")
|
||||
if len(ipPort) != 2 {
|
||||
return fmt.Errorf("invalid listen addr")
|
||||
}
|
||||
|
||||
initRedirect("tcp", p.region, ipPort[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TProxyTCP) ListenAndServe() error {
|
||||
listener, err := net.Listen("tcp", p.listenAddr)
|
||||
if err != nil {
|
||||
logs.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// set socket with ip transparent option
|
||||
file, err := listener.(*net.TCPListener).File()
|
||||
if err != nil {
|
||||
logs.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
|
||||
if err != nil {
|
||||
logs.Error("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logs.Info("region[%s] %s listen %s", p.region, p.Name(), p.listenAddr)
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
@@ -5,14 +5,13 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/gtun/route"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/proto"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"github.com/ICKelin/gtun/src/gtun/route"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"github.com/ICKelin/gtun/src/internal/proto"
|
||||
utils2 "github.com/ICKelin/gtun/src/internal/utils"
|
||||
"github.com/ICKelin/optw/transport"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -42,7 +41,6 @@ type TProxyUDPConfig struct {
|
||||
SessionTimeout int `json:"session_timeout"`
|
||||
ListenAddr string `json:"listen_addr"`
|
||||
RateLimit int `json:"rate_limit"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type TProxyUDP struct {
|
||||
@@ -60,7 +58,7 @@ type TProxyUDP struct {
|
||||
udpSessions map[string]*udpSession
|
||||
udpsessLock sync.Mutex
|
||||
|
||||
ratelimit *utils.RateLimit
|
||||
ratelimit *utils2.RateLimit
|
||||
}
|
||||
|
||||
func NewTProxyUDP() Proxy {
|
||||
@@ -71,22 +69,22 @@ func (p *TProxyUDP) Name() string {
|
||||
return "tproxy_udp"
|
||||
}
|
||||
|
||||
func (p *TProxyUDP) Setup(cfgContent json.RawMessage) error {
|
||||
func (p *TProxyUDP) Setup(region string, cfgContent json.RawMessage) error {
|
||||
var cfg = TProxyUDPConfig{}
|
||||
err := json.Unmarshal(cfgContent, &cfg)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = p.initConfig(cfg)
|
||||
err = p.initConfig(region, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.initRedirect()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TProxyUDP) initConfig(cfg TProxyUDPConfig) error {
|
||||
func (p *TProxyUDP) initConfig(region string, cfg TProxyUDPConfig) error {
|
||||
readTimeout := cfg.ReadTimeout
|
||||
if readTimeout <= 0 {
|
||||
readTimeout = defaultUDPTimeout
|
||||
@@ -102,10 +100,10 @@ func (p *TProxyUDP) initConfig(cfg TProxyUDPConfig) error {
|
||||
sessionTimeout = defaultUDPSessionTimeout
|
||||
}
|
||||
|
||||
rateLimit := utils.NewRateLimit()
|
||||
rateLimit := utils2.NewRateLimit()
|
||||
rateLimit.SetRateLimit(int64(cfg.RateLimit * 1024 * 1024))
|
||||
|
||||
p.region = cfg.Region
|
||||
p.region = region
|
||||
p.listenAddr = cfg.ListenAddr
|
||||
p.writeTimeout = time.Duration(writeTimeout) * time.Second
|
||||
p.readTimeout = time.Duration(readTimeout) * time.Second
|
||||
@@ -116,16 +114,6 @@ func (p *TProxyUDP) initConfig(cfg TProxyUDPConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TProxyUDP) initRedirect() error {
|
||||
ipPort := strings.Split(p.listenAddr, ":")
|
||||
if len(ipPort) != 2 {
|
||||
return fmt.Errorf("invalid listen addr")
|
||||
}
|
||||
|
||||
initRedirect("udp", p.region, ipPort[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListenAndServe listens an udp port, since that we use tproxy to
|
||||
// redirect traffic to this listened udp port
|
||||
// so the socket should set to ip transparent option
|
||||
@@ -177,6 +165,7 @@ func (p *TProxyUDP) ListenAndServe() error {
|
||||
}
|
||||
|
||||
func (p *TProxyUDP) serve(lconn *net.UDPConn) error {
|
||||
logs.Info("region[%s] %s listen %s", p.region, p.Name(), p.listenAddr)
|
||||
go p.recycleSession()
|
||||
buf := make([]byte, 64*1024)
|
||||
oob := make([]byte, 1024)
|
||||
@@ -283,7 +272,7 @@ func (p *TProxyUDP) doProxy(stream transport.Stream, sessionKey string, fromaddr
|
||||
break
|
||||
}
|
||||
|
||||
err = utils.SendUDPViaRaw(p.rawfd, fromaddr, toaddr, buf)
|
||||
err = utils2.SendUDPViaRaw(p.rawfd, fromaddr, toaddr, buf)
|
||||
if err != nil {
|
||||
logs.Error("send via raw socket fail: %v", err)
|
||||
}
|
@@ -2,33 +2,41 @@ package route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"github.com/ICKelin/optw/transport/transport_api"
|
||||
"time"
|
||||
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/optw/transport"
|
||||
)
|
||||
|
||||
type ConnectionConfig struct {
|
||||
Region string
|
||||
ServerAddr string
|
||||
AuthKey string
|
||||
var cm = &connManager{regionConn: map[string][]*conn{}}
|
||||
|
||||
type connManager struct {
|
||||
regionConn map[string][]*conn
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
func (cm *connManager) startConn() {
|
||||
for _, conns := range cm.regionConn {
|
||||
for _, conn := range conns {
|
||||
go conn.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
dialer transport.Dialer
|
||||
region string
|
||||
scheme string
|
||||
serverAddr string
|
||||
}
|
||||
|
||||
func CreateConnection(region, scheme, serverAddr, authKey string) (*Connection, error) {
|
||||
func newConn(region, scheme, serverAddr, authKey string) (*conn, error) {
|
||||
dialer, err := transport_api.NewDialer(scheme, serverAddr, authKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Connection{
|
||||
return &conn{
|
||||
dialer: dialer,
|
||||
region: region,
|
||||
scheme: scheme,
|
||||
@@ -36,7 +44,7 @@ func CreateConnection(region, scheme, serverAddr, authKey string) (*Connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Connection) ConnectNextHop() {
|
||||
func (c *conn) connect() {
|
||||
for {
|
||||
conn, err := c.dialer.Dial()
|
||||
if err != nil {
|
||||
@@ -46,23 +54,28 @@ func (c *Connection) ConnectNextHop() {
|
||||
}
|
||||
|
||||
logs.Info("connect to %s success", c.String())
|
||||
hopConn := &HopInfo{Conn: conn}
|
||||
|
||||
// add next hop connection to route
|
||||
GetRouteManager().AddRoute(c.region, hopConn)
|
||||
routeEle := &routeItem{
|
||||
region: c.region,
|
||||
scheme: c.scheme,
|
||||
serverAddr: c.serverAddr,
|
||||
Conn: conn,
|
||||
}
|
||||
GetRouteManager().addRoute(c.region, routeEle)
|
||||
tick := time.NewTicker(time.Second * 1)
|
||||
for range tick.C {
|
||||
if hopConn.IsClosed() {
|
||||
if conn.IsClosed() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// delete next hop connection to route
|
||||
GetRouteManager().DeleteRoute(c.region, hopConn)
|
||||
GetRouteManager().deleteRoute(c.region, routeEle)
|
||||
logs.Warn("reconnect %s", c.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) String() string {
|
||||
func (c *conn) String() string {
|
||||
return fmt.Sprintf("regions[%s] %s://%s", c.region, c.scheme, c.serverAddr)
|
||||
}
|
146
src/gtun/route/route.go
Normal file
146
src/gtun/route/route.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/src/gtun/config"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"github.com/ICKelin/optw/transport"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var routeManager = &Manager{
|
||||
tm: tm,
|
||||
cm: cm,
|
||||
routeTable: make(map[string][]*routeItem),
|
||||
}
|
||||
|
||||
type routeItem struct {
|
||||
region string
|
||||
scheme string
|
||||
serverAddr string
|
||||
transport.Conn
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
tm *traceManager
|
||||
cm *connManager
|
||||
|
||||
routeTableMu sync.Mutex
|
||||
routeTable map[string][]*routeItem
|
||||
}
|
||||
|
||||
func GetRouteManager() *Manager {
|
||||
return routeManager
|
||||
}
|
||||
|
||||
func (routeManager *Manager) Route(region, dip string) transport.Conn {
|
||||
regionRoutes, ok := routeManager.routeTable[region]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(regionRoutes) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bestNode, ok := routeManager.tm.getRegionBestTarget(region)
|
||||
if ok {
|
||||
bestAddr := bestNode.serverAddr
|
||||
for i := 0; i < len(regionRoutes); i++ {
|
||||
it := regionRoutes[i]
|
||||
if it.IsClosed() {
|
||||
logs.Warn("%s %s is closed", region, it.RemoteAddr())
|
||||
continue
|
||||
}
|
||||
|
||||
if len(bestAddr) != 0 {
|
||||
// scheme://ip:port match
|
||||
if bestAddr == it.serverAddr &&
|
||||
bestNode.scheme == it.scheme {
|
||||
logs.Debug("region[%s] best node match %s://%s",
|
||||
region, it.scheme, bestAddr)
|
||||
return it
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logs.Warn("no best node for region[%s]", region)
|
||||
}
|
||||
|
||||
logs.Warn("region[%s] use random hop", region)
|
||||
hash := 0
|
||||
for _, c := range dip {
|
||||
hash += int(c)
|
||||
}
|
||||
|
||||
hop := regionRoutes[hash%len(regionRoutes)]
|
||||
if hop == nil || hop.IsClosed() {
|
||||
return nil
|
||||
}
|
||||
return hop
|
||||
}
|
||||
|
||||
func (routeManager *Manager) addRoute(region string, item *routeItem) {
|
||||
routeManager.routeTableMu.Lock()
|
||||
defer routeManager.routeTableMu.Unlock()
|
||||
|
||||
regionItems := routeManager.routeTable[region]
|
||||
if regionItems == nil {
|
||||
regionItems = make([]*routeItem, 0)
|
||||
}
|
||||
|
||||
regionItems = append(regionItems, item)
|
||||
routeManager.routeTable[region] = regionItems
|
||||
}
|
||||
|
||||
func (routeManager *Manager) deleteRoute(region string, item *routeItem) {
|
||||
routeManager.routeTableMu.Lock()
|
||||
defer routeManager.routeTableMu.Unlock()
|
||||
regionItems := routeManager.routeTable[region]
|
||||
if regionItems == nil {
|
||||
return
|
||||
}
|
||||
|
||||
conns := make([]*routeItem, 0, len(regionItems))
|
||||
for _, it := range regionItems {
|
||||
if it == item {
|
||||
continue
|
||||
}
|
||||
|
||||
conns = append(conns, it)
|
||||
}
|
||||
|
||||
routeManager.routeTable[region] = conns
|
||||
}
|
||||
|
||||
func Setup(routeConfig []*config.RouteConfig) error {
|
||||
for _, cfg := range routeConfig {
|
||||
conn, err := newConn(cfg.Region, cfg.Scheme, cfg.Server, cfg.AuthKey)
|
||||
if err != nil {
|
||||
fmt.Printf("region[%s] connect to %s://%s fail: %v",
|
||||
cfg.Region, cfg.Scheme, cfg.Server, cfg.AuthKey)
|
||||
return err
|
||||
}
|
||||
|
||||
cm.regionConn[cfg.Region] = append(cm.regionConn[cfg.Region], conn)
|
||||
|
||||
t, ok := tm.regionTrace[cfg.Region]
|
||||
if !ok {
|
||||
logs.Debug("add region[%s] trace", cfg.Region)
|
||||
t = newTrace(cfg.Region)
|
||||
tm.regionTrace[cfg.Region] = t
|
||||
} else {
|
||||
logs.Debug("region[%s] trace exist", cfg.Region)
|
||||
}
|
||||
|
||||
t.addTarget(traceTarget{
|
||||
traceAddr: cfg.Trace,
|
||||
serverAddr: cfg.Server,
|
||||
scheme: cfg.Scheme,
|
||||
})
|
||||
}
|
||||
|
||||
go tm.startTrace()
|
||||
go cm.startConn()
|
||||
return nil
|
||||
}
|
@@ -2,82 +2,81 @@ package route
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
)
|
||||
|
||||
var traceManager = &TraceManager{
|
||||
regionTrace: make(map[string]*Trace),
|
||||
var tm = &traceManager{
|
||||
regionTrace: make(map[string]*trace),
|
||||
}
|
||||
|
||||
func GetTraceManager() *TraceManager {
|
||||
return traceManager
|
||||
type traceManager struct {
|
||||
regionTrace map[string]*trace
|
||||
}
|
||||
|
||||
// TraceManager manage region trace
|
||||
type TraceManager struct {
|
||||
regionTraceMu sync.Mutex
|
||||
regionTrace map[string]*Trace
|
||||
}
|
||||
|
||||
func (m *TraceManager) RunRace() {
|
||||
for _, race := range m.regionTrace {
|
||||
go race.Run()
|
||||
func (m *traceManager) addTraces(traces map[string]*trace) {
|
||||
for k, _ := range traces {
|
||||
m.regionTrace[k] = traces[k]
|
||||
}
|
||||
}
|
||||
|
||||
// AddRegionTrace adds a trace instance for region
|
||||
func (m *TraceManager) AddRegionTrace(region string, race *Trace) {
|
||||
m.regionTraceMu.Lock()
|
||||
defer m.regionTraceMu.Unlock()
|
||||
m.regionTrace[region] = race
|
||||
func (m *traceManager) startTrace() {
|
||||
for _, trace := range m.regionTrace {
|
||||
logs.Debug("region[%s] running trace", trace.region)
|
||||
go trace.runTraceJob()
|
||||
}
|
||||
}
|
||||
|
||||
// GetBestNode returns the highest score of region target region
|
||||
func (m *TraceManager) GetBestNode(region string) string {
|
||||
regionRace := m.regionTrace[region]
|
||||
if regionRace == nil {
|
||||
return ""
|
||||
func (m *traceManager) getRegionBestTarget(region string) (traceTarget, bool) {
|
||||
regionTrace := m.regionTrace[region]
|
||||
if regionTrace == nil {
|
||||
logs.Warn("trace for region[%s] not exist", region)
|
||||
return traceTarget{}, false
|
||||
}
|
||||
|
||||
return regionRace.GetBestNode()
|
||||
return regionTrace.getBestNode()
|
||||
}
|
||||
|
||||
// Trace is a region trace instance
|
||||
type Trace struct {
|
||||
type traceTarget struct {
|
||||
traceAddr string
|
||||
serverAddr string
|
||||
scheme string
|
||||
}
|
||||
|
||||
type trace struct {
|
||||
region string
|
||||
targets []string
|
||||
targets []traceTarget
|
||||
targetScoreMu sync.Mutex
|
||||
targetScore map[string]float64
|
||||
targetScore map[traceTarget]float64
|
||||
totalRtt int32
|
||||
}
|
||||
|
||||
// NewTrace return trace instance
|
||||
func NewTrace(region string, targets []string) *Trace {
|
||||
return &Trace{
|
||||
func newTrace(region string) *trace {
|
||||
return &trace{
|
||||
region: region,
|
||||
targets: targets,
|
||||
targetScoreMu: sync.Mutex{},
|
||||
targetScore: make(map[string]float64),
|
||||
targetScore: make(map[traceTarget]float64),
|
||||
}
|
||||
}
|
||||
|
||||
// Run trace job
|
||||
func (r *Trace) Run() {
|
||||
r.trace()
|
||||
func (t *trace) addTarget(target traceTarget) {
|
||||
t.targets = append(t.targets, target)
|
||||
}
|
||||
|
||||
func (t *trace) runTraceJob() {
|
||||
t.trace()
|
||||
tick := time.NewTicker(time.Second * 120)
|
||||
for range tick.C {
|
||||
r.trace()
|
||||
t.trace()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Trace) trace() {
|
||||
for _, target := range r.targets {
|
||||
raddr, err := net.ResolveUDPAddr("udp", target)
|
||||
func (t *trace) trace() {
|
||||
for i, target := range t.targets {
|
||||
raddr, err := net.ResolveUDPAddr("udp", target.traceAddr)
|
||||
if err != nil {
|
||||
logs.Error("resolve udp addr: %v", err)
|
||||
continue
|
||||
@@ -117,21 +116,20 @@ func (r *Trace) trace() {
|
||||
diff := time.Now().Sub(beg).Milliseconds()
|
||||
rtt += int(diff)
|
||||
}
|
||||
remoteAddr := rconn.RemoteAddr().String()
|
||||
rconn.Close()
|
||||
|
||||
if rtt < 0 {
|
||||
rtt = math.MaxInt
|
||||
}
|
||||
|
||||
lossRank := r.calcLossScore(loss)
|
||||
delayRank := r.calcRttScore(rtt)
|
||||
lossRank := t.calcLossScore(loss)
|
||||
delayRank := t.calcRttScore(rtt)
|
||||
score := lossRank + delayRank
|
||||
logs.Debug("region[%s] %s loss %d rtt %d lossRank %.4f delayRank %.4f score %.4f",
|
||||
r.region, target, loss, rtt, lossRank, delayRank, score)
|
||||
r.targetScoreMu.Lock()
|
||||
r.targetScore[remoteAddr] = score
|
||||
r.targetScoreMu.Unlock()
|
||||
t.region, target, loss, rtt, lossRank, delayRank, score)
|
||||
t.targetScoreMu.Lock()
|
||||
t.targetScore[t.targets[i]] = score
|
||||
t.targetScoreMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +138,7 @@ func (r *Trace) trace() {
|
||||
// f(p) = 35+(1.25-p)x10 0.75% < p <= 1.25%,
|
||||
// f(p) = 30+(2.25-p)x5 1.25% < p <= 2.25%,
|
||||
// f(p) = 30+(p-2.25)x5x-1 p > 2.25%
|
||||
func (r *Trace) calcLossScore(loss int) float64 {
|
||||
func (t *trace) calcLossScore(loss int) float64 {
|
||||
lossRate := float64(loss) / 60
|
||||
if 0 < lossRate && lossRate <= 0.75 {
|
||||
return 40 + (0.75-lossRate)*13
|
||||
@@ -154,7 +152,7 @@ func (r *Trace) calcLossScore(loss int) float64 {
|
||||
return 50
|
||||
}
|
||||
|
||||
func (r *Trace) calcRttScore(rtt int) float64 {
|
||||
func (t *trace) calcRttScore(rtt int) float64 {
|
||||
avgRtt := float64(rtt) / 60
|
||||
if 0 < avgRtt && avgRtt < 45.0 {
|
||||
return 50
|
||||
@@ -171,17 +169,18 @@ func (r *Trace) calcRttScore(rtt int) float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetBestNode of all the targets of trace
|
||||
func (r *Trace) GetBestNode() string {
|
||||
r.targetScoreMu.Lock()
|
||||
defer r.targetScoreMu.Unlock()
|
||||
func (t *trace) getBestNode() (traceTarget, bool) {
|
||||
t.targetScoreMu.Lock()
|
||||
defer t.targetScoreMu.Unlock()
|
||||
bestScore := float64(-1)
|
||||
node := ""
|
||||
for target, score := range r.targetScore {
|
||||
if bestScore < score {
|
||||
var node traceTarget
|
||||
var ok bool = false
|
||||
for target, score := range t.targetScore {
|
||||
if bestScore*1000 < score*1000 {
|
||||
bestScore = score
|
||||
node = target
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return node
|
||||
return node, ok
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package gtund
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
@@ -1,14 +1,13 @@
|
||||
package gtund
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/ICKelin/optw/transport/transport_api"
|
||||
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
)
|
||||
|
||||
var version = ""
|
||||
@@ -17,7 +16,7 @@ func init() {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
|
||||
func Main() {
|
||||
func main() {
|
||||
flgVersion := flag.Bool("v", false, "print version")
|
||||
flgConf := flag.String("c", "", "config file")
|
||||
flag.Parse()
|
@@ -1,17 +1,17 @@
|
||||
package gtund
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/internal/utils"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"github.com/ICKelin/gtun/src/internal/proto"
|
||||
"github.com/ICKelin/gtun/src/internal/utils"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/internal/proto"
|
||||
"github.com/ICKelin/optw/transport"
|
||||
)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package gtund
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"net"
|
||||
)
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package logs
|
@@ -24,13 +24,13 @@
|
||||
//
|
||||
// Use it like this:
|
||||
//
|
||||
// log.Trace("trace")
|
||||
// log.Info("info")
|
||||
// log.Warn("warning")
|
||||
// log.Debug("debug")
|
||||
// log.Critical("critical")
|
||||
// log.Trace("trace")
|
||||
// log.Info("info")
|
||||
// log.Warn("warning")
|
||||
// log.Debug("debug")
|
||||
// log.Critical("critical")
|
||||
//
|
||||
// more docs http://beego.me/docs/module/logs.md
|
||||
// more docs http://beego.me/docs/module/logs.md
|
||||
package logs
|
||||
|
||||
import (
|
@@ -2,7 +2,7 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ICKelin/gtun/internal/logs"
|
||||
"github.com/ICKelin/gtun/src/internal/logs"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
38
src/node-agent/config.go
Normal file
38
src/node-agent/config.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
GtunTemplateFile string `yaml:"gtun_template_file"`
|
||||
GtunService string `yaml:"gtun_service"`
|
||||
GtunConfigFilePath string `yaml:"gtun_config_file_path"`
|
||||
GtunDynamicConfigFile string `yaml:"gtun_dynamic_config_file"`
|
||||
Log Log `yaml:"log"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Days int64 `yaml:"days"`
|
||||
Level string `yaml:"level"`
|
||||
Path string `yaml:"path"`
|
||||
}
|
||||
|
||||
func ParseConfig(path string) (*Config, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseBuffer(content)
|
||||
}
|
||||
|
||||
func ParseBuffer(content []byte) (*Config, error) {
|
||||
conf := Config{}
|
||||
err := yaml.Unmarshal(content, &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &conf, err
|
||||
}
|
9
src/node-agent/config.yaml
Normal file
9
src/node-agent/config.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
log:
|
||||
days: 5
|
||||
level: Debug
|
||||
path: gtun.log
|
||||
|
||||
gtun_template_file: "/opt/apps/gtun/gtun.template"
|
||||
gtun_service: "gtun.service"
|
||||
gtun_config_file_path: "/opt/apps/gtun/gtun.yaml"
|
||||
gtun_dynamic_config_file: "/opt/apps/gtun/node.json"
|
59
src/node-agent/main.go
Normal file
59
src/node-agent/main.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/beyond-net/golib/logs"
|
||||
"github.com/radovskyb/watcher"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
confPath = ""
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&confPath, "c", "", "config file path")
|
||||
flag.Parse()
|
||||
|
||||
cfg, err := ParseConfig(confPath)
|
||||
if err != nil {
|
||||
fmt.Printf("parse config fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
logs.Init(cfg.Log.Path, cfg.Log.Level, 5)
|
||||
logs.Info("%+v", cfg)
|
||||
|
||||
w := watcher.New()
|
||||
err = w.Add(cfg.GtunDynamicConfigFile)
|
||||
if err != nil {
|
||||
logs.Error("watch %s fail: %v", cfg.GtunDynamicConfigFile)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-w.Event:
|
||||
logs.Info("file %s modify", event.FileInfo.Name())
|
||||
switch event.FileInfo.Name() {
|
||||
case cfg.GtunDynamicConfigFile:
|
||||
// TODO: reload gtun
|
||||
default:
|
||||
|
||||
}
|
||||
case err := <-w.Error:
|
||||
logs.Warn("file watcher error occurs: %v", err)
|
||||
case <-w.Closed:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = w.Start(time.Millisecond * 100)
|
||||
if err != nil {
|
||||
logs.Warn("file watcher start error: %v", err)
|
||||
}
|
||||
select {}
|
||||
}
|
90
src/node-agent/service.go
Normal file
90
src/node-agent/service.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/beyond-net/golib/logs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
gtunConfigFile = "/opt/apps/gtun/gtun.yaml"
|
||||
gtunConfigTemplate = "../etc/gtun.template"
|
||||
gtunService = "gtun.service"
|
||||
)
|
||||
|
||||
type GtunConfigItem struct {
|
||||
Region string
|
||||
Scheme string
|
||||
ServerIP string
|
||||
ServerTracePort int
|
||||
ServerPort int
|
||||
ListenPort int
|
||||
Rate int
|
||||
}
|
||||
|
||||
func ReloadGtun(config []*GtunConfigItem) error {
|
||||
// render config
|
||||
tpl, err := template.ParseFiles(gtunConfigTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
br := &bytes.Buffer{}
|
||||
err = tpl.Execute(br, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ReloadService(gtunConfigFile, br.String(), gtunService)
|
||||
}
|
||||
|
||||
func ReloadService(configFile, configContent, service string) error {
|
||||
// backup config file
|
||||
_, err := os.Stat(configFile)
|
||||
reloadSuccess := false
|
||||
if err == nil {
|
||||
backupFile := fmt.Sprintf("%s.%d", gtunConfigFile, time.Now().UnixMicro())
|
||||
_, err = exec.Command("cp", []string{gtunConfigFile, backupFile}...).CombinedOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// recover
|
||||
defer func() {
|
||||
if !reloadSuccess {
|
||||
_, err := exec.Command("cp", []string{backupFile, gtunConfigFile}...).CombinedOutput()
|
||||
if err != nil {
|
||||
logs.Error("recover config file fail: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// write new config
|
||||
fp, err := os.Create(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fp.Close()
|
||||
_, err = fp.Write([]byte(configContent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// restart service
|
||||
err = RestartService(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reloadSuccess = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func RestartService(service string) error {
|
||||
_, err := exec.Command("systemctl", []string{"restart", service}...).CombinedOutput()
|
||||
return err
|
||||
}
|
26
src/node-agent/service_test.go
Normal file
26
src/node-agent/service_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBootGtun(t *testing.T) {
|
||||
ReloadGtun([]*GtunConfigItem{
|
||||
{
|
||||
Region: "CN",
|
||||
Scheme: "kcp",
|
||||
ServerIP: "127.0.0.1",
|
||||
ServerTracePort: 3003,
|
||||
ServerPort: 3002,
|
||||
ListenPort: 8524,
|
||||
Rate: 50,
|
||||
},
|
||||
{
|
||||
Region: "US",
|
||||
Scheme: "kcp",
|
||||
ServerIP: "127.0.0.1",
|
||||
ServerTracePort: 4003,
|
||||
ServerPort: 4002,
|
||||
ListenPort: 8525,
|
||||
Rate: 50,
|
||||
},
|
||||
})
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
echo "add no proxy address"
|
||||
iptables -t mangle -D PREROUTING -m set --match-set GTUN-NOPROXY dst -j ACCEPT
|
||||
iptables -t mangle -D OUTPUT -m set --match-set GTUN-NOPROXY dst -j ACCEPT
|
||||
ipset destroy GTUN-NOPROXY
|
||||
|
||||
echo "stop gtun"
|
||||
killall gtun-linux_amd64
|
Reference in New Issue
Block a user