diff --git a/internal/client/client.go b/internal/client/client.go index 37e8bad..26c690e 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -289,6 +289,18 @@ func (c *Client) registerHandlers() { return c.writeTUN(pkt.Payload) }) + // SDWAN raw packet (binary payload) from server + c.conn.OnMessage(protocol.MsgTunnel, protocol.SubTunnelSDWANRaw, func(data []byte) error { + if len(data) <= protocol.HeaderSize { + return nil + } + payload := data[protocol.HeaderSize:] + if len(payload) == 0 { + return nil + } + return c.writeTUN(payload) + }) + // Handle edit app push c.conn.OnMessage(protocol.MsgPush, protocol.SubPushEditApp, func(data []byte) error { var app protocol.AppConfig @@ -643,18 +655,15 @@ func (c *Client) tunReadLoop() { continue } dstIP := net.IP(pkt[16:20]).String() - srcIP := net.IP(pkt[12:16]).String() c.sdwanMu.RLock() self := c.sdwanIP c.sdwanMu.RUnlock() if dstIP == self { continue } - _ = c.conn.Write(protocol.MsgTunnel, protocol.SubTunnelSDWANData, protocol.SDWANPacket{ - SrcIP: srcIP, - DstIP: dstIP, - Payload: append([]byte(nil), pkt...), - }) + // send raw binary to avoid JSON base64 overhead + frame := protocol.EncodeRaw(protocol.MsgTunnel, protocol.SubTunnelSDWANRaw, pkt) + _ = c.conn.WriteRaw(frame) } } @@ -690,6 +699,12 @@ func runCmd(name string, args ...string) error { // Stop shuts down the client. func (c *Client) Stop() { close(c.quit) + c.tunMu.Lock() + if c.tunFile != nil { + _ = c.tunFile.Close() + c.tunFile = nil + } + c.tunMu.Unlock() if c.conn != nil { c.conn.Close() } @@ -701,12 +716,6 @@ func (c *Client) Stop() { t.Close() } c.tMu.Unlock() - c.tunMu.Lock() - if c.tunFile != nil { - _ = c.tunFile.Close() - c.tunFile = nil - } - c.tunMu.Unlock() c.wg.Wait() } diff --git a/internal/server/sdwan_api.go b/internal/server/sdwan_api.go index ad47637..fd329ef 100644 --- a/internal/server/sdwan_api.go +++ b/internal/server/sdwan_api.go @@ -143,5 +143,6 @@ func (s *Server) RouteSDWANPacket(from *NodeInfo, pkt protocol.SDWANPacket) { pkt.FromNode = from.Name pkt.ToNode = toNode - _ = to.Conn.Write(protocol.MsgTunnel, protocol.SubTunnelSDWANData, pkt) + frame := protocol.EncodeRaw(protocol.MsgTunnel, protocol.SubTunnelSDWANRaw, pkt.Payload) + _ = to.Conn.WriteRaw(frame) } diff --git a/internal/server/server.go b/internal/server/server.go index b048077..c8626ad 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -309,7 +309,7 @@ func (s *Server) registerHandlers(conn *signal.Conn, node *NodeInfo) { return s.handleRelayNodeReq(conn, node, req) }) - // SDWAN data plane packet relay (server as control-plane router) + // SDWAN data plane packet relay (JSON control payload) conn.OnMessage(protocol.MsgTunnel, protocol.SubTunnelSDWANData, func(data []byte) error { var pkt protocol.SDWANPacket if err := protocol.DecodePayload(data, &pkt); err != nil { @@ -318,6 +318,26 @@ func (s *Server) registerHandlers(conn *signal.Conn, node *NodeInfo) { s.RouteSDWANPacket(node, pkt) return nil }) + + // SDWAN data plane packet relay (raw IP payload) + conn.OnMessage(protocol.MsgTunnel, protocol.SubTunnelSDWANRaw, func(data []byte) error { + if len(data) <= protocol.HeaderSize { + return nil + } + payload := data[protocol.HeaderSize:] + if len(payload) < 20 { + return nil + } + version := payload[0] >> 4 + if version != 4 { + return nil + } + srcIP := net.IP(payload[12:16]).String() + dstIP := net.IP(payload[16:20]).String() + pkt := protocol.SDWANPacket{SrcIP: srcIP, DstIP: dstIP, Payload: payload} + s.RouteSDWANPacket(node, pkt) + return nil + }) } // handleRelayNodeReq finds and returns the best relay node. diff --git a/pkg/protocol/protocol.go b/pkg/protocol/protocol.go index 845faaa..9305612 100644 --- a/pkg/protocol/protocol.go +++ b/pkg/protocol/protocol.go @@ -1,8 +1,9 @@ // Package protocol defines the INP2P wire protocol. // // Message format: [Header 8B] + [JSON payload] -// Header: DataLen(uint32 LE) + MainType(uint16 LE) + SubType(uint16 LE) -// DataLen = len(header) + len(payload) = 8 + len(json) +// +// Header: DataLen(uint32 LE) + MainType(uint16 LE) + SubType(uint16 LE) +// DataLen = len(header) + len(payload) = 8 + len(json) package protocol import ( @@ -69,6 +70,7 @@ const ( // Sub types: MsgTunnel const ( SubTunnelSDWANData uint16 = iota + SubTunnelSDWANRaw ) // ─── Sub types: MsgRelay ─── @@ -151,6 +153,20 @@ func Encode(mainType, subType uint16, payload interface{}) ([]byte, error) { return buf.Bytes(), nil } +// EncodeRaw packs header + raw (binary) payload. +func EncodeRaw(mainType, subType uint16, payload []byte) []byte { + h := Header{ + DataLen: uint32(HeaderSize + len(payload)), + MainType: mainType, + SubType: subType, + } + buf := new(bytes.Buffer) + buf.Grow(int(h.DataLen)) + _ = binary.Write(buf, binary.LittleEndian, h) + buf.Write(payload) + return buf.Bytes() +} + // DecodeHeader reads the 8-byte header from r. func DecodeHeader(data []byte) (Header, error) { if len(data) < HeaderSize { @@ -179,8 +195,8 @@ type LoginReq struct { Version string `json:"version"` NATType NATType `json:"natType"` ShareBandwidth int `json:"shareBandwidth"` - RelayEnabled bool `json:"relayEnabled"` // --relay flag - SuperRelay bool `json:"superRelay"` // --super flag + RelayEnabled bool `json:"relayEnabled"` // --relay flag + SuperRelay bool `json:"superRelay"` // --super flag PublicIP string `json:"publicIP,omitempty"` PublicPort int `json:"publicPort,omitempty"` } @@ -216,7 +232,7 @@ type PunchParams struct { IP string `json:"ip"` Port int `json:"port"` NATType NATType `json:"natType"` - Token uint64 `json:"token"` // TOTP for auth + Token uint64 `json:"token"` // TOTP for auth IPv6 string `json:"ipv6,omitempty"` HasIPv4 int `json:"hasIPv4"` LinkMode string `json:"linkMode"` // "udp" or "tcp" @@ -279,7 +295,7 @@ type SDWANConfig struct { 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 + 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"` @@ -302,17 +318,17 @@ type SDWANPacket struct { // ReportConnect is the connection result reported to server. type ReportConnect struct { - PeerNode string `json:"peerNode"` - NATType NATType `json:"natType"` - PeerNATType NATType `json:"peerNatType"` - LinkMode string `json:"linkMode"` // "udppunch", "tcppunch", "relay" - Error string `json:"error,omitempty"` - RTT int `json:"rtt,omitempty"` // milliseconds - RelayNode string `json:"relayNode,omitempty"` - Protocol string `json:"protocol,omitempty"` - SrcPort int `json:"srcPort,omitempty"` - DstPort int `json:"dstPort,omitempty"` - DstHost string `json:"dstHost,omitempty"` - Version string `json:"version,omitempty"` - ShareBandwidth int `json:"shareBandWidth,omitempty"` + PeerNode string `json:"peerNode"` + NATType NATType `json:"natType"` + PeerNATType NATType `json:"peerNatType"` + LinkMode string `json:"linkMode"` // "udppunch", "tcppunch", "relay" + Error string `json:"error,omitempty"` + RTT int `json:"rtt,omitempty"` // milliseconds + RelayNode string `json:"relayNode,omitempty"` + Protocol string `json:"protocol,omitempty"` + SrcPort int `json:"srcPort,omitempty"` + DstPort int `json:"dstPort,omitempty"` + DstHost string `json:"dstHost,omitempty"` + Version string `json:"version,omitempty"` + ShareBandwidth int `json:"shareBandWidth,omitempty"` }