mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-11-06 12:06:09 +08:00
增加基于tap设备的桥接访问模式
This commit is contained in:
@@ -2,5 +2,5 @@ package common
|
||||
|
||||
const (
|
||||
APP_NAME = "AnyLink"
|
||||
APP_VER = "0.0.1"
|
||||
APP_VER = "0.0.3"
|
||||
)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package common
|
||||
|
||||
import "testing"
|
||||
|
||||
func AssertTrue(t *testing.T, a bool) {
|
||||
t.Helper()
|
||||
if !a {
|
||||
t.Errorf("Not True %t", a)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,11 @@ import (
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
const (
|
||||
LinkModeTUN = "tun"
|
||||
LinkModeTAP = "tap"
|
||||
)
|
||||
|
||||
var (
|
||||
ServerCfg = &ServerConfig{}
|
||||
)
|
||||
@@ -24,28 +29,34 @@ var (
|
||||
// rekey-method = ssl
|
||||
|
||||
type ServerConfig struct {
|
||||
UserFile string `toml:"user_file"`
|
||||
ServerAddr string `toml:"server_addr"`
|
||||
DebugAddr string `toml:"debug_addr"`
|
||||
ProxyProtocol bool `toml:"proxy_protocol"`
|
||||
CertFile string `toml:"cert_file"`
|
||||
CertKey string `toml:"cert_key"`
|
||||
LinkGroups []string `toml:"link_groups"`
|
||||
ServerAddr string `toml:"server_addr"`
|
||||
AdminAddr string `toml:"admin_addr"`
|
||||
ProxyProtocol bool `toml:"proxy_protocol"`
|
||||
DbFile string `toml:"db_file"`
|
||||
CertFile string `toml:"cert_file"`
|
||||
CertKey string `toml:"cert_key"`
|
||||
LogLevel string `toml:"log_level"`
|
||||
|
||||
LinkMode string `toml:"link_mode"` // tun tap
|
||||
Ipv4Network string `toml:"ipv4_network"` // 192.168.1.0
|
||||
Ipv4Netmask string `toml:"ipv4_netmask"` // 255.255.255.0
|
||||
Ipv4Gateway string `toml:"ipv4_gateway"`
|
||||
Ipv4Pool []string `toml:"ipv4_pool"` // Pool[0]=192.168.1.100 Pool[1]=192.168.1.200
|
||||
Include []string `toml:"include"` // 10.10.10.0/255.255.255.0
|
||||
Exclude []string `toml:"exclude"` // 192.168.5.0/255.255.255.0
|
||||
ClientDns []string `toml:"client_dns"` // 114.114.114.114
|
||||
AllowLan bool `toml:"allow_lan"` // 允许本地LAN访问vpn网络
|
||||
MaxClient int `toml:"max_client"`
|
||||
MaxUserClient int `toml:"max_user_client"`
|
||||
|
||||
UserGroups []string `toml:"user_groups"`
|
||||
DefaultGroup string `toml:"default_group"`
|
||||
Banner string `toml:"banner"` // 欢迎语
|
||||
CstpDpd int `toml:"cstp_dpd"` // Dead peer detection in seconds
|
||||
Banner string `toml:"banner"` // 欢迎语
|
||||
CstpDpd int `toml:"cstp_dpd"` // Dead peer detection in seconds
|
||||
MobileDpd int `toml:"mobile_dpd"`
|
||||
CstpKeepalive int `toml:"cstp_keepalive"` // in seconds
|
||||
SessionTimeout int `toml:"session_timeout"` // in seconds
|
||||
AuthTimeout int `toml:"auth_timeout"` // in seconds
|
||||
MaxClient int `toml:"max_client"`
|
||||
MaxUserClient int `toml:"max_user_client"`
|
||||
Ipv4Network string `toml:"ipv4_network"` // 192.168.1.0
|
||||
Ipv4Netmask string `toml:"ipv4_netmask"` // 255.255.255.0
|
||||
Ipv4GateWay string `toml:"-"`
|
||||
Include []string `toml:"include"` // 10.10.10.0/255.255.255.0
|
||||
Exclude []string `toml:"exclude"` // 192.168.5.0/255.255.255.0
|
||||
ClientDns []string `toml:"client_dns"` // 114.114.114.114
|
||||
AllowLan bool `toml:"allow_lan"` // 允许本地LAN访问vpn网络
|
||||
}
|
||||
|
||||
func loadServer() {
|
||||
@@ -62,7 +73,7 @@ func loadServer() {
|
||||
base := filepath.Dir(sf)
|
||||
|
||||
// 转换成绝对路径
|
||||
ServerCfg.UserFile = getAbsPath(base, ServerCfg.UserFile)
|
||||
ServerCfg.DbFile = getAbsPath(base, ServerCfg.DbFile)
|
||||
ServerCfg.CertFile = getAbsPath(base, ServerCfg.CertFile)
|
||||
ServerCfg.CertKey = getAbsPath(base, ServerCfg.CertKey)
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
var (
|
||||
users = map[string]User{}
|
||||
limitClient = map[string]int{"_all": 0}
|
||||
limitMux = sync.Mutex{}
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Group string `toml:"group"`
|
||||
Username string `toml:"-"`
|
||||
Password string `toml:"password"`
|
||||
OtpSecret string `toml:"otp_secret"`
|
||||
}
|
||||
|
||||
func CheckUser(name, pwd, group string) bool {
|
||||
user, ok := users[name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
pwdHash := hashPass(pwd)
|
||||
if user.Password == pwdHash {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hashPass(pwd string) string {
|
||||
sum := sha1.Sum([]byte(pwd))
|
||||
return fmt.Sprintf("%x", sum)
|
||||
}
|
||||
|
||||
func LimitClient(name string, close bool) bool {
|
||||
limitMux.Lock()
|
||||
defer limitMux.Unlock()
|
||||
// defer fmt.Println(limitClient)
|
||||
|
||||
_all := limitClient["_all"]
|
||||
c, ok := limitClient[name]
|
||||
if !ok { // 不存在用户
|
||||
limitClient[name] = 0
|
||||
}
|
||||
|
||||
if close {
|
||||
limitClient[name] = c - 1
|
||||
limitClient["_all"] = _all - 1
|
||||
return true
|
||||
}
|
||||
|
||||
// 全局判断
|
||||
if _all >= ServerCfg.MaxClient {
|
||||
return false
|
||||
}
|
||||
|
||||
// 超出同一个用户限制
|
||||
if c >= ServerCfg.MaxUserClient {
|
||||
return false
|
||||
}
|
||||
|
||||
limitClient[name] = c + 1
|
||||
limitClient["_all"] = _all + 1
|
||||
return true
|
||||
}
|
||||
|
||||
func loadUser() {
|
||||
b, err := ioutil.ReadFile(ServerCfg.UserFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = toml.Unmarshal(b, &users)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 添加用户名
|
||||
for k, v := range users {
|
||||
v.Username = k
|
||||
users[k] = v
|
||||
}
|
||||
|
||||
fmt.Println("users:", users)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckUser(t *testing.T) {
|
||||
users["user1"] = User{Password: "7c4a8d09ca3762af61e59520943dc26494f8941b"}
|
||||
users["user2"] = User{Password: "7c4a8d09ca3762af61e59520943dc26494f8941c"}
|
||||
|
||||
var res bool
|
||||
res = CheckUser("user1", "123456", "")
|
||||
AssertTrue(t, res == true)
|
||||
|
||||
res = CheckUser("user2", "123457", "")
|
||||
AssertTrue(t, res == false)
|
||||
}
|
||||
|
||||
func TestLimitClient(t *testing.T) {
|
||||
ServerCfg.MaxClient = 2
|
||||
ServerCfg.MaxUserClient = 1
|
||||
|
||||
res1 := LimitClient("user1", false)
|
||||
res2 := LimitClient("user1", false)
|
||||
res3 := LimitClient("user2", false)
|
||||
res4 := LimitClient("user3", false)
|
||||
|
||||
AssertTrue(t, res1 == true)
|
||||
AssertTrue(t, res2 == false)
|
||||
AssertTrue(t, res3 == true)
|
||||
AssertTrue(t, res4 == false)
|
||||
|
||||
}
|
||||
@@ -12,23 +12,15 @@ var (
|
||||
CommitId string
|
||||
// 配置文件
|
||||
serverFile string
|
||||
passwd string
|
||||
// 显示版本信息
|
||||
rev bool
|
||||
)
|
||||
|
||||
func initFlag() {
|
||||
flag.StringVar(&serverFile, "conf", "./conf/server.toml", "server config file path")
|
||||
flag.StringVar(&passwd, "pass", "", "generation a sha1 password")
|
||||
flag.BoolVar(&rev, "rev", false, "display version info")
|
||||
flag.Parse()
|
||||
|
||||
if passwd != "" {
|
||||
pwdHash := hashPass(passwd)
|
||||
fmt.Printf("passwd-sha1:%s\n", pwdHash)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if rev {
|
||||
fmt.Printf("%s v%s build on %s [%s, %s] commit_id(%s) \n",
|
||||
APP_NAME, APP_VER, runtime.Version(), runtime.GOOS, runtime.GOARCH, CommitId)
|
||||
@@ -39,6 +31,5 @@ func initFlag() {
|
||||
func InitConfig() {
|
||||
initFlag()
|
||||
loadServer()
|
||||
loadUser()
|
||||
initIpPool()
|
||||
initLog()
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// ip租期 (秒)
|
||||
IpLease = 1209600
|
||||
)
|
||||
|
||||
var (
|
||||
ipPool = &IpPoolConfig{}
|
||||
macIps = map[string]*MacIp{}
|
||||
)
|
||||
|
||||
type MacIp struct {
|
||||
IsActive bool
|
||||
Ip net.IP
|
||||
MacAddr string
|
||||
LastLogin time.Time
|
||||
}
|
||||
|
||||
type IpPoolConfig struct {
|
||||
mux sync.Mutex
|
||||
// 计算动态ip
|
||||
Ipv4Net *net.IPNet
|
||||
Ipv4GateWay net.IP
|
||||
IpLongMin uint32
|
||||
IpLongMax uint32
|
||||
IpLongNow uint32
|
||||
}
|
||||
|
||||
func initIpPool() {
|
||||
// ip地址
|
||||
ip := net.ParseIP(ServerCfg.Ipv4Network)
|
||||
// 子网掩码
|
||||
maskIp := net.ParseIP(ServerCfg.Ipv4Netmask).To4()
|
||||
mask := net.IPMask(maskIp)
|
||||
|
||||
ipNet := &net.IPNet{IP: ip, Mask: mask}
|
||||
ipPool.Ipv4Net = ipNet
|
||||
|
||||
// 网络地址零值
|
||||
min := binary.BigEndian.Uint32(ip.Mask(mask))
|
||||
// 广播地址
|
||||
one, _ := ipNet.Mask.Size()
|
||||
max := min | uint32(math.Pow(2, float64(32-one))-1)
|
||||
|
||||
min += 1 // 网关
|
||||
ipPool.Ipv4GateWay = long2ip(min)
|
||||
ServerCfg.Ipv4GateWay = ipPool.Ipv4GateWay.String()
|
||||
// 第一个可用地址
|
||||
min += 1
|
||||
ipPool.IpLongMin = min
|
||||
ipPool.IpLongMax = max
|
||||
ipPool.IpLongNow = min
|
||||
}
|
||||
|
||||
func long2ip(i uint32) net.IP {
|
||||
ip := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(ip, i)
|
||||
return ip
|
||||
}
|
||||
|
||||
// 获取动态ip
|
||||
func AcquireIp(macAddr string) net.IP {
|
||||
ipPool.mux.Lock()
|
||||
defer ipPool.mux.Unlock()
|
||||
tNow := time.Now()
|
||||
|
||||
// 判断已经分配过
|
||||
if mi, ok := macIps[macAddr]; ok {
|
||||
mi.IsActive = true
|
||||
mi.LastLogin = tNow
|
||||
return mi.Ip
|
||||
}
|
||||
|
||||
// ip池分配完之前
|
||||
if ipPool.IpLongNow < ipPool.IpLongMax {
|
||||
// 递增分配一个ip
|
||||
ip := long2ip(ipPool.IpLongNow)
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
ipPool.IpLongNow += 1
|
||||
return ip
|
||||
}
|
||||
|
||||
// 查找过期数据
|
||||
farMi := &MacIp{LastLogin: tNow}
|
||||
for k, v := range macIps {
|
||||
// 跳过活跃连接
|
||||
if v.IsActive {
|
||||
continue
|
||||
}
|
||||
|
||||
// 已经超过租期
|
||||
if tNow.Sub(v.LastLogin) > IpLease*time.Second {
|
||||
delete(macIps, k)
|
||||
ip := v.Ip
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
return ip
|
||||
}
|
||||
|
||||
// 其他情况判断最早登陆的mac
|
||||
if v.LastLogin.Before(farMi.LastLogin) {
|
||||
farMi = v
|
||||
}
|
||||
}
|
||||
|
||||
// 全都在线,没有数据可用
|
||||
if farMi.MacAddr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 使用最早登陆的mac地址
|
||||
delete(macIps, farMi.MacAddr)
|
||||
ip := farMi.Ip
|
||||
mi := &MacIp{IsActive: true, Ip: ip, MacAddr: macAddr, LastLogin: tNow}
|
||||
macIps[macAddr] = mi
|
||||
return ip
|
||||
}
|
||||
|
||||
// 回收ip
|
||||
func ReleaseIp(ip net.IP, macAddr string) {
|
||||
ipPool.mux.Lock()
|
||||
defer ipPool.mux.Unlock()
|
||||
if mi, ok := macIps[macAddr]; ok {
|
||||
if mi.Ip.Equal(ip) {
|
||||
mi.IsActive = false
|
||||
mi.LastLogin = time.Now()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAcquireIp(t *testing.T) {
|
||||
ServerCfg.Ipv4Network = "192.168.1.0"
|
||||
ServerCfg.Ipv4Netmask = "255.255.255.0"
|
||||
macIps = map[string]*MacIp{}
|
||||
initIpPool()
|
||||
|
||||
var ip net.IP
|
||||
|
||||
for i := 2; i <= 100; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
ip = AcquireIp(fmt.Sprintf("mac-new"))
|
||||
AssertTrue(t, ip.Equal(net.IPv4(192, 168, 1, 101)))
|
||||
for i := 102; i <= 254; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
ip = AcquireIp(fmt.Sprintf("mac-nil"))
|
||||
AssertTrue(t, ip == nil)
|
||||
}
|
||||
|
||||
func TestReleaseIp(t *testing.T) {
|
||||
ServerCfg.Ipv4Network = "192.168.1.0"
|
||||
ServerCfg.Ipv4Netmask = "255.255.255.0"
|
||||
macIps = map[string]*MacIp{}
|
||||
initIpPool()
|
||||
|
||||
var ip net.IP
|
||||
|
||||
// 分配完所有数据
|
||||
for i := 2; i <= 254; i++ {
|
||||
ip = AcquireIp(fmt.Sprintf("mac-%d", i))
|
||||
}
|
||||
|
||||
ip = AcquireIp(fmt.Sprintf("mac-more"))
|
||||
AssertTrue(t, ip == nil)
|
||||
|
||||
ReleaseIp(net.IPv4(192, 168, 1, 123), "mac-123")
|
||||
ReleaseIp(net.IPv4(192, 168, 1, 100), "mac-100")
|
||||
ip = AcquireIp(fmt.Sprintf("mac-new"))
|
||||
// 最早过期的ip
|
||||
AssertTrue(t, ip.Equal(net.IPv4(192, 168, 1, 123)))
|
||||
}
|
||||
@@ -1,7 +1,73 @@
|
||||
package common
|
||||
|
||||
import "log"
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
const (
|
||||
debug = iota
|
||||
info
|
||||
error
|
||||
fatal
|
||||
)
|
||||
|
||||
var Log *logger
|
||||
|
||||
type logger struct {
|
||||
log *log.Logger
|
||||
level int
|
||||
}
|
||||
|
||||
func initLog() {
|
||||
// log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
l := log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)
|
||||
Log = &logger{log: l, level: logLevel2Int(ServerCfg.LogLevel)}
|
||||
}
|
||||
|
||||
func logLevel2Int(l string) int {
|
||||
switch l {
|
||||
case "debug":
|
||||
return debug
|
||||
case "info":
|
||||
return info
|
||||
case "error":
|
||||
return error
|
||||
case "fatal":
|
||||
return fatal
|
||||
default:
|
||||
return info
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Debug(v ...interface{}) {
|
||||
if l.level > debug {
|
||||
return
|
||||
}
|
||||
data := append([]interface{}{"[Debug]"}, v...)
|
||||
l.log.Println(data...)
|
||||
}
|
||||
|
||||
func (l *logger) Info(v ...interface{}) {
|
||||
if l.level > info {
|
||||
return
|
||||
}
|
||||
data := append([]interface{}{"[Info]"}, v...)
|
||||
l.log.Println(data...)
|
||||
}
|
||||
|
||||
func (l *logger) Error(v ...interface{}) {
|
||||
if l.level > error {
|
||||
return
|
||||
}
|
||||
data := append([]interface{}{"[Error]"}, v...)
|
||||
l.log.Println(data...)
|
||||
}
|
||||
|
||||
func (l *logger) Fatal(v ...interface{}) {
|
||||
if l.level > fatal {
|
||||
return
|
||||
}
|
||||
data := append([]interface{}{"[Fatal]"}, v...)
|
||||
l.log.Fatalln(data...)
|
||||
}
|
||||
|
||||
39
common/util.go
Normal file
39
common/util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package common
|
||||
|
||||
import "fmt"
|
||||
|
||||
func InArrStr(arr []string, str string) bool {
|
||||
for _, d := range arr {
|
||||
if d == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
KB = 1024
|
||||
MB = 1024 * KB
|
||||
GB = 1024 * MB
|
||||
TB = 1024 * GB
|
||||
PB = 1024 * TB
|
||||
)
|
||||
|
||||
func HumanByte(bAll float64) string {
|
||||
var hb string
|
||||
|
||||
switch {
|
||||
case bAll >= TB:
|
||||
hb = fmt.Sprintf("%0.2f TB", bAll/TB)
|
||||
case bAll >= GB:
|
||||
hb = fmt.Sprintf("%0.2f GB", bAll/GB)
|
||||
case bAll >= MB:
|
||||
hb = fmt.Sprintf("%0.2f MB", bAll/MB)
|
||||
case bAll >= KB:
|
||||
hb = fmt.Sprintf("%0.2f KB", bAll/KB)
|
||||
default:
|
||||
hb = fmt.Sprintf("%0.2f B", bAll)
|
||||
}
|
||||
|
||||
return hb
|
||||
}
|
||||
29
common/util_test.go
Normal file
29
common/util_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInArrStr(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
arr := []string{"a", "b", "c"}
|
||||
assert.True(InArrStr(arr, "b"))
|
||||
assert.False(InArrStr(arr, "d"))
|
||||
}
|
||||
|
||||
func TestHumanByte(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var s string
|
||||
s = HumanByte(999)
|
||||
assert.Equal(s, "999.00 B")
|
||||
s = HumanByte(10256)
|
||||
assert.Equal(s, "10.02 KB")
|
||||
s = HumanByte(99 * 1024 * 1024)
|
||||
assert.Equal(s, "99.00 MB")
|
||||
s = HumanByte(1023 * 1024 * 1024)
|
||||
assert.Equal(s, "1023.00 MB")
|
||||
s = HumanByte(1024 * 1024 * 1024)
|
||||
assert.Equal(s, "1.00 GB")
|
||||
}
|
||||
Reference in New Issue
Block a user