station: Default device type to "unknown" and propagate DHCP hostname
GuessDeviceType silently returned "mobile" for any device whose hostname or MAC OUI didn't match a known pattern, so the UI labelled every unidentified device as a phone. Default to "unknown" instead and broaden hostname matching (pixel/galaxy, thinkpad, imac/-pc, QEMU OUI). The hostapd backend was also dropping DHCP hostnames on the floor: the correlator only forwarded MAC->IP, and convertStation hard-coded the hostname to "". Replace UpdateIPMapping with UpdateLeaseInfo that carries both maps so hostnames flow through to ConnectedDevice.Name. Frontend gains a "Sans nom" fallback when no hostname is available and French labels for the device-type badge.
This commit is contained in:
parent
0797f7dd50
commit
a758c331c0
4 changed files with 68 additions and 35 deletions
|
|
@ -401,13 +401,17 @@ function displayDevices(devices) {
|
|||
const deviceCard = document.createElement('div');
|
||||
deviceCard.className = 'device-card';
|
||||
|
||||
const displayName = device.name && device.name.trim() !== ''
|
||||
? device.name
|
||||
: 'Sans nom';
|
||||
|
||||
deviceCard.innerHTML = `
|
||||
${getDeviceIcon(device.type)}
|
||||
<div class="device-name">${escapeHtml(device.name)}</div>
|
||||
<div class="device-type">${device.type}</div>
|
||||
<div class="device-name">${escapeHtml(displayName)}</div>
|
||||
<div class="device-type">${escapeHtml(formatDeviceType(device.type))}</div>
|
||||
<div class="device-info">
|
||||
<div>${device.ip}</div>
|
||||
<div style="font-size: 0.75rem;">${device.mac}</div>
|
||||
<div>${escapeHtml(device.ip || '—')}</div>
|
||||
<div style="font-size: 0.75rem;">${escapeHtml(device.mac)}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -415,6 +419,17 @@ function displayDevices(devices) {
|
|||
});
|
||||
}
|
||||
|
||||
function formatDeviceType(type) {
|
||||
const labels = {
|
||||
mobile: 'Mobile',
|
||||
tablet: 'Tablette',
|
||||
laptop: 'Portable',
|
||||
desktop: 'Ordinateur',
|
||||
unknown: 'Inconnu'
|
||||
};
|
||||
return labels[type] || labels.unknown;
|
||||
}
|
||||
|
||||
function addLogEntry(log) {
|
||||
const logContainer = document.getElementById('logContainer');
|
||||
|
||||
|
|
|
|||
|
|
@ -87,19 +87,26 @@ type BackendConfig struct {
|
|||
HostapdInterface string // Hostapd interface name for DBus
|
||||
}
|
||||
|
||||
// GuessDeviceType attempts to guess device type from hostname and MAC address
|
||||
// GuessDeviceType attempts to guess device type from hostname and MAC address.
|
||||
// Returns "unknown" when no signal is strong enough — the frontend renders that
|
||||
// as a neutral icon rather than mislabeling unknown devices as "mobile".
|
||||
func GuessDeviceType(hostname, mac string) string {
|
||||
hostname = strings.ToLower(hostname)
|
||||
|
||||
if strings.Contains(hostname, "iphone") || strings.Contains(hostname, "android") {
|
||||
if strings.Contains(hostname, "iphone") || strings.Contains(hostname, "android") ||
|
||||
strings.Contains(hostname, "pixel") || strings.Contains(hostname, "galaxy") {
|
||||
return "mobile"
|
||||
} else if strings.Contains(hostname, "ipad") || strings.Contains(hostname, "tablet") {
|
||||
return "tablet"
|
||||
} else if strings.Contains(hostname, "macbook") || strings.Contains(hostname, "laptop") {
|
||||
} else if strings.Contains(hostname, "macbook") || strings.Contains(hostname, "laptop") ||
|
||||
strings.Contains(hostname, "thinkpad") {
|
||||
return "laptop"
|
||||
} else if strings.Contains(hostname, "imac") || strings.Contains(hostname, "desktop") ||
|
||||
strings.Contains(hostname, "-pc") {
|
||||
return "desktop"
|
||||
}
|
||||
|
||||
// Guess by MAC prefix (OUI)
|
||||
// Guess by MAC prefix (OUI) — VM hypervisor MACs are almost always laptops
|
||||
if len(mac) >= 8 {
|
||||
macPrefix := strings.ToUpper(mac[:8])
|
||||
switch macPrefix {
|
||||
|
|
@ -107,8 +114,8 @@ func GuessDeviceType(hostname, mac string) string {
|
|||
return "laptop"
|
||||
case "08:00:27": // VirtualBox
|
||||
return "laptop"
|
||||
default:
|
||||
return "mobile"
|
||||
case "52:54:00": // QEMU/KVM
|
||||
return "laptop"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,17 +27,19 @@ type Backend struct {
|
|||
stopCh chan struct{}
|
||||
stopOnce sync.Once
|
||||
|
||||
// IP correlation - will be populated by periodic DHCP lease correlation
|
||||
ipByMAC map[string]string // MAC -> IP mapping
|
||||
// IP / hostname correlation - populated by periodic DHCP lease correlation
|
||||
ipByMAC map[string]string // MAC -> IP mapping
|
||||
hostnameByMAC map[string]string // MAC -> hostname mapping
|
||||
}
|
||||
|
||||
// NewBackend creates a new hostapd backend
|
||||
func NewBackend() *Backend {
|
||||
return &Backend{
|
||||
stations: make(map[string]*HostapdStation),
|
||||
ipByMAC: make(map[string]string),
|
||||
hostapdCLI: "hostapd_cli",
|
||||
stopCh: make(chan struct{}),
|
||||
stations: make(map[string]*HostapdStation),
|
||||
ipByMAC: make(map[string]string),
|
||||
hostnameByMAC: make(map[string]string),
|
||||
hostapdCLI: "hostapd_cli",
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,12 +300,8 @@ func (b *Backend) parseAllStaOutput(output string) map[string]*HostapdStation {
|
|||
|
||||
// convertStation converts HostapdStation to backend.Station
|
||||
func (b *Backend) convertStation(mac string, hs *HostapdStation) backend.Station {
|
||||
// Get IP address if available from correlation
|
||||
ip := b.ipByMAC[mac]
|
||||
|
||||
// Attempt hostname resolution if we have an IP
|
||||
hostname := ""
|
||||
// TODO: Could do reverse DNS lookup here if needed
|
||||
hostname := b.hostnameByMAC[mac]
|
||||
|
||||
return backend.Station{
|
||||
MAC: mac,
|
||||
|
|
@ -317,27 +315,34 @@ func (b *Backend) convertStation(mac string, hs *HostapdStation) backend.Station
|
|||
}
|
||||
}
|
||||
|
||||
// UpdateIPMapping updates the MAC -> IP mapping from external source (e.g., DHCP)
|
||||
// This should be called periodically to correlate hostapd stations with IP addresses
|
||||
func (b *Backend) UpdateIPMapping(macToIP map[string]string) {
|
||||
// UpdateLeaseInfo updates MAC -> IP and MAC -> hostname mappings from an
|
||||
// external source (e.g., DHCP lease file). Called periodically by the
|
||||
// correlator. An empty hostname leaves the existing one in place — DHCP
|
||||
// leases sometimes drop the hostname between renewals.
|
||||
func (b *Backend) UpdateLeaseInfo(macToIP, macToHostname map[string]string) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
// Track which stations got IP updates
|
||||
updated := make(map[string]bool)
|
||||
|
||||
for mac, ip := range macToIP {
|
||||
if oldIP, exists := b.ipByMAC[mac]; exists && oldIP != ip {
|
||||
// IP changed
|
||||
updated[mac] = true
|
||||
} else if !exists {
|
||||
// New IP mapping
|
||||
if oldIP, exists := b.ipByMAC[mac]; !exists || oldIP != ip {
|
||||
updated[mac] = true
|
||||
}
|
||||
b.ipByMAC[mac] = ip
|
||||
}
|
||||
|
||||
// Trigger update callbacks for stations that got new/changed IPs
|
||||
for mac, hostname := range macToHostname {
|
||||
if hostname == "" {
|
||||
continue
|
||||
}
|
||||
if oldName, exists := b.hostnameByMAC[mac]; !exists || oldName != hostname {
|
||||
updated[mac] = true
|
||||
}
|
||||
b.hostnameByMAC[mac] = hostname
|
||||
}
|
||||
|
||||
// Trigger update callbacks for stations whose info changed
|
||||
for mac := range updated {
|
||||
if station, exists := b.stations[mac]; exists {
|
||||
if cb := b.callbacks.OnStationUpdated; cb != nil {
|
||||
|
|
|
|||
|
|
@ -81,14 +81,20 @@ func (dc *DHCPCorrelator) correlate() {
|
|||
return
|
||||
}
|
||||
|
||||
// Build MAC -> IP mapping
|
||||
// Build MAC -> IP and MAC -> hostname mappings. Hostnames live in the
|
||||
// dhcpd "client-hostname" field and let us label devices instead of
|
||||
// falling back to bare MAC addresses.
|
||||
macToIP := make(map[string]string)
|
||||
macToHostname := make(map[string]string)
|
||||
for _, lease := range leases {
|
||||
macToIP[lease.MAC] = lease.IP
|
||||
mac := strings.ToLower(lease.MAC)
|
||||
macToIP[mac] = lease.IP
|
||||
if lease.Hostname != "" {
|
||||
macToHostname[mac] = lease.Hostname
|
||||
}
|
||||
}
|
||||
|
||||
// Update backend with IP mappings
|
||||
dc.backend.UpdateIPMapping(macToIP)
|
||||
dc.backend.UpdateLeaseInfo(macToIP, macToHostname)
|
||||
}
|
||||
|
||||
// parseDHCPLeases reads and parses DHCP lease file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue