📄 ieee80211_sta.c
字号:
/* * BSS client mode implementation * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. *//* TODO: * order BSS list by RSSI(?) ("quality of AP") * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, * SSID) */#include <linux/delay.h>#include <linux/if_ether.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/wireless.h>#include <linux/random.h>#include <linux/etherdevice.h>#include <net/iw_handler.h>#include <asm/types.h>#include <net/mac80211.h>#include "ieee80211_i.h"#include "ieee80211_rate.h"#include "ieee80211_led.h"#define IEEE80211_AUTH_TIMEOUT (HZ / 5)#define IEEE80211_AUTH_MAX_TRIES 3#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)#define IEEE80211_ASSOC_MAX_TRIES 3#define IEEE80211_MONITORING_INTERVAL (2 * HZ)#define IEEE80211_PROBE_INTERVAL (60 * HZ)#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)#define IEEE80211_SCAN_INTERVAL (2 * HZ)#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)#define IEEE80211_IBSS_JOIN_TIMEOUT (20 * HZ)#define IEEE80211_PROBE_DELAY (HZ / 33)#define IEEE80211_CHANNEL_TIME (HZ / 33)#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)#define IEEE80211_IBSS_MAX_STA_ENTRIES 128#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype)#define ERP_INFO_USE_PROTECTION BIT(1)static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, u8 *ssid, size_t ssid_len);static struct ieee80211_sta_bss *ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid, int channel, u8 *ssid, u8 ssid_len);static void ieee80211_rx_bss_put(struct net_device *dev, struct ieee80211_sta_bss *bss);static int ieee80211_sta_find_ibss(struct net_device *dev, struct ieee80211_if_sta *ifsta);static int ieee80211_sta_wep_configured(struct net_device *dev);static int ieee80211_sta_start_scan(struct net_device *dev, u8 *ssid, size_t ssid_len);static int ieee80211_sta_config_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta);/* Parsed Information Elements */struct ieee802_11_elems { /* pointers to IEs */ u8 *ssid; u8 *supp_rates; u8 *fh_params; u8 *ds_params; u8 *cf_params; u8 *tim; u8 *ibss_params; u8 *challenge; u8 *wpa; u8 *rsn; u8 *erp_info; u8 *ext_supp_rates; u8 *wmm_info; u8 *wmm_param; /* length of them, respectively */ u8 ssid_len; u8 supp_rates_len; u8 fh_params_len; u8 ds_params_len; u8 cf_params_len; u8 tim_len; u8 ibss_params_len; u8 challenge_len; u8 wpa_len; u8 rsn_len; u8 erp_info_len; u8 ext_supp_rates_len; u8 wmm_info_len; u8 wmm_param_len;};static void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems){ size_t left = len; u8 *pos = start; memset(elems, 0, sizeof(*elems)); while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; left -= 2; if (elen > left) return; switch (id) { case WLAN_EID_SSID: elems->ssid = pos; elems->ssid_len = elen; break; case WLAN_EID_SUPP_RATES: elems->supp_rates = pos; elems->supp_rates_len = elen; break; case WLAN_EID_FH_PARAMS: elems->fh_params = pos; elems->fh_params_len = elen; break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: elems->cf_params = pos; elems->cf_params_len = elen; break; case WLAN_EID_TIM: elems->tim = pos; elems->tim_len = elen; break; case WLAN_EID_IBSS_PARAMS: elems->ibss_params = pos; elems->ibss_params_len = elen; break; case WLAN_EID_CHALLENGE: elems->challenge = pos; elems->challenge_len = elen; break; case WLAN_EID_WPA: if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && pos[2] == 0xf2) { /* Microsoft OUI (00:50:F2) */ if (pos[3] == 1) { /* OUI Type 1 - WPA IE */ elems->wpa = pos; elems->wpa_len = elen; } else if (elen >= 5 && pos[3] == 2) { if (pos[4] == 0) { elems->wmm_info = pos; elems->wmm_info_len = elen; } else if (pos[4] == 1) { elems->wmm_param = pos; elems->wmm_param_len = elen; } } } break; case WLAN_EID_RSN: elems->rsn = pos; elems->rsn_len = elen; break; case WLAN_EID_ERP_INFO: elems->erp_info = pos; elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; default: break; } left -= elen; pos += elen; }}static int ecw2cw(int ecw){ int cw = 1; while (ecw > 0) { cw <<= 1; ecw--; } return cw - 1;}static void ieee80211_sta_wmm_params(struct net_device *dev, struct ieee80211_if_sta *ifsta, u8 *wmm_param, size_t wmm_param_len){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_tx_queue_params params; size_t left; int count; u8 *pos; if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) return; count = wmm_param[6] & 0x0f; if (count == ifsta->wmm_last_param_set) return; ifsta->wmm_last_param_set = count; pos = wmm_param + 8; left = wmm_param_len - 8; memset(¶ms, 0, sizeof(params)); if (!local->ops->conf_tx) return; local->wmm_acm = 0; for (; left >= 4; left -= 4, pos += 4) { int aci = (pos[0] >> 5) & 0x03; int acm = (pos[0] >> 4) & 0x01; int queue; switch (aci) { case 1: queue = IEEE80211_TX_QUEUE_DATA3; if (acm) { local->wmm_acm |= BIT(0) | BIT(3); } break; case 2: queue = IEEE80211_TX_QUEUE_DATA1; if (acm) { local->wmm_acm |= BIT(4) | BIT(5); } break; case 3: queue = IEEE80211_TX_QUEUE_DATA0; if (acm) { local->wmm_acm |= BIT(6) | BIT(7); } break; case 0: default: queue = IEEE80211_TX_QUEUE_DATA2; if (acm) { local->wmm_acm |= BIT(1) | BIT(2); } break; } params.aifs = pos[0] & 0x0f; params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); params.cw_min = ecw2cw(pos[1] & 0x0f); /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " "cWmin=%d cWmax=%d burst=%d\n", dev->name, queue, aci, acm, params.aifs, params.cw_min, params.cw_max, params.burst_time); /* TODO: handle ACM (block TX, fallback to next lowest allowed * AC for now) */ if (local->ops->conf_tx(local_to_hw(local), queue, ¶ms)) { printk(KERN_DEBUG "%s: failed to set TX queue " "parameters for queue %d\n", dev->name, queue); } }}static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value){ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_sta *ifsta = &sdata->u.sta; int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; int preamble_mode = (erp_value & WLAN_ERP_BARKER_PREAMBLE) != 0; u8 changes = 0; DECLARE_MAC_BUF(mac); if (use_protection != !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION)) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: CTS protection %s (BSSID=" "%s)\n", dev->name, use_protection ? "enabled" : "disabled", print_mac(mac, ifsta->bssid)); } if (use_protection) sdata->flags |= IEEE80211_SDATA_USE_PROTECTION; else sdata->flags &= ~IEEE80211_SDATA_USE_PROTECTION; changes |= IEEE80211_ERP_CHANGE_PROTECTION; } if (preamble_mode != !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE)) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: switched to %s barker preamble" " (BSSID=%s)\n", dev->name, (preamble_mode == WLAN_ERP_PREAMBLE_SHORT) ? "short" : "long", print_mac(mac, ifsta->bssid)); } if (preamble_mode) sdata->flags &= ~IEEE80211_SDATA_SHORT_PREAMBLE; else sdata->flags |= IEEE80211_SDATA_SHORT_PREAMBLE; changes |= IEEE80211_ERP_CHANGE_PREAMBLE; } if (changes) ieee80211_erp_info_change_notify(dev, changes);}static void ieee80211_sta_send_associnfo(struct net_device *dev, struct ieee80211_if_sta *ifsta){ char *buf; size_t len; int i; union iwreq_data wrqu; if (!ifsta->assocreq_ies && !ifsta->assocresp_ies) return; buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len + ifsta->assocresp_ies_len), GFP_KERNEL); if (!buf) return; len = sprintf(buf, "ASSOCINFO("); if (ifsta->assocreq_ies) { len += sprintf(buf + len, "ReqIEs="); for (i = 0; i < ifsta->assocreq_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocreq_ies[i]); } } if (ifsta->assocresp_ies) { if (ifsta->assocreq_ies) len += sprintf(buf + len, " "); len += sprintf(buf + len, "RespIEs="); for (i = 0; i < ifsta->assocresp_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocresp_ies[i]); } } len += sprintf(buf + len, ")"); if (len > IW_CUSTOM_MAX) { len = sprintf(buf, "ASSOCRESPIE="); for (i = 0; i < ifsta->assocresp_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocresp_ies[i]); } } memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = len; wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); kfree(buf);}static void ieee80211_set_associated(struct net_device *dev, struct ieee80211_if_sta *ifsta, bool assoc){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); union iwreq_data wrqu; if (!!(ifsta->flags & IEEE80211_STA_ASSOCIATED) == assoc) return; if (assoc) { struct ieee80211_sub_if_data *sdata; struct ieee80211_sta_bss *bss; ifsta->flags |= IEEE80211_STA_ASSOCIATED; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type != IEEE80211_IF_TYPE_STA) return; bss = ieee80211_rx_bss_get(dev, ifsta->bssid, local->hw.conf.channel, ifsta->ssid, ifsta->ssid_len); if (bss) { if (bss->has_erp_value) ieee80211_handle_erp_ie(dev, bss->erp_value); ieee80211_rx_bss_put(dev, bss); } netif_carrier_on(dev); ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET; memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN); memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN); ieee80211_sta_send_associnfo(dev, ifsta); } else { ifsta->flags &= ~IEEE80211_STA_ASSOCIATED; netif_carrier_off(dev); ieee80211_reset_erp_info(dev); memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); } wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); ifsta->last_probe = jiffies; ieee80211_led_assoc(local, assoc);}static void ieee80211_set_disassoc(struct net_device *dev, struct ieee80211_if_sta *ifsta, int deauth){ if (deauth) ifsta->auth_tries = 0; ifsta->assoc_tries = 0; ieee80211_set_associated(dev, ifsta, 0);}static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, int encrypt){ struct ieee80211_sub_if_data *sdata; struct ieee80211_tx_packet_data *pkt_data; sdata = IEEE80211_DEV_TO_SUB_IF(dev); skb->dev = sdata->local->mdev; skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; if (!encrypt) pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT; dev_queue_xmit(skb);}static void ieee80211_send_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, int transaction, u8 *extra, size_t extra_len, int encrypt){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sk_buff *skb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -