WIRELESS_REGULATORY_DOMAIN="00"
NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} WIRELESS_REGULATORY_DOMAIN"
-function wireless_create() {
+WIRELESS_REGULATORY_DOMAIN_DATABASE="/usr/lib/crda/regulatory.bin"
+
+WIRELESS_DEFAULT_ENCRYPTION_MODE="NONE"
+WIRELESS_VALID_ENCRYPTION_MODES="WPA2-PSK-SHA256 WPA2-PSK \
+ WPA-PSK-SHA256 WPA-PSK NONE"
+
+cli_wireless() {
+ local action=${1}
+ shift 1
+
+ case "${action}" in
+ network)
+ cli_wireless_network "$@"
+ ;;
+ *)
+ error "Unrecognized argument: ${action}"
+ exit ${EXIT_ERROR}
+ ;;
+ esac
+}
+
+wireless_create() {
local device=${1}
assert isset device
shift
local address
+ local channel
local phy
local type="managed"
while [ $# -gt 0 ]; do
case "${1}" in
--address=*)
- address=$(cli_get_val ${1})
+ address=$(cli_get_val "${1}")
+ ;;
+ --channel=*)
+ channel=$(cli_get_val "${1}")
;;
--phy=*)
- phy=$(cli_get_val ${1})
+ phy=$(cli_get_val "${1}")
phy=$(phy_get ${phy})
;;
--type=*)
- type=$(cli_get_val ${1})
+ type=$(cli_get_val "${1}")
# ap --> __ap
[ "${type}" = "ap" ] && type="__ap"
shift
done
- assert isoneof type ibss managed monitor __ap
+ case "${type}" in
+ ibss|managed|monitor|__ap)
+ ;;
+ mesh-point)
+ type="mp"
+ ;;
+ *)
+ log ERROR "Unknown type: ${type}"
+ return ${EXIT_ERROR}
+ ;;
+
+ esac
+
assert phy_exists ${phy}
isset address || address=$(mac_generate)
log ERROR "could not create wireless device '${device}' (${type}): ${ret}"
fi
+ # Set the channel
+ if isset channel; then
+ wireless_set_channel "${device}" "${channel}" "auto" || return $?
+ fi
+
return ${ret}
}
-function wireless_remove() {
+wireless_remove() {
local device=${1}
assert isset device
return ${ret}
}
-function wireless_get_reg_domain() {
+wireless_get_reg_domain() {
# Returns the country code for the wireless device.
# Defaults to 00 = world if unset.
print "${WIRELESS_REGULATORY_DOMAIN:-00}"
}
-function wireless_init_reg_domain() {
+wireless_init_reg_domain() {
local country_code="$(wireless_get_reg_domain)"
wireless_set_reg_domain "${country_code}" --no-reset
}
-function wireless_set_reg_domain() {
+wireless_set_reg_domain() {
local country_code
local reset="true"
shift
done
- assert isset country_code
+ # Check if configuration value is valid
+ if ! wireless_valid_reg_domain "${country_code}"; then
+ log ERROR "Invalid wireless regulatory domain: ${country_code}"
+ return ${EXIT_ERROR}
+ fi
# Before the wireless reg domain is set, it helps to reset to 00 first.
if enabled reset; then
iw reg set "${country_code}"
}
-function wireless_channel_to_frequency() {
- # http://en.wikipedia.org/wiki/List_of_WLAN_channels
+wireless_valid_reg_domain() {
+ local country_code="${1}"
+
+ # Empty country codes are invalid
+ isset country_code || return ${EXIT_FALSE}
+
+ local valid_country_codes="$(wireless_list_reg_domains)"
+
+ if list_match "${country_code}" ${valid_country_codes}; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+}
+wireless_list_reg_domains() {
+ if [ ! -r "${WIRELESS_REGULATORY_DOMAIN_DATABASE}" ]; then
+ log ERROR "Could not read ${WIRELESS_REGULATORY_DOMAIN_DATABASE}"
+ return ${EXIT_ERROR}
+ fi
+
+ local line
+ while read line; do
+ # Check if line starts with "country"
+ [ "${line:0:7}" = "country" ] || continue
+
+ # Print country code
+ print "${line:8:2}"
+ done <<< "$(regdbdump ${WIRELESS_REGULATORY_DOMAIN_DATABASE})"
+}
+
+# http://en.wikipedia.org/wiki/List_of_WLAN_channels
+wireless_channel_to_frequency() {
local channel=${1}
- assert isset channel
- # Channel number must be positive.
- assert [ "${channel}" -gt 0 ]
+ # Works only for valid channel numbers
+ if ! wireless_channel_is_valid "${channel}"; then
+ log ERROR "Invalid wireless channel: ${channel}"
+ return ${EXIT_ERROR}
+ fi
# 2.4 GHz band
case "${channel}" in
return ${EXIT_ERROR}
}
-function wireless_set_channel() {
- local device=${1}
- assert isset device
+wireless_frequency_to_channel() {
+ local frequency=${1}
+
+ assert isinteger frequency
+
+ # Everything that is too high
+ if [ ${frequency} -gt 5825 ]; then
+ return ${EXIT_ERROR}
+
+ # 5 GHz Band
+ elif [ ${frequency} -gt 5000 ]; then
+ (( frequency = frequency - 5000 ))
+
+ # Must be divisible by 5
+ [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
+
+ print "$(( frequency / 5 ))"
+
+ # 2.4 GHz Band - Channel 14
+ elif [ ${frequency} -eq 2484 ]; then
+ print "14"
+
+ # 2.4 GHz Band
+ elif [ ${frequency} -gt 2407 ]; then
+ (( frequency = frequency - 2407 ))
+
+ # Must be divisible by 5
+ [ "$(( frequency % 5 ))" -ne 0 ] && return ${EXIT_ERROR}
+
+ print "$(( frequency / 5 ))"
+
+ # Everything else
+ else
+ return ${EXIT_ERROR}
+ fi
+
+ return ${EXIT_OK}
+}
+
+wireless_channel_is_valid() {
+ local channel=${1}
+
+ case "${channel}" in
+ # 2.4 GHz Band
+ [123456789]|1[0123]|14)
+ return ${EXIT_TRUE}
+ ;;
+
+ # 5 GHz Band
+ 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15])
+ return ${EXIT_TRUE}
+ ;;
+ esac
+
+ # Invalid channel number given
+ return ${EXIT_FALSE}
+}
+
+wireless_channel_is_ht40_plus() {
+ local channel="${1}"
+ assert isinteger channel
+
+ # 2.4 GHz
+ if [ ${channel} -le 6 ]; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+}
+
+wireless_channel_is_ht40_minus() {
+ local channel="${1}"
+ assert isinteger channel
- local channel=${2}
- assert isset channel
+ # 2.4 GHz
+ if [ ${channel} -ge 6 ]; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+}
+
+wireless_set_channel() {
+ local device="${1}"
+ local channel="${2}"
+ local bandwidth="${3}"
+
+ # Check if the device exists
+ if ! device_exists "${device}"; then
+ log ERROR "No such device: ${device}"
+ return ${EXIT_ERROR}
+ fi
- device_exists ${device} || return ${EXIT_ERROR}
+ # Check if the channel number is valid
+ if ! wireless_channel_is_valid "${channel}"; then
+ log ERROR "Invalid wireless channel: ${channel}"
+ return ${EXIT_ERROR}
+ fi
+
+ local ht_flag
+ if [ "${bandwidth}" = "auto" ]; then
+ local phy="$(device_get_phy "${device}")"
+
+ # Offset of a 40 MHz channel
+ local ht_offset=5
+
+ if wireless_channel_is_ht40_plus "${channel}" \
+ && phy_supports_ht_capability "${phy}" "HT40+" \
+ && phy_supports_channel "${phy}" $(( channel + ht_offset )); then
+ ht_flag="HT40+"
+
+ elif wireless_channel_is_ht40_minus "${channel}" \
+ && phy_supports_ht_capability "${phy}" "HT40-" \
+ && phy_supports_channel "${phy}" $(( channel - ht_offset )); then
+ ht_flags="HT40-"
+ fi
+ fi
log DEBUG "Setting wireless channel on device '${device}' to channel '${channel}'"
- cmd_quiet iw dev ${device} set channel ${channel}
+ cmd iw dev "${device}" set channel "${channel}" "${ht_flag}"
}
-function wireless_ibss_join() {
+wireless_pre_shared_key_is_valid() {
+ local encryption_mode="${1}"
+ local psk="${2}"
+
+ # Length of the PSK
+ local l="${#psk}"
+
+ case "${encryption_mode}" in
+ # For WPA*, the key must be between 8 and 63 chars
+ WPA2-PSK|WPA2-PSK-SHA256|WPA-PSK|WPA-PSK-SHA256)
+ if [ ${l} -ge 8 ] && [ ${l} -le 63 ]; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+ ;;
+ esac
+
+ return ${EXIT_ERROR}
+}
+
+wireless_client_is_connected() {
+ local device="${1}"
+
+ device_has_carrier "${device}"
+}
+
+wireless_ibss_join() {
local device=${1}
assert isset device
shift
while [ $# -gt 0 ]; do
case "${1}" in
--bssid=*)
- bssid="$(cli_get_val ${1})"
+ bssid="$(cli_get_val "${1}")"
;;
--essid=*)
- essid="$(cli_get_val ${1})"
+ essid="$(cli_get_val "${1}")"
;;
--channel=*)
- local channel="$(cli_get_val ${1})"
+ local channel="$(cli_get_val "${1}")"
# Save the frequency of the channel instead
# of the channel itself.
"${frequency}" fixed-freq "${bssid}"
}
-function wireless_ibss_leave() {
+wireless_ibss_leave() {
local device=${1}
assert isset device
cmd_quiet iw dev "${device}" ibss leave
}
-function wireless_is_radar_frequency() {
+wireless_is_radar_frequency() {
local frequency="${1}"
assert isset frequency
[[ ${frequency} -ge 5260 ]] && [[ ${frequency} -le 5700 ]]
}
-function wireless_monitor() {
+wireless_monitor() {
local device="${1}"
assert isset device
shift
return ${EXIT_OK}
}
+
+wireless_get_ht_caps() {
+ local device="${1}"
+ assert isset device
+
+ local phy="$(device_get_phy "${device}")"
+ if ! isset phy; then
+ log ERROR "Could not determine PHY for ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ network-phy-list-ht-caps "${phy}"
+}
+
+wireless_get_vht_caps() {
+ local device="${1}"
+ assert isset device
+
+ local phy="$(device_get_phy "${device}")"
+ if ! isset phy; then
+ log ERROR "Could not determine PHY for ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ network-phy-list-vht-caps "${phy}"
+}