Use DHCP leases for LAN client wake support
This commit is contained in:
+117
-4
@@ -2732,17 +2732,54 @@ function lan_link_speed_label(string $iface = 'eth1'): string
|
||||
return $mbps . ' Mbps';
|
||||
}
|
||||
|
||||
function lan_client_rows(array $leases, array $aliases, string $iface = 'eth1'): array
|
||||
function observed_lan_macs(array $leases): array
|
||||
{
|
||||
if ($leases === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = db()->query("
|
||||
SELECT mac
|
||||
FROM wifi_observed_sessions
|
||||
WHERE band = 'LAN'
|
||||
");
|
||||
} catch (Throwable) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$macs = [];
|
||||
foreach ($stmt->fetchAll() as $row) {
|
||||
$mac = strtolower((string)($row['mac'] ?? ''));
|
||||
if (wifi_valid_mac($mac) && isset($leases[$mac])) {
|
||||
$macs[$mac] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($macs);
|
||||
}
|
||||
|
||||
function lan_client_rows(array $leases, array $aliases, array $wifiMacs = [], string $iface = 'eth1'): array
|
||||
{
|
||||
$rows = [];
|
||||
$linkSpeed = lan_link_speed_label($iface);
|
||||
$lanFdbList = lan_fdb_macs($iface);
|
||||
$lanFdbMacs = array_fill_keys($lanFdbList, true);
|
||||
$wifiMacs = array_fill_keys(array_map('strtolower', $wifiMacs), true);
|
||||
$lanMacs = array_values(array_unique(array_merge($lanFdbList, observed_lan_macs($leases))));
|
||||
|
||||
foreach ($lanMacs as $mac) {
|
||||
$mac = strtolower((string)$mac);
|
||||
if (!wifi_valid_mac($mac) || isset($wifiMacs[$mac])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (lan_fdb_macs($iface) as $mac) {
|
||||
$lease = $leases[$mac] ?? [];
|
||||
$leaseHostname = (string)($lease['hostname'] ?? 'N/A');
|
||||
$aliasHostname = $aliases[$mac] ?? null;
|
||||
$hostname = $aliasHostname ?: $leaseHostname;
|
||||
$hostnameSource = $aliasHostname ? 'alias' : ($leaseHostname === 'N/A' ? 'generated' : 'lease');
|
||||
$isFdbVisible = isset($lanFdbMacs[$mac]);
|
||||
|
||||
$rows[] = [
|
||||
'band' => 'LAN',
|
||||
@@ -2758,8 +2795,10 @@ function lan_client_rows(array $leases, array $aliases, string $iface = 'eth1'):
|
||||
'signal_source' => 'lan',
|
||||
'tx_bitrate_source' => 'lan_link',
|
||||
'rx_bitrate_source' => 'lan',
|
||||
'connected_time' => 'N/A',
|
||||
'connected_time' => $isFdbVisible ? 'N/A' : '-',
|
||||
'inactive_time' => '-',
|
||||
'presence' => $isFdbVisible ? 'fdb' : 'lease',
|
||||
'wol_available' => true,
|
||||
'rx_bytes' => 0,
|
||||
'tx_bytes' => 0,
|
||||
'rx_packets' => 0,
|
||||
@@ -2771,6 +2810,53 @@ function lan_client_rows(array $leases, array $aliases, string $iface = 'eth1'):
|
||||
return $rows;
|
||||
}
|
||||
|
||||
function send_wake_on_lan(string $mac, string $broadcast = '192.168.50.255', int $port = 9): array
|
||||
{
|
||||
$mac = strtolower(trim($mac));
|
||||
if (!wifi_valid_mac($mac)) {
|
||||
throw new InvalidArgumentException('bad_mac');
|
||||
}
|
||||
|
||||
$leases = dnsmasq_leases();
|
||||
if (!isset($leases[$mac])) {
|
||||
throw new InvalidArgumentException('unknown_dhcp_client');
|
||||
}
|
||||
|
||||
$lanMacs = array_fill_keys(array_merge(lan_fdb_macs(), observed_lan_macs($leases)), true);
|
||||
if (!isset($lanMacs[$mac])) {
|
||||
throw new InvalidArgumentException('not_lan_client');
|
||||
}
|
||||
|
||||
$macBytes = hex2bin(str_replace(':', '', $mac));
|
||||
if ($macBytes === false || strlen($macBytes) !== 6) {
|
||||
throw new InvalidArgumentException('bad_mac');
|
||||
}
|
||||
|
||||
$packet = str_repeat("\xFF", 6) . str_repeat($macBytes, 16);
|
||||
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
if ($socket === false) {
|
||||
throw new RuntimeException('socket_create_failed');
|
||||
}
|
||||
|
||||
try {
|
||||
socket_set_option($socket, SOL_SOCKET, SO_BROADCAST, 1);
|
||||
$sent = socket_sendto($socket, $packet, strlen($packet), 0, $broadcast, $port);
|
||||
if ($sent === false || $sent !== strlen($packet)) {
|
||||
throw new RuntimeException('wake_packet_failed');
|
||||
}
|
||||
} finally {
|
||||
socket_close($socket);
|
||||
}
|
||||
|
||||
return [
|
||||
'mac' => $mac,
|
||||
'ip' => $leases[$mac]['ip'] ?? null,
|
||||
'hostname' => $leases[$mac]['hostname'] ?? null,
|
||||
'broadcast' => $broadcast,
|
||||
'port' => $port,
|
||||
];
|
||||
}
|
||||
|
||||
function wifi_time_ms(string $value): int
|
||||
{
|
||||
if (preg_match('/(\d+)/', $value, $m)) {
|
||||
@@ -3155,7 +3241,14 @@ function wifi_data(): array
|
||||
parse_live_wifi_rows($iface, $band, iw_station_dump($iface), $leases, $aliases)
|
||||
);
|
||||
}
|
||||
$clients = array_merge($clients, lan_client_rows($leases, $aliases));
|
||||
$wifiMacs = array_values(array_unique(array_filter(
|
||||
array_map(
|
||||
static fn(array $client): string => strtolower((string)($client['mac'] ?? '')),
|
||||
$clients
|
||||
),
|
||||
static fn(string $mac): bool => wifi_valid_mac($mac)
|
||||
)));
|
||||
$clients = array_merge($clients, lan_client_rows($leases, $aliases, $wifiMacs));
|
||||
|
||||
$clients = dedupe_wifi_clients($clients);
|
||||
$clients = apply_wifi_estimates($clients);
|
||||
@@ -3721,6 +3814,26 @@ function control_api_dispatch(): void
|
||||
]);
|
||||
}
|
||||
|
||||
if ($action === 'wake_lan') {
|
||||
$wake = send_wake_on_lan((string)($_POST['mac'] ?? ''));
|
||||
|
||||
add_fan_action(
|
||||
'wake_lan',
|
||||
null,
|
||||
null,
|
||||
'WOL packet sent to ' . $wake['mac'] . ' ' . ($wake['hostname'] ?? ''),
|
||||
true
|
||||
);
|
||||
|
||||
json_out([
|
||||
'ok' => true,
|
||||
'data' => [
|
||||
'wake' => $wake,
|
||||
'status' => collect_snapshot(false),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($action === 'collect') {
|
||||
json_out([
|
||||
'ok' => true,
|
||||
|
||||
Reference in New Issue
Block a user