Commit | Line | Data |
---|---|---|
1b4a7c03 LJ |
1 | /* |
2 | * Packet dump helper functions | |
3 | * | |
4 | * Copyright (C) 2020, Broadcom. | |
5 | * | |
6 | * Unless you and Broadcom execute a separate written software license | |
7 | * agreement governing use of this software, this software is licensed to you | |
8 | * under the terms of the GNU General Public License version 2 (the "GPL"), | |
9 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the | |
10 | * following added to such license: | |
11 | * | |
12 | * As a special exception, the copyright holders of this software give you | |
13 | * permission to link this software with independent modules, and to copy and | |
14 | * distribute the resulting executable under terms of your choice, provided that | |
15 | * you also meet, for each linked independent module, the terms and conditions of | |
16 | * the license of that module. An independent module is a module which is not | |
17 | * derived from this software. The special exception does not apply to any | |
18 | * modifications of the software. | |
19 | * | |
20 | * | |
21 | * <<Broadcom-WL-IPTag/Open:>> | |
22 | * | |
23 | * $Id$ | |
24 | */ | |
25 | ||
26 | #include <typedefs.h> | |
27 | #include <ethernet.h> | |
28 | #include <bcmutils.h> | |
29 | #include <bcmevent.h> | |
30 | #include <bcmendian.h> | |
31 | #include <bcmtlv.h> | |
32 | #include <dngl_stats.h> | |
33 | #include <dhd.h> | |
34 | #include <dhd_dbg.h> | |
35 | #include <bcmip.h> | |
36 | #include <bcmudp.h> | |
37 | #include <bcmdhcp.h> | |
38 | #include <bcmarp.h> | |
39 | #include <bcmicmp.h> | |
40 | #include <dhd_linux_pktdump.h> | |
41 | #include <dhd_config.h> | |
42 | ||
43 | #define DHD_PKTDUMP(arg) printk arg | |
44 | #define DHD_PKTDUMP_MEM(arg) printk arg | |
45 | #define PACKED_STRUCT __attribute__ ((packed)) | |
46 | ||
47 | #define EAPOL_HDR_LEN 4 | |
48 | ||
49 | /* EAPOL types */ | |
50 | #define EAP_PACKET 0 | |
51 | #define EAPOL_START 1 | |
52 | #define EAPOL_LOGOFF 2 | |
53 | #define EAPOL_KEY 3 | |
54 | #define EAPOL_ASF 4 | |
55 | ||
56 | /* EAPOL-Key types */ | |
57 | #define EAPOL_RC4_KEY 1 | |
58 | #define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */ | |
59 | #define EAPOL_WPA_KEY 254 /* WPA */ | |
60 | ||
61 | /* EAPOL-Key header field size */ | |
62 | #define AKW_BLOCK_LEN 8 | |
63 | #define WPA_KEY_REPLAY_LEN 8 | |
64 | #define WPA_KEY_NONCE_LEN 32 | |
65 | #define WPA_KEY_IV_LEN 16 | |
66 | #define WPA_KEY_RSC_LEN 8 | |
67 | #define WPA_KEY_ID_LEN 8 | |
68 | #define WPA_KEY_MIC_LEN 16 | |
69 | #define WPA_MAX_KEY_SIZE 32 | |
70 | #define WPA_KEY_DATA_LEN (WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN) | |
71 | ||
72 | /* Key information bit */ | |
73 | #define KEYINFO_TYPE_MASK (1 << 3) | |
74 | #define KEYINFO_INSTALL_MASK (1 << 6) | |
75 | #define KEYINFO_KEYACK_MASK (1 << 7) | |
76 | #define KEYINFO_KEYMIC_MASK (1 << 8) | |
77 | #define KEYINFO_SECURE_MASK (1 << 9) | |
78 | #define KEYINFO_ERROR_MASK (1 << 10) | |
79 | #define KEYINFO_REQ_MASK (1 << 11) | |
80 | ||
81 | /* EAP Code */ | |
82 | #define EAP_CODE_REQUEST 1 /* Request */ | |
83 | #define EAP_CODE_RESPONSE 2 /* Response */ | |
84 | #define EAP_CODE_SUCCESS 3 /* Success */ | |
85 | #define EAP_CODE_FAILURE 4 /* Failure */ | |
86 | ||
87 | /* EAP Type */ | |
88 | #define EAP_TYPE_RSVD 0 /* Reserved */ | |
89 | #define EAP_TYPE_IDENT 1 /* Identify */ | |
90 | #define EAP_TYPE_NOTI 2 /* Notification */ | |
91 | #define EAP_TYPE_TLS 13 /* EAP-TLS */ | |
92 | #define EAP_TYPE_LEAP 17 /* Cisco-LEAP */ | |
93 | #define EAP_TYPE_TTLS 21 /* EAP-TTLS */ | |
94 | #define EAP_TYPE_AKA 23 /* EAP-AKA */ | |
95 | #define EAP_TYPE_PEAP 25 /* EAP-PEAP */ | |
96 | #define EAP_TYPE_FAST 43 /* EAP-FAST */ | |
97 | #define EAP_TYPE_PSK 47 /* EAP-PSK */ | |
98 | #define EAP_TYPE_AKAP 50 /* EAP-AKA' */ | |
99 | #define EAP_TYPE_EXP 254 /* Reserved for Expended Type */ | |
100 | ||
101 | /* WSC */ | |
102 | #define EAP_HDR_LEN 5 | |
103 | #define EAP_WSC_NONCE_OFFSET 10 | |
104 | #define EAP_WSC_DATA_OFFSET (OFFSETOF(eap_wsc_fmt_t, data)) | |
105 | #define EAP_WSC_MIN_DATA_LEN ((EAP_HDR_LEN) + (EAP_WSC_DATA_OFFSET)) | |
106 | #define WFA_VID "\x00\x37\x2A" /* WFA SMI code */ | |
107 | #define WFA_VID_LEN 3 /* WFA VID length */ | |
108 | #define WFA_VTYPE 1u /* WFA Vendor type */ | |
109 | ||
110 | /* WSC opcode */ | |
111 | #define WSC_OPCODE_UPNP 0 | |
112 | #define WSC_OPCODE_START 1 | |
113 | #define WSC_OPCODE_ACK 2 | |
114 | #define WSC_OPCODE_NACK 3 | |
115 | #define WSC_OPCODE_MSG 4 | |
116 | #define WSC_OPCODE_DONE 5 | |
117 | #define WSC_OPCODE_FRAG_ACK 6 | |
118 | ||
119 | /* WSC flag */ | |
120 | #define WSC_FLAG_MF 1 /* more fragements */ | |
121 | #define WSC_FLAG_LF 2 /* length field */ | |
122 | ||
123 | /* WSC message code */ | |
124 | #define WSC_ATTR_MSG 0x1022 | |
125 | #define WSC_MSG_M1 0x04 | |
126 | #define WSC_MSG_M2 0x05 | |
127 | #define WSC_MSG_M3 0x07 | |
128 | #define WSC_MSG_M4 0x08 | |
129 | #define WSC_MSG_M5 0x09 | |
130 | #define WSC_MSG_M6 0x0A | |
131 | #define WSC_MSG_M7 0x0B | |
132 | #define WSC_MSG_M8 0x0C | |
133 | ||
134 | /* Debug prints */ | |
135 | typedef enum pkt_cnt_type { | |
136 | PKT_CNT_TYPE_INVALID = 0, | |
137 | PKT_CNT_TYPE_ARP = 1, | |
138 | PKT_CNT_TYPE_DNS = 2, | |
139 | PKT_CNT_TYPE_MAX = 3 | |
140 | } pkt_cnt_type_t; | |
141 | ||
142 | typedef struct pkt_cnt { | |
143 | uint32 tx_cnt; | |
144 | uint32 tx_err_cnt; | |
145 | uint32 rx_cnt; | |
146 | } pkt_cnt_t; | |
147 | ||
148 | typedef struct pkt_cnt_log { | |
149 | bool enabled; | |
150 | uint16 reason; | |
151 | timer_list_compat_t pktcnt_timer; | |
152 | pkt_cnt_t arp_cnt; | |
153 | pkt_cnt_t dns_cnt; | |
154 | } pkt_cnts_log_t; | |
155 | ||
156 | #define PKT_CNT_TIMER_INTERNVAL_MS 5000 /* packet count timeout(ms) */ | |
157 | #define PKT_CNT_RSN_VALID(rsn) \ | |
158 | (((rsn) > (PKT_CNT_RSN_INVALID)) && ((rsn) < (PKT_CNT_RSN_MAX))) | |
159 | ||
160 | #ifdef DHD_PKTDUMP_ROAM | |
161 | static const char pkt_cnt_msg[][20] = { | |
162 | "INVALID", | |
163 | "ROAM_SUCCESS", | |
164 | "GROUP_KEY_UPDATE", | |
165 | "CONNECT_SUCCESS", | |
166 | "INVALID" | |
167 | }; | |
168 | #endif | |
169 | ||
170 | static const char tx_pktfate[][30] = { | |
171 | "TX_PKT_FATE_ACKED", /* 0: WLFC_CTL_PKTFLAG_DISCARD */ | |
172 | "TX_PKT_FATE_FW_QUEUED", /* 1: WLFC_CTL_PKTFLAG_D11SUPPRESS */ | |
173 | "TX_PKT_FATE_FW_QUEUED", /* 2: WLFC_CTL_PKTFLAG_WLSUPPRESS */ | |
174 | "TX_PKT_FATE_FW_DROP_INVALID", /* 3: WLFC_CTL_PKTFLAG_TOSSED_BYWLC */ | |
175 | "TX_PKT_FATE_SENT", /* 4: WLFC_CTL_PKTFLAG_DISCARD_NOACK */ | |
176 | "TX_PKT_FATE_FW_DROP_OTHER", /* 5: WLFC_CTL_PKTFLAG_SUPPRESS_ACKED */ | |
177 | "TX_PKT_FATE_FW_DROP_EXPTIME", /* 6: WLFC_CTL_PKTFLAG_EXPIRED */ | |
178 | "TX_PKT_FATE_FW_DROP_OTHER", /* 7: WLFC_CTL_PKTFLAG_DROPPED */ | |
179 | "TX_PKT_FATE_FW_PKT_FREE", /* 8: WLFC_CTL_PKTFLAG_MKTFREE */ | |
180 | }; | |
181 | ||
182 | #define DBGREPLAY " Replay Counter: %02x%02x%02x%02x%02x%02x%02x%02x" | |
183 | #define REPLAY_FMT(key) ((const eapol_key_hdr_t *)(key))->replay[0], \ | |
184 | ((const eapol_key_hdr_t *)(key))->replay[1], \ | |
185 | ((const eapol_key_hdr_t *)(key))->replay[2], \ | |
186 | ((const eapol_key_hdr_t *)(key))->replay[3], \ | |
187 | ((const eapol_key_hdr_t *)(key))->replay[4], \ | |
188 | ((const eapol_key_hdr_t *)(key))->replay[5], \ | |
189 | ((const eapol_key_hdr_t *)(key))->replay[6], \ | |
190 | ((const eapol_key_hdr_t *)(key))->replay[7] | |
191 | #define TXFATE_FMT " TX_PKTHASH:0x%X TX_PKT_FATE:%s" | |
192 | #define TX_PKTHASH(pkthash) ((pkthash) ? (*pkthash) : (0)) | |
193 | #define TX_FATE_STR(fate) (((*fate) <= (WLFC_CTL_PKTFLAG_MKTFREE)) ? \ | |
194 | (tx_pktfate[(*fate)]) : "TX_PKT_FATE_FW_DROP_OTHER") | |
195 | #define TX_FATE(fate) ((fate) ? (TX_FATE_STR(fate)) : "N/A") | |
196 | #define TX_FATE_ACKED(fate) ((fate) ? ((*fate) == (WLFC_CTL_PKTFLAG_DISCARD)) : (0)) | |
197 | ||
198 | #define EAP_PRINT(x, args...) \ | |
199 | do { \ | |
200 | if (dump_msg_level & DUMP_EAPOL_VAL) { \ | |
201 | if (tx) { \ | |
202 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [TX] : (%s) %s (%s)"TXFATE_FMT"\n", \ | |
203 | ifname, ## args, \ | |
204 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
205 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
206 | } else { \ | |
207 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [RX] : (%s) %s (%s)\n", \ | |
208 | ifname, ## args, \ | |
209 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf)); \ | |
210 | } \ | |
211 | } \ | |
212 | } while (0) | |
213 | ||
214 | #define EAP_PRINT_REPLAY(x, args...) \ | |
215 | do { \ | |
216 | if (dump_msg_level & DUMP_EAPOL_VAL) { \ | |
217 | if (tx) { \ | |
218 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [TX] : (%s) %s (%s)"DBGREPLAY TXFATE_FMT"\n", \ | |
219 | ifname, ## args, \ | |
220 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
221 | REPLAY_FMT(eap_key), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
222 | } else { \ | |
223 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [RX] : (%s) %s (%s)"DBGREPLAY"\n", \ | |
224 | ifname, ## args, \ | |
225 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
226 | REPLAY_FMT(eap_key))); \ | |
227 | } \ | |
228 | } \ | |
229 | } while (0) | |
230 | ||
231 | #define EAP_PRINT_OTHER(x, args...) \ | |
232 | do { \ | |
233 | if (dump_msg_level & DUMP_EAPOL_VAL) { \ | |
234 | if (tx) { \ | |
235 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [TX] : (%s) %s (%s) " \ | |
236 | "ver %d, type %d"TXFATE_FMT"\n", \ | |
237 | ifname, ## args, \ | |
238 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
239 | eapol_hdr->version, eapol_hdr->type, \ | |
240 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
241 | } else { \ | |
242 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [RX] : (%s) %s (%s) " \ | |
243 | "ver %d, type %d\n", \ | |
244 | ifname, ## args, \ | |
245 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
246 | eapol_hdr->version, eapol_hdr->type)); \ | |
247 | } \ | |
248 | } \ | |
249 | } while (0) | |
250 | ||
251 | #define EAP_PRINT_OTHER_4WAY(x, args...) \ | |
252 | do { \ | |
253 | if (dump_msg_level & DUMP_EAPOL_VAL) { \ | |
254 | if (tx) { \ | |
255 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [TX] : (%s) %s (%s) " \ | |
256 | "ver %d type %d keytype %d keyinfo 0x%02X"TXFATE_FMT"\n", \ | |
257 | ifname, ## args, \ | |
258 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
259 | eapol_hdr->version, eapol_hdr->type, eap_key->type, \ | |
260 | (uint32)hton16(eap_key->key_info), \ | |
261 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
262 | } else { \ | |
263 | DHD_PKTDUMP(("[dhd-%s] 802_1X " x " [RX] : (%s) %s (%s) " \ | |
264 | "ver %d type %d keytype %d keyinfo 0x%02X\n", \ | |
265 | ifname, ## args, \ | |
266 | tx?seabuf:deabuf, tx?"->":"<-", tx?deabuf:seabuf, \ | |
267 | eapol_hdr->version, eapol_hdr->type, eap_key->type, \ | |
268 | (uint32)hton16(eap_key->key_info))); \ | |
269 | } \ | |
270 | } \ | |
271 | } while (0) | |
272 | ||
273 | #define UDP_PORT_DNS 53 /* UDP DNS port */ | |
274 | ||
275 | /* EAPOL header */ | |
276 | typedef struct eapol_header { | |
277 | struct ether_header eth; /* 802.3/Ethernet header */ | |
278 | uint8 version; /* EAPOL protocol version */ | |
279 | uint8 type; /* EAPOL type */ | |
280 | uint16 length; /* Length of body */ | |
281 | uint8 body[1]; /* Body (optional) */ | |
282 | } PACKED_STRUCT eapol_header_t; | |
283 | ||
284 | /* EAP header */ | |
285 | typedef struct eap_header_fmt { | |
286 | uint8 code; | |
287 | uint8 id; | |
288 | uint16 len; | |
289 | uint8 type; | |
290 | uint8 data[1]; | |
291 | } PACKED_STRUCT eap_header_fmt_t; | |
292 | ||
293 | /* WSC EAP format */ | |
294 | typedef struct eap_wsc_fmt { | |
295 | uint8 oui[3]; | |
296 | uint32 ouitype; | |
297 | uint8 opcode; | |
298 | uint8 flags; | |
299 | uint8 data[1]; | |
300 | } PACKED_STRUCT eap_wsc_fmt_t; | |
301 | ||
302 | /* EAPOL-Key */ | |
303 | typedef struct eapol_key_hdr { | |
304 | uint8 type; /* Key Descriptor Type */ | |
305 | uint16 key_info; /* Key Information (unaligned) */ | |
306 | uint16 key_len; /* Key Length (unaligned) */ | |
307 | uint8 replay[WPA_KEY_REPLAY_LEN]; /* Replay Counter */ | |
308 | uint8 nonce[WPA_KEY_NONCE_LEN]; /* Nonce */ | |
309 | uint8 iv[WPA_KEY_IV_LEN]; /* Key IV */ | |
310 | uint8 rsc[WPA_KEY_RSC_LEN]; /* Key RSC */ | |
311 | uint8 id[WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */ | |
312 | uint8 mic[WPA_KEY_MIC_LEN]; /* Key MIC */ | |
313 | uint16 data_len; /* Key Data Length */ | |
314 | uint8 data[WPA_KEY_DATA_LEN]; /* Key data */ | |
315 | } PACKED_STRUCT eapol_key_hdr_t; | |
316 | ||
317 | typedef struct hdr_fmt { | |
318 | struct ipv4_hdr iph; | |
319 | struct bcmudp_hdr udph; | |
320 | } PACKED_STRUCT hdr_fmt_t; | |
321 | ||
322 | msg_eapol_t | |
323 | dhd_is_4way_msg(uint8 *pktdata) | |
324 | { | |
325 | eapol_header_t *eapol_hdr; | |
326 | eapol_key_hdr_t *eap_key; | |
327 | msg_eapol_t type = EAPOL_OTHER; | |
328 | bool pair, ack, mic, kerr, req, sec, install; | |
329 | uint16 key_info; | |
330 | ||
331 | if (!pktdata) { | |
332 | DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__)); | |
333 | return type; | |
334 | } | |
335 | ||
336 | eapol_hdr = (eapol_header_t *)pktdata; | |
337 | eap_key = (eapol_key_hdr_t *)(eapol_hdr->body); | |
338 | if (eap_key->type != EAPOL_WPA2_KEY) { | |
339 | return type; | |
340 | } | |
341 | ||
342 | key_info = hton16(eap_key->key_info); | |
343 | pair = !!(key_info & KEYINFO_TYPE_MASK); | |
344 | ack = !!(key_info & KEYINFO_KEYACK_MASK); | |
345 | mic = !!(key_info & KEYINFO_KEYMIC_MASK); | |
346 | kerr = !!(key_info & KEYINFO_ERROR_MASK); | |
347 | req = !!(key_info & KEYINFO_REQ_MASK); | |
348 | sec = !!(key_info & KEYINFO_SECURE_MASK); | |
349 | install = !!(key_info & KEYINFO_INSTALL_MASK); | |
350 | ||
351 | if (pair && !install && ack && !mic && !sec && !kerr && !req) { | |
352 | type = EAPOL_4WAY_M1; | |
353 | } else if (pair && !install && !ack && mic && !sec && !kerr && !req) { | |
354 | type = EAPOL_4WAY_M2; | |
355 | } else if (pair && ack && mic && sec && !kerr && !req) { | |
356 | type = EAPOL_4WAY_M3; | |
357 | } else if (pair && !install && !ack && mic && sec && !req && !kerr) { | |
358 | type = EAPOL_4WAY_M4; | |
359 | } else if (!pair && !install && ack && mic && sec && !req && !kerr) { | |
360 | type = EAPOL_GROUPKEY_M1; | |
361 | } else if (!pair && !install && !ack && mic && sec && !req && !kerr) { | |
362 | type = EAPOL_GROUPKEY_M2; | |
363 | } else { | |
364 | type = EAPOL_OTHER; | |
365 | } | |
366 | ||
367 | return type; | |
368 | } | |
369 | ||
370 | void | |
371 | dhd_dump_pkt(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen, | |
372 | bool tx, uint32 *pkthash, uint16 *pktfate) | |
373 | { | |
374 | struct ether_header *eh; | |
375 | uint16 ether_type; | |
376 | ||
377 | if (!pktdata || pktlen < ETHER_HDR_LEN) { | |
378 | return; | |
379 | } | |
380 | ||
381 | #if defined(BCMPCIE) && defined(DHD_PKT_LOGGING) | |
382 | if (tx && !pkthash && !pktfate) { | |
383 | return; | |
384 | } | |
385 | #endif /* BCMPCIE && DHD_PKT_LOGGING */ | |
386 | ||
387 | eh = (struct ether_header *)pktdata; | |
388 | ether_type = ntoh16(eh->ether_type); | |
389 | if (ether_type == ETHER_TYPE_802_1X) { | |
390 | dhd_dump_eapol_message(dhdp, ifidx, pktdata, pktlen, | |
391 | tx, pkthash, pktfate); | |
392 | } | |
393 | if (ether_type == ETHER_TYPE_IP) { | |
394 | if (dhd_check_dhcp(pktdata)) { | |
395 | dhd_dhcp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate); | |
396 | } else if (dhd_check_icmp(pktdata)) { | |
397 | dhd_icmp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate); | |
398 | } else if (dhd_check_dns(pktdata)) { | |
399 | dhd_dns_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate); | |
400 | } | |
401 | } | |
402 | if (ether_type == ETHER_TYPE_ARP) { | |
403 | if (dhd_check_arp(pktdata)) { | |
404 | dhd_arp_dump(dhdp, ifidx, pktdata, tx, pkthash, pktfate); | |
405 | } | |
406 | } | |
407 | dhd_trx_pkt_dump(dhdp, ifidx, pktdata, pktlen, tx); | |
408 | } | |
409 | ||
410 | #ifdef DHD_PKTDUMP_ROAM | |
411 | static void | |
412 | dhd_dump_pkt_cnts_inc(dhd_pub_t *dhdp, bool tx, uint16 *pktfate, uint16 pkttype) | |
413 | { | |
414 | pkt_cnts_log_t *pktcnts; | |
415 | pkt_cnt_t *cnt; | |
416 | ||
417 | if (!dhdp) { | |
418 | DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); | |
419 | return; | |
420 | } | |
421 | ||
422 | pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
423 | if (!pktcnts) { | |
424 | DHD_ERROR(("%s: pktcnts is NULL\n", __FUNCTION__)); | |
425 | return; | |
426 | } | |
427 | ||
428 | if (!pktcnts->enabled || (tx && !pktfate)) { | |
429 | return; | |
430 | } | |
431 | ||
432 | if (pkttype == PKT_CNT_TYPE_ARP) { | |
433 | cnt = (pkt_cnt_t *)&pktcnts->arp_cnt; | |
434 | } else if (pkttype == PKT_CNT_TYPE_DNS) { | |
435 | cnt = (pkt_cnt_t *)&pktcnts->dns_cnt; | |
436 | } else { | |
437 | /* invalid packet type */ | |
438 | return; | |
439 | } | |
440 | ||
441 | if (tx) { | |
442 | TX_FATE_ACKED(pktfate) ? cnt->tx_cnt++ : cnt->tx_err_cnt++; | |
443 | } else { | |
444 | cnt->rx_cnt++; | |
445 | } | |
446 | } | |
447 | ||
448 | static void | |
449 | dhd_dump_pkt_timer(unsigned long data) | |
450 | { | |
451 | dhd_pub_t *dhdp = (dhd_pub_t *)data; | |
452 | pkt_cnts_log_t *pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
453 | ||
454 | pktcnts->enabled = FALSE; | |
455 | ||
456 | /* print out the packet counter value */ | |
457 | DHD_PKTDUMP(("============= PACKET COUNT SUMMARY ============\n")); | |
458 | DHD_PKTDUMP(("- Reason: %s\n", pkt_cnt_msg[pktcnts->reason])); | |
459 | DHD_PKTDUMP(("- Duration: %d msec(s)\n", PKT_CNT_TIMER_INTERNVAL_MS)); | |
460 | DHD_PKTDUMP(("- ARP PACKETS: tx_success:%d tx_fail:%d rx_cnt:%d\n", | |
461 | pktcnts->arp_cnt.tx_cnt, pktcnts->arp_cnt.tx_err_cnt, | |
462 | pktcnts->arp_cnt.rx_cnt)); | |
463 | DHD_PKTDUMP(("- DNS PACKETS: tx_success:%d tx_fail:%d rx_cnt:%d\n", | |
464 | pktcnts->dns_cnt.tx_cnt, pktcnts->dns_cnt.tx_err_cnt, | |
465 | pktcnts->dns_cnt.rx_cnt)); | |
466 | DHD_PKTDUMP(("============= END OF COUNT SUMMARY ============\n")); | |
467 | } | |
468 | ||
469 | void | |
470 | dhd_dump_mod_pkt_timer(dhd_pub_t *dhdp, uint16 rsn) | |
471 | { | |
472 | pkt_cnts_log_t *pktcnts; | |
473 | ||
474 | if (!dhdp || !dhdp->pktcnts) { | |
475 | DHD_ERROR(("%s: dhdp or dhdp->pktcnts is NULL\n", | |
476 | __FUNCTION__)); | |
477 | return; | |
478 | } | |
479 | ||
480 | if (!PKT_CNT_RSN_VALID(rsn)) { | |
481 | DHD_ERROR(("%s: invalid reason code %d\n", | |
482 | __FUNCTION__, rsn)); | |
483 | return; | |
484 | } | |
485 | ||
486 | pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
487 | if (timer_pending(&pktcnts->pktcnt_timer)) { | |
488 | del_timer_sync(&pktcnts->pktcnt_timer); | |
489 | } | |
490 | ||
491 | bzero(&pktcnts->arp_cnt, sizeof(pkt_cnt_t)); | |
492 | bzero(&pktcnts->dns_cnt, sizeof(pkt_cnt_t)); | |
493 | pktcnts->reason = rsn; | |
494 | pktcnts->enabled = TRUE; | |
495 | mod_timer(&pktcnts->pktcnt_timer, | |
496 | jiffies + msecs_to_jiffies(PKT_CNT_TIMER_INTERNVAL_MS)); | |
497 | DHD_PKTDUMP(("%s: Arm the pktcnt timer. reason=%d\n", | |
498 | __FUNCTION__, rsn)); | |
499 | } | |
500 | ||
501 | void | |
502 | dhd_dump_pkt_init(dhd_pub_t *dhdp) | |
503 | { | |
504 | pkt_cnts_log_t *pktcnts; | |
505 | ||
506 | if (!dhdp) { | |
507 | DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); | |
508 | return; | |
509 | } | |
510 | ||
511 | pktcnts = (pkt_cnts_log_t *)MALLOCZ(dhdp->osh, sizeof(pkt_cnts_log_t)); | |
512 | if (!pktcnts) { | |
513 | DHD_ERROR(("%s: failed to allocate memory for pktcnts\n", | |
514 | __FUNCTION__)); | |
515 | return; | |
516 | } | |
517 | ||
518 | /* init timers */ | |
519 | init_timer_compat(&pktcnts->pktcnt_timer, dhd_dump_pkt_timer, dhdp); | |
520 | dhdp->pktcnts = pktcnts; | |
521 | } | |
522 | ||
523 | void | |
524 | dhd_dump_pkt_deinit(dhd_pub_t *dhdp) | |
525 | { | |
526 | pkt_cnts_log_t *pktcnts; | |
527 | ||
528 | if (!dhdp || !dhdp->pktcnts) { | |
529 | DHD_ERROR(("%s: dhdp or pktcnts is NULL\n", __FUNCTION__)); | |
530 | return; | |
531 | } | |
532 | ||
533 | pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
534 | pktcnts->enabled = FALSE; | |
535 | del_timer_sync(&pktcnts->pktcnt_timer); | |
536 | MFREE(dhdp->osh, dhdp->pktcnts, sizeof(pkt_cnts_log_t)); | |
537 | dhdp->pktcnts = NULL; | |
538 | } | |
539 | ||
540 | void | |
541 | dhd_dump_pkt_clear(dhd_pub_t *dhdp) | |
542 | { | |
543 | pkt_cnts_log_t *pktcnts; | |
544 | ||
545 | if (!dhdp || !dhdp->pktcnts) { | |
546 | DHD_ERROR(("%s: dhdp or pktcnts is NULL\n", __FUNCTION__)); | |
547 | return; | |
548 | } | |
549 | ||
550 | pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
551 | pktcnts->enabled = FALSE; | |
552 | del_timer_sync(&pktcnts->pktcnt_timer); | |
553 | pktcnts->reason = 0; | |
554 | bzero(&pktcnts->arp_cnt, sizeof(pkt_cnt_t)); | |
555 | bzero(&pktcnts->dns_cnt, sizeof(pkt_cnt_t)); | |
556 | } | |
557 | ||
558 | bool | |
559 | dhd_dump_pkt_enabled(dhd_pub_t *dhdp) | |
560 | { | |
561 | pkt_cnts_log_t *pktcnts; | |
562 | ||
563 | if (!dhdp || !dhdp->pktcnts) { | |
564 | return FALSE; | |
565 | } | |
566 | ||
567 | pktcnts = (pkt_cnts_log_t *)(dhdp->pktcnts); | |
568 | ||
569 | return pktcnts->enabled; | |
570 | } | |
571 | #else | |
572 | static INLINE void | |
573 | dhd_dump_pkt_cnts_inc(dhd_pub_t *dhdp, bool tx, uint16 *pktfate, uint16 pkttype) { } | |
574 | static INLINE bool | |
575 | dhd_dump_pkt_enabled(dhd_pub_t *dhdp) { return FALSE; } | |
576 | #endif /* DHD_PKTDUMP_ROAM */ | |
577 | ||
578 | #ifdef DHD_8021X_DUMP | |
579 | static void | |
580 | dhd_dump_wsc_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata, | |
581 | uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate) | |
582 | { | |
583 | eapol_header_t *eapol_hdr; | |
584 | eap_header_fmt_t *eap_hdr; | |
585 | eap_wsc_fmt_t *eap_wsc; | |
586 | char *ifname; | |
587 | uint16 eap_len; | |
588 | bool cond; | |
589 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
590 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
591 | ||
592 | if (!pktdata) { | |
593 | DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__)); | |
594 | return; | |
595 | } | |
596 | ||
597 | if (pktlen < (ETHER_HDR_LEN + EAPOL_HDR_LEN)) { | |
598 | DHD_ERROR(("%s: invalid pkt length\n", __FUNCTION__)); | |
599 | return; | |
600 | } | |
601 | ||
602 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
603 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
604 | ||
605 | eapol_hdr = (eapol_header_t *)pktdata; | |
606 | eap_hdr = (eap_header_fmt_t *)(eapol_hdr->body); | |
607 | if (eap_hdr->type != EAP_TYPE_EXP) { | |
608 | return; | |
609 | } | |
610 | ||
611 | eap_len = ntoh16(eap_hdr->len); | |
612 | if (eap_len < EAP_WSC_MIN_DATA_LEN) { | |
613 | return; | |
614 | } | |
615 | ||
616 | eap_wsc = (eap_wsc_fmt_t *)(eap_hdr->data); | |
617 | if (bcmp(eap_wsc->oui, (const uint8 *)WFA_VID, WFA_VID_LEN) || | |
618 | (ntoh32(eap_wsc->ouitype) != WFA_VTYPE)) { | |
619 | return; | |
620 | } | |
621 | ||
622 | if (eap_wsc->flags) { | |
623 | return; | |
624 | } | |
625 | ||
626 | ifname = dhd_ifname(dhd, ifidx); | |
627 | cond = (tx && pktfate) ? FALSE : TRUE; | |
628 | ||
629 | if (eap_wsc->opcode == WSC_OPCODE_MSG) { | |
630 | const uint8 *tlv_buf = (const uint8 *)(eap_wsc->data); | |
631 | const uint8 *msg; | |
632 | uint16 msglen; | |
633 | uint16 wsc_data_len = (uint16)(eap_len - EAP_HDR_LEN - EAP_WSC_DATA_OFFSET); | |
634 | bcm_xtlv_opts_t opt = BCM_XTLV_OPTION_IDBE | BCM_XTLV_OPTION_LENBE; | |
635 | ||
636 | msg = bcm_get_data_from_xtlv_buf(tlv_buf, wsc_data_len, | |
637 | WSC_ATTR_MSG, &msglen, opt); | |
638 | if (msg && msglen) { | |
639 | switch (*msg) { | |
640 | case WSC_MSG_M1: | |
641 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M1; | |
642 | DHD_STATLOG_DATA(dhd, ST(WPS_M1), ifidx, tx, cond); | |
643 | EAP_PRINT("EAP Packet, WPS M1"); | |
644 | break; | |
645 | case WSC_MSG_M2: | |
646 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M2; | |
647 | DHD_STATLOG_DATA(dhd, ST(WPS_M2), ifidx, tx, cond); | |
648 | EAP_PRINT("EAP Packet, WPS M2"); | |
649 | break; | |
650 | case WSC_MSG_M3: | |
651 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M3; | |
652 | DHD_STATLOG_DATA(dhd, ST(WPS_M3), ifidx, tx, cond); | |
653 | EAP_PRINT("EAP Packet, WPS M3"); | |
654 | break; | |
655 | case WSC_MSG_M4: | |
656 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M4; | |
657 | DHD_STATLOG_DATA(dhd, ST(WPS_M4), ifidx, tx, cond); | |
658 | EAP_PRINT("EAP Packet, WPS M4"); | |
659 | break; | |
660 | case WSC_MSG_M5: | |
661 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M5; | |
662 | DHD_STATLOG_DATA(dhd, ST(WPS_M5), ifidx, tx, cond); | |
663 | EAP_PRINT("EAP Packet, WPS M5"); | |
664 | break; | |
665 | case WSC_MSG_M6: | |
666 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M6; | |
667 | DHD_STATLOG_DATA(dhd, ST(WPS_M6), ifidx, tx, cond); | |
668 | EAP_PRINT("EAP Packet, WPS M6"); | |
669 | break; | |
670 | case WSC_MSG_M7: | |
671 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M7; | |
672 | DHD_STATLOG_DATA(dhd, ST(WPS_M7), ifidx, tx, cond); | |
673 | EAP_PRINT("EAP Packet, WPS M7"); | |
674 | break; | |
675 | case WSC_MSG_M8: | |
676 | dhd->conf->eapol_status = EAPOL_STATUS_WPS_M8; | |
677 | DHD_STATLOG_DATA(dhd, ST(WPS_M8), ifidx, tx, cond); | |
678 | EAP_PRINT("EAP Packet, WPS M8"); | |
679 | break; | |
680 | default: | |
681 | break; | |
682 | } | |
683 | } | |
684 | } else if (eap_wsc->opcode == WSC_OPCODE_START) { | |
685 | dhd->conf->eapol_status = EAPOL_STATUS_WSC_START; | |
686 | DHD_STATLOG_DATA(dhd, ST(WSC_START), ifidx, tx, cond); | |
687 | EAP_PRINT("EAP Packet, WSC Start"); | |
688 | } else if (eap_wsc->opcode == WSC_OPCODE_DONE) { | |
689 | dhd->conf->eapol_status = EAPOL_STATUS_WSC_DONE; | |
690 | DHD_STATLOG_DATA(dhd, ST(WSC_DONE), ifidx, tx, cond); | |
691 | EAP_PRINT("EAP Packet, WSC Done"); | |
692 | } | |
693 | } | |
694 | ||
695 | static void | |
696 | dhd_dump_eap_packet(dhd_pub_t *dhd, int ifidx, uint8 *pktdata, | |
697 | uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate) | |
698 | { | |
699 | eapol_header_t *eapol_hdr; | |
700 | eap_header_fmt_t *eap_hdr; | |
701 | char *ifname; | |
702 | bool cond; | |
703 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
704 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
705 | ||
706 | if (!pktdata) { | |
707 | DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__)); | |
708 | return; | |
709 | } | |
710 | ||
711 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
712 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
713 | ||
714 | eapol_hdr = (eapol_header_t *)pktdata; | |
715 | eap_hdr = (eap_header_fmt_t *)(eapol_hdr->body); | |
716 | ifname = dhd_ifname(dhd, ifidx); | |
717 | cond = (tx && pktfate) ? FALSE : TRUE; | |
718 | ||
719 | if (eap_hdr->code == EAP_CODE_REQUEST || | |
720 | eap_hdr->code == EAP_CODE_RESPONSE) { | |
721 | bool isreq = (eap_hdr->code == EAP_CODE_REQUEST); | |
722 | switch (eap_hdr->type) { | |
723 | case EAP_TYPE_IDENT: | |
724 | if (isreq) { | |
725 | dhd->conf->eapol_status = EAPOL_STATUS_REQID; | |
726 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_IDENTITY), ifidx, tx, cond); | |
727 | EAP_PRINT("EAP Packet, Request, Identity"); | |
728 | } else { | |
729 | dhd->conf->eapol_status = EAPOL_STATUS_RSPID; | |
730 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_IDENTITY), ifidx, tx, cond); | |
731 | EAP_PRINT("EAP Packet, Response, Identity"); | |
732 | } | |
733 | break; | |
734 | case EAP_TYPE_TLS: | |
735 | if (isreq) { | |
736 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_TLS), ifidx, tx, cond); | |
737 | EAP_PRINT("EAP Packet, Request, TLS"); | |
738 | } else { | |
739 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_TLS), ifidx, tx, cond); | |
740 | EAP_PRINT("EAP Packet, Response, TLS"); | |
741 | } | |
742 | break; | |
743 | case EAP_TYPE_LEAP: | |
744 | if (isreq) { | |
745 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_LEAP), ifidx, tx, cond); | |
746 | EAP_PRINT("EAP Packet, Request, LEAP"); | |
747 | } else { | |
748 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_LEAP), ifidx, tx, cond); | |
749 | EAP_PRINT("EAP Packet, Response, LEAP"); | |
750 | } | |
751 | break; | |
752 | case EAP_TYPE_TTLS: | |
753 | if (isreq) { | |
754 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_TTLS), ifidx, tx, cond); | |
755 | EAP_PRINT("EAP Packet, Request, TTLS"); | |
756 | } else { | |
757 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_TTLS), ifidx, tx, cond); | |
758 | EAP_PRINT("EAP Packet, Response, TTLS"); | |
759 | } | |
760 | break; | |
761 | case EAP_TYPE_AKA: | |
762 | if (isreq) { | |
763 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_AKA), ifidx, tx, cond); | |
764 | EAP_PRINT("EAP Packet, Request, AKA"); | |
765 | } else { | |
766 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_AKA), ifidx, tx, cond); | |
767 | EAP_PRINT("EAP Packet, Response, AKA"); | |
768 | } | |
769 | break; | |
770 | case EAP_TYPE_PEAP: | |
771 | if (isreq) { | |
772 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_PEAP), ifidx, tx, cond); | |
773 | EAP_PRINT("EAP Packet, Request, PEAP"); | |
774 | } else { | |
775 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_PEAP), ifidx, tx, cond); | |
776 | EAP_PRINT("EAP Packet, Response, PEAP"); | |
777 | } | |
778 | break; | |
779 | case EAP_TYPE_FAST: | |
780 | if (isreq) { | |
781 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_FAST), ifidx, tx, cond); | |
782 | EAP_PRINT("EAP Packet, Request, FAST"); | |
783 | } else { | |
784 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_FAST), ifidx, tx, cond); | |
785 | EAP_PRINT("EAP Packet, Response, FAST"); | |
786 | } | |
787 | break; | |
788 | case EAP_TYPE_PSK: | |
789 | if (isreq) { | |
790 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_PSK), ifidx, tx, cond); | |
791 | EAP_PRINT("EAP Packet, Request, PSK"); | |
792 | } else { | |
793 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_PSK), ifidx, tx, cond); | |
794 | EAP_PRINT("EAP Packet, Response, PSK"); | |
795 | } | |
796 | break; | |
797 | case EAP_TYPE_AKAP: | |
798 | if (isreq) { | |
799 | DHD_STATLOG_DATA(dhd, ST(EAP_REQ_AKAP), ifidx, tx, cond); | |
800 | EAP_PRINT("EAP Packet, Request, AKAP"); | |
801 | } else { | |
802 | DHD_STATLOG_DATA(dhd, ST(EAP_RESP_AKAP), ifidx, tx, cond); | |
803 | EAP_PRINT("EAP Packet, Response, AKAP"); | |
804 | } | |
805 | break; | |
806 | case EAP_TYPE_EXP: | |
807 | dhd_dump_wsc_message(dhd, ifidx, pktdata, pktlen, tx, | |
808 | pkthash, pktfate); | |
809 | break; | |
810 | default: | |
811 | break; | |
812 | } | |
813 | } else if (eap_hdr->code == EAP_CODE_SUCCESS) { | |
814 | DHD_STATLOG_DATA(dhd, ST(EAP_SUCCESS), ifidx, tx, cond); | |
815 | EAP_PRINT("EAP Packet, Success"); | |
816 | } else if (eap_hdr->code == EAP_CODE_FAILURE) { | |
817 | DHD_STATLOG_DATA(dhd, ST(EAP_FAILURE), ifidx, tx, cond); | |
818 | EAP_PRINT("EAP Packet, Failure"); | |
819 | } | |
820 | } | |
821 | ||
822 | static void | |
823 | dhd_dump_eapol_4way_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata, bool tx, | |
824 | uint32 *pkthash, uint16 *pktfate) | |
825 | { | |
826 | eapol_header_t *eapol_hdr; | |
827 | eapol_key_hdr_t *eap_key; | |
828 | msg_eapol_t type; | |
829 | char *ifname; | |
830 | bool cond; | |
831 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
832 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
833 | ||
834 | if (!pktdata) { | |
835 | DHD_PKTDUMP(("%s: pktdata is NULL\n", __FUNCTION__)); | |
836 | return; | |
837 | } | |
838 | ||
839 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
840 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
841 | ||
842 | type = dhd_is_4way_msg(pktdata); | |
843 | ifname = dhd_ifname(dhd, ifidx); | |
844 | eapol_hdr = (eapol_header_t *)pktdata; | |
845 | eap_key = (eapol_key_hdr_t *)(eapol_hdr->body); | |
846 | cond = (tx && pktfate) ? FALSE : TRUE; | |
847 | ||
848 | if (eap_key->type != EAPOL_WPA2_KEY) { | |
849 | EAP_PRINT_OTHER("NON EAPOL_WPA2_KEY"); | |
850 | return; | |
851 | } | |
852 | ||
853 | switch (type) { | |
854 | case EAPOL_4WAY_M1: | |
855 | dhd->conf->eapol_status = EAPOL_STATUS_4WAY_M1; | |
856 | DHD_STATLOG_DATA(dhd, ST(EAPOL_M1), ifidx, tx, cond); | |
857 | EAP_PRINT("EAPOL Packet, 4-way handshake, M1"); | |
858 | break; | |
859 | case EAPOL_4WAY_M2: | |
860 | dhd->conf->eapol_status = EAPOL_STATUS_4WAY_M2; | |
861 | DHD_STATLOG_DATA(dhd, ST(EAPOL_M2), ifidx, tx, cond); | |
862 | EAP_PRINT("EAPOL Packet, 4-way handshake, M2"); | |
863 | break; | |
864 | case EAPOL_4WAY_M3: | |
865 | dhd->conf->eapol_status = EAPOL_STATUS_4WAY_M3; | |
866 | DHD_STATLOG_DATA(dhd, ST(EAPOL_M3), ifidx, tx, cond); | |
867 | EAP_PRINT("EAPOL Packet, 4-way handshake, M3"); | |
868 | break; | |
869 | case EAPOL_4WAY_M4: | |
870 | dhd->conf->eapol_status = EAPOL_STATUS_4WAY_M4; | |
871 | DHD_STATLOG_DATA(dhd, ST(EAPOL_M4), ifidx, tx, cond); | |
872 | EAP_PRINT("EAPOL Packet, 4-way handshake, M4"); | |
873 | break; | |
874 | case EAPOL_GROUPKEY_M1: | |
875 | DHD_STATLOG_DATA(dhd, ST(EAPOL_GROUPKEY_M1), ifidx, tx, cond); | |
876 | EAP_PRINT_REPLAY("EAPOL Packet, GROUP Key handshake, M1"); | |
877 | break; | |
878 | case EAPOL_GROUPKEY_M2: | |
879 | DHD_STATLOG_DATA(dhd, ST(EAPOL_GROUPKEY_M2), ifidx, tx, cond); | |
880 | EAP_PRINT_REPLAY("EAPOL Packet, GROUP Key handshake, M2"); | |
881 | if (ifidx == 0 && tx && pktfate) { | |
882 | dhd_dump_mod_pkt_timer(dhd, PKT_CNT_RSN_GRPKEY_UP); | |
883 | } | |
884 | break; | |
885 | default: | |
886 | DHD_STATLOG_DATA(dhd, ST(8021X_OTHER), ifidx, tx, cond); | |
887 | EAP_PRINT_OTHER("OTHER 4WAY"); | |
888 | break; | |
889 | } | |
890 | } | |
891 | ||
892 | void | |
893 | dhd_dump_eapol_message(dhd_pub_t *dhd, int ifidx, uint8 *pktdata, | |
894 | uint32 pktlen, bool tx, uint32 *pkthash, uint16 *pktfate) | |
895 | { | |
896 | char *ifname; | |
897 | eapol_header_t *eapol_hdr = (eapol_header_t *)pktdata; | |
898 | bool cond; | |
899 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
900 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
901 | ||
902 | if (!pktdata) { | |
903 | DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__)); | |
904 | return; | |
905 | } | |
906 | ||
907 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
908 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
909 | ||
910 | eapol_hdr = (eapol_header_t *)pktdata; | |
911 | ifname = dhd_ifname(dhd, ifidx); | |
912 | cond = (tx && pktfate) ? FALSE : TRUE; | |
913 | ||
914 | if (eapol_hdr->type == EAP_PACKET) { | |
915 | dhd_dump_eap_packet(dhd, ifidx, pktdata, pktlen, tx, | |
916 | pkthash, pktfate); | |
917 | } else if (eapol_hdr->type == EAPOL_START) { | |
918 | DHD_STATLOG_DATA(dhd, ST(EAPOL_START), ifidx, tx, cond); | |
919 | EAP_PRINT("EAP Packet, EAPOL-Start"); | |
920 | } else if (eapol_hdr->type == EAPOL_KEY) { | |
921 | dhd_dump_eapol_4way_message(dhd, ifidx, pktdata, tx, | |
922 | pkthash, pktfate); | |
923 | } else { | |
924 | DHD_STATLOG_DATA(dhd, ST(8021X_OTHER), ifidx, tx, cond); | |
925 | EAP_PRINT_OTHER("OTHER 8021X"); | |
926 | } | |
927 | } | |
928 | #endif /* DHD_8021X_DUMP */ | |
929 | ||
930 | bool | |
931 | dhd_check_dhcp(uint8 *pktdata) | |
932 | { | |
933 | hdr_fmt_t *b = (hdr_fmt_t *)&pktdata[ETHER_HDR_LEN]; | |
934 | struct ipv4_hdr *iph = &b->iph; | |
935 | ||
936 | /* check IP header */ | |
937 | if ((IPV4_HLEN(iph) < IPV4_HLEN_MIN) || | |
938 | IP_VER(iph) != IP_VER_4 || | |
939 | IPV4_PROT(iph) != IP_PROT_UDP) { | |
940 | return FALSE; | |
941 | } | |
942 | ||
943 | /* check UDP port for bootp (67, 68) */ | |
944 | if (b->udph.src_port != htons(DHCP_PORT_SERVER) && | |
945 | b->udph.src_port != htons(DHCP_PORT_CLIENT) && | |
946 | b->udph.dst_port != htons(DHCP_PORT_SERVER) && | |
947 | b->udph.dst_port != htons(DHCP_PORT_CLIENT)) { | |
948 | return FALSE; | |
949 | } | |
950 | ||
951 | /* check header length */ | |
952 | if (ntohs(iph->tot_len) < ntohs(b->udph.len) + sizeof(struct bcmudp_hdr)) { | |
953 | return FALSE; | |
954 | } | |
955 | return TRUE; | |
956 | } | |
957 | ||
958 | #ifdef DHD_DHCP_DUMP | |
959 | #define BOOTP_CHADDR_LEN 16 | |
960 | #define BOOTP_SNAME_LEN 64 | |
961 | #define BOOTP_FILE_LEN 128 | |
962 | #define BOOTP_MIN_DHCP_OPT_LEN 312 | |
963 | #define BOOTP_MAGIC_COOKIE_LEN 4 | |
964 | ||
965 | #define DHCP_MSGTYPE_DISCOVER 1 | |
966 | #define DHCP_MSGTYPE_OFFER 2 | |
967 | #define DHCP_MSGTYPE_REQUEST 3 | |
968 | #define DHCP_MSGTYPE_DECLINE 4 | |
969 | #define DHCP_MSGTYPE_ACK 5 | |
970 | #define DHCP_MSGTYPE_NAK 6 | |
971 | #define DHCP_MSGTYPE_RELEASE 7 | |
972 | #define DHCP_MSGTYPE_INFORM 8 | |
973 | ||
974 | #define DHCP_PRINT(str) \ | |
975 | do { \ | |
976 | if (tx) { \ | |
977 | DHD_PKTDUMP(("[dhd-%s] " str " %8s[%8s] [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \ | |
978 | ifname, typestr, opstr, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
979 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
980 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
981 | } else { \ | |
982 | DHD_PKTDUMP(("[dhd-%s] " str " %8s[%8s] [RX] : %s(%s) %s %s(%s)\n", \ | |
983 | ifname, typestr, opstr, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
984 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf)); \ | |
985 | } \ | |
986 | } while (0) | |
987 | ||
988 | typedef struct bootp_fmt { | |
989 | struct ipv4_hdr iph; | |
990 | struct bcmudp_hdr udph; | |
991 | uint8 op; | |
992 | uint8 htype; | |
993 | uint8 hlen; | |
994 | uint8 hops; | |
995 | uint32 transaction_id; | |
996 | uint16 secs; | |
997 | uint16 flags; | |
998 | uint32 client_ip; | |
999 | uint32 assigned_ip; | |
1000 | uint32 server_ip; | |
1001 | uint32 relay_ip; | |
1002 | uint8 hw_address[BOOTP_CHADDR_LEN]; | |
1003 | uint8 server_name[BOOTP_SNAME_LEN]; | |
1004 | uint8 file_name[BOOTP_FILE_LEN]; | |
1005 | uint8 options[BOOTP_MIN_DHCP_OPT_LEN]; | |
1006 | } PACKED_STRUCT bootp_fmt_t; | |
1007 | ||
1008 | static const uint8 bootp_magic_cookie[4] = { 99, 130, 83, 99 }; | |
1009 | static char dhcp_ops[][10] = { | |
1010 | "NA", "REQUEST", "REPLY" | |
1011 | }; | |
1012 | static char dhcp_types[][10] = { | |
1013 | "NA", "DISCOVER", "OFFER", "REQUEST", "DECLINE", "ACK", "NAK", "RELEASE", "INFORM" | |
1014 | }; | |
1015 | ||
1016 | #ifdef DHD_STATUS_LOGGING | |
1017 | static const int dhcp_types_stat[9] = { | |
1018 | ST(INVALID), ST(DHCP_DISCOVER), ST(DHCP_OFFER), ST(DHCP_REQUEST), | |
1019 | ST(DHCP_DECLINE), ST(DHCP_ACK), ST(DHCP_NAK), ST(DHCP_RELEASE), | |
1020 | ST(DHCP_INFORM) | |
1021 | }; | |
1022 | #endif /* DHD_STATUS_LOGGING */ | |
1023 | ||
1024 | void | |
1025 | dhd_dhcp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx, | |
1026 | uint32 *pkthash, uint16 *pktfate) | |
1027 | { | |
1028 | bootp_fmt_t *b = (bootp_fmt_t *)&pktdata[ETHER_HDR_LEN]; | |
1029 | struct ipv4_hdr *iph = &b->iph; | |
1030 | uint8 *ptr, *opt, *end = (uint8 *) b + ntohs(b->iph.tot_len); | |
1031 | int dhcp_type = 0, len, opt_len; | |
1032 | char *ifname = NULL, *typestr = NULL, *opstr = NULL; | |
1033 | bool cond; | |
1034 | char sabuf[20]="", dabuf[20]=""; | |
1035 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
1036 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
1037 | ||
1038 | if (!(dump_msg_level & DUMP_DHCP_VAL)) | |
1039 | return; | |
1040 | bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf); | |
1041 | bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf); | |
1042 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
1043 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
1044 | ||
1045 | ifname = dhd_ifname(dhdp, ifidx); | |
1046 | cond = (tx && pktfate) ? FALSE : TRUE; | |
1047 | len = ntohs(b->udph.len) - sizeof(struct bcmudp_hdr); | |
1048 | opt_len = len - (sizeof(*b) - sizeof(struct ipv4_hdr) - | |
1049 | sizeof(struct bcmudp_hdr) - sizeof(b->options)); | |
1050 | ||
1051 | /* parse bootp options */ | |
1052 | if (opt_len >= BOOTP_MAGIC_COOKIE_LEN && | |
1053 | !memcmp(b->options, bootp_magic_cookie, BOOTP_MAGIC_COOKIE_LEN)) { | |
1054 | ptr = &b->options[BOOTP_MAGIC_COOKIE_LEN]; | |
1055 | while (ptr < end && *ptr != 0xff) { | |
1056 | opt = ptr++; | |
1057 | if (*opt == 0) { | |
1058 | continue; | |
1059 | } | |
1060 | ptr += *ptr + 1; | |
1061 | if (ptr >= end) { | |
1062 | break; | |
1063 | } | |
1064 | if (*opt == DHCP_OPT_MSGTYPE) { | |
1065 | if (opt[1]) { | |
1066 | dhcp_type = opt[2]; | |
1067 | typestr = dhcp_types[dhcp_type]; | |
1068 | opstr = dhcp_ops[b->op]; | |
1069 | DHD_STATLOG_DATA(dhdp, dhcp_types_stat[dhcp_type], | |
1070 | ifidx, tx, cond); | |
1071 | DHCP_PRINT("DHCP"); | |
1072 | break; | |
1073 | } | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | #endif /* DHD_DHCP_DUMP */ | |
1079 | ||
1080 | bool | |
1081 | dhd_check_icmp(uint8 *pktdata) | |
1082 | { | |
1083 | uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN]; | |
1084 | struct ipv4_hdr *iph = (struct ipv4_hdr *)pkt; | |
1085 | ||
1086 | /* check IP header */ | |
1087 | if ((IPV4_HLEN(iph) < IPV4_HLEN_MIN) || | |
1088 | IP_VER(iph) != IP_VER_4 || | |
1089 | IPV4_PROT(iph) != IP_PROT_ICMP) { | |
1090 | return FALSE; | |
1091 | } | |
1092 | ||
1093 | /* check header length */ | |
1094 | if (ntohs(iph->tot_len) - IPV4_HLEN(iph) < sizeof(struct bcmicmp_hdr)) { | |
1095 | return FALSE; | |
1096 | } | |
1097 | return TRUE; | |
1098 | } | |
1099 | ||
1100 | #ifdef DHD_ICMP_DUMP | |
1101 | #define ICMP_TYPE_DEST_UNREACH 3 | |
1102 | #define ICMP_ECHO_SEQ_OFFSET 6 | |
1103 | #define ICMP_ECHO_SEQ(h) (*(uint16 *)((uint8 *)(h) + (ICMP_ECHO_SEQ_OFFSET))) | |
1104 | #define ICMP_PING_PRINT(str) \ | |
1105 | do { \ | |
1106 | if (tx) { \ | |
1107 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s) SEQNUM=%d" \ | |
1108 | TXFATE_FMT"\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1109 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, seqnum, \ | |
1110 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1111 | } else { \ | |
1112 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [RX] : %s(%s) %s %s(%s) SEQNUM=%d\n", \ | |
1113 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1114 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, seqnum)); \ | |
1115 | } \ | |
1116 | } while (0) | |
1117 | ||
1118 | #define ICMP_PRINT(str) \ | |
1119 | do { \ | |
1120 | if (tx) { \ | |
1121 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s) TYPE=%d, CODE=%d" \ | |
1122 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1123 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, type, code, \ | |
1124 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1125 | } else { \ | |
1126 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [RX] : %s(%s) %s %s(%s) TYPE=%d," \ | |
1127 | " CODE=%d\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1128 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, type, code)); \ | |
1129 | } \ | |
1130 | } while (0) | |
1131 | ||
1132 | void | |
1133 | dhd_icmp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx, | |
1134 | uint32 *pkthash, uint16 *pktfate) | |
1135 | { | |
1136 | uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN]; | |
1137 | struct ipv4_hdr *iph = (struct ipv4_hdr *)pkt; | |
1138 | struct bcmicmp_hdr *icmph; | |
1139 | char *ifname; | |
1140 | bool cond; | |
1141 | uint16 seqnum, type, code; | |
1142 | char sabuf[20]="", dabuf[20]=""; | |
1143 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
1144 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
1145 | ||
1146 | if (!(dump_msg_level & DUMP_ICMP_VAL)) | |
1147 | return; | |
1148 | ||
1149 | ifname = dhd_ifname(dhdp, ifidx); | |
1150 | cond = (tx && pktfate) ? FALSE : TRUE; | |
1151 | icmph = (struct bcmicmp_hdr *)((uint8 *)pkt + sizeof(struct ipv4_hdr)); | |
1152 | seqnum = 0; | |
1153 | type = icmph->type; | |
1154 | code = icmph->code; | |
1155 | bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf); | |
1156 | bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf); | |
1157 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
1158 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
1159 | if (type == ICMP_TYPE_ECHO_REQUEST) { | |
1160 | seqnum = ntoh16(ICMP_ECHO_SEQ(icmph)); | |
1161 | DHD_STATLOG_DATA(dhdp, ST(ICMP_PING_REQ), ifidx, tx, cond); | |
1162 | ICMP_PING_PRINT("PING REQUEST"); | |
1163 | } else if (type == ICMP_TYPE_ECHO_REPLY) { | |
1164 | seqnum = ntoh16(ICMP_ECHO_SEQ(icmph)); | |
1165 | DHD_STATLOG_DATA(dhdp, ST(ICMP_PING_RESP), ifidx, tx, cond); | |
1166 | ICMP_PING_PRINT("PING REPLY "); | |
1167 | } else if (type == ICMP_TYPE_DEST_UNREACH) { | |
1168 | DHD_STATLOG_DATA(dhdp, ST(ICMP_DEST_UNREACH), ifidx, tx, cond); | |
1169 | ICMP_PRINT("ICMP DEST UNREACH"); | |
1170 | } else { | |
1171 | DHD_STATLOG_DATA(dhdp, ST(ICMP_OTHER), ifidx, tx, cond); | |
1172 | ICMP_PRINT("ICMP OTHER"); | |
1173 | } | |
1174 | } | |
1175 | #endif /* DHD_ICMP_DUMP */ | |
1176 | ||
1177 | bool | |
1178 | dhd_check_arp(uint8 *pktdata) | |
1179 | { | |
1180 | uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN]; | |
1181 | struct bcmarp *arph = (struct bcmarp *)pkt; | |
1182 | ||
1183 | /* validation check */ | |
1184 | if (arph->htype != hton16(HTYPE_ETHERNET) || | |
1185 | arph->hlen != ETHER_ADDR_LEN || | |
1186 | arph->plen != 4) { | |
1187 | return FALSE; | |
1188 | } | |
1189 | return TRUE; | |
1190 | } | |
1191 | ||
1192 | #ifdef DHD_ARP_DUMP | |
1193 | #ifdef BOARD_HIKEY | |
1194 | /* On Hikey, due to continuous ARP prints | |
1195 | * DPC not scheduled. Hence rate limit the prints. | |
1196 | */ | |
1197 | #define DHD_PKTDUMP_ARP DHD_ERROR_RLMT | |
1198 | #else | |
1199 | #define DHD_PKTDUMP_ARP DHD_PKTDUMP | |
1200 | #endif /* BOARD_HIKEY */ | |
1201 | ||
1202 | #define ARP_PRINT(str) \ | |
1203 | do { \ | |
1204 | if (tx) { \ | |
1205 | if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \ | |
1206 | DHD_PKTDUMP(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \ | |
1207 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1208 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
1209 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1210 | } else { \ | |
1211 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s)"TXFATE_FMT"\n", \ | |
1212 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1213 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
1214 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1215 | } \ | |
1216 | } else { \ | |
1217 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [RX] : %s(%s) %s %s(%s)\n", \ | |
1218 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1219 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf)); \ | |
1220 | } \ | |
1221 | } while (0) \ | |
1222 | ||
1223 | #define ARP_PRINT_OTHER(str) \ | |
1224 | do { \ | |
1225 | if (tx) { \ | |
1226 | if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \ | |
1227 | DHD_PKTDUMP(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s) op_code=%d" \ | |
1228 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1229 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, opcode, \ | |
1230 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1231 | } else { \ | |
1232 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [TX] : %s(%s) %s %s(%s) op_code=%d" \ | |
1233 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1234 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, opcode, \ | |
1235 | TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1236 | } \ | |
1237 | } else { \ | |
1238 | DHD_PKTDUMP_MEM(("[dhd-%s] "str " [RX] : %s(%s) %s %s(%s) op_code=%d\n", \ | |
1239 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1240 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, opcode)); \ | |
1241 | } \ | |
1242 | } while (0) | |
1243 | ||
1244 | void | |
1245 | dhd_arp_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx, | |
1246 | uint32 *pkthash, uint16 *pktfate) | |
1247 | { | |
1248 | uint8 *pkt = (uint8 *)&pktdata[ETHER_HDR_LEN]; | |
1249 | struct bcmarp *arph = (struct bcmarp *)pkt; | |
1250 | char *ifname; | |
1251 | uint16 opcode; | |
1252 | bool cond, dump_enabled; | |
1253 | char sabuf[20]="", dabuf[20]=""; | |
1254 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
1255 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
1256 | ||
1257 | if (!(dump_msg_level & DUMP_ARP_VAL)) | |
1258 | return; | |
1259 | ||
1260 | ifname = dhd_ifname(dhdp, ifidx); | |
1261 | opcode = ntoh16(arph->oper); | |
1262 | cond = (tx && pktfate) ? FALSE : TRUE; | |
1263 | dump_enabled = dhd_dump_pkt_enabled(dhdp); | |
1264 | bcm_ip_ntoa((struct ipv4_addr *)arph->src_ip, sabuf); | |
1265 | bcm_ip_ntoa((struct ipv4_addr *)arph->dst_ip, dabuf); | |
1266 | bcm_ether_ntoa((struct ether_addr *)arph->dst_eth, deabuf); | |
1267 | bcm_ether_ntoa((struct ether_addr *)arph->src_eth, seabuf); | |
1268 | if (opcode == ARP_OPC_REQUEST) { | |
1269 | DHD_STATLOG_DATA(dhdp, ST(ARP_REQ), ifidx, tx, cond); | |
1270 | ARP_PRINT("ARP REQUEST "); | |
1271 | } else if (opcode == ARP_OPC_REPLY) { | |
1272 | DHD_STATLOG_DATA(dhdp, ST(ARP_RESP), ifidx, tx, cond); | |
1273 | ARP_PRINT("ARP RESPONSE"); | |
1274 | } else { | |
1275 | ARP_PRINT_OTHER("ARP OTHER"); | |
1276 | } | |
1277 | ||
1278 | if (ifidx == 0) { | |
1279 | dhd_dump_pkt_cnts_inc(dhdp, tx, pktfate, PKT_CNT_TYPE_ARP); | |
1280 | } | |
1281 | } | |
1282 | #endif /* DHD_ARP_DUMP */ | |
1283 | ||
1284 | bool | |
1285 | dhd_check_dns(uint8 *pktdata) | |
1286 | { | |
1287 | hdr_fmt_t *dnsh = (hdr_fmt_t *)&pktdata[ETHER_HDR_LEN]; | |
1288 | struct ipv4_hdr *iph = &dnsh->iph; | |
1289 | ||
1290 | /* check IP header */ | |
1291 | if ((IPV4_HLEN(iph) < IPV4_HLEN_MIN) || | |
1292 | IP_VER(iph) != IP_VER_4 || | |
1293 | IPV4_PROT(iph) != IP_PROT_UDP) { | |
1294 | return FALSE; | |
1295 | } | |
1296 | ||
1297 | /* check UDP port for DNS */ | |
1298 | if (dnsh->udph.src_port != hton16(UDP_PORT_DNS) && | |
1299 | dnsh->udph.dst_port != hton16(UDP_PORT_DNS)) { | |
1300 | return FALSE; | |
1301 | } | |
1302 | ||
1303 | /* check header length */ | |
1304 | if (ntoh16(iph->tot_len) < (ntoh16(dnsh->udph.len) + | |
1305 | sizeof(struct bcmudp_hdr))) { | |
1306 | return FALSE; | |
1307 | } | |
1308 | return TRUE; | |
1309 | } | |
1310 | ||
1311 | #ifdef DHD_DNS_DUMP | |
1312 | typedef struct dns_fmt { | |
1313 | struct ipv4_hdr iph; | |
1314 | struct bcmudp_hdr udph; | |
1315 | uint16 id; | |
1316 | uint16 flags; | |
1317 | uint16 qdcount; | |
1318 | uint16 ancount; | |
1319 | uint16 nscount; | |
1320 | uint16 arcount; | |
1321 | } PACKED_STRUCT dns_fmt_t; | |
1322 | ||
1323 | #define DNS_QR_LOC 15 | |
1324 | #define DNS_OPCODE_LOC 11 | |
1325 | #define DNS_RCODE_LOC 0 | |
1326 | #define DNS_QR_MASK ((0x1) << (DNS_QR_LOC)) | |
1327 | #define DNS_OPCODE_MASK ((0xF) << (DNS_OPCODE_LOC)) | |
1328 | #define DNS_RCODE_MASK ((0xF) << (DNS_RCODE_LOC)) | |
1329 | #define GET_DNS_QR(flags) (((flags) & (DNS_QR_MASK)) >> (DNS_QR_LOC)) | |
1330 | #define GET_DNS_OPCODE(flags) (((flags) & (DNS_OPCODE_MASK)) >> (DNS_OPCODE_LOC)) | |
1331 | #define GET_DNS_RCODE(flags) (((flags) & (DNS_RCODE_MASK)) >> (DNS_RCODE_LOC)) | |
1332 | #define DNS_UNASSIGNED_OPCODE(flags) ((GET_DNS_OPCODE(flags) >= (6))) | |
1333 | ||
1334 | static const char dns_opcode_types[][11] = { | |
1335 | "QUERY", "IQUERY", "STATUS", "UNASSIGNED", "NOTIFY", "UPDATE" | |
1336 | }; | |
1337 | ||
1338 | #define DNSOPCODE(op) \ | |
1339 | (DNS_UNASSIGNED_OPCODE(flags) ? "UNASSIGNED" : dns_opcode_types[op]) | |
1340 | ||
1341 | #define DNS_REQ_PRINT(str) \ | |
1342 | do { \ | |
1343 | if (tx) { \ | |
1344 | if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \ | |
1345 | DHD_PKTDUMP(("[dhd-%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s" \ | |
1346 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1347 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
1348 | id, DNSOPCODE(opcode), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1349 | } else { \ | |
1350 | DHD_PKTDUMP_MEM(("[dhd-%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s" \ | |
1351 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1352 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
1353 | id, DNSOPCODE(opcode), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1354 | } \ | |
1355 | } else { \ | |
1356 | DHD_PKTDUMP_MEM(("[dhd-%s] " str " [RX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s\n", \ | |
1357 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, tx?"->":"<-", \ | |
1358 | tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode))); \ | |
1359 | } \ | |
1360 | } while (0) | |
1361 | ||
1362 | #define DNS_RESP_PRINT(str) \ | |
1363 | do { \ | |
1364 | if (tx) { \ | |
1365 | if (dump_enabled && pktfate && !TX_FATE_ACKED(pktfate)) { \ | |
1366 | DHD_PKTDUMP(("[dhd-%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d" \ | |
1367 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1368 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode), \ | |
1369 | GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1370 | } else { \ | |
1371 | DHD_PKTDUMP_MEM(("[dhd-%s] " str " [TX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d" \ | |
1372 | TXFATE_FMT "\n", ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1373 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, id, DNSOPCODE(opcode), \ | |
1374 | GET_DNS_RCODE(flags), TX_PKTHASH(pkthash), TX_FATE(pktfate))); \ | |
1375 | } \ | |
1376 | } else { \ | |
1377 | DHD_PKTDUMP_MEM(("[dhd-%s] " str " [RX] : %s(%s) %s %s(%s) ID:0x%04X OPCODE:%s RCODE:%d\n", \ | |
1378 | ifname, tx?sabuf:dabuf, tx?seabuf:deabuf, \ | |
1379 | tx?"->":"<-", tx?dabuf:sabuf, tx?deabuf:seabuf, \ | |
1380 | id, DNSOPCODE(opcode), GET_DNS_RCODE(flags))); \ | |
1381 | } \ | |
1382 | } while (0) | |
1383 | ||
1384 | void | |
1385 | dhd_dns_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, bool tx, | |
1386 | uint32 *pkthash, uint16 *pktfate) | |
1387 | { | |
1388 | dns_fmt_t *dnsh = (dns_fmt_t *)&pktdata[ETHER_HDR_LEN]; | |
1389 | struct ipv4_hdr *iph = &dnsh->iph; | |
1390 | uint16 flags, opcode, id; | |
1391 | char *ifname; | |
1392 | bool cond, dump_enabled; | |
1393 | char sabuf[20]="", dabuf[20]=""; | |
1394 | char seabuf[ETHER_ADDR_STR_LEN]=""; | |
1395 | char deabuf[ETHER_ADDR_STR_LEN]=""; | |
1396 | ||
1397 | if (!(dump_msg_level & DUMP_DNS_VAL)) | |
1398 | return; | |
1399 | ||
1400 | ifname = dhd_ifname(dhdp, ifidx); | |
1401 | cond = (tx && pktfate) ? FALSE : TRUE; | |
1402 | dump_enabled = dhd_dump_pkt_enabled(dhdp); | |
1403 | flags = hton16(dnsh->flags); | |
1404 | opcode = GET_DNS_OPCODE(flags); | |
1405 | id = hton16(dnsh->id); | |
1406 | bcm_ip_ntoa((struct ipv4_addr *)iph->src_ip, sabuf); | |
1407 | bcm_ip_ntoa((struct ipv4_addr *)iph->dst_ip, dabuf); | |
1408 | bcm_ether_ntoa((struct ether_addr *)pktdata, deabuf); | |
1409 | bcm_ether_ntoa((struct ether_addr *)(pktdata+6), seabuf); | |
1410 | if (GET_DNS_QR(flags)) { | |
1411 | /* Response */ | |
1412 | DHD_STATLOG_DATA(dhdp, ST(DNS_RESP), ifidx, tx, cond); | |
1413 | DNS_RESP_PRINT("DNS RESPONSE"); | |
1414 | } else { | |
1415 | /* Request */ | |
1416 | DHD_STATLOG_DATA(dhdp, ST(DNS_QUERY), ifidx, tx, cond); | |
1417 | DNS_REQ_PRINT("DNS REQUEST"); | |
1418 | } | |
1419 | ||
1420 | if (ifidx == 0) { | |
1421 | dhd_dump_pkt_cnts_inc(dhdp, tx, pktfate, PKT_CNT_TYPE_DNS); | |
1422 | } | |
1423 | } | |
1424 | #endif /* DHD_DNS_DUMP */ | |
1425 | ||
1426 | #ifdef DHD_TRX_DUMP | |
1427 | void | |
1428 | dhd_trx_pkt_dump(dhd_pub_t *dhdp, int ifidx, uint8 *pktdata, uint32 pktlen, bool tx) | |
1429 | { | |
1430 | struct ether_header *eh; | |
1431 | uint16 protocol; | |
1432 | char *pkttype = "UNKNOWN"; | |
1433 | ||
1434 | if (!(dump_msg_level & DUMP_TRX_VAL)) | |
1435 | return; | |
1436 | ||
1437 | if (!dhdp) { | |
1438 | DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); | |
1439 | return; | |
1440 | } | |
1441 | ||
1442 | if (!pktdata) { | |
1443 | DHD_ERROR(("%s: pktdata is NULL\n", __FUNCTION__)); | |
1444 | return; | |
1445 | } | |
1446 | ||
1447 | eh = (struct ether_header *)pktdata; | |
1448 | protocol = hton16(eh->ether_type); | |
1449 | BCM_REFERENCE(pktlen); | |
1450 | ||
1451 | switch (protocol) { | |
1452 | case ETHER_TYPE_IP: | |
1453 | pkttype = "IP"; | |
1454 | break; | |
1455 | case ETHER_TYPE_ARP: | |
1456 | pkttype = "ARP"; | |
1457 | break; | |
1458 | case ETHER_TYPE_BRCM: | |
1459 | pkttype = "BRCM"; | |
1460 | break; | |
1461 | case ETHER_TYPE_802_1X: | |
1462 | pkttype = "802.1X"; | |
1463 | break; | |
1464 | case ETHER_TYPE_WAI: | |
1465 | pkttype = "WAPI"; | |
1466 | break; | |
1467 | default: | |
1468 | break; | |
1469 | } | |
1470 | ||
1471 | if (protocol != ETHER_TYPE_BRCM) { | |
1472 | if (pktdata[0] == 0xFF) { | |
1473 | DHD_PKTDUMP(("[dhd-%s] %s BROADCAST DUMP - %s\n", | |
1474 | dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype)); | |
1475 | } else if (pktdata[0] & 1) { | |
1476 | DHD_PKTDUMP(("[dhd-%s] %s MULTICAST DUMP " MACDBG " - %s\n", | |
1477 | dhd_ifname(dhdp, ifidx), tx?"TX":"RX", MAC2STRDBG(pktdata), pkttype)); | |
1478 | } else { | |
1479 | DHD_PKTDUMP(("[dhd-%s] %s DUMP - %s\n", | |
1480 | dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype)); | |
1481 | } | |
1482 | #ifdef DHD_RX_FULL_DUMP | |
1483 | prhex("Data", pktdata, pktlen); | |
1484 | #endif /* DHD_RX_FULL_DUMP */ | |
1485 | } | |
1486 | else { | |
1487 | DHD_PKTDUMP(("[dhd-%s] %s DUMP - %s\n", | |
1488 | dhd_ifname(dhdp, ifidx), tx?"TX":"RX", pkttype)); | |
1489 | } | |
1490 | } | |
1491 | #endif /* DHD_RX_DUMP */ |