feat: audit api, sdwan persist, relay fallback updates
This commit is contained in:
@@ -3,6 +3,7 @@ package client
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
@@ -45,6 +46,7 @@ type Client struct {
|
||||
sdwanStop chan struct{}
|
||||
tunMu sync.Mutex
|
||||
tunFile *os.File
|
||||
sdwanPath string
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
@@ -53,6 +55,7 @@ type Client struct {
|
||||
func New(cfg config.ClientConfig) *Client {
|
||||
c := &Client{
|
||||
cfg: cfg,
|
||||
sdwanPath: "/etc/inp2p/sdwan.json",
|
||||
natType: protocol.NATUnknown,
|
||||
tunnels: make(map[string]*tunnel.Tunnel),
|
||||
sdwanStop: make(chan struct{}),
|
||||
@@ -62,7 +65,7 @@ func New(cfg config.ClientConfig) *Client {
|
||||
}
|
||||
|
||||
if cfg.RelayEnabled {
|
||||
c.relayMgr = relay.NewManager(cfg.RelayPort, true, cfg.SuperRelay, cfg.MaxRelayLoad, cfg.Token)
|
||||
c.relayMgr = relay.NewManager(cfg.RelayPort, true, cfg.SuperRelay, cfg.MaxRelayLoad, cfg.Token, cfg.ShareBandwidth)
|
||||
}
|
||||
|
||||
return c
|
||||
@@ -95,7 +98,7 @@ func (c *Client) connectAndRun() error {
|
||||
c.publicIP = natResult.PublicIP
|
||||
c.publicPort = natResult.Port1
|
||||
c.localPort = natResult.LocalPort
|
||||
log.Printf("[client] SENDING_LOGIN_TOKEN=%d NAT type=%s, publicIP=%s, publicPort=%d, localPort=%d", c.natType, c.publicIP, c.publicPort, c.localPort)
|
||||
log.Printf("[client] SENDING_LOGIN_TOKEN=%d NAT type=%s, publicIP=%s, publicPort=%d, localPort=%d", c.cfg.Token, c.natType, c.publicIP, c.publicPort, c.localPort)
|
||||
|
||||
// 2. WSS Connect
|
||||
scheme := "ws"
|
||||
@@ -130,12 +133,14 @@ func (c *Client) connectAndRun() error {
|
||||
loginReq := protocol.LoginReq{
|
||||
Node: c.cfg.Node,
|
||||
Token: c.cfg.Token,
|
||||
NodeSecret: c.cfg.NodeSecret,
|
||||
User: c.cfg.User,
|
||||
Version: config.Version,
|
||||
NATType: c.natType,
|
||||
ShareBandwidth: c.cfg.ShareBandwidth,
|
||||
RelayEnabled: c.cfg.RelayEnabled,
|
||||
SuperRelay: c.cfg.SuperRelay,
|
||||
RelayOfficial: c.cfg.RelayOfficial,
|
||||
PublicIP: c.publicIP,
|
||||
PublicPort: c.publicPort,
|
||||
}
|
||||
@@ -236,7 +241,6 @@ func (c *Client) registerHandlers() {
|
||||
return nil
|
||||
}
|
||||
log.Printf("[client] sdwan config received: gateway=%s nodes=%d mode=%s", cfg.GatewayCIDR, len(cfg.Nodes), cfg.Mode)
|
||||
_ = os.WriteFile("sdwan.json", data[protocol.HeaderSize:], 0644)
|
||||
|
||||
// apply control+data plane
|
||||
if err := c.applySDWAN(cfg); err != nil {
|
||||
@@ -396,7 +400,7 @@ func (c *Client) connectApp(app config.AppConfig) {
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("[client] connect coordination failed for %s: %v", app.PeerNode, err)
|
||||
c.tryRelay(app)
|
||||
c.tryRelay(app, "tenant")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -404,7 +408,7 @@ func (c *Client) connectApp(app config.AppConfig) {
|
||||
protocol.DecodePayload(rspData, &rsp)
|
||||
if rsp.Error != 0 {
|
||||
log.Printf("[client] connect denied: %s", rsp.Detail)
|
||||
c.tryRelay(app)
|
||||
c.tryRelay(app, "tenant")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -420,7 +424,7 @@ func (c *Client) connectApp(app config.AppConfig) {
|
||||
|
||||
if result.Error != nil {
|
||||
log.Printf("[client] punch failed for %s: %v", app.PeerNode, result.Error)
|
||||
c.tryRelay(app)
|
||||
c.tryRelay(app, "tenant")
|
||||
c.reportConnect(app, protocol.ReportConnect{
|
||||
PeerNode: app.PeerNode, Error: result.Error.Error(),
|
||||
NATType: c.natType, PeerNATType: rsp.Peer.NATType,
|
||||
@@ -448,12 +452,12 @@ func (c *Client) connectApp(app config.AppConfig) {
|
||||
}
|
||||
|
||||
// tryRelay attempts to use a relay node.
|
||||
func (c *Client) tryRelay(app config.AppConfig) {
|
||||
log.Printf("[client] trying relay for %s", app.PeerNode)
|
||||
func (c *Client) tryRelay(app config.AppConfig, mode string) {
|
||||
log.Printf("[client] trying relay(%s) for %s", mode, app.PeerNode)
|
||||
|
||||
rspData, err := c.conn.Request(
|
||||
protocol.MsgRelay, protocol.SubRelayNodeReq,
|
||||
protocol.RelayNodeReq{PeerNode: app.PeerNode},
|
||||
protocol.RelayNodeReq{PeerNode: app.PeerNode, Mode: mode},
|
||||
protocol.MsgRelay, protocol.SubRelayNodeRsp,
|
||||
10*time.Second,
|
||||
)
|
||||
@@ -465,6 +469,11 @@ func (c *Client) tryRelay(app config.AppConfig) {
|
||||
var rsp protocol.RelayNodeRsp
|
||||
protocol.DecodePayload(rspData, &rsp)
|
||||
if rsp.Error != 0 {
|
||||
if mode != "official" {
|
||||
log.Printf("[client] no relay available for %s, fallback official", app.PeerNode)
|
||||
go c.tryRelay(app, "official")
|
||||
return
|
||||
}
|
||||
log.Printf("[client] no relay available for %s", app.PeerNode)
|
||||
return
|
||||
}
|
||||
@@ -545,6 +554,19 @@ func (c *Client) reportConnect(app config.AppConfig, rc protocol.ReportConnect)
|
||||
c.conn.Write(protocol.MsgReport, protocol.SubReportConnect, rc)
|
||||
}
|
||||
|
||||
func (c *Client) writeSDWANConfig(cfg protocol.SDWANConfig) error {
|
||||
path := c.sdwanPath
|
||||
if path == "" {
|
||||
path = "/etc/inp2p/sdwan.json"
|
||||
}
|
||||
b, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = os.MkdirAll("/etc/inp2p", 0755)
|
||||
return os.WriteFile(path, b, 0644)
|
||||
}
|
||||
|
||||
func (c *Client) applySDWAN(cfg protocol.SDWANConfig) error {
|
||||
selfIP := ""
|
||||
for _, n := range cfg.Nodes {
|
||||
@@ -578,11 +600,24 @@ func (c *Client) applySDWAN(cfg protocol.SDWANConfig) error {
|
||||
// fallback broad route for hub mode / compatibility
|
||||
_ = runCmd("ip", "route", "replace", pfx.String(), "dev", "optun")
|
||||
|
||||
// refresh rule/table 100 for sdwan
|
||||
_ = runCmd("ip", "rule", "add", "pref", "100", "from", selfIP, "table", "100")
|
||||
_ = runCmd("ip", "route", "replace", pfx.String(), "dev", "optun", "table", "100")
|
||||
|
||||
c.sdwanMu.Lock()
|
||||
c.sdwan = cfg
|
||||
c.sdwanIP = selfIP
|
||||
c.sdwanMu.Unlock()
|
||||
|
||||
// persist sdwan config for local use/diagnostics
|
||||
if err := c.writeSDWANConfig(cfg); err != nil {
|
||||
log.Printf("[client] write sdwan.json failed: %v", err)
|
||||
}
|
||||
|
||||
// Apply subnet proxy (if configured)
|
||||
if err := c.applySubnetProxy(cfg); err != nil {
|
||||
log.Printf("[client] applySubnetProxy failed: %v", err)
|
||||
}
|
||||
// Try to start TUN reader, but don't fail SDWAN apply if it errors
|
||||
if err := c.ensureTUNReader(); err != nil {
|
||||
log.Printf("[client] ensureTUNReader failed (non-fatal): %v", err)
|
||||
@@ -591,6 +626,39 @@ func (c *Client) applySDWAN(cfg protocol.SDWANConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// applySubnetProxy configures local subnet proxying based on SDWAN config.
|
||||
func (c *Client) applySubnetProxy(cfg protocol.SDWANConfig) error {
|
||||
if len(cfg.SubnetProxies) == 0 {
|
||||
return nil
|
||||
}
|
||||
self := c.cfg.Node
|
||||
for _, sp := range cfg.SubnetProxies {
|
||||
if sp.Node != self {
|
||||
// for non-proxy nodes, add route to virtualCIDR via proxy node IP
|
||||
proxyIP := ""
|
||||
for _, n := range cfg.Nodes {
|
||||
if n.Node == sp.Node {
|
||||
proxyIP = strings.TrimSpace(n.IP)
|
||||
break
|
||||
}
|
||||
}
|
||||
if proxyIP == "" {
|
||||
continue
|
||||
}
|
||||
_ = runCmd("ip", "route", "replace", sp.VirtualCIDR, "via", proxyIP, "dev", "optun")
|
||||
continue
|
||||
}
|
||||
// This node is the proxy
|
||||
_ = runCmd("sysctl", "-w", "net.ipv4.ip_forward=1")
|
||||
// map virtualCIDR -> localCIDR (NETMAP)
|
||||
if sp.VirtualCIDR != "" && sp.LocalCIDR != "" {
|
||||
_ = runCmd("iptables", "-t", "nat", "-A", "PREROUTING", "-d", sp.VirtualCIDR, "-j", "NETMAP", "--to", sp.LocalCIDR)
|
||||
_ = runCmd("iptables", "-t", "nat", "-A", "POSTROUTING", "-s", sp.LocalCIDR, "-j", "MASQUERADE")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ensureTUNReader() error {
|
||||
c.tunMu.Lock()
|
||||
defer c.tunMu.Unlock()
|
||||
@@ -637,13 +705,13 @@ func (c *Client) tunReadLoop() {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
n, err := f.Read(buf)
|
||||
n, err := unix.Read(int(f.Fd()), buf)
|
||||
if err != nil {
|
||||
if c.IsStopping() {
|
||||
return
|
||||
}
|
||||
// Log only real errors, not EOF or timeout
|
||||
if err.Error() != "EOF" && err.Error() != "resource temporarily unavailable" {
|
||||
// Ignore transient errors
|
||||
if err != unix.EINTR && err != unix.EAGAIN {
|
||||
log.Printf("[client] tun read error: %v", err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
Reference in New Issue
Block a user