#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2012 IPFire Network Development Team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### HOSTAPD_CONTROL_INTERFACE_DIR="/run/hostapd/ctrl" 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 local file=${2} assert isset file # 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 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 --broadcast-ssid=*) broadcast_ssid=$(cli_get_val "${1}") ;; --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}") ;; --environment=*) environment="$(cli_get_val "${1}")" ;; --mfp=*) mfp="$(cli_get_val "${1}")" ;; --mode=*) mode=$(cli_get_val "${1}") if ! isoneof mode ${HOSTAPD_SUPPORTED_MODES}; then error "Unsupported mode: ${mode}" return ${EXIT_ERROR} fi ;; --secret=*) secret="$(cli_get_val "${1}")" ;; --ssid=*) ssid=$(cli_get_val "${1}") ;; --wmm=*) local val="$(cli_get_val "${1}")" if enabled val; then wmm="1" else 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}'." ;; esac shift done # Check if mode is set if ! isset mode; then error "Mode is not set" return ${EXIT_ERROR} fi assert isset broadcast_ssid assert isbool broadcast_ssid assert isset channel assert isinteger channel assert isset mode assert isset ssid # 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 case "${mode}" in 802.11a) hw_mode="a" ;; 802.11a/n) hw_mode="a" ieee80211n="1" # Fetch HT caps ht_caps="$(wireless_get_ht_caps "${device}")" ;; 802.11g) hw_mode="g" ;; 802.11g/n) hw_mode="g" ieee80211n="1" # Fetch HT caps ht_caps="$(wireless_get_ht_caps "${device}")" ;; 802.11ac) hw_mode="a" ieee80211ac="1" ieee80211n="1" # Fetch VHT caps vht_caps="$(wireless_get_vht_caps "${device}")" # 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 config_header "hostapd" > ${file} # Interface configuration ( print "# Interface configuration" print "driver=nl80211" print "interface=${device}" print ) >> ${file} # Wireless configuration local ignore_broadcast_ssid if enabled broadcast_ssid; then ignore_broadcast_ssid="0" else ignore_broadcast_ssid="1" fi ( # 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 && wireless_supports_dfs "${device}"; then print "ieee80211h=1" else print "ieee80211h=0" fi print # empty line print "# Wireless configuration" print "hw_mode=${hw_mode}" if isset ieee80211ac; then print "ieee80211ac=${ieee80211ac}" fi if isset ieee80211n; then print "ieee80211n=${ieee80211n}" fi print "channel=${channel}" print "ignore_broadcast_ssid=${ignore_broadcast_ssid}" print "ssid2=\"${ssid}\"" print "utf8_ssid=1" # Kick stations that are too far away print "disassoc_low_ack=1" # 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 print "vht_capab=${vht_caps}" fi # 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 "# Control interface" print "ctrl_interface=${HOSTAPD_CONTROL_INTERFACE_DIR}" print "ctrl_interface_group=0" print ) >> ${file} # Authentication Settings local wpa local wpa_key_mgmt local wpa_passphrase local sae_password local wpa_strict_rekey local sae_require_mfp # WPA3 Personal if enabled WPA3_PERSONAL; then # Enable RSN wpa="2" # Add WPA key management list_append wpa_key_mgmt "SAE" sae_password="${secret}" if enabled MFP; then sae_require_mfp="1" fi 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_passphrase="${secret}" # Enable WPA strict rekey wpa_strict_rekey="1" fi # 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_rekeyi sae_require_mfp; do if [ -n "${!var}" ]; then print "${var}=${!var}" fi done >> "${file}" # Log configuration file file_to_log DEBUG "${file}" return ${EXIT_OK} } hostapd_start() { local device=${1} assert isset device service_start "hostapd@${device}.service" local ret=$? if [ ${ret} -eq ${EXIT_OK} ]; then log DEBUG "hostapd has been successfully started on '${device}'" else log ERROR "Could not start hostapd on '${device}': ${ret}" return ${EXIT_ERROR} fi return ${EXIT_OK} } hostapd_stop() { local device=${1} assert isset device 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 }