1#!/bin/bash 2 3# This script stops shill, associates with each test AP in turn, 4# and harvests signal strength and quality numbers for us, then restarts 5# shill so the old network can be reacquired and our test can complete 6 7set +o posix; 8shopt -s extglob; 9 10oldstderr=; 11stderrlog=; 12 13output_to_vtx () { 14 # copy stderr to the next free vt so the user can see progress 15 # on the DUT, otherwise they're just sitting there with no feedback 16 # (if we can, that is - if openvt isn't there, just get on with it) 17 if which openvt; 18 then 19 stderrlog=/tmp/$RANDOM.vtx.log; 20 exec {oldstderr}>&2; 21 exec 2>$stderrlog; 22 tail --pid $$ -f $stderrlog >&$oldstderr & 23 openvt -s -w -- tail --pid $$ -f $stderrlog & 24 fi 25} 26 27close_vtx () { 28 if [[ -f "$stderrlog" ]]; then 29 rm "$stderrlog"; 30 fi; 31} 32 33progress () { echo "$@" 1>&2; } 34 35contains_modulations () { 36 # check that at least one modulation in `wanted' is present in `supported' 37 supported=$1; 38 wanted=$2; 39 40 case $supported in 41 *[$wanted]*) 42 return 0; 43 ;; 44 esac 45 46 return 1; 47} 48 49# pick a WiFi interface to test 50find_wifi_if () { 51 iface="$1"; 52 53 if [[ -z "$iface" ]]; then 54 while read _iface _ignore && test -z "$iface"; do 55 iface=$_iface; 56 done < <(iwconfig 2>/dev/null | grep '^[a-z]'); 57 fi; 58 59 test -n "$iface"; 60} 61 62wifi_status () { 63 # harvest the state of the target interface: modulation, essid and so forth: 64 find_wifi_if "$1"; 65 66 if_80211=; 67 if_essid=; 68 if_mode=; 69 if_ap=; 70 if_rate=; 71 if_txp=; 72 if_quality=; 73 if_signal=; 74 if_freq=; 75 76 # iwconfig's output is a pain to parse, but is stable. 77 # the newer tools are much easier to parse, but they are 78 # considered unstable by the authors, who specifically forbid 79 # scraping their output until the specification stabilises. 80 while read data; do 81 case "$data" in 82 $iface*) 83 if_essid=${data##*ESSID:*(\")}; 84 if_essid=${if_essid%\"*}; 85 if_80211=${data%%+( )ESSID:*}; 86 if_80211=${if_80211#*802.11}; 87 ;; 88 Mode:*) 89 if_mode=${data#Mode:} 90 if_mode=${if_mode%% *}; 91 if_ap=${data##*Access Point: }; 92 if_ap=${if_ap%% *}; 93 if_freq=${data##*Frequency:}; 94 if_freq=${if_freq%%+( )Access Point:*}; 95 if [[ "$if_ap" = "Not-Associated" ]]; then 96 if_txp=${data##*Tx-Power=}; 97 fi 98 ;; 99 Bit\ Rate*) 100 if_rate=${data%%+( )Tx-*}; 101 if_rate=${if_rate#Bit Rate=}; 102 if [[ -z $"if_txp" ]]; then 103 if_txp=${data##*Tx-Power=}; 104 fi; 105 ;; 106 Link*) 107 if_quality=${data%%+( )Signal*}; 108 if_quality=${if_quality#Link Quality=}; 109 if_signal=${data##*Signal level=}; 110 ;; 111 esac; 112 done < <(iwconfig $iface) 113} 114 115wifi_scan () { 116 iface=$1; 117 118 # Trigger a wifi scan. The DUT doesn't necessarily scan all frequencies 119 # or remember APs on frequencies it isn't currently on, so we need to do 120 # this at times to make sure we can interact with the test AP: 121 progress Bringing up $iface; 122 ifconfig $iface up 1>&2; 123 progress Scanning for CrOS test ESSIDs; 124 125 lofreq_aps=""; 126 hifreq_aps=""; 127 128 cell_freq=; 129 cell_essid=; 130 131 while read scan; do 132 if [[ -z "$cell_freq" || -z "$cell_essid" ]]; then 133 case "$scan" in 134 Frequency:*) 135 cell_freq=${scan##*Frequency:} 136 cell_freq=${cell_freq%% *}; 137 cell_freq=${cell_freq:-0}; 138 ;; 139 ESSID:*) 140 cell_essid=${scan#*ESSID:\"}; 141 cell_essid=${cell_essid%\"*}; 142 esac; 143 else 144 if [[ "${cell_essid#CrOS-test-}" != "$cell_essid" ]]; then 145 progress "Found test ESSID $cell_essid (Frequency: $cell_freq)"; 146 case "$cell_freq" in 147 2*) 148 lofreq_aps=${lofreq_aps}${lofreq_aps:+ }${cell_essid}; 149 ;; 150 [45]*) 151 hifreq_aps=${hifreq_aps}${hifreq_aps:+ }${cell_essid}; 152 ;; 153 esac; 154 else 155 progress "Ignoring ESSID $cell_essid (Frequency: $cell_freq)"; 156 fi; 157 cell_essid=""; 158 cell_freq=""; 159 fi; 160 done < <(iwlist $iface scan); 161} 162 163wifi_find_essid () { 164 iface=$1; 165 target=$2; 166 167 progress Bringing up $iface; 168 ifconfig $iface up 1>&2; 169 progress Scanning for ESSID $target; 170 iwlist $iface scan | grep -q "ESSID:\"$target\""; 171} 172 173wifi_strength () { 174 iface=$1; 175 result=; 176 macaddr=$(cat /sys/class/net/$iface/address) 177 gateway=$(ip route show dev $iface to match 0/0|\ 178 if read x x g x; then echo $g; fi); 179 180 progress Allowing link $gateway strength/quality readings to stabilise; 181 ping -n -w 5 -c 5 $gateway 1>&2; 182 183 progress Contacting AP at "/dev/tcp/$gateway/80" to collect TX strength; 184 if exec {http}<>/dev/tcp/$gateway/80; then 185 echo -e "GET /cgi-bin/txinfo HTTP/1.0\r\n\r" >&$http; 186 187 while read mac strength other; 188 do 189 if [[ x${mac,,*} = x${macaddr,,*} ]]; then result=$strength; fi; 190 done <&$http; 191 fi; 192 193 tx_db=${result:--100}" dBm"; 194} 195 196wifi_associate () { 197 wifi_status $iface; 198 199 essid=${2:-"NO-ESSID-SUPPLIED"}; 200 201 if wifi_find_essid $iface $essid; then 202 SECONDS=0; 203 iwconfig $iface essid "$essid" 1>&2; 204 205 until [[ x$if_essid = x$essid && x$if_ap != x'Not-Associated' ]]; do 206 wifi_status $iface; 207 progress "$SECONDS: $if_essid/$if_ap (want $essid)"; 208 sleep 2; 209 if [[ $SECONDS -ge 30 ]]; then if_ap=failed; fi; 210 done; 211 else 212 if_ap="Not-Found"; 213 fi 214 215 test "$if_essid" = "$essid"; 216} 217 218wifi_dhcp () { 219 iface=$1; 220 dhclient $iface \ 221 -sf /usr/local/sbin/dhclient-script \ 222 -lf /tmp/dhclient.leases 1>&2; 223} 224 225emit_result () { 226 test=$2; 227 228 if [[ "$1" = success ]]; then 229 230 cat - <<EOF; 231802.11$test freq $if_freq quality $if_quality rx $if_signal tx $tx_db 232EOF 233 234 else 235 236 cat - <<EOF; 237802.11$test freq 0 quality 0/70 rx -100 dBm tx -100 dBm 238EOF 239 240 fi; 241} 242 243test_association () { 244 wlan_if=$1; 245 ap_ssid=$2; 246 mods=$3; 247 248 if wifi_associate $wlan_if $ap_ssid; then 249 wifi_dhcp $wlan_if; 250 wifi_strength $wlan_if; 251 emit_result success $mods; 252 else 253 progress "WiFi Association failed for $wlan_if [$ap_ssid vs $if_ap]"; 254 emit_result failure $mods; 255 fi; 256} 257 258output_to_vtx; 259 260wifi_status $1; # this will figure out all our initial if_… values 261modulations=$if_80211; 262 263progress "Start: $iface ($if_mode/$if_80211) ap $if_ap essid '$if_essid'"; 264progress "Shutting down shill"; 265stop shill 1>&2 266 267progress "Looking for test APs"; 268wifi_scan $iface; 269 270progress "2.x GHz APs: $lofreq_aps"; 271progress "4+ GHz APs: $hifreq_aps"; 272 273if contains_modulations $modulations bg; then 274 for ap in $lofreq_aps; do test_association $iface $ap bg; done; 275fi 276 277if contains_modulations $modulations an; then 278 for ap in $hifreq_aps; do test_association $iface $ap an; done; 279fi 280 281start shill 1>&2; 282 283close_vtx; 284