Key fixes: - SDWAN config: use absolute path /root/.openclaw/workspace/inp2p/sdwan.json - Client: register handlers BEFORE ReadLoop (race condition fix) - Client: make ensureTUNReader non-fatal on error - Client: fix TUN device conflict between ip tuntap add and ioctl - Client: fix panic on empty TUN read (n==0 check) - Build: static binary with -extldflags=-static for glibc compatibility Verified: hcss(10.10.0.3) <-> i-6986(10.10.0.2) ping 5/5, 0% loss, 44ms
151 lines
3.0 KiB
Go
151 lines
3.0 KiB
Go
package server
|
|
|
|
import (
|
|
"log"
|
|
"net/netip"
|
|
|
|
"github.com/openp2p-cn/inp2p/pkg/protocol"
|
|
)
|
|
|
|
func (s *Server) GetSDWAN() protocol.SDWANConfig {
|
|
return s.sdwan.get()
|
|
}
|
|
|
|
func (s *Server) SetSDWAN(cfg protocol.SDWANConfig) error {
|
|
if err := s.sdwan.save(cfg); err != nil {
|
|
return err
|
|
}
|
|
s.broadcastSDWAN(s.sdwan.get())
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) broadcastSDWAN(cfg protocol.SDWANConfig) {
|
|
if !cfg.Enabled || cfg.GatewayCIDR == "" {
|
|
return
|
|
}
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
for _, n := range s.nodes {
|
|
if !n.IsOnline() {
|
|
continue
|
|
}
|
|
_ = n.Conn.Write(protocol.MsgPush, protocol.SubPushSDWANConfig, cfg)
|
|
}
|
|
}
|
|
|
|
func (s *Server) pushSDWANPeer(to *NodeInfo, peer protocol.SDWANPeer) {
|
|
if to == nil || !to.IsOnline() {
|
|
return
|
|
}
|
|
_ = to.Conn.Write(protocol.MsgPush, protocol.SubPushSDWANPeer, peer)
|
|
}
|
|
|
|
func (s *Server) pushSDWANDel(to *NodeInfo, peer protocol.SDWANPeer) {
|
|
if to == nil || !to.IsOnline() {
|
|
return
|
|
}
|
|
_ = to.Conn.Write(protocol.MsgPush, protocol.SubPushSDWANDel, peer)
|
|
}
|
|
|
|
func (s *Server) announceSDWANNodeOnline(nodeName string) {
|
|
cfg := s.sdwan.get()
|
|
if cfg.GatewayCIDR == "" {
|
|
return
|
|
}
|
|
selfIP := ""
|
|
for _, n := range cfg.Nodes {
|
|
if n.Node == nodeName {
|
|
selfIP = n.IP
|
|
break
|
|
}
|
|
}
|
|
if selfIP == "" {
|
|
return
|
|
}
|
|
|
|
s.mu.RLock()
|
|
newNode := s.nodes[nodeName]
|
|
if newNode == nil || !newNode.IsOnline() {
|
|
s.mu.RUnlock()
|
|
return
|
|
}
|
|
for _, n := range cfg.Nodes {
|
|
if n.Node == nodeName {
|
|
continue
|
|
}
|
|
other := s.nodes[n.Node]
|
|
if other == nil || !other.IsOnline() {
|
|
continue
|
|
}
|
|
// existing -> new
|
|
s.pushSDWANPeer(newNode, protocol.SDWANPeer{Node: n.Node, IP: n.IP, Online: true})
|
|
// new -> existing
|
|
s.pushSDWANPeer(other, protocol.SDWANPeer{Node: nodeName, IP: selfIP, Online: true})
|
|
}
|
|
s.mu.RUnlock()
|
|
}
|
|
|
|
func (s *Server) announceSDWANNodeOffline(nodeName string) {
|
|
cfg := s.sdwan.get()
|
|
if cfg.GatewayCIDR == "" {
|
|
return
|
|
}
|
|
selfIP := ""
|
|
for _, n := range cfg.Nodes {
|
|
if n.Node == nodeName {
|
|
selfIP = n.IP
|
|
break
|
|
}
|
|
}
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
for _, n := range s.nodes {
|
|
if n.Name == nodeName || !n.IsOnline() {
|
|
continue
|
|
}
|
|
s.pushSDWANDel(n, protocol.SDWANPeer{Node: nodeName, IP: selfIP, Online: false})
|
|
}
|
|
}
|
|
|
|
func (s *Server) RouteSDWANPacket(from *NodeInfo, pkt protocol.SDWANPacket) {
|
|
log.Printf("[sdwan] route: %s -> %s len=%d", from.Name, pkt.DstIP, len(pkt.Payload))
|
|
if from == nil {
|
|
return
|
|
}
|
|
cfg := s.sdwan.get()
|
|
if cfg.GatewayCIDR == "" || pkt.DstIP == "" || len(pkt.Payload) == 0 {
|
|
return
|
|
}
|
|
|
|
dst, err := netip.ParseAddr(pkt.DstIP)
|
|
if err != nil {
|
|
return
|
|
}
|
|
toNode := ""
|
|
for _, n := range cfg.Nodes {
|
|
if n.IP == pkt.DstIP {
|
|
toNode = n.Node
|
|
break
|
|
}
|
|
if p, err := netip.ParseAddr(n.IP); err == nil && p == dst {
|
|
toNode = n.Node
|
|
break
|
|
}
|
|
}
|
|
if toNode == "" || toNode == from.Name {
|
|
return
|
|
}
|
|
|
|
s.mu.RLock()
|
|
to := s.nodes[toNode]
|
|
s.mu.RUnlock()
|
|
if to == nil || !to.IsOnline() {
|
|
return
|
|
}
|
|
|
|
pkt.FromNode = from.Name
|
|
pkt.ToNode = toNode
|
|
frame := protocol.EncodeRaw(protocol.MsgTunnel, protocol.SubTunnelSDWANRaw, pkt.Payload)
|
|
_ = to.Conn.WriteRaw(frame)
|
|
}
|