Files
elmprodvpn/selective-vpn-api/app/transport_client_runtime_alloc_slots.go

187 lines
4.0 KiB
Go

package app
import (
"fmt"
"strconv"
"strings"
)
func transportRoutingTableForID(id string) string {
s := strings.TrimSpace(id)
if s == "" {
return "agvpn_client"
}
s = strings.ReplaceAll(s, "-", "_")
s = strings.ReplaceAll(s, ".", "_")
return normalizeTransportRoutingTable("agvpn_"+s, "agvpn_client")
}
func transportRoutingTableForIfaceID(ifaceID string) string {
id := normalizeTransportIfaceID(ifaceID)
if id == transportDefaultIfaceID {
return "agvpn_shared"
}
s := strings.ReplaceAll(id, "-", "_")
return normalizeTransportRoutingTable("agvpn_if_"+s, "agvpn_client")
}
func transportRoutingTableForIDUnique(id string, used map[string]struct{}) string {
base := transportRoutingTableForID(id)
if _, ok := used[base]; !ok {
used[base] = struct{}{}
return base
}
for i := 2; i < 1000; i++ {
suffix := "_" + strconv.Itoa(i)
maxBase := 31 - len(suffix)
if maxBase < 1 {
maxBase = 1
}
prefix := base
if len(prefix) > maxBase {
prefix = prefix[:maxBase]
}
cand := prefix + suffix
if _, ok := used[cand]; ok {
continue
}
used[cand] = struct{}{}
return cand
}
used[base] = struct{}{}
return base
}
func normalizeTransportRoutingTable(raw string, fallback string) string {
table := strings.ToLower(strings.TrimSpace(raw))
if table == "" {
table = strings.ToLower(strings.TrimSpace(fallback))
}
if table == "" {
return "agvpn_client"
}
table = strings.ReplaceAll(table, "-", "_")
table = strings.ReplaceAll(table, ".", "_")
table = strings.ReplaceAll(table, " ", "_")
var b strings.Builder
b.Grow(len(table))
lastUnderscore := false
for i := 0; i < len(table); i++ {
ch := table[i]
isAZ := ch >= 'a' && ch <= 'z'
is09 := ch >= '0' && ch <= '9'
if isAZ || is09 {
b.WriteByte(ch)
lastUnderscore = false
continue
}
if !lastUnderscore {
b.WriteByte('_')
lastUnderscore = true
}
}
table = strings.Trim(b.String(), "_")
if table == "" {
table = "agvpn_client"
}
if !strings.HasPrefix(table, "agvpn_") {
table = "agvpn_" + table
}
if len(table) > 31 {
table = strings.Trim(table[:31], "_")
}
if table == "" {
return "agvpn_client"
}
return table
}
func parseTransportMarkHex(raw string) (uint64, bool) {
s := strings.ToLower(strings.TrimSpace(raw))
if !strings.HasPrefix(s, "0x") {
return 0, false
}
n, err := strconv.ParseUint(strings.TrimPrefix(s, "0x"), 16, 64)
if err != nil {
return 0, false
}
if !transportMarkAllowed(n) {
return 0, false
}
return n, true
}
func parseTransportPref(raw int) (int, bool) {
if !transportPrefAllowed(raw) {
return 0, false
}
return raw, true
}
func transportMarkAllowed(mark uint64) bool {
if mark < transportMarkStart || mark > transportMarkEnd {
return false
}
if mark <= transportMarkReserveEnd {
return false
}
return true
}
func transportPrefAllowed(pref int) bool {
if pref < transportPrefStart || pref > transportPrefEnd {
return false
}
if pref <= transportPrefReserveEnd {
return false
}
if (pref-transportPrefStart)%transportPrefStep != 0 {
return false
}
return true
}
func nextTransportMark(used map[uint64]struct{}) uint64 {
for v := transportMarkStart; v <= transportMarkEnd; v++ {
if !transportMarkAllowed(v) {
continue
}
if _, ok := used[v]; ok {
continue
}
return v
}
// Fallback: reuse first available from pool even if exhausted.
for v := transportMarkStart; v <= transportMarkEnd; v++ {
if !transportMarkAllowed(v) {
continue
}
return v
}
return transportMarkStart
}
func nextTransportPref(used map[int]struct{}) int {
for p := transportPrefStart; p <= transportPrefEnd; p += transportPrefStep {
if !transportPrefAllowed(p) {
continue
}
if _, ok := used[p]; ok {
continue
}
return p
}
// Fallback: reuse first available from pool even if exhausted.
for p := transportPrefStart; p <= transportPrefEnd; p += transportPrefStep {
if !transportPrefAllowed(p) {
continue
}
return p
}
return transportPrefStart
}
func formatTransportMarkHex(mark uint64) string {
return fmt.Sprintf("0x%x", mark)
}