err = parse_random_mac_addr(msg, v[0] + 9);
if (err)
goto nla_put_failure;
+ } else if (!strncmp(v[0], "coloc", 5)) {
+ flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+ } else if (!strncmp(v[0], "flush", 5)) {
+ flags |= NL80211_SCAN_FLAG_FLUSH;
} else {
/* this element is not for us, so
* return to continue parsing.
nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
if (have_freqs)
nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+ else
+ flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
if (have_matchset)
nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, matchset);
if (have_plans)
} else if (strcmp(argv[i], "ap-force") == 0) {
flags |= NL80211_SCAN_FLAG_AP;
break;
+ } else if (strcmp(argv[i], "coloc") == 0) {
+ flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+ break;
} else if (strcmp(argv[i], "duration-mandatory") == 0) {
duration_mandatory = true;
break;
if (have_freqs)
nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+ else
+ flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
if (flags)
NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, flags);
if (duration)
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- __u64 capa = data[0] |
- data[1] << 8 |
- data[2] << 16 |
- data[3] << 24 |
+ __u64 capa = ((__u64) data[0]) |
+ ((__u64) data[1]) << 8 |
+ ((__u64) data[2]) << 16 |
+ ((__u64) data[3]) << 24 |
((__u64) data[4]) << 32;
printf("\n");
printf("\n");
}
+static void print_ap_channel_report(const uint8_t type, uint8_t len, const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ uint8_t oper_class = data[0];
+ int i;
+
+ printf("\n");
+ printf("\t\t * operating class: %d\n", oper_class);
+ printf("\t\t * channel(s):");
+ for (i = 1; i < len; ++i) {
+ printf(" %d", data[i]);
+ }
+ printf("\n");
+}
+
static void print_cipher(const uint8_t *data)
{
if (memcmp(data, ms_oui, 3) == 0) {
printf(" SPP-AMSDU-capable");
if (capa & 0x0800)
printf(" SPP-AMSDU-required");
+ if (capa & 0x2000)
+ printf(" Extended-Key-ID");
printf(" (0x%.4x)\n", capa);
data += 2;
len -= 2;
printf("\t\tVenue Type: %i\n", (int)(data[2]));
}
if (len == 9)
- printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
data[3], data[4], data[5], data[6], data[7], data[8]);
else if (len == 7)
- printf("\t\tHESSID: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ printf("\t\tHESSID: %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
data[1], data[2], data[3], data[4], data[5], data[6]);
}
printf("Invalid IE length.\n");
} else {
for (idx = 0; idx < ln0; idx++) {
- printf("%02hx", data[2 + idx]);
+ printf("%02hhx", data[2 + idx]);
}
printf("\n");
}
printf("Invalid IE length.\n");
} else {
for (idx = 0; idx < ln1; idx++) {
- printf("%02hx", data[2 + ln0 + idx]);
+ printf("%02hhx", data[2 + ln0 + idx]);
}
printf("\n");
}
printf("Invalid IE length.\n");
} else {
for (idx = 0; idx < ln2; idx++) {
- printf("%02hx", data[2 + ln0 + ln1 + idx]);
+ printf("%02hhx", data[2 + ln0 + ln1 + idx]);
}
printf("\n");
}
CAPA(72, "Reserved");
CAPA(73, "Extended Spectrum Management Capable");
CAPA(74, "Reserved");
+ CAPA(77, "TWT Requester Support");
+ CAPA(78, "TWT Responder Support");
+ CAPA(79, "OBSS Narrow Bandwith RU in UL OFDMA Tolerance Support");
+
default:
- printf(" %d", bit);
+ printf(" %d", bit + base);
break;
}
#undef ADD_BIT_VAL
const struct print_ies_data *ie_buffer)
{
printf("\n");
- print_vht_info(data[0] | (data[1] << 8) |
- (data[2] << 16) | (data[3] << 24),
+ print_vht_info((__u32) data[0] | ((__u32)data[1] << 8) |
+ ((__u32)data[2] << 16) | ((__u32)data[3] << 24),
data + 4);
}
printf("\n");
printf("\t\t * interval: %d TUs\n", data[0]);
- if(len <= 1)
+ if (len <= 1)
return;
p = (uint8_t *) data + 1;
printf("\t\t\t Mesh Power Save Level\n");
}
+static void print_s1g_capa(const uint8_t type, uint8_t len,
+ const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ printf("\n");
+ print_s1g_capability(data);
+}
+
+static void print_short_beacon_int(const uint8_t type, uint8_t len,
+ const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ printf(" %d\n", (data[1] << 8) | data[0]);
+}
+
+static void print_s1g_oper(const uint8_t type, uint8_t len,
+ const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ int oper_ch_width, prim_ch_width;
+ int prim_ch_width_subfield = data[0] & 0x1;
+
+ prim_ch_width = 2;
+
+ /* B1-B4 BSS channel width subfield */
+ switch ((data[0] >> 1) & 0xf) {
+ case 0:
+ oper_ch_width = 1;
+ prim_ch_width = 1;
+ if (!prim_ch_width_subfield) {
+ oper_ch_width = -1;
+ prim_ch_width = -1;
+ }
+ break;
+ case 1:
+ oper_ch_width = 2;
+ if (prim_ch_width_subfield)
+ prim_ch_width = 1;
+ break;
+ case 3:
+ oper_ch_width = 4;
+ if (prim_ch_width_subfield)
+ prim_ch_width = 1;
+ break;
+ case 7:
+ oper_ch_width = 8;
+ if (prim_ch_width_subfield)
+ prim_ch_width = 1;
+ break;
+ case 15:
+ oper_ch_width = 16;
+ if (prim_ch_width_subfield)
+ prim_ch_width = 1;
+ break;
+ default:
+ oper_ch_width = -1;
+ prim_ch_width = -1;
+ break;
+ }
+
+ printf("\n");
+ printf("\t\tChannel width:\n");
+ if (oper_ch_width == -1 || prim_ch_width == -1) {
+ printf("\t\t\tBSS primary channel width: invalid\n");
+ printf("\t\t\tBSS operating channel width: invalid\n");
+ } else {
+ printf("\t\t\tBSS primary channel width: %d MHz\n", prim_ch_width);
+ printf("\t\t\tBSS operating channel width: %d MHz\n", oper_ch_width);
+ }
+ if (data[0] & BIT(5))
+ printf("\t\t\t1 MHz primary channel located at the lower side of 2 MHz\n");
+ else
+ printf("\t\t\t1 MHz primary channel located at the upper side of 2 MHz\n");
+
+ if (data[0] & BIT(7))
+ printf("\t\t\tMCS 10 not recommended\n");
+
+ printf("\t\t* operating class: %d\n", data[1]);
+ printf("\t\t* primary channel number: %d\n", data[2]);
+
+ printf("\t\t* channel index: %d\n", data[3]);
+
+ printf("\t\tMax S1G MCS Map:\n");
+ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support((data[4] >> 2) & 0x3));
+ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((data[4] >> 6) & 0x3));
+ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((data[5] >> 2) & 0x3));
+ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support((data[5] >> 6) & 0x3));
+
+ printf("\t\tMin S1G MCS Map:\n");
+ printf("\t\t\tFor 1 SS: %s\n", s1g_ss_min_support(data[4] & 0x3));
+ printf("\t\t\tFor 2 SS: %s\n", s1g_ss_min_support((data[4] >> 4) & 0x3));
+ printf("\t\t\tFor 3 SS: %s\n", s1g_ss_min_support(data[5] & 0x3));
+ printf("\t\t\tFor 4 SS: %s\n", s1g_ss_min_support((data[5] >> 4) & 0x3));
+}
+
struct ie_print {
const char *name;
void (*print)(const uint8_t type, uint8_t len, const uint8_t *data,
}
static const struct ie_print ieprinters[] = {
- [0] = { "SSID", print_ssid, 0, 32, BIT(PRINT_SCAN) | BIT(PRINT_LINK), },
+ [0] = { "SSID", print_ssid, 0, 32,
+ BIT(PRINT_SCAN) | BIT(PRINT_LINK) | BIT(PRINT_LINK_MLO_MLD), },
[1] = { "Supported rates", print_supprates, 0, 255, BIT(PRINT_SCAN), },
[3] = { "DS Parameter set", print_ds, 1, 1, BIT(PRINT_SCAN), },
[5] = { "TIM", print_tim, 4, 255, BIT(PRINT_SCAN), },
[42] = { "ERP", print_erp, 1, 255, BIT(PRINT_SCAN), },
[45] = { "HT capabilities", print_ht_capa, 26, 26, BIT(PRINT_SCAN), },
[47] = { "ERP D4.0", print_erp, 1, 255, BIT(PRINT_SCAN), },
+ [51] = { "AP Channel Report", print_ap_channel_report, 1, 255, BIT(PRINT_SCAN), },
[59] = { "Supported operating classes", print_supp_op_classes, 1, 255, BIT(PRINT_SCAN), },
[66] = { "Measurement Pilot Transmission", print_measurement_pilot_tx, 1, 255, BIT(PRINT_SCAN), },
[74] = { "Overlapping BSS scan params", print_obss_scan_params, 14, 255, BIT(PRINT_SCAN), },
[108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), },
[111] = { "802.11u Roaming Consortium", print_11u_rcon, 2, 255, BIT(PRINT_SCAN), },
[195] = { "Transmit Power Envelope", print_tx_power_envelope, 2, 5, BIT(PRINT_SCAN), },
+ [214] = { "Short beacon interval", print_short_beacon_int, 2, 2, BIT(PRINT_SCAN), },
+ [217] = { "S1G capabilities", print_s1g_capa, 15, 15, BIT(PRINT_SCAN), },
+ [232] = { "S1G operation", print_s1g_oper, 6, 6, BIT(PRINT_SCAN), },
};
static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data,
while (len >= 4) {
subtype = (data[0] << 8) + data[1];
sublen = (data[2] << 8) + data[3];
- if (sublen > len)
+ if (sublen > len - 4)
break;
switch (subtype) {
case 0x104a:
tab_on_first(&first);
+ if (sublen < 1) {
+ printf("\t * Version: (invalid "
+ "length %d)\n", sublen);
+ break;
+ }
printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
break;
case 0x1011:
uint16_t id;
tab_on_first(&first);
if (sublen != 2) {
- printf("\t * Device Password ID: (invalid "
- "length %d)\n", sublen);
+ printf("\t * Device Password ID: (invalid length %d)\n",
+ sublen);
break;
}
id = data[4] << 8 | data[5];
printf("\t * Model Number: %.*s\n", sublen, data + 4);
break;
case 0x103b: {
- __u8 val = data[4];
+ __u8 val;
+
+ if (sublen < 1) {
+ printf("\t * Response Type: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ val = data[4];
tab_on_first(&first);
printf("\t * Response Type: %d%s\n",
val, val == 3 ? " (AP)" : "");
break;
}
case 0x103c: {
- __u8 val = data[4];
+ __u8 val;
+
+ if (sublen < 1) {
+ printf("\t * RF Bands: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ val = data[4];
tab_on_first(&first);
printf("\t * RF Bands: 0x%x\n", val);
break;
}
case 0x1041: {
- __u8 val = data[4];
+ __u8 val;
+
+ if (sublen < 1) {
+ printf("\t * Selected Registrar: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ val = data[4];
tab_on_first(&first);
printf("\t * Selected Registrar: 0x%x\n", val);
break;
printf("\t * Serial Number: %.*s\n", sublen, data + 4);
break;
case 0x1044: {
- __u8 val = data[4];
+ __u8 val;
+
+ if (sublen < 1) {
+ printf("\t * Wi-Fi Protected Setup State: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ val = data[4];
tab_on_first(&first);
printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
val,
data[12], data[13], data[14], data[15],
data[16], data[17], data[18], data[19]);
break;
+ case 0x1049:
+ tab_on_first(&first);
+ if (sublen == 6 &&
+ data[4] == 0x00 &&
+ data[5] == 0x37 &&
+ data[6] == 0x2a &&
+ data[7] == 0x00 &&
+ data[8] == 0x01) {
+ uint8_t v2 = data[9];
+ printf("\t * Version2: %d.%d\n", v2 >> 4, v2 & 0xf);
+ } else {
+ printf("\t * Unknown vendor extension. len=%u\n",
+ sublen);
+ }
+ break;
case 0x1054: {
tab_on_first(&first);
if (sublen != 8) {
- printf("\t * Primary Device Type: (invalid "
- "length %d)\n", sublen);
+ printf("\t * Primary Device Type: (invalid length %d)\n",
+ sublen);
break;
}
printf("\t * Primary Device Type: "
break;
}
case 0x1057: {
- __u8 val = data[4];
+ __u8 val;
tab_on_first(&first);
+ if (sublen < 1) {
+ printf("\t * AP setup locked: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ val = data[4];
printf("\t * AP setup locked: 0x%.2x\n", val);
break;
}
case 0x1008:
case 0x1053: {
- __u16 meth = (data[4] << 8) + data[5];
- bool comma = false;
+ __u16 meth;
+ bool comma;
+
+ if (sublen < 2) {
+ printf("\t * Config methods: (invalid length %d)\n",
+ sublen);
+ break;
+ }
+ meth = (data[4] << 8) + data[5];
+ comma = false;
tab_on_first(&first);
printf("\t * %sConfig methods:",
subtype == 0x1053 ? "Selected Registrar ": "");
case 0x12: /* invitation flags */
case 0xdd: /* vendor specific */
default: {
- const __u8 *subdata = data + 4;
+ const __u8 *subdata = data + 3;
__u16 tmplen = sublen;
tab_on_first(&first);
printf("\n");
}
+static void print_he_capa(const uint8_t type, uint8_t len, const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
+{
+ printf("\n");
+ print_he_capability(data, len);
+}
+
+static const struct ie_print ext_printers[] = {
+ [35] = { "HE capabilities", print_he_capa, 21, 54, BIT(PRINT_SCAN), },
+};
+
+static void print_extension(unsigned char len, unsigned char *ie,
+ bool unknown, enum print_ie_type ptype)
+{
+ unsigned char tag;
+
+ if (len < 1) {
+ printf("\tExtension IE: <empty>\n");
+ return;
+ }
+
+ tag = ie[0];
+ if (tag < ARRAY_SIZE(ext_printers) && ext_printers[tag].name &&
+ ext_printers[tag].flags & BIT(ptype)) {
+ print_ie(&ext_printers[tag], tag, len - 1, ie + 1, NULL);
+ return;
+ }
+
+ if (unknown) {
+ int i;
+
+ printf("\tUnknown Extension ID (%d):", ie[0]);
+ for (i = 1; i < len; i++)
+ printf(" %.2x", ie[i]);
+ printf("\n");
+ }
+}
+
void print_ies(unsigned char *ie, int ielen, bool unknown,
enum print_ie_type ptype)
{
while (ielen >= 2 && ielen - 2 >= ie[1]) {
if (ie[0] < ARRAY_SIZE(ieprinters) &&
ieprinters[ie[0]].name &&
- ieprinters[ie[0]].flags & BIT(ptype)) {
+ ieprinters[ie[0]].flags & BIT(ptype) &&
+ ie[1] > 0) {
print_ie(&ieprinters[ie[0]],
ie[0], ie[1], ie + 2, &ie_buffer);
} else if (ie[0] == 221 /* vendor */) {
print_vendor(ie[1], ie + 2, unknown, ptype);
+ } else if (ie[0] == 255 /* extension */) {
+ print_extension(ie[1], ie + 2, unknown, ptype);
} else if (unknown) {
int i;
static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
[NL80211_BSS_TSF] = { .type = NLA_U64 },
[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 },
[NL80211_BSS_BSSID] = { },
[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
}
if (bss[NL80211_BSS_FREQUENCY]) {
int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
- printf("\tfreq: %d\n", freq);
+ if (bss[NL80211_BSS_FREQUENCY_OFFSET])
+ printf("\tfreq: %d.%d\n", freq,
+ nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]));
+ else
+ printf("\tfreq: %d\n", freq);
+
if (freq > 45000)
is_dmg = true;
}
NL80211_CMD_GET_SCAN, NLM_F_DUMP, CIB_NETDEV, handle_scan_dump,
"Dump the current scan results. If -u is specified, print unknown\n"
"data in scan results.");
-COMMAND(scan, trigger, "[freq <freq>*] [duration <dur>] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force,duration-mandatory] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
+COMMAND(scan, trigger, "[freq <freq>*] [duration <dur>] [ies <hex as 00:11:..>] [meshid <meshid>] [lowpri,flush,ap-force,duration-mandatory,coloc] [randomise[=<addr>/<mask>]] [ssid <ssid>*|passive]",
NL80211_CMD_TRIGGER_SCAN, 0, CIB_NETDEV, handle_scan,
"Trigger a scan on the given frequencies with probing for the given\n"
"SSIDs (or wildcard if not given) unless passive scanning is requested.\n"