当扫描状态机切换到SCAN_SEND_PROBE状态后,mac80211将调用ieee80211_scan_state_send_probe()组装Probe Request帧,然后将其发送出去。这篇笔记来看这部分代码的实现。
数据结构
通用管理帧首部: struct ieee80211_mgmt
struct ieee80211_mgmt {
__le16 frame_control;
__le16 duration;
u8 da[6];
u8 sa[6];
u8 bssid[6];
__le16 seq_ctrl;
union {
struct {
__le16 auth_alg;
__le16 auth_transaction;
__le16 status_code;
/* possibly followed by Challenge text */
u8 variable[0];
} __attribute__ ((packed)) auth;
struct {
__le16 reason_code;
} __attribute__ ((packed)) deauth;
struct {
__le16 capab_info;
__le16 listen_interval;
/* followed by SSID and Supported rates */
u8 variable[0];
} __attribute__ ((packed)) assoc_req;
struct {
__le16 capab_info;
__le16 status_code;
__le16 aid;
/* followed by Supported rates */
u8 variable[0];
} __attribute__ ((packed)) assoc_resp, reassoc_resp;
struct {
__le16 capab_info;
__le16 listen_interval;
u8 current_ap[6];
/* followed by SSID and Supported rates */
u8 variable[0];
} __attribute__ ((packed)) reassoc_req;
struct {
__le16 reason_code;
} __attribute__ ((packed)) disassoc;
struct {
__le64 timestamp;
__le16 beacon_int;
__le16 capab_info;
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params, TIM */
u8 variable[0];
} __attribute__ ((packed)) beacon;
struct {
/* only variable items: SSID, Supported rates */
u8 variable[0];
} __attribute__ ((packed)) probe_req;
struct {
__le64 timestamp;
__le16 beacon_int;
__le16 capab_info;
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params */
u8 variable[0];
} __attribute__ ((packed)) probe_resp;
struct {
u8 category;
union {
struct {
u8 action_code;
u8 dialog_token;
u8 status_code;
u8 variable[0];
} __attribute__ ((packed)) wme_action;
struct{
u8 action_code;
u8 element_id;
u8 length;
struct ieee80211_channel_sw_ie sw_elem;
} __attribute__((packed)) chan_switch;
struct{
u8 action_code;
u8 dialog_token;
u8 element_id;
u8 length;
struct ieee80211_msrment_ie msr_elem;
} __attribute__((packed)) measurement;
struct{
u8 action_code;
u8 dialog_token;
__le16 capab;
__le16 timeout;
__le16 start_seq_num;
} __attribute__((packed)) addba_req;
struct{
u8 action_code;
u8 dialog_token;
__le16 status;
__le16 capab;
__le16 timeout;
} __attribute__((packed)) addba_resp;
struct{
u8 action_code;
__le16 params;
__le16 reason_code;
} __attribute__((packed)) delba;
struct{
u8 action_code;
/* capab_info for open and confirm,
* reason for close
*/
__le16 aux;
/* Followed in plink_confirm by status
* code, AID and supported rates,
* and directly by supported rates in
* plink_open and plink_close
*/
u8 variable[0];
} __attribute__((packed)) plink_action;
struct{
u8 action_code;
u8 variable[0];
} __attribute__((packed)) mesh_action;
struct {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} __attribute__ ((packed)) sa_query;
struct {
u8 action;
u8 smps_control;
} __attribute__ ((packed)) ht_smps;
} u;
} __attribute__ ((packed)) action;
} u;
} __attribute__ ((packed));
三地址帧首部: struct ieee80211_hdr_3addr
struct ieee80211_hdr_3addr {
__le16 frame_control;
__le16 duration_id;
u8 addr1[6];
u8 addr2[6];
u8 addr3[6];
__le16 seq_ctrl;
} __attribute__ ((packed));
支持速率定义: struct ieee80211_rate
芯片驱动程序应该用该结构定义自己支持的速率信息。
struct ieee80211_rate {
u32 flags;
u16 bitrate;
u16 hw_value, hw_value_short;
};
// mac80211_hwsim支持的速率,2.4G全支持;5G支持hwsim_rates[4]开始的速率
static const struct ieee80211_rate hwsim_rates[] = {
{ .bitrate = 10 },
{ .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60 },
{ .bitrate = 90 },
{ .bitrate = 120 },
{ .bitrate = 180 },
{ .bitrate = 240 },
{ .bitrate = 360 },
{ .bitrate = 480 },
{ .bitrate = 540 }
};
支持的band定义: struct ieee80211_supported_band
struct ieee80211_supported_band {
struct ieee80211_channel *channels; // 支持的channel列表
struct ieee80211_rate *bitrates; // 支持的速率
enum ieee80211_band band; // 2.4G or 5G
int n_channels; // 支持的channel个数
int n_bitrates; // 支持的速率个数
struct ieee80211_sta_ht_cap ht_cap; // HT能力描述
};
支持的channel定义: struct ieee80211_channel
struct ieee80211_channel {
enum ieee80211_band band; // 2.4G or 5G
u16 center_freq; // channel的中心频率
u16 hw_value;
u32 flags;
int max_antenna_gain;
int max_power; // 最大发射功率
bool beacon_found;
u32 orig_flags;
int orig_mag, orig_mpwr;
};
// mac80211_hwsim支持2.4G和5G所有的channel
#define CHAN2G(_freq) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_freq), \
.max_power = 20, \
}
#define CHAN5G(_freq) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_freq), \
.max_power = 20, \
}
static const struct ieee80211_channel hwsim_channels_2ghz[] = {
CHAN2G(2412), /* Channel 1 */
CHAN2G(2417), /* Channel 2 */
CHAN2G(2422), /* Channel 3 */
CHAN2G(2427), /* Channel 4 */
CHAN2G(2432), /* Channel 5 */
CHAN2G(2437), /* Channel 6 */
CHAN2G(2442), /* Channel 7 */
CHAN2G(2447), /* Channel 8 */
CHAN2G(2452), /* Channel 9 */
CHAN2G(2457), /* Channel 10 */
CHAN2G(2462), /* Channel 11 */
CHAN2G(2467), /* Channel 12 */
CHAN2G(2472), /* Channel 13 */
CHAN2G(2484), /* Channel 14 */
};
static const struct ieee80211_channel hwsim_channels_5ghz[] = {
CHAN5G(5180), /* Channel 36 */
CHAN5G(5200), /* Channel 40 */
CHAN5G(5220), /* Channel 44 */
CHAN5G(5240), /* Channel 48 */
CHAN5G(5260), /* Channel 52 */
CHAN5G(5280), /* Channel 56 */
CHAN5G(5300), /* Channel 60 */
CHAN5G(5320), /* Channel 64 */
CHAN5G(5500), /* Channel 100 */
CHAN5G(5520), /* Channel 104 */
CHAN5G(5540), /* Channel 108 */
CHAN5G(5560), /* Channel 112 */
CHAN5G(5580), /* Channel 116 */
CHAN5G(5600), /* Channel 120 */
CHAN5G(5620), /* Channel 124 */
CHAN5G(5640), /* Channel 128 */
CHAN5G(5660), /* Channel 132 */
CHAN5G(5680), /* Channel 136 */
CHAN5G(5700), /* Channel 140 */
CHAN5G(5745), /* Channel 149 */
CHAN5G(5765), /* Channel 153 */
CHAN5G(5785), /* Channel 157 */
CHAN5G(5805), /* Channel 161 */
CHAN5G(5825), /* Channel 165 */
};
ieee80211_scan_state_send_probe()
static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
unsigned long *next_delay)
{
int i;
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
// 对于要扫描的每个ssid,发送Probe Request帧
for (i = 0; i < local->scan_req->n_ssids; i++)
ieee80211_send_probe_req(sdata, NULL, local->scan_req->ssids[i].ssid,
local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len);
// 发送Probe帧后,最多等待1/33秒响应,然后在下一个channel上执行扫描
*next_delay = IEEE80211_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
}
local->scan_req->n_ssids直接来自用户态的扫描请求参数,从该函数的实现可以看出,即使用户态想要执行全扫描,也应该指定一个通配的SSID下来,否则将无法发起扫描。
向指定的SSID发送Probe Request帧的实现如下:
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len)
{
struct sk_buff *skb;
// 组装Probe Request帧
skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len);
// 调用标准的tx接口将Probe Request帧发送出去
if (skb)
ieee80211_tx_skb(sdata, skb);
}
组装帧: ieee80211_build_probe_req()
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt; // 通用得管理帧首部
size_t buf_len;
u8 *buf;
u8 chan;
// 分配一块临时内存
buf = kmalloc(200 + ie_len, GFP_KERNEL);
if (!buf) {
return NULL;
}
// 根据频率计算出当前要扫描的channel索引
chan = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq);
// 将IE信息填充到临时内存buf中
buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, local->hw.conf.channel->band,
sdata->rc_rateidx_mask[local->hw.conf.channel->band], chan);
// 构造并填充SKB
skb = ieee80211_probereq_get(&local->hw, &sdata->vif, ssid, ssid_len, buf, buf_len);
// 如果指定了目的地址,那么将其拷贝到帧都不得bssid字段
if (dst) {
mgmt = (struct ieee80211_mgmt *) skb->data;
memcpy(mgmt->da, dst, ETH_ALEN);
memcpy(mgmt->bssid, dst, ETH_ALEN);
}
// Probe Reuqest帧不加密
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
kfree(buf);
return skb;
}
组装IE: ieee80211_build_preq_ies()
该函数将Probe Request帧中除了SSID以外得其它信息元素都填充到buffer指定得内存块中。
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, const u8 *ie,
size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel)
{
struct ieee80211_supported_band *sband;
u8 *pos;
size_t offset = 0, noffset;
int supp_rates_len, i;
u8 rates[32];
int num_rates;
int ext_rates_len;
sband = local->hw.wiphy->bands[band];
// pos指向起始位置
pos = buffer;
// 组装支持的速率IE
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
continue; /* skip rate */
rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
}
supp_rates_len = min_t(int, num_rates, 8);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = supp_rates_len;
memcpy(pos, rates, supp_rates_len);
pos += supp_rates_len;
/* insert "request information" if in custom IEs */
if (ie && ie_len) {
static const u8 before_extrates[] = {
WLAN_EID_SSID,
WLAN_EID_SUPP_RATES,
WLAN_EID_REQUEST,
};
noffset = ieee80211_ie_split(ie, ie_len, before_extrates, ARRAY_SIZE(before_extrates), offset);
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
}
// 组装扩展速率IE
ext_rates_len = num_rates - supp_rates_len;
if (ext_rates_len > 0) {
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = ext_rates_len;
memcpy(pos, rates + supp_rates_len, ext_rates_len);
pos += ext_rates_len;
}
// 2.4G需要填充DS IE信息
if (channel && sband->band == IEEE80211_BAND_2GHZ) {
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
*pos++ = channel;
}
/* insert custom IEs that go before HT */
if (ie && ie_len) {
static const u8 before_ht[] = {
WLAN_EID_SSID,
WLAN_EID_SUPP_RATES,
WLAN_EID_REQUEST,
WLAN_EID_EXT_SUPP_RATES,
WLAN_EID_DS_PARAMS,
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
};
noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), offset);
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
}
if (sband->ht_cap.ht_supported) {
u16 cap = sband->ht_cap.cap;
__le16 tmp;
*pos++ = WLAN_EID_HT_CAPABILITY;
*pos++ = sizeof(struct ieee80211_ht_cap);
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
tmp = cpu_to_le16(cap);
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
*pos++ = sband->ht_cap.ampdu_factor |
(sband->ht_cap.ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
pos += sizeof(sband->ht_cap.mcs);
pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
}
/*
* If adding more here, adjust code in main.c
* that calculates local->scan_ies_len.
*/
/* add any remaining custom IEs */
if (ie && ie_len) {
noffset = ie_len;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
}
return pos - buffer;
}
组装skb: ieee80211_probereq_get()
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local;
struct ieee80211_hdr_3addr *hdr;
struct sk_buff *skb;
size_t ie_ssid_len;
u8 *pos;
sdata = vif_to_sdata(vif);
local = sdata->local;
ie_ssid_len = 2 + ssid_len; // 为何要加2,IE中得length字段并不包含ID和length
// 分配skb,并保留芯片驱动指定得预留空间
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + ie_ssid_len + ie_len);
if (!skb) {
return NULL;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
// 填充mac帧首部
hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
memset(hdr, 0, sizeof(*hdr));
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ);
memset(hdr->addr1, 0xff, ETH_ALEN);
memcpy(hdr->addr2, vif->addr, ETH_ALEN);
memset(hdr->addr3, 0xff, ETH_ALEN);
// 填充SSID IE
pos = skb_put(skb, ie_ssid_len);
*pos++ = WLAN_EID_SSID;
*pos++ = ssid_len;
if (ssid)
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
// 填充其它IE信息
if (ie) {
pos = skb_put(skb, ie_len);
memcpy(pos, ie, ie_len);
}
return skb;
}