Refactor stations discovery and add hostapd discovery
This commit is contained in:
parent
69594c2fe4
commit
2922a03724
15 changed files with 1339 additions and 249 deletions
184
internal/station/dhcp/backend.go
Normal file
184
internal/station/dhcp/backend.go
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package dhcp
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nemunaire/repeater/internal/station/backend"
|
||||
)
|
||||
|
||||
// Backend implements StationBackend using DHCP lease discovery
|
||||
type Backend struct {
|
||||
dhcpLeasesPath string
|
||||
lastStations map[string]backend.Station // Key: MAC address
|
||||
callbacks backend.EventCallbacks
|
||||
stopChan chan struct{}
|
||||
mu sync.RWMutex
|
||||
running bool
|
||||
}
|
||||
|
||||
// NewBackend creates a new DHCP backend
|
||||
func NewBackend() *Backend {
|
||||
return &Backend{
|
||||
lastStations: make(map[string]backend.Station),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize initializes the DHCP backend
|
||||
func (b *Backend) Initialize(config backend.BackendConfig) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
b.dhcpLeasesPath = config.DHCPLeasesPath
|
||||
if b.dhcpLeasesPath == "" {
|
||||
b.dhcpLeasesPath = "/var/lib/dhcp/dhcpd.leases"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close cleans up backend resources
|
||||
func (b *Backend) Close() error {
|
||||
b.StopEventMonitoring()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStations returns all connected stations from DHCP leases validated by ARP
|
||||
func (b *Backend) GetStations() ([]backend.Station, error) {
|
||||
b.mu.RLock()
|
||||
dhcpLeasesPath := b.dhcpLeasesPath
|
||||
b.mu.RUnlock()
|
||||
|
||||
// Read DHCP leases
|
||||
leases, err := parseDHCPLeases(dhcpLeasesPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get ARP information for validation
|
||||
arpInfo, err := getARPInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stations []backend.Station
|
||||
for _, lease := range leases {
|
||||
// Check if the device is still connected via ARP
|
||||
if _, exists := arpInfo[lease.IP]; exists {
|
||||
st := backend.Station{
|
||||
MAC: lease.MAC,
|
||||
IP: lease.IP,
|
||||
Hostname: lease.Hostname,
|
||||
Type: backend.GuessDeviceType(lease.Hostname, lease.MAC),
|
||||
Signal: 0, // Not available from DHCP
|
||||
RxBytes: 0, // Not available from DHCP
|
||||
TxBytes: 0, // Not available from DHCP
|
||||
ConnectedAt: time.Now(),
|
||||
}
|
||||
stations = append(stations, st)
|
||||
}
|
||||
}
|
||||
|
||||
return stations, nil
|
||||
}
|
||||
|
||||
// StartEventMonitoring starts monitoring for station events via polling
|
||||
func (b *Backend) StartEventMonitoring(callbacks backend.EventCallbacks) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if b.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.callbacks = callbacks
|
||||
b.running = true
|
||||
|
||||
// Start polling goroutine
|
||||
go b.pollLoop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopEventMonitoring stops event monitoring
|
||||
func (b *Backend) StopEventMonitoring() {
|
||||
b.mu.Lock()
|
||||
if !b.running {
|
||||
b.mu.Unlock()
|
||||
return
|
||||
}
|
||||
b.running = false
|
||||
b.mu.Unlock()
|
||||
|
||||
close(b.stopChan)
|
||||
}
|
||||
|
||||
// SupportsRealTimeEvents returns false (DHCP is polling-based)
|
||||
func (b *Backend) SupportsRealTimeEvents() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// pollLoop polls DHCP leases and simulates events
|
||||
func (b *Backend) pollLoop() {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
b.checkForChanges()
|
||||
case <-b.stopChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkForChanges compares current state with last state and triggers callbacks
|
||||
func (b *Backend) checkForChanges() {
|
||||
// Get current stations
|
||||
current, err := b.GetStations()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Build map of current stations
|
||||
currentMap := make(map[string]backend.Station)
|
||||
for _, st := range current {
|
||||
currentMap[st.MAC] = st
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
// Check for new stations (connected)
|
||||
for mac, st := range currentMap {
|
||||
if _, exists := b.lastStations[mac]; !exists {
|
||||
// New station connected
|
||||
if b.callbacks.OnStationConnected != nil {
|
||||
go b.callbacks.OnStationConnected(st)
|
||||
}
|
||||
} else {
|
||||
// Check for updates (IP change, hostname change, etc.)
|
||||
oldStation := b.lastStations[mac]
|
||||
if oldStation.IP != st.IP || oldStation.Hostname != st.Hostname {
|
||||
if b.callbacks.OnStationUpdated != nil {
|
||||
go b.callbacks.OnStationUpdated(st)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for disconnected stations
|
||||
for mac := range b.lastStations {
|
||||
if _, exists := currentMap[mac]; !exists {
|
||||
// Station disconnected
|
||||
if b.callbacks.OnStationDisconnected != nil {
|
||||
go b.callbacks.OnStationDisconnected(mac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update last state
|
||||
b.lastStations = currentMap
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue