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