[linux无线子系统]主动扫描之发送Probe Request帧

本文详细探讨了Linux无线子系统中如何进行主动扫描,重点关注发送Probe Request帧的过程。从数据结构如struct ieee80211_mgmt、struct ieee80211_hdr_3addr等开始,解释了如何组装管理帧首部和三地址帧首部。接着,介绍了ieee80211_scan_state_send_probe()函数的作用,它根据用户态的扫描请求参数来发送探查请求。然后,详细阐述了 Probe Request帧的组装过程,包括ieee80211_build_probe_req()和ieee80211_build_preq_ies()函数,这两个函数分别用于组装帧的IE部分和整个skb。
摘要由CSDN通过智能技术生成


当扫描状态机切换到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;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值