mirror of
https://github.com/Quad4-Software/Reticulum-Go
synced 2026-08-29 23:48:44 -04:00
702 lines
18 KiB
Go
702 lines
18 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright (c) 2024-2026 Quad4.io
|
|
|
|
package interfaces
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"quad4/reticulum-go/pkg/common"
|
|
"quad4/reticulum-go/pkg/debug"
|
|
)
|
|
|
|
// Interface is the package-local name for a network interface.
|
|
// It matches common.NetworkInterface so transport and config share one contract.
|
|
type Interface interface {
|
|
common.NetworkInterface
|
|
}
|
|
|
|
const (
|
|
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
|
|
)
|
|
|
|
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
|
|
TxPackets uint64
|
|
RxPackets uint64
|
|
lastTx time.Time
|
|
lastRx time.Time
|
|
|
|
Mutex sync.RWMutex // exported so concrete interfaces can lock with parent fields
|
|
packetCallback common.PacketCallback
|
|
|
|
// IFACIdentity is set when the interface participates in an IFAC network.
|
|
// When non-nil, outbound packets are masked before transmit and inbound
|
|
// packets are unmasked and verified. Unauthenticated packets are dropped.
|
|
|
|
IFACIdentity common.IFAC
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// ReceiveOnly blocks transmit when true (Python outgoing = no).
|
|
// Zero value is false so unset interfaces still transmit.
|
|
ReceiveOnly bool
|
|
|
|
// Path request frequency tracking (ingress/egress burst control)
|
|
created time.Time
|
|
ipFreqDeque []time.Time
|
|
opFreqDeque []time.Time
|
|
iaFreqDeque []time.Time
|
|
oaFreqDeque []time.Time
|
|
icPRBurstActive bool
|
|
icPRBurstActivated time.Time
|
|
ingressControl bool
|
|
egressControl bool
|
|
icPRBurstFreqNewV float64
|
|
icPRBurstFreqV float64
|
|
ecPRFreqV float64
|
|
|
|
currentRXS float64
|
|
currentTXS float64
|
|
sampleRXB uint64
|
|
sampleTXB uint64
|
|
sampleTS time.Time
|
|
}
|
|
|
|
// NewBaseInterface creates a BaseInterface value for embedding at construction.
|
|
// Do not copy a BaseInterface after it has been used (Mutex must not be copied).
|
|
func NewBaseInterface(name string, ifType common.InterfaceType, enabled bool) BaseInterface {
|
|
return BaseInterface{
|
|
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,
|
|
ecPRFreqV: ecPRFreq,
|
|
TxPackets: 0,
|
|
RxPackets: 0,
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
func (i *BaseInterface) ProcessIncoming(data []byte) {
|
|
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) {
|
|
i.Mutex.Lock()
|
|
i.RxBytes += uint64(len(data))
|
|
i.RxPackets++
|
|
name := i.Name
|
|
i.Mutex.Unlock()
|
|
|
|
if !admitIncomingFrom(i, name, data, peerKey) {
|
|
return
|
|
}
|
|
|
|
stripped, ok := common.ApplyIFACInbound(i, data)
|
|
if !ok {
|
|
debug.Log(debug.DebugVerbose, "Dropped packet failing IFAC policy", "name", i.Name, "size", len(data))
|
|
return
|
|
}
|
|
|
|
i.Mutex.RLock()
|
|
callback := i.packetCallback
|
|
i.Mutex.RUnlock()
|
|
|
|
if callback != nil {
|
|
callback(stripped, i)
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
func (i *BaseInterface) ProcessOutgoing(data []byte) error {
|
|
debug.Log(debug.DebugCritical, "BaseInterface.ProcessOutgoing called directly, concrete interface type must override it", "name", i.Name, "bytes", len(data))
|
|
return fmt.Errorf("ProcessOutgoing not implemented on abstract interfaces.BaseInterface (name=%q, %d bytes); concrete interface type must override it", i.Name, len(data))
|
|
}
|
|
|
|
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, 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, 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.DebugInfo, "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.DebugError, "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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
func (i *BaseInterface) GetMTU() int {
|
|
i.Mutex.RLock()
|
|
defer i.Mutex.RUnlock()
|
|
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) 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
|
|
}
|
|
|
|
func (i *BaseInterface) Start() error {
|
|
return nil
|
|
}
|
|
|
|
func (i *BaseInterface) Stop() error {
|
|
return nil
|
|
}
|
|
|
|
func (i *BaseInterface) Send(data []byte, address string) error {
|
|
if err := common.RejectReceiveOnly(i); err != nil {
|
|
return err
|
|
}
|
|
debug.Log(debug.DebugVerbose, "Interface sending bytes", "name", i.Name, "bytes", len(data), "address", address)
|
|
|
|
masked, err := common.ApplyIFACOutbound(i, data)
|
|
if err != nil {
|
|
debug.Log(debug.DebugCritical, "Failed to mask outgoing packet for IFAC", "name", i.Name, "error", err)
|
|
return err
|
|
}
|
|
|
|
if err := i.ProcessOutgoing(masked); err != nil {
|
|
debug.Log(debug.DebugCritical, "Interface failed to send data", "name", i.Name, "error", err)
|
|
return err
|
|
}
|
|
|
|
i.updateBandwidthStats(uint64(len(masked)))
|
|
return nil
|
|
}
|
|
|
|
func (i *BaseInterface) GetConn() net.Conn {
|
|
return nil
|
|
}
|
|
|
|
func (i *BaseInterface) GetBandwidthAvailable() bool {
|
|
i.Mutex.RLock()
|
|
defer i.Mutex.RUnlock()
|
|
|
|
elapsed := time.Since(i.lastTx)
|
|
// 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 {
|
|
debug.Log(debug.DebugVerbose, "Interface bandwidth available", "name", i.Name, "idle_seconds", elapsed.Seconds())
|
|
return true
|
|
}
|
|
|
|
maxUsage := float64(i.Bitrate) * PropagationRate
|
|
// 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 {
|
|
debug.Log(debug.DebugVerbose, "Interface bandwidth available", "name", i.Name, "idle_seconds", elapsed.Seconds())
|
|
return true
|
|
}
|
|
available := i.currentTXS < maxUsage
|
|
debug.Log(debug.DebugVerbose, "Interface bandwidth stats", "name", i.Name, "current_bps", i.currentTXS, "max_bps", maxUsage, "usage_percent", (i.currentTXS/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.DebugVerbose, "Interface updated bandwidth stats", "name", i.Name, "tx_bytes", i.TxBytes, "last_tx", i.lastTx)
|
|
}
|
|
|
|
// ReceivedPathRequest records an incoming path request for frequency tracking.
|
|
func (i *BaseInterface) ReceivedPathRequest() {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
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() {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
i.opFreqDeque = append(i.opFreqDeque, time.Now())
|
|
if len(i.opFreqDeque) > prFreqSamples {
|
|
i.opFreqDeque = i.opFreqDeque[1:]
|
|
}
|
|
}
|
|
|
|
// ReceivedAnnounce records an incoming announce for frequency tracking.
|
|
func (i *BaseInterface) ReceivedAnnounce() {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
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() {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
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()
|
|
return i.incomingAnnounceHz()
|
|
}
|
|
|
|
// OutgoingAnnounceFrequency returns the estimated outgoing announce rate in Hz.
|
|
func (i *BaseInterface) OutgoingAnnounceFrequency() float64 {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
return i.outgoingAnnounceHz()
|
|
}
|
|
|
|
// IncomingPRFrequency returns the estimated incoming path-request rate in Hz.
|
|
func (i *BaseInterface) IncomingPRFrequency() float64 {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
return i.incomingPRHz()
|
|
}
|
|
|
|
// OutgoingPRFrequency returns the estimated outgoing path-request rate in Hz.
|
|
func (i *BaseInterface) OutgoingPRFrequency() float64 {
|
|
i.Mutex.Lock()
|
|
defer i.Mutex.Unlock()
|
|
return i.outgoingPRHz()
|
|
}
|
|
|
|
// 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
|
|
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
|
|
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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
func (i *BaseInterface) incomingAnnounceHz() float64 {
|
|
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
|
|
}
|
|
|
|
func (i *BaseInterface) outgoingAnnounceHz() float64 {
|
|
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
|
|
}
|
|
|
|
func (i *BaseInterface) incomingPRHz() float64 {
|
|
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
|
|
}
|
|
|
|
func (i *BaseInterface) outgoingPRHz() float64 {
|
|
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
|
|
}
|
|
ipFreq := i.incomingPRHz()
|
|
|
|
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
|
|
}
|
|
|
|
opFreq := i.outgoingPRHz()
|
|
if opFreq > i.ecPRFreqV {
|
|
if len(i.opFreqDeque) >= icBurstMinSamples {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
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.DebugError, "Failed to intercept outgoing packet", "error", err)
|
|
}
|
|
}
|
|
|
|
// Call original send
|
|
return i.originalSend(data, addr)
|
|
}
|