Add configuration system and ARP-based device discovery

Implement comprehensive configuration management with CLI flags for WiFi interface, device discovery method, and file paths. Add ARP table parsing as an alternative to DHCP leases for more reliable device detection. Improve WiFi scanning to handle concurrent scan requests gracefully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2025-12-28 18:51:15 +07:00
commit 2b3a5b89f8
8 changed files with 156 additions and 28 deletions

View file

@ -2,25 +2,71 @@ package device
import (
"bufio"
"fmt"
"net"
"os"
"os/exec"
"regexp"
"strings"
"github.com/nemunaire/repeater/internal/config"
"github.com/nemunaire/repeater/internal/models"
)
// ARPEntry represents an entry in the ARP table
type ARPEntry struct {
IP net.IP
HWType int
Flags int
HWAddress net.HardwareAddr
Mask string
Device string
}
// GetConnectedDevices returns a list of connected devices
func GetConnectedDevices() ([]models.ConnectedDevice, error) {
func GetConnectedDevices(cfg *config.Config) ([]models.ConnectedDevice, error) {
if cfg.UseARPDiscovery {
return getDevicesFromARP(cfg)
}
return getDevicesFromDHCP(cfg)
}
// getDevicesFromARP discovers devices using ARP table
func getDevicesFromARP(cfg *config.Config) ([]models.ConnectedDevice, error) {
var devices []models.ConnectedDevice
// Read DHCP leases
leases, err := parseDHCPLeases()
arpEntries, err := parseARPTable(cfg.ARPTablePath)
if err != nil {
return devices, err
}
// Get ARP information
for _, entry := range arpEntries {
// Only include entries with valid flags (2 = COMPLETE, 6 = COMPLETE|PERM)
if entry.Flags == 2 || entry.Flags == 6 {
device := models.ConnectedDevice{
Name: "", // No hostname available from ARP
MAC: entry.HWAddress.String(),
IP: entry.IP.String(),
Type: guessDeviceType("", entry.HWAddress.String()),
}
devices = append(devices, device)
}
}
return devices, nil
}
// getDevicesFromDHCP discovers devices using DHCP leases and ARP validation
func getDevicesFromDHCP(cfg *config.Config) ([]models.ConnectedDevice, error) {
var devices []models.ConnectedDevice
// Read DHCP leases
leases, err := parseDHCPLeases(cfg.DHCPLeasesPath)
if err != nil {
return devices, err
}
// Get ARP information for validation
arpInfo, err := getARPInfo()
if err != nil {
return devices, err
@ -43,11 +89,57 @@ func GetConnectedDevices() ([]models.ConnectedDevice, error) {
return devices, nil
}
// parseARPTable reads and parses ARP table from /proc/net/arp format
func parseARPTable(path string) ([]ARPEntry, error) {
var entries []ARPEntry
content, err := os.ReadFile(path)
if err != nil {
return entries, err
}
for _, line := range strings.Split(string(content), "\n") {
fields := strings.Fields(line)
if len(fields) > 5 {
var entry ARPEntry
// Parse HWType (hex format)
if _, err := fmt.Sscanf(fields[1], "0x%x", &entry.HWType); err != nil {
continue
}
// Parse Flags (hex format)
if _, err := fmt.Sscanf(fields[2], "0x%x", &entry.Flags); err != nil {
continue
}
// Parse IP address
entry.IP = net.ParseIP(fields[0])
if entry.IP == nil {
continue
}
// Parse MAC address
entry.HWAddress, err = net.ParseMAC(fields[3])
if err != nil {
continue
}
entry.Mask = fields[4]
entry.Device = fields[5]
entries = append(entries, entry)
}
}
return entries, nil
}
// parseDHCPLeases reads and parses DHCP lease file
func parseDHCPLeases() ([]models.DHCPLease, error) {
func parseDHCPLeases(path string) ([]models.DHCPLease, error) {
var leases []models.DHCPLease
file, err := os.Open("/var/lib/dhcp/dhcpd.leases")
file, err := os.Open(path)
if err != nil {
return leases, err
}