[802.11] Bugfix and minor improvement potpourri
authorJoshua Oreman <oremanj@xenon.get-linux.org>
Tue, 9 Jun 2009 05:01:52 +0000 (22:01 -0700)
committerJoshua Oreman <oremanj@xenon.get-linux.org>
Tue, 9 Jun 2009 05:01:52 +0000 (22:01 -0700)
Bugs fixed:
- Call net80211_prepare_default() as soon as the device is opened,
  in case some unscrupulous code tries to transmit regardless of
  the fact that the link is down.
- If the association process is running when we're closed, kill it.
- Initialize the list head for the parallel queue of signal strengths
  of packets in the management queue.
- When advancing to the next info element in a management packet, we
  need to advance by ie->len + 2 bytes, not just ie->len. ie->len
  doesn't include the two bytes of header.
- If probe doesn't return anything, set an error code (ETIMEDOUT)
  instead of leaving it at "Error in association: no error".
- Set NET80211_WAITING state when we send an auth or assoc packet,
  so we don't try to keep processing while we're waiting for the
  reply.

Minor improvements made:
- Call the hidden-SSID setting "active-scan" instead of "hidden";
  it makes its ultimate purpose clearer, and there may be times
  people want an active scan without a hidden-SSID network around.
  (Beacons are usually transmitted with fairly low power; if the
  signal strength is poor, active scanning makes it easier to
  find the network.)
- Give the probe 4 seconds to find a better AP, up from 2. Hard
  timeout if it can't find anything at all is still at 6 seconds.
  I will probably adjust these more later.
- Probe where netX/ssid isn't set will pick up the network with the
  best signal strength around.
- Cleaned up the messages output a bit.
- Display 802.11 reason code when we receive a deauth or disassoc
  message, and don't try to reassociate - there's usually a deeper
  issue than "the AP decided it didn't like us".

src/net/net80211.c

index fe8c72d..161b9b0 100644 (file)
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <gpxe/net80211.h>
 #include <gpxe/timer.h>
 #include <gpxe/nap.h>
+#include <unistd.h>
 #include <errno.h>
 
 /** List of 802.11 devices */
@@ -54,9 +55,9 @@ struct setting net80211_ssid_setting __setting = {
  * active scan (send probe packets). If this setting is nonzero, an
  * active scan on the 2.4GHz band will be used to associate.
  */
-struct setting net80211_hidden_setting __setting = {
-       .name = "hidden",
-       .description = "Associate with 802.11 hidden network",
+struct setting net80211_active_setting __setting = {
+       .name = "active-scan",
+       .description = "Use an active scan during 802.11 association",
        .type = &setting_type_int8,
 };
 
@@ -78,9 +79,15 @@ struct net80211_rx_info {
 int net80211_netdev_open ( struct net_device *netdev )
 {
        struct net80211_device *dev = netdev->priv;
+       int rc = 0;
 
        if ( dev->op->open )
-               return dev->op->open ( dev );
+               rc = dev->op->open ( dev );
+
+       /* In case someone tries to transmit before we set link-up, we
+          need to at least be in a consistent enough state not to
+          crash. */
+       net80211_prepare_default ( dev, dev->hw->bands, 0 );
 
        net80211_autoassociate ( dev );
        return 0;
@@ -90,6 +97,9 @@ void net80211_netdev_close ( struct net_device *netdev )
 {
        struct net80211_device *dev = netdev->priv;
 
+       if ( dev->state & NET80211_WORKING )
+               process_del ( &dev->proc_assoc );
+
        if ( dev->op->close )
                dev->op->close ( dev );
 }
@@ -98,6 +108,7 @@ int net80211_netdev_transmit ( struct net_device *netdev,
                               struct io_buffer *iobuf )
 {
        struct net80211_device *dev = netdev->priv;
+       int rc = -ENOSYS;
 
        if ( dev->crypto ) {
                struct io_buffer *niob = dev->crypto->encrypt ( dev, iobuf );
@@ -109,8 +120,9 @@ int net80211_netdev_transmit ( struct net_device *netdev,
        }
 
        if ( dev->op->transmit )
-               return dev->op->transmit ( dev, iobuf );
-       return -ENOSYS;
+               rc = dev->op->transmit ( dev, iobuf );
+
+       return rc;
 }
 
 void net80211_netdev_poll ( struct net_device *netdev )
@@ -367,6 +379,7 @@ struct net80211_device * net80211_alloc ( size_t priv_size )
 
        dev->proc_assoc.step = net80211_step_associate;
        INIT_LIST_HEAD ( &dev->mgmt_queue );
+       INIT_LIST_HEAD ( &dev->mgmt_info_queue );
 
        return dev;
 }
@@ -642,7 +655,7 @@ net80211_marshal_request_info ( struct net80211_device *dev,
        ie->len = strlen ( dev->essid );
        memcpy ( ie->ssid, dev->essid, ie->len );
 
-       ie_byte += ie->len;
+       ie_byte += ie->len + 2;
        ie = ie_byte;
 
        ie->id = IEEE80211_IE_RATES;
@@ -653,7 +666,7 @@ net80211_marshal_request_info ( struct net80211_device *dev,
                ie->rates[i] = dev->hw->supported_rates[i] / 5;
        }
 
-       ie_byte += ie->len;
+       ie_byte += ie->len + 2;
        ie = ie_byte;
 
        if ( i < dev->hw->nr_supported_rates ) {
@@ -663,14 +676,14 @@ net80211_marshal_request_info ( struct net80211_device *dev,
                        ie->rates[i - 8] = dev->hw->supported_rates[i] / 5;
                }
 
-               ie_byte += ie->len;
+               ie_byte += ie->len + 2;
                ie = ie_byte;
        }
 
        return ie;
 }
 
-#define NET80211_PROBE_GATHER    2 /* seconds to always take */
+#define NET80211_PROBE_GATHER    4 /* seconds to always take */
 #define NET80211_PROBE_TIMEOUT   6 /* seconds to take at maximum */
 
 struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
@@ -774,7 +787,7 @@ struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
                ie = beacon->info_element;
                ie_byte = ie;
                while ( ie_byte < iob->tail && ie->id != IEEE80211_IE_SSID ) {
-                       ie_byte += ie->len;
+                       ie_byte += ie->len + 2;
                        ie = ie_byte;
                }
                if ( ie_byte >= iob->tail ) {
@@ -782,7 +795,7 @@ struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
                        DBG2 ( "802.11 probe: beacon with no SSID\n" );
                        goto drop;
                }
-               if ( memcmp ( essid, ie->ssid, ie->len ) != 0 ) {
+               if ( essid[0] && memcmp ( essid, ie->ssid, ie->len ) != 0 ) {
                        ie->ssid[ie->len] = 0;
                        DBG2 ( "802.11 probe: beacon with wrong SSID (%s)\n",
                               ie->ssid );
@@ -855,23 +868,24 @@ void net80211_step_associate ( struct process *proc )
 
        if ( ! dev->associating ) {
                /* state: scan */
-               int hidden = fetch_intz_setting ( NULL,
-                                                 &net80211_hidden_setting );
+               int active = fetch_intz_setting ( NULL,
+                                                 &net80211_active_setting );
                int band = dev->hw->bands;
 
-               if ( hidden )
+               if ( active )
                        band &= ~NET80211_BAND_5GHZ;
 
-               rc = net80211_prepare_default ( dev, band, hidden );
+               rc = net80211_prepare_default ( dev, band, active );
                if ( rc )
                        goto fail;
 
                dev->associating = net80211_probe ( dev, dev->essid, hidden );
-               if ( ! dev->associating )
+               if ( ! dev->associating ) {
+                       rc = -ETIMEDOUT;
                        goto fail;
+               }
 
-               DBG ( "802.11 found network %s (%s)\n",
-                     dev->associating->essid,
+               DBG ( "802.11 found network %s (%s)\n", dev->associating->essid,
                      eth_ntoa ( dev->associating->bssid ) );
 
                dev->associating->method = IEEE80211_AUTH_OPEN_SYSTEM;
@@ -934,14 +948,15 @@ void net80211_step_associate ( struct process *proc )
        /* state: done! */
        dev->netdev->state |= NETDEV_LINK_UP;
        dev->state &= ~NET80211_WORKING;
-       printf ( "[associated to %s] ", dev->essid );
+       printf ( "[802.11 associated with %s]\n", dev->essid );
        process_del ( proc );
+       return;
 
  fail:
+       dev->state &= ~NET80211_WORKING;
        DBG ( "802.11 association process failed with state=%04x "
              "rc=%08x\n", dev->state, rc );
-       printf ( "association error: %s\n", strerror ( rc ) );
-       dev->state &= ~NET80211_WORKING;
+       printf ( "[802.11 association error: %s]\n", strerror ( rc ) );
        process_del ( proc );
 }
 
@@ -949,8 +964,11 @@ void net80211_autoassociate ( struct net80211_device *dev )
 {
        int len;
 
-       if ( ! ( dev->state & NET80211_WORKING ) )
+       if ( ! ( dev->state & NET80211_WORKING ) ) {
+               DBG2 ( "802.11 spawning association process\n" );
                process_add ( &dev->proc_assoc );
+       }
+       
        if ( dev->associating )
                net80211_free_wlan ( dev->associating );
 
@@ -1061,6 +1079,7 @@ int net80211_send_auth ( struct net80211_device *dev,
        struct io_buffer *iob = alloc_iob ( 64 );
        struct ieee80211_auth *auth;
 
+       net80211_set_state ( dev, 0, NET80211_WAITING, 0 );
        iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
        auth = iob_put ( iob, sizeof ( *auth ) );
        auth->algorithm = method;
@@ -1131,6 +1150,8 @@ int net80211_send_assoc ( struct net80211_device *dev,
        struct ieee80211_ie *ie;
        void *ie_byte;
 
+       net80211_set_state ( dev, 0, NET80211_WAITING, 0 );
+
        iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
        assoc = iob->data;
 
@@ -1186,6 +1207,7 @@ static void net80211_handle_mgmt ( struct net80211_device *dev,
                                   struct io_buffer *iob, int signal )
 {
        struct ieee80211_frame *hdr = iob->data;
+       struct ieee80211_disassoc *disassoc;
        u16 stype = hdr->fc & IEEE80211_FC_SUBTYPE;
        int keep = 0;
 
@@ -1195,15 +1217,19 @@ static void net80211_handle_mgmt ( struct net80211_device *dev,
        }
 
        switch ( stype ) {
+               /* These are usually indicative of a deeper problem,
+                  so don't just reassociate right away. */
        case IEEE80211_STYPE_DEAUTH:
                net80211_set_state ( dev, NET80211_AUTHENTICATED, 0, 0 );
-               DBG ( "%s: 802.11 deauthenticated\n", dev->netdev->name );
-               net80211_autoassociate ( dev );
+               disassoc = ( struct ieee80211_disassoc * ) hdr->data;
+               DBG ( "%s: 802.11 deauthenticated: reason %d\n",
+                     dev->netdev->name, disassoc->reason );
                break;
        case IEEE80211_STYPE_DISASSOC:
                net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 );
-               DBG ( "%s: 802.11 disassociated\n", dev->netdev->name );
-               net80211_autoassociate ( dev );
+               disassoc = ( struct ieee80211_disassoc * ) hdr->data;
+               DBG ( "%s: 802.11 disassociated: reason %d\n",
+                     dev->netdev->name, disassoc->reason );
                break;
 
                /* We handle authentication and association. */