- 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、反欺骗全部更新
138 lines
2.9 KiB
Go
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)
|
|
}
|
|
}
|