2007 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			2007 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
| /*
 | |
|  * 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 */
 | |
| #include <dhd_config.h>
 | |
| #include <wl_android.h>
 | |
| 
 | |
| #define DHD_PKTDUMP(arg)	printf arg
 | |
| #define DHD_PKTDUMP_MEM(arg)	printf 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)))
 | |
| 
 | |
| #ifdef DHD_PKTDUMP_ROAM
 | |
| static const char pkt_cnt_msg[][20] = {
 | |
| 	"INVALID",
 | |
| 	"ROAM_SUCCESS",
 | |
| 	"GROUP_KEY_UPDATE",
 | |
| 	"CONNECT_SUCCESS",
 | |
| 	"INVALID"
 | |
| };
 | |
| #endif
 | |
| 
 | |
| #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) <= (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(x, args...) \
 | |
| 	do { \
 | |
| 		if (dump_msg_level & DUMP_EAPOL_VAL) { \
 | |
| 			if (tx) { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [TX] : (%s) %s (%s)"TXFATE_FMT"\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [RX] : (%s) %s (%s)\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf)); \
 | |
| 			} \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define EAP_PRINT_REPLAY(x, args...) \
 | |
| 	do { \
 | |
| 		if (dump_msg_level & DUMP_EAPOL_VAL) { \
 | |
| 			if (tx) { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [TX] : (%s) %s (%s)"DBGREPLAY TXFATE_FMT"\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					REPLAY_FMT(eap_key), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [RX] : (%s) %s (%s)"DBGREPLAY"\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					REPLAY_FMT(eap_key))); \
 | |
| 			} \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define EAP_PRINT_OTHER(x, args...) \
 | |
| 	do { \
 | |
| 		if (dump_msg_level & DUMP_EAPOL_VAL) { \
 | |
| 			if (tx) { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [TX] : (%s) %s (%s) " \
 | |
| 					"ver %d, type %d"TXFATE_FMT"\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					eapol_hdr->version, eapol_hdr->type, \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [RX] : (%s) %s (%s) " \
 | |
| 					"ver %d, type %d\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					eapol_hdr->version, eapol_hdr->type)); \
 | |
| 			} \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define EAP_PRINT_OTHER_4WAY(x, args...) \
 | |
| 	do { \
 | |
| 		if (dump_msg_level & DUMP_EAPOL_VAL) { \
 | |
| 			if (tx) { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [TX] : (%s) %s (%s) " \
 | |
| 					"ver %d type %d keytype %d keyinfo 0x%02X"TXFATE_FMT"\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					eapol_hdr->version, eapol_hdr->type, eap_key->type, \
 | |
| 					(uint32)hton16(eap_key->key_info), \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP(("[%s] 802_1X " x " [RX] : (%s) %s (%s) " \
 | |
| 					"ver %d type %d keytype %d keyinfo 0x%02X\n", \
 | |
| 					ifname, ## args, \
 | |
| 					tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \
 | |
| 					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 && eap_key->type != EAPOL_WPA_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 (eap_key->type == EAPOL_WPA2_KEY) {
 | |
| 		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;
 | |
| 			if (dump_msg_level & DUMP_EAPOL_VAL) {
 | |
| 				printf("WPA2: key_info=0x%x, pair=%d, ack=%d, mic=%d, sec=%d, kerr=%d, req=%d\n",
 | |
| 					key_info, pair, ack, mic, sec, kerr, req);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else if (eap_key->type == EAPOL_WPA_KEY) {
 | |
| 		if (pair && !install && ack && !mic && !sec && !kerr && !req) {
 | |
| 			type = EAPOL_4WAY_M1;
 | |
| 		} else if (pair && !install && !ack && mic && !sec && !kerr && !req && eap_key->data_len) {
 | |
| 			type = EAPOL_4WAY_M2;
 | |
| 		} else if (pair && install && 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;
 | |
| 			if (dump_msg_level & DUMP_EAPOL_VAL) {
 | |
| 				printf("WPA: key_info=0x%x, pair=%d, ack=%d, mic=%d, sec=%d, kerr=%d, req=%d\n",
 | |
| 					key_info, pair, ack, mic, sec, kerr, req);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else {
 | |
| 		type = EAPOL_OTHER;
 | |
| 		if (dump_msg_level & DUMP_EAPOL_VAL) {
 | |
| 			printf("OTHER: key_info=0x%x, pair=%d, ack=%d, mic=%d, sec=%d, kerr=%d, req=%d\n",
 | |
| 				key_info, pair, ack, mic, sec, kerr, req);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return type;
 | |
| }
 | |
| 
 | |
| void
 | |
| dhd_dump_pkt(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen,
 | |
| 	bool tx, uint32 *pkthash, uint16 *pktfate)
 | |
| {
 | |
| 	struct ether_header *eh;
 | |
| 	uint16 ether_type;
 | |
| 
 | |
| 	if (!pktdata || pktlen < ETHER_HDR_LEN) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| #if defined(BCMPCIE) && defined(DHD_PKT_LOGGING)
 | |
| 	if (tx && !pkthash && !pktfate) {
 | |
| 		return;
 | |
| 	}
 | |
| #endif /* BCMPCIE && DHD_PKT_LOGGING */
 | |
| 
 | |
| 	eh = (struct ether_header *)pktdata;
 | |
| 	ether_type = ntoh16(eh->ether_type);
 | |
| 	if (ether_type == ETHER_TYPE_802_1X) {
 | |
| 		dhd_dump_eapol_message(dhdp, ifidx, pktdata, pktlen,
 | |
| 			tx, pkthash, pktfate);
 | |
| 	}
 | |
| 	if (ether_type == ETHER_TYPE_IP) {
 | |
| 		if (dhd_check_dhcp(pktdata)) {
 | |
| 			dhd_dhcp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate);
 | |
| 		} else if (dhd_check_icmp(pktdata)) {
 | |
| 			dhd_icmp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate);
 | |
| 		} else if (dhd_check_dns(pktdata)) {
 | |
| 			dhd_dns_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate);
 | |
| 		}
 | |
| 	}
 | |
| 	if (ether_type == ETHER_TYPE_ARP) {
 | |
| 		if (dhd_check_arp(pktdata, ether_type)) {
 | |
| 			dhd_arp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate);
 | |
| 		}
 | |
| 	}
 | |
| 	dhd_trx_pkt_dump(dhdp, ifidx, pktdata, pktlen, tx);
 | |
| }
 | |
| 
 | |
| #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;
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 
 | |
| 	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:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M1);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M1), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M1");
 | |
| 				break;
 | |
| 			case WSC_MSG_M2:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M2);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M2), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M2");
 | |
| 				break;
 | |
| 			case WSC_MSG_M3:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M3);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M3), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M3");
 | |
| 				break;
 | |
| 			case WSC_MSG_M4:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M4);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M4), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M4");
 | |
| 				break;
 | |
| 			case WSC_MSG_M5:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M5);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M5), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M5");
 | |
| 				break;
 | |
| 			case WSC_MSG_M6:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M6);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M6), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M6");
 | |
| 				break;
 | |
| 			case WSC_MSG_M7:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M7);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M7), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M7");
 | |
| 				break;
 | |
| 			case WSC_MSG_M8:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WPS_M8);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(WPS_M8), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, WPS M8");
 | |
| 				break;
 | |
| 			default:
 | |
| 				EAP_PRINT("EAP Packet, WPS MSG TYPE %d", *msg);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	} else if (eap_wsc->opcode == WSC_OPCODE_START) {
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WSC_START);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(WSC_START), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAP Packet, WSC Start");
 | |
| 	} else if (eap_wsc->opcode == WSC_OPCODE_DONE) {
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_WSC_DONE);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(WSC_DONE), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAP Packet, WSC Done");
 | |
| 	} else {
 | |
| 		EAP_PRINT("EAP Packet, WSC opcode=%d", eap_wsc->opcode);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!pktdata) {
 | |
| 		DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 
 | |
| 	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) {
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_REQID);
 | |
| #endif
 | |
| 				DHD_STATLOG_DATA(dhd, ST(EAP_REQ_IDENTITY), ifidx, tx, cond);
 | |
| 				EAP_PRINT("EAP Packet, Request, Identity");
 | |
| 			} else {
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 				wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_RSPID);
 | |
| #endif
 | |
| 				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:
 | |
| 			EAP_PRINT("EAP Packet, EAP TYPE %d", eap_hdr->type);
 | |
| 			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");
 | |
| 	} else {
 | |
| 		EAP_PRINT("EAP Packet, EAP CODE %d", eap_hdr->code);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!pktdata) {
 | |
| 		DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 
 | |
| 	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_key->type != EAPOL_WPA_KEY) {
 | |
| 		EAP_PRINT_OTHER("NON EAPOL_WPA2_KEY %d", eap_key->type);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case EAPOL_4WAY_M1:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_4WAY_M1);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(EAPOL_M1), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAPOL Packet, 4-way handshake, M1");
 | |
| 		break;
 | |
| 	case EAPOL_4WAY_M2:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_4WAY_M2);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(EAPOL_M2), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAPOL Packet, 4-way handshake, M2");
 | |
| 		break;
 | |
| 	case EAPOL_4WAY_M3:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_4WAY_M3);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(EAPOL_M3), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAPOL Packet, 4-way handshake, M3");
 | |
| 		break;
 | |
| 	case EAPOL_4WAY_M4:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_4WAY_M4);
 | |
| #endif
 | |
| 		DHD_STATLOG_DATA(dhd, ST(EAPOL_M4), ifidx, tx, cond);
 | |
| 		EAP_PRINT("EAPOL Packet, 4-way handshake, M4");
 | |
| 		break;
 | |
| 	case EAPOL_GROUPKEY_M1:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_GROUPKEY_M1);
 | |
| #endif
 | |
| 		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:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 		wl_ext_update_conn_state(dhd, ifidx, CONN_STATE_GROUPKEY_M2);
 | |
| #endif
 | |
| 		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 type=%d", type);
 | |
| 		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;
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!pktdata) {
 | |
| 		DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 
 | |
| 	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;
 | |
| }
 | |
| 
 | |
| #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
 | |
| 
 | |
| 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;
 | |
| static const uint8 bootp_magic_cookie[4] = { 99, 130, 83, 99 };
 | |
| 
 | |
| #ifdef DHD_DHCP_DUMP
 | |
| #if defined(DHD_DHCP_DUMP) || defined(WL_CFGVENDOR_CUST_ADVLOG)
 | |
| #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
 | |
| #endif /* DHD_DHCP_DUMP || WL_CFGVENDOR_CUST_ADVLOG */
 | |
| 
 | |
| #define DHCP_PRINT(str) \
 | |
| 	do { \
 | |
| 		if (tx) { \
 | |
| 			DHD_PKTDUMP(("[%s] " str " %8s[%8s] [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \
 | |
| 				ifname, typestr, opstr, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 				TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP(("[%s] " str " %8s[%8s] [RX] : %s(%s) %s %s(%s)\n", \
 | |
| 				ifname, typestr, opstr, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf)); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define MAX_DHCP_OPS_STR 3
 | |
| #define MAX_DHCP_TYPES_STR 9
 | |
| 
 | |
| 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];
 | |
| 	struct ipv4_hdr *iph = &b->iph;
 | |
| 	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;
 | |
| 	char sabuf[20]="", dabuf[20]="";
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!(dump_msg_level & DUMP_DHCP_VAL))
 | |
| 		return;
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 
 | |
| 	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(("[%s] "str " [TX] : %s(%s) %s %s(%s) SEQNUM=%d" \
 | |
| 				TXFATE_FMT"\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, seqnum, \
 | |
| 				TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] "str " [RX] : %s(%s) %s %s(%s) SEQNUM=%d\n", \
 | |
| 				ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, seqnum)); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define ICMP_PRINT(str) \
 | |
| 	do { \
 | |
| 		if (tx) { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] "str " [TX] : %s(%s) %s %s(%s) TYPE=%d, CODE=%d" \
 | |
| 				TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, type, code, \
 | |
| 				TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] "str " [RX] : %s(%s) %s %s(%s) TYPE=%d," \
 | |
| 				" CODE=%d\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, 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 ipv4_hdr *iph = (struct ipv4_hdr *)pkt;
 | |
| 	struct bcmicmp_hdr *icmph;
 | |
| 	char *ifname;
 | |
| 	bool cond;
 | |
| 	uint16 seqnum, type, code;
 | |
| 	char sabuf[20]="", dabuf[20]="";
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!(dump_msg_level & DUMP_ICMP_VAL))
 | |
| 		return;
 | |
| 
 | |
| 	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;
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 	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(("[%s] "str " [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \
 | |
| 					ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP_MEM(("[%s] "str " [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \
 | |
| 					ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] "str " [RX] : %s(%s) %s %s(%s)\n", \
 | |
| 				ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf)); \
 | |
| 		} \
 | |
| 	} while (0) \
 | |
| 
 | |
| #define ARP_PRINT_OTHER(str) \
 | |
| 	do { \
 | |
| 		if (tx) { \
 | |
| 			if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
 | |
| 				DHD_PKTDUMP(("[%s] "str " [TX] : %s(%s) %s %s(%s) op_code=%d" \
 | |
| 					TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, opcode, \
 | |
| 					TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP_MEM(("[%s] "str " [TX] : %s(%s) %s %s(%s) op_code=%d" \
 | |
| 				TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, opcode, \
 | |
| 				TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] "str " [RX] : %s(%s) %s %s(%s) op_code=%d\n", \
 | |
| 				ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, 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;
 | |
| 	char sabuf[20]="", dabuf[20]="";
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!(dump_msg_level & DUMP_ARP_VAL))
 | |
| 		return;
 | |
| 
 | |
| 	ifname = dhd_ifname(dhdp, ifidx);
 | |
| 	opcode = ntoh16(arph->oper);
 | |
| 	cond = (tx && pktfate) ? FALSE : TRUE;
 | |
| 	dump_enabled = dhd_dump_pkt_enabled(dhdp);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)arph->src_ip, sabuf);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)arph->dst_ip, dabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)arph->dst_eth, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)arph->src_eth, seabuf);
 | |
| 	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(("[%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s" \
 | |
| 					TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 					id, DNSOPCODE(opcode), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP_MEM(("[%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s" \
 | |
| 					TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 					id, DNSOPCODE(opcode), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] " str " [RX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s\n", \
 | |
| 				ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, tx?"->":"<-", \
 | |
| 				tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode))); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #define DNS_RESP_PRINT(str) \
 | |
| 	do { \
 | |
| 		if (tx) { \
 | |
| 			if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \
 | |
| 				DHD_PKTDUMP(("[%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d" \
 | |
| 					TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode), \
 | |
| 					GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} else { \
 | |
| 				DHD_PKTDUMP_MEM(("[%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d" \
 | |
| 					TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 					tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode), \
 | |
| 					GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \
 | |
| 			} \
 | |
| 		} else { \
 | |
| 			DHD_PKTDUMP_MEM(("[%s] " str " [RX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d\n", \
 | |
| 				ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \
 | |
| 				tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \
 | |
| 				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];
 | |
| 	struct ipv4_hdr *iph = &dnsh->iph;
 | |
| 	uint16 flags, opcode, id;
 | |
| 	char *ifname;
 | |
| 	bool cond, dump_enabled;
 | |
| 	char sabuf[20]="", dabuf[20]="";
 | |
| 	char seabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 	char deabuf[ETHER_ADDR_STR_LEN]="";
 | |
| 
 | |
| 	if (!(dump_msg_level & DUMP_DNS_VAL))
 | |
| 		return;
 | |
| 
 | |
| 	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);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf);
 | |
| 	bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf);
 | |
| 	bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf);
 | |
| 	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_TRX_DUMP
 | |
| void
 | |
| dhd_trx_pkt_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen, bool tx)
 | |
| {
 | |
| 	struct ether_header *eh;
 | |
| 	uint16 protocol;
 | |
| 	char *pkttype = "UNKNOWN";
 | |
| 
 | |
| 	if (!(dump_msg_level & DUMP_TRX_VAL))
 | |
| 		return;
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	if (protocol != ETHER_TYPE_BRCM) {
 | |
| 		if (pktdata[0] == 0xFF) {
 | |
| 			DHD_PKTDUMP(("[%s] %s BROADCAST DUMP - %s\n",
 | |
| 				dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype));
 | |
| 		} else if (pktdata[0] & 1) {
 | |
| 			DHD_PKTDUMP(("[%s] %s MULTICAST DUMP " MACDBG " - %s\n",
 | |
| 				dhd_ifname(dhdp, ifidx), tx?"TX":"RX", MAC2STRDBG(pktdata), pkttype));
 | |
| 		} else {
 | |
| 			DHD_PKTDUMP(("[%s] %s DUMP - %s\n",
 | |
| 				dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype));
 | |
| 		}
 | |
| #ifdef DHD_RX_FULL_DUMP
 | |
| 		prhex("Data", pktdata, pktlen);
 | |
| #endif /* DHD_RX_FULL_DUMP */
 | |
| 	}
 | |
| 	else {
 | |
| 		DHD_PKTDUMP(("[%s] %s DUMP - %s\n",
 | |
| 			dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype));
 | |
| 	}
 | |
| }
 | |
| #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 */
 | |
| 
 | |
| #ifdef BCMPCIE
 | |
| static bool
 | |
| dhd_is_eapol_pkt(dhd_pub_t *dhd, uint8 *pktdata, uint32 pktlen)
 | |
| {
 | |
| 	eapol_header_t *eapol_hdr = (eapol_header_t *)pktdata;
 | |
| 
 | |
| 	eapol_hdr = (eapol_header_t *)pktdata;
 | |
| 
 | |
| 	if (eapol_hdr->type == EAP_PACKET) {
 | |
| 		return TRUE;
 | |
| 	} else if (eapol_hdr->type == EAPOL_START) {
 | |
| 		return TRUE;
 | |
| 	} else if (eapol_hdr->type == EAPOL_KEY) {
 | |
| 		return TRUE;
 | |
| 	} else {
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| dhd_is_arp_pkt(dhd_pub_t *dhdp, uint8 *pktdata)
 | |
| {
 | |
| 	uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
 | |
| 	struct bcmarp *arph = (struct bcmarp *)pkt;
 | |
| 	uint16 opcode;
 | |
| 
 | |
| 	/* validation check */
 | |
| 	if (arph->htype != hton16(HTYPE_ETHERNET) ||
 | |
| 		arph->hlen != ETHER_ADDR_LEN ||
 | |
| 		arph->plen != 4) {
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 
 | |
| 	opcode = ntoh16(arph->oper);
 | |
| 	if (opcode == ARP_OPC_REQUEST) {
 | |
| 		return TRUE;
 | |
| 	} else if (opcode == ARP_OPC_REPLY) {
 | |
| 		return TRUE;
 | |
| 	} else {
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| dhd_is_dhcp_pkt(dhd_pub_t *dhdp, uint8 *pktdata)
 | |
| {
 | |
| 	bootp_fmt_t *b = (bootp_fmt_t *)&pktdata[ETHER_HDR_LEN];
 | |
| 	struct ipv4_hdr *iph = &b->iph;
 | |
| 	uint8 *ptr, *opt, *end = (uint8 *) b + ntohs(b->iph.tot_len);
 | |
| 	int len, opt_len;
 | |
| 
 | |
| 	/* check IP header */
 | |
| 	if ((IPV4_HLEN(iph) < IPV4_HLEN_MIN) ||
 | |
| 		IP_VER(iph) != IP_VER_4 ||
 | |
| 		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;
 | |
| 	}
 | |
| 
 | |
| 	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]) {
 | |
| 					return TRUE;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| dhd_is_icmp_pkt(dhd_pub_t *dhd, uint8 *pktdata, uint32 pktlen)
 | |
| {
 | |
| 	uint8 *pkt;
 | |
| 	struct ipv4_hdr *iph;
 | |
| 
 | |
| 	pkt = (uint8 *)&pktdata[ETHER_HDR_LEN];
 | |
| 	iph = (struct ipv4_hdr *)pkt;
 | |
| 
 | |
| 	/* check IP header */
 | |
| 	if ((IPV4_HLEN(iph) < IPV4_HLEN_MIN) ||
 | |
| 		IP_VER(iph) != IP_VER_4 ||
 | |
| 		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_match_pkt_type(dhd_pub_t *dhd, uint8 *pktdata, uint32 pktlen)
 | |
| {
 | |
| 	struct ether_header *eh;
 | |
| 	uint16 ether_type;
 | |
| 	bool match = FALSE;
 | |
| 
 | |
| 	if (!pktdata || pktlen < ETHER_HDR_LEN) {
 | |
| 		return match;
 | |
| 	}
 | |
| 
 | |
| 	eh = (struct ether_header *)pktdata;
 | |
| 	ether_type = ntoh16(eh->ether_type);
 | |
| 	if ((dhd->conf->enq_hdr_pkt & ENQ_PKT_TYPE_EAPOL) &&
 | |
| 			ether_type == ETHER_TYPE_802_1X) {
 | |
| 		match = dhd_is_eapol_pkt(dhd, pktdata, pktlen);
 | |
| 	}
 | |
| 	else if ((dhd->conf->enq_hdr_pkt & ENQ_PKT_TYPE_ARP) &&
 | |
| 			ntoh16(eh->ether_type) == ETHER_TYPE_ARP) {
 | |
| 		match = dhd_is_arp_pkt(dhd, pktdata);
 | |
| 	}
 | |
| 	else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) {
 | |
| 		if (dhd->conf->enq_hdr_pkt & ENQ_PKT_TYPE_ICMP)
 | |
| 			match = dhd_is_icmp_pkt(dhd, pktdata, pktlen);
 | |
| 		if (!match && dhd->conf->enq_hdr_pkt & ENQ_PKT_TYPE_DHCP)
 | |
| 			match = dhd_is_dhcp_pkt(dhd, pktdata);
 | |
| 	}
 | |
| 
 | |
| 	return match;
 | |
| }
 | |
| #endif /* BCMPCIE */
 |