2026-04-30 02:08:36 -05:00
// SPDX-License-Identifier: Apache-2.0
2026-04-19 17:07:05 -05:00
// Copyright (c) 2024-2026 Quad4.io
2026-07-13 08:56:23 -05:00
2024-12-30 01:56:25 -06:00
package interfaces
import (
"encoding/binary"
2024-12-30 03:50:52 -06:00
"fmt"
2024-12-30 02:26:51 -06:00
"net"
2024-12-30 03:50:52 -06:00
"sync"
"time"
2024-12-30 01:56:25 -06:00
2026-06-14 13:22:30 -05:00
"quad4/reticulum-go/pkg/common"
"quad4/reticulum-go/pkg/debug"
2024-12-30 01:56:25 -06:00
)
2026-07-16 11:38:13 -05:00
// Interface is the package-local name for a network interface.
// It matches common.NetworkInterface so transport and config share one contract.
2024-12-30 02:26:51 -06:00
type Interface interface {
2025-01-01 00:58:37 -06:00
common . NetworkInterface
2024-12-30 02:26:51 -06:00
}
2026-05-10 01:50:59 -05:00
const (
2026-05-13 07:56:09 -05:00
prFreqSamples = 48
prMinFreqHz = 0.1
prFreqDecay = 1.0 / prMinFreqHz // 10 seconds
icDequeMinSample = 2
icBurstMinSamples = 6
icPRBurstFreqNew = 3.0
icPRBurstFreq = 8.0
ecPRFreq = 5.0
icNewTime = 2 * 60 * 60 // 2 hours in seconds
icBurstHold = 15
icBurstPenalty = 15
2026-05-10 01:50:59 -05:00
)
2024-12-30 01:56:25 -06:00
type BaseInterface struct {
2026-01-02 17:48:15 -06:00
Name string
Mode common . InterfaceMode
Type common . InterfaceType
Online bool
Enabled bool
Detached bool
2026-04-19 17:07:05 -05:00
In bool
Out bool
2026-01-02 17:48:15 -06:00
MTU int
Bitrate int64
TxBytes uint64
RxBytes uint64
TxPackets uint64
RxPackets uint64
lastTx time . Time
lastRx time . Time
2025-01-01 00:58:37 -06:00
2026-07-16 11:38:13 -05:00
Mutex sync . RWMutex // exported so concrete interfaces can lock with parent fields
2024-12-30 03:50:52 -06:00
packetCallback common . PacketCallback
2026-04-18 05:21:54 -05:00
// IFACIdentity is set when the interface participates in an IFAC network.
// When non-nil, outbound packets are masked before transmit and inbound
2026-07-07 03:20:50 -05:00
// packets are unmasked and verified. Unauthenticated packets are dropped.
2026-04-18 05:21:54 -05:00
IFACIdentity common . IFAC
2026-05-10 01:50:59 -05:00
2026-07-09 13:12:28 -05:00
// RecursivePRs enables unknown-path discovery on this interface.
RecursivePRs bool
// AnnouncesFromInternal controls rebroadcast of announces learned via an
// internal-mode next hop (default true).
AnnouncesFromInternal bool
2026-07-25 14:43:24 -05:00
// AnnouncesToInternal allows boundary next hops to feed internal interfaces
// (RNS 1.4.1). Default false.
AnnouncesToInternal bool
// Gravity is configured pathing affinity (RNS 1.4.1).
Gravity int
2026-07-16 11:16:37 -05:00
// ReceiveOnly blocks transmit when true (Python outgoing = no).
// Zero value is false so unset interfaces still transmit.
ReceiveOnly bool
2026-05-10 01:50:59 -05:00
// Path request frequency tracking (ingress/egress burst control)
2026-05-13 07:56:09 -05:00
created time . Time
ipFreqDeque [ ] time . Time
opFreqDeque [ ] time . Time
2026-07-09 11:12:31 -05:00
iaFreqDeque [ ] time . Time
oaFreqDeque [ ] time . Time
2026-05-13 07:56:09 -05:00
icPRBurstActive bool
2026-05-10 01:50:59 -05:00
icPRBurstActivated time . Time
2026-05-13 07:56:09 -05:00
ingressControl bool
egressControl bool
icPRBurstFreqNewV float64
icPRBurstFreqV float64
ecPRFreqV float64
2026-07-09 11:12:31 -05:00
currentRXS float64
currentTXS float64
sampleRXB uint64
sampleTXB uint64
sampleTS time . Time
2026-08-19 17:28:32 -05:00
2026-08-22 18:07:33 -05:00
protocolViolations uint64
ifacViolations uint64
packetFilterHits uint64
arxb , atxb uint64
prxb , ptxb uint64
arxc , atxc uint64
// deferInboundIFAC skips ApplyIFACInbound in ProcessIncoming so transport
// inbound preprocessing can apply IFAC once (RNS 1.5.0).
deferInboundIFAC bool
2026-08-26 12:26:45 -05:00
ifacScratch [ ] byte
2026-08-22 18:07:33 -05:00
prxc , ptxc uint64
sampleARXB uint64
sampleATXB uint64
samplePRXB uint64
samplePTXB uint64
currentARXS float64
currentATXS float64
currentPRXS float64
currentPTXS float64
2026-08-19 17:28:32 -05:00
announceQueue [ ] queuedAnnounce
announceAllowedAt time . Time
announceCap float64
2024-12-30 01:56:25 -06:00
}
2026-07-16 11:38:13 -05:00
// NewBaseInterface creates a BaseInterface value for embedding at construction.
// Do not copy a BaseInterface after it has been used (Mutex must not be copied).
2024-12-30 12:58:43 -06:00
func NewBaseInterface ( name string , ifType common . InterfaceType , enabled bool ) BaseInterface {
return BaseInterface {
2026-07-09 13:12:28 -05:00
Name : name ,
Mode : common . IFModeFull ,
Type : ifType ,
Online : false ,
Enabled : enabled ,
Detached : false ,
In : false ,
Out : false ,
MTU : common . DefaultMTU ,
Bitrate : BitrateMinimum ,
TxBytes : 0 ,
RxBytes : 0 ,
created : time . Now ( ) ,
AnnouncesFromInternal : true ,
ingressControl : true ,
icPRBurstFreqNewV : icPRBurstFreqNew ,
icPRBurstFreqV : icPRBurstFreq ,
2026-07-09 14:45:25 -05:00
ecPRFreqV : ecPRFreq ,
TxPackets : 0 ,
RxPackets : 0 ,
lastTx : time . Now ( ) ,
lastRx : time . Now ( ) ,
2024-12-30 12:58:43 -06:00
}
}
2026-08-15 03:14:09 -05:00
func ( i * BaseInterface ) base ( ) * BaseInterface {
return i
}
2024-12-30 01:56:25 -06:00
func ( i * BaseInterface ) SetPacketCallback ( callback common . PacketCallback ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2024-12-31 11:22:37 -06:00
i . packetCallback = callback
}
func ( i * BaseInterface ) GetPacketCallback ( ) common . PacketCallback {
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2024-12-31 11:22:37 -06:00
return i . packetCallback
2024-12-30 01:56:25 -06:00
}
2026-04-18 05:21:54 -05:00
// SetIFAC stores an Interface Access Code identity on this interface. Pass
// nil to disable IFAC. Subsequent Send / ProcessIncoming calls will use the
// new value.
func ( i * BaseInterface ) SetIFAC ( id common . IFAC ) {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
i . IFACIdentity = id
}
// GetIFAC returns the configured Interface Access Code identity, or nil if
// IFAC is disabled.
func ( i * BaseInterface ) GetIFAC ( ) common . IFAC {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . IFACIdentity
}
2026-08-22 18:07:33 -05:00
func ( i * BaseInterface ) SetDeferInboundIFAC ( deferIFAC bool ) {
i . Mutex . Lock ( )
i . deferInboundIFAC = deferIFAC
i . Mutex . Unlock ( )
}
func ( i * BaseInterface ) DeferInboundIFAC ( ) bool {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . deferInboundIFAC
}
2024-12-30 01:56:25 -06:00
func ( i * BaseInterface ) ProcessIncoming ( data [ ] byte ) {
2026-08-10 16:28:36 -05:00
i . ProcessIncomingFrom ( data , "" )
}
// ProcessIncomingFrom is ProcessIncoming plus an optional peerKey
// identifying the remote sender on a shared local interface (for example a
// listener accepting many client connections). See admitIncomingFrom.
func ( i * BaseInterface ) ProcessIncomingFrom ( data [ ] byte , peerKey string ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
2025-01-01 01:41:16 -06:00
i . RxBytes += uint64 ( len ( data ) )
2026-01-02 17:48:15 -06:00
i . RxPackets ++
2026-07-20 12:58:22 -05:00
name := i . Name
2025-12-30 21:14:13 -06:00
i . Mutex . Unlock ( )
2025-01-01 01:41:16 -06:00
2026-08-10 16:28:36 -05:00
if ! admitIncomingFrom ( i , name , data , peerKey ) {
2026-07-20 12:58:22 -05:00
return
}
2026-08-22 18:07:33 -05:00
i . Mutex . RLock ( )
deferInbound := i . deferInboundIFAC
i . Mutex . RUnlock ( )
payload := data
if ! deferInbound {
var ok bool
payload , ok = common . ApplyIFACInbound ( i , data )
if ! ok {
debug . Log ( debug . DebugVerbose , "Dropped packet failing IFAC policy" , "name" , i . Name , "size" , len ( data ) )
return
}
2026-04-18 05:21:54 -05:00
}
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
2024-12-31 11:22:37 -06:00
callback := i . packetCallback
2025-12-30 21:14:13 -06:00
i . Mutex . RUnlock ( )
2024-12-30 03:50:52 -06:00
2024-12-30 01:56:25 -06:00
if callback != nil {
2026-08-22 18:07:33 -05:00
callback ( payload , i )
2024-12-30 01:56:25 -06:00
}
}
2026-04-18 05:21:54 -05:00
// ProcessOutgoing on the abstract BaseInterface is intentionally a fail-loud
// stub: any concrete network interface that uses BaseInterface as its base
// MUST override ProcessOutgoing to actually transmit bytes. Returning an
// error (and logging at CRITICAL) surfaces dynamic-dispatch mistakes
// (e.g. a *BaseInterface pointer leaking through a callback closure)
// instead of letting the transport silently swallow every outgoing packet.
2024-12-30 01:56:25 -06:00
func ( i * BaseInterface ) ProcessOutgoing ( data [ ] byte ) error {
2026-07-17 17:43:57 -05:00
debug . Log ( debug . DebugCritical , "BaseInterface.ProcessOutgoing called directly, concrete interface type must override it" , "name" , i . Name , "bytes" , len ( data ) )
2026-04-18 05:21:54 -05:00
return fmt . Errorf ( "ProcessOutgoing not implemented on abstract interfaces.BaseInterface (name=%q, %d bytes); concrete interface type must override it" , i . Name , len ( data ) )
2024-12-30 01:56:25 -06:00
}
func ( i * BaseInterface ) SendPathRequest ( packet [ ] byte ) error {
if ! i . Online || i . Detached {
return fmt . Errorf ( "interface offline or detached" )
}
2024-12-31 11:22:37 -06:00
frame := make ( [ ] byte , 0 , len ( packet ) + 1 )
2026-04-19 17:07:05 -05:00
frame = append ( frame , 0x01 )
2024-12-30 01:56:25 -06:00
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 )
2026-04-19 17:07:05 -05:00
frame = append ( frame , 0x02 )
2024-12-30 01:56:25 -06:00
frame = append ( frame , dest ... )
2024-12-30 03:50:52 -06:00
2024-12-30 01:56:25 -06:00
ts := make ( [ ] byte , 8 )
2025-07-06 00:33:50 -05:00
binary . BigEndian . PutUint64 ( ts , uint64 ( timestamp . Unix ( ) ) ) // #nosec G115
2024-12-30 01:56:25 -06:00
frame = append ( frame , ts ... )
frame = append ( frame , data ... )
return i . ProcessOutgoing ( frame )
2024-12-30 02:26:51 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Detach ( ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2024-12-31 11:22:37 -06:00
i . Detached = true
i . Online = false
2024-12-30 02:26:51 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) IsEnabled ( ) bool {
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2024-12-31 11:22:37 -06:00
return i . Enabled && i . Online && ! i . Detached
2024-12-30 02:26:51 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Enable ( ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2025-01-01 01:41:16 -06:00
prevState := i . Enabled
2024-12-31 11:22:37 -06:00
i . Enabled = true
i . Online = true
2025-01-01 01:41:16 -06:00
2026-04-19 17:07:05 -05:00
debug . Log ( debug . DebugInfo , "Interface state changed" , "name" , i . Name , "enabled_prev" , prevState , "enabled" , i . Enabled , "online_prev" , ! i . Online , "online" , i . Online )
2024-12-30 02:26:51 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Disable ( ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2024-12-31 11:22:37 -06:00
i . Enabled = false
i . Online = false
2026-08-19 16:12:07 -05:00
debug . Log ( debug . DebugInfo , "Interface disabled and offline" , "name" , i . Name )
2024-12-31 11:22:37 -06:00
}
func ( i * BaseInterface ) GetName ( ) string {
return i . Name
2024-12-30 02:26:51 -06:00
}
func ( i * BaseInterface ) GetType ( ) common . InterfaceType {
return i . Type
}
func ( i * BaseInterface ) GetMode ( ) common . InterfaceMode {
return i . Mode
}
2026-08-13 17:50:08 -05:00
// GetBitrate returns the advertised interface bitrate in bits per second.
func ( i * BaseInterface ) GetBitrate ( ) int64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . Bitrate
}
2026-07-09 13:12:28 -05:00
// RecursivePRsEnabled reports whether unknown-path discovery is enabled.
func ( i * BaseInterface ) RecursivePRsEnabled ( ) bool {
return i . RecursivePRs
}
// AnnouncesFromInternalFlag reports whether announces from internal next hops
// may be rebroadcast (default true).
func ( i * BaseInterface ) AnnouncesFromInternalFlag ( ) bool {
return i . AnnouncesFromInternal
}
2026-07-25 14:43:24 -05:00
// AnnouncesToInternalFlag reports whether this interface may feed announces
// onto internal-mode interfaces (RNS 1.4.1).
func ( i * BaseInterface ) AnnouncesToInternalFlag ( ) bool {
return i . AnnouncesToInternal
}
// GetGravity returns configured pathing affinity.
func ( i * BaseInterface ) GetGravity ( ) int {
return i . Gravity
}
// SetGravity sets configured pathing affinity.
func ( i * BaseInterface ) SetGravity ( g int ) {
i . Gravity = g
}
2026-07-16 11:16:37 -05:00
// AllowsOutgoing reports whether this interface may transmit (config OUT).
func ( i * BaseInterface ) AllowsOutgoing ( ) bool {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return ! i . ReceiveOnly
}
// SetOutgoingAllowed sets the config-driven transmit permit.
func ( i * BaseInterface ) SetOutgoingAllowed ( allowed bool ) {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
i . ReceiveOnly = ! allowed
}
2024-12-30 02:26:51 -06:00
func ( i * BaseInterface ) GetMTU ( ) int {
2026-07-19 04:14:08 -05:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2024-12-30 02:26:51 -06:00
return i . MTU
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) IsOnline ( ) bool {
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2024-12-31 11:22:37 -06:00
return i . Online
2024-12-30 02:26:51 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) IsDetached ( ) bool {
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2024-12-31 11:22:37 -06:00
return i . Detached
}
2026-01-02 17:48:15 -06:00
func ( i * BaseInterface ) GetTxBytes ( ) uint64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . TxBytes
}
func ( i * BaseInterface ) GetRxBytes ( ) uint64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . RxBytes
}
func ( i * BaseInterface ) GetTxPackets ( ) uint64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . TxPackets
}
func ( i * BaseInterface ) GetRxPackets ( ) uint64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . RxPackets
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Start ( ) error {
2024-12-30 02:26:51 -06:00
return nil
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Stop ( ) error {
return nil
2024-12-30 12:58:43 -06:00
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) Send ( data [ ] byte , address string ) error {
2026-07-16 11:38:13 -05:00
if err := common . RejectReceiveOnly ( i ) ; err != nil {
return err
}
2026-08-19 16:12:07 -05:00
if debug . Enabled ( debug . DebugTrace ) {
debug . Log ( debug . DebugTrace , "Interface sending bytes" , "name" , i . Name , "bytes" , len ( data ) , "address" , address )
2026-08-15 02:38:24 -05:00
}
2025-01-01 01:41:16 -06:00
2026-08-26 12:26:13 -05:00
masked , err := common . ApplyIFACOutboundInto ( i , i . ifacOutboundScratch ( len ( data ) ) , data )
2025-01-01 01:41:16 -06:00
if err != nil {
2026-08-19 16:12:07 -05:00
debug . Log ( debug . DebugError , "Failed to mask outgoing packet for IFAC" , "name" , i . Name , "error" , err )
2026-04-18 05:21:54 -05:00
return err
}
if err := i . ProcessOutgoing ( masked ) ; err != nil {
2026-08-19 16:12:07 -05:00
debug . Log ( debug . DebugVerbose , "Interface failed to send data" , "name" , i . Name , "error" , err )
2025-01-01 01:41:16 -06:00
return err
}
2026-04-18 05:21:54 -05:00
i . updateBandwidthStats ( uint64 ( len ( masked ) ) )
2025-01-01 01:41:16 -06:00
return nil
2024-12-30 12:58:43 -06:00
}
2026-08-26 12:26:13 -05:00
func ( i * BaseInterface ) ifacOutboundScratch ( payloadLen int ) [ ] byte {
id := i . GetIFAC ( )
if id == nil {
return nil
}
need := payloadLen + id . Size ( ) + 2
if cap ( i . ifacScratch ) < need {
i . ifacScratch = make ( [ ] byte , need )
}
return i . ifacScratch [ : 0 ]
}
2024-12-31 11:22:37 -06:00
func ( i * BaseInterface ) GetConn ( ) net . Conn {
return nil
2024-12-30 03:50:52 -06:00
}
2025-01-01 00:58:37 -06:00
func ( i * BaseInterface ) GetBandwidthAvailable ( ) bool {
2025-12-30 21:14:13 -06:00
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
2025-01-01 00:58:37 -06:00
2026-07-10 08:14:04 -05:00
elapsed := time . Since ( i . lastTx )
2026-07-16 13:28:29 -05:00
// Coarse clocks (notably Windows) can report elapsed <= 0 on the same
// tick as lastTx. Still apply the sampled TX gate in that case.
if i . Bitrate <= 0 || elapsed > time . Second {
2026-08-19 16:12:07 -05:00
if debug . Enabled ( debug . DebugTrace ) {
debug . Log ( debug . DebugTrace , "Interface bandwidth available" , "name" , i . Name , "idle_seconds" , elapsed . Seconds ( ) )
}
2025-01-01 00:58:37 -06:00
return true
}
2026-04-19 17:07:05 -05:00
maxUsage := float64 ( i . Bitrate ) * PropagationRate
2026-07-10 09:28:05 -05:00
// Use sampled TX bitrate from SampleTraffic. Lifetime TxBytes/elapsed
// falsely reports multi-Gbps after a few KB and permanently closes the
// announce forward gate under normal mesh load.
if i . currentTXS <= 0 {
2026-08-19 16:12:07 -05:00
if debug . Enabled ( debug . DebugTrace ) {
debug . Log ( debug . DebugTrace , "Interface bandwidth available" , "name" , i . Name , "idle_seconds" , elapsed . Seconds ( ) )
}
2026-07-10 09:28:05 -05:00
return true
}
available := i . currentTXS < maxUsage
2026-08-19 16:12:07 -05:00
if debug . Enabled ( debug . DebugTrace ) {
debug . Log ( debug . DebugTrace , "Interface bandwidth stats" , "name" , i . Name , "current_bps" , i . currentTXS , "max_bps" , maxUsage , "usage_percent" , ( i . currentTXS / maxUsage ) * 100 , "available" , available )
}
2025-01-01 01:41:16 -06:00
return available
2025-01-01 00:58:37 -06:00
}
func ( i * BaseInterface ) updateBandwidthStats ( bytes uint64 ) {
2025-12-30 21:14:13 -06:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2025-01-01 00:58:37 -06:00
2026-07-05 05:51:46 -05:00
i . TxBytes += bytes
2026-08-14 15:17:03 -05:00
i . TxPackets ++
2025-01-01 00:58:37 -06:00
i . lastTx = time . Now ( )
2026-08-19 16:12:07 -05:00
if debug . Enabled ( debug . DebugTrace ) {
debug . Log ( debug . DebugTrace , "Interface updated bandwidth stats" , "name" , i . Name , "tx_bytes" , i . TxBytes , "last_tx" , i . lastTx )
}
2025-01-01 00:58:37 -06:00
}
2025-01-04 18:20:36 -06:00
2026-05-10 01:50:59 -05:00
// ReceivedPathRequest records an incoming path request for frequency tracking.
func ( i * BaseInterface ) ReceivedPathRequest ( ) {
2026-08-22 18:07:33 -05:00
i . ReceivedPathRequestBytes ( 0 )
}
// ReceivedPathRequestBytes records an incoming path request with byte size.
func ( i * BaseInterface ) ReceivedPathRequestBytes ( size int ) {
2026-05-10 01:50:59 -05:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-29 21:47:16 -05:00
i . tallyReceivedPathRequestBytes ( size )
2026-05-10 01:50:59 -05:00
i . ipFreqDeque = append ( i . ipFreqDeque , time . Now ( ) )
if len ( i . ipFreqDeque ) > prFreqSamples {
i . ipFreqDeque = i . ipFreqDeque [ 1 : ]
}
}
// SentPathRequest records an outgoing path request for frequency tracking.
func ( i * BaseInterface ) SentPathRequest ( ) {
2026-08-22 18:07:33 -05:00
i . SentPathRequestBytes ( 0 )
}
// SentPathRequestBytes records an outgoing path request with byte size.
func ( i * BaseInterface ) SentPathRequestBytes ( size int ) {
2026-05-10 01:50:59 -05:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-29 21:47:16 -05:00
i . tallySentPathRequestBytes ( size )
2026-05-10 01:50:59 -05:00
i . opFreqDeque = append ( i . opFreqDeque , time . Now ( ) )
if len ( i . opFreqDeque ) > prFreqSamples {
i . opFreqDeque = i . opFreqDeque [ 1 : ]
}
}
2026-07-09 11:12:31 -05:00
// ReceivedAnnounce records an incoming announce for frequency tracking.
func ( i * BaseInterface ) ReceivedAnnounce ( ) {
2026-08-22 18:07:33 -05:00
i . ReceivedAnnounceBytes ( 0 )
}
// ReceivedAnnounceBytes records an incoming announce with byte size.
func ( i * BaseInterface ) ReceivedAnnounceBytes ( size int ) {
2026-07-09 11:12:31 -05:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-29 21:47:16 -05:00
i . tallyReceivedAnnounceBytes ( size )
2026-07-09 11:12:31 -05:00
i . iaFreqDeque = append ( i . iaFreqDeque , time . Now ( ) )
if len ( i . iaFreqDeque ) > prFreqSamples {
i . iaFreqDeque = i . iaFreqDeque [ 1 : ]
}
}
// SentAnnounce records an outgoing announce for frequency tracking.
func ( i * BaseInterface ) SentAnnounce ( ) {
2026-08-22 18:07:33 -05:00
i . SentAnnounceBytes ( 0 )
}
// SentAnnounceBytes records an outgoing announce with byte size.
func ( i * BaseInterface ) SentAnnounceBytes ( size int ) {
2026-07-09 11:12:31 -05:00
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-29 21:47:16 -05:00
i . tallySentAnnounceBytes ( size )
2026-07-09 11:12:31 -05:00
i . oaFreqDeque = append ( i . oaFreqDeque , time . Now ( ) )
if len ( i . oaFreqDeque ) > prFreqSamples {
i . oaFreqDeque = i . oaFreqDeque [ 1 : ]
}
}
// IncomingAnnounceFrequency returns the estimated incoming announce rate in Hz.
func ( i * BaseInterface ) IncomingAnnounceFrequency ( ) float64 {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-13 10:10:04 -05:00
return i . incomingAnnounceHz ( )
2026-07-09 11:12:31 -05:00
}
// OutgoingAnnounceFrequency returns the estimated outgoing announce rate in Hz.
func ( i * BaseInterface ) OutgoingAnnounceFrequency ( ) float64 {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-13 10:10:04 -05:00
return i . outgoingAnnounceHz ( )
2026-07-09 11:12:31 -05:00
}
// IncomingPRFrequency returns the estimated incoming path-request rate in Hz.
func ( i * BaseInterface ) IncomingPRFrequency ( ) float64 {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-13 10:10:04 -05:00
return i . incomingPRHz ( )
2026-07-09 11:12:31 -05:00
}
// OutgoingPRFrequency returns the estimated outgoing path-request rate in Hz.
func ( i * BaseInterface ) OutgoingPRFrequency ( ) float64 {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
2026-08-13 10:10:04 -05:00
return i . outgoingPRHz ( )
2026-07-09 11:12:31 -05:00
}
// PRBurstActive reports whether path-request ingress burst limiting is active.
func ( i * BaseInterface ) PRBurstActive ( ) bool {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . icPRBurstActive
}
// SampleTraffic updates current RX/TX bitrates from byte-counter deltas.
func ( i * BaseInterface ) SampleTraffic ( ) {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
now := time . Now ( )
if i . sampleTS . IsZero ( ) {
i . sampleRXB = i . RxBytes
i . sampleTXB = i . TxBytes
2026-08-22 18:07:33 -05:00
i . sampleARXB = i . arxb
i . sampleATXB = i . atxb
i . samplePRXB = i . prxb
i . samplePTXB = i . ptxb
2026-07-09 11:12:31 -05:00
i . sampleTS = now
return
}
elapsed := now . Sub ( i . sampleTS ) . Seconds ( )
if elapsed <= 0 {
return
}
rxDiff := i . RxBytes - i . sampleRXB
txDiff := i . TxBytes - i . sampleTXB
i . currentRXS = float64 ( rxDiff * 8 ) / elapsed
i . currentTXS = float64 ( txDiff * 8 ) / elapsed
i . sampleRXB = i . RxBytes
i . sampleTXB = i . TxBytes
2026-08-22 18:07:33 -05:00
i . sampleTypedTraffic ( now , elapsed )
2026-07-09 11:12:31 -05:00
i . sampleTS = now
}
// GetRxSpeed returns the most recently sampled receive bitrate in bits/sec.
func ( i * BaseInterface ) GetRxSpeed ( ) float64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . currentRXS
}
// GetTxSpeed returns the most recently sampled transmit bitrate in bits/sec.
func ( i * BaseInterface ) GetTxSpeed ( ) float64 {
i . Mutex . RLock ( )
defer i . Mutex . RUnlock ( )
return i . currentTXS
}
2026-05-10 01:50:59 -05:00
// SetPRBurstConfig configures path-request burst thresholds.
func ( i * BaseInterface ) SetPRBurstConfig ( icPrBurstFreqNew , icPrBurstFreq , ecPrFreq float64 , egressControl bool ) {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
i . icPRBurstFreqNewV = icPrBurstFreqNew
i . icPRBurstFreqV = icPrBurstFreq
i . ecPRFreqV = ecPrFreq
i . egressControl = egressControl
}
// SetIngressControl sets whether ingress limiting is enabled.
func ( i * BaseInterface ) SetIngressControl ( enabled bool ) {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
i . ingressControl = enabled
}
2026-08-13 10:10:04 -05:00
func ( i * BaseInterface ) incomingAnnounceHz ( ) float64 {
2026-07-09 11:12:31 -05:00
n := len ( i . iaFreqDeque )
if n <= icDequeMinSample {
return 0
}
oldest := i . iaFreqDeque [ 0 ]
span := time . Since ( oldest ) . Seconds ( )
if span > prFreqDecay {
i . iaFreqDeque = i . iaFreqDeque [ 1 : ]
}
if span <= 0 {
return 0
}
return float64 ( n ) / span
}
2026-08-13 10:10:04 -05:00
func ( i * BaseInterface ) outgoingAnnounceHz ( ) float64 {
2026-07-09 11:12:31 -05:00
n := len ( i . oaFreqDeque )
if n <= 1 {
return 0
}
oldest := i . oaFreqDeque [ 0 ]
span := time . Since ( oldest ) . Seconds ( )
if span > prFreqDecay {
i . oaFreqDeque = i . oaFreqDeque [ 1 : ]
}
if span <= 0 {
return 0
}
return float64 ( n ) / span
}
2026-08-13 10:10:04 -05:00
func ( i * BaseInterface ) incomingPRHz ( ) float64 {
2026-05-10 01:50:59 -05:00
n := len ( i . ipFreqDeque )
if n <= icDequeMinSample {
return 0
}
oldest := i . ipFreqDeque [ 0 ]
span := time . Since ( oldest ) . Seconds ( )
if span > prFreqDecay {
i . ipFreqDeque = i . ipFreqDeque [ 1 : ]
}
if span <= 0 {
return 0
}
return float64 ( n ) / span
}
2026-08-13 10:10:04 -05:00
func ( i * BaseInterface ) outgoingPRHz ( ) float64 {
2026-05-10 01:50:59 -05:00
n := len ( i . opFreqDeque )
if n <= 1 {
return 0
}
oldest := i . opFreqDeque [ 0 ]
span := time . Since ( oldest ) . Seconds ( )
if span > prFreqDecay {
i . opFreqDeque = i . opFreqDeque [ 1 : ]
}
if span <= 0 {
return 0
}
return float64 ( n ) / span
}
func ( i * BaseInterface ) ShouldIngressLimitPR ( ) bool {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
if ! i . ingressControl {
return false
}
freqThreshold := i . icPRBurstFreqV
if time . Since ( i . created ) . Seconds ( ) < icNewTime {
freqThreshold = i . icPRBurstFreqNewV
}
2026-08-13 10:10:04 -05:00
ipFreq := i . incomingPRHz ( )
2026-05-10 01:50:59 -05:00
if i . icPRBurstActive {
if ipFreq < freqThreshold && time . Since ( i . icPRBurstActivated ) . Seconds ( ) > icBurstHold {
i . icPRBurstActive = false
}
return true
}
if ipFreq > freqThreshold {
i . icPRBurstActive = true
i . icPRBurstActivated = time . Now ( )
return true
}
return false
}
func ( i * BaseInterface ) ShouldEgressLimitPR ( ) bool {
i . Mutex . Lock ( )
defer i . Mutex . Unlock ( )
if ! i . egressControl {
return false
}
2026-08-13 10:10:04 -05:00
opFreq := i . outgoingPRHz ( )
2026-05-10 01:50:59 -05:00
if opFreq > i . ecPRFreqV {
if len ( i . opFreqDeque ) >= icBurstMinSamples {
return true
}
}
return false
}
2025-01-04 18:20:36 -06:00
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 {
2026-04-19 17:07:05 -05:00
debug . Log ( debug . DebugError , "Failed to intercept outgoing packet" , "error" , err )
2025-01-04 18:20:36 -06:00
}
}
// Call original send
return i . originalSend ( data , addr )
}