diff --git a/config/defaults.json b/config/defaults.json index c9770cc14..56f1532cf 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -25,6 +25,77 @@ "vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}" ] }, + "ax": { + "settings": [ + "# Basic settings", + "hw_mode=a", + "# Enable 802.11n/ac", + "ieee80211d=1", + "ieee80211n=1", + "ieee80211ac=1", + "# Enable 802.11ax", + "ieee80211ax=1", + "# High efficiency capabilities", + "he_su_beamformer=1", + "he_su_beamformee=1", + "he_mu_beamformer=1", + "# BSS color for spatial reuse, value 1-63", + "he_bss_color=1", + "he_oper_chwidth=1", + "ht_capab=[HT40+][SHORT-GI-20][SHORT-GI-40]", + "vht_capab=[SHORT-GI-80]", + "he_oper_centr_freq_seg0_idx=155", + "vht_oper_centr_freq_seg0_idx=155", + "wmm_enabled=1" + ] + }, + "be": { + "settings": [ + "# Basic settings", + "hw_mode=a", + "# Enable 802.11n/ac/ax", + "ieee80211n=1", + "ieee80211ac=1", + "ieee80211ax=1", + "vht_oper_chwidth=2 # 160 MHz for VHT", + "vht_oper_centr_freq_seg0_idx=50", + "vht_capab=[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][RX-STBC-1][SU-BEAMFORMER][SU-BEAMFORMEE][MU-BEAMFORMER][MU-BEAMFORMEE]", + "he_su_beamformer=1", + "he_su_beamformee=1", + "he_mu_beamformer=1", + "he_bss_color=1", + "he_oper_chwidth=2 # 160 MHz for HE", + "he_oper_centr_freq_seg0_idx=50", + "# Enable 802.11be", + "ieee80211be=1", + "# EHT configuration", + "eht_su_beamformer=1", + "eht_su_beamformee=1", + "eht_mu_beamformer=1", + "eht_oper_chwidth=2", + "# EHT operation parameters", + "eht_oper_chwidth=2 # 160 MHz, 0=20, 1=40, 2=80, 3=160, 4=320", + "eht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}", + "# WMM configuration", + "wmm_enabled=1", + "wmm_ac_bk_cwmin=4", + "wmm_ac_bk_cwmax=10", + "wmm_ac_bk_aifs=7", + "wmm_ac_bk_txop_limit=0", + "wmm_ac_be_aifs=3", + "wmm_ac_be_cwmin=4", + "wmm_ac_be_cwmax=10", + "wmm_ac_be_txop_limit=0", + "wmm_ac_vi_aifs=2", + "wmm_ac_vi_cwmin=3", + "wmm_ac_vi_cwmax=4", + "wmm_ac_vi_txop_limit=94", + "wmm_ac_vo_aifs=2", + "wmm_ac_vo_cwmin=2", + "wmm_ac_vo_cwmax=3", + "wmm_ac_vo_txop_limit=47" + ] + }, "g": { "settings": [ "hw_mode=g", diff --git a/src/RaspAP/Networking/Hotspot/HostapdManager.php b/src/RaspAP/Networking/Hotspot/HostapdManager.php index c9d0a5efa..0bcbbcb4c 100644 --- a/src/RaspAP/Networking/Hotspot/HostapdManager.php +++ b/src/RaspAP/Networking/Hotspot/HostapdManager.php @@ -102,6 +102,12 @@ private function resolveHwMode(array $config): string if (!empty($config['ieee80211ac']) && strval($config['ieee80211ac']) === '1') { $selected = 'ac'; } + if (!empty($config['ieee80211ax']) && strval($config['ieee80211ax']) === '1') { + $selected = 'ax'; + } + if (!empty($config['ieee80211be']) && strval($config['ieee80211be']) === '1') { + $selected = 'be'; + } if (!empty($config['ieee80211w']) && strval($config['ieee80211w']) === '2') { $selected = 'w'; } @@ -189,10 +195,24 @@ public function buildConfig(array $params, StatusMessage $status): string $vht_freq_idx = ($params['channel'] < RASPI_5GHZ_CHANNEL_MIN) ? 42 : 155; $hwMode = isset($params['hw_mode']) ? $params['hw_mode'] : ''; + // validate channel width for 802.11ax/be + if (in_array($hwMode, ['ax', 'be'])) { + // for 6GHz band (channels 1-233) wider bandwidths are available + $is6GHz = ($params['channel'] >= 1 && $params['channel'] <= 233); + + // for 802.11be, 320 MHz only available on 6GHz + if ($hwMode === 'be' && !$is6GHz && isset($params['eht_oper_chwidth']) && $params['eht_oper_chwidth'] == 4) { + // reset to 160 MHz if 320 MHz requested on non-6GHz + $params['eht_oper_chwidth'] = 2; + } + } + // fetch settings for selected mode $modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode); $settings = $modeSettings[$hwMode]['settings'] ?? []; + error_log("HostapdManager::buildConfig() -> settings\n" . var_export($settings, true)); + if (!empty($settings)) { foreach ($settings as $line) { if (!is_string($line)) { @@ -457,5 +477,26 @@ private function countHostapdConfigs(): int return is_array($configs) ? count($configs) : 0; } + /** + * Gets capabilities for a given IEEE 802.11 mode + * + * @param string $mode + * @return array + */ + public function getModeCapabilities(string $mode): array + { + $capabilities = [ + 'a' => ['bands' => ['5'], 'max_width' => 20], + 'b' => ['bands' => ['2.4'], 'max_width' => 22], + 'g' => ['bands' => ['2.4'], 'max_width' => 20], + 'n' => ['bands' => ['2.4', '5'], 'max_width' => 40], + 'ac' => ['bands' => ['5'], 'max_width' => 160], + 'ax' => ['bands' => ['2.4', '5', '6'], 'max_width' => 160], + 'be' => ['bands' => ['2.4', '5', '6'], 'max_width' => 320] + ]; + + return $capabilities[$mode] ?? $capabilities['g']; + } + } diff --git a/src/RaspAP/Networking/Hotspot/HotspotService.php b/src/RaspAP/Networking/Hotspot/HotspotService.php index 2f6a3c60d..d8f37c39a 100644 --- a/src/RaspAP/Networking/Hotspot/HotspotService.php +++ b/src/RaspAP/Networking/Hotspot/HotspotService.php @@ -32,7 +32,9 @@ class HotspotService 'b' => '802.11b - 2.4 GHz', 'g' => '802.11g - 2.4 GHz', 'n' => '802.11n - 2.4/5 GHz', - 'ac' => '802.11ac - 5 GHz' + 'ac' => '802.11ac - 5 GHz', + 'ax' => '802.11ax (Wi-Fi 6) - 2.4/5/6 GHz', + // 'be' => '802.11be (Wi-Fi 7) - 2.4/5/6 GHz' ]; // encryption types @@ -42,6 +44,21 @@ class HotspotService 'TKIP CCMP' => 'TKIP+CCMP' ]; + // 802.11ax (Wi-Fi 6) channel widths + private const HE_CHANNEL_WIDTHS = [ + 0 => '20/40 MHz', + 1 => '80 MHz', + 2 => '160 MHz' + ]; + + // 802.11be (Wi-Fi 7) channel widths + private const EHT_CHANNEL_WIDTHS = [ + 0 => '20 MHz', + 1 => '40 MHz', + 2 => '80 MHz', + 3 => '160 MHz', + 4 => '320 MHz (6 GHz only)' + ]; public function __construct() { @@ -67,7 +84,23 @@ public static function getEncTypes(): array } /** - * Returns translated security modes. + * Returns 802.11ax (Wi-Fi 6) channel widths + */ + public static function getHeChannelWidths(): array + { + return self::HE_CHANNEL_WIDTHS; + } + + /** + * Returns 802.11be (Wi-Fi 7) channel widths + */ + public static function getEhtChannelWidths(): array + { + return self::EHT_CHANNEL_WIDTHS; + } + + /** + * Returns translated security modes */ public static function getSecurityModes(): array { @@ -94,6 +127,26 @@ public static function get80211wOptions(): array ]; } + /** + * Checks if hardware mode supports advanced features + * + * @param string $mode + * @return array capabilities + */ + public static function getModeCapabilities(string $mode): array + { + $capabilities = [ + 'a' => ['wifi_generation' => 1, 'max_width' => 20, 'bands' => ['5']], + 'b' => ['wifi_generation' => 2, 'max_width' => 22, 'bands' => ['2.4']], + 'g' => ['wifi_generation' => 3, 'max_width' => 20, 'bands' => ['2.4']], + 'n' => ['wifi_generation' => 4, 'max_width' => 40, 'bands' => ['2.4', '5']], + 'ac' => ['wifi_generation' => 5, 'max_width' => 160, 'bands' => ['5']], + 'ax' => ['wifi_generation' => 6, 'max_width' => 160, 'bands' => ['2.4', '5', '6'], 'supports_he' => true], + 'be' => ['wifi_generation' => 7, 'max_width' => 320, 'bands' => ['2.4', '5', '6'], 'supports_he' => true, 'supports_eht' => true] + ]; + + return $capabilities[$mode] ?? $capabilities['g']; + } /** * Validates user input + saves configs for hostapd, dnsmasq & dhcp @@ -151,6 +204,23 @@ public function saveSettings( $validated['dualmode'] = !empty($states['DualAPEnable']); $validated['txpower'] = $post_data['txpower']; + error_log("HotspotService::saveSettings() -> validated\n" . var_export($validated, true)); + + // add 802.11ax/be specific parameters if present + if (in_array($validated['hw_mode'], ['ax', 'be'])) { + // Log advanced mode configuration + error_log(sprintf( + "Configuring advanced WiFi mode: %s with channel %d", + $validated['hw_mode'], + $validated['channel'] + )); + + // Validate WPA3 for WiFi 6/7 + if ($validated['wpa'] < 4 && $validated['hw_mode'] === 'be') { + $status->addMessage('Note: WiFi 7 works best with WPA3 security', 'info'); + } + } + // hostapd $config = $this->hostapd->buildConfig($validated, $status); $this->hostapd->saveConfig($config, $dualAPEnable, $validated['interface']); diff --git a/src/RaspAP/Networking/Hotspot/Validators/HostapdValidator.php b/src/RaspAP/Networking/Hotspot/Validators/HostapdValidator.php index 83627e117..80dbb8321 100644 --- a/src/RaspAP/Networking/Hotspot/Validators/HostapdValidator.php +++ b/src/RaspAP/Networking/Hotspot/Validators/HostapdValidator.php @@ -16,6 +16,15 @@ class HostapdValidator { + // Valid channel widths for 802.11ax (HE) + private const HE_VALID_CHWIDTHS = [0, 1, 2]; // 20/40, 80, 160 MHz + + // Valid channel widths for 802.11be (EHT) + private const EHT_VALID_CHWIDTHS = [0, 1, 2, 3, 4]; // 20, 40, 80, 160, 320 MHz + + // 6 GHz channel range (US) + private const CHANNEL_6GHZ_MIN = 1; + private const CHANNEL_6GHZ_MAX = 233; /** * Validates full hostapd parameter set @@ -64,6 +73,16 @@ public function validate( $goodInput = false; } + // validate 802.11ax specific parameters + if ($post['hw_mode'] === 'ax' && !$this->validateAxParams($post, $status)) { + $goodInput = false; + } + + // validate 802.11be specific parameters + if ($post['hw_mode'] === 'be' && !$this->validateBeParams($post, $status)) { + $goodInput = false; + } + // validate SSID if (empty($post['ssid']) || strlen($post['ssid']) > 32) { $status->addMessage('SSID must be between 1 and 32 characters', 'danger'); @@ -200,9 +219,97 @@ public function validate( 'bridgeStaticIp' => ($post['bridgeStaticIp']), 'bridgeNetmask' => ($post['bridgeNetmask']), 'bridgeGateway' => ($post['bridgeGateway']), - 'bridgeDNS' => ($post['bridgeDNS']) + 'bridgeDNS' => ($post['bridgeDNS']), + 'he_oper_chwidth' => $post['he_oper_chwidth'] ?? null, // 802.11ax parameters + 'he_bss_color' => $post['he_bss_color'] ?? null, // 802.11be parameters + 'eht_oper_chwidth' => $post['eht_oper_chwidth'] ?? null ]; } + /** + * Validates 802.11ax (Wi-Fi 6) specific parameters + * + * @param array $post + * @param StatusMessage $status + * @return bool + */ + private function validateAxParams(array $post, StatusMessage $status): bool + { + $valid = true; + + // Validate HE channel width + if (isset($post['he_oper_chwidth'])) { + $chwidth = (int)$post['he_oper_chwidth']; + if (!in_array($chwidth, self::HE_VALID_CHWIDTHS, true)) { + $status->addMessage('Invalid 802.11ax channel width. Must be 0 (20/40 MHz), 1 (80 MHz), or 2 (160 MHz)', 'danger'); + $valid = false; + } + } + + // Validate BSS color (1-63) + if (isset($post['he_bss_color'])) { + $bssColor = (int)$post['he_bss_color']; + if ($bssColor < 1 || $bssColor > 63) { + $status->addMessage('802.11ax BSS color must be between 1 and 63', 'danger'); + $valid = false; + } + } + + return $valid; + } + + /** + * Validates 802.11be (Wi-Fi 7) specific parameters + * + * @param array $post + * @param StatusMessage $status + * @return bool + */ + private function validateBeParams(array $post, StatusMessage $status): bool + { + $valid = true; + $channel = (int)$post['channel']; + + // Validate EHT channel width + if (isset($post['eht_oper_chwidth'])) { + $chwidth = (int)$post['eht_oper_chwidth']; + + if (!in_array($chwidth, self::EHT_VALID_CHWIDTHS, true)) { + $status->addMessage('Invalid 802.11be channel width. Must be 0-4 (20, 40, 80, 160, or 320 MHz)', 'danger'); + $valid = false; + } + + // 320 MHz only valid on 6 GHz band + if ($chwidth === 4) { + if ($channel < self::CHANNEL_6GHZ_MIN || $channel > self::CHANNEL_6GHZ_MAX) { + $status->addMessage('802.11be 320 MHz channel width is only available on 6 GHz band (channels 1-233)', 'danger'); + $valid = false; + } + } + } + + // Validate BSS color (same as 802.11ax, inherited) + if (isset($post['he_bss_color'])) { + $bssColor = (int)$post['he_bss_color']; + if ($bssColor < 1 || $bssColor > 63) { + $status->addMessage('BSS color must be between 1 and 63', 'danger'); + $valid = false; + } + } + + return $valid; + } + + /** + * Checks if channel is in 6GHz band + * + * @param int $channel + * @return bool + */ + private function is6GHzChannel(int $channel): bool + { + return $channel >= self::CHANNEL_6GHZ_MIN && $channel <= self::CHANNEL_6GHZ_MAX; + } + }