Commit | Line | Data |
---|---|---|
1b4a7c03 LJ |
1 | /* |
2 | * IP Packet Parser Module. | |
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 | #include <typedefs.h> | |
26 | #include <osl.h> | |
27 | ||
28 | #include <ethernet.h> | |
29 | #include <vlan.h> | |
30 | #include <802.3.h> | |
31 | #include <bcmip.h> | |
32 | #include <bcmendian.h> | |
33 | ||
34 | #include <dhd_dbg.h> | |
35 | ||
36 | #include <dhd_ip.h> | |
37 | ||
38 | #if defined(DHDTCPACK_SUPPRESS) || defined(DHDTCPSYNC_FLOOD_BLK) | |
39 | #include <dhd_bus.h> | |
40 | #include <dhd_proto.h> | |
41 | #include <bcmtcp.h> | |
42 | #endif /* DHDTCPACK_SUPPRESS || DHDTCPSYNC_FLOOD_BLK */ | |
43 | ||
44 | /* special values */ | |
45 | /* 802.3 llc/snap header */ | |
46 | static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | |
47 | ||
48 | pkt_frag_t pkt_frag_info(osl_t *osh, void *p) | |
49 | { | |
50 | uint8 *frame; | |
51 | int length; | |
52 | uint8 *pt; /* Pointer to type field */ | |
53 | uint16 ethertype; | |
54 | struct ipv4_hdr *iph; /* IP frame pointer */ | |
55 | int ipl; /* IP frame length */ | |
56 | uint16 iph_frag; | |
57 | ||
58 | ASSERT(osh && p); | |
59 | ||
60 | frame = PKTDATA(osh, p); | |
61 | length = PKTLEN(osh, p); | |
62 | ||
63 | /* Process Ethernet II or SNAP-encapsulated 802.3 frames */ | |
64 | if (length < ETHER_HDR_LEN) { | |
65 | DHD_INFO(("%s: short eth frame (%d)\n", __FUNCTION__, length)); | |
66 | return DHD_PKT_FRAG_NONE; | |
67 | } else if (ntoh16(*(uint16 *)(frame + ETHER_TYPE_OFFSET)) >= ETHER_TYPE_MIN) { | |
68 | /* Frame is Ethernet II */ | |
69 | pt = frame + ETHER_TYPE_OFFSET; | |
70 | } else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN && | |
71 | !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) { | |
72 | pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN; | |
73 | } else { | |
74 | DHD_INFO(("%s: non-SNAP 802.3 frame\n", __FUNCTION__)); | |
75 | return DHD_PKT_FRAG_NONE; | |
76 | } | |
77 | ||
78 | ethertype = ntoh16(*(uint16 *)pt); | |
79 | ||
80 | /* Skip VLAN tag, if any */ | |
81 | if (ethertype == ETHER_TYPE_8021Q) { | |
82 | pt += VLAN_TAG_LEN; | |
83 | ||
84 | if (pt + ETHER_TYPE_LEN > frame + length) { | |
85 | DHD_INFO(("%s: short VLAN frame (%d)\n", __FUNCTION__, length)); | |
86 | return DHD_PKT_FRAG_NONE; | |
87 | } | |
88 | ||
89 | ethertype = ntoh16(*(uint16 *)pt); | |
90 | } | |
91 | ||
92 | if (ethertype != ETHER_TYPE_IP) { | |
93 | DHD_INFO(("%s: non-IP frame (ethertype 0x%x, length %d)\n", | |
94 | __FUNCTION__, ethertype, length)); | |
95 | return DHD_PKT_FRAG_NONE; | |
96 | } | |
97 | ||
98 | iph = (struct ipv4_hdr *)(pt + ETHER_TYPE_LEN); | |
99 | ipl = (uint)(length - (pt + ETHER_TYPE_LEN - frame)); | |
100 | ||
101 | /* We support IPv4 only */ | |
102 | if ((ipl < IPV4_OPTIONS_OFFSET) || (IP_VER(iph) != IP_VER_4)) { | |
103 | DHD_INFO(("%s: short frame (%d) or non-IPv4\n", __FUNCTION__, ipl)); | |
104 | return DHD_PKT_FRAG_NONE; | |
105 | } | |
106 | ||
107 | iph_frag = ntoh16(iph->frag); | |
108 | ||
109 | if (iph_frag & IPV4_FRAG_DONT) { | |
110 | return DHD_PKT_FRAG_NONE; | |
111 | } else if ((iph_frag & IPV4_FRAG_MORE) == 0) { | |
112 | return DHD_PKT_FRAG_LAST; | |
113 | } else { | |
114 | return (iph_frag & IPV4_FRAG_OFFSET_MASK)? DHD_PKT_FRAG_CONT : DHD_PKT_FRAG_FIRST; | |
115 | } | |
116 | } | |
117 | ||
118 | #ifdef DHDTCPACK_SUPPRESS | |
119 | ||
120 | typedef struct { | |
121 | void *pkt_in_q; /* TCP ACK packet that is already in txq or DelayQ */ | |
122 | void *pkt_ether_hdr; /* Ethernet header pointer of pkt_in_q */ | |
123 | int ifidx; | |
124 | uint8 supp_cnt; | |
125 | dhd_pub_t *dhdp; | |
126 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
127 | timer_list_compat_t timer; | |
128 | #else | |
129 | struct tasklet_hrtimer timer; | |
130 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
131 | } tcpack_info_t; | |
132 | ||
133 | typedef struct _tdata_psh_info_t { | |
134 | uint32 end_seq; /* end seq# of a received TCP PSH DATA pkt */ | |
135 | struct _tdata_psh_info_t *next; /* next pointer of the link chain */ | |
136 | } tdata_psh_info_t; | |
137 | ||
138 | typedef struct { | |
139 | struct { | |
140 | uint8 src[IPV4_ADDR_LEN]; /* SRC ip addrs of this TCP stream */ | |
141 | uint8 dst[IPV4_ADDR_LEN]; /* DST ip addrs of this TCP stream */ | |
142 | } ip_addr; | |
143 | struct { | |
144 | uint8 src[TCP_PORT_LEN]; /* SRC tcp ports of this TCP stream */ | |
145 | uint8 dst[TCP_PORT_LEN]; /* DST tcp ports of this TCP stream */ | |
146 | } tcp_port; | |
147 | tdata_psh_info_t *tdata_psh_info_head; /* Head of received TCP PSH DATA chain */ | |
148 | tdata_psh_info_t *tdata_psh_info_tail; /* Tail of received TCP PSH DATA chain */ | |
149 | uint32 last_used_time; /* The last time this tcpdata_info was used(in ms) */ | |
150 | } tcpdata_info_t; | |
151 | ||
152 | /* TCPACK SUPPRESS module */ | |
153 | typedef struct { | |
154 | int tcpack_info_cnt; | |
155 | tcpack_info_t tcpack_info_tbl[TCPACK_INFO_MAXNUM]; /* Info of TCP ACK to send */ | |
156 | int tcpdata_info_cnt; | |
157 | tcpdata_info_t tcpdata_info_tbl[TCPDATA_INFO_MAXNUM]; /* Info of received TCP DATA */ | |
158 | tdata_psh_info_t *tdata_psh_info_pool; /* Pointer to tdata_psh_info elements pool */ | |
159 | tdata_psh_info_t *tdata_psh_info_free; /* free tdata_psh_info elements chain in pool */ | |
160 | #ifdef DHDTCPACK_SUP_DBG | |
161 | int psh_info_enq_num; /* Number of free TCP PSH DATA info elements in pool */ | |
162 | #endif /* DHDTCPACK_SUP_DBG */ | |
163 | } tcpack_sup_module_t; | |
164 | ||
165 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
166 | counter_tbl_t tack_tbl = {"tcpACK", 0, 1000, 10, {0, }, 1}; | |
167 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
168 | ||
169 | static void | |
170 | _tdata_psh_info_pool_enq(tcpack_sup_module_t *tcpack_sup_mod, | |
171 | tdata_psh_info_t *tdata_psh_info) | |
172 | { | |
173 | if ((tcpack_sup_mod == NULL) || (tdata_psh_info == NULL)) { | |
174 | DHD_ERROR(("%s %d: ERROR %p %p\n", __FUNCTION__, __LINE__, | |
175 | tcpack_sup_mod, tdata_psh_info)); | |
176 | return; | |
177 | } | |
178 | ||
179 | ASSERT(tdata_psh_info->next == NULL); | |
180 | tdata_psh_info->next = tcpack_sup_mod->tdata_psh_info_free; | |
181 | tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info; | |
182 | #ifdef DHDTCPACK_SUP_DBG | |
183 | tcpack_sup_mod->psh_info_enq_num++; | |
184 | #endif | |
185 | } | |
186 | ||
187 | static tdata_psh_info_t* | |
188 | _tdata_psh_info_pool_deq(tcpack_sup_module_t *tcpack_sup_mod) | |
189 | { | |
190 | tdata_psh_info_t *tdata_psh_info = NULL; | |
191 | ||
192 | if (tcpack_sup_mod == NULL) { | |
193 | DHD_ERROR(("%s %d: ERROR %p\n", __FUNCTION__, __LINE__, | |
194 | tcpack_sup_mod)); | |
195 | return NULL; | |
196 | } | |
197 | ||
198 | tdata_psh_info = tcpack_sup_mod->tdata_psh_info_free; | |
199 | if (tdata_psh_info == NULL) | |
200 | DHD_ERROR(("%s %d: Out of tdata_disc_grp\n", __FUNCTION__, __LINE__)); | |
201 | else { | |
202 | tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info->next; | |
203 | tdata_psh_info->next = NULL; | |
204 | #ifdef DHDTCPACK_SUP_DBG | |
205 | tcpack_sup_mod->psh_info_enq_num--; | |
206 | #endif /* DHDTCPACK_SUP_DBG */ | |
207 | } | |
208 | ||
209 | return tdata_psh_info; | |
210 | } | |
211 | ||
212 | #ifdef BCMSDIO | |
213 | static int _tdata_psh_info_pool_init(dhd_pub_t *dhdp, | |
214 | tcpack_sup_module_t *tcpack_sup_mod) | |
215 | { | |
216 | tdata_psh_info_t *tdata_psh_info_pool = NULL; | |
217 | uint i; | |
218 | ||
219 | DHD_TRACE(("%s %d: Enter\n", __FUNCTION__, __LINE__)); | |
220 | ||
221 | if (tcpack_sup_mod == NULL) | |
222 | return BCME_ERROR; | |
223 | ||
224 | ASSERT(tcpack_sup_mod->tdata_psh_info_pool == NULL); | |
225 | ASSERT(tcpack_sup_mod->tdata_psh_info_free == NULL); | |
226 | ||
227 | tdata_psh_info_pool = | |
228 | MALLOC(dhdp->osh, sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM); | |
229 | ||
230 | if (tdata_psh_info_pool == NULL) | |
231 | return BCME_NOMEM; | |
232 | bzero(tdata_psh_info_pool, sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM); | |
233 | #ifdef DHDTCPACK_SUP_DBG | |
234 | tcpack_sup_mod->psh_info_enq_num = 0; | |
235 | #endif /* DHDTCPACK_SUP_DBG */ | |
236 | ||
237 | /* Enqueue newly allocated tcpdata psh info elements to the pool */ | |
238 | for (i = 0; i < TCPDATA_PSH_INFO_MAXNUM; i++) | |
239 | _tdata_psh_info_pool_enq(tcpack_sup_mod, &tdata_psh_info_pool[i]); | |
240 | ||
241 | ASSERT(tcpack_sup_mod->tdata_psh_info_free != NULL); | |
242 | tcpack_sup_mod->tdata_psh_info_pool = tdata_psh_info_pool; | |
243 | ||
244 | return BCME_OK; | |
245 | } | |
246 | ||
247 | static void _tdata_psh_info_pool_deinit(dhd_pub_t *dhdp, | |
248 | tcpack_sup_module_t *tcpack_sup_mod) | |
249 | { | |
250 | uint i; | |
251 | tdata_psh_info_t *tdata_psh_info; | |
252 | ||
253 | DHD_TRACE(("%s %d: Enter\n", __FUNCTION__, __LINE__)); | |
254 | ||
255 | if (tcpack_sup_mod == NULL) { | |
256 | DHD_ERROR(("%s %d: ERROR tcpack_sup_mod NULL!\n", | |
257 | __FUNCTION__, __LINE__)); | |
258 | return; | |
259 | } | |
260 | ||
261 | for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) { | |
262 | tcpdata_info_t *tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i]; | |
263 | /* Return tdata_psh_info elements allocated to each tcpdata_info to the pool */ | |
264 | while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) { | |
265 | tcpdata_info->tdata_psh_info_head = tdata_psh_info->next; | |
266 | tdata_psh_info->next = NULL; | |
267 | _tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info); | |
268 | } | |
269 | tcpdata_info->tdata_psh_info_tail = NULL; | |
270 | } | |
271 | #ifdef DHDTCPACK_SUP_DBG | |
272 | DHD_ERROR(("%s %d: PSH INFO ENQ %d\n", | |
273 | __FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num)); | |
274 | #endif /* DHDTCPACK_SUP_DBG */ | |
275 | ||
276 | i = 0; | |
277 | /* Be sure we recollected all tdata_psh_info elements */ | |
278 | while ((tdata_psh_info = tcpack_sup_mod->tdata_psh_info_free)) { | |
279 | tcpack_sup_mod->tdata_psh_info_free = tdata_psh_info->next; | |
280 | tdata_psh_info->next = NULL; | |
281 | i++; | |
282 | } | |
283 | ASSERT(i == TCPDATA_PSH_INFO_MAXNUM); | |
284 | MFREE(dhdp->osh, tcpack_sup_mod->tdata_psh_info_pool, | |
285 | sizeof(tdata_psh_info_t) * TCPDATA_PSH_INFO_MAXNUM); | |
286 | ||
287 | return; | |
288 | } | |
289 | #endif /* BCMSDIO */ | |
290 | ||
291 | #ifdef BCMPCIE | |
292 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
293 | static void dhd_tcpack_send(ulong data) | |
294 | #else | |
295 | static enum hrtimer_restart dhd_tcpack_send(struct hrtimer *timer) | |
296 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
297 | { | |
298 | tcpack_sup_module_t *tcpack_sup_mod; | |
299 | tcpack_info_t *cur_tbl; | |
300 | dhd_pub_t *dhdp; | |
301 | int ifidx; | |
302 | void* pkt; | |
303 | unsigned long flags; | |
304 | ||
305 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
306 | cur_tbl = (tcpack_info_t *)data; | |
307 | #else | |
308 | cur_tbl = container_of(timer, tcpack_info_t, timer.timer); | |
309 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
310 | ||
311 | if (!cur_tbl) { | |
312 | goto done; | |
313 | } | |
314 | ||
315 | dhdp = cur_tbl->dhdp; | |
316 | if (!dhdp) { | |
317 | goto done; | |
318 | } | |
319 | ||
320 | flags = dhd_os_tcpacklock(dhdp); | |
321 | ||
322 | if (unlikely(dhdp->tcpack_sup_mode != TCPACK_SUP_HOLD)) { | |
323 | dhd_os_tcpackunlock(dhdp, flags); | |
324 | goto done; | |
325 | } | |
326 | ||
327 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
328 | if (!tcpack_sup_mod) { | |
329 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", | |
330 | __FUNCTION__, __LINE__)); | |
331 | dhd_os_tcpackunlock(dhdp, flags); | |
332 | goto done; | |
333 | } | |
334 | pkt = cur_tbl->pkt_in_q; | |
335 | ifidx = cur_tbl->ifidx; | |
336 | if (!pkt) { | |
337 | dhd_os_tcpackunlock(dhdp, flags); | |
338 | goto done; | |
339 | } | |
340 | cur_tbl->pkt_in_q = NULL; | |
341 | cur_tbl->pkt_ether_hdr = NULL; | |
342 | cur_tbl->ifidx = 0; | |
343 | cur_tbl->supp_cnt = 0; | |
344 | if (--tcpack_sup_mod->tcpack_info_cnt < 0) { | |
345 | DHD_ERROR(("%s %d: ERROR!!! tcp_ack_info_cnt %d\n", | |
346 | __FUNCTION__, __LINE__, tcpack_sup_mod->tcpack_info_cnt)); | |
347 | } | |
348 | ||
349 | dhd_os_tcpackunlock(dhdp, flags); | |
350 | ||
351 | dhd_sendpkt(dhdp, ifidx, pkt); | |
352 | ||
353 | done: | |
354 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
355 | return; | |
356 | #else | |
357 | return HRTIMER_NORESTART; | |
358 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
359 | } | |
360 | #endif /* BCMPCIE */ | |
361 | ||
362 | int dhd_tcpack_suppress_set(dhd_pub_t *dhdp, uint8 mode) | |
363 | { | |
364 | int ret = BCME_OK; | |
365 | unsigned long flags; | |
366 | tcpack_sup_module_t *tcpack_sup_module; | |
367 | uint8 invalid_mode = FALSE; | |
368 | int prev_mode; | |
369 | int i = 0; | |
370 | ||
371 | flags = dhd_os_tcpacklock(dhdp); | |
372 | tcpack_sup_module = dhdp->tcpack_sup_module; | |
373 | prev_mode = dhdp->tcpack_sup_mode; | |
374 | ||
375 | if (prev_mode == mode) { | |
376 | DHD_ERROR(("%s %d: already set to %d\n", __FUNCTION__, __LINE__, mode)); | |
377 | goto exit; | |
378 | } | |
379 | ||
380 | invalid_mode |= (mode >= TCPACK_SUP_LAST_MODE); | |
381 | #ifdef BCMSDIO | |
382 | invalid_mode |= (mode == TCPACK_SUP_HOLD); | |
383 | #endif /* BCMSDIO */ | |
384 | #ifdef BCMPCIE | |
385 | invalid_mode |= ((mode == TCPACK_SUP_REPLACE) || (mode == TCPACK_SUP_DELAYTX)); | |
386 | #endif /* BCMPCIE */ | |
387 | ||
388 | if (invalid_mode) { | |
389 | DHD_ERROR(("%s %d: Invalid TCP ACK Suppress mode %d\n", | |
390 | __FUNCTION__, __LINE__, mode)); | |
391 | ret = BCME_BADARG; | |
392 | goto exit; | |
393 | } | |
394 | ||
395 | printf("%s: TCP ACK Suppress mode %d -> mode %d\n", | |
396 | __FUNCTION__, dhdp->tcpack_sup_mode, mode); | |
397 | ||
398 | /* Pre-process routines to change a new mode as per previous mode */ | |
399 | switch (prev_mode) { | |
400 | case TCPACK_SUP_OFF: | |
401 | if (tcpack_sup_module == NULL) { | |
402 | tcpack_sup_module = MALLOC(dhdp->osh, sizeof(tcpack_sup_module_t)); | |
403 | if (tcpack_sup_module == NULL) { | |
404 | DHD_ERROR(("%s[%d]: Failed to allocate the new memory for " | |
405 | "tcpack_sup_module\n", __FUNCTION__, __LINE__)); | |
406 | dhdp->tcpack_sup_mode = TCPACK_SUP_OFF; | |
407 | ret = BCME_NOMEM; | |
408 | goto exit; | |
409 | } | |
410 | dhdp->tcpack_sup_module = tcpack_sup_module; | |
411 | } | |
412 | bzero(tcpack_sup_module, sizeof(tcpack_sup_module_t)); | |
413 | break; | |
414 | #ifdef BCMSDIO | |
415 | case TCPACK_SUP_DELAYTX: | |
416 | if (tcpack_sup_module) { | |
417 | /* We won't need tdata_psh_info pool and | |
418 | * tcpddata_info_tbl anymore | |
419 | */ | |
420 | _tdata_psh_info_pool_deinit(dhdp, tcpack_sup_module); | |
421 | tcpack_sup_module->tcpdata_info_cnt = 0; | |
422 | bzero(tcpack_sup_module->tcpdata_info_tbl, | |
423 | sizeof(tcpdata_info_t) * TCPDATA_INFO_MAXNUM); | |
424 | } | |
425 | ||
426 | /* For half duplex bus interface, tx precedes rx by default */ | |
427 | if (dhdp->bus) { | |
428 | dhd_bus_set_dotxinrx(dhdp->bus, TRUE); | |
429 | } | |
430 | ||
431 | if (tcpack_sup_module == NULL) { | |
432 | DHD_ERROR(("%s[%d]: tcpack_sup_module should not be NULL\n", | |
433 | __FUNCTION__, __LINE__)); | |
434 | dhdp->tcpack_sup_mode = TCPACK_SUP_OFF; | |
435 | goto exit; | |
436 | } | |
437 | break; | |
438 | #endif /* BCMSDIO */ | |
439 | } | |
440 | ||
441 | /* Update a new mode */ | |
442 | dhdp->tcpack_sup_mode = mode; | |
443 | ||
444 | /* Process for a new mode */ | |
445 | switch (mode) { | |
446 | case TCPACK_SUP_OFF: | |
447 | ASSERT(tcpack_sup_module != NULL); | |
448 | /* Clean up timer/data structure for | |
449 | * any remaining/pending packet or timer. | |
450 | */ | |
451 | if (tcpack_sup_module) { | |
452 | /* Check if previous mode is TCAPACK_SUP_HOLD */ | |
453 | if (prev_mode == TCPACK_SUP_HOLD) { | |
454 | for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { | |
455 | tcpack_info_t *tcpack_info_tbl = | |
456 | &tcpack_sup_module->tcpack_info_tbl[i]; | |
457 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
458 | del_timer(&tcpack_info_tbl->timer); | |
459 | #else | |
460 | hrtimer_cancel(&tcpack_info_tbl->timer.timer); | |
461 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
462 | if (tcpack_info_tbl->pkt_in_q) { | |
463 | PKTFREE(dhdp->osh, | |
464 | tcpack_info_tbl->pkt_in_q, TRUE); | |
465 | tcpack_info_tbl->pkt_in_q = NULL; | |
466 | } | |
467 | } | |
468 | } | |
469 | MFREE(dhdp->osh, tcpack_sup_module, sizeof(tcpack_sup_module_t)); | |
470 | dhdp->tcpack_sup_module = NULL; | |
471 | } else { | |
472 | DHD_ERROR(("%s[%d]: tcpack_sup_module should not be NULL\n", | |
473 | __FUNCTION__, __LINE__)); | |
474 | } | |
475 | break; | |
476 | #ifdef BCMSDIO | |
477 | case TCPACK_SUP_REPLACE: | |
478 | /* There is nothing to configure for this mode */ | |
479 | break; | |
480 | case TCPACK_SUP_DELAYTX: | |
481 | ret = _tdata_psh_info_pool_init(dhdp, tcpack_sup_module); | |
482 | if (ret != BCME_OK) { | |
483 | DHD_ERROR(("%s %d: pool init fail with %d\n", | |
484 | __FUNCTION__, __LINE__, ret)); | |
485 | break; | |
486 | } | |
487 | if (dhdp->bus) { | |
488 | dhd_bus_set_dotxinrx(dhdp->bus, FALSE); | |
489 | } | |
490 | break; | |
491 | #endif /* BCMSDIO */ | |
492 | #ifdef BCMPCIE | |
493 | case TCPACK_SUP_HOLD: | |
494 | dhdp->tcpack_sup_ratio = CUSTOM_TCPACK_SUPP_RATIO; | |
495 | dhdp->tcpack_sup_delay = CUSTOM_TCPACK_DELAY_TIME; | |
496 | for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { | |
497 | tcpack_info_t *tcpack_info_tbl = | |
498 | &tcpack_sup_module->tcpack_info_tbl[i]; | |
499 | tcpack_info_tbl->dhdp = dhdp; | |
500 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
501 | init_timer_compat(&tcpack_info_tbl->timer, dhd_tcpack_send, | |
502 | tcpack_info_tbl); | |
503 | #else | |
504 | tasklet_hrtimer_init(&tcpack_info_tbl->timer, | |
505 | dhd_tcpack_send, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
506 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
507 | } | |
508 | break; | |
509 | #endif /* BCMPCIE */ | |
510 | } | |
511 | ||
512 | exit: | |
513 | dhd_os_tcpackunlock(dhdp, flags); | |
514 | return ret; | |
515 | } | |
516 | ||
517 | void | |
518 | dhd_tcpack_info_tbl_clean(dhd_pub_t *dhdp) | |
519 | { | |
520 | tcpack_sup_module_t *tcpack_sup_mod = dhdp->tcpack_sup_module; | |
521 | int i; | |
522 | unsigned long flags; | |
523 | ||
524 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF) | |
525 | goto exit; | |
526 | ||
527 | flags = dhd_os_tcpacklock(dhdp); | |
528 | ||
529 | if (!tcpack_sup_mod) { | |
530 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", | |
531 | __FUNCTION__, __LINE__)); | |
532 | dhd_os_tcpackunlock(dhdp, flags); | |
533 | goto exit; | |
534 | } | |
535 | ||
536 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) { | |
537 | for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { | |
538 | if (tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q) { | |
539 | PKTFREE(dhdp->osh, tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q, | |
540 | TRUE); | |
541 | tcpack_sup_mod->tcpack_info_tbl[i].pkt_in_q = NULL; | |
542 | tcpack_sup_mod->tcpack_info_tbl[i].pkt_ether_hdr = NULL; | |
543 | tcpack_sup_mod->tcpack_info_tbl[i].ifidx = 0; | |
544 | tcpack_sup_mod->tcpack_info_tbl[i].supp_cnt = 0; | |
545 | } | |
546 | } | |
547 | } else { | |
548 | tcpack_sup_mod->tcpack_info_cnt = 0; | |
549 | bzero(tcpack_sup_mod->tcpack_info_tbl, sizeof(tcpack_info_t) * TCPACK_INFO_MAXNUM); | |
550 | } | |
551 | ||
552 | dhd_os_tcpackunlock(dhdp, flags); | |
553 | ||
554 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_HOLD) { | |
555 | for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { | |
556 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
557 | del_timer_sync(&tcpack_sup_mod->tcpack_info_tbl[i].timer); | |
558 | #else | |
559 | hrtimer_cancel(&tcpack_sup_mod->tcpack_info_tbl[i].timer.timer); | |
560 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
561 | } | |
562 | } | |
563 | ||
564 | exit: | |
565 | return; | |
566 | } | |
567 | ||
568 | inline int dhd_tcpack_check_xmit(dhd_pub_t *dhdp, void *pkt) | |
569 | { | |
570 | uint8 i; | |
571 | tcpack_sup_module_t *tcpack_sup_mod; | |
572 | tcpack_info_t *tcpack_info_tbl; | |
573 | int tbl_cnt; | |
574 | int ret = BCME_OK; | |
575 | void *pdata; | |
576 | uint32 pktlen; | |
577 | unsigned long flags; | |
578 | ||
579 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF) | |
580 | goto exit; | |
581 | ||
582 | pdata = PKTDATA(dhdp->osh, pkt); | |
583 | pktlen = PKTLEN(dhdp->osh, pkt) - dhd_prot_hdrlen(dhdp, pdata); | |
584 | ||
585 | if (pktlen < TCPACKSZMIN || pktlen > TCPACKSZMAX) { | |
586 | DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n", | |
587 | __FUNCTION__, __LINE__, pktlen)); | |
588 | goto exit; | |
589 | } | |
590 | ||
591 | flags = dhd_os_tcpacklock(dhdp); | |
592 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
593 | ||
594 | if (!tcpack_sup_mod) { | |
595 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); | |
596 | ret = BCME_ERROR; | |
597 | dhd_os_tcpackunlock(dhdp, flags); | |
598 | goto exit; | |
599 | } | |
600 | tbl_cnt = tcpack_sup_mod->tcpack_info_cnt; | |
601 | tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl; | |
602 | ||
603 | ASSERT(tbl_cnt <= TCPACK_INFO_MAXNUM); | |
604 | ||
605 | for (i = 0; i < tbl_cnt; i++) { | |
606 | if (tcpack_info_tbl[i].pkt_in_q == pkt) { | |
607 | DHD_TRACE(("%s %d: pkt %p sent out. idx %d, tbl_cnt %d\n", | |
608 | __FUNCTION__, __LINE__, pkt, i, tbl_cnt)); | |
609 | /* This pkt is being transmitted so remove the tcp_ack_info of it. */ | |
610 | if (i < tbl_cnt - 1) { | |
611 | bcopy(&tcpack_info_tbl[tbl_cnt - 1], | |
612 | &tcpack_info_tbl[i], sizeof(tcpack_info_t)); | |
613 | } | |
614 | bzero(&tcpack_info_tbl[tbl_cnt - 1], sizeof(tcpack_info_t)); | |
615 | if (--tcpack_sup_mod->tcpack_info_cnt < 0) { | |
616 | DHD_ERROR(("%s %d: ERROR!!! tcp_ack_info_cnt %d\n", | |
617 | __FUNCTION__, __LINE__, tcpack_sup_mod->tcpack_info_cnt)); | |
618 | ret = BCME_ERROR; | |
619 | } | |
620 | break; | |
621 | } | |
622 | } | |
623 | dhd_os_tcpackunlock(dhdp, flags); | |
624 | ||
625 | exit: | |
626 | return ret; | |
627 | } | |
628 | ||
629 | static INLINE bool dhd_tcpdata_psh_acked(dhd_pub_t *dhdp, uint8 *ip_hdr, | |
630 | uint8 *tcp_hdr, uint32 tcp_ack_num) | |
631 | { | |
632 | tcpack_sup_module_t *tcpack_sup_mod; | |
633 | int i; | |
634 | tcpdata_info_t *tcpdata_info = NULL; | |
635 | tdata_psh_info_t *tdata_psh_info = NULL; | |
636 | bool ret = FALSE; | |
637 | ||
638 | if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX) | |
639 | goto exit; | |
640 | ||
641 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
642 | ||
643 | if (!tcpack_sup_mod) { | |
644 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); | |
645 | goto exit; | |
646 | } | |
647 | ||
648 | DHD_TRACE(("%s %d: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
649 | " TCP port %d %d, ack %u\n", __FUNCTION__, __LINE__, | |
650 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])), | |
651 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])), | |
652 | ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
653 | ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]), | |
654 | tcp_ack_num)); | |
655 | ||
656 | for (i = 0; i < tcpack_sup_mod->tcpdata_info_cnt; i++) { | |
657 | tcpdata_info_t *tcpdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i]; | |
658 | DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
659 | " TCP port %d %d\n", __FUNCTION__, __LINE__, i, | |
660 | IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->ip_addr.src)), | |
661 | IPV4_ADDR_TO_STR(ntoh32_ua(tcpdata_info_tmp->ip_addr.dst)), | |
662 | ntoh16_ua(tcpdata_info_tmp->tcp_port.src), | |
663 | ntoh16_ua(tcpdata_info_tmp->tcp_port.dst))); | |
664 | ||
665 | /* If either IP address or TCP port number does not match, skip. */ | |
666 | if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET], | |
667 | tcpdata_info_tmp->ip_addr.dst, IPV4_ADDR_LEN) == 0 && | |
668 | memcmp(&ip_hdr[IPV4_DEST_IP_OFFSET], | |
669 | tcpdata_info_tmp->ip_addr.src, IPV4_ADDR_LEN) == 0 && | |
670 | memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET], | |
671 | tcpdata_info_tmp->tcp_port.dst, TCP_PORT_LEN) == 0 && | |
672 | memcmp(&tcp_hdr[TCP_DEST_PORT_OFFSET], | |
673 | tcpdata_info_tmp->tcp_port.src, TCP_PORT_LEN) == 0) { | |
674 | tcpdata_info = tcpdata_info_tmp; | |
675 | break; | |
676 | } | |
677 | } | |
678 | ||
679 | if (tcpdata_info == NULL) { | |
680 | DHD_TRACE(("%s %d: no tcpdata_info!\n", __FUNCTION__, __LINE__)); | |
681 | goto exit; | |
682 | } | |
683 | ||
684 | if (tcpdata_info->tdata_psh_info_head == NULL) { | |
685 | DHD_TRACE(("%s %d: No PSH DATA to be acked!\n", __FUNCTION__, __LINE__)); | |
686 | } | |
687 | ||
688 | while ((tdata_psh_info = tcpdata_info->tdata_psh_info_head)) { | |
689 | if (IS_TCPSEQ_GE(tcp_ack_num, tdata_psh_info->end_seq)) { | |
690 | DHD_TRACE(("%s %d: PSH ACKED! %u >= %u\n", | |
691 | __FUNCTION__, __LINE__, tcp_ack_num, tdata_psh_info->end_seq)); | |
692 | tcpdata_info->tdata_psh_info_head = tdata_psh_info->next; | |
693 | tdata_psh_info->next = NULL; | |
694 | _tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info); | |
695 | ret = TRUE; | |
696 | } else | |
697 | break; | |
698 | } | |
699 | if (tdata_psh_info == NULL) | |
700 | tcpdata_info->tdata_psh_info_tail = NULL; | |
701 | ||
702 | #ifdef DHDTCPACK_SUP_DBG | |
703 | DHD_TRACE(("%s %d: PSH INFO ENQ %d\n", | |
704 | __FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num)); | |
705 | #endif /* DHDTCPACK_SUP_DBG */ | |
706 | ||
707 | exit: | |
708 | return ret; | |
709 | } | |
710 | ||
711 | bool | |
712 | dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt) | |
713 | { | |
714 | uint8 *new_ether_hdr; /* Ethernet header of the new packet */ | |
715 | uint16 new_ether_type; /* Ethernet type of the new packet */ | |
716 | uint8 *new_ip_hdr; /* IP header of the new packet */ | |
717 | uint8 *new_tcp_hdr; /* TCP header of the new packet */ | |
718 | uint32 new_ip_hdr_len; /* IP header length of the new packet */ | |
719 | uint32 cur_framelen; | |
720 | uint32 new_tcp_ack_num; /* TCP acknowledge number of the new packet */ | |
721 | uint16 new_ip_total_len; /* Total length of IP packet for the new packet */ | |
722 | uint32 new_tcp_hdr_len; /* TCP header length of the new packet */ | |
723 | tcpack_sup_module_t *tcpack_sup_mod; | |
724 | tcpack_info_t *tcpack_info_tbl; | |
725 | int i; | |
726 | bool ret = FALSE; | |
727 | bool set_dotxinrx = TRUE; | |
728 | unsigned long flags; | |
729 | ||
730 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_OFF) | |
731 | goto exit; | |
732 | ||
733 | new_ether_hdr = PKTDATA(dhdp->osh, pkt); | |
734 | cur_framelen = PKTLEN(dhdp->osh, pkt); | |
735 | ||
736 | if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) { | |
737 | DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n", | |
738 | __FUNCTION__, __LINE__, cur_framelen)); | |
739 | goto exit; | |
740 | } | |
741 | ||
742 | new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13]; | |
743 | ||
744 | if (new_ether_type != ETHER_TYPE_IP) { | |
745 | DHD_TRACE(("%s %d: Not a IP packet 0x%x\n", | |
746 | __FUNCTION__, __LINE__, new_ether_type)); | |
747 | goto exit; | |
748 | } | |
749 | ||
750 | DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type)); | |
751 | ||
752 | new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN; | |
753 | cur_framelen -= ETHER_HDR_LEN; | |
754 | ||
755 | ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN); | |
756 | ||
757 | new_ip_hdr_len = IPV4_HLEN(new_ip_hdr); | |
758 | if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) { | |
759 | DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n", | |
760 | __FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr))); | |
761 | goto exit; | |
762 | } | |
763 | ||
764 | new_tcp_hdr = new_ip_hdr + new_ip_hdr_len; | |
765 | cur_framelen -= new_ip_hdr_len; | |
766 | ||
767 | ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN); | |
768 | ||
769 | DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__)); | |
770 | ||
771 | /* is it an ack ? Allow only ACK flag, not to suppress others. */ | |
772 | if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) { | |
773 | DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n", | |
774 | __FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET])); | |
775 | goto exit; | |
776 | } | |
777 | ||
778 | new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]); | |
779 | new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]); | |
780 | ||
781 | /* This packet has TCP data, so just send */ | |
782 | if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) { | |
783 | DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__)); | |
784 | goto exit; | |
785 | } | |
786 | ||
787 | ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len); | |
788 | ||
789 | new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]); | |
790 | ||
791 | DHD_TRACE(("%s %d: TCP ACK with zero DATA length" | |
792 | " IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n", | |
793 | __FUNCTION__, __LINE__, | |
794 | IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])), | |
795 | IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])), | |
796 | ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
797 | ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
798 | ||
799 | /* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */ | |
800 | flags = dhd_os_tcpacklock(dhdp); | |
801 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
802 | counter_printlog(&tack_tbl); | |
803 | tack_tbl.cnt[0]++; | |
804 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
805 | ||
806 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
807 | tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl; | |
808 | ||
809 | if (!tcpack_sup_mod) { | |
810 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); | |
811 | ret = BCME_ERROR; | |
812 | dhd_os_tcpackunlock(dhdp, flags); | |
813 | goto exit; | |
814 | } | |
815 | ||
816 | if (dhd_tcpdata_psh_acked(dhdp, new_ip_hdr, new_tcp_hdr, new_tcp_ack_num)) { | |
817 | /* This TCPACK is ACK to TCPDATA PSH pkt, so keep set_dotxinrx TRUE */ | |
818 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
819 | tack_tbl.cnt[5]++; | |
820 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
821 | } else | |
822 | set_dotxinrx = FALSE; | |
823 | ||
824 | for (i = 0; i < tcpack_sup_mod->tcpack_info_cnt; i++) { | |
825 | void *oldpkt; /* TCPACK packet that is already in txq or DelayQ */ | |
826 | uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr; | |
827 | uint32 old_ip_hdr_len, old_tcp_hdr_len; | |
828 | uint32 old_tcpack_num; /* TCP ACK number of old TCPACK packet in Q */ | |
829 | ||
830 | if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) { | |
831 | DHD_ERROR(("%s %d: Unexpected error!! cur idx %d, ttl cnt %d\n", | |
832 | __FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt)); | |
833 | break; | |
834 | } | |
835 | ||
836 | if (PKTDATA(dhdp->osh, oldpkt) == NULL) { | |
837 | DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d, ttl cnt %d\n", | |
838 | __FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpack_info_cnt)); | |
839 | break; | |
840 | } | |
841 | ||
842 | old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr; | |
843 | old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN; | |
844 | old_ip_hdr_len = IPV4_HLEN(old_ip_hdr); | |
845 | old_tcp_hdr = old_ip_hdr + old_ip_hdr_len; | |
846 | old_tcp_hdr_len = 4 * TCP_HDRLEN(old_tcp_hdr[TCP_HLEN_OFFSET]); | |
847 | ||
848 | DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
849 | " TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i, | |
850 | IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])), | |
851 | IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])), | |
852 | ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
853 | ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
854 | ||
855 | /* If either of IP address or TCP port number does not match, skip. | |
856 | * Note that src/dst addr fields in ip header are contiguous being 8 bytes in total. | |
857 | * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total. | |
858 | */ | |
859 | if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET], | |
860 | &old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) || | |
861 | memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET], | |
862 | &old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2)) | |
863 | continue; | |
864 | ||
865 | old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]); | |
866 | ||
867 | if (IS_TCPSEQ_GT(new_tcp_ack_num, old_tcpack_num)) { | |
868 | /* New packet has higher TCP ACK number, so it replaces the old packet */ | |
869 | if (new_ip_hdr_len == old_ip_hdr_len && | |
870 | new_tcp_hdr_len == old_tcp_hdr_len) { | |
871 | ASSERT(memcmp(new_ether_hdr, old_ether_hdr, ETHER_HDR_LEN) == 0); | |
872 | bcopy(new_ip_hdr, old_ip_hdr, new_ip_total_len); | |
873 | PKTFREE(dhdp->osh, pkt, FALSE); | |
874 | DHD_TRACE(("%s %d: TCP ACK replace %u -> %u\n", | |
875 | __FUNCTION__, __LINE__, old_tcpack_num, new_tcp_ack_num)); | |
876 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
877 | tack_tbl.cnt[2]++; | |
878 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
879 | ret = TRUE; | |
880 | } else { | |
881 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
882 | tack_tbl.cnt[6]++; | |
883 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
884 | DHD_TRACE(("%s %d: lenth mismatch %d != %d || %d != %d" | |
885 | " ACK %u -> %u\n", __FUNCTION__, __LINE__, | |
886 | new_ip_hdr_len, old_ip_hdr_len, | |
887 | new_tcp_hdr_len, old_tcp_hdr_len, | |
888 | old_tcpack_num, new_tcp_ack_num)); | |
889 | } | |
890 | } else if (new_tcp_ack_num == old_tcpack_num) { | |
891 | set_dotxinrx = TRUE; | |
892 | /* TCPACK retransmission */ | |
893 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
894 | tack_tbl.cnt[3]++; | |
895 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
896 | } else { | |
897 | DHD_TRACE(("%s %d: ACK number reverse old %u(0x%p) new %u(0x%p)\n", | |
898 | __FUNCTION__, __LINE__, old_tcpack_num, oldpkt, | |
899 | new_tcp_ack_num, pkt)); | |
900 | } | |
901 | dhd_os_tcpackunlock(dhdp, flags); | |
902 | goto exit; | |
903 | } | |
904 | ||
905 | if (i == tcpack_sup_mod->tcpack_info_cnt && i < TCPACK_INFO_MAXNUM) { | |
906 | /* No TCPACK packet with the same IP addr and TCP port is found | |
907 | * in tcp_ack_info_tbl. So add this packet to the table. | |
908 | */ | |
909 | DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n", | |
910 | __FUNCTION__, __LINE__, pkt, new_ether_hdr, | |
911 | tcpack_sup_mod->tcpack_info_cnt)); | |
912 | ||
913 | tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_in_q = pkt; | |
914 | tcpack_info_tbl[tcpack_sup_mod->tcpack_info_cnt].pkt_ether_hdr = new_ether_hdr; | |
915 | tcpack_sup_mod->tcpack_info_cnt++; | |
916 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
917 | tack_tbl.cnt[1]++; | |
918 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
919 | } else { | |
920 | ASSERT(i == tcpack_sup_mod->tcpack_info_cnt); | |
921 | DHD_TRACE(("%s %d: No empty tcp ack info tbl\n", | |
922 | __FUNCTION__, __LINE__)); | |
923 | } | |
924 | dhd_os_tcpackunlock(dhdp, flags); | |
925 | ||
926 | exit: | |
927 | /* Unless TCPACK_SUP_DELAYTX, dotxinrx is alwasy TRUE, so no need to set here */ | |
928 | if (dhdp->tcpack_sup_mode == TCPACK_SUP_DELAYTX && set_dotxinrx) | |
929 | dhd_bus_set_dotxinrx(dhdp->bus, TRUE); | |
930 | ||
931 | return ret; | |
932 | } | |
933 | ||
934 | bool | |
935 | dhd_tcpdata_info_get(dhd_pub_t *dhdp, void *pkt) | |
936 | { | |
937 | uint8 *ether_hdr; /* Ethernet header of the new packet */ | |
938 | uint16 ether_type; /* Ethernet type of the new packet */ | |
939 | uint8 *ip_hdr; /* IP header of the new packet */ | |
940 | uint8 *tcp_hdr; /* TCP header of the new packet */ | |
941 | uint32 ip_hdr_len; /* IP header length of the new packet */ | |
942 | uint32 cur_framelen; | |
943 | uint16 ip_total_len; /* Total length of IP packet for the new packet */ | |
944 | uint32 tcp_hdr_len; /* TCP header length of the new packet */ | |
945 | uint32 tcp_seq_num; /* TCP sequence number of the new packet */ | |
946 | uint16 tcp_data_len; /* TCP DATA length that excludes IP and TCP headers */ | |
947 | uint32 end_tcp_seq_num; /* TCP seq number of the last byte in the new packet */ | |
948 | tcpack_sup_module_t *tcpack_sup_mod; | |
949 | tcpdata_info_t *tcpdata_info = NULL; | |
950 | tdata_psh_info_t *tdata_psh_info; | |
951 | ||
952 | int i; | |
953 | bool ret = FALSE; | |
954 | unsigned long flags; | |
955 | ||
956 | if (dhdp->tcpack_sup_mode != TCPACK_SUP_DELAYTX) | |
957 | goto exit; | |
958 | ||
959 | ether_hdr = PKTDATA(dhdp->osh, pkt); | |
960 | cur_framelen = PKTLEN(dhdp->osh, pkt); | |
961 | ||
962 | ether_type = ether_hdr[12] << 8 | ether_hdr[13]; | |
963 | ||
964 | if (ether_type != ETHER_TYPE_IP) { | |
965 | DHD_TRACE(("%s %d: Not a IP packet 0x%x\n", | |
966 | __FUNCTION__, __LINE__, ether_type)); | |
967 | goto exit; | |
968 | } | |
969 | ||
970 | DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, ether_type)); | |
971 | ||
972 | ip_hdr = ether_hdr + ETHER_HDR_LEN; | |
973 | cur_framelen -= ETHER_HDR_LEN; | |
974 | ||
975 | ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN); | |
976 | ||
977 | ip_hdr_len = IPV4_HLEN(ip_hdr); | |
978 | if (IP_VER(ip_hdr) != IP_VER_4 || IPV4_PROT(ip_hdr) != IP_PROT_TCP) { | |
979 | DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n", | |
980 | __FUNCTION__, __LINE__, IP_VER(ip_hdr), IPV4_PROT(ip_hdr))); | |
981 | goto exit; | |
982 | } | |
983 | ||
984 | tcp_hdr = ip_hdr + ip_hdr_len; | |
985 | cur_framelen -= ip_hdr_len; | |
986 | ||
987 | ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN); | |
988 | ||
989 | DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__)); | |
990 | ||
991 | ip_total_len = ntoh16_ua(&ip_hdr[IPV4_PKTLEN_OFFSET]); | |
992 | tcp_hdr_len = 4 * TCP_HDRLEN(tcp_hdr[TCP_HLEN_OFFSET]); | |
993 | ||
994 | /* This packet is mere TCP ACK, so do nothing */ | |
995 | if (ip_total_len == ip_hdr_len + tcp_hdr_len) { | |
996 | DHD_TRACE(("%s %d: Do nothing for no data TCP ACK\n", __FUNCTION__, __LINE__)); | |
997 | goto exit; | |
998 | } | |
999 | ||
1000 | ASSERT(ip_total_len > ip_hdr_len + tcp_hdr_len); | |
1001 | ||
1002 | if ((tcp_hdr[TCP_FLAGS_OFFSET] & TCP_FLAG_PSH) == 0) { | |
1003 | DHD_TRACE(("%s %d: Not interested TCP DATA packet\n", __FUNCTION__, __LINE__)); | |
1004 | goto exit; | |
1005 | } | |
1006 | ||
1007 | DHD_TRACE(("%s %d: TCP DATA with nonzero DATA length" | |
1008 | " IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d, flag 0x%x\n", | |
1009 | __FUNCTION__, __LINE__, | |
1010 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])), | |
1011 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])), | |
1012 | ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
1013 | ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]), | |
1014 | tcp_hdr[TCP_FLAGS_OFFSET])); | |
1015 | ||
1016 | flags = dhd_os_tcpacklock(dhdp); | |
1017 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
1018 | ||
1019 | if (!tcpack_sup_mod) { | |
1020 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); | |
1021 | ret = BCME_ERROR; | |
1022 | dhd_os_tcpackunlock(dhdp, flags); | |
1023 | goto exit; | |
1024 | } | |
1025 | ||
1026 | /* Look for tcpdata_info that has the same ip src/dst addrs and tcp src/dst ports */ | |
1027 | i = 0; | |
1028 | while (i < tcpack_sup_mod->tcpdata_info_cnt) { | |
1029 | tcpdata_info_t *tdata_info_tmp = &tcpack_sup_mod->tcpdata_info_tbl[i]; | |
1030 | uint32 now_in_ms = OSL_SYSUPTIME(); | |
1031 | DHD_TRACE(("%s %d: data info[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
1032 | " TCP port %d %d\n", __FUNCTION__, __LINE__, i, | |
1033 | IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->ip_addr.src)), | |
1034 | IPV4_ADDR_TO_STR(ntoh32_ua(tdata_info_tmp->ip_addr.dst)), | |
1035 | ntoh16_ua(tdata_info_tmp->tcp_port.src), | |
1036 | ntoh16_ua(tdata_info_tmp->tcp_port.dst))); | |
1037 | ||
1038 | /* If both IP address and TCP port number match, we found it so break. | |
1039 | * Note that src/dst addr fields in ip header are contiguous being 8 bytes in total. | |
1040 | * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total. | |
1041 | */ | |
1042 | if (memcmp(&ip_hdr[IPV4_SRC_IP_OFFSET], | |
1043 | (void *)&tdata_info_tmp->ip_addr, IPV4_ADDR_LEN * 2) == 0 && | |
1044 | memcmp(&tcp_hdr[TCP_SRC_PORT_OFFSET], | |
1045 | (void *)&tdata_info_tmp->tcp_port, TCP_PORT_LEN * 2) == 0) { | |
1046 | tcpdata_info = tdata_info_tmp; | |
1047 | tcpdata_info->last_used_time = now_in_ms; | |
1048 | break; | |
1049 | } | |
1050 | ||
1051 | if (now_in_ms - tdata_info_tmp->last_used_time > TCPDATA_INFO_TIMEOUT) { | |
1052 | tdata_psh_info_t *tdata_psh_info_tmp; | |
1053 | tcpdata_info_t *last_tdata_info; | |
1054 | ||
1055 | while ((tdata_psh_info_tmp = tdata_info_tmp->tdata_psh_info_head)) { | |
1056 | tdata_info_tmp->tdata_psh_info_head = tdata_psh_info_tmp->next; | |
1057 | tdata_psh_info_tmp->next = NULL; | |
1058 | DHD_TRACE(("%s %d: Clean tdata_psh_info(end_seq %u)!\n", | |
1059 | __FUNCTION__, __LINE__, tdata_psh_info_tmp->end_seq)); | |
1060 | _tdata_psh_info_pool_enq(tcpack_sup_mod, tdata_psh_info_tmp); | |
1061 | } | |
1062 | #ifdef DHDTCPACK_SUP_DBG | |
1063 | DHD_ERROR(("%s %d: PSH INFO ENQ %d\n", | |
1064 | __FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num)); | |
1065 | #endif /* DHDTCPACK_SUP_DBG */ | |
1066 | tcpack_sup_mod->tcpdata_info_cnt--; | |
1067 | ASSERT(tcpack_sup_mod->tcpdata_info_cnt >= 0); | |
1068 | ||
1069 | last_tdata_info = | |
1070 | &tcpack_sup_mod->tcpdata_info_tbl[tcpack_sup_mod->tcpdata_info_cnt]; | |
1071 | if (i < tcpack_sup_mod->tcpdata_info_cnt) { | |
1072 | ASSERT(last_tdata_info != tdata_info_tmp); | |
1073 | bcopy(last_tdata_info, tdata_info_tmp, sizeof(tcpdata_info_t)); | |
1074 | } | |
1075 | bzero(last_tdata_info, sizeof(tcpdata_info_t)); | |
1076 | DHD_INFO(("%s %d: tcpdata_info(idx %d) is aged out. ttl cnt is now %d\n", | |
1077 | __FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt)); | |
1078 | /* Don't increase "i" here, so that the prev last tcpdata_info is checked */ | |
1079 | } else | |
1080 | i++; | |
1081 | } | |
1082 | ||
1083 | tcp_seq_num = ntoh32_ua(&tcp_hdr[TCP_SEQ_NUM_OFFSET]); | |
1084 | tcp_data_len = ip_total_len - ip_hdr_len - tcp_hdr_len; | |
1085 | end_tcp_seq_num = tcp_seq_num + tcp_data_len; | |
1086 | ||
1087 | if (tcpdata_info == NULL) { | |
1088 | ASSERT(i == tcpack_sup_mod->tcpdata_info_cnt); | |
1089 | if (i >= TCPDATA_INFO_MAXNUM) { | |
1090 | DHD_TRACE(("%s %d: tcp_data_info_tbl FULL! %d %d" | |
1091 | " IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n", | |
1092 | __FUNCTION__, __LINE__, i, tcpack_sup_mod->tcpdata_info_cnt, | |
1093 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])), | |
1094 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])), | |
1095 | ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
1096 | ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
1097 | dhd_os_tcpackunlock(dhdp, flags); | |
1098 | goto exit; | |
1099 | } | |
1100 | tcpdata_info = &tcpack_sup_mod->tcpdata_info_tbl[i]; | |
1101 | ||
1102 | /* No TCP flow with the same IP addr and TCP port is found | |
1103 | * in tcp_data_info_tbl. So add this flow to the table. | |
1104 | */ | |
1105 | DHD_INFO(("%s %d: Add data info to tbl[%d]: IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
1106 | " TCP port %d %d\n", | |
1107 | __FUNCTION__, __LINE__, tcpack_sup_mod->tcpdata_info_cnt, | |
1108 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_SRC_IP_OFFSET])), | |
1109 | IPV4_ADDR_TO_STR(ntoh32_ua(&ip_hdr[IPV4_DEST_IP_OFFSET])), | |
1110 | ntoh16_ua(&tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
1111 | ntoh16_ua(&tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
1112 | /* Note that src/dst addr fields in ip header are contiguous being 8 bytes in total. | |
1113 | * Also, src/dst port fields in TCP header are contiguous being 4 bytes in total. | |
1114 | */ | |
1115 | bcopy(&ip_hdr[IPV4_SRC_IP_OFFSET], (void *)&tcpdata_info->ip_addr, | |
1116 | IPV4_ADDR_LEN * 2); | |
1117 | bcopy(&tcp_hdr[TCP_SRC_PORT_OFFSET], (void *)&tcpdata_info->tcp_port, | |
1118 | TCP_PORT_LEN * 2); | |
1119 | ||
1120 | tcpdata_info->last_used_time = OSL_SYSUPTIME(); | |
1121 | tcpack_sup_mod->tcpdata_info_cnt++; | |
1122 | } | |
1123 | ||
1124 | ASSERT(tcpdata_info != NULL); | |
1125 | ||
1126 | tdata_psh_info = _tdata_psh_info_pool_deq(tcpack_sup_mod); | |
1127 | #ifdef DHDTCPACK_SUP_DBG | |
1128 | DHD_TRACE(("%s %d: PSH INFO ENQ %d\n", | |
1129 | __FUNCTION__, __LINE__, tcpack_sup_mod->psh_info_enq_num)); | |
1130 | #endif /* DHDTCPACK_SUP_DBG */ | |
1131 | ||
1132 | if (tdata_psh_info == NULL) { | |
1133 | DHD_ERROR(("%s %d: No more free tdata_psh_info!!\n", __FUNCTION__, __LINE__)); | |
1134 | ret = BCME_ERROR; | |
1135 | dhd_os_tcpackunlock(dhdp, flags); | |
1136 | goto exit; | |
1137 | } | |
1138 | tdata_psh_info->end_seq = end_tcp_seq_num; | |
1139 | ||
1140 | #if defined(DEBUG_COUNTER) && defined(DHDTCPACK_SUP_DBG) | |
1141 | tack_tbl.cnt[4]++; | |
1142 | #endif /* DEBUG_COUNTER && DHDTCPACK_SUP_DBG */ | |
1143 | ||
1144 | DHD_TRACE(("%s %d: TCP PSH DATA recvd! end seq %u\n", | |
1145 | __FUNCTION__, __LINE__, tdata_psh_info->end_seq)); | |
1146 | ||
1147 | ASSERT(tdata_psh_info->next == NULL); | |
1148 | ||
1149 | if (tcpdata_info->tdata_psh_info_head == NULL) | |
1150 | tcpdata_info->tdata_psh_info_head = tdata_psh_info; | |
1151 | else { | |
1152 | ASSERT(tcpdata_info->tdata_psh_info_tail); | |
1153 | tcpdata_info->tdata_psh_info_tail->next = tdata_psh_info; | |
1154 | } | |
1155 | tcpdata_info->tdata_psh_info_tail = tdata_psh_info; | |
1156 | ||
1157 | dhd_os_tcpackunlock(dhdp, flags); | |
1158 | ||
1159 | exit: | |
1160 | return ret; | |
1161 | } | |
1162 | ||
1163 | bool | |
1164 | dhd_tcpack_hold(dhd_pub_t *dhdp, void *pkt, int ifidx) | |
1165 | { | |
1166 | uint8 *new_ether_hdr; /* Ethernet header of the new packet */ | |
1167 | uint16 new_ether_type; /* Ethernet type of the new packet */ | |
1168 | uint8 *new_ip_hdr; /* IP header of the new packet */ | |
1169 | uint8 *new_tcp_hdr; /* TCP header of the new packet */ | |
1170 | uint32 new_ip_hdr_len; /* IP header length of the new packet */ | |
1171 | uint32 cur_framelen; | |
1172 | uint32 new_tcp_ack_num; /* TCP acknowledge number of the new packet */ | |
1173 | uint16 new_ip_total_len; /* Total length of IP packet for the new packet */ | |
1174 | uint32 new_tcp_hdr_len; /* TCP header length of the new packet */ | |
1175 | tcpack_sup_module_t *tcpack_sup_mod; | |
1176 | tcpack_info_t *tcpack_info_tbl; | |
1177 | int i, free_slot = TCPACK_INFO_MAXNUM; | |
1178 | bool hold = FALSE; | |
1179 | unsigned long flags; | |
1180 | ||
1181 | if (dhdp->tcpack_sup_mode != TCPACK_SUP_HOLD) { | |
1182 | goto exit; | |
1183 | } | |
1184 | ||
1185 | if (dhdp->tcpack_sup_ratio == 1) { | |
1186 | goto exit; | |
1187 | } | |
1188 | ||
1189 | new_ether_hdr = PKTDATA(dhdp->osh, pkt); | |
1190 | cur_framelen = PKTLEN(dhdp->osh, pkt); | |
1191 | ||
1192 | if (cur_framelen < TCPACKSZMIN || cur_framelen > TCPACKSZMAX) { | |
1193 | DHD_TRACE(("%s %d: Too short or long length %d to be TCP ACK\n", | |
1194 | __FUNCTION__, __LINE__, cur_framelen)); | |
1195 | goto exit; | |
1196 | } | |
1197 | ||
1198 | new_ether_type = new_ether_hdr[12] << 8 | new_ether_hdr[13]; | |
1199 | ||
1200 | if (new_ether_type != ETHER_TYPE_IP) { | |
1201 | DHD_TRACE(("%s %d: Not a IP packet 0x%x\n", | |
1202 | __FUNCTION__, __LINE__, new_ether_type)); | |
1203 | goto exit; | |
1204 | } | |
1205 | ||
1206 | DHD_TRACE(("%s %d: IP pkt! 0x%x\n", __FUNCTION__, __LINE__, new_ether_type)); | |
1207 | ||
1208 | new_ip_hdr = new_ether_hdr + ETHER_HDR_LEN; | |
1209 | cur_framelen -= ETHER_HDR_LEN; | |
1210 | ||
1211 | ASSERT(cur_framelen >= IPV4_MIN_HEADER_LEN); | |
1212 | ||
1213 | new_ip_hdr_len = IPV4_HLEN(new_ip_hdr); | |
1214 | if (IP_VER(new_ip_hdr) != IP_VER_4 || IPV4_PROT(new_ip_hdr) != IP_PROT_TCP) { | |
1215 | DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n", | |
1216 | __FUNCTION__, __LINE__, IP_VER(new_ip_hdr), IPV4_PROT(new_ip_hdr))); | |
1217 | goto exit; | |
1218 | } | |
1219 | ||
1220 | new_tcp_hdr = new_ip_hdr + new_ip_hdr_len; | |
1221 | cur_framelen -= new_ip_hdr_len; | |
1222 | ||
1223 | ASSERT(cur_framelen >= TCP_MIN_HEADER_LEN); | |
1224 | ||
1225 | DHD_TRACE(("%s %d: TCP pkt!\n", __FUNCTION__, __LINE__)); | |
1226 | ||
1227 | /* is it an ack ? Allow only ACK flag, not to suppress others. */ | |
1228 | if (new_tcp_hdr[TCP_FLAGS_OFFSET] != TCP_FLAG_ACK) { | |
1229 | DHD_TRACE(("%s %d: Do not touch TCP flag 0x%x\n", | |
1230 | __FUNCTION__, __LINE__, new_tcp_hdr[TCP_FLAGS_OFFSET])); | |
1231 | goto exit; | |
1232 | } | |
1233 | ||
1234 | new_ip_total_len = ntoh16_ua(&new_ip_hdr[IPV4_PKTLEN_OFFSET]); | |
1235 | new_tcp_hdr_len = 4 * TCP_HDRLEN(new_tcp_hdr[TCP_HLEN_OFFSET]); | |
1236 | ||
1237 | /* This packet has TCP data, so just send */ | |
1238 | if (new_ip_total_len > new_ip_hdr_len + new_tcp_hdr_len) { | |
1239 | DHD_TRACE(("%s %d: Do nothing for TCP DATA\n", __FUNCTION__, __LINE__)); | |
1240 | goto exit; | |
1241 | } | |
1242 | ||
1243 | ASSERT(new_ip_total_len == new_ip_hdr_len + new_tcp_hdr_len); | |
1244 | ||
1245 | new_tcp_ack_num = ntoh32_ua(&new_tcp_hdr[TCP_ACK_NUM_OFFSET]); | |
1246 | ||
1247 | DHD_TRACE(("%s %d: TCP ACK with zero DATA length" | |
1248 | " IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR" TCP port %d %d\n", | |
1249 | __FUNCTION__, __LINE__, | |
1250 | IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_SRC_IP_OFFSET])), | |
1251 | IPV4_ADDR_TO_STR(ntoh32_ua(&new_ip_hdr[IPV4_DEST_IP_OFFSET])), | |
1252 | ntoh16_ua(&new_tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
1253 | ntoh16_ua(&new_tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
1254 | ||
1255 | /* Look for tcp_ack_info that has the same ip src/dst addrs and tcp src/dst ports */ | |
1256 | flags = dhd_os_tcpacklock(dhdp); | |
1257 | ||
1258 | tcpack_sup_mod = dhdp->tcpack_sup_module; | |
1259 | tcpack_info_tbl = tcpack_sup_mod->tcpack_info_tbl; | |
1260 | ||
1261 | if (!tcpack_sup_mod) { | |
1262 | DHD_ERROR(("%s %d: tcpack suppress module NULL!!\n", __FUNCTION__, __LINE__)); | |
1263 | dhd_os_tcpackunlock(dhdp, flags); | |
1264 | goto exit; | |
1265 | } | |
1266 | ||
1267 | hold = TRUE; | |
1268 | ||
1269 | for (i = 0; i < TCPACK_INFO_MAXNUM; i++) { | |
1270 | void *oldpkt; /* TCPACK packet that is already in txq or DelayQ */ | |
1271 | uint8 *old_ether_hdr, *old_ip_hdr, *old_tcp_hdr; | |
1272 | uint32 old_ip_hdr_len; | |
1273 | uint32 old_tcpack_num; /* TCP ACK number of old TCPACK packet in Q */ | |
1274 | ||
1275 | if ((oldpkt = tcpack_info_tbl[i].pkt_in_q) == NULL) { | |
1276 | if (free_slot == TCPACK_INFO_MAXNUM) { | |
1277 | free_slot = i; | |
1278 | } | |
1279 | continue; | |
1280 | } | |
1281 | ||
1282 | if (PKTDATA(dhdp->osh, oldpkt) == NULL) { | |
1283 | DHD_ERROR(("%s %d: oldpkt data NULL!! cur idx %d\n", | |
1284 | __FUNCTION__, __LINE__, i)); | |
1285 | hold = FALSE; | |
1286 | dhd_os_tcpackunlock(dhdp, flags); | |
1287 | goto exit; | |
1288 | } | |
1289 | ||
1290 | old_ether_hdr = tcpack_info_tbl[i].pkt_ether_hdr; | |
1291 | old_ip_hdr = old_ether_hdr + ETHER_HDR_LEN; | |
1292 | old_ip_hdr_len = IPV4_HLEN(old_ip_hdr); | |
1293 | old_tcp_hdr = old_ip_hdr + old_ip_hdr_len; | |
1294 | ||
1295 | DHD_TRACE(("%s %d: oldpkt %p[%d], IP addr "IPV4_ADDR_STR" "IPV4_ADDR_STR | |
1296 | " TCP port %d %d\n", __FUNCTION__, __LINE__, oldpkt, i, | |
1297 | IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_SRC_IP_OFFSET])), | |
1298 | IPV4_ADDR_TO_STR(ntoh32_ua(&old_ip_hdr[IPV4_DEST_IP_OFFSET])), | |
1299 | ntoh16_ua(&old_tcp_hdr[TCP_SRC_PORT_OFFSET]), | |
1300 | ntoh16_ua(&old_tcp_hdr[TCP_DEST_PORT_OFFSET]))); | |
1301 | ||
1302 | /* If either of IP address or TCP port number does not match, skip. */ | |
1303 | if (memcmp(&new_ip_hdr[IPV4_SRC_IP_OFFSET], | |
1304 | &old_ip_hdr[IPV4_SRC_IP_OFFSET], IPV4_ADDR_LEN * 2) || | |
1305 | memcmp(&new_tcp_hdr[TCP_SRC_PORT_OFFSET], | |
1306 | &old_tcp_hdr[TCP_SRC_PORT_OFFSET], TCP_PORT_LEN * 2)) { | |
1307 | continue; | |
1308 | } | |
1309 | ||
1310 | old_tcpack_num = ntoh32_ua(&old_tcp_hdr[TCP_ACK_NUM_OFFSET]); | |
1311 | ||
1312 | if (IS_TCPSEQ_GE(new_tcp_ack_num, old_tcpack_num)) { | |
1313 | tcpack_info_tbl[i].supp_cnt++; | |
1314 | if (tcpack_info_tbl[i].supp_cnt >= dhdp->tcpack_sup_ratio) { | |
1315 | tcpack_info_tbl[i].pkt_in_q = NULL; | |
1316 | tcpack_info_tbl[i].pkt_ether_hdr = NULL; | |
1317 | tcpack_info_tbl[i].ifidx = 0; | |
1318 | tcpack_info_tbl[i].supp_cnt = 0; | |
1319 | hold = FALSE; | |
1320 | } else { | |
1321 | tcpack_info_tbl[i].pkt_in_q = pkt; | |
1322 | tcpack_info_tbl[i].pkt_ether_hdr = new_ether_hdr; | |
1323 | tcpack_info_tbl[i].ifidx = ifidx; | |
1324 | } | |
1325 | PKTFREE(dhdp->osh, oldpkt, TRUE); | |
1326 | } else { | |
1327 | PKTFREE(dhdp->osh, pkt, TRUE); | |
1328 | } | |
1329 | dhd_os_tcpackunlock(dhdp, flags); | |
1330 | ||
1331 | if (!hold) { | |
1332 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
1333 | del_timer_sync(&tcpack_info_tbl[i].timer); | |
1334 | #else | |
1335 | hrtimer_cancel(&tcpack_sup_mod->tcpack_info_tbl[i].timer.timer); | |
1336 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
1337 | } | |
1338 | goto exit; | |
1339 | } | |
1340 | ||
1341 | if (free_slot < TCPACK_INFO_MAXNUM) { | |
1342 | /* No TCPACK packet with the same IP addr and TCP port is found | |
1343 | * in tcp_ack_info_tbl. So add this packet to the table. | |
1344 | */ | |
1345 | DHD_TRACE(("%s %d: Add pkt 0x%p(ether_hdr 0x%p) to tbl[%d]\n", | |
1346 | __FUNCTION__, __LINE__, pkt, new_ether_hdr, | |
1347 | free_slot)); | |
1348 | ||
1349 | tcpack_info_tbl[free_slot].pkt_in_q = pkt; | |
1350 | tcpack_info_tbl[free_slot].pkt_ether_hdr = new_ether_hdr; | |
1351 | tcpack_info_tbl[free_slot].ifidx = ifidx; | |
1352 | tcpack_info_tbl[free_slot].supp_cnt = 1; | |
1353 | #ifndef TCPACK_SUPPRESS_HOLD_HRT | |
1354 | mod_timer(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer, | |
1355 | jiffies + msecs_to_jiffies(dhdp->tcpack_sup_delay)); | |
1356 | #else | |
1357 | tasklet_hrtimer_start(&tcpack_sup_mod->tcpack_info_tbl[free_slot].timer, | |
1358 | ktime_set(0, dhdp->tcpack_sup_delay*1000000), | |
1359 | HRTIMER_MODE_REL); | |
1360 | #endif /* TCPACK_SUPPRESS_HOLD_HRT */ | |
1361 | tcpack_sup_mod->tcpack_info_cnt++; | |
1362 | } else { | |
1363 | DHD_TRACE(("%s %d: No empty tcp ack info tbl\n", | |
1364 | __FUNCTION__, __LINE__)); | |
1365 | } | |
1366 | dhd_os_tcpackunlock(dhdp, flags); | |
1367 | ||
1368 | exit: | |
1369 | return hold; | |
1370 | } | |
1371 | #endif /* DHDTCPACK_SUPPRESS */ | |
1372 | ||
1373 | #ifdef DHDTCPSYNC_FLOOD_BLK | |
1374 | tcp_hdr_flag_t | |
1375 | dhd_tcpdata_get_flag(dhd_pub_t *dhdp, void *pkt) | |
1376 | { | |
1377 | uint8 *ether_hdr; /* Ethernet header of the new packet */ | |
1378 | uint16 ether_type; /* Ethernet type of the new packet */ | |
1379 | uint8 *ip_hdr; /* IP header of the new packet */ | |
1380 | uint8 *tcp_hdr; /* TCP header of the new packet */ | |
1381 | uint32 ip_hdr_len; /* IP header length of the new packet */ | |
1382 | uint32 cur_framelen; | |
1383 | uint8 flags; | |
1384 | ||
1385 | ether_hdr = PKTDATA(dhdp->osh, pkt); | |
1386 | cur_framelen = PKTLEN(dhdp->osh, pkt); | |
1387 | ||
1388 | ether_type = ether_hdr[12] << 8 | ether_hdr[13]; | |
1389 | ||
1390 | if (ether_type != ETHER_TYPE_IP) { | |
1391 | DHD_TRACE(("%s %d: Not a IP packet 0x%x\n", | |
1392 | __FUNCTION__, __LINE__, ether_type)); | |
1393 | return FLAG_OTHERS; | |
1394 | } | |
1395 | ||
1396 | ip_hdr = ether_hdr + ETHER_HDR_LEN; | |
1397 | cur_framelen -= ETHER_HDR_LEN; | |
1398 | ||
1399 | if (cur_framelen < IPV4_MIN_HEADER_LEN) { | |
1400 | return FLAG_OTHERS; | |
1401 | } | |
1402 | ||
1403 | ip_hdr_len = IPV4_HLEN(ip_hdr); | |
1404 | if (IP_VER(ip_hdr) != IP_VER_4 || IPV4_PROT(ip_hdr) != IP_PROT_TCP) { | |
1405 | DHD_TRACE(("%s %d: Not IPv4 nor TCP! ip ver %d, prot %d\n", | |
1406 | __FUNCTION__, __LINE__, IP_VER(ip_hdr), IPV4_PROT(ip_hdr))); | |
1407 | return FLAG_OTHERS; | |
1408 | } | |
1409 | ||
1410 | tcp_hdr = ip_hdr + ip_hdr_len; | |
1411 | ||
1412 | flags = (uint8)tcp_hdr[TCP_FLAGS_OFFSET]; | |
1413 | ||
1414 | if (flags & TCP_FLAG_SYN) { | |
1415 | if (flags & TCP_FLAG_ACK) { | |
1416 | return FLAG_SYNCACK; | |
1417 | } | |
1418 | return FLAG_SYNC; | |
1419 | } | |
1420 | return FLAG_OTHERS; | |
1421 | } | |
1422 | #endif /* DHDTCPSYNC_FLOOD_BLK */ |