iPXE
Functions
802.11 association handling functions

Functions

static void net80211_step_associate (struct net80211_device *dev)
 Step 802.11 association process.
static void net80211_handle_auth (struct net80211_device *dev, struct io_buffer *iob)
 Handle receipt of 802.11 authentication frame.
static void net80211_handle_assoc_reply (struct net80211_device *dev, struct io_buffer *iob)
 Handle receipt of 802.11 association reply frame.
static int net80211_send_disassoc (struct net80211_device *dev, int reason, int deauth)
 Send 802.11 disassociation frame.
static void net80211_handle_mgmt (struct net80211_device *dev, struct io_buffer *iob, int signal)
 Handle receipt of 802.11 management frame.

Function Documentation

static void net80211_step_associate ( struct net80211_device dev) [static]

Step 802.11 association process.

Parameters:
dev802.11 device

Definition at line 1646 of file net80211.c.

References net80211_device::assoc, net80211_device::assoc_rc, ASSOC_RETRIES, ASSOC_TIMEOUT, net80211_device::associating, net80211_hw_info::bands, net80211_device::bssid, net80211_wlan::bssid, net80211_device::ctx, currticks(), DBGC, ENOMEM, net80211_device::essid, net80211_wlan::essid, eth_ntoa(), ETIMEDOUT, fetch_intz_setting(), free, net80211_device::handshaker, net80211_device::hw, IEEE80211_AUTH_OPEN_SYSTEM, IEEE80211_AUTH_SHARED_KEY, IEEE80211_STATUS_AUTH_ALGO_UNSUPP, IEEE80211_STATUS_AUTH_CHALL_INVALID, IEEE80211_STATUS_SUCCESS, net80211_assoc_ctx::last_packet, net80211_assoc_ctx::method, method, NET80211_ASSOCIATED, NET80211_AUTHENTICATED, NET80211_AUTO_SSID, net80211_autoassociate(), NET80211_BAND_BIT_5GHZ, NET80211_CRYPTO_SYNCED, net80211_free_wlan(), net80211_prepare_assoc(), net80211_prepare_probe(), net80211_probe_finish_best(), net80211_probe_start(), net80211_probe_step(), NET80211_PROBED, net80211_send_assoc(), net80211_send_auth(), NET80211_STATUS_MASK, NET80211_WAITING, NET80211_WORKING, net80211_device::netdev, netdev_link_err(), netdev_link_up(), NULL, net80211_device::probe, net80211_device::proc_assoc, process_del(), rc, rc80211_init(), net80211_device::rctl, net80211_handshaker::start, net80211_handshaker::started, net80211_device::state, status, net80211_handshaker::step, strerror(), net80211_assoc_ctx::times_tried, and zalloc().

{
        int rc = 0;
        int status = dev->state & NET80211_STATUS_MASK;

        /*
         * We use a sort of state machine implemented using bits in
         * the dev->state variable. At each call, we take the
         * logically first step that has not yet succeeded; either it
         * has not been tried yet, it's being retried, or it failed.
         * If it failed, we return an error indication; otherwise we
         * perform the step. If it succeeds, RX handling code will set
         * the appropriate status bit for us.
         *
         * Probe works a bit differently, since we have to step it
         * on every call instead of waiting for a packet to arrive
         * that will set the completion bit for us.
         */

        /* If we're waiting for a reply, check for timeout condition */
        if ( dev->state & NET80211_WAITING ) {
                /* Sanity check */
                if ( ! dev->associating )
                        return;

                if ( currticks() - dev->ctx.assoc->last_packet > ASSOC_TIMEOUT ) {
                        /* Timed out - fail if too many retries, or retry */
                        dev->ctx.assoc->times_tried++;
                        if ( ++dev->ctx.assoc->times_tried > ASSOC_RETRIES ) {
                                rc = -ETIMEDOUT;
                                goto fail;
                        }
                } else {
                        /* Didn't time out - let it keep going */
                        return;
                }
        } else {
                if ( dev->state & NET80211_PROBED )
                        dev->ctx.assoc->times_tried = 0;
        }

        if ( ! ( dev->state & NET80211_PROBED ) ) {
                /* state: probe */

                if ( ! dev->ctx.probe ) {
                        /* start probe */
                        int active = fetch_intz_setting ( NULL,
                                                &net80211_active_setting );
                        int band = dev->hw->bands;

                        if ( active )
                                band &= ~NET80211_BAND_BIT_5GHZ;

                        rc = net80211_prepare_probe ( dev, band, active );
                        if ( rc )
                                goto fail;

                        dev->ctx.probe = net80211_probe_start ( dev, dev->essid,
                                                                active );
                        if ( ! dev->ctx.probe ) {
                                dev->assoc_rc = -ENOMEM;
                                goto fail;
                        }
                }

                rc = net80211_probe_step ( dev->ctx.probe );
                if ( ! rc ) {
                        return; /* still going */
                }

                dev->associating = net80211_probe_finish_best ( dev->ctx.probe );
                dev->ctx.probe = NULL;
                if ( ! dev->associating ) {
                        if ( rc > 0 ) /* "successful" probe found nothing */
                                rc = -ETIMEDOUT;
                        goto fail;
                }

                /* If we probed using a broadcast SSID, record that
                   fact for the settings applicator before we clobber
                   it with the specific SSID we've chosen. */
                if ( ! dev->essid[0] )
                        dev->state |= NET80211_AUTO_SSID;

                DBGC ( dev, "802.11 %p found network %s (%s)\n", dev,
                       dev->associating->essid,
                       eth_ntoa ( dev->associating->bssid ) );

                dev->ctx.assoc = zalloc ( sizeof ( *dev->ctx.assoc ) );
                if ( ! dev->ctx.assoc ) {
                        rc = -ENOMEM;
                        goto fail;
                }

                dev->state |= NET80211_PROBED;
                dev->ctx.assoc->method = IEEE80211_AUTH_OPEN_SYSTEM;

                return;
        }

        /* Record time of sending the packet we're about to send, for timeout */
        dev->ctx.assoc->last_packet = currticks();

        if ( ! ( dev->state & NET80211_AUTHENTICATED ) ) {
                /* state: prepare and authenticate */

                if ( status != IEEE80211_STATUS_SUCCESS ) {
                        /* we tried authenticating already, but failed */
                        int method = dev->ctx.assoc->method;

                        if ( method == IEEE80211_AUTH_OPEN_SYSTEM &&
                             ( status == IEEE80211_STATUS_AUTH_CHALL_INVALID ||
                               status == IEEE80211_STATUS_AUTH_ALGO_UNSUPP ) ) {
                                /* Maybe this network uses Shared Key? */
                                dev->ctx.assoc->method =
                                        IEEE80211_AUTH_SHARED_KEY;
                        } else {
                                goto fail;
                        }
                }

                DBGC ( dev, "802.11 %p authenticating with method %d\n", dev,
                       dev->ctx.assoc->method );

                rc = net80211_prepare_assoc ( dev, dev->associating );
                if ( rc )
                        goto fail;

                rc = net80211_send_auth ( dev, dev->associating,
                                          dev->ctx.assoc->method );
                if ( rc )
                        goto fail;

                return;
        }

        if ( ! ( dev->state & NET80211_ASSOCIATED ) ) {
                /* state: associate */

                if ( status != IEEE80211_STATUS_SUCCESS )
                        goto fail;

                DBGC ( dev, "802.11 %p associating\n", dev );

                if ( dev->handshaker && dev->handshaker->start &&
                     ! dev->handshaker->started ) {
                        rc = dev->handshaker->start ( dev );
                        if ( rc < 0 )
                                goto fail;
                        dev->handshaker->started = 1;
                }

                rc = net80211_send_assoc ( dev, dev->associating );
                if ( rc )
                        goto fail;

                return;
        }

        if ( ! ( dev->state & NET80211_CRYPTO_SYNCED ) ) {
                /* state: crypto sync */
                DBGC ( dev, "802.11 %p security handshaking\n", dev );

                if ( ! dev->handshaker || ! dev->handshaker->step ) {
                        dev->state |= NET80211_CRYPTO_SYNCED;
                        return;
                }

                rc = dev->handshaker->step ( dev );

                if ( rc < 0 ) {
                        /* Only record the returned error if we're
                           still marked as associated, because an
                           asynchronous error will have already been
                           reported to net80211_deauthenticate() and
                           assoc_rc thereby set. */
                        if ( dev->state & NET80211_ASSOCIATED )
                                dev->assoc_rc = rc;
                        rc = 0;
                        goto fail;
                }

                if ( rc > 0 ) {
                        dev->assoc_rc = 0;
                        dev->state |= NET80211_CRYPTO_SYNCED;
                }
                return;
        }

        /* state: done! */
        netdev_link_up ( dev->netdev );
        dev->assoc_rc = 0;
        dev->state &= ~NET80211_WORKING;

        free ( dev->ctx.assoc );
        dev->ctx.assoc = NULL;

        net80211_free_wlan ( dev->associating );
        dev->associating = NULL;

        dev->rctl = rc80211_init ( dev );

        process_del ( &dev->proc_assoc );

        DBGC ( dev, "802.11 %p associated with %s (%s)\n", dev,
               dev->essid, eth_ntoa ( dev->bssid ) );

        return;

 fail:
        dev->state &= ~( NET80211_WORKING | NET80211_WAITING );
        if ( rc )
                dev->assoc_rc = rc;

        netdev_link_err ( dev->netdev, dev->assoc_rc );

        /* We never reach here from the middle of a probe, so we don't
           need to worry about freeing dev->ctx.probe. */

        if ( dev->state & NET80211_PROBED ) {
                free ( dev->ctx.assoc );
                dev->ctx.assoc = NULL;
        }

        net80211_free_wlan ( dev->associating );
        dev->associating = NULL;

        process_del ( &dev->proc_assoc );

        DBGC ( dev, "802.11 %p association failed (state=%04x): "
               "%s\n", dev, dev->state, strerror ( dev->assoc_rc ) );

        /* Try it again: */
        net80211_autoassociate ( dev );
}
static void net80211_handle_auth ( struct net80211_device dev,
struct io_buffer iob 
) [static]

Handle receipt of 802.11 authentication frame.

Parameters:
dev802.11 device
iobI/O buffer

If the authentication method being used is Shared Key, and the frame that was received included challenge text, the frame is encrypted using the cryptosystem currently in effect and sent back to the AP to complete the authentication.

Definition at line 2227 of file net80211.c.

References ieee80211_frame::addr1, ieee80211_frame::addr2, ieee80211_frame::addr3, ieee80211_auth::algorithm, net80211_device::crypto, io_buffer::data, ieee80211_frame::data, DBGC, net80211_crypto::encrypt, ETH_ALEN, hdr, IEEE80211_AUTH_SHARED_KEY, IEEE80211_STATUS_FAILURE, IEEE80211_STATUS_SUCCESS, memcpy(), NET80211_AUTHENTICATED, net80211_set_state(), NET80211_WAITING, net80211_device::netdev, netdev_tx(), ieee80211_auth::status, and ieee80211_auth::tx_seq.

Referenced by net80211_handle_mgmt().

{
        struct ieee80211_frame *hdr = iob->data;
        struct ieee80211_auth *auth =
            ( struct ieee80211_auth * ) hdr->data;

        if ( auth->tx_seq & 1 ) {
                DBGC ( dev, "802.11 %p authentication received improperly "
                       "directed frame (seq. %d)\n", dev, auth->tx_seq );
                net80211_set_state ( dev, NET80211_WAITING, 0,
                                     IEEE80211_STATUS_FAILURE );
                return;
        }

        if ( auth->status != IEEE80211_STATUS_SUCCESS ) {
                DBGC ( dev, "802.11 %p authentication failed: status %d\n",
                       dev, auth->status );
                net80211_set_state ( dev, NET80211_WAITING, 0,
                                     auth->status );
                return;
        }

        if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY && ! dev->crypto ) {
                DBGC ( dev, "802.11 %p can't perform shared-key authentication "
                       "without a cryptosystem\n", dev );
                net80211_set_state ( dev, NET80211_WAITING, 0,
                                     IEEE80211_STATUS_FAILURE );
                return;
        }

        if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY &&
             auth->tx_seq == 2 ) {
                /* Since the iob we got is going to be freed as soon
                   as we return, we can do some in-place
                   modification. */
                auth->tx_seq = 3;
                auth->status = 0;

                memcpy ( hdr->addr2, hdr->addr1, ETH_ALEN );
                memcpy ( hdr->addr1, hdr->addr3, ETH_ALEN );

                netdev_tx ( dev->netdev,
                            dev->crypto->encrypt ( dev->crypto, iob ) );
                return;
        }

        net80211_set_state ( dev, NET80211_WAITING, NET80211_AUTHENTICATED,
                             IEEE80211_STATUS_SUCCESS );

        return;
}
static void net80211_handle_assoc_reply ( struct net80211_device dev,
struct io_buffer iob 
) [static]

Handle receipt of 802.11 association reply frame.

Parameters:
dev802.11 device
iobI/O buffer

Definition at line 2326 of file net80211.c.

References ieee80211_frame::addr3, net80211_device::aid, net80211_device::bssid, io_buffer::data, ieee80211_frame::data, DBGC, ETH_ALEN, hdr, ieee80211_assoc_resp, IEEE80211_STATUS_SUCCESS, memcpy(), NET80211_ASSOCIATED, net80211_process_capab(), net80211_process_ie(), net80211_set_state(), NET80211_WAITING, and io_buffer::tail.

Referenced by net80211_handle_mgmt().

{
        struct ieee80211_frame *hdr = iob->data;
        struct ieee80211_assoc_resp *assoc =
                ( struct ieee80211_assoc_resp * ) hdr->data;

        net80211_process_capab ( dev, assoc->capability );
        net80211_process_ie ( dev, assoc->info_element, iob->tail );

        if ( assoc->status != IEEE80211_STATUS_SUCCESS ) {
                DBGC ( dev, "802.11 %p association failed: status %d\n",
                       dev, assoc->status );
                net80211_set_state ( dev, NET80211_WAITING, 0,
                                     assoc->status );
                return;
        }

        /* ESSID was filled before the association request was sent */
        memcpy ( dev->bssid, hdr->addr3, ETH_ALEN );
        dev->aid = assoc->aid;

        net80211_set_state ( dev, NET80211_WAITING, NET80211_ASSOCIATED,
                             IEEE80211_STATUS_SUCCESS );
}
static int net80211_send_disassoc ( struct net80211_device dev,
int  reason,
int  deauth 
) [static]

Send 802.11 disassociation frame.

Parameters:
dev802.11 device
reasonReason for disassociation
deauthIf TRUE, send deauthentication instead of disassociation
Return values:
rcReturn status code

Definition at line 2361 of file net80211.c.

References alloc_iob(), net80211_device::bssid, EINVAL, ieee80211_disassoc, IEEE80211_STYPE_DEAUTH, IEEE80211_STYPE_DISASSOC, IEEE80211_TYP_FRAME_HEADER_LEN, iob_put, iob_reserve, NET80211_ASSOCIATED, net80211_set_state(), net80211_tx_mgmt(), reason, and net80211_device::state.

Referenced by net80211_deauthenticate(), and net80211_netdev_close().

{
        struct io_buffer *iob = alloc_iob ( 64 );
        struct ieee80211_disassoc *disassoc;

        if ( ! ( dev->state & NET80211_ASSOCIATED ) )
                return -EINVAL;

        net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 );
        iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN );
        disassoc = iob_put ( iob, sizeof ( *disassoc ) );
        disassoc->reason = reason;

        return net80211_tx_mgmt ( dev, deauth ? IEEE80211_STYPE_DEAUTH :
                                  IEEE80211_STYPE_DISASSOC, dev->bssid, iob );
}
static void net80211_handle_mgmt ( struct net80211_device dev,
struct io_buffer iob,
int  signal 
) [static]

Handle receipt of 802.11 management frame.

Parameters:
dev802.11 device
iobI/O buffer
signalSignal strength of received frame

Definition at line 2438 of file net80211.c.

References io_buffer::data, ieee80211_frame::data, DBGC, ieee80211_frame::fc, free_iob(), hdr, ieee80211_disassoc, IEEE80211_FC_SUBTYPE, IEEE80211_FC_TYPE, IEEE80211_STYPE_ACTION, IEEE80211_STYPE_ASSOC_REQ, IEEE80211_STYPE_ASSOC_RESP, IEEE80211_STYPE_AUTH, IEEE80211_STYPE_BEACON, IEEE80211_STYPE_DEAUTH, IEEE80211_STYPE_DISASSOC, IEEE80211_STYPE_PROBE_REQ, IEEE80211_STYPE_PROBE_RESP, IEEE80211_STYPE_REASSOC_REQ, IEEE80211_STYPE_REASSOC_RESP, IEEE80211_TYPE_MGMT, net80211_device::keep_mgmt, io_buffer::list, net80211_rx_info::list, list_add_tail, net80211_device::mgmt_info_queue, net80211_device::mgmt_queue, NET80211_ASSOCIATED, NET80211_AUTHENTICATED, net80211_autoassociate(), net80211_handle_assoc_reply(), net80211_handle_auth(), NET80211_IS_REASON, net80211_set_state(), net80211_update_link_quality(), net80211_rx_info::signal, net80211_device::state, and zalloc().

Referenced by net80211_rx().

{
        struct ieee80211_frame *hdr = iob->data;
        struct ieee80211_disassoc *disassoc;
        u16 stype = hdr->fc & IEEE80211_FC_SUBTYPE;
        int keep = 0;
        int is_deauth = ( stype == IEEE80211_STYPE_DEAUTH );

        if ( ( hdr->fc & IEEE80211_FC_TYPE ) != IEEE80211_TYPE_MGMT ) {
                free_iob ( iob );
                return;         /* only handle management frames */
        }

        switch ( stype ) {
                /* We reconnect on deauthentication and disassociation. */
        case IEEE80211_STYPE_DEAUTH:
        case IEEE80211_STYPE_DISASSOC:
                disassoc = ( struct ieee80211_disassoc * ) hdr->data;
                net80211_set_state ( dev, is_deauth ? NET80211_AUTHENTICATED :
                                     NET80211_ASSOCIATED, 0,
                                     NET80211_IS_REASON | disassoc->reason );
                DBGC ( dev, "802.11 %p %s: reason %d\n",
                       dev, is_deauth ? "deauthenticated" : "disassociated",
                       disassoc->reason );

                /* Try to reassociate, in case it's transient. */
                net80211_autoassociate ( dev );

                break;

                /* We handle authentication and association. */
        case IEEE80211_STYPE_AUTH:
                if ( ! ( dev->state & NET80211_AUTHENTICATED ) )
                        net80211_handle_auth ( dev, iob );
                break;

        case IEEE80211_STYPE_ASSOC_RESP:
        case IEEE80211_STYPE_REASSOC_RESP:
                if ( ! ( dev->state & NET80211_ASSOCIATED ) )
                        net80211_handle_assoc_reply ( dev, iob );
                break;

                /* We pass probes and beacons onto network scanning
                   code. Pass actions for future extensibility. */
        case IEEE80211_STYPE_BEACON:
                net80211_update_link_quality ( dev, iob );
                /* fall through */
        case IEEE80211_STYPE_PROBE_RESP:
        case IEEE80211_STYPE_ACTION:
                if ( dev->keep_mgmt ) {
                        struct net80211_rx_info *rxinf;
                        rxinf = zalloc ( sizeof ( *rxinf ) );
                        if ( ! rxinf ) {
                                DBGC ( dev, "802.11 %p out of memory\n", dev );
                                break;
                        }
                        rxinf->signal = signal;
                        list_add_tail ( &iob->list, &dev->mgmt_queue );
                        list_add_tail ( &rxinf->list, &dev->mgmt_info_queue );
                        keep = 1;
                }
                break;

        case IEEE80211_STYPE_PROBE_REQ:
                /* Some nodes send these broadcast. Ignore them. */
                break;

        case IEEE80211_STYPE_ASSOC_REQ:
        case IEEE80211_STYPE_REASSOC_REQ:
                /* We should never receive these, only send them. */
                DBGC ( dev, "802.11 %p received strange management request "
                       "(%04x)\n", dev, stype );
                break;

        default:
                DBGC ( dev, "802.11 %p received unimplemented management "
                       "packet (%04x)\n", dev, stype );
                break;
        }

        if ( ! keep )
                free_iob ( iob );
}