Files
lmvpn_server/internal/vpn/alloc.go
T
kevin 44b51b3b04 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 服务
2026-07-03 14:49:56 +08:00

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)
}
}