HOSTAPD_SUPPORTED_MODES="802.11a 802.11a/n 802.11ac 802.11g 802.11g/n"
+HOSTAPD_SUPPORTED_PAIRWISE_CIPHERS=(
+ "GCMP-256" # Galois/counter mode protocol with 256 bit key
+ "CCMP-256" # AES in Counter mode with CBC-MAC with 256 bit key
+ "GCMP-128" # Galois/counter mode protocol with 128 bit key
+ "CCMP-128" # AES in Counter mode with CBC-MAC with 128 bit key
+)
+
+# This must be supported by all stations on the network and therefore
+# can effectively only be CCMP
+HOSTAPD_SUPPORTED_GROUP_CIPHERS=(
+ "CCMP-128"
+)
+
hostapd_config_write() {
local device=${1}
assert isset device
# Shift the device and file argument.
shift 2
+ # Device must exist
+ if ! device_exists "${device}"; then
+ error "Cannot write hostapd configuration for non-existant device: ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ # Get the phy for device
+ local phy="$(device_get_phy "${device}")"
+ assert isset phy
+
local broadcast_ssid
local channel
+ local channel_bandwidth
local country_code="$(wireless_get_reg_domain)"
local dfs="on"
- local encryption
- local key
+ local environment="${WIRELESS_DEFAULT_ENVIRONMENT}"
+ local mfp="off"
local mode
+ local secret
local ssid
local wmm="1"
+ local wpa2_personal="off"
+ local wpa3_personal="off"
while [ $# -gt 0 ]; do
case "${1}" in
--channel=*)
channel=$(cli_get_val "${1}")
;;
+ --channel-bandwidth=*)
+ channel_bandwidth="$(cli_get_val "${1}")"
+ ;;
--dfs=*)
dfs="$(cli_get_val "${1}")"
;;
--encryption=*)
encryption=$(cli_get_val "${1}")
;;
- --key=*)
- key=$(cli_get_val "${1}")
+ --environment=*)
+ environment="$(cli_get_val "${1}")"
+ ;;
+ --mfp=*)
+ mfp="$(cli_get_val "${1}")"
;;
--mode=*)
mode=$(cli_get_val "${1}")
return ${EXIT_ERROR}
fi
;;
+ --secret=*)
+ secret="$(cli_get_val "${1}")"
+ ;;
--ssid=*)
ssid=$(cli_get_val "${1}")
;;
wmm="0"
fi
;;
+ --wpa2-personal=*)
+ wpa2_personal="$(cli_get_bool "${1}")"
+ ;;
+ --wpa3-personal=*)
+ wpa3_personal="$(cli_get_bool "${1}")"
+ ;;
*)
warning_log "Ignoring unknown argument '${1}'."
;;
assert isset mode
assert isset ssid
- # Check if key is set when encryption is used.
- if isset encryption; then
- assert isoneof encryption WPA WPA2 WPA/WPA2
- assert isset key
+ # Check wireless environment
+ if ! wireless_environment_is_valid "${environment}"; then
+ error "Invalid wireless environment: ${environment}"
+ return ${EXIT_ERROR}
+ fi
+
+ # With channel 0, ACS must be supported
+ if [ ${channel} -eq 0 ] && ! wireless_supports_acs "${device}"; then
+ error "ACS requested, but not supported by ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ # Check channel bandwidth for validity
+ if isset channel_bandwidth && ! wireless_channel_bandwidth_is_valid "${mode}" "${channel_bandwidth}"; then
+ error "Invalid channel bandwidth for ${mode}: ${channel_bandwidth}"
+ return ${EXIT_ERROR}
+ fi
+
+ # Management Frame Proection
+ if ! isbool mfp; then
+ error "Invalid value for --mfp: ${mfp}"
+ return ${EXIT_ERROR}
+ fi
+
+ # Check if secret is set for personal authentication
+ if ! isset secret && (enabled WPA3_PERSONAL || enabled WPA2_PERSONAL); then
+ error "Secret not set but personal authentication enabled"
+ return ${EXIT_ERROR}
fi
# 802.11ac/n flags
local ieee80211ac
local ieee80211n
local vht_caps
+ local vht_oper_chwidth="0"
local ht_caps
local hw_mode
# Fetch HT caps
ht_caps="$(wireless_get_ht_caps "${device}")"
+
+ case "${channel_bandwidth}" in
+ 80)
+ vht_oper_chwidth="1"
+ ;;
+ 160)
+ vht_oper_chwidth="2"
+ ;;
+ 80+80)
+ vht_oper_chwidth="3"
+ ;;
+ esac
;;
esac
+ # Cryptography
+ local cipher
+
+ # Get all supported pairwise ciphers
+ local pairwise_ciphers=()
+ for cipher in ${HOSTAPD_SUPPORTED_PAIRWISE_CIPHERS[*]}; do
+ if phy_supports_cipher "${phy}" "${cipher}"; then
+ pairwise_ciphers+=( "$(hostapd_cipher_name "${cipher}")" )
+ fi
+ done
+
+ # Get all supported group ciphers
+ local group_ciphers=()
+ for cipher in ${HOSTAPD_SUPPORTED_GROUP_CIPHERS[*]}; do
+ if phy_supports_cipher "${phy}" "${cipher}"; then
+ group_ciphers+=( "$(hostapd_cipher_name "${cipher}")" )
+ fi
+ done
+
# Create configuration directory.
local config_dir=$(dirname ${file})
mkdir -p ${HOSTAPD_CONTROL_INTERFACE_DIR} ${config_dir} 2>/dev/null
fi
(
- print "# Default settings"
-
# Advertise country code and maximum transmission power
print "ieee80211d=1"
+ print "country_code=${country_code}"
+
+ # Wireless Environment
+ case "${environment}" in
+ indoor)
+ print "country3=0x49"
+ country3
+ ;;
+ outdoor)
+ print "country3=0x4f"
+ ;;
+ indoor+outdoor)
+ print "country3=0x20"
+ ;;
+ esac
+
+ # Always advertise TPC
+ print "local_pwr_constraint=3"
+ print "spectrum_mgmt_required=1"
# Enable Radar Detection
- if enabled dfs; then
+ if enabled dfs && wireless_supports_dfs "${device}"; then
print "ieee80211h=1"
else
print "ieee80211h=0"
fi
print "channel=${channel}"
- print "country_code=${country_code}"
print "ignore_broadcast_ssid=${ignore_broadcast_ssid}"
- if contains_spaces "${ssid}"; then
- print "ssid=\"${ssid}\""
- else
- print "ssid=${ssid}"
- fi
+ print "ssid2=\"${ssid}\""
+ print "utf8_ssid=1"
+
+ # Kick stations that are too far away
+ print "disassoc_low_ack=1"
- # WMM
+ # WMM & WMM-PS Unscheduled Automatic Power Save Delivery
print "wmm_enabled=${wmm}"
+ print "uapsd_advertisement_enabled=1"
+
+ # Low Priority / AC_BK = Background
+ print "wmm_ac_bk_cwmin=4"
+ print "wmm_ac_bk_cwmax=10"
+ print "wmm_ac_bk_aifs=7"
+ print "wmm_ac_bk_txop_limit=0"
+ print "wmm_ac_bk_acm=0"
+ print "tx_queue_data3_aifs=7"
+ print "tx_queue_data3_cwmin=15"
+ print "tx_queue_data3_cwmax=1023"
+ print "tx_queue_data3_burst=0"
+
+ # Normal Priority / AC_BE = Best Effort
+ print "wmm_ac_be_aifs=3"
+ print "wmm_ac_be_cwmin=4"
+ print "wmm_ac_be_cwmax=10"
+ print "wmm_ac_be_txop_limit=0"
+ print "wmm_ac_be_acm=0"
+ print "tx_queue_data2_aifs=3"
+ print "tx_queue_data2_cwmin=15"
+ print "tx_queue_data2_cwmax=63"
+ print "tx_queue_data2_burst=0"
+
+ # High Priority / AC_VI = Video
+ print "wmm_ac_vi_aifs=2"
+ print "wmm_ac_vi_cwmin=3"
+ print "wmm_ac_vi_cwmax=4"
+ print "wmm_ac_vi_txop_limit=94"
+ print "wmm_ac_vi_acm=0"
+ print "tx_queue_data1_aifs=1"
+ print "tx_queue_data1_cwmin=7"
+ print "tx_queue_data1_cwmax=15"
+ print "tx_queue_data1_burst=3.0"
+
+ # Highest Priority / AC_VO = Voice
+ print "wmm_ac_vo_aifs=2"
+ print "wmm_ac_vo_cwmin=2"
+ print "wmm_ac_vo_cwmax=3"
+ print "wmm_ac_vo_txop_limit=47"
+ print "wmm_ac_vo_acm=0"
+ print "tx_queue_data0_aifs=1"
+ print "tx_queue_data0_cwmin=3"
+ print "tx_queue_data0_cwmax=7"
+ print "tx_queue_data0_burst=1.5"
# Enable VHT caps
if isset vht_caps; then
# Enable HT caps
print "ht_capab=${ht_caps}"
+ # Wider Channels
+ print "vht_oper_chwidth=${vht_oper_chwidth}"
+
print
+
+ # 802.11w - Management Frame Protection (MFP)
+ if enabled mfp; then
+ print "ieee80211w=2" # required
+ else
+ print "ieee80211w=0"
+ fi
) >> ${file}
# Control interface.
print
) >> ${file}
- # Encryption settings
- if isset encryption; then
- local encryption_mode=0
- case "${encryption}" in
- WPA)
- encryption_mode=1
- ;;
- WPA2)
- encryption_mode=2
- ;;
- WPA/WPA2)
- encryption_mode=3
- ;;
- esac
+ # Authentication Settings
+ local wpa
+ local wpa_key_mgmt
+ local wpa_passphrase
+ local sae_password
+ local wpa_strict_rekey
+
+ # WPA3 Personal
+ if enabled WPA3_PERSONAL; then
+ # Enable RSN
+ wpa="2"
+
+ # Add WPA key management
+ list_append wpa_key_mgmt "SAE"
+ sae_password="${secret}"
+ fi
+
+ # WPA2 Personal
+ if enabled WPA2_PERSONAL; then
+ # Enable RSN
+ wpa="2"
+
+ # Add WPA key management
+ list_append wpa_key_mgmt "WPA-PSK-SHA256" "WPA-PSK"
+ wpa_passphrase="${secret}"
+
+ # Enable WPA strict rekey
+ wpa_strict_rekey="1"
+ fi
- (
- print "# Encryption settings"
- print "wpa=${encryption_mode}"
- print "wpa_passphrase=${key}"
- print "wpa_key_mgmt=WPA-PSK"
- print "wpa_pairwise=TKIP"
- print "rsn_pairwise=CCMP"
- print
- ) >> ${file}
+ # Enable RSN ciphers when RSN is enabled
+ local rsn_pairwise
+ local group_cipher
+ if [ "${wpa}" = "2" ]; then
+ rsn_pairwise="${pairwise_ciphers[*]}"
+ group_cipher="${group_ciphers[*]}"
fi
+ local var
+ for var in wpa wpa_key_mgmt wpa_passphrase sae_password \
+ rsn_pairwise group_cipher wpa_strict_rekey; do
+ if [ -n "${!var}" ]; then
+ print "${var}=${!var}"
+ fi
+ done >> "${file}"
+
+ # Log configuration file
+ file_to_log DEBUG "${file}"
+
return ${EXIT_OK}
}
service_stop "hostapd@${device}.service"
}
+
+hostapd_cipher_name() {
+ local cipher="${1}"
+
+ case "${cipher}" in
+ CCMP-128)
+ print "CCMP"
+ ;;
+
+ GCMP-128)
+ print "GCMP"
+ ;;
+
+ *)
+ print "${cipher}"
+ ;;
+ esac
+}