blob: 362616c71f2d5826464fcdfa3f2dbef38486bbdc [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2011, 2013-2015 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28#include <cdf_nbuf.h> /* cdf_nbuf_t */
29
30#include <ol_htt_rx_api.h> /* htt_rx_pn_t, etc. */
31#include <ol_ctrl_txrx_api.h> /* ol_rx_err */
32
33#include <ol_txrx_internal.h> /* ol_rx_mpdu_list_next */
34#include <ol_txrx_types.h> /* ol_txrx_vdev_t, etc. */
35#include <ol_rx_pn.h> /* our own defs */
36#include <ol_rx_fwd.h> /* ol_rx_fwd_check */
37#include <ol_rx.h> /* ol_rx_deliver */
38
39/* add the MSDUs from this MPDU to the list of good frames */
40#define ADD_MPDU_TO_LIST(head, tail, mpdu, mpdu_tail) do { \
41 if (!head) { \
42 head = mpdu; \
43 } else { \
44 cdf_nbuf_set_next(tail, mpdu); \
45 } \
46 tail = mpdu_tail; \
47 } while (0)
48
49int ol_rx_pn_cmp24(union htt_rx_pn_t *new_pn,
50 union htt_rx_pn_t *old_pn, int is_unicast, int opmode)
51{
52 int rc = ((new_pn->pn24 & 0xffffff) <= (old_pn->pn24 & 0xffffff));
53 return rc;
54}
55
56int ol_rx_pn_cmp48(union htt_rx_pn_t *new_pn,
57 union htt_rx_pn_t *old_pn, int is_unicast, int opmode)
58{
59 int rc = ((new_pn->pn48 & 0xffffffffffffULL) <=
60 (old_pn->pn48 & 0xffffffffffffULL));
61 return rc;
62}
63
64int ol_rx_pn_wapi_cmp(union htt_rx_pn_t *new_pn,
65 union htt_rx_pn_t *old_pn, int is_unicast, int opmode)
66{
67 int pn_is_replay = 0;
68
69 if (new_pn->pn128[1] == old_pn->pn128[1])
70 pn_is_replay = (new_pn->pn128[0] <= old_pn->pn128[0]);
71 else
72 pn_is_replay = (new_pn->pn128[1] < old_pn->pn128[1]);
73
74 if (is_unicast) {
75 if (opmode == wlan_op_mode_ap)
76 pn_is_replay |= ((new_pn->pn128[0] & 0x1ULL) != 0);
77 else
78 pn_is_replay |= ((new_pn->pn128[0] & 0x1ULL) != 1);
79 }
80 return pn_is_replay;
81}
82
83cdf_nbuf_t
84ol_rx_pn_check_base(struct ol_txrx_vdev_t *vdev,
85 struct ol_txrx_peer_t *peer,
86 unsigned tid, cdf_nbuf_t msdu_list)
87{
88 struct ol_txrx_pdev_t *pdev = vdev->pdev;
89 union htt_rx_pn_t *last_pn;
90 cdf_nbuf_t out_list_head = NULL;
91 cdf_nbuf_t out_list_tail = NULL;
92 cdf_nbuf_t mpdu;
93 int index; /* unicast vs. multicast */
94 int pn_len;
95 void *rx_desc;
96 int last_pn_valid;
97
98 /* Make sure host pn check is not redundant */
99 if (cdf_atomic_read(&peer->fw_pn_check))
100 return msdu_list;
101
102 /* First, check whether the PN check applies */
103 rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, msdu_list);
104 cdf_assert(htt_rx_msdu_has_wlan_mcast_flag(pdev->htt_pdev, rx_desc));
105 index = htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc) ?
106 txrx_sec_mcast : txrx_sec_ucast;
107 pn_len = pdev->rx_pn[peer->security[index].sec_type].len;
108 if (pn_len == 0)
109 return msdu_list;
110
111 last_pn_valid = peer->tids_last_pn_valid[tid];
112 last_pn = &peer->tids_last_pn[tid];
113 mpdu = msdu_list;
114 while (mpdu) {
115 cdf_nbuf_t mpdu_tail, next_mpdu;
116 union htt_rx_pn_t new_pn;
117 int pn_is_replay = 0;
118
119 rx_desc = htt_rx_msdu_desc_retrieve(pdev->htt_pdev, mpdu);
120
121 /*
122 * Find the last MSDU within this MPDU, and
123 * the find the first MSDU within the next MPDU.
124 */
125 ol_rx_mpdu_list_next(pdev, mpdu, &mpdu_tail, &next_mpdu);
126
127 /* Don't check the PN replay for non-encrypted frames */
128 if (!htt_rx_mpdu_is_encrypted(pdev->htt_pdev, rx_desc)) {
129 ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu,
130 mpdu_tail);
131 mpdu = next_mpdu;
132 continue;
133 }
134
135 /* retrieve PN from rx descriptor */
136 htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &new_pn, pn_len);
137
138 /* if there was no prior PN, there's nothing to check */
139 if (last_pn_valid) {
140 pn_is_replay =
141 pdev->rx_pn[peer->security[index].sec_type].
142 cmp(&new_pn, last_pn, index == txrx_sec_ucast,
143 vdev->opmode);
144 } else {
145 last_pn_valid = peer->tids_last_pn_valid[tid] = 1;
146 }
147
148 if (pn_is_replay) {
149 cdf_nbuf_t msdu;
150 static uint32_t last_pncheck_print_time /* = 0 */;
151 int log_level;
152 uint32_t current_time_ms;
153
154 /*
155 * This MPDU failed the PN check:
156 * 1. notify the control SW of the PN failure
157 * (so countermeasures can be taken, if necessary)
158 * 2. Discard all the MSDUs from this MPDU.
159 */
160 msdu = mpdu;
161 current_time_ms =
162 cdf_system_ticks_to_msecs(cdf_system_ticks());
163 if (TXRX_PN_CHECK_FAILURE_PRINT_PERIOD_MS <
164 (current_time_ms - last_pncheck_print_time)) {
165 last_pncheck_print_time = current_time_ms;
166 log_level = TXRX_PRINT_LEVEL_WARN;
167 } else {
168 log_level = TXRX_PRINT_LEVEL_INFO2;
169 }
170
171 TXRX_PRINT(log_level,
172 "PN check failed - TID %d, peer %p "
173 "(%02x:%02x:%02x:%02x:%02x:%02x) %s\n"
174 " old PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
175 " new PN (u64 x2)= 0x%08llx %08llx (LSBs = %lld)\n"
176 " new seq num = %d\n",
177 tid, peer,
178 peer->mac_addr.raw[0], peer->mac_addr.raw[1],
179 peer->mac_addr.raw[2], peer->mac_addr.raw[3],
180 peer->mac_addr.raw[4], peer->mac_addr.raw[5],
181 (index ==
182 txrx_sec_ucast) ? "ucast" : "mcast",
183 last_pn->pn128[1], last_pn->pn128[0],
184 last_pn->pn128[0] & 0xffffffffffffULL,
185 new_pn.pn128[1], new_pn.pn128[0],
186 new_pn.pn128[0] & 0xffffffffffffULL,
187 htt_rx_mpdu_desc_seq_num(pdev->htt_pdev,
188 rx_desc));
189#if defined(ENABLE_RX_PN_TRACE)
190 ol_rx_pn_trace_display(pdev, 1);
191#endif /* ENABLE_RX_PN_TRACE */
192 ol_rx_err(pdev->ctrl_pdev,
193 vdev->vdev_id, peer->mac_addr.raw, tid,
194 htt_rx_mpdu_desc_tsf32(pdev->htt_pdev,
195 rx_desc), OL_RX_ERR_PN,
196 mpdu, NULL, 0);
197 /* free all MSDUs within this MPDU */
198 do {
199 cdf_nbuf_t next_msdu;
200 OL_RX_ERR_STATISTICS_1(pdev, vdev, peer,
201 rx_desc, OL_RX_ERR_PN);
202 next_msdu = cdf_nbuf_next(msdu);
203 htt_rx_desc_frame_free(pdev->htt_pdev, msdu);
204 if (msdu == mpdu_tail)
205 break;
206 else
207 msdu = next_msdu;
208 } while (1);
209 } else {
210 ADD_MPDU_TO_LIST(out_list_head, out_list_tail, mpdu,
211 mpdu_tail);
212 /*
213 * Remember the new PN.
214 * For simplicity, just do 2 64-bit word copies to
215 * cover the worst case (WAPI), regardless of the length
216 * of the PN.
217 * This is more efficient than doing a conditional
218 * branch to copy only the relevant portion.
219 */
220 last_pn->pn128[0] = new_pn.pn128[0];
221 last_pn->pn128[1] = new_pn.pn128[1];
222 OL_RX_PN_TRACE_ADD(pdev, peer, tid, rx_desc);
223 }
224
225 mpdu = next_mpdu;
226 }
227 /* make sure the list is null-terminated */
228 if (out_list_tail)
229 cdf_nbuf_set_next(out_list_tail, NULL);
230
231 return out_list_head;
232}
233
234void
235ol_rx_pn_check(struct ol_txrx_vdev_t *vdev,
236 struct ol_txrx_peer_t *peer, unsigned tid, cdf_nbuf_t msdu_list)
237{
238 msdu_list = ol_rx_pn_check_base(vdev, peer, tid, msdu_list);
239 ol_rx_fwd_check(vdev, peer, tid, msdu_list);
240}
241
242void
243ol_rx_pn_check_only(struct ol_txrx_vdev_t *vdev,
244 struct ol_txrx_peer_t *peer,
245 unsigned tid, cdf_nbuf_t msdu_list)
246{
247 msdu_list = ol_rx_pn_check_base(vdev, peer, tid, msdu_list);
248 ol_rx_deliver(vdev, peer, tid, msdu_list);
249}
250
251#if defined(ENABLE_RX_PN_TRACE)
252
253A_STATUS ol_rx_pn_trace_attach(ol_txrx_pdev_handle pdev)
254{
255 int num_elems;
256
257 num_elems = 1 << TXRX_RX_PN_TRACE_SIZE_LOG2;
258 pdev->rx_pn_trace.idx = 0;
259 pdev->rx_pn_trace.cnt = 0;
260 pdev->rx_pn_trace.mask = num_elems - 1;
261 pdev->rx_pn_trace.data =
262 cdf_mem_malloc(sizeof(*pdev->rx_pn_trace.data) * num_elems);
263 if (!pdev->rx_pn_trace.data)
264 return A_NO_MEMORY;
265 return A_OK;
266}
267
268void ol_rx_pn_trace_detach(ol_txrx_pdev_handle pdev)
269{
270 cdf_mem_free(pdev->rx_pn_trace.data);
271}
272
273void
274ol_rx_pn_trace_add(struct ol_txrx_pdev_t *pdev,
275 struct ol_txrx_peer_t *peer, uint16_t tid, void *rx_desc)
276{
277 uint32_t idx = pdev->rx_pn_trace.idx;
278 union htt_rx_pn_t pn;
279 uint32_t pn32;
280 uint16_t seq_num;
281 uint8_t unicast;
282
283 htt_rx_mpdu_desc_pn(pdev->htt_pdev, rx_desc, &pn, 48);
284 pn32 = pn.pn48 & 0xffffffff;
285 seq_num = htt_rx_mpdu_desc_seq_num(pdev->htt_pdev, rx_desc);
286 unicast = !htt_rx_msdu_is_wlan_mcast(pdev->htt_pdev, rx_desc);
287
288 pdev->rx_pn_trace.data[idx].peer = peer;
289 pdev->rx_pn_trace.data[idx].tid = tid;
290 pdev->rx_pn_trace.data[idx].seq_num = seq_num;
291 pdev->rx_pn_trace.data[idx].unicast = unicast;
292 pdev->rx_pn_trace.data[idx].pn32 = pn32;
293 pdev->rx_pn_trace.cnt++;
294 idx++;
295 pdev->rx_pn_trace.idx = idx & pdev->rx_pn_trace.mask;
296}
297
298void ol_rx_pn_trace_display(ol_txrx_pdev_handle pdev, int just_once)
299{
300 static int print_count /* = 0 */;
301 uint32_t i, start, end;
302 uint64_t cnt;
303 int elems;
304 int limit = 0; /* move this to the arg list? */
305
306 if (print_count != 0 && just_once)
307 return;
308
309 print_count++;
310
311 end = pdev->rx_pn_trace.idx;
312 if (pdev->rx_pn_trace.cnt <= pdev->rx_pn_trace.mask) {
313 /* trace log has not yet wrapped around - start at the top */
314 start = 0;
315 cnt = 0;
316 } else {
317 start = end;
318 cnt = pdev->rx_pn_trace.cnt - (pdev->rx_pn_trace.mask + 1);
319 }
320 elems = (end - 1 - start) & pdev->rx_pn_trace.mask;
321 if (limit > 0 && elems > limit) {
322 int delta;
323 delta = elems - limit;
324 start += delta;
325 start &= pdev->rx_pn_trace.mask;
326 cnt += delta;
327 }
328
329 i = start;
330 CDF_TRACE(CDF_MODULE_ID_TXRX, CDF_TRACE_LEVEL_INFO,
331 " seq PN");
332 CDF_TRACE(CDF_MODULE_ID_TXRX, CDF_TRACE_LEVEL_INFO,
333 " count idx peer tid uni num LSBs");
334 do {
335 CDF_TRACE(CDF_MODULE_ID_TXRX, CDF_TRACE_LEVEL_INFO,
336 " %6lld %4d %p %2d %d %4d %8d",
337 cnt, i,
338 pdev->rx_pn_trace.data[i].peer,
339 pdev->rx_pn_trace.data[i].tid,
340 pdev->rx_pn_trace.data[i].unicast,
341 pdev->rx_pn_trace.data[i].seq_num,
342 pdev->rx_pn_trace.data[i].pn32);
343 cnt++;
344 i++;
345 i &= pdev->rx_pn_trace.mask;
346 } while (i != end);
347}
348#endif /* ENABLE_RX_PN_TRACE */