#!/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 . # # # ############################################################################### # Sets the global wireless country code. Default is 00 = world. WIRELESS_REGULATORY_DOMAIN="00" NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} WIRELESS_REGULATORY_DOMAIN" WIRELESS_REGULATORY_DOMAIN_DATABASE="/usr/lib/crda/regulatory.bin" WIRELESS_NETWORK_CONFIG_SETTINGS="\ SSID \ ENCRYPTION_MODE \ KEY \ PRIORITY" WIRELESS_NETWORKS_VALID_ENCRYPTION_MODES="WPA2-PSK" 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}") ;; --channel=*) channel=$(cli_get_val "${1}") ;; --phy=*) phy=$(cli_get_val "${1}") phy=$(phy_get ${phy}) ;; --type=*) type=$(cli_get_val "${1}") # ap --> __ap [ "${type}" = "ap" ] && type="__ap" ;; *) error "Unrecognized argument: ${1}" return ${EXIT_ERROR} ;; esac shift done 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) cmd_quiet iw phy ${phy} interface add ${device} type ${type} local ret=$? if [ ${ret} -eq ${EXIT_OK} ]; then log DEBUG "created wireless device '${device}' (${type})" if isset address; then device_set_address ${device} ${address} fi else log ERROR "could not create wireless device '${device}' (${type}): ${ret}" fi # Set the channel if isset channel; then wireless_set_channel "${device}" "${channel}" || return $? fi return ${ret} } wireless_remove() { local device=${1} assert isset device if ! device_exists ${device}; then return ${EXIT_OK} fi # Tear down the device (if necessary). device_set_down ${device} # Remove it. cmd_quiet iw dev ${device} del local ret=$? if [ ${ret} -eq ${EXIT_OK} ]; then log DEBUG "removed wireless device '${device}'" else log ERROR "could not remove wireless device '${device}': ${ret}" fi return ${ret} } wireless_get_reg_domain() { # Returns the country code for the wireless device. # Defaults to 00 = world if unset. print "${WIRELESS_REGULATORY_DOMAIN:-00}" } wireless_init_reg_domain() { local country_code="$(wireless_get_reg_domain)" wireless_set_reg_domain "${country_code}" --no-reset } wireless_set_reg_domain() { local country_code local reset="true" while [ $# -gt 0 ]; do case "${1}" in --no-reset) reset="false" ;; -*) log ERROR "Ignoring invalid option: ${1}" ;; *) country_code="${1}" ;; esac shift done # 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 00 &>/dev/null fi log INFO "Setting wireless regulatory domain country to '${country_code}'" iw reg set "${country_code}" } 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} # 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 [123456789]|1[0123]) print "$(( 2407 + (${channel} * 5)))" return ${EXIT_OK} ;; 14) print "2484" return ${EXIT_OK} ;; esac # 5 GHz band case "${channel}" in 3[68]|4[02468]|5[26]|6[04]|10[048]|11[26]|12[048]|13[26]|14[09]|15[37]|16[15]) print "$(( 5000 + (${channel} * 5)))" return ${EXIT_OK} ;; esac return ${EXIT_ERROR} } 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_set_channel() { local device=${1} local channel=${2} # Check if the device exists if ! device_exists "${device}"; then log ERROR "No such device: ${device}" return ${EXIT_ERROR} fi # Check if the channel number is valid if ! wireless_channel_is_valid "${channel}"; then log ERROR "Invalid wireless channel: ${channel}" return ${EXIT_ERROR} fi log DEBUG "Setting wireless channel on device '${device}' to channel '${channel}'" cmd iw dev "${device}" set channel "${channel}" } wireless_ibss_join() { local device=${1} assert isset device shift local bssid local essid local frequency while [ $# -gt 0 ]; do case "${1}" in --bssid=*) bssid="$(cli_get_val "${1}")" ;; --essid=*) essid="$(cli_get_val "${1}")" ;; --channel=*) local channel="$(cli_get_val "${1}")" # Save the frequency of the channel instead # of the channel itself. if isset channel; then frequency="$(wireless_channel_to_frequency ${channel})" fi ;; esac shift done # Check input. assert ismac bssid assert isset essid assert isinteger frequency # Set device up. device_set_up "${device}" log INFO "${device} joining ibss network: ${essid} (${bssid})" cmd_quiet iw dev "${device}" ibss join "${essid}" \ "${frequency}" fixed-freq "${bssid}" } wireless_ibss_leave() { local device=${1} assert isset device log INFO "${device} leaving ibss network" cmd_quiet iw dev "${device}" ibss leave } wireless_is_radar_frequency() { local frequency="${1}" assert isset frequency [[ ${frequency} -ge 5260 ]] && [[ ${frequency} -le 5700 ]] } wireless_monitor() { local device="${1}" assert isset device shift local monitor_device="$(port_find_free "${PORT_PATTERN_WIRELESS_MONITOR}")" # Create an 802.11 monitoring device wireless_create "${monitor_device}" --phy="${device}" --type="monitor" local ret=$? case "${ret}" in 0) # Bring up the device device_set_up "${monitor_device}" # Starting tcpdump tcpdump -i "${monitor_device}" "$@" # Remove the monitoring interface. wireless_remove "${monitor_device}" ;; *) log ERROR "Could not create a monitoring interface on ${device}" return ${EXIT_ERROR} ;; esac return ${EXIT_OK} } cli_wireless() { local action=${1} shift 1 case "${action}" in network) cli_wireless_network "$@" ;; *) error "Unrecognized argument: ${action}" exit ${EXIT_ERROR} ;; esac } cli_wireless_network() { if wireless_network_exists "${1}"; then local ssid="${1}" local key="${2}" key=${key//-/_} shift 2 case "${key}" in encryption_mode|key|priority) wireless_network_${key} "${ssid}" "$@" ;; show) wireless_network_show "${ssid}" exit $? ;; *) error "Unrecognized argument: ${key}" exit ${EXIT_ERROR} ;; esac else local action=${1} shift case "${action}" in new) wireless_network_new "$@" ;; destroy) wireless_network_destroy "$@" ;; ""|*) if [ -n "${action}" ]; then error "Unrecognized argument: '${action}'" fi exit ${EXIT_ERROR} ;; esac fi } # This function writes all values to a via ${ssid} specificated wireless network configuration file wireless_network_write_config() { assert [ $# -ge 1 ] local ssid="${1}" local ssid_hash="$(wireless_network_hash "${ssid}")" assert isset ssid_hash if ! wireless_network_exists "${ssid}"; then log ERROR "No such wireless network: '${ssid}'" return ${EXIT_ERROR} fi local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings" if ! settings_write "${path}" ${WIRELESS_NETWORK_CONFIG_SETTINGS}; then log ERROR "Could not write configuration settings for wireless network ${ssid}" return ${EXIT_ERROR} fi # When we get here the writing of the config file was successful return ${EXIT_OK} } # This funtion writes the value for one key to a via ${ssid} specificated # wireless network configuration file wireless_network_write_config_key() { assert [ $# -ge 3 ] local ssid="${1}" local key="${2}" shift 2 local value="$@" if ! wireless_network_exists "${ssid}"; then log ERROR "No such wireless network: ${ssid}" return ${EXIT_ERROR} fi log DEBUG "Set '${key}' to new value '${value}' in wireless network '${ssid}'" local ${WIRELESS_NETWORK_CONFIG_SETTINGS} # Read the config settings if ! wireless_network_read_config "${ssid}"; then return ${EXIT_ERROR} fi # Set the key to a new value assign "${key}" "${value}" if ! wireless_network_write_config "${ssid}"; then return ${EXIT_ERROR} fi return ${EXIT_OK} } # Reads one or more keys out of a settings file or all if no key is provided. wireless_network_read_config() { assert [ $# -ge 1 ] local ssid="${1}" shift 1 local ssid_hash="$(wireless_network_hash "${ssid}")" assert isset ssid_hash if ! wireless_network_exists "${ssid}"; then log ERROR "No such wireless network : ${ssid}" return ${EXIT_ERROR} fi local args if [ $# -eq 0 ] && [ -n "${WIRELESS_NETWORK_CONFIG_SETTINGS}" ]; then list_append args ${WIRELESS_NETWORK_CONFIG_SETTINGS} else list_append args "$@" fi local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings" if ! settings_read "${path}" ${args}; then log ERROR "Could not read settings for wireless network ${ssid}" return ${EXIT_ERROR} fi } # This function checks if a wireless network exists # Returns True when yes and false when not wireless_network_exists() { assert [ $# -eq 1 ] local ssid="${1}" local ssid_hash="$(wireless_network_hash "${ssid}")" assert isset ssid_hash local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}" # We cannot use wireless_network_read_config here beacuse we would end in a loop local SSID local path_settings="${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings" if ! settings_read "${path_settings}" SSID; then return ${EXIT_FALSE} fi assert isset SSID if [ -d "${path}" ] && [[ "${ssid}" = "${SSID}" ]]; then return ${EXIT_TRUE} else return ${EXIT_FALSE} fi } wireless_network_hash() { assert [ $# -eq 1 ] local string="${1}" local hash=$(echo -n "${string}" | md5sum ) hash=${hash%% -} local path="${NETWORK_WIRELESS_NETWORKS_DIR}/*${hash}" if [ -d "${path}" ]; then basename "${path}" else local normalized=$(normalize "${string}") normalized=${normalized%-} echo "${normalized}-${hash}" fi } wireless_network_new() { if [ $# -gt 1 ]; then error "Too many arguments" return ${EXIT_ERROR} fi local ssid="${1}" if ! isset ssid; then error "Please provide a SSID" return ${EXIT_ERROR} fi local ssid_hash="$(wireless_network_hash "${ssid}")" assert isset ssid_hash # Check for duplicates if wireless_network_exists "${ssid}"; then error "The wireless network ${ssid} already exists" return ${EXIT_ERROR} fi log DEBUG "Creating wireless network '${ssid}'" if ! mkdir -p "${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}"; then log ERROR "Could not create config directory for wireless network ${ssid}" return ${EXIT_ERROR} fi # When the ssid is not set in the settings file we cannot write it because wireless_network_exists fails echo "SSID=\"${ssid}\"" >>"${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}/settings" local ${WIRELESS_NETWORK_CONFIG_SETTINGS} SSID="${ssid}" PRIORITY=500 if ! wireless_network_write_config "${ssid}"; then log ERROR "Could not write new config file" return ${EXIT_ERROR} fi } # Function that deletes based on the passed parameters # one ore more wireless networks wireless_network_destroy() { local ssid for ssid in "$@"; do local ssid_hash="$(wireless_network_hash "${ssid}")" assert isset ssid_hash if ! wireless_network_exists "${ssid}"; then log ERROR "The wireless network ${ssid} does not exist." continue fi log DEBUG "Deleting wireless network ${ssid}" if ! rm -rf "${NETWORK_WIRELESS_NETWORKS_DIR}/${ssid_hash}"; then log ERROR "Deleting the wireless network ${ssid} was not sucessful" return ${EXIT_ERROR} fi done } wireless_network_encryption_mode() { if [ ! $# -eq 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local ssid="${1}" local mode="${2}" if ! isoneof mode ${WIRELESS_NETWORKS_VALID_ENCRYPTION_MODES}; then log ERROR "Encryption mode '${mode}' is invalid" return ${EXIT_ERROR} fi if ! wireless_network_write_config_key "${ssid}" "ENCRYPTION_MODE" ${mode^^}; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi } wireless_network_key() { if [ ! $# -eq 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local ssid="${1}" local key="${2}" if ! wireless_network_write_config_key "${ssid}" "KEY" "${key}"; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi } wireless_network_priority() { if [ ! $# -eq 2 ]; then log ERROR "Not enough arguments" return ${EXIT_ERROR} fi local ssid="${1}" local priority=${2} if ! isinteger priority && [ ! ${priority} -ge 0 ]; then log ERROR "The priority must be an integer greater or eqal zero" return ${EXIT_ERROR} fi if ! wireless_network_write_config_key "${ssid}" "PRIORITY" "${priority}"; then log ERROR "Could not write configuration settings" return ${EXIT_ERROR} fi }