Files
kevin a770862c7c feat: 支持 IPv6 双栈地址派发
- model: VpnSetting 新增 Subnet6 字段,VpnReservation 新增 IPAddress6
- alloc: 处理 /64 等大子网 cidr.AddressCount 溢出,回退 65536 扫描上限
- protocol: initMessage 新增 ip6/prefix6/server_ip6(omitempty 向后兼容)
- service: 双分配器 alloc4+alloc6,ApplySettings 配置双 IP 与双路由,
  Allocate 返回 v4+v6 一对地址
- switch: SwitchConn 新增 AssignedIP6(),Register/Unregister 注册双 key,
  反欺骗按 IP 版本分别校验 v4/v6 源地址
- tunnel: tunnelConn 新增 assignedIP6,init 消息填充 v6 字段
- handler: 新增 validateSubnet6(/64~/126),settings/预留 API 全面支持 v6
- diag: 新增 IPv6 forwarding 与 NAT66 masquerade 检测
- install_linux.sh: 新增 VPN_SUBNET6、ipv6 forwarding、nft/ip6tables NAT66
- 前端: v6 子网输入框、v6 预留、v6 诊断卡片、客户端列表 v6 列
- 文档: init 消息、IP 分配、子网约束、TUN 配置、NAT、反欺骗全部更新
2026-07-07 11:38:21 +08:00

138 lines
2.9 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)
if count == 0 && m.net.IP.To4() == nil {
count = 65536
}
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 == 0 && m.net.IP.To4() == nil {
return 65533
}
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)
}
}