#!/bin/bash # This script stops shill, associates with each test AP in turn, # and harvests signal strength and quality numbers for us, then restarts # shill so the old network can be reacquired and our test can complete set +o posix; shopt -s extglob; oldstderr=; stderrlog=; output_to_vtx () { # copy stderr to the next free vt so the user can see progress # on the DUT, otherwise they're just sitting there with no feedback # (if we can, that is - if openvt isn't there, just get on with it) if which openvt; then stderrlog=/tmp/$RANDOM.vtx.log; exec {oldstderr}>&2; exec 2>$stderrlog; tail --pid $$ -f $stderrlog >&$oldstderr & openvt -s -w -- tail --pid $$ -f $stderrlog & fi } close_vtx () { if [[ -f "$stderrlog" ]]; then rm "$stderrlog"; fi; } progress () { echo "$@" 1>&2; } contains_modulations () { # check that at least one modulation in `wanted' is present in `supported' supported=$1; wanted=$2; case $supported in *[$wanted]*) return 0; ;; esac return 1; } # pick a WiFi interface to test find_wifi_if () { iface="$1"; if [[ -z "$iface" ]]; then while read _iface _ignore && test -z "$iface"; do iface=$_iface; done < <(iwconfig 2>/dev/null | grep '^[a-z]'); fi; test -n "$iface"; } wifi_status () { # harvest the state of the target interface: modulation, essid and so forth: find_wifi_if "$1"; if_80211=; if_essid=; if_mode=; if_ap=; if_rate=; if_txp=; if_quality=; if_signal=; if_freq=; # iwconfig's output is a pain to parse, but is stable. # the newer tools are much easier to parse, but they are # considered unstable by the authors, who specifically forbid # scraping their output until the specification stabilises. while read data; do case "$data" in $iface*) if_essid=${data##*ESSID:*(\")}; if_essid=${if_essid%\"*}; if_80211=${data%%+( )ESSID:*}; if_80211=${if_80211#*802.11}; ;; Mode:*) if_mode=${data#Mode:} if_mode=${if_mode%% *}; if_ap=${data##*Access Point: }; if_ap=${if_ap%% *}; if_freq=${data##*Frequency:}; if_freq=${if_freq%%+( )Access Point:*}; if [[ "$if_ap" = "Not-Associated" ]]; then if_txp=${data##*Tx-Power=}; fi ;; Bit\ Rate*) if_rate=${data%%+( )Tx-*}; if_rate=${if_rate#Bit Rate=}; if [[ -z $"if_txp" ]]; then if_txp=${data##*Tx-Power=}; fi; ;; Link*) if_quality=${data%%+( )Signal*}; if_quality=${if_quality#Link Quality=}; if_signal=${data##*Signal level=}; ;; esac; done < <(iwconfig $iface) } wifi_scan () { iface=$1; # Trigger a wifi scan. The DUT doesn't necessarily scan all frequencies # or remember APs on frequencies it isn't currently on, so we need to do # this at times to make sure we can interact with the test AP: progress Bringing up $iface; ifconfig $iface up 1>&2; progress Scanning for CrOS test ESSIDs; lofreq_aps=""; hifreq_aps=""; cell_freq=; cell_essid=; while read scan; do if [[ -z "$cell_freq" || -z "$cell_essid" ]]; then case "$scan" in Frequency:*) cell_freq=${scan##*Frequency:} cell_freq=${cell_freq%% *}; cell_freq=${cell_freq:-0}; ;; ESSID:*) cell_essid=${scan#*ESSID:\"}; cell_essid=${cell_essid%\"*}; esac; else if [[ "${cell_essid#CrOS-test-}" != "$cell_essid" ]]; then progress "Found test ESSID $cell_essid (Frequency: $cell_freq)"; case "$cell_freq" in 2*) lofreq_aps=${lofreq_aps}${lofreq_aps:+ }${cell_essid}; ;; [45]*) hifreq_aps=${hifreq_aps}${hifreq_aps:+ }${cell_essid}; ;; esac; else progress "Ignoring ESSID $cell_essid (Frequency: $cell_freq)"; fi; cell_essid=""; cell_freq=""; fi; done < <(iwlist $iface scan); } wifi_find_essid () { iface=$1; target=$2; progress Bringing up $iface; ifconfig $iface up 1>&2; progress Scanning for ESSID $target; iwlist $iface scan | grep -q "ESSID:\"$target\""; } wifi_strength () { iface=$1; result=; macaddr=$(cat /sys/class/net/$iface/address) gateway=$(ip route show dev $iface to match 0/0|\ if read x x g x; then echo $g; fi); progress Allowing link $gateway strength/quality readings to stabilise; ping -n -w 5 -c 5 $gateway 1>&2; progress Contacting AP at "/dev/tcp/$gateway/80" to collect TX strength; if exec {http}<>/dev/tcp/$gateway/80; then echo -e "GET /cgi-bin/txinfo HTTP/1.0\r\n\r" >&$http; while read mac strength other; do if [[ x${mac,,*} = x${macaddr,,*} ]]; then result=$strength; fi; done <&$http; fi; tx_db=${result:--100}" dBm"; } wifi_associate () { wifi_status $iface; essid=${2:-"NO-ESSID-SUPPLIED"}; if wifi_find_essid $iface $essid; then SECONDS=0; iwconfig $iface essid "$essid" 1>&2; until [[ x$if_essid = x$essid && x$if_ap != x'Not-Associated' ]]; do wifi_status $iface; progress "$SECONDS: $if_essid/$if_ap (want $essid)"; sleep 2; if [[ $SECONDS -ge 30 ]]; then if_ap=failed; fi; done; else if_ap="Not-Found"; fi test "$if_essid" = "$essid"; } wifi_dhcp () { iface=$1; dhclient $iface \ -sf /usr/local/sbin/dhclient-script \ -lf /tmp/dhclient.leases 1>&2; } emit_result () { test=$2; if [[ "$1" = success ]]; then cat - <<EOF; 802.11$test freq $if_freq quality $if_quality rx $if_signal tx $tx_db EOF else cat - <<EOF; 802.11$test freq 0 quality 0/70 rx -100 dBm tx -100 dBm EOF fi; } test_association () { wlan_if=$1; ap_ssid=$2; mods=$3; if wifi_associate $wlan_if $ap_ssid; then wifi_dhcp $wlan_if; wifi_strength $wlan_if; emit_result success $mods; else progress "WiFi Association failed for $wlan_if [$ap_ssid vs $if_ap]"; emit_result failure $mods; fi; } output_to_vtx; wifi_status $1; # this will figure out all our initial if_… values modulations=$if_80211; progress "Start: $iface ($if_mode/$if_80211) ap $if_ap essid '$if_essid'"; progress "Shutting down shill"; stop shill 1>&2 progress "Looking for test APs"; wifi_scan $iface; progress "2.x GHz APs: $lofreq_aps"; progress "4+ GHz APs: $hifreq_aps"; if contains_modulations $modulations bg; then for ap in $lofreq_aps; do test_association $iface $ap bg; done; fi if contains_modulations $modulations an; then for ap in $hifreq_aps; do test_association $iface $ap an; done; fi start shill 1>&2; close_vtx;