SDWAN: - protocol: add SDWANConfig/SDWANPeer/SDWANPacket structs, MsgTunnel type - server: sdwan.go (JSON file store), sdwan_api.go (Get/Set/broadcast/route) - server: push SDWAN config on login, announce peer online/offline events - server: RouteSDWANPacket routes TUN packets between nodes via signaling - client: TUN device setup (optun), tunReadLoop reads IP packets - client: handle SDWANConfig/SDWANPeer/SDWANDel push messages - client: apply routes (per-node /32 + broad CIDR fallback) UDP punch fix: - nat/detect: capture LocalPort from STUN UDP socket for punch binding - client: pass publicPort + localPort through login and punch config - coordinator: include PublicPort in PunchParams for both sides - protocol: add PublicPort to LoginReq and ReportBasic Other: - server: use client-reported PublicIP instead of raw r.RemoteAddr - server: update PublicIP/Port from ReportBasic if provided - client: config file loading with zero-value defaults backfill - .gitignore: exclude run/, *.pid, *.log, sdwan.json - go.mod: add golang.org/x/sys for TUN ioctl
88 lines
1.6 KiB
Go
88 lines
1.6 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/openp2p-cn/inp2p/pkg/protocol"
|
|
)
|
|
|
|
type sdwanStore struct {
|
|
mu sync.RWMutex
|
|
path string
|
|
cfg protocol.SDWANConfig
|
|
}
|
|
|
|
func newSDWANStore(path string) *sdwanStore {
|
|
s := &sdwanStore{path: path}
|
|
_ = s.load()
|
|
return s
|
|
}
|
|
|
|
func (s *sdwanStore) load() error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
b, err := os.ReadFile(s.path)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
var c protocol.SDWANConfig
|
|
if err := json.Unmarshal(b, &c); err != nil {
|
|
return err
|
|
}
|
|
s.cfg = normalizeSDWAN(c)
|
|
return nil
|
|
}
|
|
|
|
func (s *sdwanStore) save(cfg protocol.SDWANConfig) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
cfg = normalizeSDWAN(cfg)
|
|
cfg.UpdatedAt = time.Now().Unix()
|
|
b, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.WriteFile(s.path, b, 0644); err != nil {
|
|
return err
|
|
}
|
|
s.cfg = cfg
|
|
return nil
|
|
}
|
|
|
|
func (s *sdwanStore) get() protocol.SDWANConfig {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.cfg
|
|
}
|
|
|
|
func normalizeSDWAN(c protocol.SDWANConfig) protocol.SDWANConfig {
|
|
if c.Mode == "" {
|
|
c.Mode = "hub"
|
|
}
|
|
if !c.Enabled {
|
|
c.Enabled = true
|
|
}
|
|
// de-dup nodes by node name, keep last and sort for stable output
|
|
m := make(map[string]string)
|
|
for _, n := range c.Nodes {
|
|
if n.Node == "" {
|
|
continue
|
|
}
|
|
m[n.Node] = n.IP
|
|
}
|
|
c.Nodes = c.Nodes[:0]
|
|
for node, ip := range m {
|
|
c.Nodes = append(c.Nodes, protocol.SDWANNode{Node: node, IP: ip})
|
|
}
|
|
sort.Slice(c.Nodes, func(i, j int) bool { return c.Nodes[i].Node < c.Nodes[j].Node })
|
|
return c
|
|
}
|