blob: 449f389cbb1dd70bf197a06c9aa458529da4ad57 [file] [log] [blame]
/*
* Packet dump helper functions
*
* Copyright (C) 2022, Broadcom.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
*
* <<Broadcom-WL-IPTag/Open:>>
*
* $Id$
*/
#include <typedefs.h>
#include <ethernet.h>
#include <bcmutils.h>
#include <bcmevent.h>
#include <bcmendian.h>
#include <bcmtlv.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_dbg.h>
#include <bcmip.h>
#include <bcmudp.h>
#include <bcmdhcp.h>
#include <bcmarp.h>
#include <bcmicmp.h>
#include <bcmigmp.h>
#include <dhd_linux_pktdump.h>
#ifdef WL_CFGVENDOR_CUST_ADVLOG
#include <wl_cfg80211.h>
#include <wl_cfgvendor.h>
#include <dhd_flowring.h>
#endif /* WL_CFGVENDOR_CUST_ADVLOG */
#define DHD_PKTDUMP(arg) DHD_ERROR(arg)
#define DHD_PKTDUMP_MEM(arg) DHD_ERROR_MEM(arg)
#define PACKED_STRUCT __attribute__ ((packed))
#define EAPOL_HDR_LEN 4
/* EAPOL types */
#define EAP_PACKET 0
#define EAPOL_START 1
#define EAPOL_LOGOFF 2
#define EAPOL_KEY 3
#define EAPOL_ASF 4
/* EAPOL-Key types */
#define EAPOL_RC4_KEY 1
#define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */
#define EAPOL_WPA_KEY 254 /* WPA */
/* EAPOL-Key header field size */
#define AKW_BLOCK_LEN 8
#define WPA_KEY_REPLAY_LEN 8
#define WPA_KEY_NONCE_LEN 32
#define WPA_KEY_IV_LEN 16
#define WPA_KEY_RSC_LEN 8
#define WPA_KEY_ID_LEN 8
#define WPA_KEY_MIC_LEN 16
#define WPA_MAX_KEY_SIZE 32
#define WPA_KEY_DATA_LEN (WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN)
/* Key information bit */
#define KEYINFO_TYPE_MASK (1 << 3)
#define KEYINFO_INSTALL_MASK (1 << 6)
#define KEYINFO_KEYACK_MASK (1 << 7)
#define KEYINFO_KEYMIC_MASK (1 << 8)
#define KEYINFO_SECURE_MASK (1 << 9)
#define KEYINFO_ERROR_MASK (1 << 10)
#define KEYINFO_REQ_MASK (1 << 11)
/* EAP Code */
#define EAP_CODE_REQUEST 1 /* Request */
#define EAP_CODE_RESPONSE 2 /* Response */
#define EAP_CODE_SUCCESS 3 /* Success */
#define EAP_CODE_FAILURE 4 /* Failure */
/* EAP Type */
#define EAP_TYPE_RSVD 0 /* Reserved */
#define EAP_TYPE_IDENT 1 /* Identify */
#define EAP_TYPE_NOTI 2 /* Notification */
#define EAP_TYPE_TLS 13 /* EAP-TLS */
#define EAP_TYPE_LEAP 17 /* Cisco-LEAP */
#define EAP_TYPE_TTLS 21 /* EAP-TTLS */
#define EAP_TYPE_AKA 23 /* EAP-AKA */
#define EAP_TYPE_PEAP 25 /* EAP-PEAP */
#define EAP_TYPE_FAST 43 /* EAP-FAST */
#define EAP_TYPE_PSK 47 /* EAP-PSK */
#define EAP_TYPE_AKAP 50 /* EAP-AKA' */
#define EAP_TYPE_EXP 254 /* Reserved for Expended Type */
/* WSC */
#define EAP_HDR_LEN 5
#define EAP_WSC_NONCE_OFFSET 10
#define EAP_WSC_DATA_OFFSET (OFFSETOF(eap_wsc_fmt_t, data))
#define EAP_WSC_MIN_DATA_LEN ((EAP_HDR_LEN) + (EAP_WSC_DATA_OFFSET))
#define WFA_VID "\x00\x37\x2A" /* WFA SMI code */
#define WFA_VID_LEN 3 /* WFA VID length */
#define WFA_VTYPE 1u /* WFA Vendor type */
/* WSC opcode */
#define WSC_OPCODE_UPNP 0
#define WSC_OPCODE_START 1
#define WSC_OPCODE_ACK 2
#define WSC_OPCODE_NACK 3
#define WSC_OPCODE_MSG 4
#define WSC_OPCODE_DONE 5
#define WSC_OPCODE_FRAG_ACK 6
/* WSC flag */
#define WSC_FLAG_MF 1 /* more fragements */
#define WSC_FLAG_LF 2 /* length field */
/* WSC message code */
#define WSC_ATTR_MSG 0x1022
#define WSC_MSG_M1 0x04
#define WSC_MSG_M2 0x05
#define WSC_MSG_M3 0x07
#define WSC_MSG_M4 0x08
#define WSC_MSG_M5 0x09
#define WSC_MSG_M6 0x0A
#define WSC_MSG_M7 0x0B
#define WSC_MSG_M8 0x0C
/* Debug prints */
typedef enum pkt_cnt_type {
PKT_CNT_TYPE_INVALID = 0,
PKT_CNT_TYPE_ARP = 1,
PKT_CNT_TYPE_DNS = 2,
PKT_CNT_TYPE_MAX = 3
} pkt_cnt_type_t;
typedef struct pkt_cnt {
uint32 tx_cnt;
uint32 tx_err_cnt;
uint32 rx_cnt;
} pkt_cnt_t;
typedef struct pkt_cnt_log {
bool enabled;
uint16 reason;
timer_list_compat_t pktcnt_timer;
pkt_cnt_t arp_cnt;
pkt_cnt_t dns_cnt;
} pkt_cnts_log_t;
#define PKT_CNT_TIMER_INTERNVAL_MS 5000 /* packet count timeout(ms) */
#define PKT_CNT_RSN_VALID(rsn) \
(((rsn) > (PKT_CNT_RSN_INVALID)) && ((rsn) < (PKT_CNT_RSN_MAX)))
static const char pkt_cnt_msg[][20] = {
"INVALID",
"ROAM_SUCCESS",
"GROUP_KEY_UPDATE",
"CONNECT_SUCCESS",
"INVALID"
};
#if defined(DHD_DNS_DUMP) || defined(DHD_ARP_DUMP) || defined(DHD_ICMP_DUMP) || \
defined(DHD_DHCP_DUMP) || defined(DHD_8021X_DUMP) || defined(DHD_PKTDUMP_ROAM)
#define DHD_PKTFATE_STR_MAX 30
#define DHD_PKTFATE_EX_STR_MAX 10
typedef struct dhd_tx_pktfate_entry {
const char reason[DHD_PKTFATE_STR_MAX];
const char ex[DHD_PKTFATE_EX_STR_MAX];
} dhd_tx_pktfate_entry_t;
static dhd_tx_pktfate_entry_t tx_pktfate[] = {
{"TX_PKT_FATE_ACKED", "ACK"}, /* 0: WLFC_CTL_PKTFLAG_DISCARD */
{"TX_PKT_FATE_FW_D11SUPPRESS", "TX_FAIL"}, /* 1: WLFC_CTL_PKTFLAG_D11SUPPRESS */
{"TX_PKT_FATE_FW_WLSUPPRESS", "TX_FAIL"}, /* 2: WLFC_CTL_PKTFLAG_WLSUPPRESS */
{"TX_PKT_FATE_FW_TOSSED_BYWLC", "TX_FAIL"}, /* 3: WLFC_CTL_PKTFLAG_TOSSED_BYWLC */
{"TX_PKT_FATE_SENT_NOACK", "NO_ACK"}, /* 4: WLFC_CTL_PKTFLAG_DISCARD_NOACK */
{"TX_PKT_FATE_FW_SUPPRESS_ACKED", "TX_FAIL"}, /* 5: WLFC_CTL_PKTFLAG_SUPPRESS_ACKED */
{"TX_PKT_FATE_FW_DROP_EXPTIME", "TX_FAIL"}, /* 6: WLFC_CTL_PKTFLAG_EXPIRED */
{"TX_PKT_FATE_FW_DROP_OTHER", "TX_FAIL"}, /* 7: WLFC_CTL_PKTFLAG_DROPPED */
{"TX_PKT_FATE_FW_PKT_FREE", "TX_FAIL"}, /* 8: WLFC_CTL_PKTFLAG_MKTFREE */
{"TX_PKT_FATE_FW_MAX_SUP_RETR", "TX_FAIL"}, /* 9: WLFC_CTL_PKTFLAG_MAX_SUP_RETR */
{"TX_PKT_FATE_FW_FORCED_EXPIRED", "TX_FAIL"}, /* 10: WLFC_CTL_PKTFLAG_FORCED_EXPIRED */
};
#define DBGREPLAY " Replay Counter: %02x%02x%02x%02x%02x%02x%02x%02x"
#define REPLAY_FMT(key) ((const eapol_key_hdr_t *)(key))->replay[0], \
((const eapol_key_hdr_t *)(key))->replay[1], \
((const eapol_key_hdr_t *)(key))->replay[2], \
((const eapol_key_hdr_t *)(key))->replay[3], \
((const eapol_key_hdr_t *)(key))->replay[4], \
((const eapol_key_hdr_t *)(key))->replay[5], \
((const eapol_key_hdr_t *)(key))->replay[6], \
((const eapol_key_hdr_t *)(key))->replay[7]
#define TXFATE_FMT " TX_PKTHASH:0x%X TX_PKT_FATE:%s"
#define TX_PKTHASH(pkthash) ((pkthash) ? (*pkthash) : (0))
#define TX_FATE_STR(fate) (((*fate) >= (0) && (*fate) <= (WLFC_CTL_PKTFLAG_FORCED_EXPIRED)) ?\
(tx_pktfate[(*fate)].reason) : "TX_PKT_FATE_UNKNOWN")
#define TX_FATE_STR_EX(fate) (((*fate) <= (WLFC_CTL_PKTFLAG_FORCED_EXPIRED)) ? \
(tx_pktfate[(*fate)].ex) : NULL)
#define TX_FATE(fate) ((fate) ? (TX_FATE_STR(fate)) : "N/A")
#define TX_FATE_EX(fate) ((fate) ? (TX_FATE_STR_EX(fate)) : NULL)
#define TX_FATE_ACKED(fate) ((fate) ? ((*fate) == (WLFC_CTL_PKTFLAG_DISCARD)) : (0))
#endif /*
* DHD_DNS_DUMP || DHD_ARP_DUMP || DHD_ICMP_DUMP ||
* DHD_DHCP_DUMP || DHD_8021X_DUMP || DHD_PKTDUMP_ROAM
*/
#define EAP_PRINT(str) \
do { \
if (tx) { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [TX]: " \
str TXFATE_FMT "\n", ifname, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [RX]: " \
str "\n", ifname)); \
} \
} while (0)
#define EAP_PRINT_REPLAY(str) \
do { \
if (tx) { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [TX]: " \
str DBGREPLAY TXFATE_FMT "\n", ifname, \
REPLAY_FMT(eap_key), TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [RX]: " \
str DBGREPLAY "\n", ifname, \
REPLAY_FMT(eap_key))); \
} \
} while (0)
#define EAP_PRINT_OTHER(str) \
do { \
if (tx) { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [TX]: " \
str "ver %d, type %d" TXFATE_FMT "\n", ifname, \
eapol_hdr->version, eapol_hdr->type, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [RX]: " \
str "ver %d, type %d\n", ifname, \
eapol_hdr->version, eapol_hdr->type)); \
} \
} while (0)
#define EAP_PRINT_OTHER_4WAY(str) \
do { \
if (tx) { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [TX]: " str \
"ver %d type %d keytype %d keyinfo 0x%02X" \
TXFATE_FMT "\n", ifname, eapol_hdr->version, \
eapol_hdr->type, eap_key->type, \
(uint32)hton16(eap_key->key_info), \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP(("ETHER_TYPE_802_1X[%s] [RX]: " str \
"ver %d type %d keytype %d keyinfo 0x%02X\n", \
ifname, eapol_hdr->version, eapol_hdr->type, \
eap_key->type, (uint32)hton16(eap_key->key_info))); \
} \
} while (0)
#define UDP_PORT_DNS 53 /* UDP DNS port */
/* EAPOL header */
typedef struct eapol_header {
struct ether_header eth; /* 802.3/Ethernet header */
uint8 version; /* EAPOL protocol version */
uint8 type; /* EAPOL type */
uint16 length; /* Length of body */
uint8 body[1]; /* Body (optional) */
} PACKED_STRUCT eapol_header_t;
/* EAP header */
typedef struct eap_header_fmt {
uint8 code;
uint8 id;
uint16 len;
uint8 type;
uint8 data[1];
} PACKED_STRUCT eap_header_fmt_t;
/* WSC EAP format */
typedef struct eap_wsc_fmt {
uint8 oui[3];
uint32 ouitype;
uint8 opcode;
uint8 flags;
uint8 data[1];
} PACKED_STRUCT eap_wsc_fmt_t;
/* EAPOL-Key */
typedef struct eapol_key_hdr {
uint8 type; /* Key Descriptor Type */
uint16 key_info; /* Key Information (unaligned) */
uint16 key_len; /* Key Length (unaligned) */
uint8 replay[WPA_KEY_REPLAY_LEN]; /* Replay Counter */
uint8 nonce[WPA_KEY_NONCE_LEN]; /* Nonce */
uint8 iv[WPA_KEY_IV_LEN]; /* Key IV */
uint8 rsc[WPA_KEY_RSC_LEN]; /* Key RSC */
uint8 id[WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */
uint8 mic[WPA_KEY_MIC_LEN]; /* Key MIC */
uint16 data_len; /* Key Data Length */
uint8 data[WPA_KEY_DATA_LEN]; /* Key data */
} PACKED_STRUCT eapol_key_hdr_t;
typedef struct hdr_fmt {
struct ipv4_hdr iph;
struct bcmudp_hdr udph;
} PACKED_STRUCT hdr_fmt_t;
msg_eapol_t
dhd_is_4way_msg(uint8 *pktdata)
{
eapol_header_t *eapol_hdr;
eapol_key_hdr_t *eap_key;
msg_eapol_t type = EAPOL_OTHER;
bool pair, ack, mic, kerr, req, sec, install;
uint16 key_info;
if (!pktdata) {
DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__));
return type;
}
eapol_hdr = (eapol_header_t *)pktdata;
eap_key = (eapol_key_hdr_t *)(eapol_hdr->body);
if (eap_key->type != EAPOL_WPA2_KEY) {
return type;
}
key_info = hton16(eap_key->key_info);
pair = !!(key_info & KEYINFO_TYPE_MASK);
ack = !!(key_info & KEYINFO_KEYACK_MASK);
mic = !!(key_info & KEYINFO_KEYMIC_MASK);
kerr = !!(key_info & KEYINFO_ERROR_MASK);
req = !!(key_info & KEYINFO_REQ_MASK);
sec = !!(key_info & KEYINFO_SECURE_MASK);
install = !!(key_info & KEYINFO_INSTALL_MASK);
if (pair && !install && ack && !mic && !sec && !kerr && !req) {
type = EAPOL_4WAY_M1;
} else if (pair && !install && !ack && mic && !sec && !kerr && !req) {
type = EAPOL_4WAY_M2;
} else if (pair && ack && mic && sec && !kerr && !req) {
type = EAPOL_4WAY_M3;
} else if (pair && !install && !ack && mic && sec && !req && !kerr) {
type = EAPOL_4WAY_M4;
} else if (!pair && !install && ack && mic && sec && !req && !kerr) {
type = EAPOL_GROUPKEY_M1;
} else if (!pair && !install && !ack && mic && sec && !req && !kerr) {
type = EAPOL_GROUPKEY_M2;
} else {
type = EAPOL_OTHER;
}
return type;
}
#ifdef DHD_PKTDUMP_ROAM
static void
dhd_dump_pkt_cnts_inc(dhd_pub_t *dhdp, bool tx, uint16 *pktfate, uint16 pkttype)
{
pkt_cnts_log_t *pktcnts;
pkt_cnt_t *cnt;
if (!dhdp) {
DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
return;
}
pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
if (!pktcnts) {
DHD_ERROR(("%s: pktcnts is NULL\n", __FUNCTION__));
return;
}
if (!pktcnts->enabled || (tx && !pktfate)) {
return;
}
if (pkttype == PKT_CNT_TYPE_ARP) {
cnt = (pkt_cnt_t *)&pktcnts->arp_cnt;
} else if (pkttype == PKT_CNT_TYPE_DNS) {
cnt = (pkt_cnt_t *)&pktcnts->dns_cnt;
} else {
/* invalid packet type */
return;
}
if (tx) {
TX_FATE_ACKED(pktfate) ? cnt->tx_cnt++ : cnt->tx_err_cnt++;
} else {
cnt->rx_cnt++;
}
}
static void
dhd_dump_pkt_timer(unsigned long data)
{
dhd_pub_t *dhdp = (dhd_pub_t *)data;
pkt_cnts_log_t *pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
pktcnts->enabled = FALSE;
/* print out the packet counter value */
DHD_PKTDUMP(("============= PACKET COUNT SUMMARY ============\n"));
DHD_PKTDUMP(("- Reason: %s\n", pkt_cnt_msg[pktcnts->reason]));
DHD_PKTDUMP(("- Duration: %d msec(s)\n", PKT_CNT_TIMER_INTERNVAL_MS));
DHD_PKTDUMP(("- ARP PACKETS: tx_success:%d tx_fail:%d rx_cnt:%d\n",
pktcnts->arp_cnt.tx_cnt, pktcnts->arp_cnt.tx_err_cnt,
pktcnts->arp_cnt.rx_cnt));
DHD_PKTDUMP(("- DNS PACKETS: tx_success:%d tx_fail:%d rx_cnt:%d\n",
pktcnts->dns_cnt.tx_cnt, pktcnts->dns_cnt.tx_err_cnt,
pktcnts->dns_cnt.rx_cnt));
DHD_PKTDUMP(("============= END OF COUNT SUMMARY ============\n"));
}
void
dhd_dump_mod_pkt_timer(dhd_pub_t *dhdp, uint16 rsn)
{
pkt_cnts_log_t *pktcnts;
if (!dhdp || !dhdp->pktcnts) {
DHD_ERROR(("%s: dhdp or dhdp->pktcnts is NULL\n",
__FUNCTION__));
return;
}
if (!PKT_CNT_RSN_VALID(rsn)) {
DHD_ERROR(("%s: invalid reason code %d\n",
__FUNCTION__, rsn));
return;
}
pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
if (timer_pending(&pktcnts->pktcnt_timer)) {
del_timer_sync(&pktcnts->pktcnt_timer);
}
bzero(&pktcnts->arp_cnt, sizeof(pkt_cnt_t));
bzero(&pktcnts->dns_cnt, sizeof(pkt_cnt_t));
pktcnts->reason = rsn;
pktcnts->enabled = TRUE;
mod_timer(&pktcnts->pktcnt_timer,
jiffies + msecs_to_jiffies(PKT_CNT_TIMER_INTERNVAL_MS));
DHD_PKTDUMP(("%s: Arm the pktcnt timer. reason=%d\n",
__FUNCTION__, rsn));
}
void
dhd_dump_pkt_init(dhd_pub_t *dhdp)
{
pkt_cnts_log_t *pktcnts;
if (!dhdp) {
DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
return;
}
pktcnts = (pkt_cnts_log_t *)MALLOCZ(dhdp->osh, sizeof(pkt_cnts_log_t));
if (!pktcnts) {
DHD_ERROR(("%s: failed to allocate memory for pktcnts\n",
__FUNCTION__));
return;
}
/* init timers */
init_timer_compat(&pktcnts->pktcnt_timer, dhd_dump_pkt_timer, dhdp);
dhdp->pktcnts = pktcnts;
}
void
dhd_dump_pkt_deinit(dhd_pub_t *dhdp)
{
pkt_cnts_log_t *pktcnts;
if (!dhdp || !dhdp->pktcnts) {
DHD_ERROR(("%s: dhdp or pktcnts is NULL\n", __FUNCTION__));
return;
}
pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
pktcnts->enabled = FALSE;
del_timer_sync(&pktcnts->pktcnt_timer);
MFREE(dhdp->osh, dhdp->pktcnts, sizeof(pkt_cnts_log_t));
dhdp->pktcnts = NULL;
}
void
dhd_dump_pkt_clear(dhd_pub_t *dhdp)
{
pkt_cnts_log_t *pktcnts;
if (!dhdp || !dhdp->pktcnts) {
DHD_ERROR(("%s: dhdp or pktcnts is NULL\n", __FUNCTION__));
return;
}
pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
pktcnts->enabled = FALSE;
del_timer_sync(&pktcnts->pktcnt_timer);
pktcnts->reason = 0;
bzero(&pktcnts->arp_cnt, sizeof(pkt_cnt_t));
bzero(&pktcnts->dns_cnt, sizeof(pkt_cnt_t));
}
bool
dhd_dump_pkt_enabled(dhd_pub_t *dhdp)
{
pkt_cnts_log_t *pktcnts;
if (!dhdp || !dhdp->pktcnts) {
return FALSE;
}
pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts);
return pktcnts->enabled;
}
#else
static INLINE void
dhd_dump_pkt_cnts_inc(dhd_pub_t *dhdp, bool tx, uint16 *pktfate, uint16 pkttype) { }
static INLINE bool
dhd_dump_pkt_enabled(dhd_pub_t *dhdp) { return FALSE; }
#endif /* DHD_PKTDUMP_ROAM */
#ifdef DHD_8021X_DUMP
static void
dhd_dump_wsc_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata,
uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate)
{
eapol_header_t *eapol_hdr;
eap_header_fmt_t *eap_hdr;
eap_wsc_fmt_t *eap_wsc;
char *ifname;
uint16 eap_len;
bool cond;
if (!pktdata) {
DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__));
return;
}
if (pktlen < (ETHER_HDR_LEN + EAPOL_HDR_LEN)) {
DHD_ERROR(("%s: invalid pkt length\n", __FUNCTION__));
return;
}
eapol_hdr = (eapol_header_t *)pktdata;
eap_hdr = (eap_header_fmt_t *)(eapol_hdr->body);
if (eap_hdr->type != EAP_TYPE_EXP) {
return;
}
eap_len = ntoh16(eap_hdr->len);
if (eap_len < EAP_WSC_MIN_DATA_LEN) {
return;
}
eap_wsc = (eap_wsc_fmt_t *)(eap_hdr->data);
if (bcmp(eap_wsc->oui, (const uint8 *)WFA_VID, WFA_VID_LEN) ||
(ntoh32(eap_wsc->ouitype) != WFA_VTYPE)) {
return;
}
if (eap_wsc->flags) {
return;
}
ifname = dhd_ifname(dhd, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
if (eap_wsc->opcode == WSC_OPCODE_MSG) {
const uint8 *tlv_buf = (const uint8 *)(eap_wsc->data);
const uint8 *msg;
uint16 msglen;
uint16 wsc_data_len = (uint16)(eap_len - EAP_HDR_LEN - EAP_WSC_DATA_OFFSET);
bcm_xtlv_opts_t opt = BCM_XTLV_OPTION_IDBE | BCM_XTLV_OPTION_LENBE;
msg = bcm_get_data_from_xtlv_buf(tlv_buf, wsc_data_len,
WSC_ATTR_MSG, &msglen, opt);
if (msg && msglen) {
switch (*msg) {
case WSC_MSG_M1:
DHD_STATLOG_DATA(dhd, ST(WPS_M1), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M1");
break;
case WSC_MSG_M2:
DHD_STATLOG_DATA(dhd, ST(WPS_M2), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M2");
break;
case WSC_MSG_M3:
DHD_STATLOG_DATA(dhd, ST(WPS_M3), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M3");
break;
case WSC_MSG_M4:
DHD_STATLOG_DATA(dhd, ST(WPS_M4), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M4");
break;
case WSC_MSG_M5:
DHD_STATLOG_DATA(dhd, ST(WPS_M5), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M5");
break;
case WSC_MSG_M6:
DHD_STATLOG_DATA(dhd, ST(WPS_M6), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M6");
break;
case WSC_MSG_M7:
DHD_STATLOG_DATA(dhd, ST(WPS_M7), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M7");
break;
case WSC_MSG_M8:
DHD_STATLOG_DATA(dhd, ST(WPS_M8), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WPS M8");
break;
default:
break;
}
}
} else if (eap_wsc->opcode == WSC_OPCODE_START) {
DHD_STATLOG_DATA(dhd, ST(WSC_START), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WSC Start");
} else if (eap_wsc->opcode == WSC_OPCODE_DONE) {
DHD_STATLOG_DATA(dhd, ST(WSC_DONE), ifidx, tx, cond);
EAP_PRINT("EAP Packet, WSC Done");
}
}
static void
dhd_dump_eap_packet(dhd_pub_t *dhd, int ifidx, uint8 *pktdata,
uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate)
{
eapol_header_t *eapol_hdr;
eap_header_fmt_t *eap_hdr;
char *ifname;
bool cond;
if (!pktdata) {
DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__));
return;
}
eapol_hdr = (eapol_header_t *)pktdata;
eap_hdr = (eap_header_fmt_t *)(eapol_hdr->body);
ifname = dhd_ifname(dhd, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
if (eap_hdr->code == EAP_CODE_REQUEST ||
eap_hdr->code == EAP_CODE_RESPONSE) {
bool isreq = (eap_hdr->code == EAP_CODE_REQUEST);
switch (eap_hdr->type) {
case EAP_TYPE_IDENT:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_IDENTITY), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, Identity");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_IDENTITY), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, Identity");
}
break;
case EAP_TYPE_TLS:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_TLS), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, TLS");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_TLS), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, TLS");
}
break;
case EAP_TYPE_LEAP:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_LEAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, LEAP");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_LEAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, LEAP");
}
break;
case EAP_TYPE_TTLS:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_TTLS), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, TTLS");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_TTLS), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, TTLS");
}
break;
case EAP_TYPE_AKA:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_AKA), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, AKA");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_AKA), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, AKA");
}
break;
case EAP_TYPE_PEAP:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_PEAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, PEAP");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_PEAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, PEAP");
}
break;
case EAP_TYPE_FAST:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_FAST), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, FAST");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_FAST), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, FAST");
}
break;
case EAP_TYPE_PSK:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_PSK), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, PSK");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_PSK), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, PSK");
}
break;
case EAP_TYPE_AKAP:
if (isreq) {
DHD_STATLOG_DATA(dhd, ST(EAP_REQ_AKAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Request, AKAP");
} else {
DHD_STATLOG_DATA(dhd, ST(EAP_RESP_AKAP), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Response, AKAP");
}
break;
case EAP_TYPE_EXP:
dhd_dump_wsc_message(dhd, ifidx, pktdata, pktlen, tx,
pkthash, pktfate);
break;
default:
break;
}
} else if (eap_hdr->code == EAP_CODE_SUCCESS) {
DHD_STATLOG_DATA(dhd, ST(EAP_SUCCESS), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Success");
} else if (eap_hdr->code == EAP_CODE_FAILURE) {
DHD_STATLOG_DATA(dhd, ST(EAP_FAILURE), ifidx, tx, cond);
EAP_PRINT("EAP Packet, Failure");
}
}
static void
dhd_dump_eapol_4way_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata, bool tx,
uint32 *pkthash, uint16 *pktfate)
{
eapol_header_t *eapol_hdr;
eapol_key_hdr_t *eap_key;
msg_eapol_t type;
char *ifname;
bool cond;
if (!pktdata) {
DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__));
return;
}
type = dhd_is_4way_msg(pktdata);
ifname = dhd_ifname(dhd, ifidx);
eapol_hdr = (eapol_header_t *)pktdata;
eap_key = (eapol_key_hdr_t *)(eapol_hdr->body);
cond = (tx && pktfate) ? FALSE : TRUE;
if (eap_key->type != EAPOL_WPA2_KEY) {
EAP_PRINT_OTHER("NON EAPOL_WPA2_KEY");
return;
}
switch (type) {
case EAPOL_4WAY_M1:
DHD_STATLOG_DATA(dhd, ST(EAPOL_M1), ifidx, tx, cond);
EAP_PRINT("EAPOL Packet, 4-way handshake, M1");
break;
case EAPOL_4WAY_M2:
DHD_STATLOG_DATA(dhd, ST(EAPOL_M2), ifidx, tx, cond);
EAP_PRINT("EAPOL Packet, 4-way handshake, M2");
break;
case EAPOL_4WAY_M3:
DHD_STATLOG_DATA(dhd, ST(EAPOL_M3), ifidx, tx, cond);
EAP_PRINT("EAPOL Packet, 4-way handshake, M3");
break;
case EAPOL_4WAY_M4:
DHD_STATLOG_DATA(dhd, ST(EAPOL_M4), ifidx, tx, cond);
EAP_PRINT("EAPOL Packet, 4-way handshake, M4");
break;
case EAPOL_GROUPKEY_M1:
DHD_STATLOG_DATA(dhd, ST(EAPOL_GROUPKEY_M1), ifidx, tx, cond);
EAP_PRINT_REPLAY("EAPOL Packet, GROUP Key handshake, M1");
break;
case EAPOL_GROUPKEY_M2:
DHD_STATLOG_DATA(dhd, ST(EAPOL_GROUPKEY_M2), ifidx, tx, cond);
EAP_PRINT_REPLAY("EAPOL Packet, GROUP Key handshake, M2");
if (ifidx == 0 && tx && pktfate) {
dhd_dump_mod_pkt_timer(dhd, PKT_CNT_RSN_GRPKEY_UP);
}
break;
default:
DHD_STATLOG_DATA(dhd, ST(8021X_OTHER), ifidx, tx, cond);
EAP_PRINT_OTHER("OTHER 4WAY");
break;
}
}
void
dhd_dump_eapol_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata,
uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate)
{
char *ifname;
eapol_header_t *eapol_hdr = (eapol_header_t *)pktdata;
bool cond;
if (!pktdata) {
DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__));
return;
}
eapol_hdr = (eapol_header_t *)pktdata;
ifname = dhd_ifname(dhd, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
if (eapol_hdr->type == EAP_PACKET) {
dhd_dump_eap_packet(dhd, ifidx, pktdata, pktlen, tx,
pkthash, pktfate);
} else if (eapol_hdr->type == EAPOL_START) {
DHD_STATLOG_DATA(dhd, ST(EAPOL_START), ifidx, tx, cond);
EAP_PRINT("EAP Packet, EAPOL-Start");
} else if (eapol_hdr->type == EAPOL_KEY) {
dhd_dump_eapol_4way_message(dhd, ifidx, pktdata, tx,
pkthash, pktfate);
} else {
DHD_STATLOG_DATA(dhd, ST(8021X_OTHER), ifidx, tx, cond);
EAP_PRINT_OTHER("OTHER 8021X");
}
}
#endif /* DHD_8021X_DUMP */
bool
dhd_check_ip_prot(uint8 *pktdata, uint16 ether_type)
{
hdr_fmt_t *b = (hdr_fmt_t *)&pktdata[ETHER_HDR_LEN];
struct ipv4_hdr *iph = &b->iph;
/* check IP header */
if ((ether_type != ETHER_TYPE_IP) ||
(IPV4_HLEN(iph) < IPV4_HLEN_MIN) ||
(IP_VER(iph) != IP_VER_4)) {
return FALSE;
}
return TRUE;
}
bool
dhd_check_dhcp(uint8 *pktdata)
{
hdr_fmt_t *b = (hdr_fmt_t *)&pktdata[ETHER_HDR_LEN];
struct ipv4_hdr *iph = &b->iph;
if (IPV4_PROT(iph) != IP_PROT_UDP) {
return FALSE;
}
/* check UDP port for bootp (67, 68) */
if (b->udph.src_port != htons(DHCP_PORT_SERVER) &&
b->udph.src_port != htons(DHCP_PORT_CLIENT) &&
b->udph.dst_port != htons(DHCP_PORT_SERVER) &&
b->udph.dst_port != htons(DHCP_PORT_CLIENT)) {
return FALSE;
}
/* check header length */
if (ntohs(iph->tot_len) < ntohs(b->udph.len) + sizeof(struct bcmudp_hdr)) {
return FALSE;
}
return TRUE;
}
#if defined(DHD_DHCP_DUMP) || defined(WL_CFGVENDOR_CUST_ADVLOG)
#define BOOTP_CHADDR_LEN 16
#define BOOTP_SNAME_LEN 64
#define BOOTP_FILE_LEN 128
#define BOOTP_MIN_DHCP_OPT_LEN 312
#define BOOTP_MAGIC_COOKIE_LEN 4
#define DHCP_MSGTYPE_DISCOVER 1
#define DHCP_MSGTYPE_OFFER 2
#define DHCP_MSGTYPE_REQUEST 3
#define DHCP_MSGTYPE_DECLINE 4
#define DHCP_MSGTYPE_ACK 5
#define DHCP_MSGTYPE_NAK 6
#define DHCP_MSGTYPE_RELEASE 7
#define DHCP_MSGTYPE_INFORM 8
typedef struct bootp_fmt {
struct ipv4_hdr iph;
struct bcmudp_hdr udph;
uint8 op;
uint8 htype;
uint8 hlen;
uint8 hops;
uint32 transaction_id;
uint16 secs;
uint16 flags;
uint32 client_ip;
uint32 assigned_ip;
uint32 server_ip;
uint32 relay_ip;
uint8 hw_address[BOOTP_CHADDR_LEN];
uint8 server_name[BOOTP_SNAME_LEN];
uint8 file_name[BOOTP_FILE_LEN];
uint8 options[BOOTP_MIN_DHCP_OPT_LEN];
} PACKED_STRUCT bootp_fmt_t;
#endif /* DHD_DHCP_DUMP || WL_CFGVENDOR_CUST_ADVLOG */
#ifdef DHD_DHCP_DUMP
#define DHCP_PRINT(str) \
do { \
if (tx) { \
DHD_PKTDUMP((str " %s[%s][%s] [TX] -" TXFATE_FMT "\n", \
typestr, opstr, ifname, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP((str " %s[%s][%s] [RX]\n", \
typestr, opstr, ifname)); \
} \
} while (0)
#define MAX_DHCP_OPS_STR 3
#define MAX_DHCP_TYPES_STR 9
static const uint8 bootp_magic_cookie[4] = { 99, 130, 83, 99 };
static char dhcp_ops[MAX_DHCP_OPS_STR][10] = {
"NA", "REQUEST", "REPLY"
};
static char dhcp_types[MAX_DHCP_TYPES_STR][10] = {
"NA", "DISCOVER", "OFFER", "REQUEST", "DECLINE", "ACK", "NAK", "RELEASE", "INFORM"
};
#define DHCP_OPS_STR(ops) ((ops < MAX_DHCP_OPS_STR) ? \
(dhcp_ops[ops]) : "UNKNOWN_DHCP_OPS")
#define DHCP_TYPES_STR(type) ((type < MAX_DHCP_TYPES_STR) ? \
(dhcp_types[type]) : "UNKNOWN_DHCP_TYPE")
#ifdef DHD_STATUS_LOGGING
#define MAX_DHCP_TYPES_STAT 9
#define DHCP_TYPES_STAT(type) ((type < MAX_DHCP_TYPES_STAT) ? \
(dhcp_types_stat[type]) : ST(INVALID))
static const int dhcp_types_stat[MAX_DHCP_TYPES_STAT] = {
ST(INVALID), ST(DHCP_DISCOVER), ST(DHCP_OFFER), ST(DHCP_REQUEST),
ST(DHCP_DECLINE), ST(DHCP_ACK), ST(DHCP_NAK), ST(DHCP_RELEASE),
ST(DHCP_INFORM)
};
#endif /* DHD_STATUS_LOGGING */
void
dhd_dhcp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx,
uint32 *pkthash, uint16 *pktfate)
{
bootp_fmt_t *b = (bootp_fmt_t *)&pktdata[ETHER_HDR_LEN];
uint8 *ptr, *opt, *end = (uint8 *) b + ntohs(b->iph.tot_len);
uint8 dhcp_type = 0;
int len, opt_len;
char *ifname = NULL, *typestr = NULL, *opstr = NULL;
bool cond;
ifname = dhd_ifname(dhdp, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
len = ntohs(b->udph.len) - sizeof(struct bcmudp_hdr);
opt_len = len - (sizeof(*b) - sizeof(struct ipv4_hdr) -
sizeof(struct bcmudp_hdr) - sizeof(b->options));
/* parse bootp options */
if (opt_len >= BOOTP_MAGIC_COOKIE_LEN &&
!memcmp(b->options, bootp_magic_cookie, BOOTP_MAGIC_COOKIE_LEN)) {
ptr = &b->options[BOOTP_MAGIC_COOKIE_LEN];
while (ptr < end && *ptr != 0xff) {
opt = ptr++;
if (*opt == 0) {
continue;
}
ptr += *ptr + 1;
if (ptr >= end) {
break;
}
if (*opt == DHCP_OPT_MSGTYPE) {
if (opt[1]) {
dhcp_type = opt[2];
typestr = DHCP_TYPES_STR(dhcp_type);
opstr = DHCP_OPS_STR(b->op);
DHD_STATLOG_DATA(dhdp, DHCP_TYPES_STAT(dhcp_type),
ifidx, tx, cond);
DHCP_PRINT("DHCP");
break;
}
}
}
}
}
#endif /* DHD_DHCP_DUMP */
bool
dhd_check_icmp(uint8 *pktdata)
{
uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
struct ipv4_hdr *iph = (struct ipv4_hdr *)pkt;
if (IPV4_PROT(iph) != IP_PROT_ICMP) {
return FALSE;
}
/* check header length */
if (ntohs(iph->tot_len) - IPV4_HLEN(iph) < sizeof(struct bcmicmp_hdr)) {
return FALSE;
}
return TRUE;
}
bool
dhd_check_icmpv6(uint8 *pktdata, uint32 plen)
{
uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
struct ipv6_hdr *ip6h = (struct ipv6_hdr *)pkt;
if (IPV6_PROT(ip6h) != IP_PROT_ICMP6) {
return FALSE;
}
/* check header length */
if (plen <= IPV6_MIN_HLEN) {
return FALSE;
}
return TRUE;
}
#ifdef DHD_ICMP_DUMP
#define ICMP_TYPE_DEST_UNREACH 3
#define ICMP_ECHO_SEQ_OFFSET 6
#define ICMP_ECHO_SEQ(h) (*(uint16 *)((uint8 *)(h) + (ICMP_ECHO_SEQ_OFFSET)))
#define ICMP_PING_PRINT(str) \
do { \
if (tx) { \
DHD_PKTDUMP_MEM((str "[%s][TX] : SEQNUM=%d" \
TXFATE_FMT "\n", ifname, seqnum, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s][RX] : SEQNUM=%d\n", \
ifname, seqnum)); \
} \
} while (0)
#define ICMP_PRINT(str) \
do { \
if (tx) { \
DHD_PKTDUMP_MEM((str "[%s][TX] : TYPE=%d, CODE=%d" \
TXFATE_FMT "\n", ifname, type, code, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s][RX] : TYPE=%d," \
" CODE=%d\n", ifname, type, code)); \
} \
} while (0)
void
dhd_icmp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx,
uint32 *pkthash, uint16 *pktfate)
{
uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
struct bcmicmp_hdr *icmph;
char *ifname;
bool cond;
uint16 seqnum, type, code;
ifname = dhd_ifname(dhdp, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
icmph = (struct bcmicmp_hdr *)((uint8 *)pkt + sizeof(struct ipv4_hdr));
seqnum = 0;
type = icmph->type;
code = icmph->code;
if (type == ICMP_TYPE_ECHO_REQUEST) {
seqnum = ntoh16(ICMP_ECHO_SEQ(icmph));
DHD_STATLOG_DATA(dhdp, ST(ICMP_PING_REQ), ifidx, tx, cond);
ICMP_PING_PRINT("PING REQUEST");
} else if (type == ICMP_TYPE_ECHO_REPLY) {
seqnum = ntoh16(ICMP_ECHO_SEQ(icmph));
DHD_STATLOG_DATA(dhdp, ST(ICMP_PING_RESP), ifidx, tx, cond);
ICMP_PING_PRINT("PING REPLY");
} else if (type == ICMP_TYPE_DEST_UNREACH) {
DHD_STATLOG_DATA(dhdp, ST(ICMP_DEST_UNREACH), ifidx, tx, cond);
ICMP_PRINT("ICMP DEST UNREACH");
} else {
DHD_STATLOG_DATA(dhdp, ST(ICMP_OTHER), ifidx, tx, cond);
ICMP_PRINT("ICMP OTHER");
}
}
#endif /* DHD_ICMP_DUMP */
bool
dhd_check_arp(uint8 *pktdata, uint16 ether_type)
{
uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
struct bcmarp *arph = (struct bcmarp *)pkt;
/* validation check */
if ((ether_type != ETHER_TYPE_ARP) ||
(arph->htype != hton16(HTYPE_ETHERNET)) ||
(arph->hlen != ETHER_ADDR_LEN) ||
(arph->plen != 4)) {
return FALSE;
}
return TRUE;
}
#ifdef DHD_ARP_DUMP
#ifdef BOARD_HIKEY
/* On Hikey, due to continuous ARP prints
* DPC not scheduled. Hence rate limit the prints.
*/
#define DHD_PKTDUMP_ARP DHD_ERROR_RLMT
#else
#define DHD_PKTDUMP_ARP DHD_PKTDUMP
#endif /* BOARD_HIKEY */
#define ARP_PRINT(str) \
do { \
if (tx) { \
if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
DHD_PKTDUMP_ARP((str "[%s] [TX]" TXFATE_FMT "\n", \
ifname, TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [TX]" TXFATE_FMT "\n", \
ifname, TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [RX]\n", ifname)); \
} \
} while (0)
#define ARP_PRINT_OTHER(str) \
do { \
if (tx) { \
if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
DHD_PKTDUMP_ARP((str "[%s] [TX] op_code=%d" \
TXFATE_FMT "\n", ifname, opcode, \
TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [TX] op_code=%d" \
TXFATE_FMT "\n", ifname, opcode, \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [RX] op_code=%d\n", \
ifname, opcode)); \
} \
} while (0)
void
dhd_arp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx,
uint32 *pkthash, uint16 *pktfate)
{
uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
struct bcmarp *arph = (struct bcmarp *)pkt;
char *ifname;
uint16 opcode;
bool cond, dump_enabled;
ifname = dhd_ifname(dhdp, ifidx);
opcode = ntoh16(arph->oper);
cond = (tx && pktfate) ? FALSE : TRUE;
dump_enabled = dhd_dump_pkt_enabled(dhdp);
if (opcode == ARP_OPC_REQUEST) {
DHD_STATLOG_DATA(dhdp, ST(ARP_REQ), ifidx, tx, cond);
ARP_PRINT("ARP REQUEST");
} else if (opcode == ARP_OPC_REPLY) {
DHD_STATLOG_DATA(dhdp, ST(ARP_RESP), ifidx, tx, cond);
ARP_PRINT("ARP RESPONSE");
} else {
ARP_PRINT_OTHER("ARP OTHER");
}
if (ifidx == 0) {
dhd_dump_pkt_cnts_inc(dhdp, tx, pktfate, PKT_CNT_TYPE_ARP);
}
}
#endif /* DHD_ARP_DUMP */
bool
dhd_check_dns(uint8 *pktdata)
{
hdr_fmt_t *dnsh = (hdr_fmt_t *)&pktdata[ETHER_HDR_LEN];
struct ipv4_hdr *iph = &dnsh->iph;
if (IPV4_PROT(iph) != IP_PROT_UDP) {
return FALSE;
}
/* check UDP port for DNS */
if (dnsh->udph.src_port != hton16(UDP_PORT_DNS) &&
dnsh->udph.dst_port != hton16(UDP_PORT_DNS)) {
return FALSE;
}
/* check header length */
if (ntoh16(iph->tot_len) < (ntoh16(dnsh->udph.len) +
sizeof(struct bcmudp_hdr))) {
return FALSE;
}
return TRUE;
}
#ifdef DHD_DNS_DUMP
typedef struct dns_fmt {
struct ipv4_hdr iph;
struct bcmudp_hdr udph;
uint16 id;
uint16 flags;
uint16 qdcount;
uint16 ancount;
uint16 nscount;
uint16 arcount;
} PACKED_STRUCT dns_fmt_t;
#define DNS_QR_LOC 15
#define DNS_OPCODE_LOC 11
#define DNS_RCODE_LOC 0
#define DNS_QR_MASK ((0x1) << (DNS_QR_LOC))
#define DNS_OPCODE_MASK ((0xF) << (DNS_OPCODE_LOC))
#define DNS_RCODE_MASK ((0xF) << (DNS_RCODE_LOC))
#define GET_DNS_QR(flags) (((flags) & (DNS_QR_MASK)) >> (DNS_QR_LOC))
#define GET_DNS_OPCODE(flags) (((flags) & (DNS_OPCODE_MASK)) >> (DNS_OPCODE_LOC))
#define GET_DNS_RCODE(flags) (((flags) & (DNS_RCODE_MASK)) >> (DNS_RCODE_LOC))
#define DNS_UNASSIGNED_OPCODE(flags) ((GET_DNS_OPCODE(flags) >= (6)))
static const char dns_opcode_types[][11] = {
"QUERY", "IQUERY", "STATUS", "UNASSIGNED", "NOTIFY", "UPDATE"
};
#define DNSOPCODE(op) \
(DNS_UNASSIGNED_OPCODE(flags) ? "UNASSIGNED" : dns_opcode_types[op])
#define DNS_REQ_PRINT(str) \
do { \
if (tx) { \
if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
DHD_PKTDUMP((str "[%s] [TX] ID:0x%04X OPCODE:%s" \
TXFATE_FMT "\n", ifname, id, DNSOPCODE(opcode), \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [TX] ID:0x%04X OPCODE:%s" \
TXFATE_FMT "\n", ifname, id, DNSOPCODE(opcode), \
TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
} \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [RX] ID:0x%04X OPCODE:%s\n", \
ifname, id, DNSOPCODE(opcode))); \
} \
} while (0)
#define DNS_RESP_PRINT(str) \
do { \
if (tx) { \
if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
DHD_PKTDUMP((str "[%s] [TX] ID:0x%04X OPCODE:%s RCODE:%d" \
TXFATE_FMT "\n", ifname, id, DNSOPCODE(opcode), \
GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [TX] ID:0x%04X OPCODE:%s RCODE:%d" \
TXFATE_FMT "\n", ifname, id, DNSOPCODE(opcode), \
GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), \
TX_FATE(pktfate))); \
} \
} else { \
DHD_PKTDUMP_MEM((str "[%s] [RX] ID:0x%04X OPCODE:%s RCODE:%d\n", \
ifname, id, DNSOPCODE(opcode), GET_DNS_RCODE(flags))); \
} \
} while (0)
void
dhd_dns_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx,
uint32 *pkthash, uint16 *pktfate)
{
dns_fmt_t *dnsh = (dns_fmt_t *)&pktdata[ETHER_HDR_LEN];
uint16 flags, opcode, id;
char *ifname;
bool cond, dump_enabled;
ifname = dhd_ifname(dhdp, ifidx);
cond = (tx && pktfate) ? FALSE : TRUE;
dump_enabled = dhd_dump_pkt_enabled(dhdp);
flags = hton16(dnsh->flags);
opcode = GET_DNS_OPCODE(flags);
id = hton16(dnsh->id);
if (GET_DNS_QR(flags)) {
/* Response */
DHD_STATLOG_DATA(dhdp, ST(DNS_RESP), ifidx, tx, cond);
DNS_RESP_PRINT("DNS RESPONSE");
} else {
/* Request */
DHD_STATLOG_DATA(dhdp, ST(DNS_QUERY), ifidx, tx, cond);
DNS_REQ_PRINT("DNS REQUEST");
}
if (ifidx == 0) {
dhd_dump_pkt_cnts_inc(dhdp, tx, pktfate, PKT_CNT_TYPE_DNS);
}
}
#endif /* DHD_DNS_DUMP */
#ifdef DHD_RX_DUMP
void
dhd_rx_pkt_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen)
{
struct ether_header *eh;
uint16 protocol;
char *pkttype = "UNKNOWN";
if (!dhdp) {
DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
return;
}
if (!pktdata) {
DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__));
return;
}
eh = (struct ether_header *)pktdata;
protocol = hton16(eh->ether_type);
BCM_REFERENCE(pktlen);
switch (protocol) {
case ETHER_TYPE_IP:
pkttype = "IP";
break;
case ETHER_TYPE_ARP:
pkttype = "ARP";
break;
case ETHER_TYPE_BRCM:
pkttype = "BRCM";
break;
case ETHER_TYPE_802_1X:
pkttype = "802.1X";
break;
case ETHER_TYPE_WAI:
pkttype = "WAPI";
break;
default:
break;
}
DHD_PKTDUMP(("RX DUMP[%s] - %s\n", dhd_ifname(dhdp, ifidx), pkttype));
if (protocol != ETHER_TYPE_BRCM) {
if (pktdata[0] == 0xFF) {
DHD_PKTDUMP(("%s: BROADCAST\n", __FUNCTION__));
} else if (pktdata[0] & 1) {
DHD_PKTDUMP(("%s: MULTICAST: " MACDBG "\n",
__FUNCTION__, MAC2STRDBG(pktdata)));
}
#ifdef DHD_RX_FULL_DUMP
{
int k;
for (k = 0; k < pktlen; k++) {
DHD_PKTDUMP(("%02X ", pktdata[k]));
if ((k & 15) == 15)
DHD_PKTDUMP(("\n"));
}
DHD_PKTDUMP(("\n"));
}
#endif /* DHD_RX_FULL_DUMP */
}
}
#endif /* DHD_RX_DUMP */
#ifdef WL_CFGVENDOR_CUST_ADVLOG
typedef struct dhd_advlog_arr_map_entry {
uint32 avglog_type;
dhd_advlog_map_entry_t *arr;
uint32 arr_len;
} dhd_advlog_arr_map_entry_t;
typedef enum dhd_advlog_type {
DHD_ADVLOG_DHCP_TX = 0,
DHD_ADVLOG_DHCP_RX = 1,
DHD_ADVLOG_EAP = 2,
DHD_ADVLOG_EAPOL = 3,
DHD_ADVLOG_LAST = 4
} dhd_advlog_type_t;
static dhd_advlog_map_entry_t eap_type_map[] = {
{EAP_TYPE_IDENT, "Identity"},
{EAP_TYPE_TLS, "TLS"},
{EAP_TYPE_LEAP, "LEAP"},
{EAP_TYPE_TTLS, "TTLS"},
{EAP_TYPE_AKA, "AKA"},
{EAP_TYPE_PEAP, "PEAP"},
{EAP_TYPE_FAST, "FAST"},
{EAP_TYPE_PSK, "PSK"},
{EAP_TYPE_AKAP, "AKAP"},
};
static dhd_advlog_map_entry_t eap_advlog_map[] = {
{EAP_CODE_REQUEST, "[EAP] REQ type=%s len=%d"},
{EAP_CODE_RESPONSE, "[EAP] RESP type=%s len=%d tx_status=%s"},
{EAP_CODE_SUCCESS, "[EAP] SUCC"},
{EAP_CODE_FAILURE, "[EAP] FAIL"},
};
static dhd_advlog_map_entry_t dhcp_tx_advlog_map[] = {
{DHCP_MSGTYPE_DISCOVER, "[DHCP] DISCOVER tx_status=%s"},
{DHCP_MSGTYPE_REQUEST, "[DHCP] REQUEST tx_status=%s"},
};
static dhd_advlog_map_entry_t dhcp_rx_advlog_map[] = {
{DHCP_MSGTYPE_OFFER, "[DHCP] OFFER"},
{DHCP_MSGTYPE_ACK, "[DHCP] ACK"},
{DHCP_MSGTYPE_NAK, "[DHCP] NAK"},
};
static dhd_advlog_map_entry_t eapol_advlog_map[] = {
{EAPOL_4WAY_M1, "[EAPOL] 4WAY M1"},
{EAPOL_4WAY_M2, "[EAPOL] 4WAY M2 tx_status=%s"},
{EAPOL_4WAY_M3, "[EAPOL] 4WAY M3"},
{EAPOL_4WAY_M4, "[EAPOL] 4WAY M4 tx_status=%s"},
{EAPOL_GROUPKEY_M1, "[EAPOL] GTK M1"},
{EAPOL_GROUPKEY_M2, "[EAPOL] GTK M2 tx_status=%s"},
};
static dhd_advlog_arr_map_entry_t advlog_map_arr[] = {
{DHD_ADVLOG_DHCP_TX, dhcp_tx_advlog_map, ARRAY_SIZE(dhcp_tx_advlog_map)},
{DHD_ADVLOG_DHCP_RX, dhcp_rx_advlog_map, ARRAY_SIZE(dhcp_rx_advlog_map)},
{DHD_ADVLOG_EAP, eap_advlog_map, ARRAY_SIZE(eap_advlog_map)},
{DHD_ADVLOG_EAPOL, eapol_advlog_map, ARRAY_SIZE(eapol_advlog_map)},
};
const char* get_advlog_val(dhd_advlog_map_entry_t *arr, uint32 arr_len, int tag)
{
int i;
for (i = 0; i < arr_len; i++) {
if (tag == arr[i].key) {
return arr[i].val;
}
}
return NULL;
}
static int get_dhcp_type(uint8 *pktdata)
{
bootp_fmt_t *b = (bootp_fmt_t *)&pktdata[ETHER_HDR_LEN];
uint8 *ptr, *opt, *end = (uint8 *) b + ntohs(b->iph.tot_len);
int dhcp_type = 0, len, opt_len;
len = ntohs(b->udph.len) - sizeof(struct bcmudp_hdr);
opt_len = len - (sizeof(*b) - sizeof(struct ipv4_hdr) -
sizeof(struct bcmudp_hdr) - sizeof(b->options));
/* parse bootp options */
if (opt_len >= BOOTP_MAGIC_COOKIE_LEN &&
!memcmp(b->options, bootp_magic_cookie, BOOTP_MAGIC_COOKIE_LEN)) {
ptr = &b->options[BOOTP_MAGIC_COOKIE_LEN];
while (ptr < end && *ptr != 0xff) {
opt = ptr++;
if (*opt == 0) {
continue;
}
ptr += *ptr + 1;
if (ptr >= end) {
break;
}
if (*opt == DHCP_OPT_MSGTYPE) {
if (opt[1]) {
dhcp_type = opt[2];
return dhcp_type;
}
}
}
}
return BCME_ERROR;
}
static int dhd_send_supp(uint32 avglog_type, int arg_tag, int arg_type, uint32 pktlen,
bool tx, uint16 *pktfate)
{
const char *type_str = NULL;
const char *fmt_str = NULL;
dhd_advlog_map_entry_t *fmt_arr;
uint32 fmt_arr_len;
if (avglog_type >= DHD_ADVLOG_LAST) {
DHD_ERROR(("%s incorrect array index type:%d tag:%d\n",
__func__, avglog_type, arg_tag));
return BCME_ERROR;
}
if ((arg_type && !pktlen) || (!arg_type && pktlen)) {
DHD_ERROR(("%s Not expected value pair type:%d tag:%d pktlen:%u\n",
__func__, avglog_type, arg_tag, pktlen));
return BCME_ERROR;
}
if (arg_tag <= 0) {
DHD_ERROR(("%s Invalid tag number type:%d tag:%d\n",
__func__, avglog_type, arg_tag));
return BCME_ERROR;
}
/* Get the format string corresponding to the type */
fmt_arr = advlog_map_arr[avglog_type].arr;
fmt_arr_len = advlog_map_arr[avglog_type].arr_len;
fmt_str = get_advlog_val(fmt_arr, fmt_arr_len, arg_tag);
if (!fmt_str) {
DHD_ERROR(("%s map arr not found type:%d tag:%d\n",
__func__, avglog_type, arg_tag));
return BCME_ERROR;
}
/* EAP REQ/RESP */
if (arg_type && pktlen) {
/* Get Identity/PEAP/FAST/etc string */
type_str = get_advlog_val(eap_type_map, ARRAY_SIZE(eap_type_map), arg_type);
if (!type_str) {
DHD_ERROR(("%s type string not found type:%d tag:%d\n",
__func__, avglog_type, arg_tag));
return BCME_ERROR;
}
if (tx && pktfate) {
/* EAP RESP */
SUPP_ADVLOG((fmt_str, type_str, pktlen, TX_FATE_EX(pktfate)));
} else if (!tx) {
/* EAP REQ */
SUPP_ADVLOG((fmt_str, type_str, pktlen));
}
} else {
if (tx && pktfate) {
/* EAPOL M2/M4, GTK M2, DHCP DISC/REQ */
SUPP_ADVLOG((fmt_str, TX_FATE_EX(pktfate)));
} else if (!tx) {
/* EAPOL M1/M3, GTK M1, DHCP OFFER/ACK/NAK, EAP SUCC/FAIL */
SUPP_ADVLOG((fmt_str));
}
}
return BCME_OK;
}
void dhd_send_supp_dhcp(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx, uint16 *pktfate)
{
uint32 advlog_type;
/* Advanced Logging supports only STA mode */
if (!DHD_IF_ROLE_STA(dhdp, ifidx)) {
return;
}
if (tx) {
advlog_type = DHD_ADVLOG_DHCP_TX;
} else {
advlog_type = DHD_ADVLOG_DHCP_RX;
}
dhd_send_supp(advlog_type, get_dhcp_type(pktdata), 0, 0, tx, pktfate);
}
void dhd_send_supp_eap(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen,
bool tx, uint16 *pktfate)
{
eapol_header_t *eapol_hdr;
eap_header_fmt_t *eap_hdr;
uint16 len = 0;
uint8 type = 0;
/* Advanced Logging supports only STA mode */
if (!DHD_IF_ROLE_STA(dhdp, ifidx)) {
return;
}
eapol_hdr = (eapol_header_t *)pktdata;
eap_hdr = (eap_header_fmt_t *)(eapol_hdr->body);
if (eapol_hdr->type == EAP_PACKET) {
/* EAP SUCC/FAIL has no arg to print */
if (eap_hdr->code != EAP_CODE_SUCCESS &&
eap_hdr->code != EAP_CODE_FAILURE) {
type = eap_hdr->type;
len = ntoh16(eap_hdr->len);
}
dhd_send_supp(DHD_ADVLOG_EAP, eap_hdr->code, type, len, tx, pktfate);
} else if (eapol_hdr->type == EAPOL_KEY) {
dhd_send_supp(DHD_ADVLOG_EAPOL, dhd_is_4way_msg(pktdata),
0, 0, tx, pktfate);
}
}
#endif /* WL_CFGVENDOR_CUST_ADVLOG */