- 新增 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 服务
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
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)
|
|
}
|
|
}
|