diff options
Diffstat (limited to 'net-scripts/net.modules.d/wpa_supplicant.sh')
-rw-r--r-- | net-scripts/net.modules.d/wpa_supplicant.sh | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/net-scripts/net.modules.d/wpa_supplicant.sh b/net-scripts/net.modules.d/wpa_supplicant.sh new file mode 100644 index 0000000..22343a7 --- /dev/null +++ b/net-scripts/net.modules.d/wpa_supplicant.sh @@ -0,0 +1,368 @@ +# Copyright (c) 2004-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# Contributed by Roy Marples (uberlord@gentoo.org) + +# Fix any potential localisation problems +# Note that LC_ALL trumps LC_anything_else according to locale(7) +wpa_supplicant() { + LC_ALL=C /sbin/wpa_supplicant "$@" +} + +wpa_cli() { + LC_ALL=C /bin/wpa_cli "$@" +} + +# void wpa_supplicant_depend(void) +# +# Sets up the dependancies for the module +wpa_supplicant_depend() { + after macnet plug + before interface + provide wireless + functions interface_exists +} + +# void wpa_supplicant_expose(void) +# +# Expose variables that can be configured +wpa_supplicant_expose() { + variables associate_timeout wpa_supplicant +} + +# bool wpa_supplicant_check_installed(void) +# +# Returns 0 if wpa_supplicant is installed, otherwise 1 +wpa_supplicant_check_installed() { + local report="${1:-false}" installed="0" + if [[ ! -x /sbin/wpa_supplicant ]] ; then + installed="1" + ${report} && eerror "For WPA support (wpa_supplicant) support, emerge net-wireless/wpa_supplicant" + fi + if [[ ! -e /proc/net/packet ]] ; then + installed="1" + if ${report} ; then + eerror "wpa_supplicant requires Packet Socket" + eerror "(CONFIG_PACKET=y) enabled in the kernel" + fi + fi + return "${installed}" +} + +# bool wpa_supplicant_exists(char *interface) +# +# Checks to see if wireless extensions are enabled on the interface +wpa_supplicant_exists() { + [[ ! -e /proc/net/wireless ]] && return 1 + grep -q "^[ \t]*$1:[ \t]" /proc/net/wireless +} + +# char* wpa_supplicant_get_essid(char *interface) +# +# Gets the current ESSID of iface +wpa_supplicant_get_essid() { + local i essid + + for (( i=0; i<5; i++ )); do + essid="$( wpa_cli -i"$1" status | sed -n -e 's/^ssid=//p' )" + if [[ -n ${essid} ]] ; then + echo "${essid}" + return 0 + fi + sleep 1 + done + + return 1 +} + +# char* wpa_supplicant_get_ap_mac_address(char *interface) +# +# Returns the MAC address of the Access Point +# the interface is connected to +wpa_supplicant_get_ap_mac_address() { + wpa_cli -i"$1" status | sed -n -e 's/^bssid=\([^=]\+\).*/\U\1/p' +} + +# bool wpa_supplicant_associated(char *interface) +# +# Returns 0 if we're associated correctly or 1 if not +# Note that just because we are associated does not mean we are using the +# correct encryption keys +wpa_supplicant_associated() { + local -a status + eval status=( $( wpa_cli -i"$1" status | sed -n -e 's/^\(key_mgmt\|wpa_state\|EAP state\)=\([^=]\+\).*/\U\"\2\"/p' ) ) + + case "${status[0]}" in + "NONE") [[ ${status[1]} == "ASSOCIATED" || ${status[1]} == "COMPLETED" ]] ;; + "IEEE 802.1X (no WPA)") [[ ${status[2]} == "SUCCESS" ]] ;; + *) [[ ${status[1]} == "COMPLETED" ]] ;; + esac + + return $? +} + +# void wpa_supplicant_kill(char *interface, bool report) +# +# Kills any existing wpa_supplicant process on the interface +wpa_supplicant_kill() { + local iface="$1" report="${2:-false}" pidfile + + # Shutdown wpa_cli first, if it's running + # This is important as future versions of wpa_supplicant + # may send a disconnect message to wpa_cli when it shutsdown + pidfile="/var/run/wpa_cli-${iface}.pid" + if [[ -f ${pidfile} ]] ; then + ${report} && ebegin "Stopping wpa_cli on ${iface}" + start-stop-daemon --stop --exec /bin/wpa_cli --pidfile "${pidfile}" + ${report} && eend "$?" + fi + + # Now shutdown wpa_supplicant + pidfile="/var/run/wpa_supplicant-${iface}.pid" + if [[ -f ${pidfile} ]] ; then + ${report} && ebegin "Stopping wpa_supplicant on ${iface}" + start-stop-daemon --stop --exec /sbin/wpa_supplicant \ + --pidfile "${pidfile}" + ${report} && eend "$?" + else + # Support wpa_supplicant-0.3.x + local pid="$( pgrep -f "^/sbin/wpa_supplicant .* -i${iface}[ ]*$" )" + if [[ -n ${pid} ]] ; then + ${report} && ebegin "Stopping wpa_supplicant on ${iface}" + kill -s TERM "${pid}" + ${report} && eend 0 + fi + fi + + # If wpa_supplicant exits uncleanly, we need to remove the stale dir + [[ -S "/var/run/wpa_supplicant/${iface}" ]] \ + && rm -f "/var/run/wpa_supplicant/${iface}" +} + +# bool wpa_supplicant_associate(char *interface) +# +# Returns 0 if wpa_supplicant associates and authenticates to an AP +# otherwise, 1 +wpa_supplicant_associate() { + local iface="$1" ifvar="$(bash_variable "$1")" timeout + timeout="associate_timeout_${ifvar}" + [[ -z ${!timeout} ]] && timeout="wpa_timeout_${ifvar}" + timeout="${!timeout:--1}" + + if [[ ${timeout} == "0" ]] ; then + ewarn "WARNING: infinite timeout set for association on ${iface}" + elif [[ ${timeout} -lt 0 ]] ; then + einfo "Backgrounding ..." + exit 0 + fi + + local i=0 + while true ; do + if [[ -n ${actfile} ]] ; then + service_started "net.${iface}" && return 0 + else + if ! wpa_cli -i"${iface}" status &>/dev/null ; then + eend 1 "wpa_supplicant has exited unexpectedly" + return 1 + fi + wpa_supplicant_associated "${iface}" && return 0 + fi + sleep 1 + [[ ${timeout} == "0" ]] && continue + (( i++ )) + [[ ${i} == "${timeout}" || ${i} -gt "${timeout}" ]] && break + done + + # Spit out an appropriate error + if [[ -n ${actfile} ]] ; then + eend 1 "Failed to configure ${iface} in the background" + else + eend 1 "Timed out" + fi + + # exit without error with wpa_supplicant-0.4.x as we may get kickstarted + # when an AP comes in range + [[ -n ${actfile} ]] && exit 0 + + # Kill wpa_supplicant for 0.3.x + wpa_supplicant_kill "${iface}" + return 1 +} + +# bool wpa_supplicant_pre_start(char *interface) +# +# Start wpa_supplicant on an interface and wait for association +# Returns 0 (true) when successful, non-zero otherwise +wpa_supplicant_pre_start() { + local iface="$1" opts="" timeout="" actfile="" cfgfile="" + + # We don't configure wireless if we're being called from + # the background unless we're not currently running + if ${IN_BACKGROUND} ; then + if service_started_daemon "net.${iface}" /sbin/wpa_supplicant ; then + if wpa_supplicant_exists "${iface}" ; then + ESSID="$( wpa_supplicant_get_essid "${iface}" )" + ESSIDVAR="$( bash_variable "${ESSID}" )" + save_options "ESSID" "${ESSID}" + fi + return 0 + fi + fi + + save_options "ESSID" "" + + local ifvar="$( bash_variable "${iface}" )" + opts="wpa_supplicant_${ifvar}" + opts=" ${!opts} " + [[ ${opts} != *" -D"* ]] \ + && vewarn "wpa_supplicant_${ifvar} does not define a driver" + + # We only work on wirelesss interfaces unless a driver for wired + # has been defined + if [[ ${opts} != *" -Dwired "* && ${opts} != *" -D wired "* ]] ; then + if ! wpa_supplicant_exists "${iface}" ; then + veinfo "wpa_supplicant only works on wireless interfaces" + veinfo "unless the -D wired option is specified" + return 0 + fi + fi + + # Check for rf_kill - only ipw supports this at present, but other + # cards may in the future + if [[ -e "/sys/class/net/${iface}/device/rf_kill" ]] ; then + if [[ $( < "/sys/class/net/${iface}/device/rf_kill" ) != 0 ]] ; then + eerror "Wireless radio has been killed for interface ${iface}" + return 1 + fi + fi + + # If wireless-tools is installed, try and apply our user config + # This is needed for some drivers - such as hostap because they start + # the card in Master mode which causes problems with wpa_supplicant. + if is_function iwconfig_defaults ; then + if wpa_supplicant_exists "${iface}" ; then + iwconfig_defaults "${iface}" + iwconfig_user_config "${iface}" + fi + fi + + ebegin "Starting wpa_supplicant on ${iface}" + + cfgfile="${opts##* -c}" + if [[ -n ${cfgfile} && ${cfgfile} != "${opts}" ]] ; then + [[ ${cfgfile:0:1} == " " ]] && cfgfile="${cfgfile# *}" + cfgfile="${cfgfile%% *}" + else + # Support new and old style locations + cfgfile="/etc/wpa_supplicant/wpa_supplicant-${iface}.conf" + [[ ! -e ${cfgfile} ]] \ + && cfgfile="/etc/wpa_supplicant/wpa_supplicant.conf" + [[ ! -e ${cfgfile} ]] \ + && cfgfile="/etc/wpa_supplicant.conf" + opts="${opts} -c${cfgfile}" + fi + + if [[ ! -f ${cfgfile} ]] ; then + eend 1 "configuration file ${cfgfile} not found!" + return 1 + fi + + local ctrl_dir="$( sed -n -e 's/[ \t]*#.*//g;s/[ \t]*$//g;s/^ctrl_interface=//p' "${cfgfile}" )" + if [[ ${ctrl_dir} != "/var/run/wpa_supplicant" ]] ; then + eerror "${cfgfile} must set" + eerror " ctrl_interface=/var/run/wpa_supplicant" + eend 1 + return 1 + fi + + # Some drivers require the interface to be up + interface_up "${iface}" + + version="$( wpa_cli -v | sed -n -e 's/wpa_cli v//p' )" + version=( ${version//./ } ) + (( version = version[0] * 1000 + version[1] * 100 + version[2] )) + + # wpa_supplicant 0.4.0 and greater supports wpa_cli actions + # This is very handy as if and when different association mechanisms are + # introduced to wpa_supplicant we don't have to recode for them as + # wpa_cli is now responsible for informing us of success/failure. + # The downside of this is that we don't see the interface being configured + # for DHCP/static. + if [[ ${version} -gt 399 ]] ; then + opts="${opts} -W -P/var/run/wpa_supplicant-${iface}.pid" + actfile="/etc/wpa_supplicant/wpa_cli.sh" + # Support old file location + [[ ! -x ${actfile} ]] && actfile="/sbin/wpa_cli.action" + [[ ! -x ${actfile} ]] && unset actfile + fi + + eval start-stop-daemon --start --exec /sbin/wpa_supplicant \ + --pidfile "/var/run/wpa_supplicant-${iface}.pid" \ + -- "${opts}" -B -i"${iface}" + eend "$?" || return 1 + + # Starting wpa_supplication-0.4.0, we can get wpa_cli to + # start/stop our scripts from wpa_supplicant messages + if [[ -n ${actfile} ]] ; then + mark_service_inactive "net.${iface}" + ebegin "Starting wpa_cli on ${iface}" + start-stop-daemon --start --exec /bin/wpa_cli \ + --pidfile "/var/run/wpa_cli-${iface}.pid" \ + -- -a"${actfile}" -i"${iface}" \ + -P"/var/run/wpa_cli-${iface}.pid" -B + eend "$?" || return 1 + fi + + eindent + veinfo "Waiting for association" + eend 0 + + wpa_supplicant_associate "${iface}" || return 1 + + # Only report wireless info for wireless interfaces + if wpa_supplicant_exists "${iface}" ; then + # Set ESSID for essidnet and report + ESSID="$( wpa_supplicant_get_essid "${iface}" )" + ESSIDVAR="$( bash_variable "${ESSID}" )" + save_options "ESSID" "${ESSID}" + + local -a status + eval status=( $( wpa_cli -i"${iface}" status | sed -n -e 's/^\(bssid\|pairwise_cipher\|key_mgmt\)=\([^=]\+\).*/\"\U\2\"/p' | tr '[:lower:]' '[:upper:]' ) ) + einfo "${iface} connected to \"${ESSID//\\\\/\\\\}\" at ${status[0]}" + + if [[ ${status[2]} == "NONE" ]] ; then + if [[ ${status[1]} == "NONE" ]] ; then + ewarn "not using any encryption" + else + veinfo "using ${status[1]}" + fi + else + veinfo "using ${status[2]}/${status[1]}" + fi + eoutdent + else + einfo "${iface} connected" + fi + + if [[ -n ${actfile} ]] ; then + local addr="$( interface_get_address "${iface}" )" + einfo "${iface} configured with address ${addr}" + exit 0 + fi + + return 0 +} + +# bool wpa_supplicant_post_stop(char *iface) +# +# Stops wpa_supplicant on an interface +# Returns 0 (true) when successful, non-zero otherwise +wpa_supplicant_post_stop() { + if ${IN_BACKGROUND} ; then + # Only stop wpa_supplicant if it's not the controlling daemon + ! service_started_daemon "net.$1" /sbin/wpa_supplicant 0 + fi + [[ $? == 0 ]] && wpa_supplicant_kill "$1" true + return 0 +} + +# vim: set ft=sh ts=4 : |