Some checks failed
Go Build Multi-Platform / build (amd64, darwin) (push) Failing after 12s
Go Build Multi-Platform / build (amd64, freebsd) (push) Successful in 51s
Go Build Multi-Platform / build (amd64, linux) (push) Successful in 49s
Go Build Multi-Platform / build (arm, freebsd) (push) Successful in 49s
Go Build Multi-Platform / build (amd64, windows) (push) Successful in 57s
Go Build Multi-Platform / build (arm, windows) (push) Failing after 19s
Go Build Multi-Platform / build (arm, linux) (push) Failing after 21s
Go Build Multi-Platform / build (arm64, darwin) (push) Successful in 44s
Go Build Multi-Platform / build (arm64, freebsd) (push) Successful in 48s
Go Build Multi-Platform / build (arm64, linux) (push) Successful in 47s
Go Build Multi-Platform / build (arm64, windows) (push) Successful in 46s
Run Gosec / tests (push) Successful in 45s
Go Build Multi-Platform / Create Release (push) Has been skipped
Go Revive Lint / lint (push) Successful in 9m48s
Go Test Multi-Platform / Test (ubuntu-latest, arm64) (push) Successful in 19m13s
Go Test Multi-Platform / Test (ubuntu-latest, amd64) (push) Successful in 19m19s
240 lines
5.1 KiB
Go
240 lines
5.1 KiB
Go
package interfaces
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
|
|
"git.quad4.io/Networks/Reticulum-Go/pkg/common"
|
|
"git.quad4.io/Networks/Reticulum-Go/pkg/debug"
|
|
)
|
|
|
|
type UDPInterface struct {
|
|
BaseInterface
|
|
conn *net.UDPConn
|
|
addr *net.UDPAddr
|
|
targetAddr *net.UDPAddr
|
|
mutex sync.RWMutex
|
|
readBuffer []byte
|
|
}
|
|
|
|
func NewUDPInterface(name string, addr string, target string, enabled bool) (*UDPInterface, error) {
|
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var targetAddr *net.UDPAddr
|
|
if target != "" {
|
|
targetAddr, err = net.ResolveUDPAddr("udp", target)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
ui := &UDPInterface{
|
|
BaseInterface: NewBaseInterface(name, common.IF_TYPE_UDP, enabled),
|
|
addr: udpAddr,
|
|
targetAddr: targetAddr,
|
|
readBuffer: make([]byte, 1064),
|
|
}
|
|
|
|
ui.MTU = 1064
|
|
|
|
return ui, nil
|
|
}
|
|
|
|
func (ui *UDPInterface) GetName() string {
|
|
return ui.Name
|
|
}
|
|
|
|
func (ui *UDPInterface) GetType() common.InterfaceType {
|
|
return ui.Type
|
|
}
|
|
|
|
func (ui *UDPInterface) GetMode() common.InterfaceMode {
|
|
return ui.Mode
|
|
}
|
|
|
|
func (ui *UDPInterface) IsOnline() bool {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.Online
|
|
}
|
|
|
|
func (ui *UDPInterface) IsDetached() bool {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.Detached
|
|
}
|
|
|
|
func (ui *UDPInterface) Detach() {
|
|
ui.mutex.Lock()
|
|
defer ui.mutex.Unlock()
|
|
ui.Detached = true
|
|
if ui.conn != nil {
|
|
ui.conn.Close() // #nosec G104
|
|
}
|
|
}
|
|
|
|
func (ui *UDPInterface) Send(data []byte, addr string) error {
|
|
debug.Log(debug.DEBUG_ALL, "UDP interface sending bytes", "name", ui.Name, "bytes", len(data))
|
|
|
|
if !ui.IsEnabled() {
|
|
return fmt.Errorf("interface not enabled")
|
|
}
|
|
|
|
if ui.targetAddr == nil {
|
|
return fmt.Errorf("no target address configured")
|
|
}
|
|
|
|
// Update TX stats before sending
|
|
ui.mutex.Lock()
|
|
ui.TxBytes += uint64(len(data))
|
|
ui.mutex.Unlock()
|
|
|
|
_, err := ui.conn.WriteTo(data, ui.targetAddr)
|
|
if err != nil {
|
|
debug.Log(debug.DEBUG_CRITICAL, "UDP interface write failed", "name", ui.Name, "error", err)
|
|
} else {
|
|
debug.Log(debug.DEBUG_ALL, "UDP interface sent bytes successfully", "name", ui.Name, "bytes", len(data))
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (ui *UDPInterface) SetPacketCallback(callback common.PacketCallback) {
|
|
ui.mutex.Lock()
|
|
defer ui.mutex.Unlock()
|
|
ui.packetCallback = callback
|
|
}
|
|
|
|
func (ui *UDPInterface) GetPacketCallback() common.PacketCallback {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.packetCallback
|
|
}
|
|
|
|
func (ui *UDPInterface) ProcessIncoming(data []byte) {
|
|
if callback := ui.GetPacketCallback(); callback != nil {
|
|
callback(data, ui)
|
|
}
|
|
}
|
|
|
|
func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
|
|
if !ui.IsOnline() {
|
|
return fmt.Errorf("interface offline")
|
|
}
|
|
|
|
if ui.targetAddr == nil {
|
|
return fmt.Errorf("no target address configured")
|
|
}
|
|
|
|
_, err := ui.conn.WriteToUDP(data, ui.targetAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("UDP write failed: %v", err)
|
|
}
|
|
|
|
ui.mutex.Lock()
|
|
ui.TxBytes += uint64(len(data))
|
|
ui.mutex.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ui *UDPInterface) GetConn() net.Conn {
|
|
return ui.conn
|
|
}
|
|
|
|
func (ui *UDPInterface) GetTxBytes() uint64 {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.TxBytes
|
|
}
|
|
|
|
func (ui *UDPInterface) GetRxBytes() uint64 {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.RxBytes
|
|
}
|
|
|
|
func (ui *UDPInterface) GetMTU() int {
|
|
return ui.MTU
|
|
}
|
|
|
|
func (ui *UDPInterface) GetBitrate() int {
|
|
return int(ui.Bitrate)
|
|
}
|
|
|
|
func (ui *UDPInterface) Enable() {
|
|
ui.mutex.Lock()
|
|
defer ui.mutex.Unlock()
|
|
ui.Online = true
|
|
}
|
|
|
|
func (ui *UDPInterface) Disable() {
|
|
ui.mutex.Lock()
|
|
defer ui.mutex.Unlock()
|
|
ui.Online = false
|
|
}
|
|
|
|
func (ui *UDPInterface) Start() error {
|
|
conn, err := net.ListenUDP("udp", ui.addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ui.conn = conn
|
|
|
|
// Enable broadcast mode if we have a target address
|
|
if ui.targetAddr != nil {
|
|
// Get the raw connection file descriptor to set SO_BROADCAST
|
|
if err := conn.SetReadBuffer(1064); err != nil {
|
|
debug.Log(debug.DEBUG_ERROR, "Failed to set read buffer size", "error", err)
|
|
}
|
|
if err := conn.SetWriteBuffer(1064); err != nil {
|
|
debug.Log(debug.DEBUG_ERROR, "Failed to set write buffer size", "error", err)
|
|
}
|
|
}
|
|
|
|
ui.Online = true
|
|
|
|
// Start the read loop in a goroutine
|
|
go ui.readLoop()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ui *UDPInterface) readLoop() {
|
|
buffer := make([]byte, 1064)
|
|
for ui.IsOnline() && !ui.IsDetached() {
|
|
n, remoteAddr, err := ui.conn.ReadFromUDP(buffer)
|
|
if err != nil {
|
|
if ui.IsOnline() {
|
|
debug.Log(debug.DEBUG_ERROR, "Error reading from UDP interface", "name", ui.Name, "error", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Update RX stats
|
|
ui.mutex.Lock()
|
|
// #nosec G115 - Network read sizes are always positive and within safe range
|
|
ui.RxBytes += uint64(n)
|
|
|
|
// Auto-discover target address from first packet if not set
|
|
if ui.targetAddr == nil {
|
|
debug.Log(debug.DEBUG_ALL, "UDP interface discovered peer", "name", ui.Name, "peer", remoteAddr.String())
|
|
ui.targetAddr = remoteAddr
|
|
}
|
|
ui.mutex.Unlock()
|
|
|
|
if ui.packetCallback != nil {
|
|
ui.packetCallback(buffer[:n], ui)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (ui *UDPInterface) IsEnabled() bool {
|
|
ui.mutex.RLock()
|
|
defer ui.mutex.RUnlock()
|
|
return ui.Enabled && ui.Online && !ui.Detached
|
|
}
|