// SPDX-License-Identifier: 0BSD // Copyright (c) 2024-2026 Sudo-Ivan / Quad4.io package interfaces import ( "encoding/binary" "fmt" "net" "sync" "time" "git.quad4.io/Networks/Reticulum-Go/pkg/common" "git.quad4.io/Networks/Reticulum-Go/pkg/debug" ) const ( BITRATE_MINIMUM = 1200 // Minimum bitrate in bits/second MODE_FULL = 0x01 // Interface modes MODE_GATEWAY = 0x02 MODE_ACCESS_POINT = 0x03 MODE_ROAMING = 0x04 MODE_BOUNDARY = 0x05 // Interface types TYPE_UDP = 0x01 TYPE_TCP = 0x02 PROPAGATION_RATE = 0.02 // 2% of interface bandwidth ) type Interface interface { GetName() string GetType() common.InterfaceType GetMode() common.InterfaceMode IsOnline() bool IsDetached() bool IsEnabled() bool Detach() Enable() Disable() Send(data []byte, addr string) error SetPacketCallback(common.PacketCallback) GetPacketCallback() common.PacketCallback ProcessIncoming([]byte) ProcessOutgoing([]byte) error SendPathRequest([]byte) error SendLinkPacket([]byte, []byte, time.Time) error Start() error Stop() error GetMTU() int GetConn() net.Conn GetBandwidthAvailable() bool common.NetworkInterface } type BaseInterface struct { Name string Mode common.InterfaceMode Type common.InterfaceType Online bool Enabled bool Detached bool IN bool OUT bool MTU int Bitrate int64 TxBytes uint64 RxBytes uint64 lastTx time.Time lastRx time.Time Mutex sync.RWMutex packetCallback common.PacketCallback } func NewBaseInterface(name string, ifType common.InterfaceType, enabled bool) BaseInterface { return BaseInterface{ Name: name, Mode: common.IF_MODE_FULL, Type: ifType, Online: false, Enabled: enabled, Detached: false, IN: false, OUT: false, MTU: common.DEFAULT_MTU, Bitrate: BITRATE_MINIMUM, lastTx: time.Now(), lastRx: time.Now(), } } func (i *BaseInterface) SetPacketCallback(callback common.PacketCallback) { i.Mutex.Lock() defer i.Mutex.Unlock() i.packetCallback = callback } func (i *BaseInterface) GetPacketCallback() common.PacketCallback { i.Mutex.RLock() defer i.Mutex.RUnlock() return i.packetCallback } func (i *BaseInterface) ProcessIncoming(data []byte) { i.Mutex.Lock() i.RxBytes += uint64(len(data)) i.Mutex.Unlock() i.Mutex.RLock() callback := i.packetCallback i.Mutex.RUnlock() if callback != nil { callback(data, i) } } func (i *BaseInterface) ProcessOutgoing(data []byte) error { if !i.Online || i.Detached { debug.Log(debug.DEBUG_CRITICAL, "Interface cannot process outgoing packet - interface offline or detached", "name", i.Name) return fmt.Errorf("interface offline or detached") } i.Mutex.Lock() i.TxBytes += uint64(len(data)) i.Mutex.Unlock() debug.Log(debug.DEBUG_VERBOSE, "Interface processed outgoing packet", "name", i.Name, "bytes", len(data), "total_tx", i.TxBytes) return nil } func (i *BaseInterface) SendPathRequest(packet []byte) error { if !i.Online || i.Detached { return fmt.Errorf("interface offline or detached") } frame := make([]byte, 0, len(packet)+1) frame = append(frame, common.HEX_0x01) frame = append(frame, packet...) return i.ProcessOutgoing(frame) } func (i *BaseInterface) SendLinkPacket(dest []byte, data []byte, timestamp time.Time) error { if !i.Online || i.Detached { return fmt.Errorf("interface offline or detached") } frame := make([]byte, 0, len(dest)+len(data)+9) frame = append(frame, common.HEX_0x02) frame = append(frame, dest...) ts := make([]byte, 8) binary.BigEndian.PutUint64(ts, uint64(timestamp.Unix())) // #nosec G115 frame = append(frame, ts...) frame = append(frame, data...) return i.ProcessOutgoing(frame) } func (i *BaseInterface) Detach() { i.Mutex.Lock() defer i.Mutex.Unlock() i.Detached = true i.Online = false } func (i *BaseInterface) IsEnabled() bool { i.Mutex.RLock() defer i.Mutex.RUnlock() return i.Enabled && i.Online && !i.Detached } func (i *BaseInterface) Enable() { i.Mutex.Lock() defer i.Mutex.Unlock() prevState := i.Enabled i.Enabled = true i.Online = true debug.Log(debug.DEBUG_INFO, "Interface state changed", "name", i.Name, "enabled_prev", prevState, "enabled", i.Enabled, "online_prev", !i.Online, "online", i.Online) } func (i *BaseInterface) Disable() { i.Mutex.Lock() defer i.Mutex.Unlock() i.Enabled = false i.Online = false debug.Log(debug.DEBUG_ERROR, "Interface disabled and offline", "name", i.Name) } func (i *BaseInterface) GetName() string { return i.Name } func (i *BaseInterface) GetType() common.InterfaceType { return i.Type } func (i *BaseInterface) GetMode() common.InterfaceMode { return i.Mode } func (i *BaseInterface) GetMTU() int { return i.MTU } func (i *BaseInterface) IsOnline() bool { i.Mutex.RLock() defer i.Mutex.RUnlock() return i.Online } func (i *BaseInterface) IsDetached() bool { i.Mutex.RLock() defer i.Mutex.RUnlock() return i.Detached } func (i *BaseInterface) Start() error { return nil } func (i *BaseInterface) Stop() error { return nil } func (i *BaseInterface) Send(data []byte, address string) error { debug.Log(debug.DEBUG_VERBOSE, "Interface sending bytes", "name", i.Name, "bytes", len(data), "address", address) err := i.ProcessOutgoing(data) if err != nil { debug.Log(debug.DEBUG_CRITICAL, "Interface failed to send data", "name", i.Name, "error", err) return err } i.updateBandwidthStats(uint64(len(data))) return nil } func (i *BaseInterface) GetConn() net.Conn { return nil } func (i *BaseInterface) GetBandwidthAvailable() bool { i.Mutex.RLock() defer i.Mutex.RUnlock() now := time.Now() timeSinceLastTx := now.Sub(i.lastTx) if timeSinceLastTx > time.Second { debug.Log(debug.DEBUG_VERBOSE, "Interface bandwidth available", "name", i.Name, "idle_seconds", timeSinceLastTx.Seconds()) return true } bytesPerSec := float64(i.TxBytes) / timeSinceLastTx.Seconds() currentUsage := bytesPerSec * 8 maxUsage := float64(i.Bitrate) * PROPAGATION_RATE available := currentUsage < maxUsage debug.Log(debug.DEBUG_VERBOSE, "Interface bandwidth stats", "name", i.Name, "current_bps", currentUsage, "max_bps", maxUsage, "usage_percent", (currentUsage/maxUsage)*100, "available", available) return available } func (i *BaseInterface) updateBandwidthStats(bytes uint64) { i.Mutex.Lock() defer i.Mutex.Unlock() i.TxBytes += bytes i.lastTx = time.Now() debug.Log(debug.DEBUG_VERBOSE, "Interface updated bandwidth stats", "name", i.Name, "tx_bytes", i.TxBytes, "last_tx", i.lastTx) } type InterceptedInterface struct { Interface interceptor func([]byte, common.NetworkInterface) error originalSend func([]byte, string) error } // Create constructor for intercepted interface func NewInterceptedInterface(base Interface, interceptor func([]byte, common.NetworkInterface) error) *InterceptedInterface { return &InterceptedInterface{ Interface: base, interceptor: interceptor, originalSend: base.Send, } } // Implement Send method for intercepted interface func (i *InterceptedInterface) Send(data []byte, addr string) error { // Call interceptor if provided if i.interceptor != nil && len(data) > 0 { if err := i.interceptor(data, i); err != nil { debug.Log(debug.DEBUG_ERROR, "Failed to intercept outgoing packet", "error", err) } } // Call original send return i.originalSend(data, addr) }