lineage_kernel_xcoverpro/drivers/net/wireless/scsc/debug_frame.c

1117 lines
38 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2012 - 2018 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/ieee80211.h>
#include <linux/ratelimit.h>
#include "debug.h"
#include "fapi.h"
#include "const.h"
#include "mgt.h"
#ifdef CONFIG_SCSC_WLAN_DEBUG
/* frame decoding debug level */
static int slsi_debug_summary_frame = 3;
module_param(slsi_debug_summary_frame, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(slsi_debug_summary_frame, "Debug level (0: disable, 1: mgmt only (no scan), 2: mgmt and imp frames, 3: all");
struct slsi_decode_entry {
const char *name;
void (*decode_fn)(u8 *frame, u16 frame_length, char *result, size_t result_length);
};
struct slsi_decode_snap {
const u8 snap[8];
const char *name;
size_t (*decode_fn)(u8 *frame, u16 frame_length, char *result, size_t result_length);
};
struct slsi_value_name_decode {
const u16 value;
const char *name;
size_t (*decode_fn)(u8 *frame, u16 frame_length, char *result, size_t result_length);
};
static size_t slsi_decode_basic_ie_info(u8 *ies, u16 ies_length, char *result, size_t result_length)
{
size_t size_written = 0;
const u8 *ssid = cfg80211_find_ie(WLAN_EID_SSID, ies, ies_length);
const u8 *rsn = cfg80211_find_ie(WLAN_EID_RSN, ies, ies_length);
const u8 *wpa = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPA, ies, ies_length);
const u8 *wps = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, WLAN_OUI_TYPE_MICROSOFT_WPS, ies, ies_length);
const u8 *htop = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ies, ies_length);
const u8 *country = cfg80211_find_ie(WLAN_EID_COUNTRY, ies, ies_length);
if (htop && htop[1]) {
size_written += snprintf(result + size_written, result_length - size_written, " channel:%d", htop[2]);
} else {
const u8 *ds = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ies, ies_length);
if (ds)
size_written += snprintf(result + size_written, result_length - size_written, " channel:%d", ds[2]);
}
if (ssid) {
if (ssid[1])
size_written += snprintf(result + size_written, result_length - size_written, " %.*s", ssid[1], (char *)&ssid[2]);
else
size_written += snprintf(result + size_written, result_length - size_written, " <HIDDEN>");
}
if (country)
size_written += snprintf(result + size_written, result_length - size_written, " country:%c%c%c", country[2], country[3], country[4]);
if (wpa)
size_written += snprintf(result + size_written, result_length - size_written, " wpa");
if (rsn)
size_written += snprintf(result + size_written, result_length - size_written, " wpa2");
if (wps)
size_written += snprintf(result + size_written, result_length - size_written, " wps");
return size_written;
}
static void slsi_decode_frame_ies_only(u8 offset, u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t str_len;
result[0] = '(';
str_len = slsi_decode_basic_ie_info(frame + offset,
frame_length - offset,
result + 1,
result_length - 1);
result[1 + str_len] = ')';
result[2 + str_len] = '\0';
}
static size_t slsi_decode_frame_leu16(u8 *frame, u16 frame_length, char *result, size_t result_length, const char *name)
{
u16 value = frame[0] | frame[1] << 8;
SLSI_UNUSED_PARAMETER(frame_length);
return snprintf(result, result_length, "%s:%u", name, value);
}
#define SLSI_ASSOC_REQ_IE_OFFSET 4
static void slsi_decode_assoc_req(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
slsi_decode_frame_ies_only(SLSI_ASSOC_REQ_IE_OFFSET, frame, frame_length, result, result_length);
}
#define SLSI_ASSOC_RSP_STATUS_OFFSET 2
#define SLSI_ASSOC_RSP_IE_OFFSET 6
static void slsi_decode_assoc_rsp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t str_len = 0;
result[str_len++] = '(';
str_len += slsi_decode_frame_leu16(frame + SLSI_ASSOC_RSP_STATUS_OFFSET,
frame_length - SLSI_ASSOC_RSP_STATUS_OFFSET,
result + str_len,
result_length - str_len,
"status");
str_len += slsi_decode_basic_ie_info(frame + SLSI_ASSOC_RSP_IE_OFFSET,
frame_length - SLSI_ASSOC_RSP_IE_OFFSET,
result + str_len,
result_length - str_len);
result[str_len++] = ')';
result[str_len] = '\0';
}
#define SLSI_DEAUTH_REASON_OFFSET 0
static void slsi_decode_deauth(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t str_len = 0;
result[str_len++] = '(';
str_len += slsi_decode_frame_leu16(frame + SLSI_DEAUTH_REASON_OFFSET,
frame_length - SLSI_DEAUTH_REASON_OFFSET,
result + str_len,
result_length - str_len,
"reason_code");
result[str_len++] = ')';
result[str_len] = '\0';
}
#define SLSI_AUTH_ALGO_OFFSET 0
#define SLSI_AUTH_SEQ_OFFSET 2
#define SLSI_AUTH_STATUS_OFFSET 4
static void slsi_decode_auth(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t str_len = 0;
result[str_len++] = '(';
str_len += slsi_decode_frame_leu16(frame + SLSI_AUTH_ALGO_OFFSET,
frame_length - SLSI_AUTH_ALGO_OFFSET,
result + str_len,
result_length - str_len,
"algo");
result[str_len++] = ' ';
str_len += slsi_decode_frame_leu16(frame + SLSI_AUTH_SEQ_OFFSET,
frame_length - SLSI_AUTH_SEQ_OFFSET,
result + str_len,
result_length - str_len,
"seq");
result[str_len++] = ' ';
str_len += slsi_decode_frame_leu16(frame + SLSI_AUTH_STATUS_OFFSET,
frame_length - SLSI_AUTH_STATUS_OFFSET,
result + str_len,
result_length - str_len,
"status");
result[str_len++] = ' ';
result[str_len++] = ')';
result[str_len] = '\0';
}
#define SLSI_REASSOC_IE_OFFSET 10
static void slsi_decode_reassoc_req(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
slsi_decode_frame_ies_only(SLSI_REASSOC_IE_OFFSET, frame, frame_length, result, result_length);
}
#define SLSI_BEACON_IE_OFFSET 12
static void slsi_decode_beacon(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
slsi_decode_frame_ies_only(SLSI_BEACON_IE_OFFSET, frame, frame_length, result, result_length);
}
#define SLSI_PROBEREQ_IE_OFFSET 0
static void slsi_decode_probe_req(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
slsi_decode_frame_ies_only(SLSI_PROBEREQ_IE_OFFSET, frame, frame_length, result, result_length);
}
#define SLSI_ACTION_BLOCK_ACK_ADDBA_REQ 0
#define SLSI_ACTION_BLOCK_ACK_ADDBA_RSP 1
#define SLSI_ACTION_BLOCK_ACK_DELBA 2
static size_t slsi_decode_action_blockack(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u8 action = frame[1];
SLSI_UNUSED_PARAMETER(frame_length);
switch (action) {
case SLSI_ACTION_BLOCK_ACK_ADDBA_REQ:
{
u8 token = frame[2];
return snprintf(result, result_length, "->ADDBAReq(token:%u)", token);
}
case SLSI_ACTION_BLOCK_ACK_ADDBA_RSP:
{
u8 token = frame[2];
u16 status = frame[3] | frame[4] << 8;
return snprintf(result, result_length, "->ADDBARsp(token:%u, status:%u)", token, status);
}
case SLSI_ACTION_BLOCK_ACK_DELBA:
{
u16 reason_code = frame[4] | frame[5] << 8;
return snprintf(result, result_length, "->DELBA(reason_code:%u)", reason_code);
}
default:
return snprintf(result, result_length, "->Action(%u)", action);
}
}
#define SLSI_ACTION_PUBLIC_DISCOVERY_RSP 14
static size_t slsi_decode_action_public(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u8 action = frame[1];
SLSI_UNUSED_PARAMETER(frame_length);
switch (action) {
case SLSI_ACTION_PUBLIC_DISCOVERY_RSP:
{
u8 token = frame[2];
return snprintf(result, result_length, "->DiscoveryRsp(token:%u)", token);
}
default:
return snprintf(result, result_length, "->Action(%u)", action);
}
}
#define SLSI_ACTION_TDLS_SETUP_REQ 0
#define SLSI_ACTION_TDLS_SETUP_RSP 1
#define SLSI_ACTION_TDLS_SETUP_CFM 2
#define SLSI_ACTION_TDLS_TEARDOWN 3
#define SLSI_ACTION_TDLS_PEER_TRAFFIC_IND 4
#define SLSI_ACTION_TDLS_CHANNEL_SWITCH_REQ 5
#define SLSI_ACTION_TDLS_CHANNEL_SWITCH_RSP 6
#define SLSI_ACTION_TDLS_PEER_PSM_REQ 7
#define SLSI_ACTION_TDLS_PEER_PSM_RSP 8
#define SLSI_ACTION_TDLS_PEER_TRAFFIC_RSP 9
#define SLSI_ACTION_TDLS_DISCOVERY_REQ 10
static size_t slsi_decode_action_tdls(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u8 action = frame[1];
SLSI_UNUSED_PARAMETER(frame_length);
switch (action) {
case SLSI_ACTION_TDLS_SETUP_REQ:
{
u8 token = frame[2];
return snprintf(result, result_length, "->SetupReq(token:%u)", token);
}
case SLSI_ACTION_TDLS_SETUP_RSP:
{
u16 status = frame[2] | frame[3] << 8;
u8 token = frame[4];
return snprintf(result, result_length, "->SetupRsp(token:%u, status:%u)", token, status);
}
case SLSI_ACTION_TDLS_SETUP_CFM:
{
u16 status = frame[2] | frame[3] << 8;
u8 token = frame[4];
return snprintf(result, result_length, "->SetupCfm(token:%u, status:%u)", token, status);
}
case SLSI_ACTION_TDLS_TEARDOWN:
{
u16 reason = frame[2] | frame[3] << 8;
return snprintf(result, result_length, "->SetupCfm(reason:%u)", reason);
}
case SLSI_ACTION_TDLS_PEER_TRAFFIC_IND:
{
u8 token = frame[2];
return snprintf(result, result_length, "->PeerTrafficInd(token:%u)", token);
}
case SLSI_ACTION_TDLS_CHANNEL_SWITCH_REQ:
{
u8 channel = frame[2];
return snprintf(result, result_length, "->ChannelSwitchReq(channel:%u)", channel);
}
case SLSI_ACTION_TDLS_CHANNEL_SWITCH_RSP:
{
u16 status = frame[2] | frame[3] << 8;
return snprintf(result, result_length, "->ChannelSwitchRsp(status:%u)", status);
}
case SLSI_ACTION_TDLS_PEER_PSM_REQ:
{
u8 token = frame[2];
return snprintf(result, result_length, "->PeerPSMReq(token:%u)", token);
}
case SLSI_ACTION_TDLS_PEER_PSM_RSP:
{
u8 token = frame[2];
u16 status = frame[3] | frame[4] << 8;
return snprintf(result, result_length, "->PeerPSMRsp(token:%u, status:%u)", token, status);
}
case SLSI_ACTION_TDLS_PEER_TRAFFIC_RSP:
{
u8 token = frame[2];
return snprintf(result, result_length, "->PeerTrafficRsp(token:%u)", token);
}
case SLSI_ACTION_TDLS_DISCOVERY_REQ:
{
u8 token = frame[2];
return snprintf(result, result_length, "->DiscoveryReq(token:%u)", token);
}
default:
return snprintf(result, result_length, "->Action(%u)", action);
}
}
static const struct slsi_value_name_decode action_categories[] = {
{ 3, "BlockAck", slsi_decode_action_blockack },
{ 0, "SpectrumManagement", NULL },
{ 1, "QoS", NULL },
{ 2, "DLS", NULL },
{ 4, "Public", slsi_decode_action_public },
{ 5, "RadioMeasurement", NULL },
{ 6, "FastBSSTransition", NULL },
{ 7, "HT", NULL },
{ 8, "SAQuery", NULL },
{ 9, "ProtectedDualOfPublicAction", NULL },
{ 12, "TDLS", slsi_decode_action_tdls },
{ 17, "ReservedWFA", NULL },
{ 126, "VendorSpecificProtected", NULL },
{ 127, "VendorSpecific", NULL },
{ 132, "Public(error)", slsi_decode_action_public },
};
#define SLSI_ACTION_CAT_BLOCK_ACK 3
static void slsi_decode_action(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u8 category = frame[0];
u32 i;
for (i = 0; i < ARRAY_SIZE(action_categories); i++)
if (action_categories[i].value == category) {
int size_written = snprintf(result, result_length, "->%s", action_categories[i].name);
if (action_categories[i].decode_fn)
action_categories[i].decode_fn(frame, frame_length, result + size_written, result_length - size_written);
return;
}
snprintf(result, result_length, "->category:%u", category);
}
const char *slsi_arp_opcodes[] = {
"Reserved",
"REQUEST",
"REPLY",
"request Reverse",
"reply Reverse",
"DRARP-Request",
"DRARP-Reply",
"DRARP-Error",
"InARP-Request",
"InARP-Reply",
"ARP-NAK",
"MARS-Request",
"MARS-Multi",
"MARS-MServ",
"MARS-Join",
"MARS-Leave",
"MARS-NAK",
"MARS-Unserv",
"MARS-SJoin",
"MARS-SLeave",
"MARS-Grouplist-Request",
"MARS-Grouplist-Reply",
"MARS-Redirect-Map",
"MAPOS-UNARP",
"OP_EXP1",
"OP_EXP2",
};
static size_t slsi_decode_arp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
/* u16 htype = frame[0] << 8 | frame[1];
* u16 proto = frame[2] << 8 | frame[3];
* u8 hlen = frame[4];
* u8 plen = frame[5];
*/
u16 opcode = frame[6] << 8 | frame[7];
u8 *sha = &frame[8];
u8 *spa = &frame[14];
u8 *tha = &frame[18];
u8 *tpa = &frame[24];
SLSI_UNUSED_PARAMETER(frame_length);
if (opcode < ARRAY_SIZE(slsi_arp_opcodes))
return snprintf(result, result_length, "->%s(sha:%.2X:%.2X:%.2X:%.2X:%.2X:%.2X, spa:%u.%u.%u.%u, tha:%.2X:%.2X:%.2X:%.2X:%.2X:%.2X, tpa:%u.%u.%u.%u)",
slsi_arp_opcodes[opcode],
sha[0], sha[1], sha[2], sha[3], sha[4], sha[5],
spa[0], spa[1], spa[2], spa[3],
tha[0], tha[1], tha[2], tha[3], tha[4], tha[5],
tpa[0], tpa[1], tpa[2], tpa[3]);
else
return snprintf(result, result_length, "->(opcode:%u)", opcode);
}
static const struct slsi_value_name_decode slsi_decode_eapol_packet_types[] = {
{ 1, "Identity", NULL },
{ 2, "Notification", NULL },
{ 3, "Nak", NULL },
{ 4, "MD5Challenge", NULL },
{ 5, "OneTimePassword", NULL },
{ 6, "GenericTokenCard", NULL },
{ 9, "RSA Public Key Authentication", NULL },
{ 10, "DSS Unilateral", NULL },
{ 11, "KEA", NULL },
{ 12, "KEA-VALIDATE", NULL },
{ 13, "EAP-TLS", NULL },
{ 14, "Defender Token (AXENT)", NULL },
{ 15, "RSA Security SecurID EAP", NULL },
{ 16, "Arcot Systems EAP", NULL },
{ 17, "EAP-Cisco Wireless", NULL },
{ 18, "EAP-SIM", NULL },
{ 19, "SRP-SHA1 Part 1", NULL },
{ 21, "EAP-TTLS", NULL },
{ 22, "Remote Access Service", NULL },
{ 23, "EAP-AKA", NULL },
{ 24, "EAP-3Com Wireless", NULL },
{ 25, "PEAP", NULL },
{ 26, "MS-EAP-Authentication", NULL },
{ 27, "MAKE", NULL },
{ 28, "CRYPTOCard", NULL },
{ 29, "EAP-MSCHAP-V2", NULL },
{ 30, "DynamID", NULL },
{ 31, "Rob EAP", NULL },
{ 32, "EAP-POTP", NULL },
{ 33, "MS-Authentication-TLV", NULL },
{ 34, "SentriNET", NULL },
{ 35, "EAP-Actiontec Wireless", NULL },
{ 36, "Cogent Systems Biometrics Authentication EAP", NULL },
{ 37, "AirFortress EAP", NULL },
{ 38, "EAP-HTTP Digest", NULL },
{ 39, "SecureSuite EAP", NULL },
{ 40, "DeviceConnect EAP", NULL },
{ 41, "EAP-SPEKE", NULL },
{ 42, "EAP-MOBAC", NULL },
{ 43, "EAP-FAST", NULL },
{ 44, "ZLXEAP", NULL },
{ 45, "EAP-Link", NULL },
{ 46, "EAP-PAX", NULL },
{ 47, "EAP-PSK", NULL },
{ 48, "EAP-SAKE", NULL },
{ 49, "EAP-IKEv2", NULL },
{ 50, "EAP-AKA", NULL },
{ 51, "EAP-GPSK", NULL },
{ 52, "EAP-pwd", NULL },
{ 53, "EAP-EKE V1", NULL },
{ 254, "WPS", NULL }
};
static size_t slsi_decode_eapol_packet(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
static const char *const slsi_decode_eapol_packet_codes[] = {
"",
"Request",
"Response",
"Success",
"Failure",
};
size_t size_written = 0;
u32 i;
u8 code = frame[0];
u8 id = frame[1];
u16 length = frame[2] << 8 | frame[3];
const char *code_str = "";
SLSI_UNUSED_PARAMETER(frame_length);
if (code >= 1 && code <= 4)
code_str = slsi_decode_eapol_packet_codes[code];
if (length > 4 && (code == 1 || code == 2)) {
u8 type = frame[4];
for (i = 0; i < ARRAY_SIZE(slsi_decode_eapol_packet_types); i++)
if (slsi_decode_eapol_packet_types[i].value == type) {
size_written += snprintf(result, result_length, ":%s:%s id:%u", slsi_decode_eapol_packet_types[i].name, code_str, id);
return size_written;
}
size_written += snprintf(result, result_length, ":type:%u: %s id:%u", type, code_str, id);
} else {
size_written += snprintf(result, result_length, ":%s id:%u length:%u", code_str, id, length);
}
return size_written;
}
static const struct slsi_value_name_decode slsi_eapol_packet_type[] = {
{ 0, "EapPacket", slsi_decode_eapol_packet },
{ 1, "EapolStart", NULL },
{ 2, "EapolLogoff", NULL },
{ 3, "EapolKey", NULL }
};
static size_t slsi_decode_eapol(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t size_written = 0;
u8 packet_type = frame[1];
u16 length = frame[2] << 8 | frame[3];
SLSI_UNUSED_PARAMETER(frame_length);
if (packet_type < ARRAY_SIZE(slsi_eapol_packet_type)) {
size_written += snprintf(result, result_length, "->%s", slsi_eapol_packet_type[packet_type].name);
if (slsi_eapol_packet_type[packet_type].decode_fn)
size_written += slsi_eapol_packet_type[packet_type].decode_fn(frame + 4, length, result + size_written, result_length - size_written);
return size_written;
} else {
return snprintf(result, result_length, "->packet_type:%u", packet_type);
}
}
static size_t slsi_decode_tdls(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u8 payload_type = frame[0];
if (payload_type == 2) {
slsi_decode_action(frame + 1, frame_length - 1, result, result_length);
return 0;
} else {
return snprintf(result, result_length, "->Unknown(payload:%u", payload_type);
}
}
static size_t slsi_decode_ipv4_icmp_echo(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u16 id = frame[0] << 8 | frame[1];
u16 seq = frame[2] << 8 | frame[3];
SLSI_UNUSED_PARAMETER(frame_length);
return snprintf(result, result_length, " id:%u seq:%u", id, seq);
}
static const struct slsi_value_name_decode slsi_ipv4_icmp_types[] = {
{ 0, "EchoReply", slsi_decode_ipv4_icmp_echo },
{ 8, "Echo ", slsi_decode_ipv4_icmp_echo },
{ 3, "Destination Unreachable Ack", NULL },
{ 4, "Source Quench", NULL },
{ 5, "Redirect", NULL },
{ 6, "Alternate Host Address", NULL },
{ 9, "Router Advertisement", NULL },
{ 10, "Router Selection", NULL },
{ 11, "Time Exceeded", NULL },
{ 12, "Parameter Problem", NULL },
{ 13, "Timestamp", NULL },
{ 14, "Timestamp Reply", NULL },
{ 15, "Information Request", NULL },
{ 16, "Information Reply", NULL },
{ 17, "Address Mask Request", NULL },
{ 18, "Address Mask Reply", NULL },
{ 19, "Reserved (for Security)", NULL },
{ 30, "Traceroute", NULL },
{ 31, "Datagram Conversion Error", NULL },
{ 32, "Mobile Host Redirect", NULL },
{ 33, "IPv6 Where-Are-You", NULL },
{ 34, "IPv6 I-Am-Here", NULL },
{ 35, "Mobile Registration Request", NULL },
{ 36, "Mobile Registration Reply", NULL },
{ 39, "SKIP", NULL },
{ 40, "Photuris", NULL },
{ 253, "RFC3692-style Experiment 1", NULL },
{ 254, "RFC3692-style Experiment 2", NULL }
};
static size_t slsi_decode_ipv4_icmp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t size_written = 0;
u32 i;
u8 type = frame[0];
u8 code = frame[1];
for (i = 0; i < ARRAY_SIZE(slsi_ipv4_icmp_types); i++)
if (slsi_ipv4_icmp_types[i].value == type) {
size_written += snprintf(result, result_length, "->%s(code:%u)", slsi_ipv4_icmp_types[i].name, code);
if (slsi_ipv4_icmp_types[i].decode_fn)
size_written += slsi_ipv4_icmp_types[i].decode_fn(frame + 4, frame_length - 4, result + size_written, result_length - size_written);
return size_written;
}
return snprintf(result, result_length, "->type(%u)", type);
}
static const struct slsi_value_name_decode slsi_ipv4_udp_bootp_dhcp_option53[] = {
{ 1, "DHCP_DISCOVER", NULL },
{ 2, "DHCP_OFFER", NULL },
{ 3, "DHCP_REQUEST", NULL },
{ 4, "DHCP_DECLINE", NULL },
{ 5, "DHCP_ACK", NULL },
{ 6, "DHCP_NAK", NULL },
{ 7, "DHCP_RELEASE", NULL },
{ 8, "DHCP_INFORM", NULL },
};
#define SLSI_IPV4_UDP_BOOTP_CIADDR_OFFSET 16
#define SLSI_IPV4_UDP_BOOTP_YIADDR_OFFSET 20
#define SLSI_IPV4_UDP_BOOTP_GIADDR_OFFSET 24
#define SLSI_IPV4_UDP_BOOTP_MAGIC_OFFSET 236
static size_t slsi_decode_ipv4_udp_bootp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u32 i;
u8 *ciaddr = &frame[SLSI_IPV4_UDP_BOOTP_CIADDR_OFFSET];
u8 *yiaddr = &frame[SLSI_IPV4_UDP_BOOTP_YIADDR_OFFSET];
u8 *giaddr = &frame[SLSI_IPV4_UDP_BOOTP_GIADDR_OFFSET];
u8 *magic = &frame[SLSI_IPV4_UDP_BOOTP_MAGIC_OFFSET];
if (magic[0] == 0x63 && magic[1] == 0x82 && magic[2] == 0x53 && magic[3] == 0x63) {
u8 *p = &frame[SLSI_IPV4_UDP_BOOTP_MAGIC_OFFSET + 4];
while (p < p + frame_length) {
u8 option = p[0];
u8 option_length = p[1];
if (option == 53 && option_length == 1) {
for (i = 0; i < ARRAY_SIZE(slsi_ipv4_udp_bootp_dhcp_option53); i++)
if (slsi_ipv4_udp_bootp_dhcp_option53[i].value == p[2])
return snprintf(result, result_length, "->%s(ci:%u.%u.%u.%u yi:%u.%u.%u.%u gi:%u.%u.%u.%u)",
slsi_ipv4_udp_bootp_dhcp_option53[i].name,
ciaddr[0], ciaddr[1], ciaddr[2], ciaddr[3],
yiaddr[0], yiaddr[1], yiaddr[2], yiaddr[3],
giaddr[0], giaddr[1], giaddr[2], giaddr[3]);
return snprintf(result, result_length, "->option53(%u ci:%u.%u.%u.%u yi:%u.%u.%u.%u gi:%u.%u.%u.%u)",
p[2],
ciaddr[0], ciaddr[1], ciaddr[2], ciaddr[3],
yiaddr[0], yiaddr[1], yiaddr[2], yiaddr[3],
giaddr[0], giaddr[1], giaddr[2], giaddr[3]);
}
if (option == 0)
break;
p = p + 2 + option_length;
}
}
return 0;
}
static const struct slsi_value_name_decode slsi_ipv4_udp_ports[] = {
{ 53, "DNS", NULL },
{ 67, "Bootp", slsi_decode_ipv4_udp_bootp },
{ 68, "Bootp", slsi_decode_ipv4_udp_bootp },
};
static size_t slsi_decode_ipv4_udp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
/* 0 7 8 15 16 23 24 31
* +--------+--------+--------+--------+
* | Source | Destination |
* | Port | Port |
* +--------+--------+--------+--------+
* | | |
* | Length | Checksum |
* +--------+--------+--------+--------+
* |
* | data octets ...
* +--------------------- ...
*/
size_t size_written = 0;
u32 i;
u16 sport = frame[0] << 8 | frame[1];
u16 dport = frame[2] << 8 | frame[3];
u16 length = frame[4] << 8 | frame[5];
/*u16 chksum = frame[6] << 8 | frame[7];*/
for (i = 0; i < ARRAY_SIZE(slsi_ipv4_udp_ports); i++)
if (slsi_ipv4_udp_ports[i].value == dport || slsi_ipv4_udp_ports[i].value == sport) {
size_written += snprintf(result, result_length, "->%s", slsi_ipv4_udp_ports[i].name);
if (slsi_ipv4_udp_ports[i].decode_fn)
size_written += slsi_ipv4_udp_ports[i].decode_fn(frame + 8, length, result + size_written, result_length - size_written);
else
size_written += snprintf(result + size_written, result_length - size_written, "(dport:%u, size:%u)", dport, frame_length - 8);
return size_written;
}
return snprintf(result, result_length, "(dport:%u, size:%u)", dport, frame_length - 8);
}
static size_t slsi_decode_ipv4_tcp(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
/* TCP Header Format
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Source Port | Destination Port |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Sequence Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Acknowledgment Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Data | |U|A|P|R|S|F| |
* | Offset| Reserved |R|C|S|S|Y|I| Window |
* | | |G|K|H|T|N|N| |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Checksum | Urgent Pointer |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Options | Padding |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | data |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
u16 dport = frame[2] << 8 | frame[3];
u8 flags = frame[13];
bool fin = flags & 0x01;
bool syn = flags & 0x02;
bool rst = flags & 0x04;
bool psh = flags & 0x08;
bool ack = flags & 0x10;
bool urg = flags & 0x20;
return snprintf(result, result_length, "(dport:%u%s%s%s%s%s%s size:%u)",
dport,
fin ? " FIN" : "",
syn ? " SYN" : "",
rst ? " RST" : "",
psh ? " PSH" : "",
ack ? " ACK" : "",
urg ? " URG" : "",
frame_length - 24);
}
#define SLSI_IPV4_PROTO_ICMP 1
#define SLSI_IPV4_PROTO_IGMP 2
#define SLSI_IPV4_PROTO_TCP 6
#define SLSI_IPV4_PROTO_UDP 17
static size_t slsi_decode_ipv4(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
size_t size_written = 0;
u16 ip_data_offset = 20;
/*u8 version = frame[0] >> 4; */
u8 hlen = frame[0] & 0x0F;
/*u8 tos = frame[1]; */
/*u16 len = frame[2] << 8 | frame[3]; */
/*u16 id = frame[4] << 8 | frame[5]; */
/*u16 flags_foff = frame[6] << 8 | frame[7]; */
/*u8 ttl = frame[8]; */
u8 proto = frame[9];
/*u16 cksum = frame[10] << 8 | frame[11]; */
u8 *src_ip = &frame[12];
u8 *dest_ip = &frame[16];
if (hlen > 5)
ip_data_offset += (hlen - 5) * 4;
size_written += snprintf(result + size_written, result_length - size_written, "(s:%u.%u.%u.%u d:%u.%u.%u.%u)",
src_ip[0], src_ip[1], src_ip[2], src_ip[3],
dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3]);
switch (proto) {
case SLSI_IPV4_PROTO_TCP:
size_written += snprintf(result + size_written, result_length - size_written, "->TCP");
size_written += slsi_decode_ipv4_tcp(frame + ip_data_offset,
frame_length - ip_data_offset,
result + size_written,
result_length - size_written);
break;
case SLSI_IPV4_PROTO_UDP:
size_written += snprintf(result + size_written, result_length - size_written, "->UDP");
size_written += slsi_decode_ipv4_udp(frame + ip_data_offset,
frame_length - ip_data_offset,
result + size_written,
result_length - size_written);
break;
case SLSI_IPV4_PROTO_ICMP:
size_written += snprintf(result + size_written, result_length - size_written, "->ICMP");
size_written += slsi_decode_ipv4_icmp(frame + ip_data_offset,
frame_length - ip_data_offset,
result + size_written,
result_length - size_written);
break;
case SLSI_IPV4_PROTO_IGMP:
size_written += snprintf(result + size_written, result_length - size_written, "->IGMP");
break;
default:
size_written += snprintf(result + size_written, result_length - size_written, "->proto:%u", proto);
break;
}
return size_written;
}
static const struct slsi_decode_snap snap_types[] = {
{ { 0x08, 0x00 }, "IpV4", slsi_decode_ipv4 },
{ { 0x08, 0x06 }, "Arp", slsi_decode_arp },
{ { 0x88, 0x8e }, "Eapol", slsi_decode_eapol },
{ { 0x89, 0x0d }, NULL, slsi_decode_tdls },
{ { 0x86, 0xdd }, "IpV6", NULL },
{ { 0x88, 0xb4 }, "Wapi", NULL },
};
static void slsi_decode_proto_data(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
u32 i;
for (i = 0; i < ARRAY_SIZE(snap_types); i++)
if (memcmp(snap_types[i].snap, frame, 2) == 0) {
int slen = 0;
if (snap_types[i].name)
slen = snprintf(result, result_length, "->%s", snap_types[i].name);
if (snap_types[i].decode_fn)
slen += snap_types[i].decode_fn(frame + 2, frame_length - 2, result + slen, result_length - slen);
return;
}
snprintf(result, result_length, "(proto:0x%.2X%.2X)", frame[0], frame[1]);
}
static void slsi_decode_80211_data(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
return slsi_decode_proto_data(frame + 6, frame_length - 6, result, result_length);
}
static const struct slsi_decode_entry frame_types[4][16] = {
{
{ "AssocReq", slsi_decode_assoc_req },
{ "AssocRsp", slsi_decode_assoc_rsp },
{ "ReassocReq", slsi_decode_reassoc_req },
{ "ReassocRsp", slsi_decode_assoc_rsp }, /* Same as Assoc Req Frame*/
{ "ProbeReq", slsi_decode_probe_req },
{ "ProbeRsp", slsi_decode_beacon }, /* Same as Beacon Frame */
{ "TimingAdv", NULL },
{ "Reserved", NULL },
{ "Beacon ", slsi_decode_beacon },
{ "Atim", NULL },
{ "Disassoc", slsi_decode_deauth }, /* Same as Deauth Frame */
{ "Auth", slsi_decode_auth },
{ "Deauth", slsi_decode_deauth },
{ "Action", slsi_decode_action },
{ "ActionNoAck", slsi_decode_action },
{ "Reserved", NULL }
},
{
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "BlockAckReq", NULL },
{ "BlockAck", NULL },
{ "PsPoll", NULL },
{ "RTS", NULL },
{ "CTS", NULL },
{ "Ack", NULL },
{ "CF-End", NULL },
{ "CF-End+Ack", NULL }
},
{
{ "Data", slsi_decode_80211_data },
{ "Data+CF-Ack", slsi_decode_80211_data },
{ "Data+CF-Poll", slsi_decode_80211_data },
{ "Data+CF-Ack+Poll", slsi_decode_80211_data },
{ "Null", NULL },
{ "CF-Ack", NULL },
{ "CF-Poll", NULL },
{ "CF-Ack+Poll", NULL },
{ "QosData", slsi_decode_80211_data },
{ "QosData+CF-Ack", slsi_decode_80211_data },
{ "QosData+CF-Poll", slsi_decode_80211_data },
{ "QosData+CF-Ack+Poll", slsi_decode_80211_data },
{ "QosNull", NULL },
{ "Reserved", NULL },
{ "QosCF-Poll", NULL },
{ "QosCF-Ack+Poll", NULL }
},
{
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
{ "Reserved", NULL },
}
};
static bool slsi_decode_80211_frame(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)frame;
u16 fc_cpu = cpu_to_le16(hdr->frame_control);
int ftype_idx = (fc_cpu & 0xf) >> 2;
const struct slsi_decode_entry *entry;
int hdrlen;
int slen;
/* Only decode Management Frames at Level 1 */
if (slsi_debug_summary_frame == 1 && ftype_idx != 0)
return false;
/* Filter Scanning at the debug level 3 and above as it can be noisy with large scan results */
if (slsi_debug_summary_frame < 3 &&
(ieee80211_is_probe_req(fc_cpu) || ieee80211_is_probe_resp(fc_cpu) || ieee80211_is_beacon(fc_cpu)))
return false;
entry = &frame_types[ftype_idx][(fc_cpu >> 4) & 0xf];
hdrlen = ieee80211_hdrlen(hdr->frame_control);
slen = snprintf(result, result_length, entry->name);
if (entry->decode_fn)
entry->decode_fn(frame + hdrlen, frame_length - hdrlen, result + slen, result_length - slen);
return true;
}
static bool slsi_decode_l3_frame(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
int slen;
/* Only decode Management Frames at Level 1 */
if (slsi_debug_summary_frame == 1)
return false;
/* Only decode high important frames e.g. EAPOL, ARP, DHCP at Level 2 */
if (slsi_debug_summary_frame == 2) {
struct ethhdr *ehdr = (struct ethhdr *)frame;
u16 eth_type = be16_to_cpu(ehdr->h_proto);
switch (eth_type) {
case ETH_P_IP:
if (slsi_is_dhcp_packet(frame) == SLSI_TX_IS_NOT_DHCP)
return false;
break;
/* Fall through; process EAPOL, WAPI and ARP frames */
case ETH_P_PAE:
case ETH_P_WAI:
case ETH_P_ARP:
break;
default:
/* return for all other frames */
return false;
}
}
slen = snprintf(result, result_length, "eth");
slsi_decode_proto_data(frame + 12, frame_length - 12, result + slen, result_length - slen);
return true;
}
static bool slsi_decode_amsdu_subframe(u8 *frame, u16 frame_length, char *result, size_t result_length)
{
int slen;
/* Only decode Management Frames at Level 1 */
if (slsi_debug_summary_frame == 1)
return false;
/* Only decode high important frames e.g. EAPOL, ARP, DHCP at Level 2 */
if (slsi_debug_summary_frame == 2) {
struct msduhdr *msdu_hdr = (struct msduhdr *)frame;
u16 eth_type = be16_to_cpu(msdu_hdr->type);
switch (eth_type) {
case ETH_P_IP:
/* slsi_is_dhcp_packet() decodes the frame as Ethernet frame so
* pass a offset (difference between MSDU header and ethernet header)
* to frames so it reads at the right offset
*/
if (slsi_is_dhcp_packet(frame + 8) == SLSI_TX_IS_NOT_DHCP)
return false;
break;
/* Fall through; process EAPOL, WAPI and ARP frames */
case ETH_P_PAE:
case ETH_P_WAI:
case ETH_P_ARP:
break;
default:
/* return for all other frames */
return false;
}
}
slen = snprintf(result, result_length, "eth");
slsi_decode_proto_data(frame + 20, frame_length - 20, result + slen, result_length - slen);
return true;
}
static inline bool slsi_debug_frame_ratelimited(void)
{
static DEFINE_RATELIMIT_STATE(_rs, (5 * HZ), 200);
if (__ratelimit(&_rs))
return true;
return false;
}
/* NOTE: dev can be NULL */
void slsi_debug_frame(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb, const char *prefix)
{
char frame_info[384];
u8 *frame = fapi_get_data(skb);
u16 len = fapi_get_datalen(skb);
u8 *dst = NULL;
u8 *src = NULL;
u16 frametype = 0xFFFF;
bool print = false;
u16 id = fapi_get_sigid(skb);
u16 vif = fapi_get_vif(skb);
s16 rssi = 0;
if (!slsi_debug_summary_frame)
return;
if (!len)
return;
switch (id) {
case MA_UNITDATA_REQ:
case MA_UNITDATA_IND:
if (!slsi_debug_frame_ratelimited()) /* Limit the Data output to stop too much spam at high data rates */
return;
break;
default:
break;
}
frame_info[0] = '\0';
switch (id) {
case MA_UNITDATA_REQ:
frametype = fapi_get_u16(skb, u.ma_unitdata_req.data_unit_descriptor);
break;
case MA_UNITDATA_IND:
if (fapi_get_u16(skb, u.ma_unitdata_ind.bulk_data_descriptor) == FAPI_BULKDATADESCRIPTOR_INLINE)
frametype = fapi_get_u16(skb, u.ma_unitdata_ind.data_unit_descriptor);
break;
case MLME_SEND_FRAME_REQ:
frametype = fapi_get_u16(skb, u.mlme_send_frame_req.data_unit_descriptor);
break;
case MLME_RECEIVED_FRAME_IND:
frametype = fapi_get_u16(skb, u.mlme_received_frame_ind.data_unit_descriptor);
break;
case MLME_SCAN_IND:
frametype = FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME;
rssi = fapi_get_s16(skb, u.mlme_scan_ind.rssi);
vif = fapi_get_u16(skb, u.mlme_scan_ind.scan_id) >> 8;
break;
case MLME_CONNECT_CFM:
case MLME_CONNECT_IND:
case MLME_PROCEDURE_STARTED_IND:
case MLME_CONNECTED_IND:
case MLME_REASSOCIATE_IND:
case MLME_ROAMED_IND:
frametype = FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME;
break;
default:
return;
}
switch (frametype) {
case FAPI_DATAUNITDESCRIPTOR_IEEE802_11_FRAME:
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)frame;
dst = hdr->addr1;
src = hdr->addr2;
print = slsi_decode_80211_frame(frame, len, frame_info, sizeof(frame_info));
break;
}
case FAPI_DATAUNITDESCRIPTOR_IEEE802_3_FRAME:
{
struct ethhdr *ehdr = (struct ethhdr *)frame;
dst = ehdr->h_dest;
src = ehdr->h_source;
print = slsi_decode_l3_frame(frame, len, frame_info, sizeof(frame_info));
break;
}
case FAPI_DATAUNITDESCRIPTOR_AMSDU_SUBFRAME:
{
struct ethhdr *ehdr = (struct ethhdr *)frame;
dst = ehdr->h_dest;
src = ehdr->h_source;
print = slsi_decode_amsdu_subframe(frame, len, frame_info, sizeof(frame_info));
break;
}
default:
return;
}
if (print) {
SLSI_DBG4(sdev, SLSI_SUMMARY_FRAMES, "%-5s: %s(vif:%u rssi:%-3d, s:%pM d:%pM)->%s\n",
dev ? netdev_name(dev) : "", prefix, vif, rssi, src, dst, frame_info);
}
}
#endif /* CONFIG_SCSC_WLAN_DEBUG */