feat: 实现第三层 TUN VPN 服务,支持后台 IP 分配
- 新增 TUN 设备层(water 库),分 linux/darwin 平台配置 IP/路由/MTU - 实现 IP 分配管理:动态池自动分配 + 按用户静态预留,支持热更新 - 实现 PacketSwitch 共享 TUN 包转发:源 IP 防伪、按目的 IP 查表转发、allow-c2c - 重写隧道:自研简化 WS 协议(文本帧 JSON 控制 init/ready,二进制帧=原始 IP 包) - VpnService 单例管理 TUN 生命周期,子网变更踢线重建,预留增删热更新 - 新增 vpn_settings/vpn_reservations 表,AutoMigrate + 默认设置 seed - 新增 Admin API:settings 读写、status、clients、reservations CRUD - 前端新增 VpnView(/admin/vpn):状态面板/设置表单/在线客户端/静态预留 - main.go 启动时按 DB 设置初始化 VPN 服务
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
package vpn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/apparentlymart/go-cidr/cidr"
|
||||
)
|
||||
|
||||
type AllocationManager struct {
|
||||
mu sync.Mutex
|
||||
net *net.IPNet
|
||||
serverIP net.IP
|
||||
used map[string]bool
|
||||
reservedByUser map[uint]string
|
||||
reservedSet map[string]bool
|
||||
}
|
||||
|
||||
func NewAllocationManager(ipNet *net.IPNet, serverIP net.IP, reservations map[uint]string) *AllocationManager {
|
||||
m := &AllocationManager{
|
||||
net: ipNet,
|
||||
serverIP: serverIP,
|
||||
used: make(map[string]bool),
|
||||
reservedByUser: make(map[uint]string),
|
||||
reservedSet: make(map[string]bool),
|
||||
}
|
||||
for uid, ip := range reservations {
|
||||
m.reservedByUser[uid] = ip
|
||||
m.reservedSet[ip] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AllocationManager) ServerIP() net.IP { return m.serverIP }
|
||||
func (m *AllocationManager) Subnet() *net.IPNet { return m.net }
|
||||
|
||||
func (m *AllocationManager) Allocate(userID uint) (net.IP, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if ipStr, ok := m.reservedByUser[userID]; ok {
|
||||
if m.used[ipStr] {
|
||||
return nil, fmt.Errorf("用户预留 IP %s 已被占用", ipStr)
|
||||
}
|
||||
m.used[ipStr] = true
|
||||
return net.ParseIP(ipStr), nil
|
||||
}
|
||||
|
||||
count := cidr.AddressCount(m.net)
|
||||
maxIndex := int(count - 1)
|
||||
for i := 2; i < maxIndex; i++ {
|
||||
ip, err := cidr.Host(m.net, i)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ipStr := ip.String()
|
||||
if m.used[ipStr] || m.reservedSet[ipStr] {
|
||||
continue
|
||||
}
|
||||
m.used[ipStr] = true
|
||||
return ip, nil
|
||||
}
|
||||
return nil, errors.New("可用 IP 地址已耗尽")
|
||||
}
|
||||
|
||||
func (m *AllocationManager) Release(ip net.IP) {
|
||||
if ip == nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.used, ip.String())
|
||||
}
|
||||
|
||||
func (m *AllocationManager) IsReserved(ipStr string) bool {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.reservedSet[ipStr]
|
||||
}
|
||||
|
||||
func (m *AllocationManager) ReservedByUser(userID uint) (string, bool) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
ip, ok := m.reservedByUser[userID]
|
||||
return ip, ok
|
||||
}
|
||||
|
||||
func (m *AllocationManager) ReservedList() map[uint]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
out := make(map[uint]string, len(m.reservedByUser))
|
||||
for k, v := range m.reservedByUser {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *AllocationManager) UsedCount() int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return len(m.used)
|
||||
}
|
||||
|
||||
func (m *AllocationManager) Capacity() uint64 {
|
||||
count := cidr.AddressCount(m.net)
|
||||
if count < 3 {
|
||||
return 0
|
||||
}
|
||||
return count - 3
|
||||
}
|
||||
|
||||
func (m *AllocationManager) AddReservation(userID uint, ipStr string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if old, ok := m.reservedByUser[userID]; ok {
|
||||
delete(m.reservedSet, old)
|
||||
}
|
||||
m.reservedByUser[userID] = ipStr
|
||||
m.reservedSet[ipStr] = true
|
||||
}
|
||||
|
||||
func (m *AllocationManager) RemoveReservation(userID uint) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if old, ok := m.reservedByUser[userID]; ok {
|
||||
delete(m.reservedByUser, userID)
|
||||
delete(m.reservedSet, old)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user