1 /* 2 * Copyright (C) 2009 Joshua Oreman <oremanj (at) rwcr.net>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 FILE_LICENCE ( GPL2_OR_LATER ); 20 21 #include <stdio.h> 22 #include <console.h> 23 #include <string.h> 24 #include <errno.h> 25 #include <gpxe/net80211.h> 26 #include <gpxe/ethernet.h> 27 #include <usr/ifmgmt.h> 28 #include <usr/iwmgmt.h> 29 #include <gpxe/errortab.h> 30 31 /** @file 32 * 33 * Wireless network interface management 34 * 35 */ 36 37 /** 38 * Print status of 802.11 device 39 * 40 * @v dev 802.11 device 41 */ 42 void iwstat ( struct net80211_device *dev ) { 43 44 ifstat ( dev->netdev ); 45 46 printf ( " [802.11 "); 47 if ( dev->state & NET80211_ASSOCIATED ) { 48 printf ( "SSID '%s', ", dev->essid ); 49 } else { 50 printf ( "not associated, " ); 51 } 52 if ( dev->channel < dev->nr_channels && dev->rate < dev->nr_rates ) { 53 printf ( "Ch:%d Sig:%d", dev->channels[dev->channel].channel_nr, 54 dev->last_signal ); 55 switch ( dev->hw->signal_type ) { 56 case NET80211_SIGNAL_NONE: 57 printf ( "?" ); 58 break; 59 case NET80211_SIGNAL_ARBITRARY: 60 printf ( "/%d", dev->hw->signal_max ); 61 break; 62 case NET80211_SIGNAL_DB: 63 printf ( "/%d dB", dev->hw->signal_max ); 64 break; 65 case NET80211_SIGNAL_DBM: 66 printf ( " dBm" ); 67 break; 68 } 69 printf ( ", Qual:%d%% Rate:%d Mbps]\n", 70 ( dev->rx_beacon_interval == 0 ? 0 : 71 100 * dev->tx_beacon_interval / 72 dev->rx_beacon_interval ), 73 dev->rates[dev->rate] / 10 ); 74 } else { 75 printf ( "antenna off]\n" ); 76 } 77 78 if ( dev->state & NET80211_WORKING ) { 79 printf ( " [associating" ); 80 if ( dev->associating ) 81 printf ( " to '%s'", dev->associating->essid ); 82 printf ( "...]\n" ); 83 } 84 } 85 86 /** Identifiers for 802.11 cryptography types, indexed by type number */ 87 static const char *crypto_types[] = { 88 [NET80211_CRYPT_NONE] = "Open", 89 [NET80211_CRYPT_WEP] = "WEP ", 90 [NET80211_CRYPT_TKIP] = "WPA ", 91 [NET80211_CRYPT_CCMP] = "WPA2", 92 [NET80211_CRYPT_UNKNOWN] = "UNK ", 93 }; 94 95 /** Number of 802.11 cryptography types defined */ 96 #define NR_CRYPTO_TYPES ( sizeof ( crypto_types ) / sizeof ( crypto_types[0] ) ) 97 98 /** Identifiers for 802.11 authentication types, indexed by type number */ 99 static const char *auth_types[] = { 100 [NET80211_SECPROT_NONE] = "", 101 [NET80211_SECPROT_PSK] = "PSK", 102 [NET80211_SECPROT_EAP] = "802.1X", 103 [NET80211_SECPROT_UNKNOWN] = "UNK", 104 }; 105 106 /** Number of 802.11 authentication types defined */ 107 #define NR_AUTH_TYPES ( sizeof ( auth_types ) / sizeof ( auth_types[0] ) ) 108 109 /** 110 * Scan for wireless networks using 802.11 device 111 * 112 * @v dev 802.11 device 113 * @v active Whether to use active scanning 114 * 115 * The list of networks found will be printed in tabular format. 116 * 117 * This function is safe to call at all times, whether the 802.11 118 * device is open or not, but if called while the auto-association 119 * task is running it will return an error indication. 120 */ 121 int iwlist ( struct net80211_device *dev ) { 122 struct net80211_probe_ctx *ctx; 123 struct list_head *networks; 124 struct net80211_wlan *wlan; 125 char ssid_buf[22]; 126 int rc; 127 unsigned i; 128 int was_opened = dev->netdev->state & NETDEV_OPEN; 129 int was_channel = dev->channels[dev->channel].channel_nr; 130 131 if ( ! was_opened ) { 132 dev->state |= NET80211_NO_ASSOC; 133 rc = netdev_open ( dev->netdev ); 134 if ( rc < 0 ) 135 goto err; 136 } 137 138 if ( dev->state & NET80211_WORKING ) { 139 rc = -EINVAL; 140 goto err_close_netdev; 141 } 142 143 if ( ! was_opened ) { 144 rc = net80211_prepare_probe ( dev, dev->hw->bands, 0 ); 145 if ( rc < 0 ) 146 goto err_close_netdev; 147 } 148 149 ctx = net80211_probe_start ( dev, "", 0 ); 150 if ( ! ctx ) { 151 rc = -ENOMEM; 152 goto err_close_netdev; 153 } 154 155 while ( ! ( rc = net80211_probe_step ( ctx ) ) ) { 156 step(); 157 } 158 159 networks = net80211_probe_finish_all ( ctx ); 160 161 if ( list_empty ( networks ) ) { 162 goto err_free_networks; 163 } 164 165 rc = 0; 166 167 printf ( "Networks on %s:\n\n", dev->netdev->name ); 168 169 /* Output format: 170 * 0 1 2 3 4 5 6 171 * 0123456789012345678901234567890123456789012345678901234567890 172 * [Sig] SSID BSSID Ch Crypt/Auth 173 * ------------------------------------------------------------- 174 * [ 15] abcdefghijklmnopqrst> 00:00:00:00:00:00 11 Open 175 * ... or WPA PSK etc. 176 */ 177 178 /* Quoting the dashes and spaces verbatim uses less code space 179 than generating them programmatically. */ 180 printf ( "[Sig] SSID BSSID Ch Crypt/Auth\n" 181 "-------------------------------------------------------------\n" ); 182 183 list_for_each_entry ( wlan, networks, list ) { 184 185 /* Format SSID into 22-character string, space-padded, 186 with '>' indicating truncation */ 187 188 snprintf ( ssid_buf, sizeof ( ssid_buf ), "%s", wlan->essid ); 189 for ( i = strlen ( ssid_buf ); i < sizeof ( ssid_buf ) - 1; 190 i++ ) 191 ssid_buf[i] = ' '; 192 if ( ssid_buf[sizeof ( ssid_buf ) - 2] != ' ' ) 193 ssid_buf[sizeof ( ssid_buf ) - 2] = '>'; 194 ssid_buf[sizeof ( ssid_buf ) - 1] = 0; 195 196 /* Sanity check */ 197 if ( wlan->crypto >= NR_CRYPTO_TYPES || 198 wlan->handshaking >= NR_AUTH_TYPES ) 199 continue; 200 201 printf ( "[%3d] %s %s %2d %s %s\n", 202 wlan->signal < 0 ? 100 + wlan->signal : wlan->signal, 203 ssid_buf, eth_ntoa ( wlan->bssid ), wlan->channel, 204 crypto_types[wlan->crypto], 205 auth_types[wlan->handshaking] ); 206 } 207 printf ( "\n" ); 208 209 err_free_networks: 210 net80211_free_wlanlist ( networks ); 211 212 err_close_netdev: 213 if ( ! was_opened ) { 214 dev->state &= ~NET80211_NO_ASSOC; 215 netdev_close ( dev->netdev ); 216 } else { 217 net80211_change_channel ( dev, was_channel ); 218 } 219 220 if ( ! rc ) 221 return 0; 222 223 err: 224 printf ( "Scanning for networks on %s: %s\n", 225 dev->netdev->name, strerror ( rc ) ); 226 return rc; 227 } 228 229 230 /* Record error codes as though they come from the 802.11 stack */ 231 #undef ERRFILE 232 #define ERRFILE ERRFILE_net80211 233 234 /** Common 802.11 errors */ 235 struct errortab common_wireless_errors[] __errortab = { 236 { EINVAL | EUNIQ_06, "Packet decryption error" }, 237 { ECONNRESET | EUNIQ_01, "Unspecified reason" }, 238 { ECONNRESET | EUNIQ_04, "Disassociated due to inactivity" }, 239 { ECONNRESET | EUNIQ_0F, "4-Way Handshake timeout" }, 240 { ECONNRESET | EUNIQ_17, "IEEE 802.1X authentication failed" }, 241 { ECONNREFUSED | EUNIQ_01, "Unspecified failure" }, 242 { ECONNREFUSED | EUNIQ_0C, "Association denied" }, 243 { ECONNREFUSED | EUNIQ_0D, "Authentication method not supported" }, 244 }; 245