blob: 9a1c724654e78575130806638074415a19962652 [file] [log] [blame]
Nachiket Kukaded0dd62e2018-05-21 18:39:22 +05301/*
2 * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/**
20 * DOC: wlan_hdd_apf.c
21 *
22 * Android Packet Filter support and implementation
23 */
24
25#include "wlan_hdd_apf.h"
26#include "qca_vendor.h"
27#include "wlan_hdd_request_manager.h"
28
29/*
30 * define short names for the global vendor params
31 * used by __wlan_hdd_cfg80211_bpf_offload()
32 */
33#define BPF_INVALID \
34 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
35#define BPF_SET_RESET \
36 QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
37#define BPF_VERSION \
38 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
39#define BPF_FILTER_ID \
40 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID
41#define BPF_PACKET_SIZE \
42 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE
43#define BPF_CURRENT_OFFSET \
44 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
45#define BPF_PROGRAM \
46 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
47#define BPF_MAX \
48 QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
49
50static const struct nla_policy
51wlan_hdd_bpf_offload_policy[BPF_MAX + 1] = {
52 [BPF_SET_RESET] = {.type = NLA_U32},
53 [BPF_VERSION] = {.type = NLA_U32},
54 [BPF_FILTER_ID] = {.type = NLA_U32},
55 [BPF_PACKET_SIZE] = {.type = NLA_U32},
56 [BPF_CURRENT_OFFSET] = {.type = NLA_U32},
57 [BPF_PROGRAM] = {.type = NLA_U8},
58};
59
60struct bpf_offload_priv {
61 struct sir_bpf_get_offload bpf_get_offload;
62};
63
64static void hdd_get_bpf_offload_cb(void *context,
65 struct sir_bpf_get_offload *data)
66{
67 struct hdd_request *request;
68 struct bpf_offload_priv *priv;
69
70 hdd_enter();
71
72 request = hdd_request_get(context);
73 if (!request) {
74 hdd_err("Obsolete request");
75 return;
76 }
77
78 priv = hdd_request_priv(request);
79 priv->bpf_get_offload = *data;
80 hdd_request_complete(request);
81 hdd_request_put(request);
82}
83
84/**
85 * hdd_post_get_bpf_capabilities_rsp() - Callback function to BPF Offload
86 * @hdd_context: hdd_context
87 * @bpf_get_offload: struct for get offload
88 *
89 * Return: 0 on success, error number otherwise.
90 */
91static int hdd_post_get_bpf_capabilities_rsp(struct hdd_context *hdd_ctx,
92 struct sir_bpf_get_offload *bpf_get_offload)
93{
94 struct sk_buff *skb;
95 uint32_t nl_buf_len;
96
97 hdd_enter();
98
99 nl_buf_len = NLMSG_HDRLEN;
100 nl_buf_len +=
101 (sizeof(bpf_get_offload->max_bytes_for_bpf_inst) + NLA_HDRLEN) +
102 (sizeof(bpf_get_offload->bpf_version) + NLA_HDRLEN);
103
104 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
105 if (!skb) {
106 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
107 return -ENOMEM;
108 }
109
110 hdd_debug("BPF Version: %u BPF max bytes: %u",
111 bpf_get_offload->bpf_version,
112 bpf_get_offload->max_bytes_for_bpf_inst);
113
114 if (nla_put_u32(skb, BPF_PACKET_SIZE,
115 bpf_get_offload->max_bytes_for_bpf_inst) ||
116 nla_put_u32(skb, BPF_VERSION, bpf_get_offload->bpf_version)) {
117 hdd_err("nla put failure");
118 goto nla_put_failure;
119 }
120
121 cfg80211_vendor_cmd_reply(skb);
122 hdd_exit();
123 return 0;
124
125nla_put_failure:
126 kfree_skb(skb);
127 return -EINVAL;
128}
129
130/**
131 * hdd_get_bpf_offload - Get BPF offload Capabilities
132 * @hdd_ctx: Hdd context
133 *
134 * Return: 0 on success, errno on failure
135 */
136static int hdd_get_bpf_offload(struct hdd_context *hdd_ctx)
137{
138 QDF_STATUS status;
139 int ret;
140 void *cookie;
141 struct hdd_request *request;
142 struct bpf_offload_priv *priv;
143 static const struct hdd_request_params params = {
144 .priv_size = sizeof(*priv),
145 .timeout_ms = WLAN_WAIT_TIME_BPF,
146 };
147
148 hdd_enter();
149
150 request = hdd_request_alloc(&params);
151 if (!request) {
152 hdd_err("Unable to allocate request");
153 return -EINVAL;
154 }
155 cookie = hdd_request_cookie(request);
156
157 status = sme_get_bpf_offload_capabilities(hdd_ctx->hHal,
158 hdd_get_bpf_offload_cb,
159 cookie);
160 if (!QDF_IS_STATUS_SUCCESS(status)) {
161 hdd_err("Unable to retrieve BPF caps");
162 ret = qdf_status_to_os_return(status);
163 goto cleanup;
164 }
165 ret = hdd_request_wait_for_response(request);
166 if (ret) {
167 hdd_err("Target response timed out");
168 goto cleanup;
169 }
170 priv = hdd_request_priv(request);
171 ret = hdd_post_get_bpf_capabilities_rsp(hdd_ctx,
172 &priv->bpf_get_offload);
173 if (ret)
174 hdd_err("Failed to post get bpf capabilities");
175
176cleanup:
177 /*
178 * either we never sent a request to SME, we sent a request to
179 * SME and timed out, or we sent a request to SME, received a
180 * response from SME, and posted the response to userspace.
181 * regardless we are done with the request.
182 */
183 hdd_request_put(request);
184 hdd_exit();
185
186 return ret;
187}
188
189/**
190 * hdd_set_reset_bpf_offload - Post set/reset bpf to SME
191 * @hdd_ctx: Hdd context
192 * @tb: Length of @data
193 * @adapter: pointer to adapter struct
194 *
195 * Return: 0 on success; errno on failure
196 */
197static int hdd_set_reset_bpf_offload(struct hdd_context *hdd_ctx,
198 struct nlattr **tb,
199 struct hdd_adapter *adapter)
200{
201 struct sir_bpf_set_offload *bpf_set_offload;
202 QDF_STATUS status;
203 int prog_len;
204 int ret = 0;
205
206 hdd_enter();
207
208 if (adapter->device_mode == QDF_STA_MODE ||
209 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
210 if (!hdd_conn_is_connected(
211 WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
212 hdd_err("Not in Connected state!");
213 return -ENOTSUPP;
214 }
215 }
216
217 bpf_set_offload = qdf_mem_malloc(sizeof(*bpf_set_offload));
218 if (bpf_set_offload == NULL) {
219 hdd_err("qdf_mem_malloc failed for bpf_set_offload");
220 return -ENOMEM;
221 }
222
223 /* Parse and fetch bpf packet size */
224 if (!tb[BPF_PACKET_SIZE]) {
225 hdd_err("attr bpf packet size failed");
226 ret = -EINVAL;
227 goto fail;
228 }
229 bpf_set_offload->total_length = nla_get_u32(tb[BPF_PACKET_SIZE]);
230
231 if (!bpf_set_offload->total_length) {
232 hdd_debug("BPF reset packet filter received");
233 goto post_sme;
234 }
235
236 /* Parse and fetch bpf program */
237 if (!tb[BPF_PROGRAM]) {
238 hdd_err("attr bpf program failed");
239 ret = -EINVAL;
240 goto fail;
241 }
242
243 prog_len = nla_len(tb[BPF_PROGRAM]);
244 bpf_set_offload->program = qdf_mem_malloc(sizeof(uint8_t) * prog_len);
245
246 if (bpf_set_offload->program == NULL) {
247 hdd_err("qdf_mem_malloc failed for bpf offload program");
248 ret = -ENOMEM;
249 goto fail;
250 }
251
252 bpf_set_offload->current_length = prog_len;
253 nla_memcpy(bpf_set_offload->program, tb[BPF_PROGRAM], prog_len);
254 bpf_set_offload->session_id = adapter->session_id;
255
256 hdd_debug("BPF set instructions");
257 QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
258 bpf_set_offload->program, prog_len);
259
260 /* Parse and fetch filter Id */
261 if (!tb[BPF_FILTER_ID]) {
262 hdd_err("attr filter id failed");
263 ret = -EINVAL;
264 goto fail;
265 }
266 bpf_set_offload->filter_id = nla_get_u32(tb[BPF_FILTER_ID]);
267
268 /* Parse and fetch current offset */
269 if (!tb[BPF_CURRENT_OFFSET]) {
270 hdd_err("attr current offset failed");
271 ret = -EINVAL;
272 goto fail;
273 }
274 bpf_set_offload->current_offset = nla_get_u32(tb[BPF_CURRENT_OFFSET]);
275
276post_sme:
277 hdd_debug("Posting BPF SET/RESET to SME, session_id: %d Bpf Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d",
278 bpf_set_offload->session_id,
279 bpf_set_offload->version,
280 bpf_set_offload->filter_id,
281 bpf_set_offload->total_length,
282 bpf_set_offload->current_length,
283 bpf_set_offload->current_offset);
284
285 status = sme_set_bpf_instructions(hdd_ctx->hHal, bpf_set_offload);
286 if (!QDF_IS_STATUS_SUCCESS(status)) {
287 hdd_err("sme_set_bpf_instructions failed(err=%d)", status);
288 ret = -EINVAL;
289 goto fail;
290 }
291 hdd_exit();
292
293fail:
294 if (bpf_set_offload->current_length)
295 qdf_mem_free(bpf_set_offload->program);
296 qdf_mem_free(bpf_set_offload);
297 return ret;
298}
299
300/**
301 * wlan_hdd_cfg80211_bpf_offload() - Set/Reset to BPF Offload
302 * @wiphy: wiphy structure pointer
303 * @wdev: Wireless device structure pointer
304 * @data: Pointer to the data received
305 * @data_len: Length of @data
306 *
307 * Return: 0 on success; errno on failure
308 */
309static int
310__wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy,
311 struct wireless_dev *wdev,
312 const void *data, int data_len)
313{
314 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
315 struct net_device *dev = wdev->netdev;
316 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
317 struct nlattr *tb[BPF_MAX + 1];
318 int ret_val, packet_filter_subcmd;
319
320 hdd_enter();
321
322 ret_val = wlan_hdd_validate_context(hdd_ctx);
323 if (ret_val)
324 return ret_val;
325
326 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
327 hdd_err("Command not allowed in FTM mode");
328 return -EINVAL;
329 }
330
331 if (!hdd_ctx->bpf_enabled) {
332 hdd_err("BPF offload is not supported/enabled");
333 return -ENOTSUPP;
334 }
335
336 if (wlan_cfg80211_nla_parse(tb, BPF_MAX, data, data_len,
337 wlan_hdd_bpf_offload_policy)) {
338 hdd_err("Invalid ATTR");
339 return -EINVAL;
340 }
341
342 if (!tb[BPF_SET_RESET]) {
343 hdd_err("attr bpf set reset failed");
344 return -EINVAL;
345 }
346
347 packet_filter_subcmd = nla_get_u32(tb[BPF_SET_RESET]);
348
349 if (packet_filter_subcmd == QCA_WLAN_GET_PACKET_FILTER)
350 return hdd_get_bpf_offload(hdd_ctx);
351 else
352 return hdd_set_reset_bpf_offload(hdd_ctx, tb,
353 adapter);
354}
355
356int wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy,
357 struct wireless_dev *wdev,
358 const void *data, int data_len)
359{
360 int ret;
361
362 cds_ssr_protect(__func__);
363 ret = __wlan_hdd_cfg80211_bpf_offload(wiphy, wdev, data, data_len);
364 cds_ssr_unprotect(__func__);
365
366 return ret;
367}
368