187 lines
4.0 KiB
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)
|
|
}
|