This commit is contained in:
Sudo-Ivan
2025-01-01 01:41:16 -06:00
parent ae40d2879c
commit 73af84e24f
5 changed files with 455 additions and 262 deletions

View File

@@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"os/signal"
"sync"
"syscall"
"time"
@@ -22,7 +23,7 @@ import (
)
var (
debugLevel = flag.Int("debug", 4, "Debug level (0-7)")
debugLevel = flag.Int("debug", 7, "Debug level (0-7)")
)
func debugLog(level int, format string, v ...interface{}) {
@@ -31,14 +32,37 @@ func debugLog(level int, format string, v ...interface{}) {
}
}
const (
ANNOUNCE_RATE_TARGET = 3600 // Default target time between announces (1 hour)
ANNOUNCE_RATE_GRACE = 3 // Number of grace announces before enforcing rate
ANNOUNCE_RATE_PENALTY = 7200 // Additional penalty time for rate violations
MAX_ANNOUNCE_HOPS = 128 // Maximum number of hops for announces
DEBUG_CRITICAL = 1 // Critical errors
DEBUG_ERROR = 2 // Non-critical errors
DEBUG_INFO = 3 // Important information
DEBUG_VERBOSE = 4 // Detailed information
DEBUG_TRACE = 5 // Very detailed tracing
DEBUG_PACKETS = 6 // Packet-level details
DEBUG_ALL = 7 // Everything
)
type Reticulum struct {
config *common.ReticulumConfig
transport *transport.Transport
interfaces []interfaces.Interface
channels map[string]*channel.Channel
buffers map[string]*buffer.Buffer
announceHandlers map[string][]announce.AnnounceHandler
pathRequests map[string]*common.PathRequest
config *common.ReticulumConfig
transport *transport.Transport
interfaces []interfaces.Interface
channels map[string]*channel.Channel
buffers map[string]*buffer.Buffer
announceHandlers map[string][]announce.AnnounceHandler
pathRequests map[string]*common.PathRequest
announceHistory map[string]announceRecord
announceHistoryMu sync.RWMutex
}
type announceRecord struct {
lastSeen time.Time
seenCount int
violations int
interfaces map[string]bool
}
func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
@@ -54,7 +78,7 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
t := transport.NewTransport(cfg)
debugLog(3, "Transport initialized")
return &Reticulum{
r := &Reticulum{
config: cfg,
transport: t,
interfaces: make([]interfaces.Interface, 0),
@@ -62,15 +86,63 @@ func NewReticulum(cfg *common.ReticulumConfig) (*Reticulum, error) {
buffers: make(map[string]*buffer.Buffer),
announceHandlers: make(map[string][]announce.AnnounceHandler),
pathRequests: make(map[string]*common.PathRequest),
}, nil
announceHistory: make(map[string]announceRecord),
}
// Initialize interfaces from config
for name, ifaceConfig := range cfg.Interfaces {
if !ifaceConfig.Enabled {
continue
}
var iface interfaces.Interface
var err error
switch ifaceConfig.Type {
case "TCPClientInterface":
iface, err = interfaces.NewTCPClientInterface(
name,
ifaceConfig.TargetHost,
ifaceConfig.TargetPort,
ifaceConfig.Enabled,
true, // IN
true, // OUT
)
case "UDPInterface":
iface, err = interfaces.NewUDPInterface(
name,
ifaceConfig.Address,
ifaceConfig.TargetHost,
ifaceConfig.Enabled,
)
case "AutoInterface":
iface, err = interfaces.NewAutoInterface(name, ifaceConfig)
default:
debugLog(1, "Unknown interface type: %s", ifaceConfig.Type)
continue
}
if err != nil {
if cfg.PanicOnInterfaceErr {
return nil, fmt.Errorf("failed to create interface %s: %v", name, err)
}
debugLog(1, "Error creating interface %s: %v", name, err)
continue
}
debugLog(2, "Configuring interface %s (type=%s)...", name, ifaceConfig.Type)
r.interfaces = append(r.interfaces, iface)
}
return r, nil
}
func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
debugLog(2, "Setting up interface %s", iface.GetName())
debugLog(DEBUG_INFO, "Setting up interface %s (type=%T)", iface.GetName(), iface)
ch := channel.NewChannel(&transportWrapper{r.transport})
r.channels[iface.GetName()] = ch
debugLog(3, "Created channel for interface %s", iface.GetName())
debugLog(DEBUG_VERBOSE, "Created channel for interface %s with transport wrapper", iface.GetName())
rw := buffer.CreateBidirectionalBuffer(
1,
@@ -78,12 +150,16 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
ch,
func(size int) {
data := make([]byte, size)
debugLog(DEBUG_PACKETS, "Interface %s: Reading %d bytes from buffer", iface.GetName(), size)
iface.ProcessIncoming(data)
if len(data) > 0 && data[0] == announce.PACKET_TYPE_ANNOUNCE {
r.handleAnnounce(data, iface)
} else {
r.transport.HandlePacket(data, iface)
if len(data) > 0 {
debugLog(DEBUG_TRACE, "Interface %s: Received packet type 0x%02x", iface.GetName(), data[0])
if data[0] == announce.PACKET_TYPE_ANNOUNCE {
r.handleAnnounce(data, iface)
} else {
r.transport.HandlePacket(data, iface)
}
}
debugLog(5, "Processed %d bytes from interface %s", size, iface.GetName())
@@ -93,6 +169,7 @@ func (r *Reticulum) handleInterface(iface common.NetworkInterface) {
r.buffers[iface.GetName()] = &buffer.Buffer{
ReadWriter: rw,
}
debugLog(DEBUG_VERBOSE, "Created bidirectional buffer for interface %s", iface.GetName())
iface.SetPacketCallback(func(data []byte, ni common.NetworkInterface) {
if buf, ok := r.buffers[ni.GetName()]; ok {
@@ -112,10 +189,14 @@ func (r *Reticulum) monitorInterfaces() {
for range ticker.C {
for _, iface := range r.interfaces {
if tcpClient, ok := iface.(*interfaces.TCPClientInterface); ok {
debugLog(4, "Interface %s status - Connected: %v, RTT: %v",
debugLog(DEBUG_VERBOSE, "Interface %s status - Connected: %v, RTT: %v, TX: %d bytes (%.2f Kbps), RX: %d bytes (%.2f Kbps)",
iface.GetName(),
tcpClient.IsConnected(),
tcpClient.GetRTT(),
tcpClient.GetTxBytes(),
float64(tcpClient.GetTxBytes()*8)/(5*1024), // Calculate Kbps over 5s interval
tcpClient.GetRxBytes(),
float64(tcpClient.GetRxBytes()*8)/(5*1024),
)
}
}
@@ -159,53 +240,65 @@ func main() {
log.Fatalf("Failed to create Reticulum instance: %v", err)
}
go r.monitorInterfaces()
// Register announce handler using the instance's transport
handler := &AnnounceHandler{
aspectFilter: []string{"*"}, // Handle all aspects
}
r.transport.RegisterAnnounceHandler(handler)
// Create a destination to announce
dest, err := identity.NewIdentity()
// Create identity and destination
identity, err := identity.NewIdentity()
if err != nil {
log.Fatalf("Failed to create identity: %v", err)
}
// Create announce for the destination
debugLog(2, "Created new identity: %x", identity.Hash())
// Create announce
announce, err := announce.NewAnnounce(
dest,
[]byte("Reticulum-Go"), // App data
nil, // No ratchet ID
false, // Not a path response
identity,
[]byte("nomadnetwork.node"),
nil, // No ratchet ID
false, // Not a path response
)
if err != nil {
log.Fatalf("Failed to create announce: %v", err)
}
// Send initial announce immediately
// Start monitoring interfaces
go r.monitorInterfaces()
// Register announce handler
handler := &AnnounceHandler{
aspectFilter: []string{"*"},
}
r.transport.RegisterAnnounceHandler(handler)
// Start Reticulum
if err := r.Start(); err != nil {
log.Fatalf("Failed to start Reticulum: %v", err)
}
// Send initial announces after interfaces are ready
time.Sleep(2 * time.Second) // Give interfaces time to connect
for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok {
debugLog(2, "Sending initial announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate initial announce: %v", err)
if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending initial announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate initial announce: %v", err)
}
}
}
}
// Then start periodic announces
// Start periodic announces
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok {
for range ticker.C {
debugLog(3, "Starting periodic announce cycle")
for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending periodic announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate announce: %v", err)
debugLog(1, "Failed to propagate periodic announce: %v", err)
}
}
}
@@ -213,10 +306,6 @@ func main() {
}
}()
if err := r.Start(); err != nil {
log.Fatalf("Failed to start Reticulum: %v", err)
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
@@ -298,75 +387,75 @@ func initializeDirectories() error {
func (r *Reticulum) Start() error {
debugLog(2, "Starting Reticulum...")
// Create identity for announces
identity, err := identity.NewIdentity()
if err != nil {
return fmt.Errorf("failed to create identity: %v", err)
}
debugLog(2, "Created new identity: %x", identity.Hash())
// Create announce
announce, err := announce.NewAnnounce(
identity,
[]byte("Reticulum-Go"),
nil, // No ratchet ID
false, // Not a path response
)
if err != nil {
return fmt.Errorf("failed to create announce: %v", err)
}
// Start transport
if err := r.transport.Start(); err != nil {
return fmt.Errorf("failed to start transport: %v", err)
}
debugLog(3, "Transport started successfully")
for name, ifaceConfig := range r.config.Interfaces {
if !ifaceConfig.Enabled {
debugLog(2, "Skipping disabled interface %s", name)
continue
// Start interfaces
for _, iface := range r.interfaces {
debugLog(2, "Starting interface %s...", iface.GetName())
if err := iface.Start(); err != nil {
return fmt.Errorf("failed to start interface %s: %v", iface.GetName(), err)
}
r.handleInterface(iface)
debugLog(3, "Interface %s started successfully", iface.GetName())
}
debugLog(2, "Configuring interface %s (type=%s)...", name, ifaceConfig.Type)
var iface interfaces.Interface
// Wait for interfaces to be ready
time.Sleep(2 * time.Second)
switch ifaceConfig.Type {
case "TCPClientInterface":
client, err := interfaces.NewTCPClientInterface(
ifaceConfig.Name,
ifaceConfig.TargetHost,
ifaceConfig.TargetPort,
ifaceConfig.KISSFraming,
ifaceConfig.I2PTunneled,
ifaceConfig.Enabled,
)
if err != nil {
if r.config.PanicOnInterfaceErr {
return fmt.Errorf("failed to create TCP client interface %s: %v", name, err)
// Send initial announces
for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending initial announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate initial announce: %v", err)
}
debugLog(1, "Failed to create TCP client interface %s: %v", name, err)
continue
}
iface = client
case "TCPServerInterface":
server, err := interfaces.NewTCPServerInterface(
ifaceConfig.Name,
ifaceConfig.Address,
ifaceConfig.Port,
ifaceConfig.KISSFraming,
ifaceConfig.I2PTunneled,
ifaceConfig.PreferIPv6,
)
if err != nil {
if r.config.PanicOnInterfaceErr {
return fmt.Errorf("failed to create TCP server interface %s: %v", name, err)
}
debugLog(1, "Failed to create TCP server interface %s: %v", name, err)
continue
}
iface = server
}
if iface != nil {
debugLog(2, "Starting interface %s...", name)
if err := iface.Start(); err != nil {
if r.config.PanicOnInterfaceErr {
return fmt.Errorf("failed to start interface %s: %v", name, err)
}
debugLog(1, "Failed to start interface %s: %v", name, err)
continue
}
netIface := iface.(common.NetworkInterface)
r.handleInterface(netIface)
r.interfaces = append(r.interfaces, iface)
debugLog(3, "Interface %s started successfully", name)
}
}
// Start periodic announces
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
debugLog(3, "Starting periodic announce cycle")
for _, iface := range r.interfaces {
if netIface, ok := iface.(common.NetworkInterface); ok {
if netIface.IsEnabled() && netIface.IsOnline() {
debugLog(2, "Sending periodic announce on interface %s", netIface.GetName())
if err := announce.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Failed to propagate periodic announce: %v", err)
}
}
}
}
}
}()
debugLog(2, "Reticulum started successfully")
return nil
}
@@ -408,13 +497,51 @@ func (r *Reticulum) handleAnnounce(data []byte, iface common.NetworkInterface) {
debugLog(1, "Error handling announce: %v", err)
return
}
debugLog(3, "Successfully parsed announce packet")
// Check announce history
announceKey := fmt.Sprintf("%x", a.Hash())
r.announceHistoryMu.Lock()
record, exists := r.announceHistory[announceKey]
if exists {
// Check if this interface has already seen this announce
if record.interfaces[iface.GetName()] {
r.announceHistoryMu.Unlock()
debugLog(4, "Duplicate announce from %s, ignoring", iface.GetName())
return
}
// Check rate limiting
timeSinceLastSeen := time.Since(record.lastSeen)
if timeSinceLastSeen < time.Duration(ANNOUNCE_RATE_TARGET)*time.Second {
if record.seenCount > ANNOUNCE_RATE_GRACE {
record.violations++
waitTime := ANNOUNCE_RATE_TARGET + (record.violations * ANNOUNCE_RATE_PENALTY)
r.announceHistoryMu.Unlock()
debugLog(3, "Rate limit exceeded for announce %s, waiting %d seconds", announceKey, waitTime)
return
}
}
record.seenCount++
record.lastSeen = time.Now()
record.interfaces[iface.GetName()] = true
} else {
record = announceRecord{
lastSeen: time.Now(),
seenCount: 1,
interfaces: make(map[string]bool),
}
record.interfaces[iface.GetName()] = true
r.announceHistory[announceKey] = record
}
r.announceHistoryMu.Unlock()
// Add random delay before propagation (0-2 seconds)
delay := time.Duration(rand.Float64() * 2 * float64(time.Second))
time.Sleep(delay)
// Check interface modes and propagate according to RNS rules
// Propagate to other interfaces according to RNS rules
for _, otherIface := range r.interfaces {
if otherIface.GetName() == iface.GetName() {
continue
@@ -433,10 +560,14 @@ func (r *Reticulum) handleAnnounce(data []byte, iface common.NetworkInterface) {
continue
}
// Check if interface has bandwidth available for announces
// Check if interface has bandwidth available
if netIface, ok := otherIface.(common.NetworkInterface); ok {
if err := a.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Error propagating announce: %v", err)
if netIface.GetBandwidthAvailable() {
if err := a.Propagate([]common.NetworkInterface{netIface}); err != nil {
debugLog(1, "Error propagating announce: %v", err)
}
} else {
debugLog(3, "Interface %s has insufficient bandwidth for announce", netIface.GetName())
}
}
}