feat: SDWAN data plane + UDP punch port fix + TUN reader

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
This commit is contained in:
2026-03-02 17:48:05 +08:00
parent 673e354fe5
commit 5568ea67d9
12 changed files with 680 additions and 37 deletions

View File

@@ -61,6 +61,14 @@ const (
SubPushEditApp // add/edit tunnel app
SubPushDeleteApp // delete tunnel app
SubPushReportApps // request app list
SubPushSDWANConfig // push sdwan config to client
SubPushSDWANPeer // push sdwan peer online/update
SubPushSDWANDel // push sdwan peer offline/delete
)
// Sub types: MsgTunnel
const (
SubTunnelSDWANData uint16 = iota
)
// ─── Sub types: MsgRelay ───
@@ -174,6 +182,7 @@ type LoginReq struct {
RelayEnabled bool `json:"relayEnabled"` // --relay flag
SuperRelay bool `json:"superRelay"` // --super flag
PublicIP string `json:"publicIP,omitempty"`
PublicPort int `json:"publicPort,omitempty"`
}
type LoginRsp struct {
@@ -194,6 +203,8 @@ type ReportBasic struct {
HasIPv4 int `json:"hasIPv4"`
HasUPNPorNATPMP int `json:"hasUPNPorNATPMP"`
IPv6 string `json:"IPv6,omitempty"`
PublicIP string `json:"publicIP,omitempty"`
PublicPort int `json:"publicPort,omitempty"`
}
type ReportBasicRsp struct {
@@ -258,6 +269,37 @@ type AppConfig struct {
RelayNode string `json:"relayNode,omitempty"` // force specific relay
}
type SDWANNode struct {
Node string `json:"node"`
IP string `json:"ip"`
}
type SDWANConfig struct {
Enabled bool `json:"enabled,omitempty"`
Name string `json:"name,omitempty"`
GatewayCIDR string `json:"gatewayCIDR"`
Mode string `json:"mode,omitempty"` // hub | mesh | fullmesh
IP string `json:"ip,omitempty"` // node self IP if pushed per-node
MTU int `json:"mtu,omitempty"`
Routes []string `json:"routes,omitempty"`
Nodes []SDWANNode `json:"nodes"`
UpdatedAt int64 `json:"updatedAt,omitempty"`
}
type SDWANPeer struct {
Node string `json:"node"`
IP string `json:"ip"`
Online bool `json:"online"`
}
type SDWANPacket struct {
FromNode string `json:"fromNode,omitempty"`
ToNode string `json:"toNode,omitempty"`
SrcIP string `json:"srcIP,omitempty"`
DstIP string `json:"dstIP,omitempty"`
Payload []byte `json:"payload"`
}
// ReportConnect is the connection result reported to server.
type ReportConnect struct {
PeerNode string `json:"peerNode"`