Create wifi backend abstraction
This commit is contained in:
parent
f4481bca62
commit
79c28da9c5
7 changed files with 401 additions and 174 deletions
|
|
@ -51,8 +51,8 @@ func (a *App) Initialize(cfg *config.Config) error {
|
||||||
// Store config reference
|
// Store config reference
|
||||||
a.Config = cfg
|
a.Config = cfg
|
||||||
|
|
||||||
// Initialize WiFi D-Bus connection
|
// Initialize WiFi backend
|
||||||
if err := wifi.Initialize(cfg.WifiInterface); err != nil {
|
if err := wifi.Initialize(cfg.WifiInterface, cfg.WifiBackend); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
func declareFlags(o *Config) {
|
func declareFlags(o *Config) {
|
||||||
flag.StringVar(&o.Bind, "bind", ":8081", "Bind port/socket")
|
flag.StringVar(&o.Bind, "bind", ":8081", "Bind port/socket")
|
||||||
flag.StringVar(&o.WifiInterface, "wifi-interface", "wlan0", "WiFi interface name")
|
flag.StringVar(&o.WifiInterface, "wifi-interface", "wlan0", "WiFi interface name")
|
||||||
|
flag.StringVar(&o.WifiBackend, "wifi-backend", "", "WiFi backend to use: 'iwd' or 'wpasupplicant' (required)")
|
||||||
flag.BoolVar(&o.UseARPDiscovery, "use-arp-discovery", true, "Use ARP table for device discovery instead of DHCP leases")
|
flag.BoolVar(&o.UseARPDiscovery, "use-arp-discovery", true, "Use ARP table for device discovery instead of DHCP leases")
|
||||||
flag.StringVar(&o.DHCPLeasesPath, "dhcp-leases-path", "/var/lib/dhcp/dhcpd.leases", "Path to DHCP leases file")
|
flag.StringVar(&o.DHCPLeasesPath, "dhcp-leases-path", "/var/lib/dhcp/dhcpd.leases", "Path to DHCP leases file")
|
||||||
flag.StringVar(&o.ARPTablePath, "arp-table-path", "/proc/net/arp", "Path to ARP table file")
|
flag.StringVar(&o.ARPTablePath, "arp-table-path", "/proc/net/arp", "Path to ARP table file")
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Bind string
|
Bind string
|
||||||
WifiInterface string
|
WifiInterface string
|
||||||
|
WifiBackend string
|
||||||
UseARPDiscovery bool
|
UseARPDiscovery bool
|
||||||
DHCPLeasesPath string
|
DHCPLeasesPath string
|
||||||
ARPTablePath string
|
ARPTablePath string
|
||||||
|
|
@ -75,6 +76,11 @@ func ConsolidateConfig() (opts *Config, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate configuration
|
||||||
|
if opts.WifiBackend != "iwd" && opts.WifiBackend != "wpasupplicant" {
|
||||||
|
log.Fatalf("wifi-backend must be set to 'iwd' or 'wpasupplicant' (got: '%s')", opts.WifiBackend)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
56
internal/wifi/backend/types.go
Normal file
56
internal/wifi/backend/types.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package backend
|
||||||
|
|
||||||
|
// WiFiBackend is the interface that must be implemented by all WiFi backends (iwd, wpa_supplicant, etc.)
|
||||||
|
type WiFiBackend interface {
|
||||||
|
// Lifecycle Management
|
||||||
|
Initialize(interfaceName string) error
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Network Discovery
|
||||||
|
ScanNetworks() error
|
||||||
|
GetOrderedNetworks() ([]BackendNetwork, error)
|
||||||
|
IsScanning() (bool, error)
|
||||||
|
|
||||||
|
// Connection Management
|
||||||
|
Connect(ssid, password string) error
|
||||||
|
Disconnect() error
|
||||||
|
GetConnectionState() (ConnectionState, error)
|
||||||
|
GetConnectedSSID() string
|
||||||
|
|
||||||
|
// Event Monitoring
|
||||||
|
StartEventMonitoring(callbacks EventCallbacks) error
|
||||||
|
StopEventMonitoring()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackendNetwork represents a WiFi network in a backend-agnostic format.
|
||||||
|
// Both iwd and wpa_supplicant backends convert their native representations to this type.
|
||||||
|
type BackendNetwork struct {
|
||||||
|
SSID string
|
||||||
|
SignalDBm int16 // Signal strength in dBm (-100 to 0)
|
||||||
|
SecurityType string // "open", "wep", "psk", "8021x"
|
||||||
|
BSSID string // MAC address of the access point
|
||||||
|
Frequency uint32 // Frequency in MHz (e.g., 2412 for channel 1, 5180 for channel 36)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionState represents the WiFi connection state in a backend-agnostic way.
|
||||||
|
type ConnectionState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateConnected ConnectionState = "connected"
|
||||||
|
StateDisconnected ConnectionState = "disconnected"
|
||||||
|
StateConnecting ConnectionState = "connecting"
|
||||||
|
StateDisconnecting ConnectionState = "disconnecting"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EventCallbacks defines callback functions that backends use to notify the wifi package of events.
|
||||||
|
// This allows the wifi package to remain backend-agnostic while still receiving real-time updates.
|
||||||
|
type EventCallbacks struct {
|
||||||
|
// OnStateChange is called when the connection state changes
|
||||||
|
OnStateChange func(state ConnectionState, ssid string)
|
||||||
|
|
||||||
|
// OnScanComplete is called when a network scan completes
|
||||||
|
OnScanComplete func()
|
||||||
|
|
||||||
|
// OnSignalUpdate is called when signal strength changes for the connected network
|
||||||
|
OnSignalUpdate func(ssid string, signalDBm int16)
|
||||||
|
}
|
||||||
21
internal/wifi/factory.go
Normal file
21
internal/wifi/factory.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package wifi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nemunaire/repeater/internal/wifi/backend"
|
||||||
|
"github.com/nemunaire/repeater/internal/wifi/iwd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createBackend creates the appropriate WiFi backend based on the backend name
|
||||||
|
func createBackend(backendName string) (backend.WiFiBackend, error) {
|
||||||
|
switch backendName {
|
||||||
|
case "iwd":
|
||||||
|
return iwd.NewIWDBackend(), nil
|
||||||
|
case "wpasupplicant":
|
||||||
|
// TODO: Implement wpa_supplicant backend
|
||||||
|
return nil, fmt.Errorf("wpa_supplicant backend not yet implemented")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid wifi backend: %s (must be 'iwd' or 'wpasupplicant')", backendName)
|
||||||
|
}
|
||||||
|
}
|
||||||
255
internal/wifi/iwd/backend.go
Normal file
255
internal/wifi/iwd/backend.go
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
package iwd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
"github.com/nemunaire/repeater/internal/wifi/backend"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AgentPath = "/com/github/nemunaire/repeater/agent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IWDBackend implements the WiFiBackend interface for iwd (Intel Wireless Daemon)
|
||||||
|
type IWDBackend struct {
|
||||||
|
conn *dbus.Conn
|
||||||
|
manager *Manager
|
||||||
|
station *Station
|
||||||
|
agent *Agent
|
||||||
|
agentManager *AgentManager
|
||||||
|
signalMonitor *SignalMonitor
|
||||||
|
interfaceName string
|
||||||
|
callbacks backend.EventCallbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIWDBackend creates a new IWD backend instance
|
||||||
|
func NewIWDBackend() *IWDBackend {
|
||||||
|
return &IWDBackend{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize initializes the iwd backend with the given interface name
|
||||||
|
func (b *IWDBackend) Initialize(interfaceName string) error {
|
||||||
|
b.interfaceName = interfaceName
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Connect to D-Bus
|
||||||
|
b.conn, err = dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("échec de connexion à D-Bus: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find station for interface
|
||||||
|
b.manager = NewManager(b.conn)
|
||||||
|
b.station, err = b.manager.FindStation(interfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("impossible de trouver la station pour %s: %v", interfaceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and register agent for credential callbacks
|
||||||
|
b.agent = NewAgent(b.conn, dbus.ObjectPath(AgentPath))
|
||||||
|
if err := b.agent.Export(); err != nil {
|
||||||
|
return fmt.Errorf("échec de l'export de l'agent: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.agentManager = NewAgentManager(b.conn)
|
||||||
|
if err := b.agentManager.RegisterAgent(dbus.ObjectPath(AgentPath)); err != nil {
|
||||||
|
b.agent.Unexport()
|
||||||
|
return fmt.Errorf("échec de l'enregistrement de l'agent: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the D-Bus connection and unregisters the agent
|
||||||
|
func (b *IWDBackend) Close() error {
|
||||||
|
if b.agentManager != nil && b.agent != nil {
|
||||||
|
b.agentManager.UnregisterAgent(dbus.ObjectPath(AgentPath))
|
||||||
|
b.agent.Unexport()
|
||||||
|
}
|
||||||
|
if b.conn != nil {
|
||||||
|
b.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanNetworks triggers a network scan
|
||||||
|
func (b *IWDBackend) ScanNetworks() error {
|
||||||
|
err := b.station.Scan()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("erreur lors du scan: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrderedNetworks returns networks sorted by signal strength in backend-agnostic format
|
||||||
|
func (b *IWDBackend) GetOrderedNetworks() ([]backend.BackendNetwork, error) {
|
||||||
|
networkInfos, err := b.station.GetOrderedNetworks()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("erreur lors de la récupération des réseaux: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var networks []backend.BackendNetwork
|
||||||
|
seenSSIDs := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, netInfo := range networkInfos {
|
||||||
|
network := NewNetwork(b.conn, netInfo.Path)
|
||||||
|
props, err := network.GetProperties()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if props.Name == "" || seenSSIDs[props.Name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenSSIDs[props.Name] = true
|
||||||
|
|
||||||
|
// Convert iwd network to backend-agnostic format
|
||||||
|
backendNet := backend.BackendNetwork{
|
||||||
|
SSID: props.Name,
|
||||||
|
SignalDBm: netInfo.Signal / 100, // iwd provides 100*dBm, convert to dBm
|
||||||
|
SecurityType: props.Type,
|
||||||
|
BSSID: generateSyntheticBSSID(props.Name), // iwd doesn't expose BSSID
|
||||||
|
Frequency: 0, // iwd doesn't expose frequency in GetOrderedNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
networks = append(networks, backendNet)
|
||||||
|
}
|
||||||
|
|
||||||
|
return networks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsScanning checks if a scan is currently in progress
|
||||||
|
func (b *IWDBackend) IsScanning() (bool, error) {
|
||||||
|
return b.station.IsScanning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect connects to a WiFi network
|
||||||
|
func (b *IWDBackend) Connect(ssid, password string) error {
|
||||||
|
// Store passphrase in agent for callback
|
||||||
|
if password != "" {
|
||||||
|
b.agent.SetPassphrase(ssid, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure passphrase is cleared after connection attempt
|
||||||
|
defer func() {
|
||||||
|
if password != "" {
|
||||||
|
b.agent.ClearPassphrase(ssid)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Get network object
|
||||||
|
network, err := b.station.GetNetwork(ssid)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("réseau '%s' non trouvé: %v", ssid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect - iwd will call agent.RequestPassphrase() if needed
|
||||||
|
if err := network.Connect(); err != nil {
|
||||||
|
return fmt.Errorf("erreur lors de la connexion: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect disconnects from the current WiFi network
|
||||||
|
func (b *IWDBackend) Disconnect() error {
|
||||||
|
if err := b.station.Disconnect(); err != nil {
|
||||||
|
return fmt.Errorf("erreur lors de la déconnexion: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnectionState returns the current WiFi connection state
|
||||||
|
func (b *IWDBackend) GetConnectionState() (backend.ConnectionState, error) {
|
||||||
|
state, err := b.station.GetState()
|
||||||
|
if err != nil {
|
||||||
|
return backend.StateDisconnected, err
|
||||||
|
}
|
||||||
|
return mapIWDState(state), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnectedSSID returns the SSID of the currently connected network
|
||||||
|
func (b *IWDBackend) GetConnectedSSID() string {
|
||||||
|
network, err := b.station.GetConnectedNetwork()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
props, err := network.GetProperties()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartEventMonitoring starts monitoring WiFi events
|
||||||
|
func (b *IWDBackend) StartEventMonitoring(callbacks backend.EventCallbacks) error {
|
||||||
|
b.callbacks = callbacks
|
||||||
|
|
||||||
|
// Create signal monitor
|
||||||
|
b.signalMonitor = NewSignalMonitor(b.conn, b.station)
|
||||||
|
|
||||||
|
// Register callbacks - wrap to convert iwd types to backend types
|
||||||
|
b.signalMonitor.OnStateChange(func(state StationState, ssid string) {
|
||||||
|
if b.callbacks.OnStateChange != nil {
|
||||||
|
b.callbacks.OnStateChange(mapIWDState(state), ssid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.signalMonitor.OnScanComplete(func() {
|
||||||
|
if b.callbacks.OnScanComplete != nil {
|
||||||
|
b.callbacks.OnScanComplete()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start monitoring
|
||||||
|
return b.signalMonitor.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopEventMonitoring stops monitoring WiFi events
|
||||||
|
func (b *IWDBackend) StopEventMonitoring() {
|
||||||
|
if b.signalMonitor != nil {
|
||||||
|
b.signalMonitor.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapIWDState maps iwd-specific states to backend-agnostic states
|
||||||
|
func mapIWDState(state StationState) backend.ConnectionState {
|
||||||
|
switch state {
|
||||||
|
case StateConnected:
|
||||||
|
return backend.StateConnected
|
||||||
|
case StateConnecting:
|
||||||
|
return backend.StateConnecting
|
||||||
|
case StateDisconnecting:
|
||||||
|
return backend.StateDisconnecting
|
||||||
|
case StateDisconnected:
|
||||||
|
return backend.StateDisconnected
|
||||||
|
case StateRoaming:
|
||||||
|
// Map roaming to connected since we're still connected during roaming
|
||||||
|
return backend.StateConnected
|
||||||
|
default:
|
||||||
|
return backend.StateDisconnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSyntheticBSSID generates a consistent fake BSSID from SSID
|
||||||
|
// (iwd doesn't expose real BSSID)
|
||||||
|
func generateSyntheticBSSID(ssid string) string {
|
||||||
|
// Use a simple hash approach - consistent per SSID
|
||||||
|
hash := 0
|
||||||
|
for _, c := range ssid {
|
||||||
|
hash = ((hash << 5) - hash) + int(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate 6 bytes for MAC address
|
||||||
|
b1 := byte((hash >> 0) & 0xff)
|
||||||
|
b2 := byte((hash >> 8) & 0xff)
|
||||||
|
b3 := byte((hash >> 16) & 0xff)
|
||||||
|
b4 := byte((hash >> 24) & 0xff)
|
||||||
|
b5 := byte(len(ssid) & 0xff)
|
||||||
|
b6 := byte((len(ssid) >> 8) & 0xff)
|
||||||
|
|
||||||
|
return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", b1, b2, b3, b4, b5, b6)
|
||||||
|
}
|
||||||
|
|
@ -6,102 +6,54 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/nemunaire/repeater/internal/models"
|
"github.com/nemunaire/repeater/internal/models"
|
||||||
"github.com/nemunaire/repeater/internal/wifi/iwd"
|
"github.com/nemunaire/repeater/internal/wifi/backend"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
AGENT_PATH = "/com/github/nemunaire/repeater/agent"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wlanInterface string
|
wifiBackend backend.WiFiBackend
|
||||||
dbusConn *dbus.Conn
|
|
||||||
iwdManager *iwd.Manager
|
|
||||||
station *iwd.Station
|
|
||||||
agent *iwd.Agent
|
|
||||||
agentManager *iwd.AgentManager
|
|
||||||
eventMonitor *iwd.SignalMonitor
|
|
||||||
wifiBroadcaster *WifiBroadcaster
|
wifiBroadcaster *WifiBroadcaster
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize initializes the WiFi service with iwd D-Bus connection
|
// Initialize initializes the WiFi service with the specified backend
|
||||||
func Initialize(interfaceName string) error {
|
func Initialize(interfaceName string, backendName string) error {
|
||||||
wlanInterface = interfaceName
|
// Create the appropriate backend using the factory
|
||||||
var err error
|
var err error
|
||||||
|
wifiBackend, err = createBackend(backendName)
|
||||||
// Connect to D-Bus
|
|
||||||
dbusConn, err = dbus.SystemBus()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("échec de connexion à D-Bus: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find station for interface
|
// Initialize the backend
|
||||||
iwdManager = iwd.NewManager(dbusConn)
|
return wifiBackend.Initialize(interfaceName)
|
||||||
station, err = iwdManager.FindStation(interfaceName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("impossible de trouver la station pour %s: %v", interfaceName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and register agent for credential callbacks
|
|
||||||
agent = iwd.NewAgent(dbusConn, dbus.ObjectPath(AGENT_PATH))
|
|
||||||
if err := agent.Export(); err != nil {
|
|
||||||
return fmt.Errorf("échec de l'export de l'agent: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
agentManager = iwd.NewAgentManager(dbusConn)
|
|
||||||
if err := agentManager.RegisterAgent(dbus.ObjectPath(AGENT_PATH)); err != nil {
|
|
||||||
agent.Unexport()
|
|
||||||
return fmt.Errorf("échec de l'enregistrement de l'agent: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the D-Bus connection and unregisters the agent
|
// Close closes the backend connection
|
||||||
func Close() {
|
func Close() {
|
||||||
if agentManager != nil && agent != nil {
|
if wifiBackend != nil {
|
||||||
agentManager.UnregisterAgent(dbus.ObjectPath(AGENT_PATH))
|
wifiBackend.Close()
|
||||||
agent.Unexport()
|
|
||||||
}
|
|
||||||
if dbusConn != nil {
|
|
||||||
dbusConn.Close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCachedNetworks returns previously discovered networks without triggering a scan
|
// GetCachedNetworks returns previously discovered networks without triggering a scan
|
||||||
func GetCachedNetworks() ([]models.WiFiNetwork, error) {
|
func GetCachedNetworks() ([]models.WiFiNetwork, error) {
|
||||||
// Get ordered networks without scanning
|
// Get ordered networks from backend
|
||||||
networkInfos, err := station.GetOrderedNetworks()
|
backendNetworks, err := wifiBackend.GetOrderedNetworks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("erreur lors de la récupération des réseaux: %v", err)
|
return nil, fmt.Errorf("erreur lors de la récupération des réseaux: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var networks []models.WiFiNetwork
|
// Convert backend networks to models
|
||||||
seenSSIDs := make(map[string]bool)
|
networks := make([]models.WiFiNetwork, 0, len(backendNetworks))
|
||||||
|
for _, backendNet := range backendNetworks {
|
||||||
for _, netInfo := range networkInfos {
|
|
||||||
network := iwd.NewNetwork(dbusConn, netInfo.Path)
|
|
||||||
props, err := network.GetProperties()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if props.Name == "" || seenSSIDs[props.Name] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seenSSIDs[props.Name] = true
|
|
||||||
|
|
||||||
wifiNet := models.WiFiNetwork{
|
wifiNet := models.WiFiNetwork{
|
||||||
SSID: props.Name,
|
SSID: backendNet.SSID,
|
||||||
Signal: signalToStrength(int(netInfo.Signal) / 100),
|
Signal: signalToStrength(int(backendNet.SignalDBm)),
|
||||||
Security: mapSecurityType(props.Type),
|
Security: mapSecurityType(backendNet.SecurityType),
|
||||||
BSSID: generateSyntheticBSSID(props.Name),
|
BSSID: backendNet.BSSID,
|
||||||
Channel: 0,
|
Channel: 0, // Not yet exposed by backends
|
||||||
}
|
}
|
||||||
|
|
||||||
networks = append(networks, wifiNet)
|
networks = append(networks, wifiNet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,47 +68,34 @@ func GetCachedNetworks() ([]models.WiFiNetwork, error) {
|
||||||
// ScanNetworks scans for available WiFi networks
|
// ScanNetworks scans for available WiFi networks
|
||||||
func ScanNetworks() ([]models.WiFiNetwork, error) {
|
func ScanNetworks() ([]models.WiFiNetwork, error) {
|
||||||
// Check if already scanning
|
// Check if already scanning
|
||||||
scanning, err := station.IsScanning()
|
scanning, err := wifiBackend.IsScanning()
|
||||||
if err == nil && scanning {
|
if err == nil && scanning {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
} else {
|
} else {
|
||||||
// Trigger scan
|
// Trigger scan
|
||||||
err := station.Scan()
|
err := wifiBackend.ScanNetworks()
|
||||||
if err != nil && !strings.Contains(err.Error(), "rejected") {
|
if err != nil && !strings.Contains(err.Error(), "rejected") {
|
||||||
return nil, fmt.Errorf("erreur lors du scan: %v", err)
|
return nil, fmt.Errorf("erreur lors du scan: %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ordered networks
|
// Get ordered networks from backend
|
||||||
networkInfos, err := station.GetOrderedNetworks()
|
backendNetworks, err := wifiBackend.GetOrderedNetworks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("erreur lors de la récupération des réseaux: %v", err)
|
return nil, fmt.Errorf("erreur lors de la récupération des réseaux: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var networks []models.WiFiNetwork
|
// Convert backend networks to models
|
||||||
seenSSIDs := make(map[string]bool)
|
networks := make([]models.WiFiNetwork, 0, len(backendNetworks))
|
||||||
|
for _, backendNet := range backendNetworks {
|
||||||
for _, netInfo := range networkInfos {
|
|
||||||
network := iwd.NewNetwork(dbusConn, netInfo.Path)
|
|
||||||
props, err := network.GetProperties()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if props.Name == "" || seenSSIDs[props.Name] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seenSSIDs[props.Name] = true
|
|
||||||
|
|
||||||
wifiNet := models.WiFiNetwork{
|
wifiNet := models.WiFiNetwork{
|
||||||
SSID: props.Name,
|
SSID: backendNet.SSID,
|
||||||
Signal: signalToStrength(int(netInfo.Signal) / 100),
|
Signal: signalToStrength(int(backendNet.SignalDBm)),
|
||||||
Security: mapSecurityType(props.Type),
|
Security: mapSecurityType(backendNet.SecurityType),
|
||||||
BSSID: generateSyntheticBSSID(props.Name),
|
BSSID: backendNet.BSSID,
|
||||||
Channel: 0,
|
Channel: 0, // Not yet exposed by backends
|
||||||
}
|
}
|
||||||
|
|
||||||
networks = append(networks, wifiNet)
|
networks = append(networks, wifiNet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,29 +112,11 @@ func ScanNetworks() ([]models.WiFiNetwork, error) {
|
||||||
return networks, nil
|
return networks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect connects to a WiFi network using iwd agent callback
|
// Connect connects to a WiFi network
|
||||||
func Connect(ssid, password string) error {
|
func Connect(ssid, password string) error {
|
||||||
// Store passphrase in agent for callback
|
// Use backend to connect
|
||||||
if password != "" {
|
if err := wifiBackend.Connect(ssid, password); err != nil {
|
||||||
agent.SetPassphrase(ssid, password)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure passphrase is cleared after connection attempt
|
|
||||||
defer func() {
|
|
||||||
if password != "" {
|
|
||||||
agent.ClearPassphrase(ssid)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Get network object
|
|
||||||
network, err := station.GetNetwork(ssid)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("réseau '%s' non trouvé: %v", ssid, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect - iwd will call agent.RequestPassphrase() if needed
|
|
||||||
if err := network.Connect(); err != nil {
|
|
||||||
return fmt.Errorf("erreur lors de la connexion: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll for connection
|
// Poll for connection
|
||||||
|
|
@ -211,65 +132,51 @@ func Connect(ssid, password string) error {
|
||||||
|
|
||||||
// Disconnect disconnects from the current WiFi network
|
// Disconnect disconnects from the current WiFi network
|
||||||
func Disconnect() error {
|
func Disconnect() error {
|
||||||
if err := station.Disconnect(); err != nil {
|
return wifiBackend.Disconnect()
|
||||||
return fmt.Errorf("erreur lors de la déconnexion: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsConnected checks if WiFi is connected using iwd
|
// IsConnected checks if WiFi is connected
|
||||||
func IsConnected() bool {
|
func IsConnected() bool {
|
||||||
state, err := station.GetState()
|
state, err := wifiBackend.GetConnectionState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return state == iwd.StateConnected
|
return state == backend.StateConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConnectedSSID returns the SSID of the currently connected network
|
// GetConnectedSSID returns the SSID of the currently connected network
|
||||||
func GetConnectedSSID() string {
|
func GetConnectedSSID() string {
|
||||||
network, err := station.GetConnectedNetwork()
|
return wifiBackend.GetConnectedSSID()
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
props, err := network.GetProperties()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return props.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConnectionState returns the current WiFi connection state
|
// GetConnectionState returns the current WiFi connection state
|
||||||
func GetConnectionState() string {
|
func GetConnectionState() string {
|
||||||
state, err := station.GetState()
|
state, err := wifiBackend.GetConnectionState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return string(iwd.StateDisconnected)
|
return string(backend.StateDisconnected)
|
||||||
}
|
}
|
||||||
return string(state)
|
return string(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartEventMonitoring initializes D-Bus signal monitoring and WebSocket broadcasting
|
// StartEventMonitoring initializes signal monitoring and WebSocket broadcasting
|
||||||
func StartEventMonitoring() error {
|
func StartEventMonitoring() error {
|
||||||
// Initialize broadcaster
|
// Initialize broadcaster
|
||||||
wifiBroadcaster = NewWifiBroadcaster()
|
wifiBroadcaster = NewWifiBroadcaster()
|
||||||
|
|
||||||
// Create signal monitor
|
// Set up callbacks
|
||||||
eventMonitor = iwd.NewSignalMonitor(dbusConn, station)
|
callbacks := backend.EventCallbacks{
|
||||||
|
OnStateChange: handleStateChange,
|
||||||
|
OnScanComplete: handleScanComplete,
|
||||||
|
}
|
||||||
|
|
||||||
// Register callbacks
|
// Start backend monitoring
|
||||||
eventMonitor.OnStateChange(handleStateChange)
|
return wifiBackend.StartEventMonitoring(callbacks)
|
||||||
eventMonitor.OnScanComplete(handleScanComplete)
|
|
||||||
|
|
||||||
// Start monitoring
|
|
||||||
return eventMonitor.Start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopEventMonitoring stops D-Bus signal monitoring
|
// StopEventMonitoring stops signal monitoring
|
||||||
func StopEventMonitoring() {
|
func StopEventMonitoring() {
|
||||||
if eventMonitor != nil {
|
if wifiBackend != nil {
|
||||||
eventMonitor.Stop()
|
wifiBackend.StopEventMonitoring()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,7 +195,7 @@ func UnregisterWebSocketClient(conn *websocket.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleStateChange is called when WiFi connection state changes
|
// handleStateChange is called when WiFi connection state changes
|
||||||
func handleStateChange(newState iwd.StationState, connectedSSID string) {
|
func handleStateChange(newState backend.ConnectionState, connectedSSID string) {
|
||||||
if wifiBroadcaster != nil {
|
if wifiBroadcaster != nil {
|
||||||
wifiBroadcaster.BroadcastStateChange(string(newState), connectedSSID)
|
wifiBroadcaster.BroadcastStateChange(string(newState), connectedSSID)
|
||||||
}
|
}
|
||||||
|
|
@ -303,9 +210,9 @@ func handleScanComplete() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapSecurityType maps iwd security types to display format
|
// mapSecurityType maps backend security types to display format
|
||||||
func mapSecurityType(iwdType string) string {
|
func mapSecurityType(securityType string) string {
|
||||||
switch iwdType {
|
switch securityType {
|
||||||
case "open":
|
case "open":
|
||||||
return "Open"
|
return "Open"
|
||||||
case "wep":
|
case "wep":
|
||||||
|
|
@ -319,25 +226,6 @@ func mapSecurityType(iwdType string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateSyntheticBSSID generates a consistent fake BSSID from SSID
|
|
||||||
func generateSyntheticBSSID(ssid string) string {
|
|
||||||
// Use a simple hash approach - consistent per SSID
|
|
||||||
hash := 0
|
|
||||||
for _, c := range ssid {
|
|
||||||
hash = ((hash << 5) - hash) + int(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate 6 bytes for MAC address
|
|
||||||
b1 := byte((hash >> 0) & 0xff)
|
|
||||||
b2 := byte((hash >> 8) & 0xff)
|
|
||||||
b3 := byte((hash >> 16) & 0xff)
|
|
||||||
b4 := byte((hash >> 24) & 0xff)
|
|
||||||
b5 := byte(len(ssid) & 0xff)
|
|
||||||
b6 := byte((len(ssid) >> 8) & 0xff)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", b1, b2, b3, b4, b5, b6)
|
|
||||||
}
|
|
||||||
|
|
||||||
// signalToStrength converts signal level (dBm) to strength (1-5)
|
// signalToStrength converts signal level (dBm) to strength (1-5)
|
||||||
func signalToStrength(level int) int {
|
func signalToStrength(level int) int {
|
||||||
if level >= -30 {
|
if level >= -30 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue