Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,4 @@
CONFIG_KUNIT=y
CONFIG_CFG80211=y
CONFIG_MAC80211=y
CONFIG_MAC80211_KUNIT_TEST=y
+3
View File
@@ -0,0 +1,3 @@
mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o s1g_tim.o ttlm.o
obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o
+275
View File
@@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for channel mode functions
*
* Copyright (C) 2024-2026 Intel Corporation
*/
#include <net/cfg80211.h>
#include <kunit/test.h>
#include "util.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static const struct determine_chan_mode_case {
const char *desc;
u8 extra_supp_rate;
enum ieee80211_conn_mode conn_mode;
enum ieee80211_conn_mode expected_mode;
bool strict;
u8 userspace_selector;
struct ieee80211_ht_cap ht_capa_mask;
struct ieee80211_vht_cap vht_capa;
struct ieee80211_vht_cap vht_capa_mask;
u8 vht_basic_mcs_1_4_set:1,
vht_basic_mcs_5_8_set:1,
he_basic_mcs_1_4_set:1,
he_basic_mcs_5_8_set:1;
u8 vht_basic_mcs_1_4, vht_basic_mcs_5_8;
u8 he_basic_mcs_1_4, he_basic_mcs_5_8;
u8 eht_mcs7_min_nss;
u16 eht_disabled_subchannels;
u8 eht_bw;
enum ieee80211_conn_bw_limit conn_bw_limit;
enum ieee80211_conn_bw_limit expected_bw_limit;
int error;
} determine_chan_mode_cases[] = {
{
.desc = "Normal case, EHT is working",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_EHT,
}, {
.desc = "Requiring EHT support is fine",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_EHT,
.extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY,
}, {
.desc = "Lowering the mode limits us",
.conn_mode = IEEE80211_CONN_MODE_VHT,
.expected_mode = IEEE80211_CONN_MODE_VHT,
}, {
.desc = "Requesting a basic rate/selector that we do not support",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1),
.error = EINVAL,
}, {
.desc = "As before, but userspace says it is taking care of it",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.userspace_selector = BSS_MEMBERSHIP_SELECTOR_MIN - 1,
.extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1),
.expected_mode = IEEE80211_CONN_MODE_EHT,
}, {
.desc = "Masking out a supported rate in HT capabilities",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_LEGACY,
.ht_capa_mask = {
.mcs.rx_mask[0] = 0xf7,
},
.strict = true,
}, {
.desc = "Masking out a RX rate in VHT capabilities",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HT,
/* Only one RX stream at MCS 0-7 */
.vht_capa = {
.supp_mcs.rx_mcs_map =
cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7),
},
.vht_capa_mask = {
.supp_mcs.rx_mcs_map = cpu_to_le16(0xffff),
},
.strict = true,
}, {
.desc = "Masking out a TX rate in VHT capabilities",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HT,
/* Only one TX stream at MCS 0-7 */
.vht_capa = {
.supp_mcs.tx_mcs_map =
cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7),
},
.vht_capa_mask = {
.supp_mcs.tx_mcs_map = cpu_to_le16(0xffff),
},
.strict = true,
}, {
.desc = "AP has higher VHT requirement than client",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HT,
.vht_basic_mcs_5_8_set = 1,
.vht_basic_mcs_5_8 = 0xFE, /* require 5th stream */
.strict = true,
}, {
.desc = "all zero VHT basic rates are ignored (many APs broken)",
.conn_mode = IEEE80211_CONN_MODE_VHT,
.expected_mode = IEEE80211_CONN_MODE_VHT,
.vht_basic_mcs_1_4_set = 1,
.vht_basic_mcs_5_8_set = 1,
}, {
.desc = "AP requires 3 HE streams but client only has two",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_VHT,
.he_basic_mcs_1_4 = 0b11001010,
.he_basic_mcs_1_4_set = 1,
}, {
.desc = "all zero HE basic rates are ignored (iPhone workaround)",
.conn_mode = IEEE80211_CONN_MODE_HE,
.expected_mode = IEEE80211_CONN_MODE_HE,
.he_basic_mcs_1_4_set = 1,
.he_basic_mcs_5_8_set = 1,
}, {
.desc = "AP requires too many RX streams with EHT MCS 7",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HE,
.eht_mcs7_min_nss = 0x15,
}, {
.desc = "AP requires too many TX streams with EHT MCS 7",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HE,
.eht_mcs7_min_nss = 0x51,
}, {
.desc = "AP requires too many RX streams with EHT MCS 7 and EHT is required",
.extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY,
.conn_mode = IEEE80211_CONN_MODE_EHT,
.eht_mcs7_min_nss = 0x15,
.error = EINVAL,
}, {
.desc = "80 MHz EHT is downgraded to 40 MHz HE due to puncturing",
.conn_mode = IEEE80211_CONN_MODE_EHT,
.expected_mode = IEEE80211_CONN_MODE_HE,
.conn_bw_limit = IEEE80211_CONN_BW_LIMIT_80,
.expected_bw_limit = IEEE80211_CONN_BW_LIMIT_40,
.eht_disabled_subchannels = 0x08,
.eht_bw = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ,
}
};
KUNIT_ARRAY_PARAM_DESC(determine_chan_mode, determine_chan_mode_cases, desc)
static void test_determine_chan_mode(struct kunit *test)
{
const struct determine_chan_mode_case *params = test->param_value;
struct t_sdata *t_sdata = T_SDATA(test);
struct ieee80211_conn_settings conn = {
.mode = params->conn_mode,
.bw_limit = params->conn_bw_limit,
};
struct cfg80211_bss cbss = {
.channel = &t_sdata->band_5ghz.channels[0],
};
unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {};
u8 bss_ies[] = {
/* Supported Rates */
WLAN_EID_SUPP_RATES, 0x08,
0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24,
/* Extended Supported Rates */
WLAN_EID_EXT_SUPP_RATES, 0x05,
0x30, 0x48, 0x60, 0x6c, params->extra_supp_rate,
/* HT Capabilities */
WLAN_EID_HT_CAPABILITY, 0x1a,
0x0c, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* HT Information (0xff for 1 stream) */
WLAN_EID_HT_OPERATION, 0x16,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* VHT Capabilities */
WLAN_EID_VHT_CAPABILITY, 0xc,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00,
/* VHT Operation */
WLAN_EID_VHT_OPERATION, 0x05,
0x00, 0x00, 0x00,
params->vht_basic_mcs_1_4_set ?
params->vht_basic_mcs_1_4 :
le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff),
params->vht_basic_mcs_5_8_set ?
params->vht_basic_mcs_5_8 :
le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff00),
/* HE Capabilities */
WLAN_EID_EXTENSION, 0x16, WLAN_EID_EXT_HE_CAPABILITY,
0x01, 0x78, 0xc8, 0x1a, 0x40, 0x00, 0x00, 0xbf,
0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfa, 0xff, 0xfa, 0xff,
/* HE Operation (permit overriding values) */
WLAN_EID_EXTENSION, 0x07, WLAN_EID_EXT_HE_OPERATION,
0xf0, 0x3f, 0x00, 0xb0,
params->he_basic_mcs_1_4_set ? params->he_basic_mcs_1_4 : 0xfc,
params->he_basic_mcs_5_8_set ? params->he_basic_mcs_5_8 : 0xff,
/* EHT Capabilities */
WLAN_EID_EXTENSION, 0x12, WLAN_EID_EXT_EHT_CAPABILITY,
0x07, 0x00, 0x1c, 0x00, 0x00, 0xfe, 0xff, 0xff,
0x7f, 0x01, 0x00, 0x88, 0x88, 0x88, 0x00, 0x00,
0x00,
/* EHT Operation */
WLAN_EID_EXTENSION, 0x0b, WLAN_EID_EXT_EHT_OPERATION,
0x03, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11,
0x00, 0x00, 0x00, params->eht_bw,
params->eht_bw == IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ ? 42 : 36,
0x00,
u16_get_bits(params->eht_disabled_subchannels, 0xff),
u16_get_bits(params->eht_disabled_subchannels, 0xff00),
};
struct ieee80211_chan_req chanreq = {};
struct cfg80211_chan_def ap_chandef = {};
struct ieee802_11_elems *elems;
/* To force EHT downgrade to HE on punctured 80 MHz downgraded to 40 MHz */
set_bit(IEEE80211_HW_DISALLOW_PUNCTURING, t_sdata->local.hw.flags);
if (params->strict)
set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags);
else
clear_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags);
t_sdata->sdata->u.mgd.ht_capa_mask = params->ht_capa_mask;
t_sdata->sdata->u.mgd.vht_capa = params->vht_capa;
t_sdata->sdata->u.mgd.vht_capa_mask = params->vht_capa_mask;
if (params->userspace_selector)
set_bit(params->userspace_selector, userspace_selectors);
rcu_assign_pointer(cbss.ies,
kunit_kzalloc(test,
sizeof(cbss) + sizeof(bss_ies),
GFP_KERNEL));
KUNIT_ASSERT_NOT_NULL(test, rcu_access_pointer(cbss.ies));
((struct cfg80211_bss_ies *)rcu_access_pointer(cbss.ies))->len = sizeof(bss_ies);
memcpy((void *)rcu_access_pointer(cbss.ies)->data, bss_ies,
sizeof(bss_ies));
rcu_read_lock();
elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss,
NULL, 0, &chanreq, &ap_chandef,
userspace_selectors);
rcu_read_unlock();
/* We do not need elems, free them if they are valid. */
if (!IS_ERR_OR_NULL(elems))
kfree(elems);
if (params->error) {
KUNIT_ASSERT_TRUE(test, IS_ERR(elems));
KUNIT_ASSERT_EQ(test, PTR_ERR(elems), -params->error);
} else {
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elems);
KUNIT_ASSERT_EQ(test, conn.mode, params->expected_mode);
KUNIT_ASSERT_EQ(test, conn.bw_limit, params->expected_bw_limit);
}
}
static struct kunit_case chan_mode_cases[] = {
KUNIT_CASE_PARAM(test_determine_chan_mode,
determine_chan_mode_gen_params),
{}
};
static struct kunit_suite chan_mode = {
.name = "mac80211-mlme-chan-mode",
.test_cases = chan_mode_cases,
};
kunit_test_suite(chan_mode);
+386
View File
@@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for element parsing
*
* Copyright (C) 2023-2025 Intel Corporation
*/
#include <kunit/test.h>
#include "../ieee80211_i.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static const struct mesh_preq_parse_test_case {
const char *desc;
u8 len;
bool ae_enabled;
u8 target_count;
bool result;
} mesh_preq_parse_cases[] = {
{
.desc = "shorter than header",
.len = 16,
.ae_enabled = false,
.target_count = 1,
.result = false,
},
{
.desc = "too short non AE, target count is not included",
.len = 29,
.ae_enabled = false,
.target_count = 1,
.result = false,
},
{
.desc = "too short non AE, target count is 1",
.len = 36,
.ae_enabled = false,
.target_count = 1,
.result = false,
},
{
.desc = "too short AE, target count is not included",
.len = 35,
.ae_enabled = true,
.target_count = 1,
.result = false,
},
{
.desc = "too short AE, target count is 1",
.len = 42,
.ae_enabled = true,
.target_count = 1,
.result = false,
},
{
.desc = "target count is zero",
.len = 26,
.ae_enabled = false,
.target_count = 0,
.result = false,
},
{
.desc = "target count is 21",
.len = 255,
.ae_enabled = false,
.target_count = 21,
.result = false,
},
{
.desc = "non AE, target count is 1",
.len = 37,
.ae_enabled = false,
.target_count = 1,
.result = true,
},
{
.desc = "non AE, target count is 20",
.len = 246,
.ae_enabled = false,
.target_count = 20,
.result = true,
},
{
.desc = "AE, target count is 1",
.len = 43,
.ae_enabled = true,
.target_count = 1,
.result = true,
},
{
.desc = "AE, target count is 20",
.len = 252,
.ae_enabled = true,
.target_count = 20,
.result = true,
},
};
KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc);
static const struct mesh_prep_parse_test_case {
const char *desc;
u8 len;
bool ae_enabled;
bool result;
} mesh_prep_parse_cases[] = {
{
.desc = "shorter than header",
.len = 12,
.ae_enabled = false,
.result = false,
},
{
.desc = "non AE short",
.len = 30,
.ae_enabled = false,
.result = false,
},
{
.desc = "non AE",
.len = 31,
.ae_enabled = false,
.result = true,
},
{
.desc = "AE short",
.len = 36,
.ae_enabled = true,
.result = false,
},
{
.desc = "AE",
.len = 37,
.ae_enabled = true,
.result = true,
},
};
KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc);
static const struct mesh_perr_parse_test_case {
const char *desc;
u8 len;
u8 number_of_dst;
int ae_enabled_idx;
bool result;
} mesh_perr_parse_cases[] = {
{
.desc = "shorter than header",
.len = 1,
.number_of_dst = 1,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 0",
.len = 2,
.number_of_dst = 0,
.ae_enabled_idx = -1,
.result = true,
},
{
.desc = "number_of_dst is 20",
.len = 255,
.number_of_dst = 20,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 1, non AE, short",
.len = 14,
.number_of_dst = 1,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 1, non AE",
.len = 15,
.number_of_dst = 1,
.ae_enabled_idx = -1,
.result = true,
},
{
.desc = "number_of_dst is 1, non AE, extra short dst header",
.len = 25,
.number_of_dst = 1,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 1, non AE, extra dst header",
.len = 26,
.number_of_dst = 1,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 1, AE, short",
.len = 20,
.number_of_dst = 1,
.ae_enabled_idx = 0,
.result = false,
},
{
.desc = "number_of_dst is 1, AE",
.len = 21,
.number_of_dst = 1,
.ae_enabled_idx = 0,
.result = true,
},
{
.desc = "number_of_dst is 19, non AE, short",
.len = 2 + 13 * 19 - 1,
.number_of_dst = 19,
.ae_enabled_idx = -1,
.result = false,
},
{
.desc = "number_of_dst is 19, non AE",
.len = 2 + 13 * 19,
.number_of_dst = 19,
.ae_enabled_idx = -1,
.result = true,
},
{
.desc = "number_of_dst is 19, AE, short",
.len = 2 + 13 * 19 + 6 - 1,
.number_of_dst = 19,
.ae_enabled_idx = 18,
.result = false,
},
{
.desc = "number_of_dst is 19, AE",
.len = 2 + 13 * 19 + 6,
.number_of_dst = 19,
.ae_enabled_idx = 18,
.result = true,
},
};
KUNIT_ARRAY_PARAM_DESC(mesh_perr_parse, mesh_perr_parse_cases, desc);
static void mle_defrag(struct kunit *test)
{
struct ieee80211_elems_parse_params parse_params = {
.link_id = 12,
.from_ap = true,
.mode = IEEE80211_CONN_MODE_EHT,
/* type is not really relevant here */
.type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON,
};
struct ieee802_11_elems *parsed;
struct sk_buff *skb;
u8 *len_mle, *len_prof;
int i;
skb = alloc_skb(1024, GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, skb);
if (skb_pad(skb, skb_tailroom(skb))) {
KUNIT_FAIL(test, "failed to pad skb");
return;
}
/* build a multi-link element */
skb_put_u8(skb, WLAN_EID_EXTENSION);
len_mle = skb_put(skb, 1);
skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK);
put_unaligned_le16(IEEE80211_ML_CONTROL_TYPE_BASIC,
skb_put(skb, 2));
/* struct ieee80211_mle_basic_common_info */
skb_put_u8(skb, 7); /* includes len field */
skb_put_data(skb, "\x00\x00\x00\x00\x00\x00", ETH_ALEN); /* MLD addr */
/* with a STA profile inside */
skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE);
len_prof = skb_put(skb, 1);
put_unaligned_le16(IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE |
parse_params.link_id,
skb_put(skb, 2));
skb_put_u8(skb, 1); /* fake sta_info_len - includes itself */
/* put a bunch of useless elements into it */
for (i = 0; i < 20; i++) {
skb_put_u8(skb, WLAN_EID_SSID);
skb_put_u8(skb, 20);
skb_put(skb, 20);
}
/* fragment STA profile */
ieee80211_fragment_element(skb, len_prof,
IEEE80211_MLE_SUBELEM_FRAGMENT);
/* fragment MLE */
ieee80211_fragment_element(skb, len_mle, WLAN_EID_FRAGMENT);
parse_params.start = skb->data;
parse_params.len = skb->len;
parsed = ieee802_11_parse_elems_full(&parse_params);
/* should return ERR_PTR or valid, not NULL */
KUNIT_EXPECT_NOT_NULL(test, parsed);
if (IS_ERR_OR_NULL(parsed))
goto free_skb;
KUNIT_EXPECT_NOT_NULL(test, parsed->ml_basic);
KUNIT_EXPECT_EQ(test,
parsed->ml_basic_len,
2 /* control */ +
7 /* common info */ +
2 /* sta profile element header */ +
3 /* sta profile header */ +
20 * 22 /* sta profile data */ +
2 /* sta profile fragment element */);
KUNIT_EXPECT_NOT_NULL(test, parsed->prof);
KUNIT_EXPECT_EQ(test,
parsed->sta_prof_len,
3 /* sta profile header */ +
20 * 22 /* sta profile data */);
kfree(parsed);
free_skb:
kfree_skb(skb);
}
static void mesh_preq_parse(struct kunit *test)
{
const struct mesh_preq_parse_test_case *params = test->param_value;
u8 data[64] = {};
struct ieee80211_mesh_hwmp_preq_top *top = (void *)data;
struct ieee80211_mesh_hwmp_preq_bottom *bottom;
top->flags = params->ae_enabled ? AE_F : 0;
bottom = ieee80211_mesh_hwmp_preq_get_bottom(data);
bottom->target_count = params->target_count;
KUNIT_EXPECT_EQ(test,
ieee80211_mesh_preq_size_ok(data, params->len),
params->result);
}
static void mesh_prep_parse(struct kunit *test)
{
const struct mesh_prep_parse_test_case *params = test->param_value;
u8 data[64] = {};
struct ieee80211_mesh_hwmp_prep_top *top = (void *)data;
top->flags = params->ae_enabled ? AE_F : 0;
KUNIT_EXPECT_EQ(test,
ieee80211_mesh_prep_size_ok(data, params->len),
params->result);
}
static void mesh_perr_parse(struct kunit *test)
{
const struct mesh_perr_parse_test_case *params = test->param_value;
u8 data[256] = {};
struct ieee80211_mesh_hwmp_perr *perr = (void *)data;
perr->number_of_dst = params->number_of_dst;
if (params->ae_enabled_idx > -1) {
struct ieee80211_mesh_hwmp_perr_dst *dst =
ieee80211_mesh_hwmp_perr_get_dst(
data, params->ae_enabled_idx);
dst->flags = AE_F;
}
KUNIT_EXPECT_EQ(test,
ieee80211_mesh_perr_size_ok(data, params->len),
params->result);
}
static struct kunit_case element_parsing_test_cases[] = {
KUNIT_CASE(mle_defrag),
KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params),
KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params),
KUNIT_CASE_PARAM(mesh_perr_parse, mesh_perr_parse_gen_params),
{}
};
static struct kunit_suite element_parsing = {
.name = "mac80211-element-parsing",
.test_cases = element_parsing_test_cases,
};
kunit_test_suite(element_parsing);
+286
View File
@@ -0,0 +1,286 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for management frame acceptance
*
* Copyright (C) 2023 Intel Corporation
*/
#include <kunit/test.h>
#include <kunit/skbuff.h>
#include "../ieee80211_i.h"
#include "../sta_info.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static const struct mfp_test_case {
const char *desc;
bool sta, mfp, decrypted, unicast, assoc;
u8 category;
u8 stype;
u8 action;
ieee80211_rx_result result;
} accept_mfp_cases[] = {
/* regular public action */
{
.desc = "public action: accept unicast from unknown peer",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.result = RX_CONTINUE,
},
{
.desc = "public action: accept multicast from unknown peer",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.result = RX_CONTINUE,
},
{
.desc = "public action: accept unicast without MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.sta = true,
.result = RX_CONTINUE,
},
{
.desc = "public action: accept multicast without MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.sta = true,
.result = RX_CONTINUE,
},
{
.desc = "public action: drop unicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.sta = true,
.mfp = true,
.result = RX_DROP_U_UNPROT_UNICAST_PUB_ACTION,
},
{
.desc = "public action: accept multicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PUBLIC,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.sta = true,
.mfp = true,
.result = RX_CONTINUE,
},
/* protected dual of public action */
{
.desc = "protected dual: drop unicast from unknown peer",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: drop multicast from unknown peer",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: drop unicast without MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.sta = true,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: drop multicast without MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.sta = true,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: drop undecrypted unicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = true,
.sta = true,
.mfp = true,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: drop undecrypted multicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.unicast = false,
.sta = true,
.mfp = true,
.result = RX_DROP_U_UNPROT_DUAL,
},
{
.desc = "protected dual: accept unicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.decrypted = true,
.unicast = true,
.sta = true,
.mfp = true,
.result = RX_CONTINUE,
},
{
.desc = "protected dual: accept multicast with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION,
.action = WLAN_PUB_ACTION_DSE_ENABLEMENT,
.decrypted = true,
.unicast = false,
.sta = true,
.mfp = true,
.result = RX_CONTINUE,
},
/* deauth/disassoc before keys are set */
{
.desc = "deauth: accept unicast with MFP but w/o key",
.stype = IEEE80211_STYPE_DEAUTH,
.sta = true,
.mfp = true,
.unicast = true,
.result = RX_CONTINUE,
},
{
.desc = "disassoc: accept unicast with MFP but w/o key",
.stype = IEEE80211_STYPE_DEAUTH,
.sta = true,
.mfp = true,
.unicast = true,
.result = RX_CONTINUE,
},
/* non-public robust action frame ... */
{
.desc = "BA action: drop unicast before assoc",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_BACK,
.unicast = true,
.sta = true,
.result = RX_DROP_U_UNPROT_ROBUST_ACTION,
},
{
.desc = "BA action: drop unprotected after assoc",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_BACK,
.unicast = true,
.sta = true,
.mfp = true,
.result = RX_DROP_U_UNPROT_UCAST_MGMT,
},
{
.desc = "BA action: accept unprotected without MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_BACK,
.unicast = true,
.sta = true,
.assoc = true,
.mfp = false,
.result = RX_CONTINUE,
},
{
.desc = "BA action: drop unprotected with MFP",
.stype = IEEE80211_STYPE_ACTION,
.category = WLAN_CATEGORY_BACK,
.unicast = true,
.sta = true,
.mfp = true,
.result = RX_DROP_U_UNPROT_UCAST_MGMT,
},
};
KUNIT_ARRAY_PARAM_DESC(accept_mfp, accept_mfp_cases, desc);
static void accept_mfp(struct kunit *test)
{
static struct sta_info sta;
const struct mfp_test_case *params = test->param_value;
struct ieee80211_rx_data rx = {
.sta = params->sta ? &sta : NULL,
};
struct ieee80211_rx_status *status;
struct ieee80211_hdr_3addr hdr = {
.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
params->stype),
.addr1 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
.addr2 = { 0x12, 0x22, 0x33, 0x44, 0x55, 0x66 },
/* A3/BSSID doesn't matter here */
};
memset(&sta, 0, sizeof(sta));
if (!params->sta) {
KUNIT_ASSERT_FALSE(test, params->mfp);
KUNIT_ASSERT_FALSE(test, params->decrypted);
}
if (params->mfp)
set_sta_flag(&sta, WLAN_STA_MFP);
if (params->assoc)
set_bit(WLAN_STA_ASSOC, &sta._flags);
rx.skb = kunit_zalloc_skb(test, 128, GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, rx.skb);
status = IEEE80211_SKB_RXCB(rx.skb);
if (params->decrypted) {
status->flag |= RX_FLAG_DECRYPTED;
if (params->unicast)
hdr.frame_control |=
cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
if (params->unicast)
hdr.addr1[0] = 0x02;
skb_put_data(rx.skb, &hdr, sizeof(hdr));
switch (params->stype) {
case IEEE80211_STYPE_ACTION:
skb_put_u8(rx.skb, params->category);
skb_put_u8(rx.skb, params->action);
break;
case IEEE80211_STYPE_DEAUTH:
case IEEE80211_STYPE_DISASSOC: {
__le16 reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED);
skb_put_data(rx.skb, &reason, sizeof(reason));
}
break;
}
KUNIT_EXPECT_EQ(test,
(__force u32)ieee80211_drop_unencrypted_mgmt(&rx),
(__force u32)params->result);
}
static struct kunit_case mfp_test_cases[] = {
KUNIT_CASE_PARAM(accept_mfp, accept_mfp_gen_params),
{}
};
static struct kunit_suite mfp = {
.name = "mac80211-mfp",
.test_cases = mfp_test_cases,
};
kunit_test_suite(mfp);
+10
View File
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This is just module boilerplate for the mac80211 kunit module.
*
* Copyright (C) 2023 Intel Corporation
*/
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tests for mac80211");
+356
View File
@@ -0,0 +1,356 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for S1G TIM PVB decoding. This test suite covers
* IEEE80211-2024 Annex L figures 8, 9, 10, 12, 13, 14. ADE mode
* is not covered as it is an optional encoding format and is not
* currently supported by mac80211.
*
* Copyright (C) 2025 Morse Micro
*/
#include <linux/ieee80211.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#define MAX_AID 128
#define BC(enc_mode, inverse, blk_off) \
((((blk_off) & 0x1f) << 3) | ((inverse) ? BIT(2) : 0) | \
((enc_mode) & 0x3))
static void byte_to_bitstr(u8 v, char *out)
{
for (int b = 7; b >= 0; b--)
*out++ = (v & BIT(b)) ? '1' : '0';
*out = '\0';
}
static void dump_tim_bits(struct kunit *test,
const struct ieee80211_tim_ie *tim, u8 tim_len)
{
const u8 *ptr = tim->virtual_map;
const u8 *end = (const u8 *)tim + tim_len;
unsigned int oct = 1;
unsigned int blk = 0;
char bits[9];
while (ptr < end) {
u8 ctrl = *ptr++;
u8 mode = ctrl & 0x03;
bool inverse = ctrl & BIT(2);
u8 blk_off = ctrl >> 3;
kunit_info(
test, "Block %u (ENC=%s, blk_off=%u, inverse=%u)", blk,
(mode == IEEE80211_S1G_TIM_ENC_MODE_BLOCK) ? "BLOCK" :
(mode == IEEE80211_S1G_TIM_ENC_MODE_SINGLE) ? "SINGLE" :
"OLB",
blk_off, inverse);
byte_to_bitstr(ctrl, bits);
kunit_info(test, " octet %2u (ctrl) : %s (0x%02x)", oct,
bits, ctrl);
++oct;
switch (mode) {
case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: {
u8 blkmap = *ptr++;
byte_to_bitstr(blkmap, bits);
kunit_info(test, " octet %2u (blk-map) : %s (0x%02x)",
oct, bits, blkmap);
++oct;
for (u8 sb = 0; sb < 8; sb++) {
if (!(blkmap & BIT(sb)))
continue;
u8 sub = *ptr++;
byte_to_bitstr(sub, bits);
kunit_info(
test,
" octet %2u (SB %2u) : %s (0x%02x)",
oct, sb, bits, sub);
++oct;
}
break;
}
case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: {
u8 single = *ptr++;
byte_to_bitstr(single, bits);
kunit_info(test, " octet %2u (single) : %s (0x%02x)",
oct, bits, single);
++oct;
break;
}
case IEEE80211_S1G_TIM_ENC_MODE_OLB: {
u8 len = *ptr++;
byte_to_bitstr(len, bits);
kunit_info(test, " octet %2u (len=%2u) : %s (0x%02x)",
oct, len, bits, len);
++oct;
for (u8 i = 0; i < len && ptr < end; i++) {
u8 sub = *ptr++;
byte_to_bitstr(sub, bits);
kunit_info(
test,
" octet %2u (SB %2u) : %s (0x%02x)",
oct, i, bits, sub);
++oct;
}
break;
}
default:
kunit_info(test, " ** unknown encoding 0x%x **", mode);
return;
}
blk++;
}
}
static void tim_push(u8 **p, u8 v)
{
*(*p)++ = v;
}
static void tim_begin(struct ieee80211_tim_ie *tim, u8 **p)
{
tim->dtim_count = 0;
tim->dtim_period = 1;
tim->bitmap_ctrl = 0;
*p = tim->virtual_map;
}
static u8 tim_end(struct ieee80211_tim_ie *tim, u8 *tail)
{
return tail - (u8 *)tim;
}
static void pvb_add_block_bitmap(u8 **p, u8 blk_off, bool inverse, u8 blk_bmap,
const u8 *subblocks)
{
u8 enc = IEEE80211_S1G_TIM_ENC_MODE_BLOCK;
u8 n = hweight8(blk_bmap);
tim_push(p, BC(enc, inverse, blk_off));
tim_push(p, blk_bmap);
for (u8 i = 0; i < n; i++)
tim_push(p, subblocks[i]);
}
static void pvb_add_single_aid(u8 **p, u8 blk_off, bool inverse, u8 single6)
{
u8 enc = IEEE80211_S1G_TIM_ENC_MODE_SINGLE;
tim_push(p, BC(enc, inverse, blk_off));
tim_push(p, single6 & GENMASK(5, 0));
}
static void pvb_add_olb(u8 **p, u8 blk_off, bool inverse, const u8 *subblocks,
u8 len)
{
u8 enc = IEEE80211_S1G_TIM_ENC_MODE_OLB;
tim_push(p, BC(enc, inverse, blk_off));
tim_push(p, len);
for (u8 i = 0; i < len; i++)
tim_push(p, subblocks[i]);
}
static void check_all_aids(struct kunit *test,
const struct ieee80211_tim_ie *tim, u8 tim_len,
const unsigned long *expected)
{
for (u16 aid = 1; aid <= MAX_AID; aid++) {
bool want = test_bit(aid, expected);
bool got = ieee80211_s1g_check_tim(tim, tim_len, aid);
KUNIT_ASSERT_EQ_MSG(test, got, want,
"AID %u mismatch (got=%d want=%d)", aid,
got, want);
}
}
static void fill_bitmap(unsigned long *bm, const u16 *list, size_t n)
{
size_t i;
bitmap_zero(bm, MAX_AID + 1);
for (i = 0; i < n; i++)
__set_bit(list[i], bm);
}
static void fill_bitmap_inverse(unsigned long *bm, u16 max_aid,
const u16 *except, size_t n_except)
{
bitmap_zero(bm, MAX_AID + 1);
for (u16 aid = 1; aid <= max_aid; aid++)
__set_bit(aid, bm);
for (size_t i = 0; i < n_except; i++)
if (except[i] <= max_aid)
__clear_bit(except[i], bm);
}
static void s1g_tim_block_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
static const u8 subblocks[] = {
0x42, /* SB m=0: AIDs 1,6 */
0xA0, /* SB m=2: AIDs 21,23 */
};
u8 blk_bmap = 0x05; /* bits 0 and 2 set */
bool inverse = false;
static const u16 set_list[] = { 1, 6, 21, 23 };
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
tim_len = tim_end(tim, p);
fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static void s1g_tim_single_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
bool inverse = false;
u8 blk_off = 0;
u8 single6 = 0x1f; /* 31 */
static const u16 set_list[] = { 31 };
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_single_aid(&p, blk_off, inverse, single6);
tim_len = tim_end(tim, p);
fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static void s1g_tim_olb_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
bool inverse = false;
u8 blk_off = 0;
static const u16 set_list[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33,
38, 45, 47, 49, 54, 61, 63, 65, 70 };
static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
0xA0, 0x42, 0xA0, 0x42 };
u8 len = ARRAY_SIZE(subblocks);
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_olb(&p, blk_off, inverse, subblocks, len);
tim_len = tim_end(tim, p);
fill_bitmap(exp, set_list, ARRAY_SIZE(set_list));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static void s1g_tim_inverse_block_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
/* Same sub-block content as Figure L-8, but inverse = true */
static const u8 subblocks[] = {
0x42, /* SB m=0: AIDs 1,6 */
0xA0, /* SB m=2: AIDs 21,23 */
};
u8 blk_bmap = 0x05;
bool inverse = true;
/* All AIDs except 1,6,21,23 are set */
static const u16 except[] = { 1, 6, 21, 23 };
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_block_bitmap(&p, 0, inverse, blk_bmap, subblocks);
tim_len = tim_end(tim, p);
fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static void s1g_tim_inverse_single_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
bool inverse = true;
u8 blk_off = 0;
u8 single6 = 0x1f; /* 31 */
/* All AIDs except 31 are set */
static const u16 except[] = { 31 };
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_single_aid(&p, blk_off, inverse, single6);
tim_len = tim_end(tim, p);
fill_bitmap_inverse(exp, 63, except, ARRAY_SIZE(except));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static void s1g_tim_inverse_olb_test(struct kunit *test)
{
u8 buf[256] = {};
struct ieee80211_tim_ie *tim = (void *)buf;
u8 *p, tim_len;
bool inverse = true;
u8 blk_off = 0, len;
/* All AIDs except the list below are set */
static const u16 except[] = { 1, 6, 13, 15, 17, 22, 29, 31, 33,
38, 45, 47, 49, 54, 61, 63, 65, 70 };
static const u8 subblocks[] = { 0x42, 0xA0, 0x42, 0xA0, 0x42,
0xA0, 0x42, 0xA0, 0x42 };
len = ARRAY_SIZE(subblocks);
DECLARE_BITMAP(exp, MAX_AID + 1);
tim_begin(tim, &p);
pvb_add_olb(&p, blk_off, inverse, subblocks, len);
tim_len = tim_end(tim, p);
fill_bitmap_inverse(exp, 127, except, ARRAY_SIZE(except));
dump_tim_bits(test, tim, tim_len);
check_all_aids(test, tim, tim_len, exp);
}
static struct kunit_case s1g_tim_test_cases[] = {
KUNIT_CASE(s1g_tim_block_test),
KUNIT_CASE(s1g_tim_single_test),
KUNIT_CASE(s1g_tim_olb_test),
KUNIT_CASE(s1g_tim_inverse_block_test),
KUNIT_CASE(s1g_tim_inverse_single_test),
KUNIT_CASE(s1g_tim_inverse_olb_test),
{}
};
static struct kunit_suite s1g_tim = {
.name = "mac80211-s1g-tim",
.test_cases = s1g_tim_test_cases,
};
kunit_test_suite(s1g_tim);
+284
View File
@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for TPE element handling
*
* Copyright (C) 2024 Intel Corporation
*/
#include <kunit/test.h>
#include "../ieee80211_i.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct ieee80211_channel chan6g_1 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5955,
};
static struct ieee80211_channel chan6g_33 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 6115,
};
static struct ieee80211_channel chan6g_61 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 6255,
};
static const struct subchan_test_case {
const char *desc;
struct cfg80211_chan_def c;
u8 n;
int expect;
} subchan_offset_cases[] = {
{
.desc = "identical 20 MHz",
.c.width = NL80211_CHAN_WIDTH_20,
.c.chan = &chan6g_1,
.c.center_freq1 = 5955,
.n = 1,
.expect = 0,
},
{
.desc = "identical 40 MHz",
.c.width = NL80211_CHAN_WIDTH_40,
.c.chan = &chan6g_1,
.c.center_freq1 = 5965,
.n = 2,
.expect = 0,
},
{
.desc = "identical 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_1,
.c.center_freq1 = 5985,
.c.center_freq2 = 6225,
.n = 16,
.expect = 0,
},
{
.desc = "identical 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_1,
.c.center_freq1 = 6105,
.n = 16,
.expect = 0,
},
{
.desc = "lower 160 MHz of 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_1,
.c.center_freq1 = 6105,
.n = 8,
.expect = 0,
},
{
.desc = "upper 160 MHz of 320 MHz",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_61,
.c.center_freq1 = 6105,
.n = 8,
.expect = 8,
},
{
.desc = "upper 160 MHz of 320 MHz, go to 40",
.c.width = NL80211_CHAN_WIDTH_320,
.c.chan = &chan6g_61,
.c.center_freq1 = 6105,
.n = 2,
.expect = 8 + 4 + 2,
},
{
.desc = "secondary 80 above primary in 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_1,
.c.center_freq1 = 5985,
.c.center_freq2 = 6225,
.n = 4,
.expect = 0,
},
{
.desc = "secondary 80 below primary in 80+80 MHz",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_61,
.c.center_freq1 = 6225,
.c.center_freq2 = 5985,
.n = 4,
.expect = 4,
},
{
.desc = "secondary 80 below primary in 80+80 MHz, go to 20",
/* not really is valid? doesn't matter for the test */
.c.width = NL80211_CHAN_WIDTH_80P80,
.c.chan = &chan6g_61,
.c.center_freq1 = 6225,
.c.center_freq2 = 5985,
.n = 1,
.expect = 7,
},
};
KUNIT_ARRAY_PARAM_DESC(subchan_offset, subchan_offset_cases, desc);
static void subchan_offset(struct kunit *test)
{
const struct subchan_test_case *params = test->param_value;
int offset;
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->c), true);
offset = ieee80211_calc_chandef_subchan_offset(&params->c, params->n);
KUNIT_EXPECT_EQ(test, params->expect, offset);
}
static const struct psd_reorder_test_case {
const char *desc;
struct cfg80211_chan_def ap, used;
struct ieee80211_parsed_tpe_psd psd, out;
} psd_reorder_cases[] = {
{
.desc = "no changes, 320 MHz",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_320,
.used.chan = &chan6g_1,
.used.center_freq1 = 6105,
.psd.valid = true,
.psd.count = 16,
.psd.n = 8,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 16,
.out.n = 8,
.out.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
},
{
.desc = "no changes, 320 MHz, 160 MHz used, n=0",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_1,
.used.center_freq1 = 6025,
.psd.valid = true,
.psd.count = 16,
.psd.n = 0,
.psd.power = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
.out.valid = true,
.out.count = 8,
.out.n = 0,
.out.power = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
},
{
.desc = "320 MHz, HE is 80, used 160, all lower",
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_1,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_1,
.used.center_freq1 = 6025,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 0, 1, 2, 3, 4, 5, 6, 7, 127, 127, 127, 127, 127, 127, 127, 127},
},
{
.desc = "320 MHz, HE is 80, used 160, all upper",
/*
* EHT: | | | | | | | | | | | | | | | | |
* HE: | | | | |
* used: | | | | | | | | |
*/
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_61,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_61,
.used.center_freq1 = 6185,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 12, 13, 14, 15, 0, 1, 2, 3, 127, 127, 127, 127, 127, 127, 127, 127},
},
{
.desc = "320 MHz, HE is 80, used 160, split",
/*
* EHT: | | | | | | | | | | | | | | | | |
* HE: | | | | |
* used: | | | | | | | | |
*/
.ap.width = NL80211_CHAN_WIDTH_320,
.ap.chan = &chan6g_33,
.ap.center_freq1 = 6105,
.used.width = NL80211_CHAN_WIDTH_160,
.used.chan = &chan6g_33,
.used.center_freq1 = 6185,
.psd.valid = true,
.psd.count = 16,
.psd.n = 4,
.psd.power = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
.out.valid = true,
.out.count = 8,
.out.n = 4,
.out.power = { 0, 1, 2, 3, 12, 13, 14, 15, 127, 127, 127, 127, 127, 127, 127, 127},
},
};
KUNIT_ARRAY_PARAM_DESC(psd_reorder, psd_reorder_cases, desc);
static void psd_reorder(struct kunit *test)
{
const struct psd_reorder_test_case *params = test->param_value;
struct ieee80211_parsed_tpe_psd tmp = params->psd;
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->ap), true);
KUNIT_ASSERT_EQ(test, cfg80211_chandef_valid(&params->used), true);
ieee80211_rearrange_tpe_psd(&tmp, &params->ap, &params->used);
KUNIT_EXPECT_MEMEQ(test, &tmp, &params->out, sizeof(tmp));
}
static struct kunit_case tpe_test_cases[] = {
KUNIT_CASE_PARAM(subchan_offset, subchan_offset_gen_params),
KUNIT_CASE_PARAM(psd_reorder, psd_reorder_gen_params),
{}
};
static struct kunit_suite tpe = {
.name = "mac80211-tpe",
.test_cases = tpe_test_cases,
};
kunit_test_suite(tpe);
+175
View File
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for negotiated TTLM (TID-To-Link Mapping) parsing
*
* Copyright (C) 2026 Michael Bommarito <michael.bommarito@gmail.com>
*/
#include <kunit/test.h>
#include <linux/ieee80211.h>
#include "../ieee80211_i.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
/*
* Build a negotiated TTLM element in caller-supplied buffer.
*
* @buf: destination buffer (must be at least elem_size bytes)
* @elem_size: sizeof(ttlm_elem) + 1 (presence byte) + npresent * bm_size
* @presence: link_map_presence bitmask; each set bit => one map follows
* @bm_size: bytes per map (1 or 2); 2 => LINK_MAP_SIZE bit clear
* @maps: array of npresent u16 maps, one per set bit in presence
*
* Control field encodes direction=BOTH; no switch-time, no expected-dur,
* no DEF_LINK_MAP. LINK_MAP_SIZE bit is set iff bm_size==1.
*
* Returns pointer to the ieee80211_ttlm_elem at buf.
*/
static const struct ieee80211_ttlm_elem *
build_neg_ttlm_elem(u8 *buf, size_t elem_size,
u8 presence, u8 bm_size, const u16 *maps)
{
struct ieee80211_ttlm_elem *t = (void *)buf;
u8 control;
u8 *pos;
int i, tid;
memset(buf, 0, elem_size);
control = IEEE80211_TTLM_DIRECTION_BOTH; /* bits [1:0] = 2 */
if (bm_size == 1)
control |= IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE;
t->control = control;
pos = (u8 *)t->optional;
*pos++ = presence;
i = 0;
for (tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) {
if (!(presence & BIT(tid)))
continue;
if (bm_size == 1)
*pos = (u8)maps[i];
else
put_unaligned_le16(maps[i], pos);
pos += bm_size;
i++;
}
return t;
}
/*
* sparse_presence_no_oob_read - BIT(0)|BIT(7) presence, bm_size=2
*
* Only TID 0 and TID 7 have maps; TIDs 1-6 are absent. Element length
* is exactly 6 bytes (1 control + 1 presence + 2 * 2-byte maps).
*
* Pre-fix the parser advanced pos by bm_size AFTER the switch() block
* (i.e. unconditionally for every TID), so when processing TID 7 it
* had already advanced 6 * bm_size = 12 bytes past the presence byte
* for the absent TIDs before reading the TID-7 map - 14 bytes past the
* end of the 2-byte TID-7 map. Under KASAN that is a slab-out-of-bounds.
*
* After the fix pos is advanced only inside the presence-bit branch so
* the cursor lands exactly at end-of-element after processing TID 7.
*/
static void sparse_presence_no_oob_read(struct kunit *test)
{
/*
* presence = BIT(0)|BIT(7): 2 maps present.
* elem_size = sizeof(ttlm_elem) + 1 (presence) + 2*2 (maps) = 6.
*/
const u8 presence = BIT(0) | BIT(7);
const u8 bm_size = 2;
const int npresent = 2;
const size_t elem_size = sizeof(struct ieee80211_ttlm_elem)
+ 1 + npresent * bm_size;
/*
* Allocate exact-size buffer so a pre-fix OOB read walks into the
* KASAN red zone immediately after the allocation.
*/
u8 *buf = kunit_kzalloc(test, elem_size, GFP_KERNEL);
const struct ieee80211_ttlm_elem *ttlm;
struct ieee80211_neg_ttlm neg_ttlm = {};
/* Non-zero maps so the parser does not reject with -EINVAL. */
const u16 maps[2] = { 0x0001, 0x0001 };
u8 direction = 0;
int ret;
KUNIT_ASSERT_NOT_NULL(test, buf);
ttlm = build_neg_ttlm_elem(buf, elem_size, presence, bm_size, maps);
/*
* Pass NULL for sdata: the only sdata dereference in this code path
* is inside mlme_dbg() on error returns, which are guarded by
* MAC80211_MLME_DEBUG == 0 in non-debug builds and by the dead-code
* eliminator in KUnit builds. The success path does not touch sdata.
*/
ret = ieee80211_parse_neg_ttlm(NULL, ttlm, &neg_ttlm, &direction);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)direction, IEEE80211_TTLM_DIRECTION_BOTH);
/* TID 0: map present */
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[0], 0x0001);
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[0], 0x0001);
/* TID 3: absent => map should be 0 */
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[3], 0);
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[3], 0);
/* TID 7: map present */
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[7], 0x0001);
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[7], 0x0001);
}
/*
* dense_presence_baseline - presence=0xff (all 8 TIDs), bm_size=2
*
* Every TID has a map; this is the dense layout the parser handled
* correctly even before the fix. Confirms the cursor-advance fix
* does not regress the already-correct path.
*/
static void dense_presence_baseline(struct kunit *test)
{
const u8 presence = 0xff;
const u8 bm_size = 2;
const int npresent = 8;
const size_t elem_size = sizeof(struct ieee80211_ttlm_elem)
+ 1 + npresent * bm_size;
u8 *buf = kunit_kzalloc(test, elem_size, GFP_KERNEL);
const struct ieee80211_ttlm_elem *ttlm;
struct ieee80211_neg_ttlm neg_ttlm = {};
const u16 maps[8] = {
0x0003, 0x0003, 0x0003, 0x0003,
0x0003, 0x0003, 0x0003, 0x0003,
};
u8 direction = 0;
int ret;
KUNIT_ASSERT_NOT_NULL(test, buf);
ttlm = build_neg_ttlm_elem(buf, elem_size, presence, bm_size, maps);
ret = ieee80211_parse_neg_ttlm(NULL, ttlm, &neg_ttlm, &direction);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, (int)direction, IEEE80211_TTLM_DIRECTION_BOTH);
/* All TIDs present: every downlink/uplink entry must be 0x0003. */
for (int tid = 0; tid < IEEE80211_TTLM_NUM_TIDS; tid++) {
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.downlink[tid], 0x0003);
KUNIT_EXPECT_EQ(test, (int)neg_ttlm.uplink[tid], 0x0003);
}
}
static struct kunit_case mac80211_ttlm_test_cases[] = {
KUNIT_CASE(sparse_presence_no_oob_read),
KUNIT_CASE(dense_presence_baseline),
{}
};
static struct kunit_suite mac80211_ttlm = {
.name = "mac80211-ttlm",
.test_cases = mac80211_ttlm_test_cases,
};
kunit_test_suite(mac80211_ttlm);
+309
View File
@@ -0,0 +1,309 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Utilities for mac80211 unit testing
*
* Copyright (C) 2024 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <net/mac80211.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include "util.h"
#define CHAN2G(_freq) { \
.band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_freq), \
}
static const struct ieee80211_channel 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 */
};
#define CHAN5G(_freq) { \
.band = NL80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_freq), \
}
static const struct ieee80211_channel channels_5ghz[] = {
CHAN5G(5180), /* Channel 36 */
CHAN5G(5200), /* Channel 40 */
CHAN5G(5220), /* Channel 44 */
CHAN5G(5240), /* Channel 48 */
};
static const struct ieee80211_rate bitrates[] = {
{ .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 }
};
/* Copied from hwsim except that it only supports 4 EHT streams and STA/P2P mode */
static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
{
.types_mask = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT),
.he_cap = {
.has_he = true,
.he_cap_elem = {
.mac_cap_info[0] =
IEEE80211_HE_MAC_CAP0_HTC_HE,
.mac_cap_info[1] =
IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
.mac_cap_info[2] =
IEEE80211_HE_MAC_CAP2_BSR |
IEEE80211_HE_MAC_CAP2_MU_CASCADING |
IEEE80211_HE_MAC_CAP2_ACK_EN,
.mac_cap_info[3] =
IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
.mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
.phy_cap_info[0] =
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
.phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
.phy_cap_info[2] =
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
/* Leave all the other PHY capability bytes
* unset, as DCM, beam forming, RU and PPE
* threshold information are not supported
*/
},
.he_mcs_nss_supp = {
.rx_mcs_80 = cpu_to_le16(0xfffa),
.tx_mcs_80 = cpu_to_le16(0xfffa),
.rx_mcs_160 = cpu_to_le16(0xfffa),
.tx_mcs_160 = cpu_to_le16(0xfffa),
.rx_mcs_80p80 = cpu_to_le16(0xfffa),
.tx_mcs_80p80 = cpu_to_le16(0xfffa),
},
},
.eht_cap = {
.has_eht = true,
.eht_cap_elem = {
.mac_cap_info[0] =
IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS |
IEEE80211_EHT_MAC_CAP0_OM_CONTROL |
IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1,
.phy_cap_info[0] =
IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER |
IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE |
IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK,
.phy_cap_info[1] =
IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK |
IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK,
.phy_cap_info[2] =
IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK |
IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK,
.phy_cap_info[3] =
IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
.phy_cap_info[4] =
IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
IEEE80211_EHT_PHY_CAP4_PSR_SR_SUPP |
IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI |
IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK,
.phy_cap_info[5] =
IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT |
IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK |
IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK,
.phy_cap_info[6] =
IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK |
IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK,
.phy_cap_info[7] =
IEEE80211_EHT_PHY_CAP7_20MHZ_STA_RX_NDP_WIDER_BW |
IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ |
IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ,
},
/* For all MCS and bandwidth, set 4 NSS for both Tx and
* Rx
*/
.eht_mcs_nss_supp = {
/*
* As B1 and B2 are set in the supported
* channel width set field in the HE PHY
* capabilities information field include all
* the following MCS/NSS.
*/
.bw._80 = {
.rx_tx_mcs9_max_nss = 0x44,
.rx_tx_mcs11_max_nss = 0x44,
.rx_tx_mcs13_max_nss = 0x44,
},
.bw._160 = {
.rx_tx_mcs9_max_nss = 0x44,
.rx_tx_mcs11_max_nss = 0x44,
.rx_tx_mcs13_max_nss = 0x44,
},
},
/* PPE threshold information is not supported */
},
},
};
int t_sdata_init(struct kunit_resource *resource, void *ctx)
{
struct kunit *test = kunit_get_current_test();
struct t_sdata *t_sdata;
t_sdata = kzalloc_obj(*t_sdata);
KUNIT_ASSERT_NOT_NULL(test, t_sdata);
resource->data = t_sdata;
resource->name = "sdata";
t_sdata->sdata = kzalloc_obj(*t_sdata->sdata);
KUNIT_ASSERT_NOT_NULL(test, t_sdata->sdata);
t_sdata->wiphy = kzalloc_obj(*t_sdata->wiphy);
KUNIT_ASSERT_NOT_NULL(test, t_sdata->wiphy);
strscpy(t_sdata->sdata->name, "kunit");
t_sdata->sdata->local = &t_sdata->local;
t_sdata->sdata->local->hw.wiphy = t_sdata->wiphy;
t_sdata->sdata->wdev.wiphy = t_sdata->wiphy;
t_sdata->sdata->vif.type = NL80211_IFTYPE_STATION;
t_sdata->sdata->deflink.sdata = t_sdata->sdata;
t_sdata->sdata->deflink.link_id = 0;
t_sdata->wiphy->bands[NL80211_BAND_2GHZ] = &t_sdata->band_2ghz;
t_sdata->wiphy->bands[NL80211_BAND_5GHZ] = &t_sdata->band_5ghz;
for (int band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
struct ieee80211_supported_band *sband;
sband = t_sdata->wiphy->bands[band];
sband->band = band;
sband->bitrates =
kmemdup(bitrates, sizeof(bitrates), GFP_KERNEL);
sband->n_bitrates = ARRAY_SIZE(bitrates);
/* Initialize channels, feel free to add more channels/bands */
switch (band) {
case NL80211_BAND_2GHZ:
sband->channels = kmemdup(channels_2ghz,
sizeof(channels_2ghz),
GFP_KERNEL);
sband->n_channels = ARRAY_SIZE(channels_2ghz);
sband->bitrates = kmemdup(bitrates,
sizeof(bitrates),
GFP_KERNEL);
sband->n_bitrates = ARRAY_SIZE(bitrates);
break;
case NL80211_BAND_5GHZ:
sband->channels = kmemdup(channels_5ghz,
sizeof(channels_5ghz),
GFP_KERNEL);
sband->n_channels = ARRAY_SIZE(channels_5ghz);
sband->bitrates = kmemdup(bitrates,
sizeof(bitrates),
GFP_KERNEL);
sband->n_bitrates = ARRAY_SIZE(bitrates);
sband->vht_cap.vht_supported = true;
sband->vht_cap.cap =
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_SHORT_GI_80 |
IEEE80211_VHT_CAP_SHORT_GI_160 |
IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_4 |
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
sband->vht_cap.vht_mcs.rx_mcs_map =
cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
IEEE80211_VHT_MCS_SUPPORT_0_9 << 6);
sband->vht_cap.vht_mcs.tx_mcs_map =
sband->vht_cap.vht_mcs.rx_mcs_map;
break;
default:
continue;
}
sband->ht_cap.ht_supported = band != NL80211_BAND_6GHZ;
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40;
sband->ht_cap.ampdu_factor = 0x3;
sband->ht_cap.ampdu_density = 0x6;
memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
sband->ht_cap.mcs.rx_mask[0] = 0xff;
sband->ht_cap.mcs.rx_mask[1] = 0xff;
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
ieee80211_set_sband_iftype_data(&t_sdata->band_5ghz, sband_capa_5ghz);
return 0;
}
void t_sdata_exit(struct kunit_resource *resource)
{
struct t_sdata *t_sdata = resource->data;
kfree(t_sdata->band_2ghz.channels);
kfree(t_sdata->band_2ghz.bitrates);
kfree(t_sdata->band_5ghz.channels);
kfree(t_sdata->band_5ghz.bitrates);
kfree(t_sdata->sdata);
kfree(t_sdata->wiphy);
kfree(t_sdata);
}
+36
View File
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Utilities for mac80211 unit testing
*
* Copyright (C) 2024 Intel Corporation
*/
#ifndef __MAC80211_UTILS_H
#define __MAC80211_UTILS_H
#include "../ieee80211_i.h"
struct t_sdata {
struct ieee80211_sub_if_data *sdata;
struct wiphy *wiphy;
struct ieee80211_local local;
void *ctx;
struct ieee80211_supported_band band_2ghz;
struct ieee80211_supported_band band_5ghz;
};
#define T_SDATA(test) ({ \
struct t_sdata *__t_sdata = \
kunit_alloc_resource(test, t_sdata_init, \
t_sdata_exit, \
GFP_KERNEL, NULL); \
\
KUNIT_ASSERT_NOT_NULL(test, __t_sdata); \
__t_sdata; \
})
int t_sdata_init(struct kunit_resource *resource, void *data);
void t_sdata_exit(struct kunit_resource *resource);
#endif /* __MAC80211_UTILS_H */